diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Bindings/ManualBindings.cpp | 30 | ||||
-rw-r--r-- | src/Chunk.cpp | 2 | ||||
-rw-r--r-- | src/Entities/EnderCrystal.cpp | 6 | ||||
-rw-r--r-- | src/Entities/Entity.cpp | 4 | ||||
-rw-r--r-- | src/Entities/Pickup.cpp | 5 | ||||
-rw-r--r-- | src/Entities/Pickup.h | 2 | ||||
-rw-r--r-- | src/Entities/ProjectileEntity.cpp | 6 | ||||
-rw-r--r-- | src/Items/ItemBoat.h | 6 | ||||
-rw-r--r-- | src/Items/ItemBottle.h | 4 | ||||
-rw-r--r-- | src/Items/ItemBucket.h | 14 | ||||
-rw-r--r-- | src/Items/ItemLilypad.h | 6 | ||||
-rw-r--r-- | src/Mobs/AggressiveMonster.cpp | 6 | ||||
-rw-r--r-- | src/Mobs/Enderman.cpp | 4 | ||||
-rw-r--r-- | src/Mobs/Monster.cpp | 6 | ||||
-rw-r--r-- | src/Mobs/Zombie.cpp | 1 | ||||
-rw-r--r-- | src/Mobs/ZombieVillager.cpp | 1 | ||||
-rw-r--r-- | src/Physics/Explodinator.cpp | 159 | ||||
-rw-r--r-- | src/Physics/Tracers/BlockTracer.h | 133 | ||||
-rw-r--r-- | src/Physics/Tracers/LineBlockTracer.cpp | 402 | ||||
-rw-r--r-- | src/Physics/Tracers/LineBlockTracer.h | 78 | ||||
-rw-r--r-- | src/World.cpp | 6 | ||||
-rw-r--r-- | src/WorldStorage/WSSAnvil.cpp | 2 |
22 files changed, 355 insertions, 528 deletions
diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp index f5517dc84..8a4ac8750 100644 --- a/src/Bindings/ManualBindings.cpp +++ b/src/Bindings/ManualBindings.cpp @@ -32,7 +32,7 @@ #include "../Generating/ChunkDesc.h" #include "../HTTP/UrlParser.h" #include "../Item.h" -#include "../LineBlockTracer.h" +#include "../Physics/Tracers/LineBlockTracer.h" #include "../Server.h" #include "../Root.h" #include "../StringCompression.h" @@ -2871,9 +2871,9 @@ tolua_lerror: -/** Provides interface between a Lua table of callbacks and the cBlockTracer::cCallbacks */ +/** Provides interface between a Lua table of callbacks and the BlockTracerCallbacks */ class cLuaBlockTracerCallbacks : - public cBlockTracer::cCallbacks + public BlockTracerCallbacks { public: cLuaBlockTracerCallbacks(cLuaState::cTableRefPtr && a_Callbacks): @@ -2962,7 +2962,7 @@ protected: -/** Provides interface between a Lua table of callbacks and the cBlockTracer::cCallbacks +/** Provides interface between a Lua table of callbacks and the BlockTracerCallbacks This is the deprecated version of cLuaBlockTracerCallback, used when the plugin calls the Trace function with number-based coords. */ class cLuaBlockTracerCallbacksOld : @@ -3093,7 +3093,7 @@ static int tolua_cLineBlockTracer_FirstSolidHitTrace(lua_State * tolua_S) Vector3d hitCoords; Vector3i hitBlockCoords; eBlockFace hitBlockFace; - auto isHit = cLineBlockTracer::FirstSolidHitTrace(*world, Vector3d(startX, startY, startZ), Vector3d(endX, endY, endZ), hitCoords, hitBlockCoords, hitBlockFace); + auto isHit = LineBlockTracer::FirstSolidHitTrace(*world, Vector3d(startX, startY, startZ), Vector3d(endX, endY, endZ), hitCoords, hitBlockCoords, hitBlockFace); L.Push(isHit); if (!isHit) { @@ -3129,7 +3129,7 @@ static int tolua_cLineBlockTracer_FirstSolidHitTrace(lua_State * tolua_S) Vector3d hitCoords; Vector3i hitBlockCoords; eBlockFace hitBlockFace; - auto isHit = cLineBlockTracer::FirstSolidHitTrace(*world, start, end, hitCoords, hitBlockCoords, hitBlockFace); + auto isHit = LineBlockTracer::FirstSolidHitTrace(*world, start, end, hitCoords, hitBlockCoords, hitBlockFace); L.Push(isHit); if (!isHit) { @@ -3197,9 +3197,9 @@ static int tolua_cLineBlockTracer_LineOfSightTrace(lua_State * tolua_S) L.LogStackValues("Values on the stack"); return 0; } - int lineOfSight = cLineBlockTracer::losAir | cLineBlockTracer::losWater; + int lineOfSight = LineBlockTracer::LineOfSight::AirWater; L.GetStackValue(idx + 7, lineOfSight); - L.Push(cLineBlockTracer::LineOfSightTrace(*world, Vector3d(startX, startY, startZ), Vector3d(endX, endY, endZ), lineOfSight)); + L.Push(LineBlockTracer::LineOfSightTrace(*world, Vector3d(startX, startY, startZ), Vector3d(endX, endY, endZ), static_cast<LineBlockTracer::LineOfSight>(lineOfSight))); return 1; } @@ -3225,9 +3225,9 @@ static int tolua_cLineBlockTracer_LineOfSightTrace(lua_State * tolua_S) L.LogStackValues("Values on the stack"); return 0; } - int lineOfSight = cLineBlockTracer::losAirWater; + int lineOfSight = LineBlockTracer::LineOfSight::AirWater; L.GetStackValue(idx + 7, lineOfSight); - L.Push(cLineBlockTracer::LineOfSightTrace(*world, start, end, lineOfSight)); + L.Push(LineBlockTracer::LineOfSightTrace(*world, start, end, static_cast<LineBlockTracer::LineOfSight>(lineOfSight))); return 1; } @@ -3292,7 +3292,7 @@ static int tolua_cLineBlockTracer_Trace(lua_State * tolua_S) L.LogStackTrace(); // Trace: cLuaBlockTracerCallbacksOld tracerCallbacks(std::move(callbacks)); - bool res = cLineBlockTracer::Trace(*world, tracerCallbacks, start, end); + bool res = LineBlockTracer::Trace(*world, tracerCallbacks, start, end); tolua_pushboolean(L, res ? 1 : 0); return 1; } @@ -3311,7 +3311,7 @@ static int tolua_cLineBlockTracer_Trace(lua_State * tolua_S) } // Trace: cLuaBlockTracerCallbacks tracerCallbacks(std::move(callbacks)); - bool res = cLineBlockTracer::Trace(*world, tracerCallbacks, start, end); + bool res = LineBlockTracer::Trace(*world, tracerCallbacks, start, end); tolua_pushboolean(L, res ? 1 : 0); return 1; } @@ -4640,9 +4640,9 @@ void cManualBindings::Bind(lua_State * tolua_S) tolua_function(tolua_S, "LineOfSightTrace", tolua_cLineBlockTracer_LineOfSightTrace); tolua_function(tolua_S, "Trace", tolua_cLineBlockTracer_Trace); - tolua_constant(tolua_S, "losAir", cLineBlockTracer::losAir); - tolua_constant(tolua_S, "losWater", cLineBlockTracer::losWater); - tolua_constant(tolua_S, "losLava", cLineBlockTracer::losLava); + tolua_constant(tolua_S, "losAir", LineBlockTracer::LineOfSight::Air); + tolua_constant(tolua_S, "losWater", LineBlockTracer::LineOfSight::Water); + tolua_constant(tolua_S, "losLava", LineBlockTracer::LineOfSight::Lava); tolua_endmodule(tolua_S); tolua_beginmodule(tolua_S, "cLuaWindow"); diff --git a/src/Chunk.cpp b/src/Chunk.cpp index 4bf4557d9..746a8657a 100644 --- a/src/Chunk.cpp +++ b/src/Chunk.cpp @@ -785,7 +785,7 @@ void cChunk::MoveEntityToNewChunk(OwnedEntity a_Entity) cChunk * Neighbor = GetNeighborChunk(a_Entity->GetChunkX() * cChunkDef::Width, a_Entity->GetChunkZ() * cChunkDef::Width); if (Neighbor == nullptr) { - LOGWARNING("%s: Entity at %p (%s, ID %d) moving to a non-existent chunk.", + LOGD("%s: Entity at %p (%s, ID %d) moving to a non-existent chunk", __FUNCTION__, static_cast<void *>(a_Entity.get()), a_Entity->GetClass(), a_Entity->GetUniqueID() ); diff --git a/src/Entities/EnderCrystal.cpp b/src/Entities/EnderCrystal.cpp index c356caa70..d39da5a74 100644 --- a/src/Entities/EnderCrystal.cpp +++ b/src/Entities/EnderCrystal.cpp @@ -45,7 +45,11 @@ void cEnderCrystal::SetShowBottom(bool a_ShowBottom) void cEnderCrystal::SetBeamTarget(Vector3i a_BeamTarget) { m_BeamTarget = a_BeamTarget; - m_World->BroadcastEntityMetadata(*this); + + if (m_DisplayBeam) + { + m_World->BroadcastEntityMetadata(*this); + } } diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp index 0718da06c..59930a4f1 100644 --- a/src/Entities/Entity.cpp +++ b/src/Entities/Entity.cpp @@ -11,7 +11,7 @@ #include "../Chunk.h" #include "../Simulator/FluidSimulator.h" #include "../Bindings/PluginManager.h" -#include "../LineBlockTracer.h" +#include "../Physics/Tracers/LineBlockTracer.h" #include "../Items/ItemHandler.h" #include "../FastRandom.h" #include "../NetherPortalScanner.h" @@ -1157,7 +1157,7 @@ void cEntity::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) Vector3i HitBlockCoords; eBlockFace HitBlockFace; Vector3d wantNextPos = NextPos + NextSpeed * DtSec.count(); - auto isHit = cLineBlockTracer::FirstSolidHitTrace(*GetWorld(), NextPos, wantNextPos, HitCoords, HitBlockCoords, HitBlockFace); + auto isHit = LineBlockTracer::FirstSolidHitTrace(*GetWorld(), NextPos, wantNextPos, HitCoords, HitBlockCoords, HitBlockFace); if (isHit) { // Set our position to where the block was hit: diff --git a/src/Entities/Pickup.cpp b/src/Entities/Pickup.cpp index 699bd5944..9c5bcc6b8 100644 --- a/src/Entities/Pickup.cpp +++ b/src/Entities/Pickup.cpp @@ -116,12 +116,13 @@ private: //////////////////////////////////////////////////////////////////////////////// // cPickup: -cPickup::cPickup(Vector3d a_Position, cItem && a_Item, Vector3d a_Speed, cTickTime a_CollectionDelay, cTickTime a_Lifetime) : +cPickup::cPickup(Vector3d a_Position, cItem && a_Item, Vector3d a_Speed, cTickTime a_CollectionDelay, cTickTime a_Lifetime, bool a_CanCombine) : Super(etPickup, a_Position, 0.25f, 0.25f), m_Item(std::move(a_Item)), m_RemainingCollectionDelay(a_CollectionDelay), m_RemainingLifetime(a_Lifetime), - m_IsCollected(false) + m_IsCollected(false), + m_IsCombinable(a_CanCombine) { SetGravity(-16.0f); SetAirDrag(0.02f); diff --git a/src/Entities/Pickup.h b/src/Entities/Pickup.h index b03ae0846..e8184e8af 100644 --- a/src/Entities/Pickup.h +++ b/src/Entities/Pickup.h @@ -26,7 +26,7 @@ public: // tolua_export CLASS_PROTODEF(cPickup) - cPickup(Vector3d a_Position, cItem && a_Item, Vector3d a_Speed, cTickTime a_CollectionDelay, cTickTime a_Lifetime); + cPickup(Vector3d a_Position, cItem && a_Item, Vector3d a_Speed, cTickTime a_CollectionDelay, cTickTime a_Lifetime, bool a_CanCombine); cItem & GetItem(void) {return m_Item; } // tolua_export const cItem & GetItem(void) const {return m_Item; } diff --git a/src/Entities/ProjectileEntity.cpp b/src/Entities/ProjectileEntity.cpp index ea59c9c46..03c9c72d8 100644 --- a/src/Entities/ProjectileEntity.cpp +++ b/src/Entities/ProjectileEntity.cpp @@ -9,7 +9,7 @@ #include "ProjectileEntity.h" #include "../BlockInfo.h" #include "../ClientHandle.h" -#include "../LineBlockTracer.h" +#include "../Physics/Tracers/LineBlockTracer.h" #include "../BoundingBox.h" #include "../ChunkMap.h" #include "../Chunk.h" @@ -34,7 +34,7 @@ // cProjectileTracerCallback: class cProjectileTracerCallback : - public cBlockTracer::cCallbacks + public BlockTracerCallbacks { public: cProjectileTracerCallback(cProjectileEntity * a_Projectile) : @@ -411,7 +411,7 @@ void cProjectileEntity::HandlePhysics(std::chrono::milliseconds a_Dt, cChunk & a // Trace the tick's worth of movement as a line: cProjectileTracerCallback TracerCallback(this); - if (!cLineBlockTracer::Trace(*m_World, TracerCallback, Pos, NextPos)) + if (!LineBlockTracer::Trace(*m_World, TracerCallback, Pos, NextPos)) { // Something has been hit, abort all other processing return; diff --git a/src/Items/ItemBoat.h b/src/Items/ItemBoat.h index 4fc041787..62d6f546e 100644 --- a/src/Items/ItemBoat.h +++ b/src/Items/ItemBoat.h @@ -2,7 +2,7 @@ #pragma once #include "../Entities/Boat.h" -#include "../LineBlockTracer.h" +#include "../Physics/Tracers/LineBlockTracer.h" @@ -38,7 +38,7 @@ public: // Find the actual placement position by tracing line of sight until non-air block: class cCallbacks: - public cBlockTracer::cCallbacks + public BlockTracerCallbacks { public: Vector3d m_Pos; @@ -62,7 +62,7 @@ public: } Callbacks; auto Start = a_Player->GetEyePosition() + a_Player->GetLookVector(); auto End = a_Player->GetEyePosition() + a_Player->GetLookVector() * 5; - cLineBlockTracer::Trace(*a_World, Callbacks, Start, End); + LineBlockTracer::Trace(*a_World, Callbacks, Start, End); if (!Callbacks.m_HasFound) { return false; diff --git a/src/Items/ItemBottle.h b/src/Items/ItemBottle.h index a324eb101..748dc0860 100644 --- a/src/Items/ItemBottle.h +++ b/src/Items/ItemBottle.h @@ -28,7 +28,7 @@ public: bool GetBlockFromTrace(cWorld * a_World, cPlayer * a_Player, Vector3i & a_BlockPos) const { class cCallbacks: - public cBlockTracer::cCallbacks + public BlockTracerCallbacks { public: Vector3i m_Pos; @@ -57,7 +57,7 @@ public: } Callbacks; auto Start = a_Player->GetEyePosition() + a_Player->GetLookVector(); auto End = a_Player->GetEyePosition() + a_Player->GetLookVector() * 5; - cLineBlockTracer::Trace(*a_World, Callbacks, Start, End); + LineBlockTracer::Trace(*a_World, Callbacks, Start, End); if (!Callbacks.m_HasHitFluid) { return false; diff --git a/src/Items/ItemBucket.h b/src/Items/ItemBucket.h index e7fa0fe38..4403342f8 100644 --- a/src/Items/ItemBucket.h +++ b/src/Items/ItemBucket.h @@ -6,7 +6,7 @@ #include "../World.h" #include "../Simulator/FluidSimulator.h" #include "../Blocks/BlockHandler.h" -#include "../LineBlockTracer.h" +#include "../Physics/Tracers/LineBlockTracer.h" #include "../Blocks/ChunkInterface.h" @@ -185,7 +185,7 @@ public: bool GetBlockFromTrace(cWorld * a_World, cPlayer * a_Player, Vector3i & a_BlockPos) const { class cCallbacks : - public cBlockTracer::cCallbacks + public BlockTracerCallbacks { public: Vector3i m_Pos; @@ -213,11 +213,10 @@ public: } } Callbacks; - cLineBlockTracer Tracer(*a_World, Callbacks); Vector3d Start(a_Player->GetEyePosition() + a_Player->GetLookVector()); Vector3d End(a_Player->GetEyePosition() + a_Player->GetLookVector() * 5); - Tracer.Trace(Start, End); + LineBlockTracer::Trace(*a_World, Callbacks, Start, End); if (!Callbacks.m_HasHitFluid) { @@ -236,7 +235,7 @@ public: bool GetPlacementCoordsFromTrace(cWorld * a_World, cPlayer * a_Player, Vector3i & a_BlockPos, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta, eBlockFace & a_BlockFace) const { class cCallbacks : - public cBlockTracer::cCallbacks + public BlockTracerCallbacks { public: Vector3i m_Pos; @@ -262,14 +261,13 @@ public: } } Callbacks; - cLineBlockTracer Tracer(*a_World, Callbacks); Vector3d Start(a_Player->GetEyePosition()); Vector3d End(a_Player->GetEyePosition() + a_Player->GetLookVector() * 5); - // cLineBlockTracer::Trace() returns true when whole line was traversed. By returning true from the callback when we hit something, + // LineBlockTracer::Trace() returns true when whole line was traversed. By returning true from the callback when we hit something, // we ensure that this never happens if liquid could be placed // Use this to judge whether the position is valid - if (!Tracer.Trace(Start, End)) + if (!LineBlockTracer::Trace(*a_World, Callbacks, Start, End)) { a_BlockPos = Callbacks.m_Pos; a_BlockType = Callbacks.m_ReplacedBlockType; diff --git a/src/Items/ItemLilypad.h b/src/Items/ItemLilypad.h index f13212b07..10bc5f75a 100644 --- a/src/Items/ItemLilypad.h +++ b/src/Items/ItemLilypad.h @@ -3,7 +3,7 @@ #include "ItemHandler.h" #include "../Entities/Player.h" -#include "../LineBlockTracer.h" +#include "../Physics/Tracers/LineBlockTracer.h" @@ -89,7 +89,7 @@ public: } class cCallbacks: - public cBlockTracer::cCallbacks + public BlockTracerCallbacks { public: @@ -118,7 +118,7 @@ public: const auto EyePosition = a_Player->GetEyePosition(); const auto End = EyePosition + a_Player->GetLookVector() * 5; - if (cLineBlockTracer::Trace(*a_Player->GetWorld(), Callbacks, EyePosition, End)) + if (LineBlockTracer::Trace(*a_Player->GetWorld(), Callbacks, EyePosition, End)) { // The line traced to completion; no suitable water was found: return false; diff --git a/src/Mobs/AggressiveMonster.cpp b/src/Mobs/AggressiveMonster.cpp index f7392d92e..a2a9274c8 100644 --- a/src/Mobs/AggressiveMonster.cpp +++ b/src/Mobs/AggressiveMonster.cpp @@ -5,7 +5,7 @@ #include "../World.h" #include "../Entities/Player.h" -#include "../LineBlockTracer.h" +#include "../Physics/Tracers/LineBlockTracer.h" @@ -68,11 +68,11 @@ void cAggressiveMonster::Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) if ( (GetTarget() != nullptr) && TargetIsInRange() && - cLineBlockTracer::LineOfSightTrace( + LineBlockTracer::LineOfSightTrace( *GetWorld(), GetPosition().addedY(GetHeight()), GetTarget()->GetPosition().addedY(GetTarget()->GetHeight()), - (IsNetherNative() ? cLineBlockTracer::losAirWaterLava : cLineBlockTracer::losAirWater) + (IsNetherNative() ? LineBlockTracer::LineOfSight::AirWaterLava : LineBlockTracer::LineOfSight::AirWater) ) && (GetHealth() > 0.0) ) diff --git a/src/Mobs/Enderman.cpp b/src/Mobs/Enderman.cpp index 656668fb3..66e23b8db 100644 --- a/src/Mobs/Enderman.cpp +++ b/src/Mobs/Enderman.cpp @@ -4,7 +4,7 @@ #include "Chunk.h" #include "Enderman.h" #include "../Entities/Player.h" -#include "../LineBlockTracer.h" +#include "../Physics/Tracers/LineBlockTracer.h" @@ -55,7 +55,7 @@ public: } // TODO: Check if endermen are angered through water in Vanilla - if (!cLineBlockTracer::LineOfSightTrace(*a_Player.GetWorld(), m_EndermanHeadPosition, PlayerHeadPosition, cLineBlockTracer::losAirWater)) + if (!LineBlockTracer::LineOfSightTrace(*a_Player.GetWorld(), m_EndermanHeadPosition, PlayerHeadPosition, LineBlockTracer::LineOfSight::AirWater)) { // No direct line of sight return false; diff --git a/src/Mobs/Monster.cpp b/src/Mobs/Monster.cpp index 8e0d3a179..54b7319a4 100644 --- a/src/Mobs/Monster.cpp +++ b/src/Mobs/Monster.cpp @@ -2,7 +2,7 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "IncludeAllMonsters.h" -#include "LineBlockTracer.h" +#include "Physics/Tracers/LineBlockTracer.h" #include "../BlockInfo.h" #include "../Root.h" #include "../Server.h" @@ -754,7 +754,7 @@ void cMonster::CheckEventSeePlayer(cChunk & a_Chunk) // TODO: Currently all mobs see through lava, but only Nether-native mobs should be able to. if ( (TargetDistance < ClosestDistance) && - cLineBlockTracer::LineOfSightTrace(*GetWorld(), MyHeadPosition, TargetHeadPosition, cLineBlockTracer::losAirWaterLava) + LineBlockTracer::LineOfSightTrace(*GetWorld(), MyHeadPosition, TargetHeadPosition, LineBlockTracer::LineOfSight::AirWaterLava) ) { TargetPlayer = &a_Player; @@ -800,7 +800,7 @@ void cMonster::CheckEventLostPlayer(const std::chrono::milliseconds a_Dt) const auto MyHeadPosition = GetPosition().addedY(GetHeight()); const auto TargetHeadPosition = Target->GetPosition().addedY(Target->GetHeight()); - if (!cLineBlockTracer::LineOfSightTrace(*GetWorld(), MyHeadPosition, TargetHeadPosition, cLineBlockTracer::losAirWaterLava)) + if (!LineBlockTracer::LineOfSightTrace(*GetWorld(), MyHeadPosition, TargetHeadPosition, LineBlockTracer::LineOfSight::AirWaterLava)) { if ((m_LoseSightAbandonTargetTimer += a_Dt) > std::chrono::seconds(4)) { diff --git a/src/Mobs/Zombie.cpp b/src/Mobs/Zombie.cpp index 09e39a14b..e517b338b 100644 --- a/src/Mobs/Zombie.cpp +++ b/src/Mobs/Zombie.cpp @@ -3,7 +3,6 @@ #include "Zombie.h" #include "../World.h" -#include "../LineBlockTracer.h" diff --git a/src/Mobs/ZombieVillager.cpp b/src/Mobs/ZombieVillager.cpp index cde099947..d91619cd4 100644 --- a/src/Mobs/ZombieVillager.cpp +++ b/src/Mobs/ZombieVillager.cpp @@ -3,7 +3,6 @@ #include "ZombieVillager.h" #include "../World.h" -#include "../LineBlockTracer.h" #include "../Entities/Player.h" diff --git a/src/Physics/Explodinator.cpp b/src/Physics/Explodinator.cpp index ba1a8cbcf..058078cdc 100644 --- a/src/Physics/Explodinator.cpp +++ b/src/Physics/Explodinator.cpp @@ -7,7 +7,7 @@ #include "Chunk.h" #include "ClientHandle.h" #include "Entities/FallingBlock.h" -#include "LineBlockTracer.h" +#include "Physics/Tracers/LineBlockTracer.h" #include "Simulator/SandSimulator.h" @@ -22,27 +22,10 @@ namespace Explodinator static const auto TraceCubeSideLength = 16U; static const auto BoundingBoxStepUnit = 0.5; - /** 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 - }; - } - /** Returns how much of an explosion Destruction Lazor's (tm) intensity the given block attenuates. Values are scaled as 0.3 * (0.3 + Wiki) since some compilers miss the constant folding optimisation. Wiki values are https://minecraft.gamepedia.com/Explosion#Blast_resistance as of 2021-02-06. */ - static float GetExplosionAbsorption(const BLOCKTYPE Block) + static constexpr float GetExplosionAbsorption(const BLOCKTYPE Block) { switch (Block) { @@ -162,7 +145,7 @@ namespace Explodinator /** Calculates the approximate percentage of an Entity's bounding box that is exposed to an explosion centred at Position. */ static float CalculateEntityExposure(const cChunk & a_Chunk, const cEntity & a_Entity, const Vector3f a_Position, const int a_SquareRadius) { - class LineOfSightCallbacks final : public cLineBlockTracer::cCallbacks + class LineOfSightCallbacks final : public BlockTracerCallbacks { virtual bool OnNextBlock(Vector3i a_BlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, eBlockFace a_EntryFace) override { @@ -173,7 +156,6 @@ namespace Explodinator const Vector3d Position = a_Position; unsigned Unobstructed = 0, Total = 0; const auto Box = a_Entity.GetBoundingBox(); - cLineBlockTracer Tracer(*a_Chunk.GetWorld(), Callback); for (double X = Box.GetMinX(); X < Box.GetMaxX(); X += BoundingBoxStepUnit) { @@ -182,6 +164,7 @@ namespace Explodinator for (double Z = Box.GetMinZ(); Z < Box.GetMaxZ(); Z += BoundingBoxStepUnit) { const Vector3d Destination{X, Y, Z}; + if ((Destination - Position).SqrLength() > a_SquareRadius) { // Don't bother with points outside our designated area-of-effect @@ -189,7 +172,7 @@ namespace Explodinator continue; } - if (Tracer.Trace(a_Position, Destination)) + if (LineBlockTracer::Trace(a_Chunk, Callback, Position, Destination)) { Unobstructed++; } @@ -257,134 +240,146 @@ namespace Explodinator } /** Sets the block at the given position, updating surroundings. */ - 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) + static void SetBlock(cChunk & a_Chunk, const Vector3i a_AbsolutePosition, const Vector3i a_RelativePosition, const BLOCKTYPE a_CurrentBlock, const NIBBLETYPE a_CurrentMeta, const BLOCKTYPE a_NewBlock, const cEntity * const a_ExplodingEntity) { - 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()); - cBlockHandler::For(a_DestroyedBlock).OnBroken(Interface, a_World, a_AbsolutePosition, a_DestroyedBlock, DestroyedMeta, a_ExplodingEntity); + auto & World = *a_Chunk.GetWorld(); + cChunkInterface Interface(World.GetChunkMap()); + cBlockHandler::For(a_CurrentBlock).OnBroken(Interface, World, a_AbsolutePosition, a_CurrentBlock, a_CurrentMeta, a_ExplodingEntity); } /** 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. */ - static void DestroyBlock(cChunk & a_Chunk, const Vector3i a_Position, const int a_Power, const bool a_Fiery, const cEntity * const a_ExplodingEntity) + static void DestroyBlock(MTRand & a_Random, cChunk & a_Chunk, const Vector3i a_AbsolutePosition, const Vector3i a_RelativePosition, const BLOCKTYPE a_CurrentBlock, const NIBBLETYPE a_CurrentMeta, const cBoundingBox a_ExplosionBounds, const int a_Power, const bool a_Fiery, const cEntity * const a_ExplodingEntity) { - 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) // If the block is TNT we should set it off + if (a_CurrentBlock == E_BLOCK_TNT) // If the block is TNT we should set it off { // Random fuse between 10 to 30 game ticks. - const int FuseTime = Random.RandInt(10, 30); + const int FuseTime = a_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); + World.SpawnPrimedTNT(Vector3d(0.5, 0, 0.5) + a_AbsolutePosition, FuseTime, 1, false); } - else if ((a_ExplodingEntity != nullptr) && (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 + else if ((a_ExplodingEntity != nullptr) && (a_ExplodingEntity->IsTNT() || BlockAlwaysDrops(a_CurrentBlock) || a_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); - a_Chunk.GetWorld()->SpawnItemPickups(cBlockHandler::For(DestroyedBlock).ConvertToPickups(DestroyedMeta), Absolute); + for (auto & Item : cBlockHandler::For(a_CurrentBlock).ConvertToPickups(a_CurrentMeta)) + { + World.SpawnItemPickup(Vector3d(0.5, 0, 0.5) + a_AbsolutePosition, std::move(Item), Vector3d(), a_ExplosionBounds); + } } - else if (a_Fiery && Random.RandBool(1 / 3.0)) // 33% chance of starting fires if it can start fires + else if (a_Fiery && a_Random.RandBool(1 / 3.0)) // 33% chance of starting fires if it can start fires { - const auto Below = a_Position.addedY(-1); + const auto Below = a_AbsolutePosition.addedY(-1); if ((Below.y >= 0) && cBlockInfo::FullyOccupiesVoxel(a_Chunk.GetBlock(Below))) { // Start a fire: - SetBlock(World, a_Chunk, Absolute, a_Position, DestroyedBlock, E_BLOCK_FIRE, a_ExplodingEntity); + SetBlock(a_Chunk, a_AbsolutePosition, a_RelativePosition, a_CurrentBlock, a_CurrentMeta, E_BLOCK_FIRE, a_ExplodingEntity); return; } } - else if (const auto Shrapnel = World.GetTNTShrapnelLevel(); (Shrapnel > slNone) && Random.RandBool(0)) // Currently 0% chance of flinging stuff around + else if (const auto Shrapnel = World.GetTNTShrapnelLevel(); (Shrapnel > slNone) && a_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 ( - ((Shrapnel == slAll) && cBlockInfo::FullyOccupiesVoxel(DestroyedBlock)) || - ((Shrapnel == slGravityAffectedOnly) && cSandSimulator::IsAllowedBlock(DestroyedBlock)) + ((Shrapnel == slAll) && cBlockInfo::FullyOccupiesVoxel(a_CurrentBlock)) || + ((Shrapnel == slGravityAffectedOnly) && cSandSimulator::IsAllowedBlock(a_CurrentBlock)) ) { - const auto DestroyedMeta = a_Chunk.GetMeta(a_Position); - auto FallingBlock = std::make_unique<cFallingBlock>(Vector3d(0.5, 0, 0.5) + Absolute, DestroyedBlock, DestroyedMeta); + auto FallingBlock = std::make_unique<cFallingBlock>(Vector3d(0.5, 0, 0.5) + a_AbsolutePosition, a_CurrentBlock, a_CurrentMeta); // TODO: correct velocity FallingBlock->SetSpeedY(40); FallingBlock->Initialize(std::move(FallingBlock), World); } } - SetBlock(World, a_Chunk, Absolute, a_Position, DestroyedBlock, E_BLOCK_AIR, a_ExplodingEntity); + SetBlock(a_Chunk, a_AbsolutePosition, a_RelativePosition, a_CurrentBlock, a_CurrentMeta, 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. */ - static void DestructionTrace(cChunk * a_Chunk, Vector3f a_Origin, const Vector3f a_Direction, const int a_Power, const bool a_Fiery, float a_Intensity, const cEntity * const a_ExplodingEntity) + /** Returns a random intensity for an Explosion Lazor (tm) as a function of the explosion's power. */ + static float RandomIntensity(MTRand & a_Random, const int a_Power) + { + return a_Power * (0.7f + a_Random.RandReal(0.6f)); + } + + /** Traces the path taken by one Explosion Lazor (tm) with given direction and random intensity, that will destroy blocks until it is exhausted. */ + static void DestructionTrace(MTRand & a_Random, cChunk * a_Chunk, Vector3f a_Origin, const Vector3f a_Direction, const cBoundingBox a_ExplosionBounds, const int a_Power, const bool a_Fiery, const cEntity * const a_ExplodingEntity) { // The current position the ray is at. auto Checkpoint = a_Origin; + auto Position = Checkpoint.Floor(); + + auto Intensity = RandomIntensity(a_Random, a_Power); + // The displacement that the ray in one iteration step should travel. const auto Step = a_Direction.NormalizeCopy() * StepUnit; // Loop until intensity runs out: - while (a_Intensity > 0) + while (Intensity > 0) { - auto Position = Checkpoint.Floor(); if (!cChunkDef::IsValidHeight(Position)) { break; } - const auto Neighbour = a_Chunk->GetRelNeighborChunkAdjustCoords(Position); - if ((Neighbour == nullptr) || !Neighbour->IsValid()) + Vector3i RelativePosition; + + if (!a_Chunk->GetChunkAndRelByAbsolute(Position, &a_Chunk, RelativePosition)) { break; } - a_Intensity -= GetExplosionAbsorption(Neighbour->GetBlock(Position)); - if (a_Intensity <= 0) + BLOCKTYPE CurrentBlock; + NIBBLETYPE CurrentMeta; + a_Chunk->GetBlockTypeMeta(RelativePosition, CurrentBlock, CurrentMeta); + + Intensity -= GetExplosionAbsorption(CurrentBlock); + if (Intensity <= 0) { // The ray is exhausted: break; } - DestroyBlock(*Neighbour, Position, a_Power, a_Fiery, a_ExplodingEntity); - - // 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; + if (CurrentBlock != E_BLOCK_AIR) + { + DestroyBlock(a_Random, *a_Chunk, Position, RelativePosition, CurrentBlock, CurrentMeta, a_ExplosionBounds, a_Power, a_Fiery, a_ExplodingEntity); + } // Increment the simulation, weaken the ray: Checkpoint += Step; - a_Intensity -= StepAttenuation; - } - } + Intensity -= StepAttenuation; - /** Returns a random intensity for an Explosion Lazor (tm) as a function of the explosion's power. */ - static float RandomIntensity(MTRand & a_Random, const int a_Power) - { - return a_Power * (0.7f + a_Random.RandReal(0.6f)); + for (int i = 0; i != 2; i++) + { + const auto PreviousPosition = Position; + Position = Checkpoint.Floor(); + + if (Position != PreviousPosition) + { + break; + } + + Checkpoint += Step; + Intensity -= StepAttenuation + GetExplosionAbsorption(E_BLOCK_AIR); + } + } } /** Sends out Explosion Lazors (tm) originating from the given position that destroy blocks. */ static void DamageBlocks(cChunk & a_Chunk, const Vector3f a_Position, const int a_Power, const bool a_Fiery, const cEntity * const a_ExplodingEntity) { // 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; + auto & Random = GetRandomProvider(); + const int HalfSide = TraceCubeSideLength / 2; + const cBoundingBox ExplosionBounds(a_Position, TraceCubeSideLength); // The following loops implement the tracing algorithm described in http://minecraft.gamepedia.com/Explosion @@ -394,8 +389,8 @@ namespace Explodinator { for (float OffsetZ = -HalfSide; OffsetZ < HalfSide; OffsetZ++) { - DestructionTrace(&a_Chunk, a_Position, Vector3f(OffsetX, +HalfSide, OffsetZ), a_Power, a_Fiery, RandomIntensity(Random, a_Power), a_ExplodingEntity); - DestructionTrace(&a_Chunk, a_Position, Vector3f(OffsetX, -HalfSide, OffsetZ), a_Power, a_Fiery, RandomIntensity(Random, a_Power), a_ExplodingEntity); + DestructionTrace(Random, &a_Chunk, a_Position, Vector3f(OffsetX, +HalfSide, OffsetZ), ExplosionBounds, a_Power, a_Fiery, a_ExplodingEntity); + DestructionTrace(Random, &a_Chunk, a_Position, Vector3f(OffsetX, -HalfSide, OffsetZ), ExplosionBounds, a_Power, a_Fiery, a_ExplodingEntity); } } @@ -404,8 +399,8 @@ namespace Explodinator { for (float OffsetY = -HalfSide + 1; OffsetY < HalfSide - 1; OffsetY++) { - DestructionTrace(&a_Chunk, a_Position, Vector3f(OffsetX, OffsetY, +HalfSide), a_Power, a_Fiery, RandomIntensity(Random, a_Power), a_ExplodingEntity); - DestructionTrace(&a_Chunk, a_Position, Vector3f(OffsetX, OffsetY, -HalfSide), a_Power, a_Fiery, RandomIntensity(Random, a_Power), a_ExplodingEntity); + DestructionTrace(Random, &a_Chunk, a_Position, Vector3f(OffsetX, OffsetY, +HalfSide), ExplosionBounds, a_Power, a_Fiery, a_ExplodingEntity); + DestructionTrace(Random, &a_Chunk, a_Position, Vector3f(OffsetX, OffsetY, -HalfSide), ExplosionBounds, a_Power, a_Fiery, a_ExplodingEntity); } } @@ -414,8 +409,8 @@ namespace Explodinator { for (float OffsetY = -HalfSide + 1; OffsetY < HalfSide - 1; OffsetY++) { - DestructionTrace(&a_Chunk, a_Position, Vector3f(+HalfSide, OffsetY, OffsetZ), a_Power, a_Fiery, RandomIntensity(Random, a_Power), a_ExplodingEntity); - DestructionTrace(&a_Chunk, a_Position, Vector3f(-HalfSide, OffsetY, OffsetZ), a_Power, a_Fiery, RandomIntensity(Random, a_Power), a_ExplodingEntity); + DestructionTrace(Random, &a_Chunk, a_Position, Vector3f(+HalfSide, OffsetY, OffsetZ), ExplosionBounds, a_Power, a_Fiery, a_ExplodingEntity); + DestructionTrace(Random, &a_Chunk, a_Position, Vector3f(-HalfSide, OffsetY, OffsetZ), ExplosionBounds, a_Power, a_Fiery, a_ExplodingEntity); } } } @@ -435,7 +430,7 @@ namespace Explodinator { 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, a_ExplodingEntity); + DamageBlocks(a_Chunk, a_Position, a_Power, a_Fiery, a_ExplodingEntity); return false; }); diff --git a/src/Physics/Tracers/BlockTracer.h b/src/Physics/Tracers/BlockTracer.h index 62ae1636e..e32ef3b21 100644 --- a/src/Physics/Tracers/BlockTracer.h +++ b/src/Physics/Tracers/BlockTracer.h @@ -1,7 +1,7 @@ // BlockTracer.h -// Declares the classes common for all blocktracers +// Declares the callback common to all blocktracers @@ -9,118 +9,61 @@ #pragma once - - - - #include "Defines.h" #include "ChunkDef.h" -// fwd: World.h -class cWorld; - +/** The callback class is used to notify the caller of individual events that are being traced. */ +class BlockTracerCallbacks abstract +{ +public: + // Force a virtual destructor in descendants: + virtual ~BlockTracerCallbacks() {} + /** Called on each block encountered along the path, including the first block (path start) + When this callback returns true, the tracing is aborted. */ + virtual bool OnNextBlock(Vector3i a_BlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, eBlockFace a_EntryFace) = 0; -class cBlockTracer abstract -{ -public: - /** The callback class is used to notify the caller of individual events that are being traced. - */ - class cCallbacks abstract - { - public: - // Force a virtual destructor in descendants: - virtual ~cCallbacks() {} - - /** Called on each block encountered along the path, including the first block (path start) - When this callback returns true, the tracing is aborted. - */ - virtual bool OnNextBlock(Vector3i a_BlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, eBlockFace a_EntryFace) = 0; - - /** Called on each block encountered along the path, including the first block (path start), if chunk data is not loaded - When this callback returns true, the tracing is aborted. - */ - virtual bool OnNextBlockNoData(Vector3i a_BlockPos, eBlockFace a_EntryFace) - { - UNUSED(a_BlockPos); - UNUSED(a_EntryFace); - return false; - } - - /** Called when the path goes out of world, either below (a_BlockPos.y < 0) or above (a_BlockPos.y >= cChunkDef::Height) - The coords specify the exact point at which the path exited the world. - If this callback returns true, the tracing is aborted. - Note that some paths can go out of the world and come back again (parabola), - in such a case this callback is followed by OnIntoWorld() and further OnNextBlock() calls - */ - virtual bool OnOutOfWorld(Vector3d a_BlockPos) - { - UNUSED(a_BlockPos); - return false; - } - - /** Called when the path goes into the world, from either below (a_BlockPos.y < 0) or above (a_BlockPos.y >= cChunkDef::Height) - The coords specify the exact point at which the path entered the world. - If this callback returns true, the tracing is aborted. - Note that some paths can go out of the world and come back again (parabola), - in such a case this callback is followed by further OnNextBlock() calls - */ - virtual bool OnIntoWorld(Vector3d a_BlockPos) - { - UNUSED(a_BlockPos); - return false; - } - - /** Called when the path is sure not to hit any more blocks. - Note that for some shapes this might never happen (line with constant Y) - */ - virtual void OnNoMoreHits(void) {} - - /** Called when the block tracing walks into a chunk that is not allocated. - This usually means that the tracing is aborted. - */ - virtual void OnNoChunk(void) {} - } ; - - - /** Creates the BlockTracer parent with the specified callbacks */ - cBlockTracer(cWorld & a_World, cCallbacks & a_Callbacks) : - m_World(&a_World), - m_Callbacks(&a_Callbacks) + /** Called on each block encountered along the path, including the first block (path start), if chunk data is not loaded + When this callback returns true, the tracing is aborted. */ + virtual bool OnNextBlockNoData(Vector3i a_BlockPos, eBlockFace a_EntryFace) { + UNUSED(a_BlockPos); + UNUSED(a_EntryFace); + return false; } - - /** Sets new world, returns the old one. Note that both need to be valid */ - cWorld & SetWorld(cWorld & a_World) + /** Called when the path goes out of world, either below (a_BlockPos.y < 0) or above (a_BlockPos.y >= cChunkDef::Height) + The coords specify the exact point at which the path exited the world. + If this callback returns true, the tracing is aborted. + Note that some paths can go out of the world and come back again (parabola), + in such a case this callback is followed by OnIntoWorld() and further OnNextBlock() calls. */ + virtual bool OnOutOfWorld(Vector3d a_BlockPos) { - cWorld & Old = *m_World; - m_World = &a_World; - return Old; + UNUSED(a_BlockPos); + return false; } - - /** Sets new callbacks, returns the old ones. Note that both need to be valid */ - cCallbacks & SetCallbacks(cCallbacks & a_NewCallbacks) + /** Called when the path goes into the world, from either below (a_BlockPos.y < 0) or above (a_BlockPos.y >= cChunkDef::Height) + The coords specify the exact point at which the path entered the world. + If this callback returns true, the tracing is aborted. + Note that some paths can go out of the world and come back again (parabola), + in such a case this callback is followed by further OnNextBlock() calls. */ + virtual bool OnIntoWorld(Vector3d a_BlockPos) { - cCallbacks & Old = *m_Callbacks; - m_Callbacks = &a_NewCallbacks; - return Old; + UNUSED(a_BlockPos); + return false; } -protected: - /** The world upon which to operate */ - cWorld * m_World; + /** Called when the path is sure not to hit any more blocks. + Note that for some shapes this might never happen (line with constant Y). */ + virtual void OnNoMoreHits(void) {} - /** The callback to use for reporting */ - cCallbacks * m_Callbacks; + /** Called when the block tracing walks into a chunk that is not allocated. + This usually means that the tracing is aborted. */ + virtual void OnNoChunk(void) {} } ; - - - - diff --git a/src/Physics/Tracers/LineBlockTracer.cpp b/src/Physics/Tracers/LineBlockTracer.cpp index 49eba9ac8..265d97dab 100644 --- a/src/Physics/Tracers/LineBlockTracer.cpp +++ b/src/Physics/Tracers/LineBlockTracer.cpp @@ -14,35 +14,186 @@ -cLineBlockTracer::cLineBlockTracer(cWorld & a_World, cCallbacks & a_Callbacks) : - Super(a_World, a_Callbacks), - m_Start(), - m_End(), - m_Diff(), - m_Dir(), - m_Current(), - m_CurrentFace(BLOCK_FACE_NONE) +static Vector3d CalcXZIntersection(double a_Y, const Vector3d a_Start, const Vector3d a_End) { + const double Ratio = (a_Start.y - a_Y) / (a_Start.y - a_End.y); + return { a_Start.x + (a_End.x - a_Start.x) * Ratio, a_Y, a_Start.z + (a_End.z - a_Start.z) * Ratio }; } -bool cLineBlockTracer::Trace(cWorld & a_World, cBlockTracer::cCallbacks & a_Callbacks, const Vector3d a_Start, const Vector3d a_End) +static Vector3d FixStartAboveWorld(const Vector3d a_Start, const Vector3d a_End) { - cLineBlockTracer Tracer(a_World, a_Callbacks); - return Tracer.Trace(a_Start, a_End); + // We must set the start Y to less than cChunkDef::Height so that it is considered inside the world later on. + // Therefore we use an EPS-offset from the height, as small as reasonably possible. + const double Height = static_cast<double>(cChunkDef::Height) - 0.00001; + return CalcXZIntersection(Height, a_Start, a_End); +} + + + + + +static Vector3d FixStartBelowWorld(const Vector3d a_Start, const Vector3d a_End) +{ + return CalcXZIntersection(0, a_Start, a_End); +} + + + + + +static eBlockFace MoveToNextBlock(Vector3i & a_CurrentBlock, const Vector3i a_Adjustment, const Vector3d a_Direction, const Vector3d a_Start, const Vector3i a_StepOffset, const eBlockFace a_StepXFace, const eBlockFace a_StepYFace, const eBlockFace a_StepZFace) +{ + const auto Coeff = (Vector3d(a_CurrentBlock + a_Adjustment) - a_Start) / a_Direction; + + if (Coeff.x <= Coeff.y) + { + if (Coeff.x <= Coeff.z) + { + a_CurrentBlock.x += a_StepOffset.x; + return a_StepXFace; + } + } + else if (Coeff.y <= Coeff.z) + { + a_CurrentBlock.y += a_StepOffset.y; + return a_StepYFace; + } + + a_CurrentBlock.z += a_StepOffset.z; + return a_StepZFace; } -bool cLineBlockTracer::LineOfSightTrace(cWorld & a_World, const Vector3d & a_Start, const Vector3d & a_End, int a_Sight) +bool LineBlockTracer::Trace(const cChunk & a_Chunk, BlockTracerCallbacks & a_Callbacks, Vector3d a_Start, Vector3d a_End) +{ + // Clamp the start coords into the world by advancing them along the line: + if (a_Start.y < 0) + { + if (a_End.y < 0) + { + // Nothing to trace: + a_Callbacks.OnNoMoreHits(); + return true; + } + + a_Start = FixStartBelowWorld(a_Start, a_End); + a_Callbacks.OnIntoWorld(a_Start); + } + else if (a_Start.y >= cChunkDef::Height) + { + if (a_End.y >= cChunkDef::Height) + { + a_Callbacks.OnNoMoreHits(); + return true; + } + + a_Start = FixStartAboveWorld(a_Start, a_End); + a_Callbacks.OnIntoWorld(a_Start); + } + + const auto EndPosition = a_End.Floor(); + const auto Direction = a_End - a_Start; + const bool XPositive = a_Start.x <= a_End.x; + const bool YPositive = a_Start.y <= a_End.y; + const bool ZPositive = a_Start.z <= a_End.z; + const auto StepXFace = XPositive ? BLOCK_FACE_XM : BLOCK_FACE_XP; + const auto StepYFace = YPositive ? BLOCK_FACE_YM : BLOCK_FACE_YP; + const auto StepZFace = ZPositive ? BLOCK_FACE_ZM : BLOCK_FACE_ZP; + const Vector3i Adjustment(XPositive ? 1 : 0, YPositive ? 1 : 0, ZPositive ? 1 : 0); + const Vector3i StepOffset(XPositive ? 1 : -1, YPositive ? 1 : -1, ZPositive ? 1 : -1); + + auto Position = a_Start.Floor(); + auto Chunk = const_cast<cChunk &>(a_Chunk).GetNeighborChunk(Position.x, Position.z); + + // We should always start in a valid chunk: + ASSERT(Chunk != nullptr); + + // This is guaranteed by FixStartAboveWorld() / FixStartBelowWorld(): + ASSERT(cChunkDef::IsValidHeight(Position)); + + // This is the actual line tracing loop. + for (;;) + { + if (Position == EndPosition) + { + // We've reached the end + a_Callbacks.OnNoMoreHits(); + return true; + } + + // The face of the next block the line just entered. + const auto CurrentFace = MoveToNextBlock(Position, Adjustment, Direction, a_Start, StepOffset, StepXFace, StepYFace, StepZFace); + + if (!cChunkDef::IsValidHeight(Position)) + { + // We've gone out of the world, that's the end of this trace. + if (a_Callbacks.OnOutOfWorld(CalcXZIntersection(static_cast<double>(Position.y), a_Start, a_End))) + { + // The callback terminated the trace + return false; + } + a_Callbacks.OnNoMoreHits(); + return true; + } + + // Update the current chunk: + Chunk = Chunk->GetNeighborChunk(Position.x, Position.z); + if (Chunk == nullptr) + { + a_Callbacks.OnNoChunk(); + return false; + } + + // Report the current block through the callbacks: + if (Chunk->IsValid()) + { + BLOCKTYPE BlockType; + NIBBLETYPE BlockMeta; + int RelX = Position.x - Chunk->GetPosX() * cChunkDef::Width; + int RelZ = Position.z - Chunk->GetPosZ() * cChunkDef::Width; + Chunk->GetBlockTypeMeta(RelX, Position.y, RelZ, BlockType, BlockMeta); + if (a_Callbacks.OnNextBlock(Position, BlockType, BlockMeta, CurrentFace)) + { + // The callback terminated the trace. + return false; + } + } + else if (a_Callbacks.OnNextBlockNoData(Position, CurrentFace)) + { + // The callback terminated the trace. + return false; + } + } +} + + + + + +bool LineBlockTracer::Trace(cWorld & a_World, BlockTracerCallbacks & a_Callbacks, const Vector3d a_Start, const Vector3d a_End) +{ + int BlockX = FloorC(a_Start.x); + int BlockZ = FloorC(a_Start.z); + int ChunkX, ChunkZ; + cChunkDef::BlockToChunk(BlockX, BlockZ, ChunkX, ChunkZ); + return a_World.DoWithChunk(ChunkX, ChunkZ, [&a_Callbacks, a_Start, a_End](cChunk & a_Chunk) { return Trace(a_Chunk, a_Callbacks, a_Start, a_End); }); +} + + + + + +bool LineBlockTracer::LineOfSightTrace(cWorld & a_World, const Vector3d & a_Start, const Vector3d & a_End, LineOfSight a_Sight) { static class LineOfSightCallbacks: - public cLineBlockTracer::cCallbacks + public BlockTracerCallbacks { bool m_IsAirOpaque; bool m_IsWaterOpaque; @@ -67,9 +218,9 @@ bool cLineBlockTracer::LineOfSightTrace(cWorld & a_World, const Vector3d & a_Sta } } } callbacks( - (a_Sight & losAir) == 0, - (a_Sight & losWater) == 0, - (a_Sight & losLava) == 0 + (a_Sight & LineOfSight::Air) == 0, + (a_Sight & LineOfSight::Water) == 0, + (a_Sight & LineOfSight::Lava) == 0 ); return Trace(a_World, callbacks, a_Start, a_End); } @@ -78,7 +229,7 @@ bool cLineBlockTracer::LineOfSightTrace(cWorld & a_World, const Vector3d & a_Sta -bool cLineBlockTracer::FirstSolidHitTrace( +bool LineBlockTracer::FirstSolidHitTrace( cWorld & a_World, const Vector3d & a_Start, const Vector3d & a_End, Vector3d & a_HitCoords, @@ -86,7 +237,7 @@ bool cLineBlockTracer::FirstSolidHitTrace( ) { class cSolidHitCallbacks: - public cCallbacks + public BlockTracerCallbacks { public: cSolidHitCallbacks(const Vector3d & a_CBStart, const Vector3d & a_CBEnd, Vector3d & a_CBHitCoords, Vector3i & a_CBHitBlockCoords, eBlockFace & a_CBHitBlockFace): @@ -129,218 +280,3 @@ bool cLineBlockTracer::FirstSolidHitTrace( } callbacks(a_Start, a_End, a_HitCoords, a_HitBlockCoords, a_HitBlockFace); return !Trace(a_World, callbacks, a_Start, a_End); } - - - - - -bool cLineBlockTracer::Trace(const Vector3d a_Start, const Vector3d a_End) -{ - // Initialize the member veriables: - m_Start = a_Start; - m_End = a_End; - m_Dir.x = (m_Start.x < m_End.x) ? 1 : -1; - m_Dir.y = (m_Start.y < m_End.y) ? 1 : -1; - m_Dir.z = (m_Start.z < m_End.z) ? 1 : -1; - m_CurrentFace = BLOCK_FACE_NONE; - - // Check the start coords, adjust into the world: - if (m_Start.y < 0) - { - if (m_End.y < 0) - { - // Nothing to trace - m_Callbacks->OnNoMoreHits(); - return true; - } - FixStartBelowWorld(); - m_Callbacks->OnIntoWorld(m_Start); - } - else if (m_Start.y >= cChunkDef::Height) - { - if (m_End.y >= cChunkDef::Height) - { - m_Callbacks->OnNoMoreHits(); - return true; - } - FixStartAboveWorld(); - m_Callbacks->OnIntoWorld(m_Start); - } - - m_Current = m_Start.Floor(); - - m_Diff = m_End - m_Start; - - // The actual trace is handled with ChunkMapCS locked by calling our ChunkCallback for the specified chunk - int BlockX = FloorC(m_Start.x); - int BlockZ = FloorC(m_Start.z); - int ChunkX, ChunkZ; - cChunkDef::BlockToChunk(BlockX, BlockZ, ChunkX, ChunkZ); - return m_World->DoWithChunk(ChunkX, ChunkZ, [this](cChunk & a_Chunk) { return ChunkCallback(&a_Chunk); }); -} - - - - - -void cLineBlockTracer::FixStartAboveWorld(void) -{ - // We must set the start Y to less than cChunkDef::Height so that it is considered inside the world later on - // Therefore we use an EPS-offset from the height, as small as reasonably possible. - const double Height = static_cast<double>(cChunkDef::Height) - 0.00001; - CalcXZIntersection(Height, m_Start.x, m_Start.z); - m_Start.y = Height; -} - - - - - -void cLineBlockTracer::FixStartBelowWorld(void) -{ - CalcXZIntersection(0, m_Start.x, m_Start.z); - m_Start.y = 0; -} - - - - - -void cLineBlockTracer::CalcXZIntersection(double a_Y, double & a_IntersectX, double & a_IntersectZ) -{ - double Ratio = (m_Start.y - a_Y) / (m_Start.y - m_End.y); - a_IntersectX = m_Start.x + (m_End.x - m_Start.x) * Ratio; - a_IntersectZ = m_Start.z + (m_End.z - m_Start.z) * Ratio; -} - - - - - -bool cLineBlockTracer::MoveToNextBlock(void) -{ - // Find out which of the current block's walls gets hit by the path: - static const double EPS = 0.00001; - enum - { - dirNONE, - dirX, - dirY, - dirZ, - } Direction = dirNONE; - - // Calculate the next YZ wall hit: - double Coeff = 1; - if (std::abs(m_Diff.x) > EPS) - { - double DestX = (m_Dir.x > 0) ? (m_Current.x + 1) : m_Current.x; - double CoeffX = (DestX - m_Start.x) / m_Diff.x; - if (CoeffX <= 1) // We need to include equality for the last block in the trace - { - Coeff = CoeffX; - Direction = dirX; - } - } - - // If the next XZ wall hit is closer, use it instead: - if (std::abs(m_Diff.y) > EPS) - { - double DestY = (m_Dir.y > 0) ? (m_Current.y + 1) : m_Current.y; - double CoeffY = (DestY - m_Start.y) / m_Diff.y; - if (CoeffY <= Coeff) // We need to include equality for the last block in the trace - { - Coeff = CoeffY; - Direction = dirY; - } - } - - // If the next XY wall hit is closer, use it instead: - if (std::abs(m_Diff.z) > EPS) - { - double DestZ = (m_Dir.z > 0) ? (m_Current.z + 1) : m_Current.z; - double CoeffZ = (DestZ - m_Start.z) / m_Diff.z; - if (CoeffZ <= Coeff) // We need to include equality for the last block in the trace - { - Direction = dirZ; - } - } - - // Based on the wall hit, adjust the current coords - switch (Direction) - { - case dirX: m_Current.x += m_Dir.x; m_CurrentFace = (m_Dir.x > 0) ? BLOCK_FACE_XM : BLOCK_FACE_XP; break; - case dirY: m_Current.y += m_Dir.y; m_CurrentFace = (m_Dir.y > 0) ? BLOCK_FACE_YM : BLOCK_FACE_YP; break; - case dirZ: m_Current.z += m_Dir.z; m_CurrentFace = (m_Dir.z > 0) ? BLOCK_FACE_ZM : BLOCK_FACE_ZP; break; - case dirNONE: return false; - } - return true; -} - - - - - -bool cLineBlockTracer::ChunkCallback(cChunk * a_Chunk) -{ - ASSERT((m_Current.y >= 0) && (m_Current.y < cChunkDef::Height)); // This should be provided by FixStartAboveWorld() / FixStartBelowWorld() - - // This is the actual line tracing loop. - for (;;) - { - // Our caller (DoWithChunk callback) should never give nothing: - ASSERT(a_Chunk != nullptr); - - // Move to next block - if (!MoveToNextBlock()) - { - // We've reached the end - m_Callbacks->OnNoMoreHits(); - return true; - } - - if ((m_Current.y < 0) || (m_Current.y >= cChunkDef::Height)) - { - // We've gone out of the world, that's the end of this trace - double IntersectX, IntersectZ; - CalcXZIntersection(m_Current.y, IntersectX, IntersectZ); - if (m_Callbacks->OnOutOfWorld({IntersectX, double(m_Current.y), IntersectZ})) - { - // The callback terminated the trace - return false; - } - m_Callbacks->OnNoMoreHits(); - return true; - } - - // Update the current chunk - a_Chunk = a_Chunk->GetNeighborChunk(m_Current.x, m_Current.z); - if (a_Chunk == nullptr) - { - m_Callbacks->OnNoChunk(); - return false; - } - - // Report the current block through the callbacks: - if (a_Chunk->IsValid()) - { - BLOCKTYPE BlockType; - NIBBLETYPE BlockMeta; - int RelX = m_Current.x - a_Chunk->GetPosX() * cChunkDef::Width; - int RelZ = m_Current.z - a_Chunk->GetPosZ() * cChunkDef::Width; - a_Chunk->GetBlockTypeMeta(RelX, m_Current.y, RelZ, BlockType, BlockMeta); - if (m_Callbacks->OnNextBlock(m_Current, BlockType, BlockMeta, m_CurrentFace)) - { - // The callback terminated the trace - return false; - } - } - else if (m_Callbacks->OnNextBlockNoData(m_Current, m_CurrentFace)) - { - // The callback terminated the trace - return false; - } - } -} - - - diff --git a/src/Physics/Tracers/LineBlockTracer.h b/src/Physics/Tracers/LineBlockTracer.h index b1dacee20..41b26c354 100644 --- a/src/Physics/Tracers/LineBlockTracer.h +++ b/src/Physics/Tracers/LineBlockTracer.h @@ -1,7 +1,7 @@ // LineBlockTracer.h -// Declares the cLineBlockTracer class representing a cBlockTracer that traces along a straight line between two points +// Declares the LineBlockTracer namespace representing a tracer that visits every block along a straight line between two points @@ -15,47 +15,36 @@ -// fwd: Chunk.h class cChunk; +class cWorld; -class cLineBlockTracer: - public cBlockTracer +namespace LineBlockTracer { - using Super = cBlockTracer; - -public: - - enum eLineOfSight + /* Bit flags used for LineOfSightTrace's Sight parameter. */ + enum LineOfSight { - // Bit flags used for LineOfSightTrace's Sight parameter: - losAir = 1, // Can see through air - losWater = 2, // Can see through water - losLava = 4, // Can see through lava + Air = 1, // Can see through air. + Water = 2, // Can see through water. + Lava = 4, // Can see through lava. // Common combinations: - losAirWaterLava = losAir | losWater | losLava, - losAirWater = losAir | losWater, + AirWaterLava = Air | Water | Lava, + AirWater = Air | Water, }; - - cLineBlockTracer(cWorld & a_World, cCallbacks & a_Callbacks); - /** Traces one line between Start and End; returns true if the entire line was traced (until OnNoMoreHits()) */ - bool Trace(Vector3d a_Start, Vector3d a_End); - - - // Utility functions for simple one-line usage: + bool Trace(const cChunk & a_Chunk, BlockTracerCallbacks & a_Callbacks, Vector3d a_Start, Vector3d a_End); /** Traces one line between Start and End; returns true if the entire line was traced (until OnNoMoreHits()) */ - static bool Trace(cWorld & a_World, cCallbacks & a_Callbacks, const Vector3d a_Start, const Vector3d a_End); + bool Trace(cWorld & a_World, BlockTracerCallbacks & a_Callbacks, const Vector3d a_Start, const Vector3d a_End); /** Returns true if the two positions are within line of sight (not obscured by blocks). a_Sight specifies which blocks are considered transparent for the trace, is an OR-combination of eLineOfSight constants. */ - static bool LineOfSightTrace(cWorld & a_World, const Vector3d & a_Start, const Vector3d & a_End, int a_Sight); + bool LineOfSightTrace(cWorld & a_World, const Vector3d & a_Start, const Vector3d & a_End, LineOfSight a_Sight); /** Traces until the first solid block is hit (or until end, whichever comes first. If a solid block was hit, returns true and fills a_HitCoords, a_HitBlockCoords and a_HitBlockFace. @@ -63,48 +52,11 @@ public: a_HitCoords is the exact coords of the hit, a_HitBlockCoords are the coords of the solid block that was hit, a_HitBlockFace is the face of the solid block that was hit. */ - static bool FirstSolidHitTrace( + bool FirstSolidHitTrace( cWorld & a_World, const Vector3d & a_Start, const Vector3d & a_End, Vector3d & a_HitCoords, Vector3i & a_HitBlockCoords, eBlockFace & a_HitBlockFace ); - -protected: - /** The start point of the trace */ - Vector3d m_Start; - - /** The end point of the trace */ - Vector3d m_End; - - /** The difference in coords, End - Start */ - Vector3d m_Diff; - - /** The increment at which the block coords are going from Start to End; either +1 or -1 */ - Vector3i m_Dir; - - /** The current block */ - Vector3i m_Current; - - /** The face through which the current block has been entered */ - eBlockFace m_CurrentFace; - - - /** Adjusts the start point above the world to just at the world's top */ - void FixStartAboveWorld(void); - - /** Adjusts the start point below the world to just at the world's bottom */ - void FixStartBelowWorld(void); - - /** Calculates the XZ coords of an intersection with the specified Yconst plane; assumes that such an intersection exists */ - void CalcXZIntersection(double a_Y, double & a_IntersectX, double & a_IntersectZ); - - /** Moves m_Current to the next block on the line; returns false if no move is possible (reached the end) */ - bool MoveToNextBlock(void); - - bool ChunkCallback(cChunk * a_Chunk); -} ; - - - +} diff --git a/src/World.cpp b/src/World.cpp index 35f41d389..da73b46bd 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -12,7 +12,7 @@ #include "Generating/ComposableGenerator.h" #include "SetChunkData.h" #include "DeadlockDetect.h" -#include "LineBlockTracer.h" +#include "Physics/Tracers/LineBlockTracer.h" #include "UUID.h" #include "BlockInServerPluginInterface.h" @@ -1844,7 +1844,7 @@ void cWorld::SpawnItemPickups(const cItems & a_Pickups, Vector3d a_Pos, Vector3d UInt32 cWorld::SpawnItemPickup(Vector3d a_Position, cItem && a_Item, Vector3d a_Speed, cTickTime a_CollectionDelay, cTickTime a_Lifetime, bool a_CanCombine) { - auto Pickup = std::make_unique<cPickup>(a_Position, std::move(a_Item), a_Speed, a_CollectionDelay, a_Lifetime); + auto Pickup = std::make_unique<cPickup>(a_Position, std::move(a_Item), a_Speed, a_CollectionDelay, a_Lifetime, a_CanCombine); auto PickupPtr = Pickup.get(); if (!PickupPtr->Initialize(std::move(Pickup), *this)) @@ -2413,7 +2413,7 @@ bool cWorld::DoWithNearestPlayer(Vector3d a_Pos, double a_RangeLimit, cPlayerLis // Check LineOfSight, if requested: if ( a_CheckLineOfSight && - !cLineBlockTracer::LineOfSightTrace(*this, a_Pos, Pos, cLineBlockTracer::losAirWater) + !LineBlockTracer::LineOfSightTrace(*this, a_Pos, Pos, LineBlockTracer::LineOfSight::AirWater) ) { continue; diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp index fa7f8a349..18ef810f7 100644 --- a/src/WorldStorage/WSSAnvil.cpp +++ b/src/WorldStorage/WSSAnvil.cpp @@ -1942,7 +1942,7 @@ void cWSSAnvil::LoadPickupFromNBT(cEntityList & a_Entities, const cParsedNBT & a return; } - auto Pickup = std::make_unique<cPickup>(Vector3d(), std::move(Item), Vector3d(), 0_tick, 0_tick); // Pickup delay loaded later + auto Pickup = std::make_unique<cPickup>(Vector3d(), std::move(Item), Vector3d(), 0_tick, 0_tick, true); // Pickup delay loaded later if (!LoadEntityBaseFromNBT(*Pickup.get(), a_NBT, a_TagIdx)) { return; |