Merge pull request #2274 from cuberite/ItemsRemovalFix
Refactored block placement workflow.
This commit is contained in:
commit
d83c9f194f
@ -25,10 +25,11 @@ public:
|
||||
}
|
||||
|
||||
|
||||
virtual bool OnPlayerPlace(
|
||||
virtual bool GetBlocksToPlace(
|
||||
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
|
||||
int a_CursorX, int a_CursorY, int a_CursorZ,
|
||||
sSetBlockVector & a_BlocksToPlace
|
||||
) override
|
||||
{
|
||||
// Can only be placed on the floor:
|
||||
@ -36,12 +37,10 @@ public:
|
||||
{
|
||||
return false;
|
||||
}
|
||||
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
|
||||
|
||||
// The "foot" block:
|
||||
sSetBlockVector blks;
|
||||
NIBBLETYPE BlockMeta = cBlockBedHandler::RotationToMetaData(a_Player.GetYaw());
|
||||
blks.emplace_back(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_BED, BlockMeta);
|
||||
a_BlocksToPlace.emplace_back(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_BED, BlockMeta);
|
||||
|
||||
// Check if there is empty space for the "head" block:
|
||||
// (Vanilla only allows beds to be placed into air)
|
||||
@ -50,10 +49,8 @@ public:
|
||||
{
|
||||
return false;
|
||||
}
|
||||
blks.emplace_back(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z, E_BLOCK_BED, BlockMeta | 0x08);
|
||||
|
||||
// Place both bed blocks:
|
||||
return a_Player.PlaceBlocks(blks);
|
||||
a_BlocksToPlace.emplace_back(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z, E_BLOCK_BED, BlockMeta | 0x08);
|
||||
return true;
|
||||
}
|
||||
} ;
|
||||
|
||||
|
@ -27,10 +27,11 @@ public:
|
||||
}
|
||||
|
||||
|
||||
virtual bool OnPlayerPlace(
|
||||
virtual bool GetBlocksToPlace(
|
||||
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
|
||||
int a_CursorX, int a_CursorY, int a_CursorZ,
|
||||
sSetBlockVector & a_BlocksToSet
|
||||
) override
|
||||
{
|
||||
// Can only be placed on the floor:
|
||||
@ -38,16 +39,14 @@ public:
|
||||
{
|
||||
return false;
|
||||
}
|
||||
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
|
||||
|
||||
// Place both blocks atomically:
|
||||
sSetBlockVector blks;
|
||||
blks.emplace_back(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_BIG_FLOWER, a_EquippedItem.m_ItemDamage & 0x07);
|
||||
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
|
||||
a_BlocksToSet.emplace_back(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_BIG_FLOWER, a_EquippedItem.m_ItemDamage & 0x07);
|
||||
if (a_BlockY < cChunkDef::Height - 1)
|
||||
{
|
||||
blks.emplace_back(a_BlockX, a_BlockY + 1, a_BlockZ, E_BLOCK_BIG_FLOWER, (a_EquippedItem.m_ItemDamage & 0x07) | 0x08);
|
||||
a_BlocksToSet.emplace_back(a_BlockX, a_BlockY + 1, a_BlockZ, E_BLOCK_BIG_FLOWER, (a_EquippedItem.m_ItemDamage & 0x07) | 0x08);
|
||||
}
|
||||
return a_Player.PlaceBlocks(blks);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -27,6 +27,8 @@ public:
|
||||
}
|
||||
|
||||
|
||||
/** We need an OnPlayerPlace override because we're processing neighbor chests and changing their metas,
|
||||
the parent class cannot do that. */
|
||||
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,
|
||||
@ -118,34 +120,32 @@ public:
|
||||
} // for j
|
||||
} // for i
|
||||
|
||||
// If there's no chest neighbor, place the single block chest and bail out:
|
||||
// Get the meta of the placed chest; take existing neighbors into account:
|
||||
BLOCKTYPE ChestBlockType = static_cast<BLOCKTYPE>(m_ItemType);
|
||||
if (NeighborIdx < 0)
|
||||
NIBBLETYPE Meta;
|
||||
auto yaw = a_Player.GetYaw();
|
||||
switch (NeighborIdx)
|
||||
{
|
||||
NIBBLETYPE Meta = cBlockChestHandler::PlayerYawToMetaData(a_Player.GetYaw());
|
||||
return a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, ChestBlockType, Meta);
|
||||
}
|
||||
|
||||
// There is a neighbor to which we need to adjust
|
||||
double yaw = a_Player.GetYaw();
|
||||
if ((NeighborIdx == 0) || (NeighborIdx == 2))
|
||||
{
|
||||
// The neighbor is in the X axis, form a X-axis-aligned dblchest:
|
||||
NIBBLETYPE Meta = ((yaw >= -90) && (yaw < 90)) ? E_META_CHEST_FACING_ZM : E_META_CHEST_FACING_ZP;
|
||||
|
||||
// Place the new chest:
|
||||
if (!a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, ChestBlockType, Meta))
|
||||
case 0:
|
||||
case 2:
|
||||
{
|
||||
return false;
|
||||
// The neighbor is in the X axis, form a X-axis-aligned dblchest:
|
||||
Meta = ((yaw >= -90) && (yaw < 90)) ? E_META_CHEST_FACING_ZM : E_META_CHEST_FACING_ZP;
|
||||
break;
|
||||
}
|
||||
|
||||
// Adjust the existing chest:
|
||||
a_World.FastSetBlock(a_BlockX + CrossCoords[NeighborIdx].x, a_BlockY, a_BlockZ + CrossCoords[NeighborIdx].z, ChestBlockType, Meta);
|
||||
return true;
|
||||
}
|
||||
|
||||
// The neighbor is in the Z axis, form a Z-axis-aligned dblchest:
|
||||
NIBBLETYPE Meta = (yaw < 0) ? E_META_CHEST_FACING_XM : E_META_CHEST_FACING_XP;
|
||||
case 1:
|
||||
case 3:
|
||||
{
|
||||
// The neighbor is in the Z axis, form a Z-axis-aligned dblchest:
|
||||
Meta = (yaw < 0) ? E_META_CHEST_FACING_XM : E_META_CHEST_FACING_XP;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
Meta = cBlockChestHandler::PlayerYawToMetaData(yaw);
|
||||
break;
|
||||
}
|
||||
} // switch (NeighborIdx)
|
||||
|
||||
// Place the new chest:
|
||||
if (!a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, ChestBlockType, Meta))
|
||||
@ -153,8 +153,31 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
// Adjust the existing chest:
|
||||
a_World.FastSetBlock(a_BlockX + CrossCoords[NeighborIdx].x, a_BlockY, a_BlockZ + CrossCoords[NeighborIdx].z, ChestBlockType, Meta);
|
||||
// Adjust the existing chest, if any:
|
||||
if (NeighborIdx > 0)
|
||||
{
|
||||
a_World.FastSetBlock(a_BlockX + CrossCoords[NeighborIdx].x, a_BlockY, a_BlockZ + CrossCoords[NeighborIdx].z, ChestBlockType, Meta);
|
||||
}
|
||||
|
||||
// Play the placement sound:
|
||||
AString PlaceSound = cBlockInfo::GetPlaceSound(ChestBlockType);
|
||||
float Volume = 1.0f, Pitch = 0.8f;
|
||||
if (PlaceSound == "dig.metal")
|
||||
{
|
||||
Pitch = 1.2f;
|
||||
PlaceSound = "dig.stone";
|
||||
}
|
||||
else if (PlaceSound == "random.anvil_land")
|
||||
{
|
||||
Volume = 0.65f;
|
||||
}
|
||||
a_World.BroadcastSoundEffect(PlaceSound, a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, Volume, Pitch);
|
||||
|
||||
// Remove the "placed" item:
|
||||
if (a_Player.IsGameModeSurvival())
|
||||
{
|
||||
a_Player.GetInventory().RemoveOneEquippedItem();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -20,10 +20,11 @@ public:
|
||||
}
|
||||
|
||||
|
||||
virtual bool OnPlayerPlace(
|
||||
virtual bool GetBlocksToPlace(
|
||||
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
|
||||
int a_CursorX, int a_CursorY, int a_CursorZ,
|
||||
sSetBlockVector & a_BlocksToSet
|
||||
) override
|
||||
{
|
||||
// Vanilla only allows door placement while clicking on the top face of the block below the door:
|
||||
@ -107,10 +108,9 @@ public:
|
||||
}
|
||||
|
||||
// Set the blocks:
|
||||
sSetBlockVector blks;
|
||||
blks.emplace_back(a_BlockX, a_BlockY, a_BlockZ, BlockType, LowerBlockMeta);
|
||||
blks.emplace_back(a_BlockX, a_BlockY + 1, a_BlockZ, BlockType, UpperBlockMeta);
|
||||
return a_Player.PlaceBlocks(blks);
|
||||
a_BlocksToSet.emplace_back(a_BlockX, a_BlockY, a_BlockZ, BlockType, LowerBlockMeta);
|
||||
a_BlocksToSet.emplace_back(a_BlockX, a_BlockY + 1, a_BlockZ, BlockType, UpperBlockMeta);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
@ -367,37 +367,51 @@ bool cItemHandler::OnPlayerPlace(
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
BLOCKTYPE BlockType;
|
||||
NIBBLETYPE BlockMeta;
|
||||
if (!GetPlacementBlockTypeMeta(&a_World, &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta))
|
||||
|
||||
// Get all the blocks to place:
|
||||
sSetBlockVector blocks;
|
||||
if (!GetBlocksToPlace(a_World, a_Player, a_EquippedItem, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, blocks))
|
||||
{
|
||||
// Handler refused the placement, send that information back to the client:
|
||||
for (const auto & blk: blocks)
|
||||
{
|
||||
a_World.SendBlockTo(blk.GetX(), blk.GetY(), blk.GetZ(), &a_Player);
|
||||
}
|
||||
a_World.SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, &a_Player);
|
||||
a_Player.GetInventory().SendEquippedSlot();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta))
|
||||
// Try to place the blocks:
|
||||
if (!a_Player.PlaceBlocks(blocks))
|
||||
{
|
||||
// The placement failed, the block has already been re-sent, re-send inventory:
|
||||
// The placement failed, the blocks have already been re-sent, re-send inventory:
|
||||
a_Player.GetInventory().SendEquippedSlot();
|
||||
return false;
|
||||
}
|
||||
|
||||
AString PlaceSound = cBlockInfo::GetPlaceSound(BlockType);
|
||||
float Volume = 1.0f, Pitch = 0.8f;
|
||||
if (PlaceSound == "dig.metal")
|
||||
// Play the placement sound for the main block:
|
||||
for (const auto & blk: blocks)
|
||||
{
|
||||
Pitch = 1.2f;
|
||||
PlaceSound = "dig.stone";
|
||||
}
|
||||
else if (PlaceSound == "random.anvil_land")
|
||||
{
|
||||
Volume = 0.65f;
|
||||
}
|
||||
|
||||
a_World.BroadcastSoundEffect(PlaceSound, a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, Volume, Pitch);
|
||||
// Find the main block by comparing the coords:
|
||||
if ((blk.GetX() != a_BlockX) || (blk.GetY() != a_BlockY) || (blk.GetZ() != a_BlockZ))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
AString PlaceSound = cBlockInfo::GetPlaceSound(blk.m_BlockType);
|
||||
float Volume = 1.0f, Pitch = 0.8f;
|
||||
if (PlaceSound == "dig.metal")
|
||||
{
|
||||
Pitch = 1.2f;
|
||||
PlaceSound = "dig.stone";
|
||||
}
|
||||
else if (PlaceSound == "random.anvil_land")
|
||||
{
|
||||
Volume = 0.65f;
|
||||
}
|
||||
a_World.BroadcastSoundEffect(PlaceSound, a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, Volume, Pitch);
|
||||
break;
|
||||
} // for blk - blocks[]
|
||||
|
||||
// Remove the "placed" item:
|
||||
if (a_Player.IsGameModeSurvival())
|
||||
@ -411,6 +425,27 @@ bool cItemHandler::OnPlayerPlace(
|
||||
|
||||
|
||||
|
||||
bool cItemHandler::GetBlocksToPlace(
|
||||
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,
|
||||
sSetBlockVector & a_BlocksToSet
|
||||
)
|
||||
{
|
||||
BLOCKTYPE BlockType;
|
||||
NIBBLETYPE BlockMeta;
|
||||
if (!GetPlacementBlockTypeMeta(&a_World, &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
a_BlocksToSet.emplace_back(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cItemHandler::OnItemUse(
|
||||
cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item,
|
||||
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace
|
||||
|
@ -35,8 +35,9 @@ public:
|
||||
|
||||
|
||||
/** Called when the player tries to place the item (right mouse button, IsPlaceable() == true).
|
||||
The default handler uses GetPlacementBlockTypeMeta and places the returned block.
|
||||
Override this function for advanced behavior such as placing multiple blocks.
|
||||
The block coords are for the block that has been clicked.
|
||||
The default handler uses GetBlocksToPlace() and places the returned blocks.
|
||||
Override if the item needs advanced processing, such as spawning a mob based on the blocks being placed.
|
||||
If the block placement is refused inside this call, it will automatically revert the client-side changes.
|
||||
Returns true if the placement succeeded, false if the placement was aborted for any reason. */
|
||||
virtual bool OnPlayerPlace(
|
||||
@ -46,7 +47,20 @@ public:
|
||||
);
|
||||
|
||||
|
||||
/** Called when the player right-clicks with this item and IsPlaceable() == true, and OnPlace() is not overridden.
|
||||
/** Called from OnPlayerPlace() to determine the blocks that the current placement operation should set.
|
||||
The block coords are where the new (main) block should be placed.
|
||||
The default handler uses GetPlacementBlockTypeMeta() and provides that as the single block at the specified coords.
|
||||
Returns true if the placement succeeded, false if the placement was aborted for any reason.
|
||||
If aborted, the server then sends all original blocks in the coords provided in a_BlocksToSet to the client. */
|
||||
virtual bool GetBlocksToPlace(
|
||||
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,
|
||||
sSetBlockVector & a_BlocksToSet
|
||||
);
|
||||
|
||||
|
||||
/** Called when the player right-clicks with this item and IsPlaceable() == true, and OnPlayerPlace() is not overridden.
|
||||
This function should provide the block type and meta for the placed block, or refuse the placement.
|
||||
Returns true to allow placement, false to refuse. */
|
||||
virtual bool GetPlacementBlockTypeMeta(
|
||||
|
@ -12,9 +12,11 @@
|
||||
class cItemMobHeadHandler :
|
||||
public cItemHandler
|
||||
{
|
||||
typedef cItemHandler super;
|
||||
|
||||
public:
|
||||
cItemMobHeadHandler(int a_ItemType) :
|
||||
cItemHandler(a_ItemType)
|
||||
super(a_ItemType)
|
||||
{
|
||||
}
|
||||
|
||||
@ -30,34 +32,36 @@ public:
|
||||
{
|
||||
return true;
|
||||
}
|
||||
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
|
||||
auto placedX = a_BlockX, placedY = a_BlockY, placedZ = a_BlockZ;
|
||||
AddFaceDirection(placedY, placedY, placedZ, a_BlockFace);
|
||||
|
||||
// If the placed head is a wither, try to spawn the wither first:
|
||||
if (a_EquippedItem.m_ItemDamage == E_META_HEAD_WITHER)
|
||||
{
|
||||
if (TrySpawnWitherAround(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ))
|
||||
if (TrySpawnWitherAround(a_World, a_Player, placedX, placedY, placedZ))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// Wither not created, proceed with regular head placement
|
||||
}
|
||||
|
||||
return PlaceRegularHead(a_World, a_Player, a_EquippedItem, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
|
||||
cItem itemCopy(a_EquippedItem); // Make a copy in case this is the player's last head item and OnPlayerPlace removes it
|
||||
if (!super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
RegularHeadPlaced(a_World, a_Player, itemCopy, placedX, placedY, placedZ, a_BlockFace);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/** Places a regular head block with no mob spawning checking. */
|
||||
bool PlaceRegularHead(
|
||||
/** Called after placing a regular head block with no mob spawning.
|
||||
Adjusts the mob head entity based on the equipped item's data. */
|
||||
void RegularHeadPlaced(
|
||||
cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem,
|
||||
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace
|
||||
)
|
||||
{
|
||||
// Place the block:
|
||||
if (!a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_HEAD, BlockFaceToBlockMeta(a_BlockFace)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Use a callback to set the properties of the mob head block entity:
|
||||
class cCallback : public cBlockEntityCallback
|
||||
{
|
||||
@ -71,7 +75,7 @@ public:
|
||||
{
|
||||
return false;
|
||||
}
|
||||
cMobHeadEntity * MobHeadEntity = static_cast<cMobHeadEntity *>(a_BlockEntity);
|
||||
auto MobHeadEntity = static_cast<cMobHeadEntity *>(a_BlockEntity);
|
||||
|
||||
int Rotation = 0;
|
||||
if (m_BlockMeta == 1)
|
||||
@ -94,7 +98,6 @@ public:
|
||||
};
|
||||
cCallback Callback(a_Player, static_cast<eMobHeadType>(a_EquippedItem.m_ItemDamage), static_cast<NIBBLETYPE>(a_BlockFace));
|
||||
a_World.DoWithBlockEntityAt(a_BlockX, a_BlockY, a_BlockZ, Callback);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -340,7 +343,7 @@ public:
|
||||
) override
|
||||
{
|
||||
a_BlockType = E_BLOCK_HEAD;
|
||||
a_BlockMeta = (NIBBLETYPE)(a_Player->GetEquippedItem().m_ItemDamage & 0x0f);
|
||||
a_BlockMeta = BlockFaceToBlockMeta(a_BlockFace);
|
||||
return true;
|
||||
}
|
||||
} ;
|
||||
|
Loading…
Reference in New Issue
Block a user