From 5402b214b31af60bc96cd4e47e9211715c3e99f5 Mon Sep 17 00:00:00 2001 From: Lane Kolbly Date: Fri, 28 Jul 2017 11:59:21 -0500 Subject: Check for intersection between placed blocks and entities. (#3850) * Check for intersection between placed blocks and entities. + Implemented GetPlacementCollisionBox, to permit custom placement collision boxes for blocks. * Factored block-entity placement checking into another function in cPlayer. - Removed vector min/max functions * Use GetWorld to get the world in DoesPlacingBlocksIntersectEntity. + Added block height checks, allow different cEntity subclasses to decide whether they will prevent block placement. --- src/Entities/Entity.h | 3 ++ src/Entities/Pickup.h | 2 ++ src/Entities/Player.cpp | 95 +++++++++++++++++++++++++++++++++++++++++++++++++ src/Entities/Player.h | 3 ++ 4 files changed, 103 insertions(+) (limited to 'src/Entities') diff --git a/src/Entities/Entity.h b/src/Entities/Entity.h index d55955b0c..fb3103abc 100644 --- a/src/Entities/Entity.h +++ b/src/Entities/Entity.h @@ -191,6 +191,9 @@ public: /** Returns the topmost class's parent class name for the object. cEntity returns an empty string (no parent). */ virtual const char * GetParentClass(void) const; + /** Returns whether blocks can be placed intersecting this entities' hitbox */ + virtual bool DoesPreventBlockPlacement(void) const { return true; } + cWorld * GetWorld(void) const { return m_World; } double GetHeadYaw (void) const { return m_HeadYaw; } // In degrees diff --git a/src/Entities/Pickup.h b/src/Entities/Pickup.h index c2fcbd7f2..61e433e07 100644 --- a/src/Entities/Pickup.h +++ b/src/Entities/Pickup.h @@ -36,6 +36,8 @@ public: virtual void Tick(std::chrono::milliseconds a_Dt, cChunk & a_Chunk) override; + virtual bool DoesPreventBlockPlacement(void) const override { return false; } + /** Returns whether this pickup is allowed to combine with other similar pickups */ bool CanCombine(void) const { return m_bCanCombine; } // tolua_export diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index afeb8fa04..f66f2b73d 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -2641,8 +2641,103 @@ void cPlayer::SendBlocksAround(int a_BlockX, int a_BlockY, int a_BlockZ, int a_R +bool cPlayer::DoesPlacingBlocksIntersectEntity(const sSetBlockVector & a_Blocks) +{ + // Compute the bounding box for each block to be placed + std::vector PlacementBoxes; + cBoundingBox PlacingBounds(0, 0, 0, 0, 0, 0); + bool HasInitializedBounds = false; + for (auto blk: a_Blocks) + { + cBlockHandler * BlockHandler = cBlockInfo::GetHandler(blk.m_BlockType); + int x = blk.GetX(); + int y = blk.GetY(); + int z = blk.GetZ(); + cBoundingBox BlockBox = BlockHandler->GetPlacementCollisionBox( + m_World->GetBlock(x - 1, y, z), + m_World->GetBlock(x + 1, y, z), + (y == 0) ? E_BLOCK_AIR : m_World->GetBlock(x, y - 1, z), + (y == cChunkDef::Height - 1) ? E_BLOCK_AIR : m_World->GetBlock(x, y + 1, z), + m_World->GetBlock(x, y, z - 1), + m_World->GetBlock(x, y, z + 1) + ); + BlockBox.Move(x, y, z); + + PlacementBoxes.push_back(BlockBox); + + if (HasInitializedBounds) + { + PlacingBounds = PlacingBounds.Union(BlockBox); + } + else + { + PlacingBounds = BlockBox; + HasInitializedBounds = true; + } + } + + cWorld * World = GetWorld(); + + // Check to see if any entity intersects any block being placed + class DoesIntersectBlock : public cEntityCallback + { + public: + const std::vector & m_BoundingBoxes; + + // The distance inside the block the entity can still be. + const double EPSILON = 0.0005; + + DoesIntersectBlock(const std::vector & a_BoundingBoxes) : + m_BoundingBoxes(a_BoundingBoxes) + { + } + + virtual bool Item(cEntity * a_Entity) override + { + if (!a_Entity->DoesPreventBlockPlacement()) + { + return false; + } + cBoundingBox EntBox(a_Entity->GetPosition(), a_Entity->GetWidth() / 2, a_Entity->GetHeight()); + for (auto BlockBox: m_BoundingBoxes) + { + + // Put in a little bit of wiggle room + BlockBox.Expand(-EPSILON, -EPSILON, -EPSILON); + if (EntBox.DoesIntersect(BlockBox)) + { + return true; + } + } + return false; + } + } Callback(PlacementBoxes); + + // See if any entities in that bounding box collide with anyone + if (!World->ForEachEntityInBox(PlacingBounds, Callback)) + { + return true; + } + + return false; +} + + + + + bool cPlayer::PlaceBlocks(const sSetBlockVector & a_Blocks) { + if (DoesPlacingBlocksIntersectEntity(a_Blocks)) + { + // Abort - re-send all the current blocks in the a_Blocks' coords to the client: + for (auto blk2: a_Blocks) + { + m_World->SendBlockTo(blk2.GetX(), blk2.GetY(), blk2.GetZ(), this); + } + return false; + } + // Call the "placing" hooks; if any fail, abort: cPluginManager * pm = cPluginManager::Get(); for (auto blk: a_Blocks) diff --git a/src/Entities/Player.h b/src/Entities/Player.h index 8ad803998..c00dbc7f1 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -485,6 +485,9 @@ public: /** Update movement-related statistics. */ void UpdateMovementStats(const Vector3d & a_DeltaPos, bool a_PreviousIsOnGround); + /** Whether placing the given blocks would intersect any entitiy */ + bool DoesPlacingBlocksIntersectEntity(const sSetBlockVector & a_Blocks); + // tolua_begin /** Returns wheter the player can fly or not. */ -- cgit v1.2.3