summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTiger Wang <ziwei.tiger@outlook.com>2020-09-12 20:57:44 +0200
committerGitHub <noreply@github.com>2020-09-12 20:57:44 +0200
commit93adbdce9a769b42baeb70f9ead5c7c6a35834b5 (patch)
treee0116770113a78af0d2d1c4d44a3878f551f0703
parentfixed missing include for FreeBSD. (#4852) (diff)
downloadcuberite-93adbdce9a769b42baeb70f9ead5c7c6a35834b5.tar
cuberite-93adbdce9a769b42baeb70f9ead5c7c6a35834b5.tar.gz
cuberite-93adbdce9a769b42baeb70f9ead5c7c6a35834b5.tar.bz2
cuberite-93adbdce9a769b42baeb70f9ead5c7c6a35834b5.tar.lz
cuberite-93adbdce9a769b42baeb70f9ead5c7c6a35834b5.tar.xz
cuberite-93adbdce9a769b42baeb70f9ead5c7c6a35834b5.tar.zst
cuberite-93adbdce9a769b42baeb70f9ead5c7c6a35834b5.zip
-rw-r--r--src/BlockInfo.cpp121
-rw-r--r--src/BlockInfo.h4
-rw-r--r--src/CMakeLists.txt4
-rw-r--r--src/Chunk.cpp1
-rw-r--r--src/ChunkMap.cpp197
-rw-r--r--src/ChunkMap.h3
-rw-r--r--src/ClientHandle.cpp24
-rw-r--r--src/ClientHandle.h2
-rw-r--r--src/Entities/Entity.h1
-rw-r--r--src/Entities/TNTEntity.cpp13
-rw-r--r--src/Entities/TNTEntity.h10
-rw-r--r--src/Physics/CMakeLists.txt9
-rw-r--r--src/Physics/Explodinator.cpp295
-rw-r--r--src/Physics/Explodinator.h20
-rw-r--r--src/Protocol/Protocol.h2
-rw-r--r--src/Protocol/Protocol_1_8.cpp24
-rw-r--r--src/Protocol/Protocol_1_8.h2
-rw-r--r--src/Simulator/IncrementalRedstoneSimulator/RedstoneSimulatorChunkData.h3
-rw-r--r--src/Simulator/SandSimulator.cpp46
-rw-r--r--src/Simulator/SandSimulator.h4
-rw-r--r--src/World.cpp43
-rw-r--r--src/WorldStorage/StatSerializer.cpp1
22 files changed, 530 insertions, 299 deletions
diff --git a/src/BlockInfo.cpp b/src/BlockInfo.cpp
index f096ccc80..a967b9acf 100644
--- a/src/BlockInfo.cpp
+++ b/src/BlockInfo.cpp
@@ -436,6 +436,127 @@ bool cBlockInfo::IsSnowable(BLOCKTYPE a_BlockType)
+float cBlockInfo::GetExplosionAbsorption(const BLOCKTYPE Block)
+{
+ switch (Block)
+ {
+ case E_BLOCK_BEDROCK:
+ case E_BLOCK_COMMAND_BLOCK:
+ case E_BLOCK_END_GATEWAY:
+ case E_BLOCK_END_PORTAL:
+ case E_BLOCK_END_PORTAL_FRAME: return 3600000;
+ case E_BLOCK_ANVIL:
+ case E_BLOCK_ENCHANTMENT_TABLE:
+ case E_BLOCK_OBSIDIAN: return 1200;
+ case E_BLOCK_ENDER_CHEST: return 600;
+ case E_BLOCK_LAVA:
+ case E_BLOCK_STATIONARY_LAVA:
+ case E_BLOCK_WATER:
+ case E_BLOCK_STATIONARY_WATER: return 100;
+ case E_BLOCK_DRAGON_EGG:
+ case E_BLOCK_END_STONE:
+ case E_BLOCK_END_BRICKS: return 9;
+ case E_BLOCK_STONE:
+ case E_BLOCK_BLOCK_OF_COAL:
+ case E_BLOCK_DIAMOND_BLOCK:
+ case E_BLOCK_EMERALD_BLOCK:
+ case E_BLOCK_GOLD_BLOCK:
+ case E_BLOCK_IRON_BLOCK:
+ case E_BLOCK_BLOCK_OF_REDSTONE:
+ case E_BLOCK_BRICK:
+ case E_BLOCK_BRICK_STAIRS:
+ case E_BLOCK_COBBLESTONE:
+ case E_BLOCK_COBBLESTONE_STAIRS:
+ case E_BLOCK_IRON_BARS:
+ case E_BLOCK_JUKEBOX:
+ case E_BLOCK_MOSSY_COBBLESTONE:
+ case E_BLOCK_NETHER_BRICK:
+ case E_BLOCK_NETHER_BRICK_FENCE:
+ case E_BLOCK_NETHER_BRICK_STAIRS:
+ case E_BLOCK_PRISMARINE_BLOCK:
+ case E_BLOCK_STONE_BRICKS:
+ case E_BLOCK_STONE_BRICK_STAIRS:
+ case E_BLOCK_COBBLESTONE_WALL: return 6;
+ case E_BLOCK_IRON_DOOR:
+ case E_BLOCK_IRON_TRAPDOOR:
+ case E_BLOCK_MOB_SPAWNER: return 5;
+ case E_BLOCK_HOPPER: return 4.8f;
+ case E_BLOCK_TERRACOTTA: return 4.2f;
+ case E_BLOCK_COBWEB: return 4;
+ case E_BLOCK_DISPENSER:
+ case E_BLOCK_DROPPER:
+ case E_BLOCK_FURNACE:
+ case E_BLOCK_OBSERVER: return 3.5f;
+ case E_BLOCK_BEACON:
+ case E_BLOCK_COAL_ORE:
+ case E_BLOCK_COCOA_POD:
+ case E_BLOCK_DIAMOND_ORE:
+ case E_BLOCK_EMERALD_ORE:
+ case E_BLOCK_GOLD_ORE:
+ case E_BLOCK_IRON_ORE:
+ case E_BLOCK_LAPIS_BLOCK:
+ case E_BLOCK_LAPIS_ORE:
+ case E_BLOCK_NETHER_QUARTZ_ORE:
+ case E_BLOCK_PLANKS:
+ case E_BLOCK_REDSTONE_ORE:
+ case E_BLOCK_FENCE:
+ case E_BLOCK_FENCE_GATE:
+ case E_BLOCK_WOODEN_DOOR:
+ case E_BLOCK_WOODEN_SLAB:
+ case E_BLOCK_WOODEN_STAIRS:
+ case E_BLOCK_TRAPDOOR: return 3;
+ case E_BLOCK_CHEST:
+ case E_BLOCK_WORKBENCH:
+ case E_BLOCK_TRAPPED_CHEST: return 2.5f;
+ case E_BLOCK_BONE_BLOCK:
+ case E_BLOCK_CAULDRON:
+ case E_BLOCK_LOG: return 2;
+ case E_BLOCK_CONCRETE: return 1.8f;
+ case E_BLOCK_BOOKCASE: return 1.5f;
+ case E_BLOCK_STANDING_BANNER:
+ case E_BLOCK_WALL_BANNER:
+ case E_BLOCK_JACK_O_LANTERN:
+ case E_BLOCK_MELON:
+ case E_BLOCK_HEAD:
+ case E_BLOCK_NETHER_WART_BLOCK:
+ case E_BLOCK_PUMPKIN:
+ case E_BLOCK_SIGN_POST:
+ case E_BLOCK_WALLSIGN: return 1;
+ case E_BLOCK_QUARTZ_BLOCK:
+ case E_BLOCK_QUARTZ_STAIRS:
+ case E_BLOCK_RED_SANDSTONE:
+ case E_BLOCK_RED_SANDSTONE_STAIRS:
+ case E_BLOCK_SANDSTONE:
+ case E_BLOCK_SANDSTONE_STAIRS:
+ case E_BLOCK_WOOL: return 0.8f;
+ case E_BLOCK_SILVERFISH_EGG: return 0.75f;
+ case E_BLOCK_ACTIVATOR_RAIL:
+ case E_BLOCK_DETECTOR_RAIL:
+ case E_BLOCK_POWERED_RAIL:
+ case E_BLOCK_RAIL: return 0.7f;
+ case E_BLOCK_GRASS_PATH:
+ case E_BLOCK_CLAY:
+ case E_BLOCK_FARMLAND:
+ case E_BLOCK_GRASS:
+ case E_BLOCK_GRAVEL:
+ case E_BLOCK_SPONGE: return 0.6f;
+ case E_BLOCK_BREWING_STAND:
+ case E_BLOCK_STONE_BUTTON:
+ case E_BLOCK_WOODEN_BUTTON:
+ case E_BLOCK_CAKE:
+ case E_BLOCK_CONCRETE_POWDER:
+ case E_BLOCK_DIRT:
+ case E_BLOCK_FROSTED_ICE:
+ case E_BLOCK_HAY_BALE:
+ case E_BLOCK_ICE: return 0.5f;
+ default: return 0;
+ }
+}
+
+
+
+
+
void cBlockInfo::sHandlerDeleter::operator () (cBlockHandler * a_Handler)
{
delete a_Handler;
diff --git a/src/BlockInfo.h b/src/BlockInfo.h
index 9644eb208..5992db9d9 100644
--- a/src/BlockInfo.h
+++ b/src/BlockInfo.h
@@ -47,6 +47,10 @@ public:
// tolua_end
+ /** Returns how much of an explosion Destruction Lazor's (tm) intensity the given block attenuates.
+ See Physics\Explodinator.cpp for details of explosion block destruction. */
+ static float GetExplosionAbsorption(BLOCKTYPE Block);
+
inline static cBlockHandler * GetHandler (BLOCKTYPE a_Type) { return Get(a_Type).m_Handler.get(); }
/** Creates a default BlockInfo structure, initializes all values to their defaults */
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index fd8d83e17..9f18fd9d0 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -160,7 +160,7 @@ target_sources(
set(FOLDERS
Bindings BlockEntities Blocks Entities
Generating HTTP Items mbedTLS++ Mobs Noise
- OSSupport Protocol Registries Simulator
+ OSSupport Physics Protocol Registries Simulator
Simulator/IncrementalRedstoneSimulator UI WorldStorage
)
@@ -173,7 +173,7 @@ file(WRITE "${CMAKE_BINARY_DIR}/include/Globals.h"
"/* This file allows Globals.h to be included with an absolute path */\n#include \"${PROJECT_SOURCE_DIR}/src/Globals.h\"\n")
configure_file("BuildInfo.h.cmake" "${CMAKE_BINARY_DIR}/include/BuildInfo.h")
-target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE "${CMAKE_BINARY_DIR}/include/")
+target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE "${CMAKE_BINARY_DIR}/include/" ".")
# Generate AllFiles.lst for CheckBasicStyle.lua
get_target_property(ALL_FILES ${CMAKE_PROJECT_NAME} SOURCES)
diff --git a/src/Chunk.cpp b/src/Chunk.cpp
index eeeff3e8b..3f6a653a4 100644
--- a/src/Chunk.cpp
+++ b/src/Chunk.cpp
@@ -419,6 +419,7 @@ void cChunk::WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlock
{
if (affectedArea.IsInside(itr->second->GetPos()))
{
+ itr->second->Destroy();
itr = m_BlockEntities.erase(itr);
}
else
diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp
index 92cce9b55..71b8a7a24 100644
--- a/src/ChunkMap.cpp
+++ b/src/ChunkMap.cpp
@@ -1020,203 +1020,6 @@ bool cChunkMap::ForEachEntityInBox(const cBoundingBox & a_Box, cEntityCallback a
-void cChunkMap::DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_BlockY, double a_BlockZ, cVector3iArray & a_BlocksAffected)
-{
- // Don't explode if outside of Y range (prevents the following test running into unallocated memory):
- if (!cChunkDef::IsValidHeight(FloorC(a_BlockY)))
- {
- return;
- }
-
- bool ShouldDestroyBlocks = true;
-
- // Don't explode if the explosion center is inside a liquid block:
- if (IsBlockLiquid(m_World->GetBlock(FloorC(a_BlockX), FloorC(a_BlockY), FloorC(a_BlockZ))))
- {
- ShouldDestroyBlocks = false;
- }
-
- int ExplosionSizeInt = CeilC(a_ExplosionSize);
- int ExplosionSizeSq = ExplosionSizeInt * ExplosionSizeInt;
-
- int bx = FloorC(a_BlockX);
- int by = FloorC(a_BlockY);
- int bz = FloorC(a_BlockZ);
-
- int MinY = std::max(FloorC(a_BlockY - ExplosionSizeInt), 0);
- int MaxY = std::min(CeilC(a_BlockY + ExplosionSizeInt), cChunkDef::Height - 1);
-
- if (ShouldDestroyBlocks)
- {
- cBlockArea area;
- a_BlocksAffected.reserve(8 * static_cast<size_t>(ExplosionSizeInt * ExplosionSizeInt * ExplosionSizeInt));
- if (!area.Read(*m_World, bx - ExplosionSizeInt, static_cast<int>(ceil(a_BlockX + ExplosionSizeInt)), MinY, MaxY, bz - ExplosionSizeInt, static_cast<int>(ceil(a_BlockZ + ExplosionSizeInt))))
- {
- return;
- }
-
- for (int x = -ExplosionSizeInt; x < ExplosionSizeInt; x++)
- {
- for (int y = -ExplosionSizeInt; y < ExplosionSizeInt; y++)
- {
- if ((by + y >= cChunkDef::Height) || (by + y < 0))
- {
- // Outside of the world
- continue;
- }
- for (int z = -ExplosionSizeInt; z < ExplosionSizeInt; z++)
- {
- if ((x * x + y * y + z * z) > ExplosionSizeSq)
- {
- // Too far away
- continue;
- }
-
- BLOCKTYPE Block = area.GetBlockType(bx + x, by + y, bz + z);
- switch (Block)
- {
- case E_BLOCK_TNT:
- {
- // Activate the TNT, with a random fuse between 10 to 30 game ticks
- int FuseTime = GetRandomProvider().RandInt(10, 30);
- m_World->SpawnPrimedTNT({a_BlockX + x + 0.5, a_BlockY + y + 0.5, a_BlockZ + z + 0.5}, FuseTime, 1, false); // Initial velocity, no fuse sound
- area.SetBlockTypeMeta(bx + x, by + y, bz + z, E_BLOCK_AIR, 0);
- a_BlocksAffected.push_back(Vector3i(bx + x, by + y, bz + z));
- break;
- }
-
- case E_BLOCK_OBSIDIAN:
- case E_BLOCK_BEACON:
- case E_BLOCK_BEDROCK:
- case E_BLOCK_BARRIER:
- case E_BLOCK_WATER:
- case E_BLOCK_LAVA:
- {
- // These blocks are not affected by explosions
- break;
- }
-
- case E_BLOCK_STATIONARY_WATER:
- {
- // Turn into simulated water:
- area.SetBlockType(bx + x, by + y, bz + z, E_BLOCK_WATER);
- break;
- }
-
- case E_BLOCK_STATIONARY_LAVA:
- {
- // Turn into simulated lava:
- area.SetBlockType(bx + x, by + y, bz + z, E_BLOCK_LAVA);
- break;
- }
-
- case E_BLOCK_AIR:
- {
- // No pickups for air
- break;
- }
-
- default:
- {
- auto & Random = GetRandomProvider();
- if (Random.RandBool(0.25)) // 25% chance of pickups
- {
- auto pickups = area.PickupsFromBlock({bx + x, by + y, bz + z});
- m_World->SpawnItemPickups(pickups, bx + x, by + y, bz + z);
- }
- else if ((m_World->GetTNTShrapnelLevel() > slNone) && Random.RandBool(0.20)) // 20% chance of flinging stuff around
- {
- // If the block is shrapnel-able, make a falling block entity out of it:
- if (
- ((m_World->GetTNTShrapnelLevel() == slAll) && cBlockInfo::FullyOccupiesVoxel(Block)) ||
- ((m_World->GetTNTShrapnelLevel() == slGravityAffectedOnly) && ((Block == E_BLOCK_SAND) || (Block == E_BLOCK_GRAVEL)))
- )
- {
- m_World->SpawnFallingBlock(bx + x, by + y + 5, bz + z, Block, area.GetBlockMeta(bx + x, by + y, bz + z));
- }
- }
-
- // Destroy any block entities
- if (cBlockEntity::IsBlockEntityBlockType(Block))
- {
- Vector3i BlockPos(bx + x, by + y, bz + z);
- DoWithBlockEntityAt(BlockPos.x, BlockPos.y, BlockPos.z, [](cBlockEntity & a_BE)
- {
- a_BE.Destroy();
- return true;
- }
- );
- }
-
- area.SetBlockTypeMeta(bx + x, by + y, bz + z, E_BLOCK_AIR, 0);
- a_BlocksAffected.push_back(Vector3i(bx + x, by + y, bz + z));
- break;
- }
- } // switch (BlockType)
- } // for z
- } // for y
- } // for x
- area.Write(*m_World, bx - ExplosionSizeInt, MinY, bz - ExplosionSizeInt);
- }
-
- Vector3d ExplosionPos{ a_BlockX, a_BlockY, a_BlockZ };
- cBoundingBox bbTNT(ExplosionPos, 0.5, 1);
- bbTNT.Expand(ExplosionSizeInt * 2, ExplosionSizeInt * 2, ExplosionSizeInt * 2);
-
- ForEachEntity([&](cEntity & a_Entity)
- {
- if (a_Entity.IsPickup() && (a_Entity.GetTicksAlive() < 20))
- {
- // If pickup age is smaller than one second, it is invincible (so we don't kill pickups that were just spawned)
- return false;
- }
-
- Vector3d DistanceFromExplosion = a_Entity.GetPosition() - ExplosionPos;
-
- if (!a_Entity.IsTNT() && !a_Entity.IsFallingBlock()) // Don't apply damage to other TNT entities and falling blocks, they should be invincible
- {
- auto EntityBox = a_Entity.GetBoundingBox();
- if (!bbTNT.IsInside(EntityBox)) // If entity box is inside tnt box, not vice versa!
- {
- return false;
- }
-
- // Ensure that the damage dealt is inversely proportional to the distance to the TNT centre - the closer a player is, the harder they are hit
- a_Entity.TakeDamage(dtExplosion, nullptr, static_cast<int>((1 / std::max(1.0, DistanceFromExplosion.Length())) * 8 * ExplosionSizeInt), 0);
- }
-
- float EntityExposure = a_Entity.GetExplosionExposureRate(ExplosionPos, static_cast<float>(a_ExplosionSize));
-
- // Exposure reduced by armor
- EntityExposure = EntityExposure * (1.0f - a_Entity.GetEnchantmentBlastKnockbackReduction());
-
- auto Impact = std::pow(std::max(0.2, DistanceFromExplosion.Length()), -1);
- Impact *= EntityExposure * ExplosionSizeInt * 6.0;
-
- if (Impact > 0.0)
- {
- DistanceFromExplosion.Normalize();
- DistanceFromExplosion *= Vector3d{Impact, 0.0, Impact};
- DistanceFromExplosion.y += 0.3 * Impact;
-
- a_Entity.SetSpeed(DistanceFromExplosion);
- }
-
- return false;
- }
- );
-
- // Wake up all simulators for the area, so that water and lava flows and sand falls into the blasted holes (FS #391):
- m_World->GetSimulatorManager()->WakeUp(cCuboid(
- {bx - ExplosionSizeInt - 1, MinY, bz - ExplosionSizeInt - 1},
- {bx + ExplosionSizeInt + 1, MaxY, bz + ExplosionSizeInt + 1}
- ));
-}
-
-
-
-
-
bool cChunkMap::DoWithEntityByID(UInt32 a_UniqueID, cEntityCallback a_Callback) const
{
cCSLock Lock(m_CSChunks);
diff --git a/src/ChunkMap.h b/src/ChunkMap.h
index 795672135..4a7e952b5 100644
--- a/src/ChunkMap.h
+++ b/src/ChunkMap.h
@@ -222,9 +222,6 @@ public:
If any chunk in the box is missing, ignores the entities in that chunk silently. */
bool ForEachEntityInBox(const cBoundingBox & a_Box, cEntityCallback a_Callback); // Lua-accessible
- /** Destroys and returns a list of blocks destroyed in the explosion at the specified coordinates */
- void DoExplosionAt(double a_ExplosionSize, double a_BlockX, double a_BlockY, double a_BlockZ, cVector3iArray & a_BlockAffected);
-
/** Calls the callback if the entity with the specified ID is found, with the entity object as the callback param.
Returns true if entity found and callback returned false. */
bool DoWithEntityByID(UInt32 a_EntityID, cEntityCallback a_Callback) const; // Lua-accessible
diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp
index 86e90aa8b..098a67afd 100644
--- a/src/ClientHandle.cpp
+++ b/src/ClientHandle.cpp
@@ -2637,7 +2637,7 @@ void cClientHandle::SendEntityVelocity(const cEntity & a_Entity)
-void cClientHandle::SendExplosion(const Vector3d a_Pos, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d a_PlayerMotion)
+void cClientHandle::SendExplosion(const Vector3f a_Position, const float a_Power)
{
if (m_NumExplosionsThisTick > MAX_EXPLOSIONS_PER_TICK)
{
@@ -2648,7 +2648,27 @@ void cClientHandle::SendExplosion(const Vector3d a_Pos, float a_Radius, const cV
// Update the statistics:
m_NumExplosionsThisTick++;
- m_Protocol->SendExplosion(a_Pos.x, a_Pos.y, a_Pos.z, a_Radius, a_BlocksAffected, a_PlayerMotion);
+ auto & Random = GetRandomProvider();
+ const auto SoundPitchMultiplier = 1.0f + (Random.RandReal() - Random.RandReal()) * 0.2f;
+
+ // Sound:
+ SendSoundEffect("entity.generic.explode", a_Position, 4.0f, SoundPitchMultiplier * 0.7f);
+
+ const auto ParticleFormula = a_Power * 0.33f;
+ auto Spread = ParticleFormula * 0.5f;
+ auto ParticleCount = std::min(static_cast<int>(ParticleFormula * 125), 600);
+
+ // Dark smoke particles:
+ SendParticleEffect("largesmoke", a_Position.x, a_Position.y, a_Position.z, 0, 0, 0, Spread, static_cast<int>(ParticleCount));
+
+ Spread = ParticleFormula * 0.35f;
+ ParticleCount = std::min(static_cast<int>(ParticleFormula * 550), 1800);
+
+ // Light smoke particles:
+ SendParticleEffect("explode", a_Position.x, a_Position.y, a_Position.z, 0, 0, 0, Spread, static_cast<int>(ParticleCount));
+
+ // Shockwave effect:
+ m_Protocol->SendExplosion(a_Position, a_Power);
}
diff --git a/src/ClientHandle.h b/src/ClientHandle.h
index 755e176dd..cf5139fef 100644
--- a/src/ClientHandle.h
+++ b/src/ClientHandle.h
@@ -175,7 +175,7 @@ public: // tolua_export
void SendEntityVelocity (const cEntity & a_Entity);
void SendExperience (void);
void SendExperienceOrb (const cExpOrb & a_ExpOrb);
- void SendExplosion (const Vector3d a_Pos, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d a_PlayerMotion);
+ void SendExplosion (Vector3f a_Position, float a_Power);
void SendGameMode (eGameMode a_GameMode);
void SendHealth (void);
void SendHeldItemChange (int a_ItemIndex);
diff --git a/src/Entities/Entity.h b/src/Entities/Entity.h
index 8bc941354..627c1ce71 100644
--- a/src/Entities/Entity.h
+++ b/src/Entities/Entity.h
@@ -241,6 +241,7 @@ public:
int GetChunkX(void) const { return FloorC(m_Position.x / cChunkDef::Width); }
int GetChunkZ(void) const { return FloorC(m_Position.z / cChunkDef::Width); }
+ // Get the Entity's axis aligned bounding box, with absolute (world-relative) coordinates.
cBoundingBox GetBoundingBox() const { return cBoundingBox(GetPosition(), GetWidth() / 2, GetHeight()); }
void SetHeadYaw (double a_HeadYaw);
diff --git a/src/Entities/TNTEntity.cpp b/src/Entities/TNTEntity.cpp
index aaf3261b4..6aea6e228 100644
--- a/src/Entities/TNTEntity.cpp
+++ b/src/Entities/TNTEntity.cpp
@@ -8,7 +8,7 @@
-cTNTEntity::cTNTEntity(Vector3d a_Pos, int a_FuseTicks) :
+cTNTEntity::cTNTEntity(Vector3d a_Pos, unsigned a_FuseTicks) :
Super(etTNT, a_Pos, 0.98, 0.98),
m_FuseTicks(a_FuseTicks)
{
@@ -33,10 +33,14 @@ void cTNTEntity::SpawnOn(cClientHandle & a_ClientHandle)
void cTNTEntity::Explode(void)
{
- m_FuseTicks = 0;
- Destroy();
FLOGD("BOOM at {0}", GetPosition());
- m_World->DoExplosionAt(4.0, GetPosX(), GetPosY(), GetPosZ(), true, esPrimedTNT, this);
+
+ // Destroy first so the Explodinator doesn't find us (when iterating through entities):
+ Destroy();
+
+ // TODO: provided centred coordinates to all calls to DoExplosionAt, from entities and blocks
+ // This is to ensure maximum efficiency of explosions
+ m_World->DoExplosionAt(4.0, GetPosX(), GetPosY() + GetHeight() / 2, GetPosZ(), true, esPrimedTNT, this);
}
@@ -51,6 +55,7 @@ void cTNTEntity::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk)
// The base class tick destroyed us
return;
}
+
BroadcastMovementUpdate();
m_FuseTicks -= 1;
diff --git a/src/Entities/TNTEntity.h b/src/Entities/TNTEntity.h
index ef8b8ec3c..5b1c853bd 100644
--- a/src/Entities/TNTEntity.h
+++ b/src/Entities/TNTEntity.h
@@ -19,7 +19,7 @@ public: // tolua_export
CLASS_PROTODEF(cTNTEntity)
- cTNTEntity(Vector3d a_Pos, int a_FuseTicks = 80);
+ cTNTEntity(Vector3d a_Pos, unsigned a_FuseTicks = 80);
// cEntity overrides:
virtual void SpawnOn(cClientHandle & a_ClientHandle) override;
@@ -31,17 +31,13 @@ public: // tolua_export
void Explode(void);
/** Returns the fuse ticks until the tnt will explode */
- int GetFuseTicks(void) const { return m_FuseTicks; }
+ unsigned GetFuseTicks(void) const { return m_FuseTicks; }
/** Set the fuse ticks until the tnt will explode */
- void SetFuseTicks(int a_FuseTicks) { m_FuseTicks = a_FuseTicks; }
+ void SetFuseTicks(unsigned a_FuseTicks) { m_FuseTicks = a_FuseTicks; }
// tolua_end
protected:
int m_FuseTicks; ///< How much ticks is left, while the tnt will explode
}; // tolua_export
-
-
-
-
diff --git a/src/Physics/CMakeLists.txt b/src/Physics/CMakeLists.txt
new file mode 100644
index 000000000..a4c9bcc65
--- /dev/null
+++ b/src/Physics/CMakeLists.txt
@@ -0,0 +1,9 @@
+target_sources(
+ ${CMAKE_PROJECT_NAME} PRIVATE
+
+ Explodinator.cpp
+ # Lightning.cpp
+
+ Explodinator.h
+ # Lightning.h
+)
diff --git a/src/Physics/Explodinator.cpp b/src/Physics/Explodinator.cpp
new file mode 100644
index 000000000..3bfb78611
--- /dev/null
+++ b/src/Physics/Explodinator.cpp
@@ -0,0 +1,295 @@
+
+#include "Globals.h"
+#include "BlockInfo.h"
+#include "Blocks/BlockHandler.h"
+#include "Blocks/ChunkInterface.h"
+#include "Chunk.h"
+#include "ClientHandle.h"
+#include "Entities/FallingBlock.h"
+#include "LineBlockTracer.h"
+#include "Simulator/SandSimulator.h"
+
+
+
+
+
+namespace Explodinator
+{
+ const auto StepUnit = 0.3f;
+ const auto KnockbackFactor = 25U;
+ const auto StepAttenuation = 0.225f;
+ const auto TraceCubeSideLength = 16U;
+ const auto BoundingBoxStepUnit = 0.5f;
+
+ /** Converts an absolute floating-point Position into a Chunk-relative one. */
+ static Vector3f AbsoluteToRelative(const Vector3f a_Position, const cChunkCoords a_ChunkPosition)
+ {
+ return { a_Position.x - a_ChunkPosition.m_ChunkX * cChunkDef::Width, a_Position.y, a_Position.z - a_ChunkPosition.m_ChunkZ * cChunkDef::Width };
+ }
+
+ /** Make a From Chunk-relative Position into a To Chunk-relative position. */
+ static Vector3f RebaseRelativePosition(const cChunkCoords a_From, const cChunkCoords a_To, const Vector3f a_Position)
+ {
+ return
+ {
+ a_Position.x + (a_From.m_ChunkX - a_To.m_ChunkX) * cChunkDef::Width,
+ a_Position.y,
+ a_Position.z + (a_From.m_ChunkZ - a_To.m_ChunkZ) * cChunkDef::Width
+ };
+ }
+
+ /** Calculates the approximate percentage of an Entity's bounding box that is exposed to an explosion centred at Position. */
+ static float CalculateEntityExposure(cChunk & a_Chunk, const cEntity & a_Entity, const Vector3f a_Position, const float a_SquareRadius)
+ {
+ unsigned Unobstructed = 0, Total = 0;
+ const auto Box = a_Entity.GetBoundingBox();
+
+ for (float X = Box.GetMinX(); X < Box.GetMaxX(); X += BoundingBoxStepUnit)
+ {
+ for (float Y = Box.GetMinY(); Y < Box.GetMaxY(); Y += BoundingBoxStepUnit)
+ {
+ for (float Z = Box.GetMinZ(); Z < Box.GetMaxZ(); Z += BoundingBoxStepUnit)
+ {
+ const auto Destination = Vector3f(X, Y, Z);
+ if ((Destination - a_Position).SqrLength() > a_SquareRadius)
+ {
+ // Don't bother with points outside our designated area-of-effect
+ // This is, surprisingly, a massive amount of work saved (~3m to detonate a sphere of 37k TNT before, ~1m after):
+ continue;
+ }
+
+ if (cLineBlockTracer::LineOfSightTrace(*a_Chunk.GetWorld(), a_Position, Destination, cLineBlockTracer::eLineOfSight::losAir))
+ {
+ Unobstructed++;
+ }
+ Total++;
+ }
+ }
+ }
+
+ return static_cast<float>(Unobstructed) / Total;
+ }
+
+ /** Applies distance-based damage and knockback to all entities within the explosion's effect range. */
+ static void DamageEntities(cChunk & a_Chunk, const Vector3f a_Position, const unsigned a_Power)
+ {
+ const auto Radius = a_Power * 2.f;
+ const auto SquareRadius = Radius * Radius;
+
+ a_Chunk.GetWorld()->ForEachEntityInBox({ a_Position, Radius * 2 }, [&a_Chunk, a_Position, a_Power, Radius, SquareRadius](cEntity & Entity)
+ {
+ // Percentage of rays unobstructed.
+ const auto Exposure = CalculateEntityExposure(a_Chunk, Entity, a_Position, SquareRadius);
+ const auto Direction = Entity.GetPosition() - a_Position;
+ const auto Impact = (1 - (static_cast<float>(Direction.Length()) / Radius)) * Exposure;
+
+ // Don't apply damage to other TNT entities and falling blocks, they should be invincible:
+ if (!Entity.IsTNT() && !Entity.IsFallingBlock())
+ {
+ const auto Damage = (Impact * Impact + Impact) * 7 * a_Power + 1;
+ Entity.TakeDamage(dtExplosion, nullptr, FloorC(Damage), 0);
+ }
+
+ // Impact reduced by armour:
+ const auto ReducedImpact = Impact - Impact * Entity.GetEnchantmentBlastKnockbackReduction(); // TODO: call is very expensive, should only apply to Pawns
+ Entity.SetSpeed(Direction.NormalizeCopy() * KnockbackFactor * ReducedImpact);
+
+ // Continue iteration:
+ return false;
+ });
+ }
+
+ /** 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 auto DestroyedMeta = a_Chunk.GetMeta(a_RelativePosition);
+
+ // SetBlock wakes up all simulators for the area, so that water and lava flows and sand falls into the blasted holes
+ // It also is responsible for calling cBlockHandler::OnNeighborChanged to pop off blocks that fail CanBeAt
+ // An explicit call to cBlockHandler::OnBroken handles the destruction of multiblock structures
+ // References at (FS #391, GH #4418):
+ a_Chunk.SetBlock(a_RelativePosition, a_NewBlock, 0);
+
+ cChunkInterface Interface(a_World.GetChunkMap());
+ cBlockInfo::GetHandler(a_DestroyedBlock)->OnBroken(Interface, a_World, a_AbsolutePosition, a_DestroyedBlock, DestroyedMeta);
+ }
+
+ /** Sets the block at the given Position to air, updates surroundings, and spawns pickups, fire, shrapnel according to 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 auto DestroyedBlock = a_Chunk.GetBlock(a_Position);
+ if (DestroyedBlock == E_BLOCK_AIR)
+ {
+ // There's nothing left for us here, but a barren and empty land
+ // Let's go.
+ return;
+ }
+
+ auto & World = *a_Chunk.GetWorld();
+ auto & Random = GetRandomProvider();
+ const auto Absolute = cChunkDef::RelativeToAbsolute(a_Position, a_Chunk.GetPos());
+ if (DestroyedBlock == E_BLOCK_TNT)
+ {
+ // Random fuse between 10 to 30 game ticks.
+ const int FuseTime = Random.RandInt(10, 30);
+
+ // Activate the TNT, with initial velocity and no fuse sound:
+ World.SpawnPrimedTNT(Vector3d(0.5, 0, 0.5) + Absolute, FuseTime, 1, false);
+ }
+ else if (Random.RandBool(1.f / a_Power))
+ {
+ const auto DestroyedMeta = a_Chunk.GetMeta(a_Position);
+ a_Chunk.GetWorld()->SpawnItemPickups(
+ cBlockInfo::GetHandler(DestroyedBlock)->ConvertToPickups(DestroyedMeta, a_Chunk.GetBlockEntityRel(a_Position)),
+ Absolute
+ );
+ }
+ else if (a_Fiery && Random.RandBool(1.f / 3.f)) // 33% chance of starting fires if it can start fires
+ {
+ const auto Below = a_Position.addedY(-1);
+ if ((Below.y >= 0) && cBlockInfo::FullyOccupiesVoxel(a_Chunk.GetBlock(Below)))
+ {
+ // Start a fire:
+ DestroyBlock(World, a_Chunk, Absolute, a_Position, DestroyedBlock, E_BLOCK_FIRE);
+ return;
+ }
+ }
+ else if (const auto Shrapnel = World.GetTNTShrapnelLevel(); (Shrapnel > slNone) && Random.RandBool(0)) // 20% chance of flinging stuff around
+ {
+ // If the block is shrapnel-able, make a falling block entity out of it:
+ if (
+ ((Shrapnel == slAll) && cBlockInfo::FullyOccupiesVoxel(DestroyedBlock)) ||
+ ((Shrapnel == slGravityAffectedOnly) && cSandSimulator::IsAllowedBlock(DestroyedBlock))
+ )
+ {
+ const auto DestroyedMeta = a_Chunk.GetMeta(a_Position);
+ auto FallingBlock = std::make_unique<cFallingBlock>(Vector3d(0.5, 0, 0.5) + Absolute, DestroyedBlock, DestroyedMeta);
+ // TODO: correct velocity FallingBlock->SetSpeedY(40);
+ FallingBlock->Initialize(std::move(FallingBlock), World);
+ }
+ }
+
+ DestroyBlock(World, a_Chunk, Absolute, a_Position, DestroyedBlock, E_BLOCK_AIR);
+ }
+
+ /** 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)
+ {
+ // The current position the ray is at.
+ auto Checkpoint = a_Origin;
+
+ // The total displacement the ray must travel.
+ const auto TraceDisplacement = (a_Destination - a_Origin);
+
+ // The displacement that they ray in one iteration step should travel.
+ const auto Step = TraceDisplacement.NormalizeCopy() * StepUnit;
+
+ // Loop until we've reached the prescribed destination:
+ while (TraceDisplacement > (Checkpoint - a_Origin))
+ {
+ auto Position = Checkpoint.Floor();
+ if (!cChunkDef::IsValidHeight(Position.y))
+ {
+ break;
+ }
+
+ const auto Neighbour = a_Chunk->GetRelNeighborChunkAdjustCoords(Position);
+ if ((Neighbour == nullptr) || !Neighbour->IsValid())
+ {
+ break;
+ }
+
+ a_Intensity -= 0.3f * (0.3f + cBlockInfo::GetExplosionAbsorption(Neighbour->GetBlock(Position)));
+ if (a_Intensity <= 0)
+ {
+ // The ray is exhausted:
+ break;
+ }
+
+ DestroyBlock(*Neighbour, Position, a_Power, a_Fiery);
+
+ // Adjust coordinates to be relative to the neighbour chunk:
+ Checkpoint = RebaseRelativePosition(a_Chunk->GetPos(), Neighbour->GetPos(), Checkpoint);
+ a_Origin = RebaseRelativePosition(a_Chunk->GetPos(), Neighbour->GetPos(), a_Origin);
+ a_Chunk = Neighbour;
+
+ // Increment the simulation, weaken the ray:
+ Checkpoint += Step;
+ a_Intensity -= StepAttenuation;
+ }
+ }
+
+ /** 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 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...
+ const int HalfSide = TraceCubeSideLength / 2;
+
+ // 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
+ // in the YM and YP directions:
+ for (int OffsetX = -HalfSide; OffsetX < HalfSide; OffsetX++)
+ {
+ for (int OffsetZ = -HalfSide; OffsetZ < HalfSide; OffsetZ++)
+ {
+ DestructionTrace(&a_Chunk, a_Position, a_Position + Vector3f(OffsetX, +ExplosionRadius, OffsetZ), a_Power, a_Fiery, Intensity);
+ DestructionTrace(&a_Chunk, a_Position, a_Position + Vector3f(OffsetX, -ExplosionRadius, OffsetZ), a_Power, a_Fiery, Intensity);
+ }
+ }
+
+ /*
+ Trace rays from the centre to the sides of the explosion cube, being careful to avoid duplicates:
+
+ 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++)
+ {
+ DestructionTrace(&a_Chunk, a_Position, a_Position + Vector3f(ExplosionRadius, OffsetY, Offset + 1), a_Power, a_Fiery, Intensity);
+ DestructionTrace(&a_Chunk, a_Position, a_Position + Vector3f(-ExplosionRadius, OffsetY, Offset), a_Power, a_Fiery, Intensity);
+ DestructionTrace(&a_Chunk, a_Position, a_Position + Vector3f(Offset, OffsetY, ExplosionRadius), a_Power, a_Fiery, Intensity);
+ DestructionTrace(&a_Chunk, a_Position, a_Position + Vector3f(Offset + 1, OffsetY, -ExplosionRadius), a_Power, a_Fiery, Intensity);
+ }
+ }
+ }
+
+ /** Sends an explosion packet to all clients in the given chunk. */
+ static void LagTheClient(cChunk & a_Chunk, const Vector3f a_Position, const unsigned a_Power)
+ {
+ for (const auto Client : a_Chunk.GetAllClients())
+ {
+ Client->SendExplosion(a_Position, a_Power);
+ }
+ }
+
+ void Kaboom(cWorld & a_World, const Vector3f a_Position, const unsigned a_Power, const bool a_Fiery)
+ {
+ a_World.DoWithChunkAt(a_Position.Floor(), [a_Position, a_Power, a_Fiery](cChunk & a_Chunk)
+ {
+ LagTheClient(a_Chunk, a_Position, a_Power);
+ DamageEntities(a_Chunk, a_Position, a_Power);
+ DamageBlocks(a_Chunk, AbsoluteToRelative(a_Position, a_Chunk.GetPos()), a_Power, a_Fiery);
+
+ return false;
+ });
+ }
+}
diff --git a/src/Physics/Explodinator.h b/src/Physics/Explodinator.h
new file mode 100644
index 000000000..daf86456a
--- /dev/null
+++ b/src/Physics/Explodinator.h
@@ -0,0 +1,20 @@
+
+#pragma once
+
+
+
+
+
+class cWorld;
+
+
+
+
+
+namespace Explodinator
+{
+ /** Creates an explosion of Power, centred at Position, with ability to set fires as provided.
+ For maximum efficiency, Position should be in the centre of the entity or block that exploded.
+ Kaboom indeed, you drunken wretch. */
+ void Kaboom(cWorld & World, Vector3f Position, unsigned Power, bool Fiery);
+}
diff --git a/src/Protocol/Protocol.h b/src/Protocol/Protocol.h
index 2456ac976..222e6031e 100644
--- a/src/Protocol/Protocol.h
+++ b/src/Protocol/Protocol.h
@@ -374,7 +374,7 @@ public:
virtual void SendEntityProperties (const cEntity & a_Entity) = 0;
virtual void SendEntityStatus (const cEntity & a_Entity, char a_Status) = 0;
virtual void SendEntityVelocity (const cEntity & a_Entity) = 0;
- virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) = 0;
+ virtual void SendExplosion (Vector3f a_Position, float a_Power) = 0;
virtual void SendGameMode (eGameMode a_GameMode) = 0;
virtual void SendHealth (void) = 0;
virtual void SendHeldItemChange (int a_ItemIndex) = 0;
diff --git a/src/Protocol/Protocol_1_8.cpp b/src/Protocol/Protocol_1_8.cpp
index 39c20b6cf..e5ba82111 100644
--- a/src/Protocol/Protocol_1_8.cpp
+++ b/src/Protocol/Protocol_1_8.cpp
@@ -620,25 +620,19 @@ void cProtocol_1_8_0::SendExperienceOrb(const cExpOrb & a_ExpOrb)
-void cProtocol_1_8_0::SendExplosion(double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion)
+void cProtocol_1_8_0::SendExplosion(const Vector3f a_Position, const float a_Power)
{
ASSERT(m_State == 3); // In game mode?
cPacketizer Pkt(*this, pktExplosion);
- Pkt.WriteBEFloat(static_cast<float>(a_BlockX));
- Pkt.WriteBEFloat(static_cast<float>(a_BlockY));
- Pkt.WriteBEFloat(static_cast<float>(a_BlockZ));
- Pkt.WriteBEFloat(static_cast<float>(a_Radius));
- Pkt.WriteBEUInt32(static_cast<UInt32>(a_BlocksAffected.size()));
- for (cVector3iArray::const_iterator itr = a_BlocksAffected.begin(), end = a_BlocksAffected.end(); itr != end; ++itr)
- {
- Pkt.WriteBEInt8(static_cast<Int8>(itr->x));
- Pkt.WriteBEInt8(static_cast<Int8>(itr->y));
- Pkt.WriteBEInt8(static_cast<Int8>(itr->z));
- } // for itr - a_BlockAffected[]
- Pkt.WriteBEFloat(static_cast<float>(a_PlayerMotion.x));
- Pkt.WriteBEFloat(static_cast<float>(a_PlayerMotion.y));
- Pkt.WriteBEFloat(static_cast<float>(a_PlayerMotion.z));
+ Pkt.WriteBEFloat(a_Position.x);
+ Pkt.WriteBEFloat(a_Position.y);
+ Pkt.WriteBEFloat(a_Position.z);
+ Pkt.WriteBEFloat(a_Power);
+ Pkt.WriteBEUInt32(0);
+ Pkt.WriteBEFloat(0);
+ Pkt.WriteBEFloat(0);
+ Pkt.WriteBEFloat(0);
}
diff --git a/src/Protocol/Protocol_1_8.h b/src/Protocol/Protocol_1_8.h
index f48ed4f9c..120676dd8 100644
--- a/src/Protocol/Protocol_1_8.h
+++ b/src/Protocol/Protocol_1_8.h
@@ -64,7 +64,7 @@ public:
virtual void SendEntityVelocity (const cEntity & a_Entity) override;
virtual void SendExperience (void) override;
virtual void SendExperienceOrb (const cExpOrb & a_ExpOrb) override;
- virtual void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) override;
+ virtual void SendExplosion (Vector3f a_Position, float a_Power) override;
virtual void SendGameMode (eGameMode a_GameMode) override;
virtual void SendHealth (void) override;
virtual void SendHeldItemChange (int a_ItemIndex) override;
diff --git a/src/Simulator/IncrementalRedstoneSimulator/RedstoneSimulatorChunkData.h b/src/Simulator/IncrementalRedstoneSimulator/RedstoneSimulatorChunkData.h
index 2d2010d57..02da327a1 100644
--- a/src/Simulator/IncrementalRedstoneSimulator/RedstoneSimulatorChunkData.h
+++ b/src/Simulator/IncrementalRedstoneSimulator/RedstoneSimulatorChunkData.h
@@ -67,8 +67,7 @@ public:
return 0;
}
- std::swap(Result->second, Power);
- return Power;
+ return std::exchange(Result->second, Power);
}
/** Adjust From-relative coordinates into To-relative coordinates. */
diff --git a/src/Simulator/SandSimulator.cpp b/src/Simulator/SandSimulator.cpp
index 95f514cc9..72b1a8378 100644
--- a/src/Simulator/SandSimulator.cpp
+++ b/src/Simulator/SandSimulator.cpp
@@ -74,29 +74,6 @@ void cSandSimulator::SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX,
-bool cSandSimulator::IsAllowedBlock(BLOCKTYPE a_BlockType)
-{
- switch (a_BlockType)
- {
- case E_BLOCK_ANVIL:
- case E_BLOCK_CONCRETE_POWDER:
- case E_BLOCK_DRAGON_EGG:
- case E_BLOCK_GRAVEL:
- case E_BLOCK_SAND:
- {
- return true;
- }
- default:
- {
- return false;
- }
- }
-}
-
-
-
-
-
void cSandSimulator::AddBlock(cChunk & a_Chunk, Vector3i a_Position, BLOCKTYPE a_Block)
{
if (!IsAllowedBlock(a_Block))
@@ -286,6 +263,29 @@ void cSandSimulator::FinishFalling(
+bool cSandSimulator::IsAllowedBlock(BLOCKTYPE a_BlockType)
+{
+ switch (a_BlockType)
+ {
+ case E_BLOCK_ANVIL:
+ case E_BLOCK_CONCRETE_POWDER:
+ case E_BLOCK_DRAGON_EGG:
+ case E_BLOCK_GRAVEL:
+ case E_BLOCK_SAND:
+ {
+ return true;
+ }
+ default:
+ {
+ return false;
+ }
+ }
+}
+
+
+
+
+
void cSandSimulator::DoInstantFall(cChunk * a_Chunk, int a_RelX, int a_RelY, int a_RelZ)
{
// Remove the original block:
diff --git a/src/Simulator/SandSimulator.h b/src/Simulator/SandSimulator.h
index 113484158..e05b0de80 100644
--- a/src/Simulator/SandSimulator.h
+++ b/src/Simulator/SandSimulator.h
@@ -51,13 +51,13 @@ public:
BLOCKTYPE a_FallingBlockType, NIBBLETYPE a_FallingBlockMeta
);
+ static bool IsAllowedBlock(BLOCKTYPE a_BlockType);
+
private:
virtual void Simulate(float a_Dt) override { UNUSED(a_Dt);} // not used
virtual void SimulateChunk(std::chrono::milliseconds a_Dt, int a_ChunkX, int a_ChunkZ, cChunk * a_Chunk) override;
- static bool IsAllowedBlock(BLOCKTYPE a_BlockType);
-
bool m_IsInstantFall; // If set to true, blocks don't fall using cFallingBlock entity, but instantly instead
int m_TotalBlocks; // Total number of blocks currently in the queue for simulating
diff --git a/src/World.cpp b/src/World.cpp
index f5b64f297..d99c9c607 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -4,6 +4,7 @@
#include "World.h"
#include "BlockInfo.h"
#include "ClientHandle.h"
+#include "Physics/Explodinator.h"
#include "Server.h"
#include "Root.h"
#include "IniFile.h"
@@ -1393,47 +1394,13 @@ 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)
{
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))
- {
- return;
- }
-
- // TODO: Implement block hardiness
- cVector3iArray BlocksAffected;
- m_ChunkMap->DoExplosionAt(a_ExplosionSize, a_BlockX, a_BlockY, a_BlockZ, BlocksAffected);
- auto & Random = GetRandomProvider();
- auto SoundPitchMultiplier = 1.0f + (Random.RandReal(1.0f) - Random.RandReal(1.0f)) * 0.2f;
-
- BroadcastSoundEffect("entity.generic.explode", Vector3d(a_BlockX, a_BlockY, a_BlockZ), 4.0f, SoundPitchMultiplier * 0.7f);
-
- Vector3d ExplosionPos(a_BlockX, a_BlockY, a_BlockZ);
- for (auto Player : m_Players)
+ if (!cPluginManager::Get()->CallHookExploding(*this, a_ExplosionSize, a_CanCauseFire, a_BlockX, a_BlockY, a_BlockZ, a_Source, a_SourceData) && (a_ExplosionSize > 0))
{
- cClientHandle * ch = Player->GetClientHandle();
- if (ch == nullptr)
- {
- continue;
- }
-
- bool InRange = (Player->GetExplosionExposureRate(ExplosionPos, static_cast<float>(a_ExplosionSize)) > 0);
- auto Speed = InRange ? Player->GetSpeed() : Vector3d{};
- ch->SendExplosion({a_BlockX, a_BlockY, a_BlockZ}, static_cast<float>(a_ExplosionSize), BlocksAffected, Speed);
+ // TODO: CanCauseFire gets reset to false for some reason
+ Explodinator::Kaboom(*this, Vector3f(a_BlockX, a_BlockY, a_BlockZ), a_ExplosionSize, a_CanCauseFire);
+ cPluginManager::Get()->CallHookExploded(*this, a_ExplosionSize, a_CanCauseFire, a_BlockX, a_BlockY, a_BlockZ, a_Source, a_SourceData);
}
-
- auto Position = Vector3d(a_BlockX, a_BlockY - 0.5f, a_BlockZ);
- auto ParticleFormula = a_ExplosionSize * 0.33f;
- auto Spread = ParticleFormula * 0.5f;
- auto ParticleCount = std::min((ParticleFormula * 125), 600.0);
-
- BroadcastParticleEffect("largesmoke", Position, Vector3f{}, static_cast<float>(Spread), static_cast<int>(ParticleCount));
-
- Spread = ParticleFormula * 0.35f;
- ParticleCount = std::min((ParticleFormula * 550), 1800.0);
-
- BroadcastParticleEffect("explode", Position, Vector3f{}, static_cast<float>(Spread), static_cast<int>(ParticleCount));
-
- cPluginManager::Get()->CallHookExploded(*this, a_ExplosionSize, a_CanCauseFire, a_BlockX, a_BlockY, a_BlockZ, a_Source, a_SourceData);
}
diff --git a/src/WorldStorage/StatSerializer.cpp b/src/WorldStorage/StatSerializer.cpp
index 66be4fc7f..95652a8d7 100644
--- a/src/WorldStorage/StatSerializer.cpp
+++ b/src/WorldStorage/StatSerializer.cpp
@@ -3,7 +3,6 @@
#include "Globals.h"
-#include "StatSerializer.h"
#include "../Statistics.h"
#include "NamespaceSerializer.h"