From ccdf03daaf880dd0c89a03b50c11eb083ee1cfb0 Mon Sep 17 00:00:00 2001 From: Mattes D Date: Wed, 24 Dec 2014 07:20:17 +0100 Subject: Refactored all player block placing to go through hooks. Fixes #1618. --- src/Items/ItemPumpkin.h | 156 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 src/Items/ItemPumpkin.h (limited to 'src/Items/ItemPumpkin.h') diff --git a/src/Items/ItemPumpkin.h b/src/Items/ItemPumpkin.h new file mode 100644 index 000000000..fa00179d3 --- /dev/null +++ b/src/Items/ItemPumpkin.h @@ -0,0 +1,156 @@ + +// ItemPumpkin.h + +// Declares the cItemPumpkinHandler class representing the pumpkin block in its item form + + + + + +#pragma once + +#include "ItemHandler.h" + + + + + +class cItemPumpkinHandler: + public cItemHandler +{ + typedef cItemHandler super; + +public: + cItemPumpkinHandler(void): + super(E_BLOCK_PUMPKIN) + { + } + + + virtual bool OnPlayerPlace( + cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem, + int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, + int a_CursorX, int a_CursorY, int a_CursorZ + ) override + { + // First try spawning a snow golem or an iron golem: + int PlacedBlockX = a_BlockX; + int PlacedBlockY = a_BlockY; + int PlacedBlockZ = a_BlockZ; + AddFaceDirection(PlacedBlockX, PlacedBlockY, PlacedBlockZ, a_BlockFace); + if (TrySpawnGolem(a_World, a_Player, PlacedBlockX, PlacedBlockY, PlacedBlockZ)) + { + // The client thinks that they placed the pumpkin, let them know it's been replaced: + a_Player.SendBlocksAround(PlacedBlockX, PlacedBlockY, PlacedBlockZ); + return true; + } + + // No golem at these coords, place the block normally: + return super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ); + } + + + /** Spawns a snow / iron golem if the shape matches the recipe, supposing that the block placed at the specified coords is a pumpkin. + Returns true if the golem blocks are removed (for spawning), false if the recipe is not matched. */ + bool TrySpawnGolem(cWorld & a_World, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) + { + // A golem can't form with a pumpkin below level 2 or above level 255 + if ((a_BlockY < 2) || (a_BlockY >= cChunkDef::Height)) + { + return false; + } + + // Decide which golem to try spawning based on the block below the placed pumpkin: + switch (a_World.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ)) + { + case E_BLOCK_SNOW_BLOCK: return TrySpawnSnowGolem(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ); + case E_BLOCK_IRON_BLOCK: return TrySpawnIronGolem(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ); + default: + { + // No golem here + return false; + } + } + } + + + /** Spawns a snow golem if the shape matches the recipe, supposing that the block placed at the specified coords is a pumpkin. + Returns true if the golem blocks are removed (for spawning), false if the recipe is not matched. + Assumes that the block below the specified block has already been checked and is a snow block. */ + bool TrySpawnSnowGolem(cWorld & a_World, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) + { + // Need one more snow block 2 blocks below the pumpkin: + if (a_World.GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ) != E_BLOCK_SNOW_BLOCK) + { + return false; + } + + // Try to place air blocks where the original recipe blocks were: + sSetBlockVector AirBlocks; + AirBlocks.emplace_back(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); // Head + AirBlocks.emplace_back(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); // Torso + AirBlocks.emplace_back(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); // Legs + if (!a_Player.PlaceBlocks(AirBlocks)) + { + return false; + } + + // Spawn the golem: + a_World.SpawnMob(static_cast(a_BlockX) + 0.5, a_BlockY - 2, static_cast(a_BlockZ) + 0.5, mtSnowGolem); + return true; + } + + + /** Spawns an iron golem if the shape matches the recipe, supposing that the block placed at the specified coords is a pumpkin. + Returns true if the golem blocks are removed (for spawning), false if the recipe is not matched. + Assumes that the block below the specified block has already been checked and is an iron block. */ + bool TrySpawnIronGolem(cWorld & a_World, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) + { + // Need one more iron block 2 blocks below the pumpkin: + if (a_World.GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ) != E_BLOCK_IRON_BLOCK) + { + return false; + } + + // Check the two arm directions (X, Z) using a loop over two sets of offset vectors: + static const Vector3i ArmOffsets[] = + { + {1, 0, 0}, + {0, 0, 1}, + }; + for (size_t i = 0; i < ARRAYCOUNT(ArmOffsets); i++) + { + // If the arm blocks don't match, bail out of this loop repetition: + if ( + (a_World.GetBlock(a_BlockX + ArmOffsets[i].x, a_BlockY - 1, a_BlockZ + ArmOffsets[i].z) != E_BLOCK_IRON_BLOCK) || + (a_World.GetBlock(a_BlockX - ArmOffsets[i].x, a_BlockY - 1, a_BlockZ - ArmOffsets[i].z) != E_BLOCK_IRON_BLOCK) + ) + { + continue; + } + + // Try to place air blocks where the original recipe blocks were: + sSetBlockVector AirBlocks; + AirBlocks.emplace_back(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); // Head + AirBlocks.emplace_back(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); // Torso + AirBlocks.emplace_back(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); // Legs + AirBlocks.emplace_back(a_BlockX + ArmOffsets[i].x, a_BlockY - 1, a_BlockZ + ArmOffsets[i].z, E_BLOCK_AIR, 0); // Arm + AirBlocks.emplace_back(a_BlockX - ArmOffsets[i].x, a_BlockY - 1, a_BlockZ - ArmOffsets[i].z, E_BLOCK_AIR, 0); // Arm + if (!a_Player.PlaceBlocks(AirBlocks)) + { + return false; + } + + // Spawn the golem: + a_World.SpawnMob(static_cast(a_BlockX) + 0.5, a_BlockY - 2, static_cast(a_BlockZ) + 0.5, mtIronGolem); + return true; + } // for i - ArmOffsets[] + + // Neither arm offset matched, this thing is not a complete golem + return false; + } +}; + + + + -- cgit v1.2.3