TNT Changes (#4970)
+ Make TNT drop pickups, change a few comments. + Give each ray random intensity, instead of each explosion. * Use direction instead of destination, rewrite for pairs of edges.
This commit is contained in:
parent
64442b05f8
commit
c11ca96c3d
|
@ -134,6 +134,15 @@ bool IsBlockFence(BLOCKTYPE a_BlockType)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
bool IsBlockShulkerBox(BLOCKTYPE a_BlockType)
|
||||||
|
{
|
||||||
|
return ((a_BlockType >= E_BLOCK_WHITE_SHULKER_BOX) && (a_BlockType <= E_BLOCK_BLACK_SHULKER_BOX));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool IsBlockMaterialWood(BLOCKTYPE a_BlockType)
|
bool IsBlockMaterialWood(BLOCKTYPE a_BlockType)
|
||||||
{
|
{
|
||||||
switch (a_BlockType)
|
switch (a_BlockType)
|
||||||
|
|
|
@ -86,6 +86,8 @@ bool IsBlockTypeOfDirt(BLOCKTYPE a_BlockType);
|
||||||
|
|
||||||
bool IsBlockFence(BLOCKTYPE a_BlockType);
|
bool IsBlockFence(BLOCKTYPE a_BlockType);
|
||||||
|
|
||||||
|
bool IsBlockShulkerBox(BLOCKTYPE a_BlockType);
|
||||||
|
|
||||||
bool IsBlockMaterialWood(BLOCKTYPE a_BlockType);
|
bool IsBlockMaterialWood(BLOCKTYPE a_BlockType);
|
||||||
|
|
||||||
bool IsBlockMaterialPlants(BLOCKTYPE a_BlockType);
|
bool IsBlockMaterialPlants(BLOCKTYPE a_BlockType);
|
||||||
|
|
|
@ -92,17 +92,44 @@ namespace Explodinator
|
||||||
Entity.TakeDamage(dtExplosion, nullptr, FloorC(Damage), 0);
|
Entity.TakeDamage(dtExplosion, nullptr, FloorC(Damage), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Impact reduced by armour:
|
// Impact reduced by armour, expensive call so only apply to Pawns:
|
||||||
const auto ReducedImpact = Impact - Impact * Entity.GetEnchantmentBlastKnockbackReduction(); // TODO: call is very expensive, should only apply to Pawns
|
if (Entity.IsPawn())
|
||||||
Entity.SetSpeed(Direction.NormalizeCopy() * KnockbackFactor * ReducedImpact);
|
{
|
||||||
|
const auto ReducedImpact = Impact - Impact * Entity.GetEnchantmentBlastKnockbackReduction();
|
||||||
|
Entity.SetSpeed(Direction.NormalizeCopy() * KnockbackFactor * ReducedImpact);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Entity.SetSpeed(Direction.NormalizeCopy() * KnockbackFactor * Impact);
|
||||||
|
}
|
||||||
|
|
||||||
// Continue iteration:
|
// Continue iteration:
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns true if block should always drop when exploded.
|
||||||
|
Currently missing conduits from 1.13 */
|
||||||
|
static bool BlockAlwaysDrops(const BLOCKTYPE a_Block)
|
||||||
|
{
|
||||||
|
// If it's a Shulker box
|
||||||
|
if (IsBlockShulkerBox(a_Block))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (a_Block)
|
||||||
|
{
|
||||||
|
case E_BLOCK_DRAGON_EGG:
|
||||||
|
case E_BLOCK_BEACON:
|
||||||
|
case E_BLOCK_HEAD: return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/** Sets the block at the given position, updating surroundings. */
|
/** Sets the block at the given position, updating surroundings. */
|
||||||
static void DestroyBlock(cWorld & a_World, cChunk & a_Chunk, const Vector3i a_AbsolutePosition, const Vector3i a_RelativePosition, const BLOCKTYPE a_DestroyedBlock, const BLOCKTYPE a_NewBlock, const cEntity * const a_ExplodingEntity)
|
static void SetBlock(cWorld & a_World, cChunk & a_Chunk, const Vector3i a_AbsolutePosition, const Vector3i a_RelativePosition, const BLOCKTYPE a_DestroyedBlock, const BLOCKTYPE a_NewBlock, const cEntity * const a_ExplodingEntity)
|
||||||
{
|
{
|
||||||
const auto DestroyedMeta = a_Chunk.GetMeta(a_RelativePosition);
|
const auto DestroyedMeta = a_Chunk.GetMeta(a_RelativePosition);
|
||||||
|
|
||||||
|
@ -116,7 +143,8 @@ namespace Explodinator
|
||||||
cBlockHandler::For(a_DestroyedBlock).OnBroken(Interface, a_World, a_AbsolutePosition, a_DestroyedBlock, DestroyedMeta, a_ExplodingEntity);
|
cBlockHandler::For(a_DestroyedBlock).OnBroken(Interface, a_World, a_AbsolutePosition, a_DestroyedBlock, DestroyedMeta, a_ExplodingEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sets the block at the given Position to air, updates surroundings, and spawns pickups, fire, shrapnel according to Minecraft rules.
|
/** Work out what should happen when an explosion destroys the given block.
|
||||||
|
Tasks include lighting TNT, dropping pickups, setting fire and flinging shrapnel according to Minecraft rules.
|
||||||
OK, _mostly_ Minecraft rules. */
|
OK, _mostly_ Minecraft rules. */
|
||||||
static void DestroyBlock(cChunk & a_Chunk, const Vector3i a_Position, const unsigned a_Power, const bool a_Fiery, const cEntity * const a_ExplodingEntity)
|
static void DestroyBlock(cChunk & a_Chunk, const Vector3i a_Position, const unsigned a_Power, const bool a_Fiery, const cEntity * const a_ExplodingEntity)
|
||||||
{
|
{
|
||||||
|
@ -131,7 +159,8 @@ namespace Explodinator
|
||||||
auto & World = *a_Chunk.GetWorld();
|
auto & World = *a_Chunk.GetWorld();
|
||||||
auto & Random = GetRandomProvider();
|
auto & Random = GetRandomProvider();
|
||||||
const auto Absolute = cChunkDef::RelativeToAbsolute(a_Position, a_Chunk.GetPos());
|
const auto Absolute = cChunkDef::RelativeToAbsolute(a_Position, a_Chunk.GetPos());
|
||||||
if (DestroyedBlock == E_BLOCK_TNT)
|
|
||||||
|
if (DestroyedBlock == E_BLOCK_TNT) // If the block is TNT we should set it off
|
||||||
{
|
{
|
||||||
// Random fuse between 10 to 30 game ticks.
|
// Random fuse between 10 to 30 game ticks.
|
||||||
const int FuseTime = Random.RandInt(10, 30);
|
const int FuseTime = Random.RandInt(10, 30);
|
||||||
|
@ -139,7 +168,7 @@ namespace Explodinator
|
||||||
// Activate the TNT, with initial velocity and no fuse sound:
|
// Activate the TNT, with initial velocity and no fuse sound:
|
||||||
World.SpawnPrimedTNT(Vector3d(0.5, 0, 0.5) + Absolute, FuseTime, 1, false);
|
World.SpawnPrimedTNT(Vector3d(0.5, 0, 0.5) + Absolute, FuseTime, 1, false);
|
||||||
}
|
}
|
||||||
else if (Random.RandBool(1.f / a_Power))
|
else if (a_ExplodingEntity->IsTNT() || BlockAlwaysDrops(DestroyedBlock) || Random.RandBool(1.f / a_Power)) // For TNT explosions, destroying a block that always drops, or if RandBool, drop pickups
|
||||||
{
|
{
|
||||||
const auto DestroyedMeta = a_Chunk.GetMeta(a_Position);
|
const auto DestroyedMeta = a_Chunk.GetMeta(a_Position);
|
||||||
a_Chunk.GetWorld()->SpawnItemPickups(cBlockHandler::For(DestroyedBlock).ConvertToPickups(DestroyedMeta), Absolute);
|
a_Chunk.GetWorld()->SpawnItemPickups(cBlockHandler::For(DestroyedBlock).ConvertToPickups(DestroyedMeta), Absolute);
|
||||||
|
@ -150,11 +179,11 @@ namespace Explodinator
|
||||||
if ((Below.y >= 0) && cBlockInfo::FullyOccupiesVoxel(a_Chunk.GetBlock(Below)))
|
if ((Below.y >= 0) && cBlockInfo::FullyOccupiesVoxel(a_Chunk.GetBlock(Below)))
|
||||||
{
|
{
|
||||||
// Start a fire:
|
// Start a fire:
|
||||||
DestroyBlock(World, a_Chunk, Absolute, a_Position, DestroyedBlock, E_BLOCK_FIRE, a_ExplodingEntity);
|
SetBlock(World, a_Chunk, Absolute, a_Position, DestroyedBlock, E_BLOCK_FIRE, a_ExplodingEntity);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (const auto Shrapnel = World.GetTNTShrapnelLevel(); (Shrapnel > slNone) && Random.RandBool(0)) // 20% chance of flinging stuff around
|
else if (const auto Shrapnel = World.GetTNTShrapnelLevel(); (Shrapnel > slNone) && Random.RandBool(0)) // Currently 0% chance of flinging stuff around
|
||||||
{
|
{
|
||||||
// If the block is shrapnel-able, make a falling block entity out of it:
|
// If the block is shrapnel-able, make a falling block entity out of it:
|
||||||
if (
|
if (
|
||||||
|
@ -169,23 +198,20 @@ namespace Explodinator
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DestroyBlock(World, a_Chunk, Absolute, a_Position, DestroyedBlock, E_BLOCK_AIR, a_ExplodingEntity);
|
SetBlock(World, a_Chunk, Absolute, a_Position, DestroyedBlock, E_BLOCK_AIR, a_ExplodingEntity);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Traces the path taken by one Explosion Lazor (tm) with given direction and intensity, that will destroy blocks until it is exhausted. */
|
/** Traces the path taken by one Explosion Lazor (tm) with given direction and intensity, that will destroy blocks until it is exhausted. */
|
||||||
static void DestructionTrace(cChunk * a_Chunk, Vector3f a_Origin, const Vector3f a_Destination, const unsigned a_Power, const bool a_Fiery, float a_Intensity, const cEntity * const a_ExplodingEntity)
|
static void DestructionTrace(cChunk * a_Chunk, Vector3f a_Origin, const Vector3f a_Direction, const unsigned a_Power, const bool a_Fiery, float a_Intensity, const cEntity * const a_ExplodingEntity)
|
||||||
{
|
{
|
||||||
// The current position the ray is at.
|
// The current position the ray is at.
|
||||||
auto Checkpoint = a_Origin;
|
auto Checkpoint = a_Origin;
|
||||||
|
|
||||||
// The total displacement the ray must travel.
|
// The displacement that the ray in one iteration step should travel.
|
||||||
const auto TraceDisplacement = (a_Destination - a_Origin);
|
const auto Step = a_Direction.NormalizeCopy() * StepUnit;
|
||||||
|
|
||||||
// The displacement that they ray in one iteration step should travel.
|
// Loop until intensity runs out:
|
||||||
const auto Step = TraceDisplacement.NormalizeCopy() * StepUnit;
|
while (a_Intensity > 0)
|
||||||
|
|
||||||
// Loop until we've reached the prescribed destination:
|
|
||||||
while (TraceDisplacement > (Checkpoint - a_Origin))
|
|
||||||
{
|
{
|
||||||
auto Position = Checkpoint.Floor();
|
auto Position = Checkpoint.Floor();
|
||||||
if (!cChunkDef::IsValidHeight(Position.y))
|
if (!cChunkDef::IsValidHeight(Position.y))
|
||||||
|
@ -219,54 +245,49 @@ namespace Explodinator
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns a random intensity for an Explosion Lazor (tm) as a function of the explosion's power. */
|
||||||
|
static float RandomIntensity(MTRand & a_Random, const unsigned a_Power)
|
||||||
|
{
|
||||||
|
return a_Power * (0.7f + a_Random.RandReal(0.6f));
|
||||||
|
}
|
||||||
|
|
||||||
/** Sends out Explosion Lazors (tm) originating from the given position that destroy blocks. */
|
/** Sends out Explosion Lazors (tm) originating from the given position that destroy blocks. */
|
||||||
static void DamageBlocks(cChunk & a_Chunk, const Vector3f a_Position, const unsigned a_Power, const bool a_Fiery, const cEntity * const a_ExplodingEntity)
|
static void DamageBlocks(cChunk & a_Chunk, const Vector3f a_Position, const unsigned a_Power, const bool a_Fiery, const cEntity * const a_ExplodingEntity)
|
||||||
{
|
{
|
||||||
const auto Intensity = a_Power * (0.7f + GetRandomProvider().RandReal(0.6f));
|
|
||||||
const auto ExplosionRadius = CeilC((Intensity / StepAttenuation) * StepUnit);
|
|
||||||
|
|
||||||
// Oh boy... Better hope you have a hot cache, 'cos this little manoeuvre's gonna cost us 1352 raytraces in one tick...
|
// Oh boy... Better hope you have a hot cache, 'cos this little manoeuvre's gonna cost us 1352 raytraces in one tick...
|
||||||
const int HalfSide = TraceCubeSideLength / 2;
|
const int HalfSide = TraceCubeSideLength / 2;
|
||||||
|
auto & Random = GetRandomProvider();
|
||||||
|
|
||||||
// The following loops implement the tracing algorithm described in http://minecraft.gamepedia.com/Explosion
|
// The following loops implement the tracing algorithm described in http://minecraft.gamepedia.com/Explosion
|
||||||
|
|
||||||
// Trace rays from the explosion centre to all points in a square of area TraceCubeSideLength * TraceCubeSideLength
|
// Trace rays from the explosion centre to all points in a square of area TraceCubeSideLength * TraceCubeSideLength
|
||||||
// in the YM and YP directions:
|
// for the top and bottom sides:
|
||||||
for (int OffsetX = -HalfSide; OffsetX < HalfSide; OffsetX++)
|
for (int OffsetX = -HalfSide; OffsetX < HalfSide; OffsetX++)
|
||||||
{
|
{
|
||||||
for (int OffsetZ = -HalfSide; OffsetZ < HalfSide; OffsetZ++)
|
for (int OffsetZ = -HalfSide; OffsetZ < HalfSide; OffsetZ++)
|
||||||
{
|
{
|
||||||
DestructionTrace(&a_Chunk, a_Position, a_Position + Vector3f(OffsetX, +ExplosionRadius, OffsetZ), a_Power, a_Fiery, Intensity, a_ExplodingEntity);
|
DestructionTrace(&a_Chunk, a_Position, Vector3f(OffsetX, +1, OffsetZ), a_Power, a_Fiery, RandomIntensity(Random, a_Power), a_ExplodingEntity);
|
||||||
DestructionTrace(&a_Chunk, a_Position, a_Position + Vector3f(OffsetX, -ExplosionRadius, OffsetZ), a_Power, a_Fiery, Intensity, a_ExplodingEntity);
|
DestructionTrace(&a_Chunk, a_Position, Vector3f(OffsetX, -1, OffsetZ), a_Power, a_Fiery, RandomIntensity(Random, a_Power), a_ExplodingEntity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// Left and right sides, avoid duplicates at top and bottom edges:
|
||||||
Trace rays from the centre to the sides of the explosion cube, being careful to avoid duplicates:
|
for (int OffsetX = -HalfSide; OffsetX < HalfSide; OffsetX++)
|
||||||
|
|
||||||
Top view:
|
|
||||||
______
|
|
||||||
. | (dot to make style checker happy)
|
|
||||||
| |
|
|
||||||
| |
|
|
||||||
| ______
|
|
||||||
|
|
||||||
Side view:
|
|
||||||
+ +
|
|
||||||
|====== |
|
|
||||||
| | |
|
|
||||||
|====== |
|
|
||||||
+ +
|
|
||||||
|
|
||||||
*/
|
|
||||||
for (int Offset = -HalfSide; Offset < HalfSide - 1; Offset++)
|
|
||||||
{
|
{
|
||||||
for (int OffsetY = -HalfSide + 1; OffsetY < HalfSide - 1; OffsetY++)
|
for (int OffsetY = -HalfSide + 1; OffsetY < HalfSide - 1; OffsetY++)
|
||||||
{
|
{
|
||||||
DestructionTrace(&a_Chunk, a_Position, a_Position + Vector3f(ExplosionRadius, OffsetY, Offset + 1), a_Power, a_Fiery, Intensity, a_ExplodingEntity);
|
DestructionTrace(&a_Chunk, a_Position, Vector3f(OffsetX, OffsetY, +1), a_Power, a_Fiery, RandomIntensity(Random, a_Power), a_ExplodingEntity);
|
||||||
DestructionTrace(&a_Chunk, a_Position, a_Position + Vector3f(-ExplosionRadius, OffsetY, Offset), a_Power, a_Fiery, Intensity, a_ExplodingEntity);
|
DestructionTrace(&a_Chunk, a_Position, Vector3f(OffsetX, OffsetY, -1), a_Power, a_Fiery, RandomIntensity(Random, a_Power), a_ExplodingEntity);
|
||||||
DestructionTrace(&a_Chunk, a_Position, a_Position + Vector3f(Offset, OffsetY, ExplosionRadius), a_Power, a_Fiery, Intensity, a_ExplodingEntity);
|
}
|
||||||
DestructionTrace(&a_Chunk, a_Position, a_Position + Vector3f(Offset + 1, OffsetY, -ExplosionRadius), a_Power, a_Fiery, Intensity, a_ExplodingEntity);
|
}
|
||||||
|
|
||||||
|
// Front and back sides, avoid all edges:
|
||||||
|
for (int OffsetZ = -HalfSide + 1; OffsetZ < HalfSide - 1; OffsetZ++)
|
||||||
|
{
|
||||||
|
for (int OffsetY = -HalfSide + 1; OffsetY < HalfSide - 1; OffsetY++)
|
||||||
|
{
|
||||||
|
DestructionTrace(&a_Chunk, a_Position, Vector3f(+1, OffsetY, OffsetZ), a_Power, a_Fiery, RandomIntensity(Random, a_Power), a_ExplodingEntity);
|
||||||
|
DestructionTrace(&a_Chunk, a_Position, Vector3f(-1, OffsetY, OffsetZ), a_Power, a_Fiery, RandomIntensity(Random, a_Power), a_ExplodingEntity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1390,10 +1390,9 @@ bool cWorld::ForEachFurnaceInChunk(int a_ChunkX, int a_ChunkZ, cFurnaceCallback
|
||||||
void cWorld::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_BlockY, double a_BlockZ, bool a_CanCauseFire, eExplosionSource a_Source, void * a_SourceData)
|
void cWorld::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_BlockY, double a_BlockZ, bool a_CanCauseFire, eExplosionSource a_Source, void * a_SourceData)
|
||||||
{
|
{
|
||||||
cLock Lock(*this);
|
cLock Lock(*this);
|
||||||
|
|
||||||
if (!cPluginManager::Get()->CallHookExploding(*this, a_ExplosionSize, a_CanCauseFire, a_BlockX, a_BlockY, a_BlockZ, a_Source, a_SourceData) && (a_ExplosionSize > 0))
|
if (!cPluginManager::Get()->CallHookExploding(*this, a_ExplosionSize, a_CanCauseFire, a_BlockX, a_BlockY, a_BlockZ, a_Source, a_SourceData) && (a_ExplosionSize > 0))
|
||||||
{
|
{
|
||||||
// TODO: CanCauseFire gets reset to false for some reason
|
// TODO: CanCauseFire gets reset to false for some reason, (plugin has ability to change it, might be related)
|
||||||
|
|
||||||
const cEntity * Entity;
|
const cEntity * Entity;
|
||||||
switch (a_Source)
|
switch (a_Source)
|
||||||
|
|
Loading…
Reference in New Issue