1
0
Fork 0

Vector3 in Handlers (#4680)

Refactored all cBlockHandler and cItemHandler descendants to use Vector3.
This commit is contained in:
Mattes D 2020-04-21 22:19:22 +02:00 committed by GitHub
parent 246acb19f9
commit 487f9a2aa9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
111 changed files with 2687 additions and 1510 deletions

View File

@ -35,21 +35,34 @@ public:
virtual bool OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override virtual bool OnUse(
cChunkInterface & a_ChunkInterface,
cWorldInterface & a_WorldInterface,
cPlayer & a_Player,
const Vector3i a_BlockPos,
eBlockFace a_BlockFace,
const Vector3i a_CursorPos
) override
{ {
cWindow * Window = new cAnvilWindow(a_BlockX, a_BlockY, a_BlockZ); cWindow * Window = new cAnvilWindow(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z);
a_Player.OpenWindow(*Window); a_Player.OpenWindow(*Window);
return true; return true;
} }
virtual bool GetPlacementBlockTypeMeta( virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player, cChunkInterface & a_ChunkInterface,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, cPlayer & a_Player,
int a_CursorX, int a_CursorY, int a_CursorZ, const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override ) override
{ {
if (!Super::GetPlacementBlockTypeMeta(a_ChunkInterface, a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta)) if (!Super::GetPlacementBlockTypeMeta(a_ChunkInterface, a_Player, a_PlacedBlockPos, a_ClickedBlockFace, a_CursorPos, a_BlockType, a_BlockMeta))
{ {
return false; return false;
} }
@ -58,11 +71,19 @@ public:
return true; return true;
} }
virtual bool IsUseable() override virtual bool IsUseable() override
{ {
return true; return true;
} }
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{ {
UNUSED(a_Meta); UNUSED(a_Meta);

View File

@ -15,7 +15,11 @@
void cBlockBedHandler::OnBroken(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, Vector3i a_BlockPos, BLOCKTYPE a_OldBlockType, NIBBLETYPE a_OldBlockMeta) void cBlockBedHandler::OnBroken(
cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface,
const Vector3i a_BlockPos,
BLOCKTYPE a_OldBlockType, NIBBLETYPE a_OldBlockMeta
)
{ {
auto Direction = MetaDataToDirection(a_OldBlockMeta & 0x03); auto Direction = MetaDataToDirection(a_OldBlockMeta & 0x03);
if ((a_OldBlockMeta & 0x08) != 0) if ((a_OldBlockMeta & 0x08) != 0)
@ -50,87 +54,97 @@ void cBlockBedHandler::OnBroken(cChunkInterface & a_ChunkInterface, cWorldInterf
bool cBlockBedHandler::OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) bool cBlockBedHandler::OnUse(
cChunkInterface & a_ChunkInterface,
cWorldInterface & a_WorldInterface,
cPlayer & a_Player,
const Vector3i a_BlockPos,
eBlockFace a_BlockFace,
const Vector3i a_CursorPos
)
{ {
Vector3i Coords(a_BlockX, a_BlockY, a_BlockZ); // Sleeping in bed only allowed in Overworld, beds explode elsewhere:
if (a_WorldInterface.GetDimension() != dimOverworld) if (a_WorldInterface.GetDimension() != dimOverworld)
{ {
a_WorldInterface.DoExplosionAt(5, a_BlockX, a_BlockY, a_BlockZ, true, esBed, &Coords); auto PosCopy = a_BlockPos;
a_WorldInterface.DoExplosionAt(5, a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, true, esBed, &PosCopy);
return true;
} }
else if (!((a_WorldInterface.GetTimeOfDay() > 12541) && (a_WorldInterface.GetTimeOfDay() < 23458))) // Source: https://minecraft.gamepedia.com/Bed#Sleeping
// Sleeping is allowed only during night:
// TODO: Also during thunderstorms
if (!((a_WorldInterface.GetTimeOfDay() > 12541) && (a_WorldInterface.GetTimeOfDay() < 23458))) // Source: https://minecraft.gamepedia.com/Bed#Sleeping
{ {
a_Player.SendMessageFailure("You can only sleep at night"); a_Player.SendMessageFailure("You can only sleep at night");
return true;
}
// Check if the bed is occupied:
auto Meta = a_ChunkInterface.GetBlockMeta(a_BlockPos);
if ((Meta & 0x04) == 0x04)
{
a_Player.SendMessageFailure("This bed is occupied");
return true;
}
// Cannot sleep if there are hostile mobs nearby:
auto FindMobs = [](cEntity & a_Entity)
{
return (
(a_Entity.GetEntityType() == cEntity::etMonster) &&
(static_cast<cMonster&>(a_Entity).GetMobFamily() == cMonster::mfHostile)
);
};
if (!a_Player.GetWorld()->ForEachEntityInBox(cBoundingBox(a_Player.GetPosition() - Vector3i(0, 5, 0), 8, 10), FindMobs))
{
a_Player.SendMessageFailure("You may not rest now, there are monsters nearby");
return true;
}
// Broadcast the "Use bed" for the pillow block:
if ((Meta & 0x8) == 0x8)
{
// Is pillow
a_WorldInterface.GetBroadcastManager().BroadcastUseBed(a_Player, a_BlockPos);
} }
else else
{ {
NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta(Coords); // Is foot end
if ((Meta & 0x4) == 0x4) VERIFY((Meta & 0x04) != 0x04); // Occupied flag should never be set, else our compilator (intended) is broken
auto PillowPos = a_BlockPos + MetaDataToDirection(Meta & 0x03);
if (a_ChunkInterface.GetBlock(PillowPos) == E_BLOCK_BED) // Must always use pillow location for sleeping
{ {
a_Player.SendMessageFailure("This bed is occupied"); a_WorldInterface.GetBroadcastManager().BroadcastUseBed(a_Player, PillowPos);
} }
else }
// Occupy the bed:
a_Player.SetBedPos(a_BlockPos);
SetBedOccupationState(a_ChunkInterface, a_Player.GetLastBedPos(), true);
a_Player.SetIsInBed(true);
a_Player.SendMessageSuccess("Home position set successfully");
// Fast-forward the time if all players in the world are in their beds:
auto TimeFastForwardTester = [](cPlayer & a_OtherPlayer)
{
if (!a_OtherPlayer.IsInBed())
{ {
auto FindMobs = [](cEntity & a_Entity) return true;
{
return (
(a_Entity.GetEntityType() == cEntity::etMonster) &&
(static_cast<cMonster&>(a_Entity).GetMobFamily() == cMonster::mfHostile)
);
};
if (!a_Player.GetWorld()->ForEachEntityInBox(cBoundingBox(a_Player.GetPosition() - Vector3i(0, 5, 0), 8, 10), FindMobs))
{
a_Player.SendMessageFailure("You may not rest now, there are monsters nearby");
}
else
{
Vector3i PillowDirection(0, 0, 0);
if ((Meta & 0x8) == 0x8)
{
// Is pillow
a_WorldInterface.GetBroadcastManager().BroadcastUseBed(a_Player, { a_BlockX, a_BlockY, a_BlockZ });
}
else
{
// Is foot end
VERIFY((Meta & 0x4) != 0x4); // Occupied flag should never be set, else our compilator (intended) is broken
PillowDirection = MetaDataToDirection(Meta & 0x3);
if (a_ChunkInterface.GetBlock(Coords + PillowDirection) == E_BLOCK_BED) // Must always use pillow location for sleeping
{
a_WorldInterface.GetBroadcastManager().BroadcastUseBed(a_Player, Vector3i{a_BlockX, a_BlockY, a_BlockZ} + PillowDirection);
}
}
a_Player.SetBedPos(Coords);
SetBedOccupationState(a_ChunkInterface, a_Player.GetLastBedPos(), true);
a_Player.SetIsInBed(true);
a_Player.SendMessageSuccess("Home position set successfully");
auto TimeFastForwardTester = [](cPlayer & a_OtherPlayer)
{
if (!a_OtherPlayer.IsInBed())
{
return true;
}
return false;
};
if (a_WorldInterface.ForEachPlayer(TimeFastForwardTester))
{
a_WorldInterface.ForEachPlayer([&](cPlayer & a_OtherPlayer)
{
cBlockBedHandler::SetBedOccupationState(a_ChunkInterface, a_OtherPlayer.GetLastBedPos(), false);
a_OtherPlayer.SetIsInBed(false);
return false;
}
);
a_WorldInterface.SetTimeOfDay(0);
a_ChunkInterface.SetBlockMeta({a_BlockX, a_BlockY, a_BlockZ}, Meta & 0x0b); // Clear the "occupied" bit of the bed's block
}
}
} }
return false;
};
if (a_WorldInterface.ForEachPlayer(TimeFastForwardTester))
{
a_WorldInterface.ForEachPlayer([&](cPlayer & a_OtherPlayer)
{
cBlockBedHandler::SetBedOccupationState(a_ChunkInterface, a_OtherPlayer.GetLastBedPos(), false);
a_OtherPlayer.SetIsInBed(false);
return false;
}
);
a_WorldInterface.SetTimeOfDay(0);
a_ChunkInterface.SetBlockMeta(a_BlockPos, Meta & 0x0b); // Clear the "occupied" bit of the bed's block
} }
return true; return true;
} }

View File

@ -32,10 +32,30 @@ public:
// Overrides: // Overrides:
virtual void OnBroken(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, Vector3i a_BlockPos, BLOCKTYPE a_OldBlockType, NIBBLETYPE a_OldBlockMeta) override; virtual void OnBroken(
virtual bool OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface,
virtual cItems ConvertToPickups(NIBBLETYPE a_BlockMeta, cBlockEntity * a_BlockEntity, const cEntity * a_Digger, const cItem * a_Tool) override; const Vector3i a_BlockPos,
virtual void OnPlacedByPlayer(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, const sSetBlock & a_BlockChange) override; BLOCKTYPE a_OldBlockType, NIBBLETYPE a_OldBlockMeta
) override;
virtual bool OnUse(
cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos
) override;
virtual cItems ConvertToPickups(
NIBBLETYPE a_BlockMeta,
cBlockEntity * a_BlockEntity,
const cEntity * a_Digger,
const cItem * a_Tool
) override;
virtual void OnPlacedByPlayer(
cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player,
const sSetBlock & a_BlockChange
) override;

View File

@ -9,7 +9,7 @@
class cBlockBigFlowerHandler : class cBlockBigFlowerHandler:
public cBlockHandler public cBlockHandler
{ {
using Super = cBlockHandler; using Super = cBlockHandler;
@ -95,15 +95,15 @@ public:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) override
{ {
if (a_RelY <= 0) if (a_RelPos.y <= 0)
{ {
return false; return false;
} }
BLOCKTYPE BlockType; BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta; NIBBLETYPE BlockMeta;
a_Chunk.GetBlockTypeMeta(a_RelX, a_RelY - 1, a_RelZ, BlockType, BlockMeta); a_Chunk.GetBlockTypeMeta(a_RelPos.addedY(-1), BlockType, BlockMeta);
return IsBlockTypeOfDirt(BlockType) || ((BlockType == E_BLOCK_BIG_FLOWER) && !IsMetaTopPart(BlockMeta)); return IsBlockTypeOfDirt(BlockType) || ((BlockType == E_BLOCK_BIG_FLOWER) && !IsMetaTopPart(BlockMeta));
} }
@ -112,7 +112,7 @@ public:
virtual void OnBroken(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, Vector3i a_BlockPos, BLOCKTYPE a_OldBlockType, NIBBLETYPE a_OldBlockMeta) override virtual void OnBroken(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, const Vector3i a_BlockPos, BLOCKTYPE a_OldBlockType, NIBBLETYPE a_OldBlockMeta) override
{ {
if ((a_OldBlockMeta & 0x8) != 0) if ((a_OldBlockMeta & 0x8) != 0)
{ {

View File

@ -24,36 +24,40 @@ public:
virtual bool OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override virtual bool OnUse(
cChunkInterface & a_ChunkInterface,
cWorldInterface & a_WorldInterface,
cPlayer & a_Player,
const Vector3i a_BlockPos,
eBlockFace a_BlockFace,
const Vector3i a_CursorPos
) override
{ {
Vector3i Pos(a_BlockX, a_BlockY, a_BlockZ); NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta(a_BlockPos);
NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta(Pos);
Vector3d SoundPos(Pos); // If button is already on, do nothing:
// If button is already on do nothing
if (Meta & 0x08) if (Meta & 0x08)
{ {
return false; return false;
} }
// Set p the ON bit to on // Set the ON bit to on
Meta |= 0x08; Meta |= 0x08;
a_ChunkInterface.SetBlockMeta({a_BlockX, a_BlockY, a_BlockZ}, Meta, false); a_ChunkInterface.SetBlockMeta(a_BlockPos, Meta, false);
a_WorldInterface.WakeUpSimulators(Pos); a_WorldInterface.WakeUpSimulators(a_BlockPos);
a_WorldInterface.GetBroadcastManager().BroadcastSoundEffect("block.stone_button.click_on", SoundPos, 0.5f, 0.6f); a_WorldInterface.GetBroadcastManager().BroadcastSoundEffect("block.stone_button.click_on", a_BlockPos, 0.5f, 0.6f);
// Queue a button reset (unpress) // Queue a button reset (unpress)
auto TickDelay = (m_BlockType == E_BLOCK_STONE_BUTTON) ? 20 : 30; auto TickDelay = (m_BlockType == E_BLOCK_STONE_BUTTON) ? 20 : 30;
a_Player.GetWorld()->ScheduleTask(TickDelay, [SoundPos, Pos, this](cWorld & a_World) a_Player.GetWorld()->ScheduleTask(TickDelay, [a_BlockPos, this](cWorld & a_World)
{ {
if (a_World.GetBlock(Pos) == m_BlockType) if (a_World.GetBlock(a_BlockPos) == m_BlockType)
{ {
// Block hasn't change in the meantime; set its meta // Block hasn't change in the meantime; set its meta
a_World.SetBlockMeta(Pos.x, Pos.y, Pos.z, a_World.GetBlockMeta(Pos) & 0x07, false); a_World.SetBlockMeta(a_BlockPos, a_World.GetBlockMeta(a_BlockPos) & 0x07, false);
a_World.WakeUpSimulators(Pos); a_World.WakeUpSimulators(a_BlockPos);
a_World.BroadcastSoundEffect("block.stone_button.click_off", SoundPos, 0.5f, 0.5f); a_World.BroadcastSoundEffect("block.stone_button.click_off", a_BlockPos, 0.5f, 0.5f);
} }
} }
); );
@ -61,23 +65,38 @@ public:
return true; return true;
} }
virtual bool IsUseable(void) override virtual bool IsUseable(void) override
{ {
return true; return true;
} }
virtual bool GetPlacementBlockTypeMeta( virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player, cChunkInterface & a_ChunkInterface,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, cPlayer & a_Player,
int a_CursorX, int a_CursorY, int a_CursorZ, const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override ) override
{ {
a_BlockType = m_BlockType; a_BlockType = m_BlockType;
a_BlockMeta = BlockFaceToMetaData(a_BlockFace); a_BlockMeta = BlockFaceToMetaData(a_ClickedBlockFace);
return true; return true;
} }
/** Converts the block face of the neighbor to which the button is attached, to the block meta for this button. */
inline static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_BlockFace) inline static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_BlockFace)
{ {
switch (a_BlockFace) switch (a_BlockFace)
@ -97,6 +116,11 @@ public:
UNREACHABLE("Unsupported block face"); UNREACHABLE("Unsupported block face");
} }
/** Converts the block meta of this button into a block face of the neighbor to which the button is attached. */
inline static eBlockFace BlockMetaDataToBlockFace(NIBBLETYPE a_Meta) inline static eBlockFace BlockMetaDataToBlockFace(NIBBLETYPE a_Meta)
{ {
switch (a_Meta & 0x7) switch (a_Meta & 0x7)
@ -115,24 +139,39 @@ public:
} }
} }
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) override
{ {
NIBBLETYPE Meta; auto Meta = a_Chunk.GetMeta(a_RelPos);
a_Chunk.UnboundedRelGetBlockMeta(a_RelX, a_RelY, a_RelZ, Meta); auto SupportRelPos = AddFaceDirection(a_RelPos, BlockMetaDataToBlockFace(Meta), true);
if (!cChunkDef::IsValidHeight(SupportRelPos.y))
{
return false;
}
BLOCKTYPE SupportBlockType;
a_Chunk.UnboundedRelGetBlockType(SupportRelPos, SupportBlockType);
AddFaceDirection(a_RelX, a_RelY, a_RelZ, BlockMetaDataToBlockFace(Meta), true); return cBlockInfo::FullyOccupiesVoxel(SupportBlockType);
BLOCKTYPE BlockIsOn; a_Chunk.UnboundedRelGetBlockType(a_RelX, a_RelY, a_RelZ, BlockIsOn);
return (a_RelY > 0) && (cBlockInfo::FullyOccupiesVoxel(BlockIsOn));
} }
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{ {
UNUSED(a_Meta); UNUSED(a_Meta);
return 0; return 0;
} }
/** Extracts the ON bit from metadata and returns if true if it is set */
/** Extracts the ON bit from metadata. */
static bool IsButtonOn(NIBBLETYPE a_BlockMeta) static bool IsButtonOn(NIBBLETYPE a_BlockMeta)
{ {
return ((a_BlockMeta & 0x8) == 0x8); return ((a_BlockMeta & 0x8) == 0x8);

View File

@ -23,13 +23,13 @@ public:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) override
{ {
if (a_RelY <= 0) if (a_RelPos.y <= 0)
{ {
return false; return false;
} }
BLOCKTYPE Surface = a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ); BLOCKTYPE Surface = a_Chunk.GetBlock(a_RelPos.addedY(-1));
if ((Surface != E_BLOCK_SAND) && (Surface != E_BLOCK_CACTUS)) if ((Surface != E_BLOCK_SAND) && (Surface != E_BLOCK_CACTUS))
{ {
// Cactus can only be placed on sand and itself // Cactus can only be placed on sand and itself
@ -37,22 +37,19 @@ public:
} }
// Check surroundings. Cacti may ONLY be surrounded by non-solid blocks // Check surroundings. Cacti may ONLY be surrounded by non-solid blocks
static const struct static const Vector3i Coords[] =
{ {
int x, z; {-1, 0, 0},
} Coords[] = { 1, 0, 0},
{ { 0, 0, -1},
{-1, 0}, { 0, 0, 1},
{ 1, 0}, };
{ 0, -1},
{ 0, 1},
} ;
for (size_t i = 0; i < ARRAYCOUNT(Coords); i++) for (size_t i = 0; i < ARRAYCOUNT(Coords); i++)
{ {
BLOCKTYPE BlockType; BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta; NIBBLETYPE BlockMeta;
if ( if (
a_Chunk.UnboundedRelGetBlock(a_RelX + Coords[i].x, a_RelY, a_RelZ + Coords[i].z, BlockType, BlockMeta) && a_Chunk.UnboundedRelGetBlock(a_RelPos + Coords[i], BlockType, BlockMeta) &&
( (
cBlockInfo::IsSolid(BlockType) || cBlockInfo::IsSolid(BlockType) ||
(BlockType == E_BLOCK_LAVA) || (BlockType == E_BLOCK_LAVA) ||

View File

@ -20,9 +20,16 @@ public:
virtual bool OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override virtual bool OnUse(
cChunkInterface & a_ChunkInterface,
cWorldInterface & a_WorldInterface,
cPlayer & a_Player,
const Vector3i a_BlockPos,
eBlockFace a_BlockFace,
const Vector3i a_CursorPos
) override
{ {
NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta({a_BlockX, a_BlockY, a_BlockZ}); NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta(a_BlockPos);
if (!a_Player.Feed(2, 0.4)) if (!a_Player.Feed(2, 0.4))
{ {
@ -31,11 +38,11 @@ public:
if (Meta >= 5) if (Meta >= 5)
{ {
a_ChunkInterface.DigBlock(a_WorldInterface, {a_BlockX, a_BlockY, a_BlockZ}); a_ChunkInterface.DigBlock(a_WorldInterface, a_BlockPos);
} }
else else
{ {
a_ChunkInterface.SetBlockMeta({a_BlockX, a_BlockY, a_BlockZ}, Meta + 1); a_ChunkInterface.SetBlockMeta(a_BlockPos, Meta + 1);
} }
return true; return true;
} }

View File

@ -14,14 +14,14 @@
class cBlockCarpetHandler : class cBlockCarpetHandler:
public cBlockHandler public cBlockHandler
{ {
using Super = cBlockHandler; using Super = cBlockHandler;
public: public:
cBlockCarpetHandler(BLOCKTYPE a_BlockType) : cBlockCarpetHandler(BLOCKTYPE a_BlockType):
Super(a_BlockType) Super(a_BlockType)
{ {
} }
@ -31,9 +31,11 @@ public:
virtual bool GetPlacementBlockTypeMeta( virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player, cChunkInterface & a_ChunkInterface,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, cPlayer & a_Player,
int a_CursorX, int a_CursorY, int a_CursorZ, const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override ) override
{ {
@ -46,9 +48,9 @@ public:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) override
{ {
return (a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) != E_BLOCK_AIR); return (a_RelPos.y > 0) && (a_Chunk.GetBlock(a_RelPos.addedY(-1)) != E_BLOCK_AIR);
} }

View File

@ -32,9 +32,16 @@ public:
virtual bool OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override virtual bool OnUse(
cChunkInterface & a_ChunkInterface,
cWorldInterface & a_WorldInterface,
cPlayer & a_Player,
const Vector3i a_BlockPos,
eBlockFace a_BlockFace,
const Vector3i a_CursorPos
) override
{ {
NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta({a_BlockX, a_BlockY, a_BlockZ}); NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta(a_BlockPos);
auto EquippedItem = a_Player.GetEquippedItem(); auto EquippedItem = a_Player.GetEquippedItem();
switch (EquippedItem.m_ItemType) switch (EquippedItem.m_ItemType)
{ {
@ -42,7 +49,7 @@ public:
{ {
if (Meta == 3) if (Meta == 3)
{ {
a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, 0); a_ChunkInterface.SetBlockMeta(a_BlockPos, 0);
// Give new bucket, filled with fluid when the gamemode is not creative: // Give new bucket, filled with fluid when the gamemode is not creative:
if (!a_Player.IsGameModeCreative()) if (!a_Player.IsGameModeCreative())
{ {
@ -55,7 +62,7 @@ public:
{ {
if (Meta < 3) if (Meta < 3)
{ {
a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, 3); a_ChunkInterface.SetBlockMeta(a_BlockPos, 3);
// Give empty bucket back when the gamemode is not creative: // Give empty bucket back when the gamemode is not creative:
if (!a_Player.IsGameModeCreative()) if (!a_Player.IsGameModeCreative())
{ {
@ -68,7 +75,7 @@ public:
{ {
if (Meta > 0) if (Meta > 0)
{ {
a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, --Meta); a_ChunkInterface.SetBlockMeta(a_BlockPos, --Meta);
// Give new potion when the gamemode is not creative: // Give new potion when the gamemode is not creative:
if (!a_Player.IsGameModeCreative()) if (!a_Player.IsGameModeCreative())
{ {
@ -82,8 +89,8 @@ public:
// Refill cauldron with water bottles. // Refill cauldron with water bottles.
if ((Meta < 3) && (EquippedItem.m_ItemDamage == 0)) if ((Meta < 3) && (EquippedItem.m_ItemDamage == 0))
{ {
a_ChunkInterface.SetBlockMeta(Vector3i(a_BlockX, a_BlockY, a_BlockZ), ++Meta); a_ChunkInterface.SetBlockMeta(Vector3i(a_BlockPos), ++Meta);
// Give empty bottle when the gamemode is not creative: // Give back an empty bottle when the gamemode is not creative:
if (!a_Player.IsGameModeCreative()) if (!a_Player.IsGameModeCreative())
{ {
a_Player.ReplaceOneEquippedItemTossRest(cItem(E_ITEM_GLASS_BOTTLE)); a_Player.ReplaceOneEquippedItemTossRest(cItem(E_ITEM_GLASS_BOTTLE));

View File

@ -27,14 +27,16 @@ public:
virtual bool GetPlacementBlockTypeMeta( virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player, cChunkInterface & a_ChunkInterface,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, cPlayer & a_Player,
int a_CursorX, int a_CursorY, int a_CursorZ, const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override ) override
{ {
// Is there a doublechest already next to this block? // Cannot place right next to double-chest:
if (!CanBeAt(a_ChunkInterface, a_BlockX, a_BlockY, a_BlockZ)) if (!CanBeAt(a_ChunkInterface, a_PlacedBlockPos))
{ {
// Yup, cannot form a triple-chest, refuse: // Yup, cannot form a triple-chest, refuse:
return false; return false;
@ -42,13 +44,13 @@ public:
// Try to read double-chest information: // Try to read double-chest information:
cBlockArea Area; cBlockArea Area;
if (!Area.Read(a_ChunkInterface, a_BlockX - 1, a_BlockX + 1, a_BlockY, a_BlockY, a_BlockZ - 1, a_BlockZ + 1)) if (!Area.Read(a_ChunkInterface, a_PlacedBlockPos - Vector3i(1, 0, 1), a_PlacedBlockPos + Vector3i(1, 0, 1)))
{ {
return false; return false;
} }
// Get meta as if this was a single-chest: // Get meta as if this was a single-chest:
if (!Super::GetPlacementBlockTypeMeta(a_ChunkInterface, a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta)) if (!Super::GetPlacementBlockTypeMeta(a_ChunkInterface, a_Player, a_PlacedBlockPos, a_ClickedBlockFace, a_CursorPos, a_BlockType, a_BlockMeta))
{ {
return false; return false;
} }
@ -80,21 +82,20 @@ public:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) override
{ {
int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width; auto BlockPos = a_Chunk.RelativeToAbsolute(a_RelPos);
int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width; return CanBeAt(a_ChunkInterface, BlockPos);
return CanBeAt(a_ChunkInterface, BlockX, a_RelY, BlockZ);
} }
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_BlockX, int a_BlockY, int a_BlockZ) virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_BlockPos)
{ {
cBlockArea Area; cBlockArea Area;
if (!Area.Read(a_ChunkInterface, a_BlockX - 2, a_BlockX + 2, a_BlockY, a_BlockY, a_BlockZ - 2, a_BlockZ + 2)) if (!Area.Read(a_ChunkInterface, a_BlockPos - Vector3i(2, 0, 2), a_BlockPos + Vector3i(2, 0, 2)))
{ {
// Cannot read the surroundings, probably at the edge of loaded chunks. Disallow. // Cannot read the surroundings, probably at the edge of loaded chunks. Disallow.
return false; return false;

View File

@ -22,16 +22,15 @@ public:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) override
{ {
eBlockFace BlockFace = MetaToBlockFace(a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ)); // Check that we're attached to a jungle log block:
AddFaceDirection(a_RelX, a_RelY, a_RelZ, BlockFace, true); eBlockFace BlockFace = MetaToBlockFace(a_Chunk.GetMeta(a_RelPos));
auto LogPos = AddFaceDirection(a_RelPos, BlockFace, true);
BLOCKTYPE BlockType; BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta; NIBBLETYPE BlockMeta;
a_Chunk.UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ, BlockType, BlockMeta); a_Chunk.UnboundedRelGetBlock(LogPos, BlockType, BlockMeta);
return ((BlockType == E_BLOCK_LOG) && ((BlockMeta & 0x03) == E_META_LOG_JUNGLE));
return ((BlockType == E_BLOCK_LOG) && ((BlockMeta & 0x3) == E_META_LOG_JUNGLE));
} }

View File

@ -21,45 +21,114 @@ public:
{ {
} }
virtual bool OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override
virtual bool OnUse(
cChunkInterface & a_ChunkInterface,
cWorldInterface & a_WorldInterface,
cPlayer & a_Player,
const Vector3i a_BlockPos,
eBlockFace a_BlockFace,
const Vector3i a_CursorPos
) override
{ {
NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta({a_BlockX, a_BlockY, a_BlockZ}); // Toggle the 3rd bit (addition / subtraction):
Meta ^= 0x04; // Toggle 3rd (addition / subtraction) bit with XOR NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta(a_BlockPos);
a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta); Meta ^= 0x04;
a_ChunkInterface.SetBlockMeta(a_BlockPos, Meta);
return true; return true;
} }
virtual void OnCancelRightClick(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) override
virtual void OnCancelRightClick(
cChunkInterface & a_ChunkInterface,
cWorldInterface & a_WorldInterface,
cPlayer & a_Player,
const Vector3i a_BlockPos,
eBlockFace a_BlockFace
) override
{ {
UNUSED(a_ChunkInterface); UNUSED(a_ChunkInterface);
a_WorldInterface.SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, a_Player); UNUSED(a_BlockFace);
a_WorldInterface.SendBlockTo(a_BlockPos, a_Player);
} }
virtual bool IsUseable(void) override virtual bool IsUseable(void) override
{ {
return true; return true;
} }
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) override
{ {
return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) != E_BLOCK_AIR)); if (a_RelPos.y <= 0)
{
return false;
}
BLOCKTYPE BelowBlock;
NIBBLETYPE BelowBlockMeta;
a_Chunk.GetBlockTypeMeta(a_RelPos.addedY(-1), BelowBlock, BelowBlockMeta);
if (cBlockInfo::FullyOccupiesVoxel(BelowBlock))
{
return true;
}
else if (cBlockSlabHandler::IsAnySlabType(BelowBlock))
{
// Check if the slab is turned up side down
if ((BelowBlockMeta & 0x08) == 0x08)
{
return true;
}
}
return false;
} }
virtual cItems ConvertToPickups(NIBBLETYPE a_BlockMeta, cBlockEntity * a_BlockEntity, const cEntity * a_Digger, const cItem * a_Tool) override virtual cItems ConvertToPickups(NIBBLETYPE a_BlockMeta, cBlockEntity * a_BlockEntity, const cEntity * a_Digger, const cItem * a_Tool) override
{ {
return cItem(E_ITEM_COMPARATOR, 1, 0); return cItem(E_ITEM_COMPARATOR, 1, 0);
} }
inline static bool IsInSubtractionMode(NIBBLETYPE a_Meta) inline static bool IsInSubtractionMode(NIBBLETYPE a_Meta)
{ {
return ((a_Meta & 0x4) == 0x4); return ((a_Meta & 0x4) == 0x4);
} }
inline static bool IsOn(NIBBLETYPE a_Meta) inline static bool IsOn(NIBBLETYPE a_Meta)
{ {
return ((a_Meta & 0x8) == 0x8); return ((a_Meta & 0x8) == 0x8);
} }
inline static Vector3i GetSideCoordinate(Vector3i a_Position, NIBBLETYPE a_Meta, bool a_bInverse) inline static Vector3i GetSideCoordinate(Vector3i a_Position, NIBBLETYPE a_Meta, bool a_bInverse)
{ {
if (!a_bInverse) if (!a_bInverse)
@ -98,6 +167,10 @@ public:
return a_Position; return a_Position;
} }
inline static Vector3i GetRearCoordinate(Vector3i a_Position, NIBBLETYPE a_Meta) inline static Vector3i GetRearCoordinate(Vector3i a_Position, NIBBLETYPE a_Meta)
{ {
switch (a_Meta) switch (a_Meta)
@ -117,6 +190,10 @@ public:
return a_Position; return a_Position;
} }
inline static Vector3i GetFrontCoordinate(Vector3i a_Position, NIBBLETYPE a_Meta) inline static Vector3i GetFrontCoordinate(Vector3i a_Position, NIBBLETYPE a_Meta)
{ {
switch (a_Meta) switch (a_Meta)
@ -136,6 +213,10 @@ public:
return a_Position; return a_Position;
} }
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{ {
UNUSED(a_Meta); UNUSED(a_Meta);

View File

@ -108,13 +108,15 @@ public:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) override
{ {
return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) == E_BLOCK_FARMLAND)); return ((a_RelPos.y > 0) && (a_Chunk.GetBlock(a_RelPos.addedY(-1)) == E_BLOCK_FARMLAND));
} }
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{ {
UNUSED(a_Meta); UNUSED(a_Meta);

View File

@ -31,14 +31,14 @@ public:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) override
{ {
if (a_RelY <= 0) if (a_RelPos.y <= 0)
{ {
return false; return false;
} }
BLOCKTYPE BelowBlock = a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ); BLOCKTYPE BelowBlock = a_Chunk.GetBlock(a_RelPos.addedY(-1));
switch (BelowBlock) switch (BelowBlock)
{ {
case E_BLOCK_CLAY: case E_BLOCK_CLAY:

View File

@ -40,15 +40,20 @@ void cBlockDoorHandler::OnBroken(cChunkInterface & a_ChunkInterface, cWorldInter
bool cBlockDoorHandler::OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) bool cBlockDoorHandler::OnUse(
cChunkInterface & a_ChunkInterface,
cWorldInterface & a_WorldInterface,
cPlayer & a_Player,
const Vector3i a_BlockPos,
eBlockFace a_BlockFace,
const Vector3i a_CursorPos
)
{ {
UNUSED(a_WorldInterface); UNUSED(a_WorldInterface);
UNUSED(a_BlockFace); UNUSED(a_BlockFace);
UNUSED(a_CursorX); UNUSED(a_CursorPos);
UNUSED(a_CursorY);
UNUSED(a_CursorZ);
switch (a_ChunkInterface.GetBlock({a_BlockX, a_BlockY, a_BlockZ})) switch (a_ChunkInterface.GetBlock(a_BlockPos))
{ {
default: default:
{ {
@ -61,14 +66,14 @@ bool cBlockDoorHandler::OnUse(cChunkInterface & a_ChunkInterface, cWorldInterfac
case E_BLOCK_SPRUCE_DOOR: case E_BLOCK_SPRUCE_DOOR:
case E_BLOCK_OAK_DOOR: case E_BLOCK_OAK_DOOR:
{ {
ChangeDoor(a_ChunkInterface, a_BlockX, a_BlockY, a_BlockZ); ChangeDoor(a_ChunkInterface, a_BlockPos);
a_Player.GetWorld()->BroadcastSoundParticleEffect(EffectID::SFX_RANDOM_WOODEN_DOOR_OPEN, {a_BlockX, a_BlockY, a_BlockZ}, 0, a_Player.GetClientHandle()); a_Player.GetWorld()->BroadcastSoundParticleEffect(EffectID::SFX_RANDOM_WOODEN_DOOR_OPEN, a_BlockPos, 0, a_Player.GetClientHandle());
break; break;
} }
// Prevent iron door from opening on player click // Prevent iron door from opening on player click
case E_BLOCK_IRON_DOOR: case E_BLOCK_IRON_DOOR:
{ {
OnCancelRightClick(a_ChunkInterface, a_WorldInterface, a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); OnCancelRightClick(a_ChunkInterface, a_WorldInterface, a_Player, a_BlockPos, a_BlockFace);
break; break;
} }
} }
@ -80,22 +85,29 @@ bool cBlockDoorHandler::OnUse(cChunkInterface & a_ChunkInterface, cWorldInterfac
void cBlockDoorHandler::OnCancelRightClick(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) void cBlockDoorHandler::OnCancelRightClick(
cChunkInterface & a_ChunkInterface,
cWorldInterface & a_WorldInterface,
cPlayer & a_Player,
const Vector3i a_BlockPos,
eBlockFace a_BlockFace
)
{ {
UNUSED(a_ChunkInterface); UNUSED(a_ChunkInterface);
UNUSED(a_BlockFace);
a_WorldInterface.SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, a_Player); a_WorldInterface.SendBlockTo(a_BlockPos, a_Player);
NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta({a_BlockX, a_BlockY, a_BlockZ}); NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta(a_BlockPos);
if (Meta & 0x8) if (Meta & 0x08)
{ {
// Current block is top of the door // Current block is top of the door, send the bottom part:
a_WorldInterface.SendBlockTo(a_BlockX, a_BlockY - 1, a_BlockZ, a_Player); a_WorldInterface.SendBlockTo(a_BlockPos.addedY(-1), a_Player);
} }
else else
{ {
// Current block is bottom of the door // Current block is bottom of the door, send the top part:
a_WorldInterface.SendBlockTo(a_BlockX, a_BlockY + 1, a_BlockZ, a_Player); a_WorldInterface.SendBlockTo(a_BlockPos.addedY(1), a_Player);
} }
} }

View File

@ -20,37 +20,63 @@ public:
cBlockDoorHandler(BLOCKTYPE a_BlockType); cBlockDoorHandler(BLOCKTYPE a_BlockType);
virtual void OnBroken(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, Vector3i a_BlockPos, BLOCKTYPE a_OldBlockType, NIBBLETYPE a_OldBlockMeta) override; virtual void OnBroken(
virtual bool OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override; cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface,
virtual void OnCancelRightClick(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) override; Vector3i a_BlockPos,
BLOCKTYPE a_OldBlockType, NIBBLETYPE a_OldBlockMeta
) override;
virtual bool OnUse(
cChunkInterface & a_ChunkInterface,
cWorldInterface & a_WorldInterface,
cPlayer & a_Player,
const Vector3i a_BlockPos,
eBlockFace a_BlockFace,
const Vector3i a_CursorPos
) override;
virtual void OnCancelRightClick(
cChunkInterface & a_ChunkInterface,
cWorldInterface & a_WorldInterface,
cPlayer & a_Player,
const Vector3i a_BlockPos,
eBlockFace a_BlockFace
) override;
virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override; virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override;
virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override; virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override;
virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override; virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override;
virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override; virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override;
virtual bool GetPlacementBlockTypeMeta( virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player, cChunkInterface & a_ChunkInterface,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, cPlayer & a_Player,
int a_CursorX, int a_CursorY, int a_CursorZ, const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override ) override
{ {
// If clicking a bottom face, place the door one block lower: // If clicking a bottom face, place the door one block lower:
if (a_BlockFace == BLOCK_FACE_BOTTOM) auto PlacedPos = a_PlacedBlockPos;
if (a_ClickedBlockFace == BLOCK_FACE_BOTTOM)
{ {
a_BlockY--; PlacedPos.y--;
} }
if ( if (
!CanReplaceBlock(a_ChunkInterface.GetBlock({a_BlockX, a_BlockY, a_BlockZ})) || !CanReplaceBlock(a_ChunkInterface.GetBlock(PlacedPos)) ||
!CanReplaceBlock(a_ChunkInterface.GetBlock({a_BlockX, a_BlockY + 1, a_BlockZ})) !CanReplaceBlock(a_ChunkInterface.GetBlock(PlacedPos.addedY(1)))
) )
{ {
return false; return false;
} }
return Super::GetPlacementBlockTypeMeta(a_ChunkInterface, a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta); return Super::GetPlacementBlockTypeMeta(a_ChunkInterface, a_Player, PlacedPos, a_ClickedBlockFace, a_CursorPos, a_BlockType, a_BlockMeta);
} }
virtual cBoundingBox GetPlacementCollisionBox(BLOCKTYPE a_XM, BLOCKTYPE a_XP, BLOCKTYPE a_YM, BLOCKTYPE a_YP, BLOCKTYPE a_ZM, BLOCKTYPE a_ZP) override; virtual cBoundingBox GetPlacementCollisionBox(BLOCKTYPE a_XM, BLOCKTYPE a_XP, BLOCKTYPE a_YM, BLOCKTYPE a_YP, BLOCKTYPE a_ZM, BLOCKTYPE a_ZP) override;
@ -93,11 +119,19 @@ public:
return true; return true;
} }
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) override
{ {
return ((a_RelY > 0) && CanBeOn(a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ), a_Chunk.GetMeta(a_RelX, a_RelY - 1, a_RelZ))); return ((a_RelPos.y > 0) && CanBeOn(a_Chunk.GetBlock(a_RelPos.addedY(-1)), a_Chunk.GetMeta(a_RelPos.addedY(-1))));
} }
/** Returns true if door can be placed on the specified block type. */ /** Returns true if door can be placed on the specified block type. */
static bool CanBeOn(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) static bool CanBeOn(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
{ {
@ -188,9 +222,9 @@ public:
/** Returns true iff the door at the specified coords is open. /** Returns true iff the door at the specified coords is open.
The coords may point to either the top part or the bottom part of the door. */ The coords may point to either the top part or the bottom part of the door. */
static NIBBLETYPE IsOpen(cChunkInterface & a_ChunkInterface, int a_BlockX, int a_BlockY, int a_BlockZ) static NIBBLETYPE IsOpen(cChunkInterface & a_ChunkInterface, const Vector3i a_BlockPos)
{ {
NIBBLETYPE Meta = GetCompleteDoorMeta(a_ChunkInterface, a_BlockX, a_BlockY, a_BlockZ); NIBBLETYPE Meta = GetCompleteDoorMeta(a_ChunkInterface, a_BlockPos);
return ((Meta & 0x04) != 0); return ((Meta & 0x04) != 0);
} }
@ -201,17 +235,17 @@ public:
/** Returns the complete meta composed from the both parts of the door as (TopMeta << 4) | BottomMeta /** Returns the complete meta composed from the both parts of the door as (TopMeta << 4) | BottomMeta
The coords may point to either part of the door. The coords may point to either part of the door.
The returned value has bit 3 (0x08) set iff the coords point to the top part of the door. The returned value has bit 3 (0x08) set iff the coords point to the top part of the door.
Fails gracefully for (invalid) doors on the world's top and bottom. */ Fails silently for (invalid) doors on the world's top and bottom. */
static NIBBLETYPE GetCompleteDoorMeta(cChunkInterface & a_ChunkInterface, int a_BlockX, int a_BlockY, int a_BlockZ) static NIBBLETYPE GetCompleteDoorMeta(cChunkInterface & a_ChunkInterface, const Vector3i a_BlockPos)
{ {
NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta({a_BlockX, a_BlockY, a_BlockZ}); NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta(a_BlockPos);
if ((Meta & 0x08) != 0) if ((Meta & 0x08) != 0)
{ {
// The coords are pointing at the top part of the door // The coords are pointing at the top part of the door
if (a_BlockY > 0) if (a_BlockPos.y > 0)
{ {
NIBBLETYPE DownMeta = a_ChunkInterface.GetBlockMeta({a_BlockX, a_BlockY - 1, a_BlockZ}); NIBBLETYPE DownMeta = a_ChunkInterface.GetBlockMeta(a_BlockPos.addedY(-1));
return static_cast<NIBBLETYPE>((DownMeta & 0x07) | 0x08 | (Meta << 4)); return static_cast<NIBBLETYPE>((DownMeta & 0x07) | 0x08 | (Meta << 4));
} }
// This is the top part of the door at the bottommost layer of the world, there's no bottom: // This is the top part of the door at the bottommost layer of the world, there's no bottom:
@ -220,9 +254,9 @@ public:
else else
{ {
// The coords are pointing at the bottom part of the door // The coords are pointing at the bottom part of the door
if (a_BlockY < cChunkDef::Height - 1) if (a_BlockPos.y < cChunkDef::Height - 1)
{ {
NIBBLETYPE UpMeta = a_ChunkInterface.GetBlockMeta({a_BlockX, a_BlockY + 1, a_BlockZ}); NIBBLETYPE UpMeta = a_ChunkInterface.GetBlockMeta(a_BlockPos.addedY(1));
return static_cast<NIBBLETYPE>(Meta | (UpMeta << 4)); return static_cast<NIBBLETYPE>(Meta | (UpMeta << 4));
} }
// This is the bottom part of the door at the topmost layer of the world, there's no top: // This is the bottom part of the door at the topmost layer of the world, there's no top:
@ -235,15 +269,15 @@ public:
/** Sets the door to the specified state. If the door is already in that state, does nothing. */ /** Sets the door to the specified state. If the door is already in that state, does nothing. */
static void SetOpen(cChunkInterface & a_ChunkInterface, int a_BlockX, int a_BlockY, int a_BlockZ, bool a_Open) static void SetOpen(cChunkInterface & a_ChunkInterface, const Vector3i a_BlockPos, bool a_Open)
{ {
BLOCKTYPE Block = a_ChunkInterface.GetBlock({a_BlockX, a_BlockY, a_BlockZ}); BLOCKTYPE Block = a_ChunkInterface.GetBlock(a_BlockPos);
if (!IsDoorBlockType(Block)) if (!IsDoorBlockType(Block))
{ {
return; return;
} }
NIBBLETYPE Meta = GetCompleteDoorMeta(a_ChunkInterface, a_BlockX, a_BlockY, a_BlockZ); NIBBLETYPE Meta = GetCompleteDoorMeta(a_ChunkInterface, a_BlockPos);
bool IsOpened = ((Meta & 0x04) != 0); bool IsOpened = ((Meta & 0x04) != 0);
if (IsOpened == a_Open) if (IsOpened == a_Open)
{ {
@ -255,14 +289,14 @@ public:
if ((Meta & 0x08) == 0) if ((Meta & 0x08) == 0)
{ {
// The block is the bottom part of the door // The block is the bottom part of the door
a_ChunkInterface.SetBlockMeta({a_BlockX, a_BlockY, a_BlockZ}, NewMeta); a_ChunkInterface.SetBlockMeta(a_BlockPos, NewMeta);
} }
else else
{ {
// The block is the top part of the door, set the meta to the corresponding top part // The block is the top part of the door, set the meta to the corresponding top part
if (a_BlockY > 0) if (a_BlockPos.y > 0)
{ {
a_ChunkInterface.SetBlockMeta({a_BlockX, a_BlockY - 1, a_BlockZ}, NewMeta); a_ChunkInterface.SetBlockMeta(a_BlockPos.addedY(-1), NewMeta);
} }
} }
} }
@ -272,9 +306,9 @@ public:
/** Changes the door at the specified coords from open to close or vice versa */ /** Changes the door at the specified coords from open to close or vice versa */
static void ChangeDoor(cChunkInterface & a_ChunkInterface, int a_BlockX, int a_BlockY, int a_BlockZ) static void ChangeDoor(cChunkInterface & a_ChunkInterface, const Vector3i a_BlockPos)
{ {
SetOpen(a_ChunkInterface, a_BlockX, a_BlockY, a_BlockZ, !IsOpen(a_ChunkInterface, a_BlockX, a_BlockY, a_BlockZ)); SetOpen(a_ChunkInterface, a_BlockPos, !IsOpen(a_ChunkInterface, a_BlockPos));
} }

View File

@ -9,27 +9,49 @@
class cBlockEnchantmentTableHandler : class cBlockEnchantmentTableHandler:
public cBlockHandler public cBlockHandler
{ {
using Super = cBlockHandler;
public: public:
cBlockEnchantmentTableHandler(BLOCKTYPE a_BlockType)
: cBlockHandler(a_BlockType) cBlockEnchantmentTableHandler(BLOCKTYPE a_BlockType):
Super(a_BlockType)
{ {
} }
virtual bool OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override
virtual bool OnUse(
cChunkInterface & a_ChunkInterface,
cWorldInterface & a_WorldInterface,
cPlayer & a_Player,
const Vector3i a_BlockPos,
eBlockFace a_BlockFace,
const Vector3i a_CursorPos
) override
{ {
cWindow * Window = new cEnchantingWindow(a_BlockX, a_BlockY, a_BlockZ); cWindow * Window = new cEnchantingWindow(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z);
a_Player.OpenWindow(*Window); a_Player.OpenWindow(*Window);
return true; return true;
} }
virtual bool IsUseable(void) override virtual bool IsUseable(void) override
{ {
return true; return true;
} }
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{ {
UNUSED(a_Meta); UNUSED(a_Meta);

View File

@ -7,22 +7,26 @@
class cBlockEndPortalFrameHandler : class cBlockEndPortalFrameHandler:
public cMetaRotator<cBlockHandler, 0x03, public cMetaRotator<cBlockHandler, 0x03,
E_META_END_PORTAL_FRAME_ZM,
E_META_END_PORTAL_FRAME_XP,
E_META_END_PORTAL_FRAME_ZP,
E_META_END_PORTAL_FRAME_XM
>
{
public:
cBlockEndPortalFrameHandler(BLOCKTYPE a_BlockType):
cMetaRotator<cBlockHandler, 0x03,
E_META_END_PORTAL_FRAME_ZM, E_META_END_PORTAL_FRAME_ZM,
E_META_END_PORTAL_FRAME_XP, E_META_END_PORTAL_FRAME_XP,
E_META_END_PORTAL_FRAME_ZP, E_META_END_PORTAL_FRAME_ZP,
E_META_END_PORTAL_FRAME_XM E_META_END_PORTAL_FRAME_XM
>(a_BlockType) >
{
using Super = cMetaRotator<
cBlockHandler, 0x03,
E_META_END_PORTAL_FRAME_ZM,
E_META_END_PORTAL_FRAME_XP,
E_META_END_PORTAL_FRAME_ZP,
E_META_END_PORTAL_FRAME_XM
>;
public:
cBlockEndPortalFrameHandler(BLOCKTYPE a_BlockType):
Super(a_BlockType)
{ {
} }
@ -31,9 +35,11 @@ public:
virtual bool GetPlacementBlockTypeMeta( virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player, cChunkInterface & a_ChunkInterface,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, cPlayer & a_Player,
int a_CursorX, int a_CursorY, int a_CursorZ, const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override ) override
{ {

View File

@ -28,9 +28,16 @@ public:
virtual bool OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override virtual bool OnUse(
cChunkInterface & a_ChunkInterface,
cWorldInterface & a_WorldInterface,
cPlayer & a_Player,
const Vector3i a_BlockPos,
eBlockFace a_BlockFace,
const Vector3i a_CursorPos
) override
{ {
return a_ChunkInterface.UseBlockEntity(&a_Player, a_BlockX, a_BlockY, a_BlockZ); return a_ChunkInterface.UseBlockEntity(&a_Player, a_BlockPos.x, a_BlockPos.y, a_BlockPos.z);
} }

View File

@ -10,21 +10,28 @@
class cBlockFenceHandler : class cBlockFenceHandler:
public cBlockHandler public cBlockHandler
{ {
using Super = cBlockHandler;
public: public:
// These are the min and max coordinates (X and Z) for a straight fence. // These are the min and max coordinates (X and Z) for a straight fence.
// 0.4 and 0.6 are really just guesses, but they seem pretty good. // 0.4 and 0.6 are really just guesses, but they seem pretty good.
// (0.4 to 0.6 is a fence that's 0.2 wide, down the center of the block) // (0.4 to 0.6 is a fence that's 0.2 wide, down the center of the block)
const double MIN_COORD = 0.4; const double MIN_COORD = 0.4;
const double MAX_COORD = 0.6; const double MAX_COORD = 0.6;
cBlockFenceHandler(BLOCKTYPE a_BlockType) cBlockFenceHandler(BLOCKTYPE a_BlockType):
: cBlockHandler(a_BlockType) Super(a_BlockType)
{ {
} }
virtual cBoundingBox GetPlacementCollisionBox(BLOCKTYPE a_XM, BLOCKTYPE a_XP, BLOCKTYPE a_YM, BLOCKTYPE a_YP, BLOCKTYPE a_ZM, BLOCKTYPE a_ZP) override virtual cBoundingBox GetPlacementCollisionBox(BLOCKTYPE a_XM, BLOCKTYPE a_XP, BLOCKTYPE a_YM, BLOCKTYPE a_YP, BLOCKTYPE a_ZM, BLOCKTYPE a_ZP) override
{ {
bool XMSolid = cBlockInfo::IsSolid(a_XM); bool XMSolid = cBlockInfo::IsSolid(a_XM);
@ -76,9 +83,20 @@ public:
return PlacementBox; return PlacementBox;
} }
virtual bool OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override
virtual bool OnUse(
cChunkInterface & a_ChunkInterface,
cWorldInterface & a_WorldInterface,
cPlayer & a_Player,
const Vector3i a_BlockPos,
eBlockFace a_BlockFace,
const Vector3i a_CursorPos
) override
{ {
auto LeashKnot = cLeashKnot::FindKnotAtPos(*a_Player.GetWorld(), { a_BlockX, a_BlockY, a_BlockZ }); auto LeashKnot = cLeashKnot::FindKnotAtPos(*a_Player.GetWorld(), a_BlockPos);
auto KnotAlreadyExists = (LeashKnot != nullptr); auto KnotAlreadyExists = (LeashKnot != nullptr);
if (KnotAlreadyExists) if (KnotAlreadyExists)
@ -89,7 +107,7 @@ public:
// New knot? needs to init and produce sound effect // New knot? needs to init and produce sound effect
else else
{ {
auto NewLeashKnot = cpp14::make_unique<cLeashKnot>(a_BlockFace, Vector3i{a_BlockX, a_BlockY, a_BlockZ}); auto NewLeashKnot = cpp14::make_unique<cLeashKnot>(a_BlockFace, a_BlockPos);
auto NewLeashKnotPtr = NewLeashKnot.get(); auto NewLeashKnotPtr = NewLeashKnot.get();
NewLeashKnotPtr->TiePlayersLeashedMobs(a_Player, KnotAlreadyExists); NewLeashKnotPtr->TiePlayersLeashedMobs(a_Player, KnotAlreadyExists);
@ -112,11 +130,25 @@ public:
return true; return true;
} }
virtual void OnCancelRightClick(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) override
virtual void OnCancelRightClick(
cChunkInterface & a_ChunkInterface,
cWorldInterface & a_WorldInterface,
cPlayer & a_Player,
const Vector3i a_BlockPos,
eBlockFace a_BlockFace
) override
{ {
a_WorldInterface.SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, a_Player); a_WorldInterface.SendBlockTo(a_BlockPos, a_Player);
} }
virtual bool IsUseable(void) override virtual bool IsUseable(void) override
{ {
return true; return true;

View File

@ -20,36 +20,65 @@ public:
{ {
} }
virtual bool OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override
virtual bool OnUse(
cChunkInterface & a_ChunkInterface,
cWorldInterface & a_WorldInterface,
cPlayer & a_Player,
const Vector3i a_BlockPos,
eBlockFace a_BlockFace,
const Vector3i a_CursorPos
) override
{ {
NIBBLETYPE OldMetaData = a_ChunkInterface.GetBlockMeta({a_BlockX, a_BlockY, a_BlockZ}); NIBBLETYPE OldMetaData = a_ChunkInterface.GetBlockMeta(a_BlockPos);
NIBBLETYPE NewMetaData = YawToMetaData(a_Player.GetYaw()); NIBBLETYPE NewMetaData = YawToMetaData(a_Player.GetYaw());
OldMetaData ^= 4; // Toggle the gate OldMetaData ^= 4; // Toggle the gate
if ((OldMetaData & 1) == (NewMetaData & 1)) if ((OldMetaData & 1) == (NewMetaData & 1))
{ {
// Standing in front of the gate - apply new direction // Standing in front of the gate - apply new direction
a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, (OldMetaData & 4) | (NewMetaData & 3)); a_ChunkInterface.SetBlockMeta(a_BlockPos, (OldMetaData & 4) | (NewMetaData & 3));
} }
else else
{ {
// Standing aside - use last direction // Standing aside - use last direction
a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, OldMetaData); a_ChunkInterface.SetBlockMeta(a_BlockPos, OldMetaData);
} }
a_Player.GetWorld()->BroadcastSoundParticleEffect(EffectID::SFX_RANDOM_FENCE_GATE_OPEN, {a_BlockX, a_BlockY, a_BlockZ}, 0, a_Player.GetClientHandle()); a_Player.GetWorld()->BroadcastSoundParticleEffect(EffectID::SFX_RANDOM_FENCE_GATE_OPEN, a_BlockPos, 0, a_Player.GetClientHandle());
return true; return true;
} }
virtual void OnCancelRightClick(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) override
virtual void OnCancelRightClick(
cChunkInterface & a_ChunkInterface,
cWorldInterface & a_WorldInterface,
cPlayer & a_Player,
const Vector3i a_BlockPos,
eBlockFace a_BlockFace
) override
{ {
a_WorldInterface.SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, a_Player); a_WorldInterface.SendBlockTo(a_BlockPos, a_Player);
} }
virtual bool IsUseable(void) override virtual bool IsUseable(void) override
{ {
return true; return true;
} }
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{ {
UNUSED(a_Meta); UNUSED(a_Meta);

View File

@ -7,12 +7,15 @@
class cBlockFlowerHandler : class cBlockFlowerHandler:
public cBlockHandler public cBlockHandler
{ {
using Super = cBlockHandler;
public: public:
cBlockFlowerHandler(BLOCKTYPE a_BlockType)
: cBlockHandler(a_BlockType) cBlockFlowerHandler(BLOCKTYPE a_BlockType):
Super(a_BlockType)
{ {
} }
@ -30,11 +33,15 @@ public:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) override
{ {
return (a_RelY > 0) && IsBlockTypeOfDirt(a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)); return ((a_RelPos.y > 0) && IsBlockTypeOfDirt(a_Chunk.GetBlock(a_RelPos.addedY(-1))));
} }
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{ {
UNUSED(a_Meta); UNUSED(a_Meta);

View File

@ -90,30 +90,31 @@ public:
// Y Coord out of range // Y Coord out of range
continue; continue;
} }
auto chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(Pos); auto Chunk = a_Chunk.GetRelNeighborChunkAdjustCoords(Pos);
if (chunk == nullptr) if (Chunk == nullptr)
{ {
// Unloaded chunk // Unloaded chunk
continue; continue;
} }
chunk->GetBlockTypeMeta(Pos, DestBlock, DestMeta); Chunk->GetBlockTypeMeta(Pos, DestBlock, DestMeta);
if ((DestBlock != E_BLOCK_DIRT) || (DestMeta != E_META_DIRT_NORMAL)) if ((DestBlock != E_BLOCK_DIRT) || (DestMeta != E_META_DIRT_NORMAL))
{ {
// Not a regular dirt block // Not a regular dirt block
continue; continue;
} }
BLOCKTYPE Above = chunk->GetBlock(AbovePos); auto AboveDestPos = Pos.addedY(1);
NIBBLETYPE light = std::max(chunk->GetBlockLight(AbovePos), chunk->GetTimeAlteredLight(chunk->GetSkyLight(AbovePos))); auto AboveBlockType = Chunk->GetBlock(AboveDestPos);
if ((light > 4) && auto Light = std::max(Chunk->GetBlockLight(AboveDestPos), Chunk->GetTimeAlteredLight(Chunk->GetSkyLight(AboveDestPos)));
cBlockInfo::IsTransparent(Above) && if ((Light > 4) &&
(!IsBlockLava(Above)) && cBlockInfo::IsTransparent(AboveBlockType) &&
(!IsBlockWaterOrIce(Above)) (!IsBlockLava(AboveBlockType)) &&
(!IsBlockWaterOrIce(AboveBlockType))
) )
{ {
auto absPos = chunk->RelativeToAbsolute(Pos); auto AbsPos = Chunk->RelativeToAbsolute(Pos);
if (!cRoot::Get()->GetPluginManager()->CallHookBlockSpread(*chunk->GetWorld(), absPos.x, absPos.y, absPos.z, ssGrassSpread)) if (!cRoot::Get()->GetPluginManager()->CallHookBlockSpread(*Chunk->GetWorld(), AbsPos.x, AbsPos.y, AbsPos.z, ssGrassSpread))
{ {
chunk->FastSetBlock(Pos, E_BLOCK_GRASS, 0); Chunk->FastSetBlock(Pos, E_BLOCK_GRASS, 0);
} }
} }
} // for i - repeat twice } // for i - repeat twice

View File

@ -406,8 +406,9 @@ cBlockHandler::cBlockHandler(BLOCKTYPE a_BlockType)
bool cBlockHandler::GetPlacementBlockTypeMeta( bool cBlockHandler::GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player, cChunkInterface & a_ChunkInterface, cPlayer & a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, const Vector3i a_ClickedBlockPos,
int a_CursorX, int a_CursorY, int a_CursorZ, eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) )
{ {
@ -437,7 +438,7 @@ void cBlockHandler::OnUpdate(
void cBlockHandler::OnPlacedByPlayer(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, const sSetBlock & a_BlockChange) void cBlockHandler::OnPlacedByPlayer(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, const sSetBlock & a_BlockChange)
{ {
OnPlaced(a_ChunkInterface, a_WorldInterface, a_BlockChange.GetPos(), a_BlockChange.m_BlockType, a_BlockChange.m_BlockMeta); OnPlaced(a_ChunkInterface, a_WorldInterface, a_BlockChange.GetAbsolutePos(), a_BlockChange.m_BlockType, a_BlockChange.m_BlockMeta);
} }
@ -509,7 +510,7 @@ cItems cBlockHandler::ConvertToPickups(
bool cBlockHandler::CanBeAt(cChunkInterface & a_ChunkInterface, int a_BlockX, int a_BlockY, int a_BlockZ, const cChunk & a_Chunk) bool cBlockHandler::CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk)
{ {
return true; return true;
} }
@ -554,10 +555,10 @@ bool cBlockHandler::DoesDropOnUnsuitable(void)
/* default functionality: only test for height, since we assume full voxels with varying height */ bool cBlockHandler::IsInsideBlock(const Vector3d a_RelPosition, const NIBBLETYPE a_BlockMeta)
bool cBlockHandler::IsInsideBlock(Vector3d a_Position, const BLOCKTYPE a_BlockType, const NIBBLETYPE a_BlockMeta)
{ {
return a_Position.y < cBlockInfo::GetBlockHeight(a_BlockType); // Default functionality: Test the height, since we assume full voxels with varying height
return (a_RelPosition.y < cBlockInfo::GetBlockHeight(m_BlockType));
} }
@ -584,7 +585,7 @@ void cBlockHandler::Check(
cChunk & a_Chunk cChunk & a_Chunk
) )
{ {
if (!CanBeAt(a_ChunkInterface, a_RelPos.x, a_RelPos.y, a_RelPos.z, a_Chunk)) if (!CanBeAt(a_ChunkInterface, a_RelPos, a_Chunk))
{ {
if (DoesDropOnUnsuitable()) if (DoesDropOnUnsuitable())
{ {

View File

@ -44,19 +44,28 @@ public:
blocktype of the minus-X neighbor, the positive-X neighbor, etc. */ blocktype of the minus-X neighbor, the positive-X neighbor, etc. */
virtual cBoundingBox GetPlacementCollisionBox(BLOCKTYPE a_XM, BLOCKTYPE a_XP, BLOCKTYPE a_YM, BLOCKTYPE a_YP, BLOCKTYPE a_ZM, BLOCKTYPE a_ZP); virtual cBoundingBox GetPlacementCollisionBox(BLOCKTYPE a_XM, BLOCKTYPE a_XP, BLOCKTYPE a_YM, BLOCKTYPE a_YP, BLOCKTYPE a_ZM, BLOCKTYPE a_ZP);
/** Called before a block is placed into a world. /** Called before a block is placed into a world by player, by cItemHandler::GetPlacementBlockTypeMeta().
The handler should return true to allow placement, false to refuse. The handler should return true to allow placement, false to refuse.
Also, the handler should set a_BlockType and a_BlockMeta to correct values for the newly placed block. a_PlacedBlockPos is the coords of the block being placed
Called by cItemHandler::GetPlacementBlockTypeMeta() if the item is a block */ a_ClickedBlockFace is the face of the neighbor block clicked by the client to place this block.
a_CursorPos is the position of the cursor within the neighbor's face
The descendant handler should set a_BlockType and a_BlockMeta to correct values for the newly placed block.
The default handler uses the stored block type and meta copied from the lowest 4 bits of the player's equipped item's damage value. */
virtual bool GetPlacementBlockTypeMeta( virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player, cChunkInterface & a_ChunkInterface,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, cPlayer & a_Player,
int a_CursorX, int a_CursorY, int a_CursorZ, const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
); );
/** Called by cWorld::SetBlock() after the block has been set */ /** Called by cWorld::SetBlock() after the block has been set */
virtual void OnPlaced(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, Vector3i a_BlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); virtual void OnPlaced(
cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface,
Vector3i a_BlockPos,
BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta
);
/** Called by cPlayer::PlaceBlocks() for each block after it has been set to the world. Called after OnPlaced(). */ /** Called by cPlayer::PlaceBlocks() for each block after it has been set to the world. Called after OnPlaced(). */
virtual void OnPlacedByPlayer( virtual void OnPlacedByPlayer(
@ -118,18 +127,37 @@ public:
cChunkInterface & a_ChunkInterface, cChunkInterface & a_ChunkInterface,
cWorldInterface & a_WorldInterface, cWorldInterface & a_WorldInterface,
cPlayer & a_Player, cPlayer & a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ const Vector3i a_BlockPos
) )
{ {
} }
/** Called if the user right clicks the block and the block is useable /** Called when the user right clicks the block and the block is useable.
returns true if the use was successful, return false to use the block as a "normal" block */ a_CursorPos is the cursor position within the clicked block face.
virtual bool OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) { return false; } Returns true if the use was successful, return false to use the block as a "normal" block */
virtual bool OnUse(
cChunkInterface & a_ChunkInterface,
cWorldInterface & a_WorldInterface,
cPlayer & a_Player,
const Vector3i a_BlockPos,
eBlockFace a_BlockFace,
const Vector3i a_CursorPos
)
{
return false;
}
/** Called when a right click to this block is cancelled. /** Called when a right click to this block is cancelled.
It forces the server to send the real state of a block to the client to prevent client assuming the operation is successfull */ Descendants should force the server to send the real state of a block to the client to prevent client assuming the operation was successfull. */
virtual void OnCancelRightClick(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) {} virtual void OnCancelRightClick(
cChunkInterface & a_ChunkInterface,
cWorldInterface & a_WorldInterface,
cPlayer & a_Player,
const Vector3i a_BlockPos,
eBlockFace a_BlockFace
)
{
}
/** Returns the pickups that would result if the block was mined by a_Digger using a_Tool. /** Returns the pickups that would result if the block was mined by a_Digger using a_Tool.
Doesn't do any actual block change / mining, only calculates the pickups. Doesn't do any actual block change / mining, only calculates the pickups.
@ -145,41 +173,37 @@ public:
); );
/** Checks if the block can stay at the specified relative coords in the chunk */ /** Checks if the block can stay at the specified relative coords in the chunk */
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk); virtual bool CanBeAt(
cChunkInterface & a_ChunkInterface,
const Vector3i a_RelPos,
const cChunk & a_Chunk
);
/** Checks whether the block has an effect on growing the plant */ /** Checks whether the block has an effect on growing the plant */
virtual bool CanSustainPlant(BLOCKTYPE a_Plant) { return false; } virtual bool CanSustainPlant(BLOCKTYPE a_Plant) { return false; }
/** Checks if the block can be placed at this point.
Default: CanBeAt(...)
NOTE: This call doesn't actually place the block
*/
// virtual bool CanBePlacedAt(cWorld * a_World, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir);
/** Called to check whether this block supports a rclk action. /** Called to check whether this block supports a rclk action.
If it returns true, OnUse() is called */ If it returns true, OnUse() is called */
virtual bool IsUseable(void); virtual bool IsUseable(void);
/** Indicates whether the client will click through this block. /** Indicates whether the client will click through this block.
For example digging a fire will hit the block below the fire so fire is clicked through For example digging a fire will hit the block below the fire so fire is clicked through. */
*/
virtual bool IsClickedThrough(void); virtual bool IsClickedThrough(void);
/** Checks if the player can build "inside" this block. /** Checks if the player can build "inside" this block.
For example blocks placed "on" snow will be placed at the same position. So: Snow ignores Build collision For example blocks placed "on" snow will be placed at the same position. So: Snow ignores Build collision
@param a_Pos Position of the block @param a_Pos Position of the block
@param a_Player Player trying to build on the block @param a_Player Player trying to build on the block
@param a_Meta Meta value of the block currently at a_Pos @param a_Meta Meta value of the block currently at a_Pos */
*/ virtual bool DoesIgnoreBuildCollision(cChunkInterface & ChunkInterface, const Vector3i a_Pos, cPlayer & a_Player, NIBBLETYPE a_Meta);
virtual bool DoesIgnoreBuildCollision(cChunkInterface & ChunkInterface, Vector3i a_Pos, cPlayer & a_Player, NIBBLETYPE a_Meta);
/** Returns if this block drops if it gets destroyed by an unsuitable situation. /** Returns if this block drops if it gets destroyed by an unsuitable situation.
Default: true */ Default: true */
virtual bool DoesDropOnUnsuitable(void); virtual bool DoesDropOnUnsuitable(void);
/** Tests if a_Position is inside the block where a_Position is relative to the origin of the block /** Tests if a_RelPosition is inside the block, where a_RelPosition is relative to the origin of the block.
Note that this is considered from a "top-down" perspective i.e. empty spaces on the bottom of a block don't matter */ Coords in a_RelPosition are guaranteed to be in the [0..1] range. */
virtual bool IsInsideBlock(Vector3d a_Position, const BLOCKTYPE a_BlockType, const NIBBLETYPE a_BlockMeta); virtual bool IsInsideBlock(const Vector3d a_RelPosition, const NIBBLETYPE a_BlockMeta);
/** Called when one of the neighbors gets set; equivalent to MC block update. /** Called when one of the neighbors gets set; equivalent to MC block update.
By default drops (DropBlockAsPickup() / SetBlock()) if the position is no longer suitable (CanBeAt(), DoesDropOnUnsuitable()), By default drops (DropBlockAsPickup() / SetBlock()) if the position is no longer suitable (CanBeAt(), DoesDropOnUnsuitable()),

View File

@ -24,16 +24,18 @@ public:
virtual bool GetPlacementBlockTypeMeta( virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player, cChunkInterface & a_ChunkInterface,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, cPlayer & a_Player,
int a_CursorX, int a_CursorY, int a_CursorZ, const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override ) override
{ {
a_BlockType = m_BlockType; a_BlockType = m_BlockType;
// Convert the blockface into meta: // Convert the blockface into meta:
switch (a_BlockFace) switch (a_ClickedBlockFace)
{ {
case BLOCK_FACE_BOTTOM: a_BlockMeta = E_META_HOPPER_FACING_YM; break; case BLOCK_FACE_BOTTOM: a_BlockMeta = E_META_HOPPER_FACING_YM; break;
case BLOCK_FACE_TOP: a_BlockMeta = E_META_HOPPER_FACING_YM; break; case BLOCK_FACE_TOP: a_BlockMeta = E_META_HOPPER_FACING_YM; break;

View File

@ -8,7 +8,7 @@
class cBlockLadderHandler : class cBlockLadderHandler:
public cClearMetaOnDrop<cMetaRotator<cBlockHandler, 0x07, 0x02, 0x05, 0x03, 0x04> > public cClearMetaOnDrop<cMetaRotator<cBlockHandler, 0x07, 0x02, 0x05, 0x03, 0x04> >
{ {
using Super = cClearMetaOnDrop<cMetaRotator<cBlockHandler, 0x07, 0x02, 0x05, 0x03, 0x04>>; using Super = cClearMetaOnDrop<cMetaRotator<cBlockHandler, 0x07, 0x02, 0x05, 0x03, 0x04>>;
@ -25,24 +25,26 @@ public:
virtual bool GetPlacementBlockTypeMeta( virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player, cChunkInterface & a_ChunkInterface,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, cPlayer & a_Player,
int a_CursorX, int a_CursorY, int a_CursorZ, const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override ) override
{ {
if (!LadderCanBePlacedAt(a_ChunkInterface, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace)) // Try finding a suitable neighbor block face for the ladder; start with the given one.
if (!LadderCanBePlacedAt(a_ChunkInterface, a_PlacedBlockPos, a_ClickedBlockFace))
{ {
a_BlockFace = FindSuitableBlockFace(a_ChunkInterface, a_BlockX, a_BlockY, a_BlockZ); a_ClickedBlockFace = FindSuitableBlockFace(a_ChunkInterface, a_PlacedBlockPos);
if (a_ClickedBlockFace == BLOCK_FACE_NONE)
if (a_BlockFace == BLOCK_FACE_BOTTOM)
{ {
return false; return false;
} }
} }
a_BlockType = m_BlockType; a_BlockType = m_BlockType;
a_BlockMeta = DirectionToMetaData(a_BlockFace); a_BlockMeta = BlockFaceToMetaData(a_ClickedBlockFace);
return true; return true;
} }
@ -50,9 +52,10 @@ public:
static NIBBLETYPE DirectionToMetaData(eBlockFace a_Direction) /** Converts the block face of the neighbor to which the ladder is attached to the ladder block's meta. */
static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_NeighborBlockFace)
{ {
switch (a_Direction) switch (a_NeighborBlockFace)
{ {
case BLOCK_FACE_ZM: return 0x2; case BLOCK_FACE_ZM: return 0x2;
case BLOCK_FACE_ZP: return 0x3; case BLOCK_FACE_ZP: return 0x3;
@ -65,14 +68,15 @@ public:
return 0x2; return 0x2;
} }
} }
UNREACHABLE("Unsupported block face"); UNREACHABLE("Unsupported neighbor block face");
} }
static eBlockFace MetaDataToDirection(NIBBLETYPE a_MetaData) /** Converts the ladder block's meta to the block face of the neighbor to which the ladder is attached. */
static eBlockFace MetaDataToBlockFace(NIBBLETYPE a_MetaData)
{ {
switch (a_MetaData) switch (a_MetaData)
{ {
@ -88,47 +92,51 @@ public:
/** Finds a suitable Direction for the Ladder. Returns BLOCK_FACE_BOTTOM on failure */ /** Finds a suitable block face value for the Ladder.
static eBlockFace FindSuitableBlockFace(cChunkInterface & a_ChunkInterface, int a_BlockX, int a_BlockY, int a_BlockZ) The returned value is the block face of the neighbor of the specified block to which a ladder at a_LadderPos can be attached.
Returns BLOCK_FACE_NONE if no valid location is found */
static eBlockFace FindSuitableBlockFace(cChunkInterface & a_ChunkInterface, const Vector3i a_LadderPos)
{ {
for (int FaceInt = BLOCK_FACE_ZM; FaceInt <= BLOCK_FACE_XP; FaceInt++) for (int FaceInt = BLOCK_FACE_ZM; FaceInt <= BLOCK_FACE_XP; FaceInt++)
{ {
eBlockFace Face = static_cast<eBlockFace>(FaceInt); eBlockFace Face = static_cast<eBlockFace>(FaceInt);
if (LadderCanBePlacedAt(a_ChunkInterface, a_BlockX, a_BlockY, a_BlockZ, Face)) if (LadderCanBePlacedAt(a_ChunkInterface, a_LadderPos, Face))
{ {
return Face; return Face;
} }
} }
return BLOCK_FACE_BOTTOM; return BLOCK_FACE_NONE;
} }
static bool LadderCanBePlacedAt(cChunkInterface & a_ChunkInterface, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) /** Returns true if the ladder in the specified position will be supported by its neghbor through the specified neighbor's blockface. */
static bool LadderCanBePlacedAt(cChunkInterface & a_ChunkInterface, const Vector3i a_LadderPos, eBlockFace a_NeighborBlockFace)
{ {
if ((a_BlockFace == BLOCK_FACE_BOTTOM) || (a_BlockFace == BLOCK_FACE_TOP)) if (
(a_NeighborBlockFace == BLOCK_FACE_NONE) ||
(a_NeighborBlockFace == BLOCK_FACE_BOTTOM) ||
(a_NeighborBlockFace == BLOCK_FACE_TOP)
)
{ {
return false; return false;
} }
AddFaceDirection( a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, true); auto NeighborPos = AddFaceDirection(a_LadderPos, a_NeighborBlockFace, true);
return cBlockInfo::IsSolid(a_ChunkInterface.GetBlock(NeighborPos));
return cBlockInfo::IsSolid(a_ChunkInterface.GetBlock({a_BlockX, a_BlockY, a_BlockZ}));
} }
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) override
{ {
// TODO: Use AdjustCoordsByMeta(), then cChunk::UnboundedRelGetBlock() and finally some comparison auto NeighborBlockFace = MetaDataToBlockFace(a_Chunk.GetMeta(a_RelPos));
eBlockFace BlockFace = MetaDataToDirection(a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ)); auto LadderAbsPos = a_Chunk.RelativeToAbsolute(a_RelPos);
int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width; return LadderCanBePlacedAt(a_ChunkInterface, LadderAbsPos, NeighborBlockFace);
int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width;
return LadderCanBePlacedAt(a_ChunkInterface, BlockX, a_RelY, BlockZ, BlockFace);
} }

View File

@ -6,14 +6,14 @@
#include "BlockSlab.h" #include "BlockSlab.h"
class cBlockLeverHandler : class cBlockLeverHandler:
public cMetaRotator<cBlockHandler, 0x07, 0x04, 0x01, 0x03, 0x02, false> public cMetaRotator<cBlockHandler, 0x07, 0x04, 0x01, 0x03, 0x02, false>
{ {
using Super = cMetaRotator<cBlockHandler, 0x07, 0x04, 0x01, 0x03, 0x02, false>; using Super = cMetaRotator<cBlockHandler, 0x07, 0x04, 0x01, 0x03, 0x02, false>;
public: public:
cBlockLeverHandler(BLOCKTYPE a_BlockType) : cBlockLeverHandler(BLOCKTYPE a_BlockType):
Super(a_BlockType) Super(a_BlockType)
{ {
} }
@ -22,15 +22,21 @@ public:
virtual bool OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override virtual bool OnUse(
cChunkInterface & a_ChunkInterface,
cWorldInterface & a_WorldInterface,
cPlayer & a_Player,
const Vector3i a_BlockPos,
eBlockFace a_BlockFace,
const Vector3i a_CursorPos
) override
{ {
Vector3i Coords(a_BlockX, a_BlockY, a_BlockZ);
// Flip the ON bit on / off using the XOR bitwise operation // Flip the ON bit on / off using the XOR bitwise operation
NIBBLETYPE Meta = (a_ChunkInterface.GetBlockMeta(Coords) ^ 0x08); NIBBLETYPE Meta = (a_ChunkInterface.GetBlockMeta(a_BlockPos) ^ 0x08);
a_ChunkInterface.SetBlockMeta(Coords.x, Coords.y, Coords.z, Meta); a_ChunkInterface.SetBlockMeta(a_BlockPos, Meta);
a_WorldInterface.WakeUpSimulators(Coords); a_WorldInterface.WakeUpSimulators(a_BlockPos);
a_WorldInterface.GetBroadcastManager().BroadcastSoundEffect("block.lever.click", Vector3d(Coords), 0.5f, (Meta & 0x08) ? 0.6f : 0.5f); a_WorldInterface.GetBroadcastManager().BroadcastSoundEffect("block.lever.click", a_BlockPos, 0.5f, (Meta & 0x08) ? 0.6f : 0.5f);
return true; return true;
} }
@ -58,14 +64,16 @@ public:
virtual bool GetPlacementBlockTypeMeta( virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player, cChunkInterface & a_ChunkInterface,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, cPlayer & a_Player,
int a_CursorX, int a_CursorY, int a_CursorZ, const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override ) override
{ {
a_BlockType = m_BlockType; a_BlockType = m_BlockType;
a_BlockMeta = LeverDirectionToMetaData(a_BlockFace); a_BlockMeta = LeverDirectionToMetaData(a_ClickedBlockFace);
return true; return true;
} }
@ -73,6 +81,7 @@ public:
/** Converts the block face of the neighbor to which the lever is attached to the lever block's meta. */
inline static NIBBLETYPE LeverDirectionToMetaData(eBlockFace a_Dir) inline static NIBBLETYPE LeverDirectionToMetaData(eBlockFace a_Dir)
{ {
// Determine lever direction: // Determine lever direction:
@ -93,6 +102,7 @@ public:
/** Converts the leve block's meta to the block face of the neighbor to which the lever is attached. */
inline static eBlockFace BlockMetaDataToBlockFace(NIBBLETYPE a_Meta) inline static eBlockFace BlockMetaDataToBlockFace(NIBBLETYPE a_Meta)
{ {
switch (a_Meta & 0x7) switch (a_Meta & 0x7)
@ -117,34 +127,33 @@ public:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) override
{ {
NIBBLETYPE Meta = a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ); // Find the type of block the lever is attached to:
auto Meta = a_Chunk.GetMeta(a_RelPos);
eBlockFace Face = BlockMetaDataToBlockFace(Meta); auto NeighborFace = BlockMetaDataToBlockFace(Meta);
auto NeighborPos = AddFaceDirection(a_RelPos, NeighborFace, true);
AddFaceDirection(a_RelX, a_RelY, a_RelZ, Face, true); if (!cChunkDef::IsValidHeight(NeighborPos.y))
{
if ((a_RelY < 0) || (a_RelY >= cChunkDef::Height -1)) return false;
}
BLOCKTYPE NeighborBlockType;
if (!a_Chunk.UnboundedRelGetBlock(NeighborPos, NeighborBlockType, Meta))
{ {
return false; return false;
} }
BLOCKTYPE BlockIsOn; // Allow any full block or the "good" side of a half-slab:
a_Chunk.UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ, BlockIsOn, Meta); if (cBlockInfo::FullyOccupiesVoxel(NeighborBlockType))
if (cBlockInfo::FullyOccupiesVoxel(BlockIsOn))
{ {
return true; return true;
} }
else if (cBlockSlabHandler::IsAnySlabType(BlockIsOn)) else if (cBlockSlabHandler::IsAnySlabType(NeighborBlockType))
{ {
// Check if the slab is turned up side down return (
if (((Meta & 0x08) == 0x08) && (Face == BLOCK_FACE_TOP)) (((Meta & 0x08) == 0x08) && (NeighborFace == BLOCK_FACE_TOP)) ||
{ (((Meta & 0x08) == 0) && (NeighborFace == BLOCK_FACE_BOTTOM))
return true; );
}
} }
return false; return false;

View File

@ -19,6 +19,10 @@ public:
{ {
} }
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{ {
UNUSED(a_Meta); UNUSED(a_Meta);
@ -26,15 +30,19 @@ public:
} }
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) override
{ {
if ((a_RelY < 1) || (a_RelY >= cChunkDef::Height)) auto UnderPos = a_RelPos.addedY(-1);
if (!cChunkDef::IsValidHeight(UnderPos.y))
{ {
return false; return false;
} }
BLOCKTYPE UnderType; BLOCKTYPE UnderType;
NIBBLETYPE UnderMeta; NIBBLETYPE UnderMeta;
a_Chunk.GetBlockTypeMeta(a_RelX, a_RelY - 1, a_RelZ, UnderType, UnderMeta); a_Chunk.GetBlockTypeMeta(UnderPos, UnderType, UnderMeta);
return ( return (
(((UnderType == E_BLOCK_STATIONARY_WATER) || (UnderType == E_BLOCK_WATER)) && (UnderMeta == 0)) || // A water source is below (((UnderType == E_BLOCK_STATIONARY_WATER) || (UnderType == E_BLOCK_WATER)) && (UnderMeta == 0)) || // A water source is below
(UnderType == E_BLOCK_ICE) || (UnderType == E_BLOCK_FROSTED_ICE) // Or (frosted) ice (UnderType == E_BLOCK_ICE) || (UnderType == E_BLOCK_FROSTED_ICE) // Or (frosted) ice

View File

@ -8,22 +8,38 @@
class cBlockMobSpawnerHandler : class cBlockMobSpawnerHandler:
public cBlockHandler public cBlockHandler
{ {
using Super = cBlockHandler;
public: public:
cBlockMobSpawnerHandler(BLOCKTYPE a_BlockType)
: cBlockHandler(a_BlockType) cBlockMobSpawnerHandler(BLOCKTYPE a_BlockType):
Super(a_BlockType)
{ {
} }
virtual bool OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override
virtual bool OnUse(
cChunkInterface & a_ChunkInterface,
cWorldInterface & a_WorldInterface,
cPlayer & a_Player,
const Vector3i a_BlockPos,
eBlockFace a_BlockFace,
const Vector3i a_CursorPos
) override
{ {
return a_ChunkInterface.UseBlockEntity(&a_Player, a_BlockX, a_BlockY, a_BlockZ); return a_ChunkInterface.UseBlockEntity(&a_Player, a_BlockPos.x, a_BlockPos.y, a_BlockPos.z);
} }
virtual bool IsUseable() override virtual bool IsUseable() override
{ {
return true; return true;

View File

@ -7,6 +7,7 @@
/** Handler for the small (singleblock) mushrooms. */
class cBlockMushroomHandler: class cBlockMushroomHandler:
public cClearMetaOnDrop<cBlockHandler> public cClearMetaOnDrop<cBlockHandler>
{ {
@ -19,18 +20,26 @@ public:
{ {
} }
// TODO: Add Mushroom Spread // TODO: Add Mushroom Spread
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) override
{ {
if (a_RelY <= 0) if (a_RelPos.y <= 0)
{ {
return false; return false;
} }
// TODO: Cannot be at too much daylight // TODO: Cannot be at too much daylight
switch (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)) switch (a_Chunk.GetBlock(a_RelPos.addedY(-1)))
{ {
case E_BLOCK_GLASS: case E_BLOCK_GLASS:
case E_BLOCK_CACTUS: case E_BLOCK_CACTUS:
@ -45,6 +54,10 @@ public:
return true; return true;
} }
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{ {
UNUSED(a_Meta); UNUSED(a_Meta);

View File

@ -8,7 +8,7 @@
class cBlockNetherWartHandler : class cBlockNetherWartHandler:
public cBlockPlant<false> public cBlockPlant<false>
{ {
using Super = cBlockPlant<false>; using Super = cBlockPlant<false>;
@ -64,10 +64,10 @@ public:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) override
{ {
// Needs to be placed on top of a Soulsand block: // Needs to be placed on top of a Soulsand block:
return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) == E_BLOCK_SOULSAND)); return ((a_RelPos.y > 0) && (a_Chunk.GetBlock(a_RelPos.addedY(-1)) == E_BLOCK_SOULSAND));
} }

View File

@ -7,18 +7,28 @@
class cBlockPlanksHandler : public cBlockHandler class cBlockPlanksHandler:
public cBlockHandler
{ {
using Super = cBlockHandler;
public: public:
cBlockPlanksHandler(BLOCKTYPE a_BlockType)
: cBlockHandler(a_BlockType) cBlockPlanksHandler(BLOCKTYPE a_BlockType):
Super(a_BlockType)
{ {
} }
virtual bool GetPlacementBlockTypeMeta( virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player, cChunkInterface & a_ChunkInterface,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, cPlayer & a_Player,
int a_CursorX, int a_CursorY, int a_CursorZ, const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override ) override
{ {
@ -27,6 +37,10 @@ public:
return true; return true;
} }
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{ {
switch (a_Meta) switch (a_Meta)

View File

@ -7,25 +7,33 @@
class cBlockPortalHandler : class cBlockPortalHandler:
public cBlockHandler public cBlockHandler
{ {
using Super = cBlockHandler;
public: public:
cBlockPortalHandler(BLOCKTYPE a_BlockType)
: cBlockHandler(a_BlockType) cBlockPortalHandler(BLOCKTYPE a_BlockType):
Super(a_BlockType)
{ {
} }
virtual bool GetPlacementBlockTypeMeta( virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player, cChunkInterface & a_ChunkInterface,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, cPlayer & a_Player,
int a_CursorX, int a_CursorY, int a_CursorZ, const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override ) override
{ {
// We set to zero so MCS doesn't stop you from building weird portals like vanilla does // We set meta to zero so Cuberite doesn't stop a Creative-mode player from building custom portal shapes
// CanBeAt doesn't do anything if meta is zero // CanBeAt doesn't do anything if meta is zero
// We set to zero because the client sends meta = 2 to the server (it calculates rotation itself) // We set to zero because the client sends meta = 1 or 2 to the server (it calculates rotation itself)
a_BlockType = m_BlockType; a_BlockType = m_BlockType;
a_BlockMeta = 0; a_BlockMeta = 0;
@ -67,14 +75,14 @@ public:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) override
{ {
if ((a_RelY <= 0) || (a_RelY >= cChunkDef::Height - 1)) if ((a_RelPos.y <= 0) || (a_RelPos.y >= cChunkDef::Height - 1))
{ {
return false; // In case someone places a portal with meta 1 or 2 at boundaries, and server tries to get invalid coords at Y - 1 or Y + 1 return false; // In case someone places a portal with meta 1 or 2 at boundaries, and server tries to get invalid coords at Y - 1 or Y + 1
} }
switch (a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ)) switch (a_Chunk.GetMeta(a_RelPos))
{ {
case 0x1: case 0x1:
{ {
@ -91,8 +99,7 @@ public:
for (const auto & Direction : PortalCheck) for (const auto & Direction : PortalCheck)
{ {
BLOCKTYPE Block; BLOCKTYPE Block;
a_Chunk.UnboundedRelGetBlockType(a_RelX + Direction.x, a_RelY + Direction.y, a_RelZ + Direction.z, Block); a_Chunk.UnboundedRelGetBlockType(a_RelPos + Direction, Block);
if ((Block != E_BLOCK_NETHER_PORTAL) && (Block != E_BLOCK_OBSIDIAN)) if ((Block != E_BLOCK_NETHER_PORTAL) && (Block != E_BLOCK_OBSIDIAN))
{ {
return false; return false;
@ -115,8 +122,7 @@ public:
for (const auto & Direction : PortalCheck) for (const auto & Direction : PortalCheck)
{ {
BLOCKTYPE Block; BLOCKTYPE Block;
a_Chunk.UnboundedRelGetBlockType(a_RelX + Direction.x, a_RelY + Direction.y, a_RelZ + Direction.z, Block); a_Chunk.UnboundedRelGetBlockType(a_RelPos + Direction, Block);
if ((Block != E_BLOCK_NETHER_PORTAL) && (Block != E_BLOCK_OBSIDIAN)) if ((Block != E_BLOCK_NETHER_PORTAL) && (Block != E_BLOCK_OBSIDIAN))
{ {
return false; return false;
@ -128,6 +134,10 @@ public:
return true; return true;
} }
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{ {
UNUSED(a_Meta); UNUSED(a_Meta);

View File

@ -18,15 +18,19 @@ public:
{ {
} }
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) override
{ {
if (a_RelY - 1 <= 0) if (a_RelPos.y <= 1)
{ {
return false; return false;
} }
// TODO: check if the block is upside-down slab or upside-down stairs // TODO: check if the block is upside-down slab or upside-down stairs
BLOCKTYPE Block = a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ); auto Block = a_Chunk.GetBlock(a_RelPos.addedY(-1));
switch (Block) switch (Block)
{ {
case E_BLOCK_ACACIA_FENCE: case E_BLOCK_ACACIA_FENCE:
@ -47,6 +51,10 @@ public:
} }
} }
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{ {
UNUSED(a_Meta); UNUSED(a_Meta);

View File

@ -7,42 +7,57 @@
class cBlockQuartzHandler : public cBlockHandler class cBlockQuartzHandler:
public cBlockHandler
{ {
using Super = cBlockHandler;
public: public:
cBlockQuartzHandler(BLOCKTYPE a_BlockType)
: cBlockHandler(a_BlockType) cBlockQuartzHandler(BLOCKTYPE a_BlockType):
Super(a_BlockType)
{ {
} }
virtual bool GetPlacementBlockTypeMeta( virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player, cChunkInterface & a_ChunkInterface,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, cPlayer & a_Player,
int a_CursorX, int a_CursorY, int a_CursorZ, const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override ) override
{ {
a_BlockType = m_BlockType; a_BlockType = m_BlockType;
NIBBLETYPE Meta = static_cast<NIBBLETYPE>(a_Player.GetEquippedItem().m_ItemDamage); auto Meta = static_cast<NIBBLETYPE>(a_Player.GetEquippedItem().m_ItemDamage);
if (Meta != E_META_QUARTZ_PILLAR) // Check if the block is a pillar block. // Pillar block needs additional direction in the metadata:
if (Meta != E_META_QUARTZ_PILLAR)
{ {
a_BlockMeta = Meta; a_BlockMeta = Meta;
return true; return true;
} }
a_BlockMeta = BlockFaceToMetaData(a_ClickedBlockFace);
a_BlockMeta = BlockFaceToMetaData(a_BlockFace, Meta);
return true; return true;
} }
inline static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_BlockFace, NIBBLETYPE a_QuartzMeta)
/** Converts the block face of the pillar block's "base" to the block's metadata. */
inline static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_BlockFace)
{ {
switch (a_BlockFace) switch (a_BlockFace)
{ {
case BLOCK_FACE_YM: case BLOCK_FACE_YM:
case BLOCK_FACE_YP: case BLOCK_FACE_YP:
{ {
return a_QuartzMeta; // Top or bottom, just return original return E_META_QUARTZ_PILLAR; // Top or bottom
} }
case BLOCK_FACE_ZP: case BLOCK_FACE_ZP:
@ -57,15 +72,17 @@ public:
return 0x3; // East or west return 0x3; // East or west
} }
case BLOCK_FACE_NONE: default:
{ {
ASSERT(!"Unhandled block face!"); return E_META_QUARTZ_PILLAR;
return a_QuartzMeta; // No idea, give a special meta (all sides the same)
} }
} }
UNREACHABLE("Unsupported block face");
} }
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{ {
UNUSED(a_Meta); UNUSED(a_Meta);

View File

@ -28,21 +28,26 @@ public:
{ {
} }
virtual bool GetPlacementBlockTypeMeta( virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player, cChunkInterface & a_ChunkInterface,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, cPlayer & a_Player,
int a_CursorX, int a_CursorY, int a_CursorZ, const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override ) override
{ {
a_BlockType = m_BlockType; a_BlockType = m_BlockType;
Vector3i Pos{ a_BlockX, a_BlockY, a_BlockZ }; a_BlockMeta = FindMeta(a_ChunkInterface, a_PlacedBlockPos);
a_BlockMeta = FindMeta(a_ChunkInterface, Pos); return a_Player.GetWorld()->DoWithChunkAt(a_PlacedBlockPos,
return a_Player.GetWorld()->DoWithChunkAt(Pos, [this, a_PlacedBlockPos, &a_ChunkInterface](cChunk & a_Chunk)
[this, Pos, &a_ChunkInterface](cChunk & a_Chunk)
{ {
auto RelPos = cChunkDef::AbsoluteToRelative(Pos); auto RelPos = cChunkDef::AbsoluteToRelative(a_PlacedBlockPos);
return CanBeAt(a_ChunkInterface, RelPos.x, RelPos.y, RelPos.z, a_Chunk); return CanBeAt(a_ChunkInterface, RelPos, a_Chunk);
} }
); );
} }
@ -111,18 +116,18 @@ public:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) override
{ {
if (a_RelY <= 0) if (a_RelPos.y <= 0)
{ {
return false; return false;
} }
if (!cBlockInfo::FullyOccupiesVoxel(a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ))) if (!cBlockInfo::FullyOccupiesVoxel(a_Chunk.GetBlock(a_RelPos.addedY(-1))))
{ {
return false; return false;
} }
NIBBLETYPE Meta = a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ); NIBBLETYPE Meta = a_Chunk.GetMeta(a_RelPos);
switch (Meta) switch (Meta)
{ {
case E_META_RAIL_ASCEND_XP: case E_META_RAIL_ASCEND_XP:
@ -132,19 +137,16 @@ public:
{ {
// Mapping between the meta and the neighbors that need checking // Mapping between the meta and the neighbors that need checking
Meta -= E_META_RAIL_ASCEND_XP; // Base index at zero Meta -= E_META_RAIL_ASCEND_XP; // Base index at zero
static const struct static const Vector3i Coords[] =
{ {
int x, z; { 1, 0, 0}, // east, XP
} Coords[] = {-1, 0, 0}, // west, XM
{ { 0, 0, -1}, // north, ZM
{ 1, 0}, // east, XP { 0, 0, 1}, // south, ZP
{-1, 0}, // west, XM
{ 0, -1}, // north, ZM
{ 0, 1}, // south, ZP
} ; } ;
BLOCKTYPE BlockType; BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta; NIBBLETYPE BlockMeta;
if (!a_Chunk.UnboundedRelGetBlock(a_RelX + Coords[Meta].x, a_RelY, a_RelZ + Coords[Meta].z, BlockType, BlockMeta)) if (!a_Chunk.UnboundedRelGetBlock(a_RelPos + Coords[Meta], BlockType, BlockMeta))
{ {
// Too close to the edge, cannot simulate // Too close to the edge, cannot simulate
return true; return true;
@ -155,6 +157,10 @@ public:
return true; return true;
} }
NIBBLETYPE FindMeta(cChunkInterface & a_ChunkInterface, Vector3i a_BlockPos) NIBBLETYPE FindMeta(cChunkInterface & a_ChunkInterface, Vector3i a_BlockPos)
{ {
NIBBLETYPE Meta = 0; NIBBLETYPE Meta = 0;

View File

@ -8,7 +8,7 @@
class cBlockRedstoneHandler : class cBlockRedstoneHandler:
public cBlockHandler public cBlockHandler
{ {
using Super = cBlockHandler; using Super = cBlockHandler;
@ -20,16 +20,20 @@ public:
{ {
} }
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) override
{ {
if (a_RelY <= 0) if (a_RelPos.y <= 0)
{ {
return false; return false;
} }
BLOCKTYPE BelowBlock; BLOCKTYPE BelowBlock;
NIBBLETYPE BelowBlockMeta; NIBBLETYPE BelowBlockMeta;
a_Chunk.GetBlockTypeMeta(a_RelX, a_RelY - 1, a_RelZ, BelowBlock, BelowBlockMeta); a_Chunk.GetBlockTypeMeta(a_RelPos.addedY(-1), BelowBlock, BelowBlockMeta);
if (cBlockInfo::FullyOccupiesVoxel(BelowBlock)) if (cBlockInfo::FullyOccupiesVoxel(BelowBlock))
{ {
@ -46,11 +50,19 @@ public:
return false; return false;
} }
virtual cItems ConvertToPickups(NIBBLETYPE a_BlockMeta, cBlockEntity * a_BlockEntity, const cEntity * a_Digger, const cItem * a_Tool) override virtual cItems ConvertToPickups(NIBBLETYPE a_BlockMeta, cBlockEntity * a_BlockEntity, const cEntity * a_Digger, const cItem * a_Tool) override
{ {
return cItem(E_ITEM_REDSTONE_DUST, 1, 0); return cItem(E_ITEM_REDSTONE_DUST, 1, 0);
} }
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{ {
UNUSED(a_Meta); UNUSED(a_Meta);

View File

@ -17,31 +17,39 @@ public:
using Super::Super; // Inherit constructor from base using Super::Super; // Inherit constructor from base
virtual bool OnUse( virtual bool OnUse(
cChunkInterface & a_ChunkInterface, cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player,
cWorldInterface & a_WorldInterface, const Vector3i a_BlockPos,
cPlayer & a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ,
eBlockFace a_BlockFace, eBlockFace a_BlockFace,
int a_CursorX, int a_CursorY, int a_CursorZ const Vector3i a_CursorPos
) override ) override
{ {
Vector3i BlockPos{a_BlockX, a_BlockY, a_BlockZ}; a_ChunkInterface.SetBlock(a_BlockPos, E_BLOCK_REDSTONE_ORE_GLOWING, 0);
a_ChunkInterface.SetBlock(BlockPos, E_BLOCK_REDSTONE_ORE_GLOWING, 0);
return false; return false;
} }
virtual void OnDigging( virtual void OnDigging(
cChunkInterface & a_ChunkInterface, cChunkInterface & a_ChunkInterface,
cWorldInterface & a_WorldInterface, cWorldInterface & a_WorldInterface,
cPlayer & a_Player, cPlayer & a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ const Vector3i a_BlockPos
) override ) override
{ {
Vector3i BlockPos{a_BlockX, a_BlockY, a_BlockZ}; a_ChunkInterface.SetBlock(a_BlockPos, E_BLOCK_REDSTONE_ORE_GLOWING, 0);
a_ChunkInterface.SetBlock(BlockPos, E_BLOCK_REDSTONE_ORE_GLOWING, 0);
} }
virtual bool IsUseable() override virtual bool IsUseable() override
{ {
return true; return true;
@ -61,6 +69,10 @@ public:
using Super::Super; // Inherit constructor from base using Super::Super; // Inherit constructor from base
virtual void OnUpdate( virtual void OnUpdate(
cChunkInterface & a_ChunkInterface, cChunkInterface & a_ChunkInterface,
cWorldInterface & a_WorldInterface, cWorldInterface & a_WorldInterface,

View File

@ -23,33 +23,63 @@ public:
{ {
} }
virtual bool OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override
virtual bool OnUse(
cChunkInterface & a_ChunkInterface,
cWorldInterface & a_WorldInterface,
cPlayer & a_Player,
const Vector3i a_BlockPos,
eBlockFace a_BlockFace,
const Vector3i a_CursorPos
) override
{ {
a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, ((a_ChunkInterface.GetBlockMeta({a_BlockX, a_BlockY, a_BlockZ}) + 0x04) & 0x0f)); // Increment the delay setting:
a_ChunkInterface.SetBlockMeta(a_BlockPos, ((a_ChunkInterface.GetBlockMeta(a_BlockPos) + 0x04) & 0x0f));
return true; return true;
} }
virtual void OnCancelRightClick(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) override
virtual void OnCancelRightClick(
cChunkInterface & a_ChunkInterface,
cWorldInterface & a_WorldInterface,
cPlayer & a_Player,
const Vector3i a_BlockPos,
eBlockFace a_BlockFace
) override
{ {
UNUSED(a_ChunkInterface); UNUSED(a_ChunkInterface);
a_WorldInterface.SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, a_Player); a_WorldInterface.SendBlockTo(a_BlockPos, a_Player);
} }
virtual bool IsUseable(void) override virtual bool IsUseable(void) override
{ {
return true; return true;
} }
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) override
{ {
if (a_RelY <= 0) if (a_RelPos.y <= 0)
{ {
return false; return false;
} }
BLOCKTYPE BelowBlock; BLOCKTYPE BelowBlock;
NIBBLETYPE BelowBlockMeta; NIBBLETYPE BelowBlockMeta;
a_Chunk.GetBlockTypeMeta(a_RelX, a_RelY - 1, a_RelZ, BelowBlock, BelowBlockMeta); a_Chunk.GetBlockTypeMeta(a_RelPos.addedY(-1), BelowBlock, BelowBlockMeta);
if (cBlockInfo::FullyOccupiesVoxel(BelowBlock)) if (cBlockInfo::FullyOccupiesVoxel(BelowBlock))
{ {
@ -66,17 +96,29 @@ public:
return false; return false;
} }
virtual cItems ConvertToPickups(NIBBLETYPE a_BlockMeta, cBlockEntity * a_BlockEntity, const cEntity * a_Digger, const cItem * a_Tool) override virtual cItems ConvertToPickups(NIBBLETYPE a_BlockMeta, cBlockEntity * a_BlockEntity, const cEntity * a_Digger, const cItem * a_Tool) override
{ {
return cItem(E_ITEM_REDSTONE_REPEATER, 1, 0); return cItem(E_ITEM_REDSTONE_REPEATER, 1, 0);
} }
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{ {
UNUSED(a_Meta); UNUSED(a_Meta);
return 11; return 11;
} }
inline static Vector3i GetLeftCoordinateOffset(NIBBLETYPE a_Meta) inline static Vector3i GetLeftCoordinateOffset(NIBBLETYPE a_Meta)
{ {
switch (a_Meta & E_META_REDSTONE_REPEATER_FACING_MASK) // We only want the direction (bottom) bits switch (a_Meta & E_META_REDSTONE_REPEATER_FACING_MASK) // We only want the direction (bottom) bits
@ -95,11 +137,18 @@ public:
} }
} }
inline static Vector3i GetFrontCoordinateOffset(NIBBLETYPE a_Meta) inline static Vector3i GetFrontCoordinateOffset(NIBBLETYPE a_Meta)
{ {
return -GetRearCoordinateOffset(a_Meta); return -GetRearCoordinateOffset(a_Meta);
} }
inline static Vector3i GetRearCoordinateOffset(NIBBLETYPE a_Meta) inline static Vector3i GetRearCoordinateOffset(NIBBLETYPE a_Meta)
{ {
switch (a_Meta & E_META_REDSTONE_REPEATER_FACING_MASK) // We only want the direction (bottom) bits switch (a_Meta & E_META_REDSTONE_REPEATER_FACING_MASK) // We only want the direction (bottom) bits

View File

@ -34,9 +34,9 @@ public:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) override
{ {
return (a_RelY > 0) && IsBlockTypeOfDirt(a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)); return (a_RelPos.y > 0) && IsBlockTypeOfDirt(a_Chunk.GetBlock(a_RelPos.addedY(-1)));
} }

View File

@ -27,15 +27,17 @@ public:
virtual bool GetPlacementBlockTypeMeta( virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player, cChunkInterface & a_ChunkInterface,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, cPlayer & a_Player,
int a_CursorX, int a_CursorY, int a_CursorZ, const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override ) override
{ {
a_BlockType = m_BlockType; a_BlockType = m_BlockType;
NIBBLETYPE Meta = static_cast<NIBBLETYPE>(a_Player.GetEquippedItem().m_ItemDamage); NIBBLETYPE Meta = static_cast<NIBBLETYPE>(a_Player.GetEquippedItem().m_ItemDamage);
a_BlockMeta = BlockFaceToMetaData(a_BlockFace, Meta); a_BlockMeta = BlockFaceToMetaData(a_ClickedBlockFace, Meta);
return true; return true;
} }

View File

@ -32,17 +32,21 @@ public:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) override
{ {
if (a_RelY <= 0) if (a_RelPos.y <= 0)
{ {
return false; return false;
} }
BLOCKTYPE Type = a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ); BLOCKTYPE Type = a_Chunk.GetBlock(a_RelPos.addedY(-1));
return ((Type == E_BLOCK_SIGN_POST) || (Type == E_BLOCK_WALLSIGN) || cBlockInfo::IsSolid(Type)); return ((Type == E_BLOCK_SIGN_POST) || (Type == E_BLOCK_WALLSIGN) || cBlockInfo::IsSolid(Type));
} }
/** Converts the (player) rotation to placed-signpost block meta. */
static NIBBLETYPE RotationToMetaData(double a_Rotation) static NIBBLETYPE RotationToMetaData(double a_Rotation)
{ {
a_Rotation += 180 + (180 / 16); // So it's not aligned with axis a_Rotation += 180 + (180 / 16); // So it's not aligned with axis
@ -56,16 +60,28 @@ public:
return (static_cast<char>(a_Rotation)) % 16; return (static_cast<char>(a_Rotation)) % 16;
} }
virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override
{ {
return (a_Meta + 4) & 0x0f; return (a_Meta + 4) & 0x0f;
} }
virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override
{ {
return (a_Meta + 12) & 0x0f; return (a_Meta + 12) & 0x0f;
} }
virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override
{ {
// Mirrors signs over the XY plane (North-South Mirroring) // Mirrors signs over the XY plane (North-South Mirroring)
@ -75,6 +91,10 @@ public:
return (a_Meta < 0x08) ? (0x08 - a_Meta) : (0x18 - a_Meta); return (a_Meta < 0x08) ? (0x08 - a_Meta) : (0x18 - a_Meta);
} }
virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override
{ {
// Mirrors signs over the YZ plane (East-West Mirroring) // Mirrors signs over the YZ plane (East-West Mirroring)
@ -84,6 +104,10 @@ public:
return 0x0f - a_Meta; return 0x0f - a_Meta;
} }
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{ {
UNUSED(a_Meta); UNUSED(a_Meta);

View File

@ -44,9 +44,11 @@ public:
virtual bool GetPlacementBlockTypeMeta( virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player, cChunkInterface & a_ChunkInterface,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, cPlayer & a_Player,
int a_CursorX, int a_CursorY, int a_CursorZ, const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override ) override
{ {
@ -54,18 +56,18 @@ public:
NIBBLETYPE Meta = static_cast<NIBBLETYPE>(a_Player.GetEquippedItem().m_ItemDamage); NIBBLETYPE Meta = static_cast<NIBBLETYPE>(a_Player.GetEquippedItem().m_ItemDamage);
// Set the correct metadata based on player equipped item (i.e. a_BlockMeta not initialised yet) // Set the correct metadata based on player equipped item (i.e. a_BlockMeta not initialised yet)
switch (a_BlockFace) switch (a_ClickedBlockFace)
{ {
case BLOCK_FACE_TOP: case BLOCK_FACE_TOP:
{ {
// Bottom half slab block // Bottom half slab block
a_BlockMeta = Meta & 0x7; a_BlockMeta = Meta & 0x07;
break; break;
} }
case BLOCK_FACE_BOTTOM: case BLOCK_FACE_BOTTOM:
{ {
// Top half slab block // Top half slab block
a_BlockMeta = Meta | 0x8; a_BlockMeta = Meta | 0x08;
break; break;
} }
case BLOCK_FACE_EAST: case BLOCK_FACE_EAST:
@ -73,15 +75,15 @@ public:
case BLOCK_FACE_SOUTH: case BLOCK_FACE_SOUTH:
case BLOCK_FACE_WEST: case BLOCK_FACE_WEST:
{ {
if (a_CursorY > 7) if (a_CursorPos.y > 7)
{ {
// Cursor at top half of block, place top slab // Cursor at top half of block, place top slab
a_BlockMeta = Meta | 0x8; break; a_BlockMeta = Meta | 0x08; break;
} }
else else
{ {
// Cursor at bottom half of block, place bottom slab // Cursor at bottom half of block, place bottom slab
a_BlockMeta = Meta & 0x7; break; a_BlockMeta = Meta & 0x07; break;
} }
} }
case BLOCK_FACE_NONE: return false; case BLOCK_FACE_NONE: return false;
@ -89,10 +91,10 @@ public:
// Check if the block at the coordinates is a single slab. Eligibility for combining has already been processed in ClientHandle // Check if the block at the coordinates is a single slab. Eligibility for combining has already been processed in ClientHandle
// Changed to-be-placed to a double slab if we are clicking on a single slab, as opposed to placing one for the first time // Changed to-be-placed to a double slab if we are clicking on a single slab, as opposed to placing one for the first time
if (IsAnySlabType(a_ChunkInterface.GetBlock({a_BlockX, a_BlockY, a_BlockZ}))) if (IsAnySlabType(a_ChunkInterface.GetBlock(a_PlacedBlockPos)))
{ {
a_BlockType = GetDoubleSlabType(m_BlockType); a_BlockType = GetDoubleSlabType(m_BlockType);
a_BlockMeta = a_BlockMeta & 0x7; a_BlockMeta = a_BlockMeta & 0x07;
} }
return true; return true;
@ -117,7 +119,13 @@ public:
virtual void OnCancelRightClick(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) override virtual void OnCancelRightClick(
cChunkInterface & a_ChunkInterface,
cWorldInterface & a_WorldInterface,
cPlayer & a_Player,
const Vector3i a_BlockPos,
eBlockFace a_BlockFace
) override
{ {
if ((a_BlockFace == BLOCK_FACE_NONE) || (a_Player.GetEquippedItem().m_ItemType != static_cast<short>(m_BlockType))) if ((a_BlockFace == BLOCK_FACE_NONE) || (a_Player.GetEquippedItem().m_ItemType != static_cast<short>(m_BlockType)))
{ {
@ -125,7 +133,7 @@ public:
} }
// Sends the slab back to the client. It's to refuse a doubleslab placement. */ // Sends the slab back to the client. It's to refuse a doubleslab placement. */
a_Player.GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, a_Player); a_Player.GetWorld()->SendBlockTo(a_BlockPos, a_Player);
} }
@ -222,13 +230,13 @@ public:
virtual bool IsInsideBlock(Vector3d a_Position, const BLOCKTYPE a_BlockType, const NIBBLETYPE a_BlockMeta) override virtual bool IsInsideBlock(Vector3d a_Position, const NIBBLETYPE a_BlockMeta) override
{ {
if (a_BlockMeta & 0x8) // top half if (a_BlockMeta & 0x08) // top half
{ {
return true; return true;
} }
return cBlockHandler::IsInsideBlock(a_Position, a_BlockType, a_BlockMeta); return cBlockHandler::IsInsideBlock(a_Position, a_BlockMeta);
} }
} ; } ;
@ -236,7 +244,7 @@ public:
class cBlockDoubleSlabHandler : class cBlockDoubleSlabHandler:
public cBlockHandler public cBlockHandler
{ {
using Super = cBlockHandler; using Super = cBlockHandler;

View File

@ -10,30 +10,40 @@
class cBlockSnowHandler : class cBlockSnowHandler :
public cBlockHandler public cBlockHandler
{ {
using Super = cBlockHandler;
public: public:
enum enum
{ {
FullBlockMeta = 7 // Meta value of a full-height snow block FullBlockMeta = 7 // Meta value of a full-height snow block
}; };
cBlockSnowHandler(BLOCKTYPE a_BlockType)
: cBlockHandler(a_BlockType) cBlockSnowHandler(BLOCKTYPE a_BlockType):
Super(a_BlockType)
{ {
} }
virtual bool GetPlacementBlockTypeMeta( virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player, cChunkInterface & a_ChunkInterface,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, cPlayer & a_Player,
int a_CursorX, int a_CursorY, int a_CursorZ, const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override ) override
{ {
a_BlockType = m_BlockType; a_BlockType = m_BlockType;
// Check if incrementing existing snow height:
BLOCKTYPE BlockBeforePlacement; BLOCKTYPE BlockBeforePlacement;
NIBBLETYPE MetaBeforePlacement; NIBBLETYPE MetaBeforePlacement;
a_ChunkInterface.GetBlockTypeMeta({a_BlockX, a_BlockY, a_BlockZ}, BlockBeforePlacement, MetaBeforePlacement); a_ChunkInterface.GetBlockTypeMeta(a_PlacedBlockPos, BlockBeforePlacement, MetaBeforePlacement);
if ((BlockBeforePlacement == E_BLOCK_SNOW) && (MetaBeforePlacement < FullBlockMeta)) if ((BlockBeforePlacement == E_BLOCK_SNOW) && (MetaBeforePlacement < FullBlockMeta))
{ {
// Only increment if: // Only increment if:
@ -45,16 +55,19 @@ public:
// First time placement, check placement is valid // First time placement, check placement is valid
a_BlockMeta = 0; a_BlockMeta = 0;
BLOCKTYPE BlockBelow; BLOCKTYPE BlockBelow;
NIBBLETYPE MetaBelow; NIBBLETYPE MetaBelow;
return ( return (
(a_BlockY > 0) && (a_PlacedBlockPos.y > 0) &&
a_ChunkInterface.GetBlockTypeMeta({a_BlockX, a_BlockY - 1, a_BlockZ}, BlockBelow, MetaBelow) && a_ChunkInterface.GetBlockTypeMeta(a_PlacedBlockPos.addedY(-1), BlockBelow, MetaBelow) &&
CanBeOn(BlockBelow, MetaBelow) CanBeOn(BlockBelow, MetaBelow)
); );
} }
virtual bool DoesIgnoreBuildCollision(cChunkInterface & a_ChunkInterface, Vector3i a_Pos, cPlayer & a_Player, NIBBLETYPE a_Meta) override virtual bool DoesIgnoreBuildCollision(cChunkInterface & a_ChunkInterface, Vector3i a_Pos, cPlayer & a_Player, NIBBLETYPE a_Meta) override
{ {
if ((a_Player.GetEquippedItem().m_ItemType == E_BLOCK_SNOW) && (a_Meta < FullBlockMeta)) if ((a_Player.GetEquippedItem().m_ItemType == E_BLOCK_SNOW) && (a_Meta < FullBlockMeta))
@ -97,35 +110,47 @@ public:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) override
{ {
if (a_RelY > 0) if (a_RelPos.y <= 0)
{ {
BLOCKTYPE BlockBelow = a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ); return false;
NIBBLETYPE MetaBelow = a_Chunk.GetMeta(a_RelX, a_RelY - 1, a_RelZ);
return CanBeOn(BlockBelow, MetaBelow);
} }
auto BelowPos = a_RelPos.addedY(-1);
return false; auto BlockBelow = a_Chunk.GetBlock(BelowPos);
auto MetaBelow = a_Chunk.GetMeta(BelowPos);
return CanBeOn(BlockBelow, MetaBelow);
} }
virtual bool DoesDropOnUnsuitable(void) override virtual bool DoesDropOnUnsuitable(void) override
{ {
return false; return false;
} }
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{ {
UNUSED(a_Meta); UNUSED(a_Meta);
return 14; return 14;
} }
virtual bool IsInsideBlock(Vector3d a_Position, const BLOCKTYPE a_BlockType, const NIBBLETYPE a_BlockMeta) override
virtual bool IsInsideBlock(const Vector3d a_RelPosition, const NIBBLETYPE a_BlockMeta) override
{ {
return a_Position.y < (cBlockInfo::GetBlockHeight(a_BlockType) * (a_BlockMeta & 0x07)); return a_RelPosition.y < (cBlockInfo::GetBlockHeight(m_BlockType) * (a_BlockMeta & 0x07));
} }
private: private:
/** Returns true if snow can be placed on top of a block with the given type and meta. */ /** Returns true if snow can be placed on top of a block with the given type and meta. */

View File

@ -17,25 +17,27 @@ public:
cBlockStairsHandler(BLOCKTYPE a_BlockType): cBlockStairsHandler(BLOCKTYPE a_BlockType):
Super(a_BlockType) Super(a_BlockType)
{ {
} }
virtual bool GetPlacementBlockTypeMeta( virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player, cChunkInterface & a_ChunkInterface,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, cPlayer & a_Player,
int a_CursorX, int a_CursorY, int a_CursorZ, const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override ) override
{ {
UNUSED(a_ChunkInterface); UNUSED(a_ChunkInterface);
UNUSED(a_BlockX); UNUSED(a_PlacedBlockPos);
UNUSED(a_BlockY); UNUSED(a_CursorPos);
UNUSED(a_BlockZ);
UNUSED(a_CursorX);
UNUSED(a_CursorZ);
a_BlockType = m_BlockType; a_BlockType = m_BlockType;
a_BlockMeta = RotationToMetaData(a_Player.GetYaw()); a_BlockMeta = RotationToMetaData(a_Player.GetYaw());
switch (a_BlockFace) switch (a_ClickedBlockFace)
{ {
case BLOCK_FACE_TOP: break; case BLOCK_FACE_TOP: break;
case BLOCK_FACE_BOTTOM: a_BlockMeta = a_BlockMeta | 0x4; break; // When placing onto a bottom face, always place an upside-down stairs block case BLOCK_FACE_BOTTOM: a_BlockMeta = a_BlockMeta | 0x4; break; // When placing onto a bottom face, always place an upside-down stairs block
@ -45,7 +47,7 @@ public:
case BLOCK_FACE_WEST: case BLOCK_FACE_WEST:
{ {
// When placing onto a sideways face, check cursor, if in top half, make it an upside-down stairs block // When placing onto a sideways face, check cursor, if in top half, make it an upside-down stairs block
if (a_CursorY > 8) if (a_CursorPos.y > 8)
{ {
a_BlockMeta |= 0x4; a_BlockMeta |= 0x4;
} }
@ -56,6 +58,10 @@ public:
return true; return true;
} }
static NIBBLETYPE RotationToMetaData(double a_Rotation) static NIBBLETYPE RotationToMetaData(double a_Rotation)
{ {
a_Rotation += 90 + 45; // So its not aligned with axis a_Rotation += 90 + 45; // So its not aligned with axis
@ -81,12 +87,20 @@ public:
} }
} }
virtual NIBBLETYPE MetaMirrorXZ(NIBBLETYPE a_Meta) override virtual NIBBLETYPE MetaMirrorXZ(NIBBLETYPE a_Meta) override
{ {
// Toggle bit 3: // Toggle bit 3:
return (a_Meta & 0x0b) | ((~a_Meta) & 0x04); return (a_Meta & 0x0b) | ((~a_Meta) & 0x04);
} }
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{ {
UNUSED(a_Meta); UNUSED(a_Meta);
@ -114,12 +128,16 @@ public:
} }
} }
/** EXCEPTION a.k.a. why is this removed: /** EXCEPTION a.k.a. why is this removed:
This collision-detection is actually more accurate than the client, but since the client itself This collision-detection is actually more accurate than the client, but since the client itself
sends inaccurate / sparse data, it's easier to just err on the side of the client and keep the sends inaccurate / sparse data, it's easier to just err on the side of the client and keep the
two in sync by assuming that if a player hit ANY of the stair's bounding cube, it counts as the ground. */ two in sync by assuming that if a player hit ANY of the stair's bounding cube, it counts as the ground. */
#if 0 #if 0
bool IsInsideBlock(const Vector3d & a_Position, const BLOCKTYPE a_BlockType, const NIBBLETYPE a_BlockMeta) bool IsInsideBlock(Vector3d a_RelPosition, const BLOCKTYPE a_BlockType, const NIBBLETYPE a_BlockMeta)
{ {
if (a_BlockMeta & 0x4) // upside down if (a_BlockMeta & 0x4) // upside down
{ {
@ -127,19 +145,19 @@ public:
} }
else if ((a_BlockMeta & 0x3) == 0) // tall side is east (+X) else if ((a_BlockMeta & 0x3) == 0) // tall side is east (+X)
{ {
return a_Position.y < ((a_Position.x > 0.5) ? 1.0 : 0.5); return (a_RelPosition.y < ((a_RelPosition.x > 0.5) ? 1.0 : 0.5));
} }
else if ((a_BlockMeta & 0x3) == 1) // tall side is west (-X) else if ((a_BlockMeta & 0x3) == 1) // tall side is west (-X)
{ {
return a_Position.y < ((a_Position.x < 0.5) ? 1.0 : 0.5); return (a_RelPosition.y < ((a_RelPosition.x < 0.5) ? 1.0 : 0.5));
} }
else if ((a_BlockMeta & 0x3) == 2) // tall side is south (+Z) else if ((a_BlockMeta & 0x3) == 2) // tall side is south (+Z)
{ {
return a_Position.y < ((a_Position.z > 0.5) ? 1.0 : 0.5); return (a_RelPosition.y < ((a_RelPosition.z > 0.5) ? 1.0 : 0.5));
} }
else if ((a_BlockMeta & 0x3) == 3) // tall side is north (-Z) else if ((a_BlockMeta & 0x3) == 3) // tall side is north (-Z)
{ {
return a_Position.y < ((a_Position.z < 0.5) ? 1.0 : 0.5); return (a_RelPosition.y < ((a_RelPosition.z < 0.5) ? 1.0 : 0.5));
} }
return false; return false;
} }

View File

@ -36,9 +36,9 @@ public:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) override
{ {
return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) == E_BLOCK_FARMLAND)); return ((a_RelPos.y > 0) && (a_Chunk.GetBlock(a_RelPos.addedY(-1)) == E_BLOCK_FARMLAND));
} }

View File

@ -32,36 +32,32 @@ public:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) override
{ {
if (a_RelY <= 0) if (a_RelPos.y <= 0)
{ {
return false; return false;
} }
switch (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)) switch (a_Chunk.GetBlock(a_RelPos.addedY(-1)))
{ {
case E_BLOCK_DIRT: case E_BLOCK_DIRT:
case E_BLOCK_GRASS: case E_BLOCK_GRASS:
case E_BLOCK_FARMLAND: case E_BLOCK_FARMLAND:
case E_BLOCK_SAND: case E_BLOCK_SAND:
{ {
static const struct static const Vector3i Coords[] =
{ {
int x, z; {-1, -1, 0},
} Coords[] = { 1, -1, 0},
{ { 0, -1, -1},
{-1, 0}, { 0, -1, 1},
{ 1, 0},
{ 0, -1},
{ 0, 1},
} ; } ;
a_RelY -= 1;
for (size_t i = 0; i < ARRAYCOUNT(Coords); i++) for (size_t i = 0; i < ARRAYCOUNT(Coords); i++)
{ {
BLOCKTYPE BlockType; BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta; NIBBLETYPE BlockMeta;
if (!a_Chunk.UnboundedRelGetBlock(a_RelX + Coords[i].x, a_RelY, a_RelZ + Coords[i].z, BlockType, BlockMeta)) if (!a_Chunk.UnboundedRelGetBlock(a_RelPos + Coords[i], BlockType, BlockMeta))
{ {
// Too close to the edge, cannot simulate // Too close to the edge, cannot simulate
return true; return true;

View File

@ -10,17 +10,34 @@
class cBlockTNTHandler : class cBlockTNTHandler :
public cBlockHandler public cBlockHandler
{ {
using Super = cBlockHandler;
public: public:
cBlockTNTHandler(BLOCKTYPE a_BlockType)
: cBlockHandler(a_BlockType) cBlockTNTHandler(BLOCKTYPE a_BlockType):
Super(a_BlockType)
{ {
} }
virtual void OnCancelRightClick(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) override
virtual void OnCancelRightClick(
cChunkInterface & a_ChunkInterface,
cWorldInterface & a_WorldInterface,
cPlayer & a_Player,
const Vector3i a_BlockPos,
eBlockFace a_BlockFace
) override
{ {
a_WorldInterface.SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, a_Player); a_WorldInterface.SendBlockTo(a_BlockPos, a_Player);
} }
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{ {
UNUSED(a_Meta); UNUSED(a_Meta);

View File

@ -54,14 +54,14 @@ public:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) override
{ {
if (a_RelY <= 0) if (a_RelPos.y <= 0)
{ {
return false; return false;
} }
BLOCKTYPE BelowBlock = a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ); BLOCKTYPE BelowBlock = a_Chunk.GetBlock(a_RelPos.addedY(-1));
return IsBlockTypeOfDirt(BelowBlock); return IsBlockTypeOfDirt(BelowBlock);
} }

View File

@ -8,7 +8,7 @@
class cBlockTorchHandler : class cBlockTorchHandler:
public cClearMetaOnDrop<cMetaRotator<cBlockHandler, 0x7, 0x4, 0x1, 0x3, 0x2>> public cClearMetaOnDrop<cMetaRotator<cBlockHandler, 0x7, 0x4, 0x1, 0x3, 0x2>>
{ {
using Super = cClearMetaOnDrop<cMetaRotator<cBlockHandler, 0x7, 0x4, 0x1, 0x3, 0x2>>; using Super = cClearMetaOnDrop<cMetaRotator<cBlockHandler, 0x7, 0x4, 0x1, 0x3, 0x2>>;
@ -20,39 +20,46 @@ public:
{ {
} }
virtual bool GetPlacementBlockTypeMeta( virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player, cChunkInterface & a_ChunkInterface,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, cPlayer & a_Player,
int a_CursorX, int a_CursorY, int a_CursorZ, const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override ) override
{ {
BLOCKTYPE Block; BLOCKTYPE ClickedBlockType;
NIBBLETYPE Meta; NIBBLETYPE ClickedBlockMeta;
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, true); // Set to clicked block auto ClickedBlockPos = AddFaceDirection(a_PlacedBlockPos, a_ClickedBlockFace, true);
a_ChunkInterface.GetBlockTypeMeta({a_BlockX, a_BlockY, a_BlockZ}, Block, Meta); a_ChunkInterface.GetBlockTypeMeta(ClickedBlockPos, ClickedBlockType, ClickedBlockMeta);
if (!CanBePlacedOn(ClickedBlockType, ClickedBlockMeta, a_ClickedBlockFace))
if (!CanBePlacedOn(Block, Meta, a_BlockFace)) // Try to preserve original direction
{ {
// Torch couldn't be placed on whatever face was clicked, last ditch resort - find another face // Couldn't be placed on whatever face was clicked, last ditch resort - find another face
a_ClickedBlockFace = FindSuitableFace(a_ChunkInterface, a_PlacedBlockPos); // Set a_BlockFace to a valid direction which will be converted later to a metadata
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, false); // Reset to torch block if (a_ClickedBlockFace == BLOCK_FACE_NONE)
a_BlockFace = FindSuitableFace(a_ChunkInterface, a_BlockX, a_BlockY, a_BlockZ); // Set a_BlockFace to a valid direction which will be converted later to a metadata
if (a_BlockFace == BLOCK_FACE_NONE)
{ {
// No attachable face found - don't place the torch // No attachable face found - don't place the torch
return false; return false;
} }
} }
a_BlockType = m_BlockType; a_BlockType = m_BlockType;
a_BlockMeta = DirectionToMetaData(a_BlockFace); a_BlockMeta = BlockFaceToMetaData(a_ClickedBlockFace);
return true; return true;
} }
inline static NIBBLETYPE DirectionToMetaData(eBlockFace a_Direction)
/** Converts the block face of the neighbor to which the torch is attached, to the torch block's meta. */
inline static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_NeighborBlockFace)
{ {
switch (a_Direction) switch (a_NeighborBlockFace)
{ {
case BLOCK_FACE_BOTTOM: ASSERT(!"Shouldn't be getting this face"); return 0; case BLOCK_FACE_BOTTOM: ASSERT(!"Shouldn't be getting this face"); return 0;
case BLOCK_FACE_TOP: return E_META_TORCH_FLOOR; case BLOCK_FACE_TOP: return E_META_TORCH_FLOOR;
@ -69,7 +76,12 @@ public:
return 0x0; return 0x0;
} }
inline static eBlockFace MetaDataToDirection(NIBBLETYPE a_MetaData)
/** Converts the torch block's meta to the block face of the neighbor to which the torch is attached. */
inline static eBlockFace MetaDataToBlockFace(NIBBLETYPE a_MetaData)
{ {
switch (a_MetaData) switch (a_MetaData)
{ {
@ -88,6 +100,11 @@ public:
return BLOCK_FACE_TOP; return BLOCK_FACE_TOP;
} }
/** Returns true if the torch can be placed on the specified block's face. */
static bool CanBePlacedOn(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, eBlockFace a_BlockFace) static bool CanBePlacedOn(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, eBlockFace a_BlockFace)
{ {
switch (a_BlockType) switch (a_BlockType)
@ -115,7 +132,7 @@ public:
case E_BLOCK_STONE_SLAB: case E_BLOCK_STONE_SLAB:
case E_BLOCK_WOODEN_SLAB: case E_BLOCK_WOODEN_SLAB:
{ {
// Toches can be placed on the top of these slabs only if the occupy the top half of the voxel // Toches can be placed only on the top of top-half-slabs
return ((a_BlockFace == BLOCK_FACE_YP) && ((a_BlockMeta & 0x08) == 0x08)); return ((a_BlockFace == BLOCK_FACE_YP) && ((a_BlockMeta & 0x08) == 0x08));
} }
case E_BLOCK_OAK_WOOD_STAIRS: case E_BLOCK_OAK_WOOD_STAIRS:
@ -146,45 +163,52 @@ public:
} }
} }
/** Finds a suitable face to place the torch, returning BLOCK_FACE_NONE on failure */
static eBlockFace FindSuitableFace(cChunkInterface & a_ChunkInterface, int a_BlockX, int a_BlockY, int a_BlockZ)
{
for (int i = BLOCK_FACE_YM; i <= BLOCK_FACE_XP; i++) // Loop through all directions
{
eBlockFace Face = static_cast<eBlockFace>(i);
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, Face, true);
BLOCKTYPE BlockInQuestion;
NIBBLETYPE BlockInQuestionMeta;
a_ChunkInterface.GetBlockTypeMeta({a_BlockX, a_BlockY, a_BlockZ}, BlockInQuestion, BlockInQuestionMeta);
if (CanBePlacedOn(BlockInQuestion, BlockInQuestionMeta, Face))
/** Returns a suitable neighbor's blockface to place the torch at the specified pos
Returns BLOCK_FACE_NONE on failure */
static eBlockFace FindSuitableFace(cChunkInterface & a_ChunkInterface, const Vector3i a_TorchPos)
{
for (int i = BLOCK_FACE_YM; i <= BLOCK_FACE_XP; i++) // Loop through all faces
{
auto Face = static_cast<eBlockFace>(i);
auto NeighborPos = AddFaceDirection(a_TorchPos, Face, true);
BLOCKTYPE NeighborBlockType;
NIBBLETYPE NeighborBlockMeta;
a_ChunkInterface.GetBlockTypeMeta(NeighborPos, NeighborBlockType, NeighborBlockMeta);
if (CanBePlacedOn(NeighborBlockType, NeighborBlockMeta, Face))
{ {
return Face; return Face;
} }
else
{
// Reset coords in preparation for next iteration
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, Face, false);
}
} }
return BLOCK_FACE_NONE; return BLOCK_FACE_NONE;
} }
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
{
eBlockFace Face = MetaDataToDirection(a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ));
AddFaceDirection(a_RelX, a_RelY, a_RelZ, Face, true);
BLOCKTYPE BlockInQuestion;
NIBBLETYPE BlockInQuestionMeta;
if (!a_Chunk.UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ, BlockInQuestion, BlockInQuestionMeta))
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) override
{
auto Face = MetaDataToBlockFace(a_Chunk.GetMeta(a_RelPos));
auto NeighborRelPos = AddFaceDirection(a_RelPos, Face, true);
BLOCKTYPE NeighborBlockType;
NIBBLETYPE NeighborBlockMeta;
if (!a_Chunk.UnboundedRelGetBlock(NeighborRelPos, NeighborBlockType, NeighborBlockMeta))
{ {
// Neighbor in an unloaded chunk, bail out without changint this.
return false; return false;
} }
return CanBePlacedOn(BlockInQuestion, BlockInQuestionMeta, Face); return CanBePlacedOn(NeighborBlockType, NeighborBlockMeta, Face);
} }
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{ {
UNUSED(a_Meta); UNUSED(a_Meta);

View File

@ -32,7 +32,14 @@ public:
virtual bool OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override virtual bool OnUse(
cChunkInterface & a_ChunkInterface,
cWorldInterface & a_WorldInterface,
cPlayer & a_Player,
const Vector3i a_BlockPos,
eBlockFace a_BlockFace,
const Vector3i a_CursorPos
) override
{ {
if (m_BlockType == E_BLOCK_IRON_TRAPDOOR) if (m_BlockType == E_BLOCK_IRON_TRAPDOOR)
{ {
@ -41,36 +48,56 @@ public:
} }
// Flip the ON bit on / off using the XOR bitwise operation // Flip the ON bit on / off using the XOR bitwise operation
NIBBLETYPE Meta = (a_ChunkInterface.GetBlockMeta({a_BlockX, a_BlockY, a_BlockZ}) ^ 0x04); NIBBLETYPE Meta = (a_ChunkInterface.GetBlockMeta(a_BlockPos) ^ 0x04);
a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta); a_ChunkInterface.SetBlockMeta(a_BlockPos, Meta);
a_WorldInterface.GetBroadcastManager().BroadcastSoundParticleEffect(EffectID::SFX_RANDOM_FENCE_GATE_OPEN, { a_BlockX, a_BlockY, a_BlockZ }, 0, a_Player.GetClientHandle()); a_WorldInterface.GetBroadcastManager().BroadcastSoundParticleEffect(EffectID::SFX_RANDOM_FENCE_GATE_OPEN, a_BlockPos, 0, a_Player.GetClientHandle());
return true; return true;
} }
virtual void OnCancelRightClick(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) override
virtual void OnCancelRightClick(
cChunkInterface & a_ChunkInterface,
cWorldInterface & a_WorldInterface,
cPlayer & a_Player,
const Vector3i a_BlockPos,
eBlockFace a_BlockFace
) override
{ {
UNUSED(a_ChunkInterface); UNUSED(a_ChunkInterface);
a_WorldInterface.SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, a_Player); a_WorldInterface.SendBlockTo(a_BlockPos, a_Player);
} }
virtual bool GetPlacementBlockTypeMeta( virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player, cChunkInterface & a_ChunkInterface,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, cPlayer & a_Player,
int a_CursorX, int a_CursorY, int a_CursorZ, const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override ) override
{ {
a_BlockType = m_BlockType; a_BlockType = m_BlockType;
a_BlockMeta = BlockFaceToMetaData(a_BlockFace); a_BlockMeta = BlockFaceToMetaData(a_ClickedBlockFace);
if (a_CursorY > 7) if (a_CursorPos.y > 7)
{ {
a_BlockMeta |= 0x8; a_BlockMeta |= 0x8;
} }
return true; return true;
} }
inline static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_BlockFace) inline static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_BlockFace)
{ {
switch (a_BlockFace) switch (a_BlockFace)
@ -90,6 +117,10 @@ public:
UNREACHABLE("Unsupported block face"); UNREACHABLE("Unsupported block face");
} }
inline static eBlockFace BlockMetaDataToBlockFace(NIBBLETYPE a_Meta) inline static eBlockFace BlockMetaDataToBlockFace(NIBBLETYPE a_Meta)
{ {
switch (a_Meta & 0x3) switch (a_Meta & 0x3)
@ -106,6 +137,10 @@ public:
} }
} }
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{ {
UNUSED(a_Meta); UNUSED(a_Meta);

View File

@ -24,14 +24,16 @@ public:
virtual bool GetPlacementBlockTypeMeta( virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player, cChunkInterface & a_ChunkInterface,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, cPlayer & a_Player,
int a_CursorX, int a_CursorY, int a_CursorZ, const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override ) override
{ {
a_BlockType = m_BlockType; a_BlockType = m_BlockType;
a_BlockMeta = DirectionToMetadata(a_BlockFace); a_BlockMeta = DirectionToMetadata(a_ClickedBlockFace);
return true; return true;
} }
@ -79,16 +81,17 @@ public:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) override
{ {
NIBBLETYPE Meta; auto Meta = a_Chunk.GetMeta(a_RelPos);
a_Chunk.UnboundedRelGetBlockMeta(a_RelX, a_RelY, a_RelZ, Meta); auto NeighborPos = AddFaceDirection(a_RelPos, MetadataToDirection(Meta), true);
if (!cChunkDef::IsValidHeight(NeighborPos.y))
AddFaceDirection(a_RelX, a_RelY, a_RelZ, MetadataToDirection(Meta), true); {
BLOCKTYPE BlockIsOn; return false;
a_Chunk.UnboundedRelGetBlockType(a_RelX, a_RelY, a_RelZ, BlockIsOn); }
BLOCKTYPE NeighborBlockType;
return ((a_RelY > 0) && cBlockInfo::FullyOccupiesVoxel(BlockIsOn)); a_Chunk.UnboundedRelGetBlockType(a_RelPos, NeighborBlockType);
return cBlockInfo::FullyOccupiesVoxel(NeighborBlockType);
} }

View File

@ -23,23 +23,25 @@ public:
virtual bool GetPlacementBlockTypeMeta( virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player, cChunkInterface & a_ChunkInterface,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, cPlayer & a_Player,
int a_CursorX, int a_CursorY, int a_CursorZ, const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override ) override
{ {
// TODO: Disallow placement where the vine doesn't attach to something properly // TODO: Disallow placement where the vine doesn't attach to something properly
BLOCKTYPE BlockType = 0; BLOCKTYPE BlockType = 0;
NIBBLETYPE BlockMeta; NIBBLETYPE BlockMeta;
a_ChunkInterface.GetBlockTypeMeta({a_BlockX, a_BlockY, a_BlockZ}, BlockType, BlockMeta); a_ChunkInterface.GetBlockTypeMeta(a_PlacedBlockPos, BlockType, BlockMeta);
if (BlockType == m_BlockType) if (BlockType == m_BlockType)
{ {
a_BlockMeta = BlockMeta | DirectionToMetaData(a_BlockFace); a_BlockMeta = BlockMeta | DirectionToMetaData(a_ClickedBlockFace);
} }
else else
{ {
a_BlockMeta = DirectionToMetaData(a_BlockFace); a_BlockMeta = DirectionToMetaData(a_ClickedBlockFace);
} }
a_BlockType = m_BlockType; a_BlockType = m_BlockType;
return true; return true;

View File

@ -33,39 +33,45 @@ public:
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, const Vector3i a_RelPos, const cChunk & a_Chunk) override
{ {
int BlockX = (a_Chunk.GetPosX() * cChunkDef::Width) + a_RelX; auto NeighborPos = a_RelPos + GetOffsetBehindTheSign(a_Chunk.GetMeta(a_RelPos));
int BlockZ = (a_Chunk.GetPosZ() * cChunkDef::Width) + a_RelZ; BLOCKTYPE NeighborType;
GetBlockCoordsBehindTheSign(a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ), BlockX, BlockZ); if (!a_Chunk.UnboundedRelGetBlockType(NeighborPos, NeighborType))
BLOCKTYPE Type = a_ChunkInterface.GetBlock({BlockX, a_RelY, BlockZ}); {
// The neighbor is not accessible (unloaded chunk), bail out without changing this
return ((Type == E_BLOCK_WALLSIGN) || (Type == E_BLOCK_SIGN_POST) || cBlockInfo::IsSolid(Type)); return true;
}
return ((NeighborType == E_BLOCK_WALLSIGN) || (NeighborType == E_BLOCK_SIGN_POST) || cBlockInfo::IsSolid(NeighborType));
} }
static void GetBlockCoordsBehindTheSign(NIBBLETYPE a_BlockMeta, int & a_BlockX, int & a_BlockZ) /** Returns the offset from the sign coords to the block to which the wallsign is attached, based on the wallsign's block meta.
Asserts / returns a zero vector on wrong meta. */
static Vector3i GetOffsetBehindTheSign(NIBBLETYPE a_BlockMeta)
{ {
switch (a_BlockMeta) switch (a_BlockMeta)
{ {
case 2: a_BlockZ++; break; case 2: return Vector3i( 0, 0, 1);
case 3: a_BlockZ--; break; case 3: return Vector3i( 0, 0, -1);
case 4: a_BlockX++; break; case 4: return Vector3i( 1, 0, 0);
case 5: a_BlockX--; break; case 5: return Vector3i(-1, 0, 0);
default: break;
} }
ASSERT(!"Invalid wallsign block meta");
return Vector3i();
} }
static NIBBLETYPE DirectionToMetaData(eBlockFace a_Direction) /** Converts the block face of the neighbor to which the wallsign is attached to the wallsign block's meta. */
static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_NeighborBlockFace)
{ {
switch (a_Direction) switch (a_NeighborBlockFace)
{ {
case BLOCK_FACE_ZM: return 0x02; case BLOCK_FACE_ZM: return 0x02;
case BLOCK_FACE_ZP: return 0x03; case BLOCK_FACE_ZP: return 0x03;

View File

@ -12,24 +12,46 @@
class cBlockWorkbenchHandler: class cBlockWorkbenchHandler:
public cBlockHandler public cBlockHandler
{ {
using Super = cBlockHandler;
public: public:
cBlockWorkbenchHandler(BLOCKTYPE a_BlockType)
: cBlockHandler(a_BlockType) cBlockWorkbenchHandler(BLOCKTYPE a_BlockType):
Super(a_BlockType)
{ {
} }
virtual bool OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override
virtual bool OnUse(
cChunkInterface & a_ChunkInterface,
cWorldInterface & a_WorldInterface,
cPlayer & a_Player,
const Vector3i a_BlockPos,
eBlockFace a_BlockFace,
const Vector3i a_CursorPos
) override
{ {
cWindow * Window = new cCraftingWindow(a_BlockX, a_BlockY, a_BlockZ); cWindow * Window = new cCraftingWindow(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z);
a_Player.OpenWindow(*Window); a_Player.OpenWindow(*Window);
return true; return true;
} }
virtual bool IsUseable(void) override virtual bool IsUseable(void) override
{ {
return true; return true;
} }
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{ {
UNUSED(a_Meta); UNUSED(a_Meta);

View File

@ -189,7 +189,8 @@ public:
cYawRotator(BLOCKTYPE a_BlockType): cYawRotator(BLOCKTYPE a_BlockType):
Super(a_BlockType) Super(a_BlockType)
{} {
}
@ -197,13 +198,14 @@ public:
virtual bool GetPlacementBlockTypeMeta( virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player, cChunkInterface & a_ChunkInterface, cPlayer & a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, const Vector3i a_BlockPos,
int a_CursorX, int a_CursorY, int a_CursorZ, eBlockFace a_BlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override ) override
{ {
NIBBLETYPE BaseMeta; NIBBLETYPE BaseMeta;
if (!Super::GetPlacementBlockTypeMeta(a_ChunkInterface, a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, BaseMeta)) if (!Super::GetPlacementBlockTypeMeta(a_ChunkInterface, a_Player, a_BlockPos, a_BlockFace, a_CursorPos, a_BlockType, BaseMeta))
{ {
return false; return false;
} }
@ -259,25 +261,29 @@ class cPitchYawRotator:
public cYawRotator<Base, BitMask, North, East, South, West> public cYawRotator<Base, BitMask, North, East, South, West>
{ {
using Super = cYawRotator<Base, BitMask, North, East, South, West>; using Super = cYawRotator<Base, BitMask, North, East, South, West>;
public: public:
cPitchYawRotator(BLOCKTYPE a_BlockType): cPitchYawRotator(BLOCKTYPE a_BlockType):
Super(a_BlockType) Super(a_BlockType)
{} {
}
virtual bool GetPlacementBlockTypeMeta( virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player, cChunkInterface & a_ChunkInterface,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, cPlayer & a_Player,
int a_CursorX, int a_CursorY, int a_CursorZ, const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override ) override
{ {
NIBBLETYPE BaseMeta; NIBBLETYPE BaseMeta;
if (!Super::GetPlacementBlockTypeMeta(a_ChunkInterface, a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, BaseMeta)) if (!Super::GetPlacementBlockTypeMeta(a_ChunkInterface, a_Player, a_PlacedBlockPos, a_ClickedBlockFace, a_CursorPos, a_BlockType, BaseMeta))
{ {
return false; return false;
} }

View File

@ -60,6 +60,12 @@ public:
/** Sends the block on those coords to the player */ /** Sends the block on those coords to the player */
virtual void SendBlockTo(int a_BlockX, int a_BlockY, int a_BlockZ, cPlayer & a_Player) = 0; virtual void SendBlockTo(int a_BlockX, int a_BlockY, int a_BlockZ, cPlayer & a_Player) = 0;
/** Sends the block on those coords to the player */
inline void SendBlockTo(const Vector3i a_BlockPos, cPlayer & a_Player)
{
SendBlockTo(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, a_Player);
}
/** Calls the callback for each player in the list; returns true if all players processed, false if the callback aborted by returning true */ /** Calls the callback for each player in the list; returns true if all players processed, false if the callback aborted by returning true */
virtual bool ForEachPlayer(cPlayerListCallback a_Callback) = 0; virtual bool ForEachPlayer(cPlayerListCallback a_Callback) = 0;

View File

@ -572,7 +572,7 @@ public:
/** Converts the coord relative to this chunk into an absolute coord. /** Converts the coord relative to this chunk into an absolute coord.
Doesn't check relative coord validity. */ Doesn't check relative coord validity. */
Vector3i RelativeToAbsolute(Vector3i a_RelBlockPosition) Vector3i RelativeToAbsolute(Vector3i a_RelBlockPosition) const
{ {
return cChunkDef::RelativeToAbsolute(a_RelBlockPosition, {m_PosX, m_PosZ}); return cChunkDef::RelativeToAbsolute(a_RelBlockPosition, {m_PosX, m_PosZ});
} }

View File

@ -555,8 +555,17 @@ struct sSetBlock
/** Returns the absolute Z coord of the stored block. */ /** Returns the absolute Z coord of the stored block. */
int GetZ(void) const { return m_RelZ + cChunkDef::Width * m_ChunkZ; } int GetZ(void) const { return m_RelZ + cChunkDef::Width * m_ChunkZ; }
/** Returns the absolute position of the stored block. */ /** Returns the absolute coords of the stored block. */
Vector3i GetPos(void) const { return Vector3i(GetX(), GetY(), GetZ()); } Vector3i GetAbsolutePos() const
{
return Vector3i(GetX(), GetY(), GetZ());
}
/** Returns the relative position of the stored block within its chunk. */
Vector3i GetRelativePos() const
{
return Vector3i(m_RelX, m_RelY, m_RelZ);
}
}; };
typedef std::list<sSetBlock> sSetBlockList; typedef std::list<sSetBlock> sSetBlockList;

View File

@ -1165,7 +1165,7 @@ void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, eB
{ {
ItemHandler = cItemHandler::GetItemHandler(m_Player->GetInventory().GetShieldSlot()); ItemHandler = cItemHandler::GetItemHandler(m_Player->GetInventory().GetShieldSlot());
} }
ItemHandler->OnItemShoot(m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); ItemHandler->OnItemShoot(m_Player, {a_BlockX, a_BlockY, a_BlockZ}, a_BlockFace);
} }
return; return;
} }
@ -1294,10 +1294,10 @@ void cClientHandle::HandleBlockDigStarted(int a_BlockX, int a_BlockY, int a_Bloc
cWorld * World = m_Player->GetWorld(); cWorld * World = m_Player->GetWorld();
cChunkInterface ChunkInterface(World->GetChunkMap()); cChunkInterface ChunkInterface(World->GetChunkMap());
cBlockHandler * Handler = cBlockInfo::GetHandler(a_OldBlock); cBlockHandler * Handler = cBlockInfo::GetHandler(a_OldBlock);
Handler->OnDigging(ChunkInterface, *World, *m_Player, a_BlockX, a_BlockY, a_BlockZ); Handler->OnDigging(ChunkInterface, *World, *m_Player, {a_BlockX, a_BlockY, a_BlockZ});
cItemHandler * ItemHandler = cItemHandler::GetItemHandler(m_Player->GetEquippedItem()); cItemHandler * ItemHandler = cItemHandler::GetItemHandler(m_Player->GetEquippedItem());
ItemHandler->OnDiggingBlock(World, m_Player, m_Player->GetEquippedItem(), a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); ItemHandler->OnDiggingBlock(World, m_Player, m_Player->GetEquippedItem(), {a_BlockX, a_BlockY, a_BlockZ}, a_BlockFace);
} }
@ -1444,9 +1444,11 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
// TODO: This distance should be calculated from the point that the cursor pointing at, instead of the center of the block // TODO: This distance should be calculated from the point that the cursor pointing at, instead of the center of the block
// Distance from the block's center to the player's eye height // Distance from the block's center to the player's eye height
double Dist = (Vector3d(a_BlockX, a_BlockY, a_BlockZ) + Vector3d(0.5, 0.5, 0.5) - m_Player->GetEyePosition()).Length(); auto ClickedBlockPos = Vector3i(a_BlockX, a_BlockY, a_BlockZ);
FLOGD("HandleRightClick: {0}, face {1}, Hand: {2}, HeldItem: {3}; Dist: {4:.02f}", auto CursorPos = Vector3i(a_CursorX, a_CursorY, a_CursorZ);
Vector3i{a_BlockX, a_BlockY, a_BlockZ}, a_BlockFace, a_Hand, ItemToFullString(HeldItem), Dist double Dist = (Vector3d(ClickedBlockPos) + Vector3d(0.5, 0.5, 0.5) - m_Player->GetEyePosition()).Length();
FLOGD("HandleRightClick: {0}, face {1}, Cursor {2}, Hand: {3}, HeldItem: {4}; Dist: {5:.02f}",
ClickedBlockPos, a_BlockFace, CursorPos, a_Hand, ItemToFullString(HeldItem), Dist
); );
// Check the reach distance: // Check the reach distance:
@ -1464,7 +1466,7 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
{ {
BLOCKTYPE BlockType; BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta; NIBBLETYPE BlockMeta;
World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); World->GetBlockTypeMeta(ClickedBlockPos, BlockType, BlockMeta);
cBlockHandler * BlockHandler = cBlockInfo::GetHandler(BlockType); cBlockHandler * BlockHandler = cBlockInfo::GetHandler(BlockType);
bool Placeable = ItemHandler->IsPlaceable() && !m_Player->IsGameModeAdventure() && !m_Player->IsGameModeSpectator(); bool Placeable = ItemHandler->IsPlaceable() && !m_Player->IsGameModeAdventure() && !m_Player->IsGameModeSpectator();
@ -1476,7 +1478,7 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
cChunkInterface ChunkInterface(World->GetChunkMap()); cChunkInterface ChunkInterface(World->GetChunkMap());
if (!PlgMgr->CallHookPlayerUsingBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta)) if (!PlgMgr->CallHookPlayerUsingBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta))
{ {
if (BlockHandler->OnUse(ChunkInterface, *World, *m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ)) if (BlockHandler->OnUse(ChunkInterface, *World, *m_Player, ClickedBlockPos, a_BlockFace, CursorPos))
{ {
// block use was successful, we're done // block use was successful, we're done
PlgMgr->CallHookPlayerUsedBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta); PlgMgr->CallHookPlayerUsedBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta);
@ -1487,14 +1489,14 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
if (!Success && Placeable) if (!Success && Placeable)
{ {
// place a block // place a block
Success = ItemHandler->OnPlayerPlace(*World, *m_Player, HeldItem, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ); Success = ItemHandler->OnPlayerPlace(*World, *m_Player, HeldItem, {a_BlockX, a_BlockY, a_BlockZ}, a_BlockFace, {a_CursorX, a_CursorY, a_CursorZ});
} }
} }
else else
{ {
// TODO: OnCancelRightClick seems to do the same thing with updating blocks at the end of this function. Need to double check // TODO: OnCancelRightClick seems to do the same thing with updating blocks at the end of this function. Need to double check
// A plugin doesn't agree with the action, replace the block on the client and quit: // A plugin doesn't agree with the action, replace the block on the client and quit:
BlockHandler->OnCancelRightClick(ChunkInterface, *World, *m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); BlockHandler->OnCancelRightClick(ChunkInterface, *World, *m_Player, {a_BlockX, a_BlockY, a_BlockZ}, a_BlockFace);
} }
} }
else if (Placeable) else if (Placeable)
@ -1507,7 +1509,7 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
return; return;
} }
// place a block // place a block
Success = ItemHandler->OnPlayerPlace(*World, *m_Player, HeldItem, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ); Success = ItemHandler->OnPlayerPlace(*World, *m_Player, HeldItem, {a_BlockX, a_BlockY, a_BlockZ}, a_BlockFace, {a_CursorX, a_CursorY, a_CursorZ});
} }
else else
{ {
@ -1516,7 +1518,7 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
{ {
// All plugins agree with using the item // All plugins agree with using the item
cBlockInServerPluginInterface PluginInterface(*World); cBlockInServerPluginInterface PluginInterface(*World);
ItemHandler->OnItemUse(World, m_Player, PluginInterface, HeldItem, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); ItemHandler->OnItemUse(World, m_Player, PluginInterface, HeldItem, {a_BlockX, a_BlockY, a_BlockZ}, a_BlockFace);
PlgMgr->CallHookPlayerUsedItem(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ); PlgMgr->CallHookPlayerUsedItem(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ);
Success = true; Success = true;
} }
@ -1827,7 +1829,7 @@ void cClientHandle::HandleUseItem(eHand a_Hand)
{ {
// All plugins agree with using the item // All plugins agree with using the item
cBlockInServerPluginInterface PluginInterface(*World); cBlockInServerPluginInterface PluginInterface(*World);
ItemHandler->OnItemUse(World, m_Player, PluginInterface, HeldItem, -1, 255, -1, BLOCK_FACE_NONE); ItemHandler->OnItemUse(World, m_Player, PluginInterface, HeldItem, {-1, 255, -1}, BLOCK_FACE_NONE);
PlgMgr->CallHookPlayerUsedItem(*m_Player, -1, 255, -1, BLOCK_FACE_NONE, 0, 0, 0); PlgMgr->CallHookPlayerUsedItem(*m_Player, -1, 255, -1, BLOCK_FACE_NONE, 0, 0, 0);
} }
} }
@ -2641,7 +2643,7 @@ void cClientHandle::SendEntityVelocity(const cEntity & a_Entity)
void cClientHandle::SendExplosion(double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion) void cClientHandle::SendExplosion(const Vector3d a_Pos, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d a_PlayerMotion)
{ {
if (m_NumExplosionsThisTick > MAX_EXPLOSIONS_PER_TICK) if (m_NumExplosionsThisTick > MAX_EXPLOSIONS_PER_TICK)
{ {
@ -2652,7 +2654,7 @@ void cClientHandle::SendExplosion(double a_BlockX, double a_BlockY, double a_Blo
// Update the statistics: // Update the statistics:
m_NumExplosionsThisTick++; m_NumExplosionsThisTick++;
m_Protocol->SendExplosion(a_BlockX, a_BlockY, a_BlockZ, a_Radius, a_BlocksAffected, a_PlayerMotion); m_Protocol->SendExplosion(a_Pos.x, a_Pos.y, a_Pos.z, a_Radius, a_BlocksAffected, a_PlayerMotion);
} }

View File

@ -173,7 +173,7 @@ public: // tolua_export
void SendEntityVelocity (const cEntity & a_Entity); void SendEntityVelocity (const cEntity & a_Entity);
void SendExperience (void); void SendExperience (void);
void SendExperienceOrb (const cExpOrb & a_ExpOrb); void SendExperienceOrb (const cExpOrb & a_ExpOrb);
void SendExplosion (double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion); void SendExplosion (const Vector3d a_Pos, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d a_PlayerMotion);
void SendGameMode (eGameMode a_GameMode); void SendGameMode (eGameMode a_GameMode);
void SendHealth (void); void SendHealth (void);
void SendHeldItemChange (int a_ItemIndex); void SendHeldItemChange (int a_ItemIndex);

View File

@ -331,10 +331,7 @@ void cPawn::HandleFalling(void)
/* The blocks we're interested in relative to the player to account for larger than 1 blocks. /* The blocks we're interested in relative to the player to account for larger than 1 blocks.
This can be extended to do additional checks in case there are blocks that are represented as one block This can be extended to do additional checks in case there are blocks that are represented as one block
in memory but have a hitbox larger than 1 (like fences) */ in memory but have a hitbox larger than 1 (like fences) */
static const struct static const Vector3i BlockSampleOffsets[] =
{
int x, y, z;
} BlockSampleOffsets[] =
{ {
{ 0, 0, 0 }, // TODO: something went wrong here (offset 0?) { 0, 0, 0 }, // TODO: something went wrong here (offset 0?)
{ 0, -1, 0 }, // Potentially causes mis-detection (IsFootInWater) when player stands on block diagonal to water (i.e. on side of pool) { 0, -1, 0 }, // Potentially causes mis-detection (IsFootInWater) when player stands on block diagonal to water (i.e. on side of pool)
@ -354,26 +351,26 @@ void cPawn::HandleFalling(void)
/* We go through the blocks that we consider "relevant" */ /* We go through the blocks that we consider "relevant" */
for (size_t j = 0; j < ARRAYCOUNT(BlockSampleOffsets); j++) for (size_t j = 0; j < ARRAYCOUNT(BlockSampleOffsets); j++)
{ {
Vector3i BlockTestPosition = CrossTestPosition.Floor() + Vector3i(BlockSampleOffsets[j].x, BlockSampleOffsets[j].y, BlockSampleOffsets[j].z); Vector3i BlockTestPosition = CrossTestPosition.Floor() + BlockSampleOffsets[j];
if (!cChunkDef::IsValidHeight(BlockTestPosition.y)) if (!cChunkDef::IsValidHeight(BlockTestPosition.y))
{ {
continue; continue;
} }
BLOCKTYPE Block = GetWorld()->GetBlock(BlockTestPosition); BLOCKTYPE BlockType = GetWorld()->GetBlock(BlockTestPosition);
NIBBLETYPE BlockMeta = GetWorld()->GetBlockMeta(BlockTestPosition); NIBBLETYPE BlockMeta = GetWorld()->GetBlockMeta(BlockTestPosition);
/* we do the cross-shaped sampling to check for water / liquids, but only on our level because water blocks are never bigger than unit voxels */ /* we do the cross-shaped sampling to check for water / liquids, but only on our level because water blocks are never bigger than unit voxels */
if (j == 0) if (j == 0)
{ {
IsFootInWater |= IsBlockWater(Block); IsFootInWater |= IsBlockWater(BlockType);
IsFootInLiquid |= IsFootInWater || IsBlockLava(Block) || (Block == E_BLOCK_COBWEB); // okay so cobweb is not _technically_ a liquid... IsFootInLiquid |= IsFootInWater || IsBlockLava(BlockType) || (BlockType == E_BLOCK_COBWEB); // okay so cobweb is not _technically_ a liquid...
IsFootOnSlimeBlock |= (Block == E_BLOCK_SLIME_BLOCK); IsFootOnSlimeBlock |= (BlockType == E_BLOCK_SLIME_BLOCK);
} }
/* If the block is solid, and the blockhandler confirms the block to be inside, we're officially on the ground. */ /* If the block is solid, and the blockhandler confirms the block to be inside, we're officially on the ground. */
if ((cBlockInfo::IsSolid(Block)) && (cBlockInfo::GetHandler(Block)->IsInsideBlock(CrossTestPosition - BlockTestPosition, Block, BlockMeta))) if ((cBlockInfo::IsSolid(BlockType)) && (cBlockInfo::GetHandler(BlockType)->IsInsideBlock(CrossTestPosition - BlockTestPosition, BlockMeta)))
{ {
OnGround = true; OnGround = true;
} }

View File

@ -8,43 +8,52 @@
class cItemArmorHandler : class cItemArmorHandler:
public cItemHandler public cItemHandler
{ {
using Super = cItemHandler;
public: public:
cItemArmorHandler(int a_ItemType) :
cItemHandler(a_ItemType) cItemArmorHandler(int a_ItemType):
Super(a_ItemType)
{ {
} }
/** Move the armor to the armor slot of the player's inventory */ /** Move the armor to the armor slot of the player's inventory */
virtual bool OnItemUse( virtual bool OnItemUse(
cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item, cWorld * a_World,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace cPlayer * a_Player,
cBlockPluginInterface & a_PluginInterface,
const cItem & a_HeldItem,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace
) override ) override
{ {
int SlotNum; int SlotNum;
if (ItemCategory::IsHelmet(a_Item.m_ItemType)) if (ItemCategory::IsHelmet(a_HeldItem.m_ItemType))
{ {
SlotNum = 0; SlotNum = 0;
} }
else if (ItemCategory::IsChestPlate(a_Item.m_ItemType)) else if (ItemCategory::IsChestPlate(a_HeldItem.m_ItemType))
{ {
SlotNum = 1; SlotNum = 1;
} }
else if (ItemCategory::IsLeggings(a_Item.m_ItemType)) else if (ItemCategory::IsLeggings(a_HeldItem.m_ItemType))
{ {
SlotNum = 2; SlotNum = 2;
} }
else if (ItemCategory::IsBoots(a_Item.m_ItemType)) else if (ItemCategory::IsBoots(a_HeldItem.m_ItemType))
{ {
SlotNum = 3; SlotNum = 3;
} }
else else
{ {
LOGWARNING("Used unknown armor: %i", a_Item.m_ItemType); LOGWARNING("Used unknown armor: %i", a_HeldItem.m_ItemType);
return false; return false;
} }
@ -53,9 +62,9 @@ public:
return false; return false;
} }
a_Player->GetInventory().SetArmorSlot(SlotNum, a_Item.CopyOne()); a_Player->GetInventory().SetArmorSlot(SlotNum, a_HeldItem.CopyOne());
cItem Item(a_Item); cItem Item(a_HeldItem);
Item.m_ItemCount--; Item.m_ItemCount--;
if (Item.m_ItemCount <= 0) if (Item.m_ItemCount <= 0)
{ {
@ -67,6 +76,8 @@ public:
virtual bool CanRepairWithRawMaterial(short a_ItemType) override virtual bool CanRepairWithRawMaterial(short a_ItemType) override
{ {
switch (m_ItemType) switch (m_ItemType)

View File

@ -9,51 +9,60 @@
class cItemBedHandler : class cItemBedHandler:
public cItemHandler public cItemHandler
{ {
using Super = cItemHandler;
public: public:
cItemBedHandler(int a_ItemType) :
cItemHandler(a_ItemType) cItemBedHandler(int a_ItemType):
Super(a_ItemType)
{ {
} }
virtual bool IsPlaceable(void) override virtual bool IsPlaceable(void) override
{ {
return true; return true;
} }
virtual bool GetBlocksToPlace( virtual bool GetBlocksToPlace(
cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem, cWorld & a_World,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, cPlayer & a_Player,
int a_CursorX, int a_CursorY, int a_CursorZ, const cItem & a_EquippedItem,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
sSetBlockVector & a_BlocksToPlace sSetBlockVector & a_BlocksToPlace
) override ) override
{ {
// Can only be placed on the floor: // Can only be placed on the floor:
if (a_BlockFace != BLOCK_FACE_TOP) if (a_ClickedBlockFace != BLOCK_FACE_TOP)
{ {
return false; return false;
} }
// The "foot" block: // The "foot" block:
NIBBLETYPE BlockMeta = cBlockBedHandler::YawToMetaData(a_Player.GetYaw()); NIBBLETYPE BlockMeta = cBlockBedHandler::YawToMetaData(a_Player.GetYaw());
a_BlocksToPlace.emplace_back(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_BED, BlockMeta); a_BlocksToPlace.emplace_back(a_PlacedBlockPos, E_BLOCK_BED, BlockMeta);
// Check if there is empty space for the "head" block: // Check if there is empty space for the "head" block:
// (Vanilla only allows beds to be placed into air) // (Vanilla only allows beds to be placed into air)
Vector3i Direction = cBlockBedHandler::MetaDataToDirection(BlockMeta); auto Direction = cBlockBedHandler::MetaDataToDirection(BlockMeta);
if (a_World.GetBlock(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z) != E_BLOCK_AIR) auto HeadPos = a_PlacedBlockPos + Direction;
if (a_World.GetBlock(HeadPos) != E_BLOCK_AIR)
{ {
return false; return false;
} }
a_BlocksToPlace.emplace_back(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z, E_BLOCK_BED, BlockMeta | 0x08); a_BlocksToPlace.emplace_back(HeadPos, E_BLOCK_BED, BlockMeta | 0x08);
return true; return true;
} }
} ; };

View File

@ -15,43 +15,50 @@ class cItemBigFlowerHandler:
public: public:
cItemBigFlowerHandler(void): cItemBigFlowerHandler():
Super(E_BLOCK_BIG_FLOWER) Super(E_BLOCK_BIG_FLOWER)
{ {
} }
virtual bool GetBlocksToPlace( virtual bool GetBlocksToPlace(
cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem, cWorld & a_World,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, cPlayer & a_Player,
int a_CursorX, int a_CursorY, int a_CursorZ, const cItem & a_EquippedItem,
sSetBlockVector & a_BlocksToSet const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
sSetBlockVector & a_BlocksToPlace
) override ) override
{ {
// Can only be placed on dirt: // Can only be placed on dirt:
if ((a_BlockY <= 0) || !IsBlockTypeOfDirt(a_World.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ))) if ((a_PlacedBlockPos.y <= 0) || !IsBlockTypeOfDirt(a_World.GetBlock(a_PlacedBlockPos.addedY(-1))))
{ {
return false; return false;
} }
// Needs at least two free blocks to build in // Needs at least two free blocks to build in
if (a_BlockY >= cChunkDef::Height - 1) if (a_PlacedBlockPos.y >= cChunkDef::Height - 1)
{ {
return false; return false;
} }
auto TopPos = a_PlacedBlockPos.addedY(1);
BLOCKTYPE TopType; BLOCKTYPE TopType;
NIBBLETYPE TopMeta; NIBBLETYPE TopMeta;
a_World.GetBlockTypeMeta(a_BlockX, a_BlockY + 1, a_BlockZ, TopType, TopMeta); a_World.GetBlockTypeMeta(TopPos, TopType, TopMeta);
cChunkInterface ChunkInterface(a_World.GetChunkMap()); cChunkInterface ChunkInterface(a_World.GetChunkMap());
if (!BlockHandler(TopType)->DoesIgnoreBuildCollision(ChunkInterface, { a_BlockX, a_BlockY + 1, a_BlockZ }, a_Player, TopMeta)) if (!BlockHandler(TopType)->DoesIgnoreBuildCollision(ChunkInterface, TopPos, a_Player, TopMeta))
{ {
return false; return false;
} }
a_BlocksToSet.emplace_back(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_BIG_FLOWER, a_EquippedItem.m_ItemDamage & 0x07); a_BlocksToPlace.emplace_back(a_PlacedBlockPos, E_BLOCK_BIG_FLOWER, a_EquippedItem.m_ItemDamage & 0x07);
a_BlocksToSet.emplace_back(a_BlockX, a_BlockY + 1, a_BlockZ, E_BLOCK_BIG_FLOWER, E_META_BIG_FLOWER_TOP); a_BlocksToPlace.emplace_back(TopPos, E_BLOCK_BIG_FLOWER, E_META_BIG_FLOWER_TOP);
return true; return true;
} }
}; };

View File

@ -22,24 +22,32 @@ public:
virtual bool OnItemUse( virtual bool OnItemUse(
cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item, cWorld * a_World,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace cPlayer * a_Player,
cBlockPluginInterface & a_PluginInterface,
const cItem & a_HeldItem,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace
) override ) override
{ {
if ((a_BlockFace != BLOCK_FACE_YM) && (a_BlockFace != BLOCK_FACE_NONE)) // Only allow placing blocks on top of blocks, or when not in range of dest block:
if ((a_ClickedBlockFace != BLOCK_FACE_YM) && (a_ClickedBlockFace != BLOCK_FACE_NONE))
{ {
return false; return false;
} }
class cCallbacks : // Find the actual placement position by tracing line of sight until non-air block:
class cCallbacks:
public cBlockTracer::cCallbacks public cBlockTracer::cCallbacks
{ {
public: public:
Vector3d m_Pos; Vector3d m_Pos;
bool m_HasFound; bool m_HasFound;
cCallbacks(void) : cCallbacks():
m_HasFound(false) m_HasFound(false)
{ {
} }
@ -55,27 +63,21 @@ public:
return false; return false;
} }
} Callbacks; } Callbacks;
auto Start = a_Player->GetEyePosition() + a_Player->GetLookVector();
cLineBlockTracer Tracer(*a_World, Callbacks); auto End = a_Player->GetEyePosition() + a_Player->GetLookVector() * 5;
Vector3d Start(a_Player->GetEyePosition() + a_Player->GetLookVector()); cLineBlockTracer::Trace(*a_World, Callbacks, Start, End);
Vector3d End(a_Player->GetEyePosition() + a_Player->GetLookVector() * 5);
Tracer.Trace(Start.x, Start.y, Start.z, End.x, End.y, End.z);
if (!Callbacks.m_HasFound) if (!Callbacks.m_HasFound)
{ {
return false; return false;
} }
auto x = Callbacks.m_Pos.x;
auto y = Callbacks.m_Pos.y;
auto z = Callbacks.m_Pos.z;
auto bx = FloorC(x);
auto by = FloorC(y);
auto bz = FloorC(z);
// Block above must be air to spawn a boat (prevents spawning a boat underwater) // Block above must be air to spawn a boat (prevents spawning a boat underwater)
BLOCKTYPE BlockAbove = a_World->GetBlock(bx, by + 1, bz); auto PosAbove = Callbacks.m_Pos.Floor().addedY(1);
if (!cChunkDef::IsValidHeight(PosAbove.y))
{
return false;
}
BLOCKTYPE BlockAbove = a_World->GetBlock(PosAbove);
if (BlockAbove != E_BLOCK_AIR) if (BlockAbove != E_BLOCK_AIR)
{ {
return false; return false;

View File

@ -9,19 +9,28 @@
class cItemBottleHandler : class cItemBottleHandler:
public cItemHandler public cItemHandler
{ {
using Super = cItemHandler;
public: public:
cItemBottleHandler() :
cItemHandler(E_ITEM_GLASS_BOTTLE) cItemBottleHandler():
Super(E_ITEM_GLASS_BOTTLE)
{ {
} }
/** Searches for a water source block in the line of sight.
Returns true and sets a_BlockPos if a water source block is found within line-of-sight.
Returns false if not. */
bool GetBlockFromTrace(cWorld * a_World, cPlayer * a_Player, Vector3i & a_BlockPos) bool GetBlockFromTrace(cWorld * a_World, cPlayer * a_Player, Vector3i & a_BlockPos)
{ {
class cCallbacks : class cCallbacks:
public cBlockTracer::cCallbacks public cBlockTracer::cCallbacks
{ {
public: public:
@ -29,7 +38,7 @@ public:
bool m_HasHitFluid; bool m_HasHitFluid;
cCallbacks(void) : cCallbacks():
m_HasHitFluid(false) m_HasHitFluid(false)
{ {
} }
@ -49,31 +58,32 @@ public:
return false; return false;
} }
} Callbacks; } Callbacks;
auto Start = a_Player->GetEyePosition() + a_Player->GetLookVector();
cLineBlockTracer Tracer(*a_World, Callbacks); auto End = a_Player->GetEyePosition() + a_Player->GetLookVector() * 5;
Vector3d Start(a_Player->GetEyePosition() + a_Player->GetLookVector()); cLineBlockTracer::Trace(*a_World, Callbacks, Start, End);
Vector3d End(a_Player->GetEyePosition() + a_Player->GetLookVector() * 5);
Tracer.Trace(Start.x, Start.y, Start.z, End.x, End.y, End.z);
if (!Callbacks.m_HasHitFluid) if (!Callbacks.m_HasHitFluid)
{ {
return false; return false;
} }
a_BlockPos = Callbacks.m_Pos; a_BlockPos = Callbacks.m_Pos;
return true; return true;
} }
virtual bool OnItemUse( virtual bool OnItemUse(
cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item, cWorld * a_World,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace cPlayer * a_Player,
) override cBlockPluginInterface & a_PluginInterface,
const cItem & a_HeldItem,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace
) override
{ {
if (a_BlockFace != BLOCK_FACE_NONE) if (a_ClickedBlockFace != BLOCK_FACE_NONE)
{ {
return false; return false;
} }

View File

@ -22,9 +22,15 @@ public:
virtual bool OnItemUse( virtual bool OnItemUse(
cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item, cWorld * a_World,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace cPlayer * a_Player,
cBlockPluginInterface & a_PluginInterface,
const cItem & a_HeldItem,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace
) override ) override
{ {
ASSERT(a_Player != nullptr); ASSERT(a_Player != nullptr);
@ -41,9 +47,10 @@ public:
virtual void OnItemShoot(cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) override virtual void OnItemShoot(cPlayer * a_Player, const Vector3i a_BlockPos, eBlockFace a_BlockFace) override
{ {
// Actual shot - produce the arrow with speed based on the ticks that the bow was charged // Actual shot - produce the arrow with speed based on the number of ticks that the bow was charged
UNUSED(a_BlockPos);
ASSERT(a_Player != nullptr); ASSERT(a_Player != nullptr);
int BowCharge = a_Player->FinishChargingBow(); int BowCharge = a_Player->FinishChargingBow();
@ -88,7 +95,6 @@ public:
a_Player->UseEquippedItem(); a_Player->UseEquippedItem();
} }
if (a_Player->GetEquippedItem().m_Enchantments.GetLevel(cEnchantments::enchFlame) > 0) if (a_Player->GetEquippedItem().m_Enchantments.GetLevel(cEnchantments::enchFlame) > 0)
{ {
ArrowPtr->StartBurning(100); ArrowPtr->StartBurning(100);

View File

@ -7,12 +7,15 @@
class cItemBrewingStandHandler : class cItemBrewingStandHandler:
public cItemHandler public cItemHandler
{ {
using Super = cItemHandler;
public: public:
cItemBrewingStandHandler(int a_ItemType) :
cItemHandler(a_ItemType) cItemBrewingStandHandler(int a_ItemType):
Super(a_ItemType)
{ {
} }
@ -25,8 +28,9 @@ public:
virtual bool GetPlacementBlockTypeMeta( virtual bool GetPlacementBlockTypeMeta(
cWorld * a_World, cPlayer * a_Player, cWorld * a_World, cPlayer * a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, const Vector3i a_PlacedBlockPos,
int a_CursorX, int a_CursorY, int a_CursorZ, eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override ) override
{ {

View File

@ -16,25 +16,34 @@
class cItemBucketHandler : class cItemBucketHandler :
public cItemHandler public cItemHandler
{ {
using Super = cItemHandler;
public: public:
cItemBucketHandler(int a_ItemType) :
cItemHandler(a_ItemType) cItemBucketHandler(int a_ItemType):
Super(a_ItemType)
{ {
} }
virtual bool OnItemUse( virtual bool OnItemUse(
cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item, cWorld * a_World,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace cPlayer * a_Player,
cBlockPluginInterface & a_PluginInterface,
const cItem & a_HeldItem,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace
) override ) override
{ {
switch (m_ItemType) switch (m_ItemType)
{ {
case E_ITEM_BUCKET: return ScoopUpFluid(a_World, a_Player, a_Item, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); case E_ITEM_BUCKET: return ScoopUpFluid(a_World, a_Player, a_HeldItem, a_ClickedBlockPos, a_ClickedBlockFace);
case E_ITEM_LAVA_BUCKET: return PlaceFluid (a_World, a_Player, a_PluginInterface, a_Item, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, E_BLOCK_LAVA); case E_ITEM_LAVA_BUCKET: return PlaceFluid (a_World, a_Player, a_PluginInterface, a_HeldItem, a_ClickedBlockPos, a_ClickedBlockFace, E_BLOCK_LAVA);
case E_ITEM_WATER_BUCKET: return PlaceFluid (a_World, a_Player, a_PluginInterface, a_Item, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, E_BLOCK_WATER); case E_ITEM_WATER_BUCKET: return PlaceFluid (a_World, a_Player, a_PluginInterface, a_HeldItem, a_ClickedBlockPos, a_ClickedBlockFace, E_BLOCK_WATER);
default: default:
{ {
ASSERT(!"Unhandled ItemType"); ASSERT(!"Unhandled ItemType");
@ -45,7 +54,9 @@ public:
bool ScoopUpFluid(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace)
bool ScoopUpFluid(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, const Vector3i a_ClickedBlockPos, eBlockFace a_ClickedBlockFace)
{ {
// Players can't pick up fluid while in adventure mode. // Players can't pick up fluid while in adventure mode.
if (a_Player->IsGameModeAdventure()) if (a_Player->IsGameModeAdventure())
@ -53,7 +64,8 @@ public:
return false; return false;
} }
if (a_BlockFace != BLOCK_FACE_NONE) // Needs a valid clicked block:
if (a_ClickedBlockFace != BLOCK_FACE_NONE)
{ {
return false; return false;
} }
@ -114,7 +126,7 @@ public:
bool PlaceFluid( bool PlaceFluid(
cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item, 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, BLOCKTYPE a_FluidBlock const Vector3i a_BlockPos, eBlockFace a_BlockFace, BLOCKTYPE a_FluidBlock
) )
{ {
// Players can't place fluid while in adventure mode. // Players can't place fluid while in adventure mode.
@ -219,6 +231,8 @@ public:
bool GetPlacementCoordsFromTrace(cWorld * a_World, cPlayer * a_Player, Vector3i & a_BlockPos, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta, eBlockFace & a_BlockFace) bool GetPlacementCoordsFromTrace(cWorld * a_World, cPlayer * a_Player, Vector3i & a_BlockPos, BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta, eBlockFace & a_BlockFace)
{ {
class cCallbacks : class cCallbacks :

View File

@ -7,12 +7,15 @@
class cItemCakeHandler : class cItemCakeHandler:
public cItemHandler public cItemHandler
{ {
using Super = cItemHandler;
public: public:
cItemCakeHandler(int a_ItemType) :
cItemHandler(a_ItemType) cItemCakeHandler(int a_ItemType):
Super(a_ItemType)
{ {
} }
@ -25,8 +28,9 @@ public:
virtual bool GetPlacementBlockTypeMeta( virtual bool GetPlacementBlockTypeMeta(
cWorld * a_World, cPlayer * a_Player, cWorld * a_World, cPlayer * a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, const Vector3i a_PlacedBlockPos,
int a_CursorX, int a_CursorY, int a_CursorZ, eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override ) override
{ {

View File

@ -7,12 +7,15 @@
class cItemCauldronHandler : class cItemCauldronHandler:
public cItemHandler public cItemHandler
{ {
using Super = cItemHandler;
public: public:
cItemCauldronHandler(int a_ItemType) :
cItemHandler(a_ItemType) cItemCauldronHandler(int a_ItemType):
Super(a_ItemType)
{ {
} }
@ -25,8 +28,9 @@ public:
virtual bool GetPlacementBlockTypeMeta( virtual bool GetPlacementBlockTypeMeta(
cWorld * a_World, cPlayer * a_Player, cWorld * a_World, cPlayer * a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, const Vector3i a_PlacedBlockPos,
int a_CursorX, int a_CursorY, int a_CursorZ, eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override ) override
{ {

View File

@ -25,56 +25,57 @@ public:
/** We need an OnPlayerPlace override because we're processing neighbor chests and changing their metas, /** We need an OnPlayerPlace override because we're processing neighbor chests and changing their metas,
the parent class cannot do that. */ the parent class cannot do that. */
virtual bool OnPlayerPlace( virtual bool OnPlayerPlace(
cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem, cWorld & a_World,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, cPlayer & a_Player,
int a_CursorX, int a_CursorY, int a_CursorZ const cItem & a_EquippedItem,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos
) override ) override
{ {
if (a_BlockFace < 0) if (a_ClickedBlockFace < 0)
{ {
// Clicked in air // Clicked in air
return false; return false;
} }
if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height)) if (!cChunkDef::IsValidHeight(a_ClickedBlockPos.y))
{ {
// The clicked block is outside the world, ignore this call altogether (#128) // The clicked block is outside the world, ignore this call altogether (#128)
return false; return false;
} }
// Check if the block ignores build collision (water, grass etc.): // Check if the block ignores build collision (water, grass etc.):
BLOCKTYPE clickedBlock; BLOCKTYPE ClickedBlockType;
NIBBLETYPE clickedBlockMeta; NIBBLETYPE ClickedBlockMeta;
Vector3i blockPos(a_BlockX, a_BlockY, a_BlockZ); a_World.GetBlockTypeMeta(a_ClickedBlockPos, ClickedBlockType, ClickedBlockMeta);
a_World.GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, clickedBlock, clickedBlockMeta);
cChunkInterface ChunkInterface(a_World.GetChunkMap()); cChunkInterface ChunkInterface(a_World.GetChunkMap());
auto blockHandler = BlockHandler(clickedBlock); auto blockHandler = BlockHandler(ClickedBlockType);
if (blockHandler->DoesIgnoreBuildCollision(ChunkInterface, blockPos, a_Player, clickedBlockMeta)) Vector3i PlacePos;
if (blockHandler->DoesIgnoreBuildCollision(ChunkInterface, a_ClickedBlockPos, a_Player, ClickedBlockMeta))
{ {
blockHandler->OnPlayerBreakingBlock(ChunkInterface, a_World, a_Player, blockPos); blockHandler->OnPlayerBreakingBlock(ChunkInterface, a_World, a_Player, a_ClickedBlockPos);
PlacePos = a_ClickedBlockPos;
} }
else else
{ {
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); PlacePos = AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace);
if (!cChunkDef::IsValidHeight(PlacePos.y))
if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height))
{ {
// The block is being placed outside the world, ignore this packet altogether (#128) // The block is being placed outside the world, ignore this packet altogether (#128)
return false; return false;
} }
NIBBLETYPE PlaceMeta; // Check if the chest can overwrite the block at PlacePos:
BLOCKTYPE PlaceBlock; BLOCKTYPE PlaceBlock;
a_World.GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, PlaceBlock, PlaceMeta); NIBBLETYPE PlaceMeta;
a_World.GetBlockTypeMeta(PlacePos, PlaceBlock, PlaceMeta);
// Clicked on side of block, make sure that placement won't be cancelled if there is a slab able to be double slabbed. blockHandler = BlockHandler(PlaceBlock);
// No need to do combinability (dblslab) checks, client will do that here. if (!blockHandler->DoesIgnoreBuildCollision(ChunkInterface, PlacePos, a_Player, PlaceMeta))
if (blockHandler->DoesIgnoreBuildCollision(ChunkInterface, blockPos, a_Player, clickedBlockMeta))
{ {
// Tried to place a block into another?
// Happens when you place a block aiming at side of block with a torch on it or stem beside it
return false; return false;
} }
blockHandler->OnPlayerBreakingBlock(ChunkInterface, a_World, a_Player, PlacePos);
} }
// Check that there is at most one single neighbor of the same chest type: // Check that there is at most one single neighbor of the same chest type:
@ -88,7 +89,8 @@ public:
int NeighborIdx = -1; int NeighborIdx = -1;
for (size_t i = 0; i < ARRAYCOUNT(CrossCoords); i++) for (size_t i = 0; i < ARRAYCOUNT(CrossCoords); i++)
{ {
if (a_World.GetBlock(a_BlockX + CrossCoords[i].x, a_BlockY, a_BlockZ + CrossCoords[i].z) != m_ItemType) auto NeighborPos = PlacePos + CrossCoords[i];
if (a_World.GetBlock(NeighborPos) != m_ItemType)
{ {
continue; continue;
} }
@ -100,12 +102,11 @@ public:
NeighborIdx = static_cast<int>(i); NeighborIdx = static_cast<int>(i);
// Check that this neighbor is a single chest: // Check that this neighbor is a single chest:
int bx = a_BlockX + CrossCoords[i].x;
int bz = a_BlockZ + CrossCoords[i].z;
for (size_t j = 0; j < ARRAYCOUNT(CrossCoords); j++) for (size_t j = 0; j < ARRAYCOUNT(CrossCoords); j++)
{ {
if (a_World.GetBlock(bx + CrossCoords[j].x, a_BlockY, bz + CrossCoords[j].z) == m_ItemType) if (a_World.GetBlock(NeighborPos + CrossCoords[j]) == m_ItemType)
{ {
// Trying to place next to a dblchest
return false; return false;
} }
} // for j } // for j
@ -133,13 +134,14 @@ public:
} }
default: default:
{ {
// No neighbor, place based on yaw:
Meta = cBlockChestHandler::PlayerYawToMetaData(yaw); Meta = cBlockChestHandler::PlayerYawToMetaData(yaw);
break; break;
} }
} // switch (NeighborIdx) } // switch (NeighborIdx)
// Place the new chest: // Place the new chest:
if (!a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, ChestBlockType, Meta)) if (!a_Player.PlaceBlock(PlacePos.x, PlacePos.y, PlacePos.z, ChestBlockType, Meta))
{ {
return false; return false;
} }
@ -147,10 +149,10 @@ public:
// Adjust the existing chest, if any: // Adjust the existing chest, if any:
if (NeighborIdx != -1) if (NeighborIdx != -1)
{ {
a_World.FastSetBlock(a_BlockX + CrossCoords[NeighborIdx].x, a_BlockY, a_BlockZ + CrossCoords[NeighborIdx].z, ChestBlockType, Meta); a_World.FastSetBlock(PlacePos + CrossCoords[NeighborIdx], ChestBlockType, Meta);
} }
// Remove the "placed" item: // Remove the "placed" item from inventory:
if (a_Player.IsGameModeSurvival()) if (a_Player.IsGameModeSurvival())
{ {
a_Player.GetInventory().RemoveOneEquippedItem(); a_Player.GetInventory().RemoveOneEquippedItem();

View File

@ -8,24 +8,36 @@
class cItemComparatorHandler : class cItemComparatorHandler:
public cItemHandler public cItemHandler
{ {
using Super = cItemHandler;
public: public:
cItemComparatorHandler(int a_ItemType) :
cItemComparatorHandler(int a_ItemType):
cItemHandler(a_ItemType) cItemHandler(a_ItemType)
{ {
} }
virtual bool IsPlaceable(void) override virtual bool IsPlaceable(void) override
{ {
return true; return true;
} }
virtual bool GetPlacementBlockTypeMeta( virtual bool GetPlacementBlockTypeMeta(
cWorld * a_World, cPlayer * a_Player, cWorld * a_World, cPlayer * a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, const Vector3i a_PlacedBlockPos,
int a_CursorX, int a_CursorY, int a_CursorZ, eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override ) override
{ {

View File

@ -9,38 +9,45 @@
class cItemDoorHandler : class cItemDoorHandler:
public cItemHandler public cItemHandler
{ {
using Super = cItemHandler;
public: public:
cItemDoorHandler(int a_ItemType) :
cItemHandler(a_ItemType) cItemDoorHandler(int a_ItemType):
Super(a_ItemType)
{ {
} }
virtual bool GetBlocksToPlace( virtual bool GetBlocksToPlace(
cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem, cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, const Vector3i a_PlacedBlockPos,
int a_CursorX, int a_CursorY, int a_CursorZ, eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
sSetBlockVector & a_BlocksToSet sSetBlockVector & a_BlocksToSet
) override ) override
{ {
// Vanilla only allows door placement while clicking on the top face of the block below the door: // Vanilla only allows door placement while clicking on the top face of the block below the door:
if (a_BlockFace != BLOCK_FACE_TOP) if (a_ClickedBlockFace != BLOCK_FACE_TOP)
{ {
return false; return false;
} }
// Door (bottom block) can be placed in Y range of [1, 254]: // Door (bottom block) can be placed in Y range of [1, 254]:
if ((a_BlockY < 1) || (a_BlockY >= cChunkDef::Height - 2)) if ((a_PlacedBlockPos.y < 1) || (a_PlacedBlockPos.y >= cChunkDef::Height - 2))
{ {
return false; return false;
} }
// The door needs a compatible block below it: // The door needs a compatible block below it:
if (!cBlockDoorHandler::CanBeOn(a_World.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ), a_World.GetBlockMeta(a_BlockX, a_BlockY - 1, a_BlockZ))) auto BelowPos = a_PlacedBlockPos.addedY(-1);
if (!cBlockDoorHandler::CanBeOn(a_World.GetBlock(BelowPos), a_World.GetBlockMeta(BelowPos)))
{ {
return false; return false;
} }
@ -64,8 +71,9 @@ public:
} }
// Check the two blocks that will get replaced by the door: // Check the two blocks that will get replaced by the door:
BLOCKTYPE LowerBlockType = a_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ); auto UpperBlockPos = a_PlacedBlockPos.addedY(1);
BLOCKTYPE UpperBlockType = a_World.GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ); BLOCKTYPE LowerBlockType = a_World.GetBlock(a_PlacedBlockPos);
BLOCKTYPE UpperBlockType = a_World.GetBlock(UpperBlockPos);
if ( if (
!cBlockDoorHandler::CanReplaceBlock(LowerBlockType) || !cBlockDoorHandler::CanReplaceBlock(LowerBlockType) ||
!cBlockDoorHandler::CanReplaceBlock(UpperBlockType)) !cBlockDoorHandler::CanReplaceBlock(UpperBlockType))
@ -78,10 +86,10 @@ public:
Vector3i RelDirToOutside = cBlockDoorHandler::GetRelativeDirectionToOutside(LowerBlockMeta); Vector3i RelDirToOutside = cBlockDoorHandler::GetRelativeDirectionToOutside(LowerBlockMeta);
Vector3i LeftNeighborPos = RelDirToOutside; Vector3i LeftNeighborPos = RelDirToOutside;
LeftNeighborPos.TurnCW(); LeftNeighborPos.TurnCW();
LeftNeighborPos.Move(a_BlockX, a_BlockY, a_BlockZ); LeftNeighborPos.Move(a_PlacedBlockPos);
Vector3i RightNeighborPos = RelDirToOutside; Vector3i RightNeighborPos = RelDirToOutside;
RightNeighborPos.TurnCCW(); RightNeighborPos.TurnCCW();
RightNeighborPos.Move(a_BlockX, a_BlockY, a_BlockZ); RightNeighborPos.Move(a_PlacedBlockPos);
// Decide whether the hinge is on the left (default) or on the right: // Decide whether the hinge is on the left (default) or on the right:
NIBBLETYPE UpperBlockMeta = 0x08; NIBBLETYPE UpperBlockMeta = 0x08;
@ -89,7 +97,7 @@ public:
BLOCKTYPE RightNeighborBlock = a_World.GetBlock(RightNeighborPos); BLOCKTYPE RightNeighborBlock = a_World.GetBlock(RightNeighborPos);
/* /*
// DEBUG: // DEBUG:
FLOGD("Door being placed at {0}", Vector3i{a_BlockX, a_BlockY, a_BlockZ}); FLOGD("Door being placed at {0}", a_PlacedBlockPos);
FLOGD("RelDirToOutside: {0}", RelDirToOutside); FLOGD("RelDirToOutside: {0}", RelDirToOutside);
FLOGD("Left neighbor at {0}: {1} ({2})", LeftNeighborPos, LeftNeighborBlock, ItemTypeToString(LeftNeighborBlock)); FLOGD("Left neighbor at {0}: {1} ({2})", LeftNeighborPos, LeftNeighborBlock, ItemTypeToString(LeftNeighborBlock));
FLOGD("Right neighbor at {0}: {1} ({2})", RightNeighborPos, RightNeighborBlock, ItemTypeToString(RightNeighborBlock)); FLOGD("Right neighbor at {0}: {1} ({2})", RightNeighborPos, RightNeighborBlock, ItemTypeToString(RightNeighborBlock));
@ -108,12 +116,14 @@ public:
} }
// Set the blocks: // Set the blocks:
a_BlocksToSet.emplace_back(a_BlockX, a_BlockY, a_BlockZ, BlockType, LowerBlockMeta); a_BlocksToSet.emplace_back(a_PlacedBlockPos, BlockType, LowerBlockMeta);
a_BlocksToSet.emplace_back(a_BlockX, a_BlockY + 1, a_BlockZ, BlockType, UpperBlockMeta); a_BlocksToSet.emplace_back(UpperBlockPos, BlockType, UpperBlockMeta);
return true; return true;
} }
virtual bool IsPlaceable(void) override virtual bool IsPlaceable(void) override
{ {
return true; return true;

View File

@ -26,14 +26,18 @@ public:
virtual bool OnItemUse( virtual bool OnItemUse(
cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item, cWorld * a_World,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace cPlayer * a_Player,
cBlockPluginInterface & a_PluginInterface,
const cItem & a_HeldItem,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace
) override ) override
{ {
if ((a_Item.m_ItemDamage == E_META_DYE_WHITE) && (a_BlockFace != BLOCK_FACE_NONE)) if ((a_HeldItem.m_ItemDamage == E_META_DYE_WHITE) && (a_ClickedBlockFace != BLOCK_FACE_NONE))
{ {
// Bonemeal (white dye) is used to fertilize plants: // Bonemeal (white dye) is used to fertilize plants:
if (fertilizePlant(*a_World, {a_BlockX, a_BlockY, a_BlockZ})) if (FertilizePlant(*a_World, a_ClickedBlockPos))
{ {
if (a_Player->IsGameModeSurvival()) if (a_Player->IsGameModeSurvival())
{ {
@ -42,7 +46,7 @@ public:
} }
} }
} }
else if ((a_Item.m_ItemDamage == E_META_DYE_BROWN) && (a_BlockFace >= BLOCK_FACE_ZM) && (a_BlockFace <= BLOCK_FACE_XP)) else if ((a_HeldItem.m_ItemDamage == E_META_DYE_BROWN) && (a_ClickedBlockFace >= BLOCK_FACE_ZM) && (a_ClickedBlockFace <= BLOCK_FACE_XP))
{ {
// Players can't place blocks while in adventure mode. // Players can't place blocks while in adventure mode.
if (a_Player->IsGameModeAdventure()) if (a_Player->IsGameModeAdventure())
@ -53,25 +57,24 @@ public:
// Cocoa (brown dye) can be planted on jungle logs: // Cocoa (brown dye) can be planted on jungle logs:
BLOCKTYPE BlockType; BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta; NIBBLETYPE BlockMeta;
a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); a_World->GetBlockTypeMeta(a_ClickedBlockPos, BlockType, BlockMeta);
// Check if the block that the player clicked is a jungle log. // Check if the block that the player clicked is a jungle log.
if ((BlockType != E_BLOCK_LOG) || ((BlockMeta & 0x3) != E_META_LOG_JUNGLE)) if ((BlockType != E_BLOCK_LOG) || ((BlockMeta & 0x03) != E_META_LOG_JUNGLE))
{ {
return false; return false;
} }
// Get the location from the new cocoa pod. // Get the location from the new cocoa pod.
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, false); auto CocoaPos = AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace, false);
BlockMeta = cBlockCocoaPodHandler::BlockFaceToMeta(a_BlockFace); BlockMeta = cBlockCocoaPodHandler::BlockFaceToMeta(a_ClickedBlockFace);
if (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) != E_BLOCK_AIR) // Place the cocoa pod:
if (a_World->GetBlock(CocoaPos) != E_BLOCK_AIR)
{ {
return false; return false;
} }
if (a_Player->PlaceBlock(CocoaPos.x, CocoaPos.y, CocoaPos.z, E_BLOCK_COCOA_POD, BlockMeta))
// Place the cocoa pod:
if (a_Player->PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_COCOA_POD, BlockMeta))
{ {
if (a_Player->IsGameModeSurvival()) if (a_Player->IsGameModeSurvival())
{ {
@ -97,7 +100,7 @@ public:
Returns true if the plant was fertilized successfully, false if not / not a plant. Returns true if the plant was fertilized successfully, false if not / not a plant.
Note that successful fertilization doesn't mean successful growth - for blocks that have only a chance to grow, Note that successful fertilization doesn't mean successful growth - for blocks that have only a chance to grow,
fertilization success is reported even in the case when the chance fails (bonemeal still needs to be consumed). */ fertilization success is reported even in the case when the chance fails (bonemeal still needs to be consumed). */
bool fertilizePlant(cWorld & a_World, Vector3i a_BlockPos) bool FertilizePlant(cWorld & a_World, Vector3i a_BlockPos)
{ {
BLOCKTYPE blockType; BLOCKTYPE blockType;
NIBBLETYPE blockMeta; NIBBLETYPE blockMeta;

View File

@ -23,15 +23,19 @@ public:
virtual bool OnItemUse( virtual bool OnItemUse(
cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item, cWorld * a_World,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace cPlayer * a_Player,
cBlockPluginInterface & a_PluginInterface,
const cItem & a_HeldItem,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace
) override ) override
{ {
UNUSED(a_Item); UNUSED(a_HeldItem);
UNUSED(a_BlockX); UNUSED(a_ClickedBlockFace);
UNUSED(a_BlockZ);
UNUSED(a_BlockFace);
// The map center is fixed at the central point of the 8x8 block of chunks you are standing in when you right-click it. // The map center is fixed at the central point of the 8x8 block of chunks you are standing in when you right-click it.
@ -40,22 +44,14 @@ public:
int CenterX = FloorC(a_Player->GetPosX() / RegionWidth) * RegionWidth + (RegionWidth / 2); int CenterX = FloorC(a_Player->GetPosX() / RegionWidth) * RegionWidth + (RegionWidth / 2);
int CenterZ = FloorC(a_Player->GetPosZ() / RegionWidth) * RegionWidth + (RegionWidth / 2); int CenterZ = FloorC(a_Player->GetPosZ() / RegionWidth) * RegionWidth + (RegionWidth / 2);
cMap * NewMap = a_World->GetMapManager().CreateMap(CenterX, CenterZ, DEFAULT_SCALE); auto NewMap = a_World->GetMapManager().CreateMap(CenterX, CenterZ, DEFAULT_SCALE);
// Remove empty map from inventory
if (!a_Player->GetInventory().RemoveOneEquippedItem())
{
ASSERT(!"Inventory mismatch");
return true;
}
if (NewMap == nullptr) if (NewMap == nullptr)
{ {
return true; return true;
} }
a_Player->GetInventory().AddItem(cItem(E_ITEM_MAP, 1, static_cast<short>(NewMap->GetID() & 0x7fff))); // Replace map in the inventory:
a_Player->ReplaceOneEquippedItemTossRest(cItem(E_ITEM_MAP, 1, static_cast<short>(NewMap->GetID() & 0x7fff)));
return true; return true;
} }
} ; } ;

View File

@ -8,46 +8,53 @@
class cItemEyeOfEnderHandler : class cItemEyeOfEnderHandler:
public cItemThrowableHandler public cItemThrowableHandler
{ {
typedef cItemThrowableHandler super; using Super = cItemThrowableHandler;
public: public:
cItemEyeOfEnderHandler(void) :
super(E_ITEM_EYE_OF_ENDER, cProjectileEntity::pkSnowball, 30) cItemEyeOfEnderHandler():
Super(E_ITEM_EYE_OF_ENDER, cProjectileEntity::pkSnowball, 30)
{ {
} }
virtual bool OnItemUse( virtual bool OnItemUse(
cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item, 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 const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace
) override ) override
{ {
BLOCKTYPE FacingBlock; // Try to fill an End Portal Frame block:
NIBBLETYPE FacingMeta; if (a_ClickedBlockFace != BLOCK_FACE_NONE)
a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, FacingBlock, FacingMeta);
switch (FacingBlock)
{ {
case E_BLOCK_END_PORTAL_FRAME: BLOCKTYPE FacingBlock;
NIBBLETYPE FacingMeta;
a_World->GetBlockTypeMeta(a_ClickedBlockPos, FacingBlock, FacingMeta);
if (FacingBlock == E_BLOCK_END_PORTAL_FRAME)
{ {
// Fill the portal frame. E_META_END_PORTAL_EYE is the bit for holding the eye of ender. // Fill the portal frame. E_META_END_PORTAL_EYE is the bit for holding the eye of ender.
if ((FacingMeta & E_META_END_PORTAL_FRAME_EYE) != E_META_END_PORTAL_FRAME_EYE) if ((FacingMeta & E_META_END_PORTAL_FRAME_EYE) != E_META_END_PORTAL_FRAME_EYE)
{ {
a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_END_PORTAL_FRAME, FacingMeta | E_META_END_PORTAL_FRAME_EYE); a_World->SetBlock(a_ClickedBlockPos, E_BLOCK_END_PORTAL_FRAME, FacingMeta | E_META_END_PORTAL_FRAME_EYE);
if (!a_Player->IsGameModeCreative()) if (!a_Player->IsGameModeCreative())
{ {
a_Player->GetInventory().RemoveOneEquippedItem(); a_Player->GetInventory().RemoveOneEquippedItem();
} }
return true;
} }
break;
}
default:
{
// TODO: Create projectile for Eye Of Ender
// return cItemThrowableHandler::OnItemUse(a_World, a_Player, a_PluginInterface, a_Item, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
} }
return false;
} }
// TODO: Create projectile for Eye Of Ender
// return Super::OnItemUse(a_World, a_Player, a_PluginInterface, a_Item, a_ClickedBlockPos, a_ClickedBlockFace);
return false; return false;
} }

View File

@ -63,190 +63,36 @@ public:
virtual bool OnItemUse( virtual bool OnItemUse(
cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item, cWorld * a_World,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace cPlayer * a_Player,
cBlockPluginInterface & a_PluginInterface,
const cItem & a_HeldItem,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace
) override ) override
{ {
if (a_BlockFace != BLOCK_FACE_NONE) if (a_ClickedBlockFace != BLOCK_FACE_NONE)
{ {
return false; return false;
} }
auto & Random = GetRandomProvider();
if (a_Player->IsFishing()) if (a_Player->IsFishing())
{ {
cFloaterCallback FloaterInfo; ReelIn(*a_World, *a_Player);
a_World->DoWithEntityByID(a_Player->GetFloaterID(), FloaterInfo);
a_Player->SetIsFishing(false);
if (FloaterInfo.IsAttached())
{
a_World->DoWithEntityByID(FloaterInfo.GetAttachedMobID(), [=](cEntity & a_Entity)
{
Vector3d Speed = a_Player->GetPosition() - a_Entity.GetPosition();
a_Entity.AddSpeed(Speed);
return true;
}
);
a_Player->UseEquippedItem(5);
}
else if (FloaterInfo.CanPickup())
{
UInt32 LotSLevel = std::min(a_Player->GetEquippedItem().m_Enchantments.GetLevel(cEnchantments::enchLuckOfTheSea), 3u);
// Chances for getting an item from the category for each level of Luck of the Sea (0 - 3)
const int TreasureChances[] = {50, 71, 92, 113}; // 5% | 7.1% | 9.2% | 11.3%
const int JunkChances[] = {100, 81, 61, 42}; // 10% | 8.1% | 6.1% | 4.2%
cItems Drops;
int ItemCategory = Random.RandInt(999);
if (ItemCategory < TreasureChances[LotSLevel])
{
switch (Random.RandInt(5)) // Each piece of treasure has an equal chance of 1 / 6
{
case 0:
{
cItem Bow(E_ITEM_BOW, 1, Random.RandInt<short>(50));
Bow.EnchantByXPLevels(Random.RandInt(22, 30));
Drops.Add(Bow);
break;
}
case 1:
{
cItem Book(E_ITEM_BOOK);
Book.EnchantByXPLevels(30);
Drops.Add(Book);
break;
}
case 2:
{
cItem Rod(E_ITEM_FISHING_ROD, 1, Random.RandInt<short>(50));
Rod.EnchantByXPLevels(Random.RandInt(22, 30));
Drops.Add(Rod);
break;
}
case 3:
{
Drops.Add(cItem(E_ITEM_NAME_TAG));
break;
}
case 4:
{
Drops.Add(cItem(E_ITEM_SADDLE));
break;
}
case 5:
{
Drops.Add(cItem(E_BLOCK_LILY_PAD));
break;
}
}
a_Player->GetStatManager().AddValue(statTreasureFished, 1);
}
else if (ItemCategory < JunkChances[LotSLevel])
{
int Junk = Random.RandInt(82);
if (Junk < 10) // 10 / 83 chance of spawning a bowl
{
Drops.Add(cItem(E_ITEM_BOWL));
}
else if (Junk < 12) // 2 / 83 chance of spawning a fishing rod
{
// Fishing Rods caught from the Junk category will be 10%-100% damaged, and always unenchanted.
Drops.Add(cItem(E_ITEM_FISHING_ROD, 1, Random.RandInt<short>(7, 65)));
}
else if (Junk < 22) // 10 / 83 chance of spawning leather
{
Drops.Add(cItem(E_ITEM_LEATHER));
}
else if (Junk < 32) // 10 / 83 chance of spawning leather boots
{
// Leather boots caught from the Junk category will be 10%-100% damaged, and always unenchanted.
Drops.Add(cItem(E_ITEM_LEATHER_BOOTS, 1, Random.RandInt<short>(7, 66)));
}
else if (Junk < 42) // 10 / 83 chance of spawning rotten flesh
{
Drops.Add(cItem(E_ITEM_ROTTEN_FLESH));
}
else if (Junk < 47) // 5 / 83 chance of spawning a stick
{
Drops.Add(cItem(E_ITEM_STICK));
}
else if (Junk < 52) // 5 / 83 chance of spawning string
{
Drops.Add(cItem(E_ITEM_STRING));
}
else if (Junk < 62) // 10 / 83 chance of spawning a water bottle
{
Drops.Add(cItem(E_ITEM_POTION));
}
else if (Junk < 72) // 10 / 83 chance of spawning a bone
{
Drops.Add(cItem(E_ITEM_BONE));
}
else if (Junk < 73) // 1 / 83 chance of spawning an ink sac
{
Drops.Add(cItem(E_ITEM_DYE));
}
else // 10 / 83 chance of spawning a tripwire hook
{
Drops.Add(cItem(E_BLOCK_TRIPWIRE_HOOK));
}
a_Player->GetStatManager().AddValue(statJunkFished, 1);
}
else
{
int FishType = Random.RandInt(99);
if (FishType <= 1) // Clownfish has a 2% chance of spawning
{
Drops.Add(cItem(E_ITEM_RAW_FISH, 1, E_META_RAW_FISH_CLOWNFISH));
}
else if (FishType <= 12) // Pufferfish has a 13% chance of spawning
{
Drops.Add(cItem(E_ITEM_RAW_FISH, 1, E_META_RAW_FISH_PUFFERFISH));
}
else if (FishType <= 24) // Raw salmon has a 25% chance of spawning
{
Drops.Add(cItem(E_ITEM_RAW_FISH, 1, E_META_RAW_FISH_SALMON));
}
else // Raw fish has a 60% chance of spawning
{
Drops.Add(cItem(E_ITEM_RAW_FISH, 1, E_META_RAW_FISH_FISH));
}
a_Player->GetStatManager().AddValue(statFishCaught, 1);
}
if (cRoot::Get()->GetPluginManager()->CallHookPlayerFishing(*a_Player, Drops))
{
return true;
}
Vector3d FloaterPos = FloaterInfo.GetBitePos();
FloaterPos.y += 0.5f;
const float FISH_SPEED_MULT = 2.25f;
Vector3d FlyDirection = (a_Player->GetEyePosition() - FloaterPos).addedY(1.0f) * FISH_SPEED_MULT;
a_World->SpawnItemPickups(Drops, FloaterPos, FlyDirection);
a_World->SpawnExperienceOrb(a_Player->GetPosition(), Random.RandInt(1, 6));
a_Player->UseEquippedItem(1);
cRoot::Get()->GetPluginManager()->CallHookPlayerFished(*a_Player, Drops);
}
else
{
BLOCKTYPE Block = a_World->GetBlock(FloaterInfo.GetPos() - Vector3d(0, 0.1, 0));
if ((Block != E_BLOCK_AIR) && !IsBlockWater(Block))
{
a_Player->UseEquippedItem(2);
}
}
} }
else else
{ {
auto Floater = cpp14::make_unique<cFloater>(a_Player->GetEyePosition(), a_Player->GetLookVector() * 15, a_Player->GetUniqueID(), (Random.RandInt(100, 900) - static_cast<int>(a_Player->GetEquippedItem().m_Enchantments.GetLevel(cEnchantments::enchLure) * 100))); // Cast a hook:
auto & Random = GetRandomProvider();
auto CountDownTime = Random.RandInt(100, 900) - static_cast<int>(a_Player->GetEquippedItem().m_Enchantments.GetLevel(cEnchantments::enchLure) * 100);
auto Floater = cpp14::make_unique<cFloater>(
a_Player->GetEyePosition(), a_Player->GetLookVector() * 15,
a_Player->GetUniqueID(),
CountDownTime
);
auto FloaterPtr = Floater.get(); auto FloaterPtr = Floater.get();
if (!FloaterPtr->Initialize(std::move(Floater), *a_World)) if (!FloaterPtr->Initialize(std::move(Floater), *a_World))
{ {
@ -256,4 +102,206 @@ public:
} }
return true; return true;
} }
/** Reels back the fishing line, reeling any attached mob, or creating fished loot, or just breaking the fishing rod. */
void ReelIn(cWorld & a_World, cPlayer & a_Player)
{
cFloaterCallback FloaterInfo;
a_World.DoWithEntityByID(a_Player.GetFloaterID(), FloaterInfo);
a_Player.SetIsFishing(false);
// If attached to an entity, reel it in:
if (FloaterInfo.IsAttached())
{
ReelInEntity(a_World, a_Player, FloaterInfo.GetAttachedMobID());
return;
}
// If loot can be caught, get it:
if (FloaterInfo.CanPickup())
{
ReelInLoot(a_World, a_Player, FloaterInfo.GetBitePos());
return;
}
// Empty fishing rod, just damage it:
auto BlockType = a_World.GetBlock(FloaterInfo.GetPos() - Vector3d(0, 0.1, 0));
if ((BlockType != E_BLOCK_AIR) && !IsBlockWater(BlockType))
{
a_Player.UseEquippedItem(2);
}
}
/** Reels back the entity, specified by the ID, and damages the fishing rod accordingly. */
void ReelInEntity(cWorld & a_World, cPlayer & a_Player, UInt32 a_EntityID)
{
auto PlayerPos = a_Player.GetPosition();
a_World.DoWithEntityByID(a_EntityID, [=](cEntity & a_Entity)
{
auto Speed = PlayerPos - a_Entity.GetPosition();
a_Entity.AddSpeed(Speed);
return true;
}
);
a_Player.UseEquippedItem(5);
}
void ReelInLoot(cWorld & a_World, cPlayer & a_Player, const Vector3d a_FloaterBitePos)
{
auto LotSLevel = std::min(a_Player.GetEquippedItem().m_Enchantments.GetLevel(cEnchantments::enchLuckOfTheSea), 3u);
// Chances for getting an item from the category for each level of Luck of the Sea (0 - 3)
const int TreasureChances[] = {50, 71, 92, 113}; // 5% | 7.1% | 9.2% | 11.3%
const int JunkChances[] = {100, 81, 61, 42}; // 10% | 8.1% | 6.1% | 4.2%
cItems Drops;
auto & Random = GetRandomProvider();
int ItemCategory = Random.RandInt(999);
if (ItemCategory < TreasureChances[LotSLevel])
{
switch (Random.RandInt(5)) // Each piece of treasure has an equal chance of 1 / 6
{
case 0:
{
cItem Bow(E_ITEM_BOW, 1, Random.RandInt<short>(50));
Bow.EnchantByXPLevels(Random.RandInt(22, 30));
Drops.Add(Bow);
break;
}
case 1:
{
cItem Book(E_ITEM_BOOK);
Book.EnchantByXPLevels(30);
Drops.Add(Book);
break;
}
case 2:
{
cItem Rod(E_ITEM_FISHING_ROD, 1, Random.RandInt<short>(50));
Rod.EnchantByXPLevels(Random.RandInt(22, 30));
Drops.Add(Rod);
break;
}
case 3:
{
Drops.Add(cItem(E_ITEM_NAME_TAG));
break;
}
case 4:
{
Drops.Add(cItem(E_ITEM_SADDLE));
break;
}
case 5:
{
Drops.Add(cItem(E_BLOCK_LILY_PAD));
break;
}
}
a_Player.GetStatManager().AddValue(statTreasureFished, 1);
}
else if (ItemCategory < JunkChances[LotSLevel])
{
int Junk = Random.RandInt(82);
if (Junk < 10) // 10 / 83 chance of spawning a bowl
{
Drops.Add(cItem(E_ITEM_BOWL));
}
else if (Junk < 12) // 2 / 83 chance of spawning a fishing rod
{
// Fishing Rods caught from the Junk category will be 10% .. 100% damaged, and always unenchanted.
Drops.Add(cItem(E_ITEM_FISHING_ROD, 1, Random.RandInt<short>(7, 65)));
}
else if (Junk < 22) // 10 / 83 chance of spawning leather
{
Drops.Add(cItem(E_ITEM_LEATHER));
}
else if (Junk < 32) // 10 / 83 chance of spawning leather boots
{
// Leather boots caught from the Junk category will be 10% .. 100% damaged, and always unenchanted.
Drops.Add(cItem(E_ITEM_LEATHER_BOOTS, 1, Random.RandInt<short>(7, 66)));
}
else if (Junk < 42) // 10 / 83 chance of spawning rotten flesh
{
Drops.Add(cItem(E_ITEM_ROTTEN_FLESH));
}
else if (Junk < 47) // 5 / 83 chance of spawning a stick
{
Drops.Add(cItem(E_ITEM_STICK));
}
else if (Junk < 52) // 5 / 83 chance of spawning string
{
Drops.Add(cItem(E_ITEM_STRING));
}
else if (Junk < 62) // 10 / 83 chance of spawning a water bottle
{
Drops.Add(cItem(E_ITEM_POTION));
}
else if (Junk < 72) // 10 / 83 chance of spawning a bone
{
Drops.Add(cItem(E_ITEM_BONE));
}
else if (Junk < 73) // 1 / 83 chance of spawning an ink sac
{
Drops.Add(cItem(E_ITEM_DYE));
}
else // 10 / 83 chance of spawning a tripwire hook
{
Drops.Add(cItem(E_BLOCK_TRIPWIRE_HOOK));
}
a_Player.GetStatManager().AddValue(statJunkFished, 1);
}
else
{
int FishType = Random.RandInt(99);
if (FishType <= 1) // Clownfish has a 2% chance of spawning
{
Drops.Add(cItem(E_ITEM_RAW_FISH, 1, E_META_RAW_FISH_CLOWNFISH));
}
else if (FishType <= 12) // Pufferfish has a 13% chance of spawning
{
Drops.Add(cItem(E_ITEM_RAW_FISH, 1, E_META_RAW_FISH_PUFFERFISH));
}
else if (FishType <= 24) // Raw salmon has a 25% chance of spawning
{
Drops.Add(cItem(E_ITEM_RAW_FISH, 1, E_META_RAW_FISH_SALMON));
}
else // Raw fish has a 60% chance of spawning
{
Drops.Add(cItem(E_ITEM_RAW_FISH, 1, E_META_RAW_FISH_FISH));
}
a_Player.GetStatManager().AddValue(statFishCaught, 1);
}
// Check with plugins if this loot is acceptable:
if (cRoot::Get()->GetPluginManager()->CallHookPlayerFishing(a_Player, Drops))
{
return;
}
// Spawn the loot and the experience orb:
auto FloaterPos = a_FloaterBitePos.addedY(0.5);
const float FISH_SPEED_MULT = 2.25f;
Vector3d FlyDirection = (a_Player.GetEyePosition() - FloaterPos).addedY(1.0f) * FISH_SPEED_MULT;
a_World.SpawnItemPickups(Drops, FloaterPos, FlyDirection);
a_World.SpawnExperienceOrb(a_Player.GetPosition(), Random.RandInt(1, 6));
a_Player.UseEquippedItem(1);
// Notify plugins
cRoot::Get()->GetPluginManager()->CallHookPlayerFished(a_Player, Drops);
}
} ; } ;

View File

@ -7,26 +7,36 @@
class cItemFlowerPotHandler : class cItemFlowerPotHandler:
public cItemHandler public cItemHandler
{ {
using Super = cItemHandler;
public: public:
cItemFlowerPotHandler(int a_ItemType) :
cItemHandler(a_ItemType) cItemFlowerPotHandler(int a_ItemType):
Super(a_ItemType)
{ {
} }
virtual bool IsPlaceable(void) override virtual bool IsPlaceable(void) override
{ {
return true; return true;
} }
virtual bool GetPlacementBlockTypeMeta( virtual bool GetPlacementBlockTypeMeta(
cWorld * a_World, cPlayer * a_Player, cWorld * a_World, cPlayer * a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, const Vector3i a_PlacedBlockPos,
int a_CursorX, int a_CursorY, int a_CursorZ, eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override ) override
{ {

View File

@ -346,41 +346,44 @@ cItemHandler::cItemHandler(int a_ItemType)
bool cItemHandler::OnPlayerPlace( bool cItemHandler::OnPlayerPlace(
cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem, cWorld & a_World,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, cPlayer & a_Player,
int a_CursorX, int a_CursorY, int a_CursorZ const cItem & a_EquippedItem,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos
) )
{ {
if (a_BlockFace < 0) if (a_ClickedBlockFace == BLOCK_FACE_NONE)
{ {
// Clicked in air // Clicked in the air, no placement possible
return false; return false;
} }
if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height)) if (!cChunkDef::IsValidHeight(a_ClickedBlockPos.y))
{ {
// The clicked block is outside the world, ignore this call altogether (#128) // The clicked block is outside the world, ignore this call altogether (#128)
return false; return false;
} }
BLOCKTYPE ClickedBlock; BLOCKTYPE ClickedBlockType;
NIBBLETYPE ClickedBlockMeta; NIBBLETYPE ClickedBlockMeta;
a_World.GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlock, ClickedBlockMeta); a_World.GetBlockTypeMeta(a_ClickedBlockPos, ClickedBlockType, ClickedBlockMeta);
cChunkInterface ChunkInterface(a_World.GetChunkMap()); cChunkInterface ChunkInterface(a_World.GetChunkMap());
// Check if the block ignores build collision (water, grass etc.): // Check if the block ignores build collision (water, grass etc.):
auto blockHandler = BlockHandler(ClickedBlock); auto HandlerB = BlockHandler(ClickedBlockType);
Vector3i absPos(a_BlockX, a_BlockY, a_BlockZ); auto PlacedBlockPos = AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace);
if (blockHandler->DoesIgnoreBuildCollision(ChunkInterface, absPos, a_Player, ClickedBlockMeta)) if (HandlerB->DoesIgnoreBuildCollision(ChunkInterface, a_ClickedBlockPos, a_Player, ClickedBlockMeta))
{ {
a_World.DropBlockAsPickups(absPos, &a_Player, nullptr); // Replace the clicked block:
a_World.DropBlockAsPickups(a_ClickedBlockPos, &a_Player, nullptr);
PlacedBlockPos = a_ClickedBlockPos;
} }
else else
{ {
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); if (!cChunkDef::IsValidHeight(PlacedBlockPos.y))
if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height))
{ {
// The block is being placed outside the world, ignore this packet altogether (#128) // The block is being placed outside the world, ignore this packet altogether (#128)
return false; return false;
@ -388,11 +391,11 @@ bool cItemHandler::OnPlayerPlace(
NIBBLETYPE PlaceMeta; NIBBLETYPE PlaceMeta;
BLOCKTYPE PlaceBlock; BLOCKTYPE PlaceBlock;
a_World.GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, PlaceBlock, PlaceMeta); a_World.GetBlockTypeMeta(PlacedBlockPos, PlaceBlock, PlaceMeta);
// Clicked on side of block, make sure that placement won't be cancelled if there is a slab able to be double slabbed. // Clicked on side of block, make sure that placement won't be cancelled if there is a slab able to be double slabbed.
// No need to do combinability (dblslab) checks, client will do that here. // No need to do combinability (dblslab) checks, client will do that here.
if (!BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision(ChunkInterface, { a_BlockX, a_BlockY, a_BlockZ }, a_Player, PlaceMeta)) if (!BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision(ChunkInterface, PlacedBlockPos, a_Player, PlaceMeta))
{ {
// Tried to place a block into another? // Tried to place a block into another?
// Happens when you place a block aiming at side of block with a torch on it or stem beside it // Happens when you place a block aiming at side of block with a torch on it or stem beside it
@ -402,14 +405,15 @@ bool cItemHandler::OnPlayerPlace(
// Get all the blocks to place: // Get all the blocks to place:
sSetBlockVector blocks; 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)) if (!GetBlocksToPlace(a_World, a_Player, a_EquippedItem, PlacedBlockPos, a_ClickedBlockFace, a_CursorPos, blocks))
{ {
// Handler refused the placement, send that information back to the client: // Handler refused the placement, send that information back to the client:
for (const auto & blk: blocks) for (const auto & blk: blocks)
{ {
a_World.SendBlockTo(blk.GetX(), blk.GetY(), blk.GetZ(), a_Player); const auto & AbsPos = blk.GetAbsolutePos();
a_World.SendBlockTo(AbsPos, a_Player);
} }
a_World.SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, a_Player); a_World.SendBlockTo(PlacedBlockPos, a_Player);
a_Player.GetInventory().SendEquippedSlot(); a_Player.GetInventory().SendEquippedSlot();
return false; return false;
} }
@ -436,18 +440,19 @@ bool cItemHandler::OnPlayerPlace(
bool cItemHandler::GetBlocksToPlace( bool cItemHandler::GetBlocksToPlace(
cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem, cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, const Vector3i a_PlacedBlockPos,
int a_CursorX, int a_CursorY, int a_CursorZ, eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
sSetBlockVector & a_BlocksToSet sSetBlockVector & a_BlocksToSet
) )
{ {
BLOCKTYPE BlockType; BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta; NIBBLETYPE BlockMeta;
if (!GetPlacementBlockTypeMeta(&a_World, &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta)) if (!GetPlacementBlockTypeMeta(&a_World, &a_Player, a_PlacedBlockPos, a_ClickedBlockFace, a_CursorPos, BlockType, BlockMeta))
{ {
return false; return false;
} }
a_BlocksToSet.emplace_back(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta); a_BlocksToSet.emplace_back(a_PlacedBlockPos, BlockType, BlockMeta);
return true; return true;
} }
@ -457,17 +462,15 @@ bool cItemHandler::GetBlocksToPlace(
bool cItemHandler::OnItemUse( bool cItemHandler::OnItemUse(
cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item, 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 const Vector3i a_ClickedBlockPos, eBlockFace a_ClickedBlockFace
) )
{ {
UNUSED(a_World); UNUSED(a_World);
UNUSED(a_Player); UNUSED(a_Player);
UNUSED(a_PluginInterface); UNUSED(a_PluginInterface);
UNUSED(a_Item); UNUSED(a_Item);
UNUSED(a_BlockX); UNUSED(a_ClickedBlockPos);
UNUSED(a_BlockY); UNUSED(a_ClickedBlockFace);
UNUSED(a_BlockZ);
UNUSED(a_BlockFace);
return false; return false;
} }
@ -476,15 +479,19 @@ bool cItemHandler::OnItemUse(
bool cItemHandler::OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir) bool cItemHandler::OnDiggingBlock(
cWorld * a_World,
cPlayer * a_Player,
const cItem & a_HeldItem,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace
)
{ {
UNUSED(a_World); UNUSED(a_World);
UNUSED(a_Player); UNUSED(a_Player);
UNUSED(a_Item); UNUSED(a_HeldItem);
UNUSED(a_BlockX); UNUSED(a_ClickedBlockPos);
UNUSED(a_BlockY); UNUSED(a_ClickedBlockFace);
UNUSED(a_BlockZ);
UNUSED(a_Dir);
return false; return false;
} }
@ -815,8 +822,8 @@ bool cItemHandler::CanHarvestBlock(BLOCKTYPE a_BlockType)
bool cItemHandler::GetPlacementBlockTypeMeta( bool cItemHandler::GetPlacementBlockTypeMeta(
cWorld * a_World, cPlayer * a_Player, cWorld * a_World, cPlayer * a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, const Vector3i a_PlacedBlockPos, eBlockFace a_ClickedBlockFace,
int a_CursorX, int a_CursorY, int a_CursorZ, const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) )
{ {
@ -832,8 +839,8 @@ bool cItemHandler::GetPlacementBlockTypeMeta(
cChunkInterface ChunkInterface(a_World->GetChunkMap()); cChunkInterface ChunkInterface(a_World->GetChunkMap());
return BlockH->GetPlacementBlockTypeMeta( return BlockH->GetPlacementBlockTypeMeta(
ChunkInterface, *a_Player, ChunkInterface, *a_Player,
a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_PlacedBlockPos, a_ClickedBlockFace,
a_CursorX, a_CursorY, a_CursorZ, a_CursorPos,
a_BlockType, a_BlockMeta a_BlockType, a_BlockMeta
); );
} }

View File

@ -38,28 +38,39 @@ public:
/** Called when the player tries to place the item (right mouse button, IsPlaceable() == true). /** Called when the player tries to place the item (right mouse button, IsPlaceable() == true).
The block coords are for the block that has been clicked. a_ClickedBlockPos is the (neighbor) block that has been clicked to place this item.
a_ClickedBlockFace is the face of the neighbor that has been clicked to place this item.
a_CursorPos is the position of the player's cursor within a_ClickedBlockFace.
The default handler uses GetBlocksToPlace() and places the returned blocks. 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. 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. 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. */ Returns true if the placement succeeded, false if the placement was aborted for any reason. */
virtual bool OnPlayerPlace( virtual bool OnPlayerPlace(
cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem, cWorld & a_World,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, cPlayer & a_Player,
int a_CursorX, int a_CursorY, int a_CursorZ const cItem & a_EquippedItem,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos
); );
/** Called from OnPlayerPlace() to determine the blocks that the current placement operation should set. /** 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. a_PlacedBlockPos points to the location where the new block should be set.
a_ClickedBlockFace is the block face of the neighbor that was clicked to place this block.
a_CursorPos is the position of the mouse cursor within the clicked (neighbor's) block face.
The blocks in a_BlocksToPlace will be sent through cPlayer::PlaceBlocks() after returning from this function.
The default handler uses GetPlacementBlockTypeMeta() and provides that as the single block at the specified coords. 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. 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. */ If aborted, the server then sends all original blocks in the coords provided in a_BlocksToSet to the client. */
virtual bool GetBlocksToPlace( virtual bool GetBlocksToPlace(
cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem, cWorld & a_World,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, cPlayer & a_Player,
int a_CursorX, int a_CursorY, int a_CursorZ, const cItem & a_EquippedItem,
sSetBlockVector & a_BlocksToSet const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
sSetBlockVector & a_BlocksToPlace
); );
@ -68,26 +79,29 @@ public:
Returns true to allow placement, false to refuse. */ Returns true to allow placement, false to refuse. */
virtual bool GetPlacementBlockTypeMeta( virtual bool GetPlacementBlockTypeMeta(
cWorld * a_World, cPlayer * a_Player, cWorld * a_World, cPlayer * a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, const Vector3i a_PlacedBlockPos,
int a_CursorX, int a_CursorY, int a_CursorZ, eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
); );
/** Called when the player tries to use the item (right mouse button). /** Called when the player tries to use the item (right mouse button).
Return false to abort the usage. DEFAULT: False */ Descendants can return false to abort the usage (default behavior). */
virtual bool OnItemUse( virtual bool OnItemUse(
cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item, cWorld * a_World,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace cPlayer * a_Player,
cBlockPluginInterface & a_PluginInterface,
const cItem & a_HeldItem,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace
); );
/** Called when the client sends the SHOOT status in the lclk packet */ /** Called when the client sends the SHOOT status in the lclk packet (releasing the bow). */
virtual void OnItemShoot(cPlayer *, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace) virtual void OnItemShoot(cPlayer *, const Vector3i a_BlockPos, eBlockFace a_BlockFace)
{ {
UNUSED(a_BlockX); UNUSED(a_BlockPos);
UNUSED(a_BlockY);
UNUSED(a_BlockZ);
UNUSED(a_BlockFace); UNUSED(a_BlockFace);
} }
@ -100,9 +114,13 @@ public:
} }
/** Called while the player digs a block using this item */ /** Called while the player digs a block using this item */
virtual bool OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, const cItem & a_HeldItem, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace); virtual bool OnDiggingBlock(
cWorld * a_World, cPlayer * a_Player, const cItem & a_HeldItem,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace
);
/** Called when a player attacks a other entity. */ /** Called when a player attacks an entity with this item in hand. */
virtual void OnEntityAttack(cPlayer * a_Attacker, cEntity * a_AttackedEntity); virtual void OnEntityAttack(cPlayer * a_Attacker, cEntity * a_AttackedEntity);
/** Called after the player has eaten this item. */ /** Called after the player has eaten this item. */

View File

@ -9,64 +9,68 @@
class cItemHoeHandler : class cItemHoeHandler:
public cItemHandler public cItemHandler
{ {
using Super = cItemHandler;
public: public:
cItemHoeHandler(int a_ItemType)
: cItemHandler(a_ItemType) cItemHoeHandler(int a_ItemType):
Super(a_ItemType)
{ {
} }
virtual bool OnItemUse( virtual bool OnItemUse(
cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item, cWorld * a_World,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace cPlayer * a_Player,
cBlockPluginInterface & a_PluginInterface,
const cItem & a_HeldItem,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace
) override ) override
{ {
if ((a_BlockFace == BLOCK_FACE_NONE) || (a_BlockY >= cChunkDef::Height)) if ((a_ClickedBlockFace == BLOCK_FACE_NONE) || (a_ClickedBlockPos.y >= cChunkDef::Height))
{ {
return false; return false;
} }
BLOCKTYPE UpperBlock = a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ);
BLOCKTYPE Block; // Need air above the hoe-d block to transform it:
NIBBLETYPE BlockMeta; BLOCKTYPE UpperBlockType = a_World->GetBlock(a_ClickedBlockPos.addedY(1));
a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, Block, BlockMeta); if (UpperBlockType != E_BLOCK_AIR)
if (((Block == E_BLOCK_DIRT) || (Block == E_BLOCK_GRASS)) && (UpperBlock == E_BLOCK_AIR))
{ {
BLOCKTYPE NewBlock = E_BLOCK_FARMLAND; return false;
if (Block == E_BLOCK_DIRT)
{
switch (BlockMeta)
{
case E_META_DIRT_COARSE:
{
// Transform to normal dirt
NewBlock = E_BLOCK_DIRT;
break;
}
case E_META_DIRT_PODZOL:
{
// You can't transform this block with a hoe in vanilla
return false;
}
default: break;
}
}
a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, NewBlock, 0);
a_World->BroadcastSoundEffect("item.hoe.till", {a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5}, 1.0f, 0.8f);
a_Player->UseEquippedItem();
return true;
} }
return false; // Can only transform dirt or grass blocks:
BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta;
a_World->GetBlockTypeMeta(a_ClickedBlockPos, BlockType, BlockMeta);
if ((BlockType != E_BLOCK_DIRT) && (BlockType != E_BLOCK_GRASS))
{
return false;
}
if ((BlockType == E_BLOCK_DIRT) && (BlockMeta == E_META_DIRT_PODZOL))
{
return false;
}
// Transform:
auto NewBlockType = ((BlockType == E_BLOCK_DIRT) && (BlockMeta == E_META_DIRT_COARSE)) ? E_BLOCK_DIRT : E_BLOCK_FARMLAND;
a_World->SetBlock(a_ClickedBlockPos, NewBlockType, 0);
a_World->BroadcastSoundEffect("item.hoe.till", a_ClickedBlockPos + Vector3d(0.5, 0.5, 0.5), 1.0f, 0.8f);
a_Player->UseEquippedItem();
return true;
} }
virtual short GetDurabilityLossByAction(eDurabilityLostAction a_Action) override virtual short GetDurabilityLossByAction(eDurabilityLostAction a_Action) override
{ {
switch (a_Action) switch (a_Action)

View File

@ -9,51 +9,59 @@
class cItemItemFrameHandler : class cItemItemFrameHandler:
public cItemHandler public cItemHandler
{ {
public: using Super = cItemHandler;
cItemItemFrameHandler(int a_ItemType)
: cItemHandler(a_ItemType)
{
public:
cItemItemFrameHandler(int a_ItemType):
Super(a_ItemType)
{
} }
virtual bool OnItemUse( virtual bool OnItemUse(
cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item, cWorld * a_World,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace cPlayer * a_Player,
cBlockPluginInterface & a_PluginInterface,
const cItem & a_HeldItem,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace
) override ) override
{ {
if ((a_BlockFace == BLOCK_FACE_NONE) || (a_BlockFace == BLOCK_FACE_YP) || (a_BlockFace == BLOCK_FACE_YM)) // Can only place on a side face:
if ((a_ClickedBlockFace == BLOCK_FACE_NONE) || (a_ClickedBlockFace == BLOCK_FACE_YP) || (a_ClickedBlockFace == BLOCK_FACE_YM))
{ {
// Client sends this if clicked on top or bottom face
return false; return false;
} }
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); // Make sure block that will be occupied is free // Make sure block that will be occupied by the item frame is free now:
BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); auto PlacePos = AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace);
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, true); // We want the clicked block, so go back again BLOCKTYPE Block = a_World->GetBlock(PlacePos);
if (Block != E_BLOCK_AIR)
if (Block == E_BLOCK_AIR)
{ {
auto ItemFrame = cpp14::make_unique<cItemFrame>(a_BlockFace, Vector3i{a_BlockX, a_BlockY, a_BlockZ}); return false;
auto ItemFramePtr = ItemFrame.get();
if (!ItemFramePtr->Initialize(std::move(ItemFrame), *a_World))
{
return false;
}
if (!a_Player->IsGameModeCreative())
{
a_Player->GetInventory().RemoveOneEquippedItem();
}
return true;
} }
return false;
// Place the item frame:
auto ItemFrame = cpp14::make_unique<cItemFrame>(a_ClickedBlockFace, a_ClickedBlockPos);
auto ItemFramePtr = ItemFrame.get();
if (!ItemFramePtr->Initialize(std::move(ItemFrame), *a_World))
{
return false;
}
if (!a_Player->IsGameModeCreative())
{
a_Player->GetInventory().RemoveOneEquippedItem();
}
return true;
} }
}; };

View File

@ -19,17 +19,23 @@ public:
{ {
} }
virtual bool GetPlacementBlockTypeMeta( virtual bool GetPlacementBlockTypeMeta(
cWorld * a_World, cPlayer * a_Player, cWorld * a_World, cPlayer * a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, const Vector3i a_PlacedBlockPos,
int a_CursorX, int a_CursorY, int a_CursorZ, eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override ) override
{ {
bool res = Super::GetPlacementBlockTypeMeta( bool res = Super::GetPlacementBlockTypeMeta(
a_World, a_Player, a_World, a_Player,
a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_PlacedBlockPos,
a_CursorX, a_CursorY, a_CursorZ, a_ClickedBlockFace,
a_CursorPos,
a_BlockType, a_BlockMeta a_BlockType, a_BlockMeta
); );
a_BlockMeta = a_BlockMeta | 0x4; // 0x4 bit set means this is a player-placed leaves block, not to be decayed a_BlockMeta = a_BlockMeta | 0x4; // 0x4 bit set means this is a player-placed leaves block, not to be decayed

View File

@ -9,23 +9,32 @@
class cItemLighterHandler : class cItemLighterHandler:
public cItemHandler public cItemHandler
{ {
using Super = cItemHandler;
public: public:
cItemLighterHandler(int a_ItemType) :
cItemHandler(a_ItemType) cItemLighterHandler(int a_ItemType):
Super(a_ItemType)
{ {
} }
virtual bool OnItemUse( virtual bool OnItemUse(
cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item, cWorld * a_World,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace cPlayer * a_Player,
cBlockPluginInterface & a_PluginInterface,
const cItem & a_HeldItem,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace
) override ) override
{ {
if (a_BlockFace < 0) if (a_ClickedBlockFace < 0)
{ {
return false; return false;
} }
@ -51,27 +60,27 @@ public:
} }
} }
switch (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ)) switch (a_World->GetBlock(a_ClickedBlockPos))
{ {
case E_BLOCK_TNT: case E_BLOCK_TNT:
{ {
// Activate the TNT: // Activate the TNT:
a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); a_World->SetBlock(a_ClickedBlockPos, E_BLOCK_AIR, 0);
a_World->SpawnPrimedTNT({a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5}); // 80 ticks to boom a_World->SpawnPrimedTNT(Vector3d(a_ClickedBlockPos) + Vector3d(0.5, 0.5, 0.5)); // 80 ticks to boom
break; break;
} }
default: default:
{ {
// Light a fire next to / on top of the block if air: // Light a fire next to / on top of the block if air:
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); auto FirePos = AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace);
if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height)) if (!cChunkDef::IsValidHeight(FirePos.y))
{ {
break; break;
} }
if (a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_AIR) if (a_World->GetBlock(FirePos) == E_BLOCK_AIR)
{ {
a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_FIRE, 0); a_World->SetBlock(FirePos, E_BLOCK_FIRE, 0);
a_World->BroadcastSoundEffect("item.flintandsteel.use", Vector3d(a_BlockX, a_BlockY, a_BlockZ), 1.0F, 1.04F); a_World->BroadcastSoundEffect("item.flintandsteel.use", FirePos, 1.0f, 1.04f);
break; break;
} }
} }

View File

@ -23,6 +23,9 @@ public:
} }
virtual bool IsPlaceable(void) override virtual bool IsPlaceable(void) override
{ {
return false; // Set as not placeable so OnItemUse is called return false; // Set as not placeable so OnItemUse is called
@ -30,16 +33,22 @@ public:
virtual bool OnItemUse( virtual bool OnItemUse(
cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item, cWorld * a_World,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace cPlayer * a_Player,
cBlockPluginInterface & a_PluginInterface,
const cItem & a_HeldItem,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace
) override ) override
{ {
if (a_BlockFace > BLOCK_FACE_NONE) if (a_ClickedBlockFace > BLOCK_FACE_NONE)
{ {
// Clicked on the side of a submerged block; vanilla allows placement, so should we // Clicked on a face of a submerged block; vanilla allows placement, so should we
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); auto PlacePos = AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace);
a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_LILY_PAD, 0); a_World->SetBlock(PlacePos, E_BLOCK_LILY_PAD, 0);
if (!a_Player->IsGameModeCreative()) if (!a_Player->IsGameModeCreative())
{ {
a_Player->GetInventory().RemoveOneEquippedItem(); a_Player->GetInventory().RemoveOneEquippedItem();
@ -47,12 +56,12 @@ public:
return true; return true;
} }
class cCallbacks : class cCallbacks:
public cBlockTracer::cCallbacks public cBlockTracer::cCallbacks
{ {
public: public:
cCallbacks(void) : cCallbacks():
m_HasHitFluid(false) m_HasHitFluid(false)
{ {
} }
@ -84,18 +93,14 @@ public:
Vector3i m_Pos; Vector3i m_Pos;
bool m_HasHitFluid; bool m_HasHitFluid;
}; } Callbacks;
auto Start = a_Player->GetEyePosition() + a_Player->GetLookVector();
cCallbacks Callbacks; auto End = a_Player->GetEyePosition() + a_Player->GetLookVector() * 5;
cLineBlockTracer Tracer(*a_Player->GetWorld(), Callbacks); cLineBlockTracer::Trace(*a_Player->GetWorld(), Callbacks, Start.x, Start.y, Start.z, End.x, End.y, End.z);
Vector3d Start(a_Player->GetEyePosition() + a_Player->GetLookVector());
Vector3d End(a_Player->GetEyePosition() + a_Player->GetLookVector() * 5);
Tracer.Trace(Start.x, Start.y, Start.z, End.x, End.y, End.z);
if (Callbacks.m_HasHitFluid) if (Callbacks.m_HasHitFluid)
{ {
a_World->SetBlock(Callbacks.m_Pos.x, Callbacks.m_Pos.y, Callbacks.m_Pos.z, E_BLOCK_LILY_PAD, 0); a_World->SetBlock(Callbacks.m_Pos, E_BLOCK_LILY_PAD, 0);
if (!a_Player->IsGameModeCreative()) if (!a_Player->IsGameModeCreative())
{ {
a_Player->GetInventory().RemoveOneEquippedItem(); a_Player->GetInventory().RemoveOneEquippedItem();

View File

@ -1,8 +1,6 @@
#pragma once #pragma once
#include "../Entities/Minecart.h"
@ -21,18 +19,25 @@ public:
virtual bool OnItemUse( virtual bool OnItemUse(
cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item, cWorld * a_World,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace cPlayer * a_Player,
cBlockPluginInterface & a_PluginInterface,
const cItem & a_HeldItem,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace
) override ) override
{ {
if (a_BlockFace < 0) // Must be used on a block
if (a_ClickedBlockFace < 0)
{ {
return false; return false;
} }
// Check that there's rail in there: // Check that there's rail in there:
BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); BLOCKTYPE Block = a_World->GetBlock(a_ClickedBlockPos);
switch (Block) switch (Block)
{ {
case E_BLOCK_MINECART_TRACKS: case E_BLOCK_MINECART_TRACKS:
@ -50,15 +55,14 @@ public:
} }
} }
double x = static_cast<double>(a_BlockX) + 0.5; // Spawn the minecart:
double y = static_cast<double>(a_BlockY) + 0.5; auto SpawnPos = Vector3d(a_ClickedBlockPos) + Vector3d(0.5, 0.5, 0.5);
double z = static_cast<double>(a_BlockZ) + 0.5; if (a_World->SpawnMinecart(SpawnPos, m_ItemType) == cEntity::INVALID_ID)
if (a_World->SpawnMinecart(x, y, z, m_ItemType) == cEntity::INVALID_ID)
{ {
return false; return false;
} }
// Remove the item from inventory:
if (!a_Player->IsGameModeCreative()) if (!a_Player->IsGameModeCreative())
{ {
a_Player->GetInventory().RemoveOneEquippedItem(); a_Player->GetInventory().RemoveOneEquippedItem();

View File

@ -22,52 +22,60 @@ public:
} }
virtual bool OnPlayerPlace( virtual bool OnPlayerPlace(
cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem, cWorld & a_World,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, cPlayer & a_Player,
int a_CursorX, int a_CursorY, int a_CursorZ const cItem & a_EquippedItem,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos
) override ) override
{ {
// Cannot place a head at "no face" and from the bottom: // Cannot place a head at "no face" and from the bottom:
if ((a_BlockFace == BLOCK_FACE_NONE) || (a_BlockFace == BLOCK_FACE_BOTTOM)) if ((a_ClickedBlockFace == BLOCK_FACE_NONE) || (a_ClickedBlockFace == BLOCK_FACE_BOTTOM))
{ {
return true; return true;
} }
auto placedX = a_BlockX, placedY = a_BlockY, placedZ = a_BlockZ; const auto PlacePos = AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace);
AddFaceDirection(placedY, placedY, placedZ, a_BlockFace);
// If the placed head is a wither, try to spawn the wither first: // If the placed head is a wither, try to spawn the wither first:
if (a_EquippedItem.m_ItemDamage == E_META_HEAD_WITHER) if (a_EquippedItem.m_ItemDamage == E_META_HEAD_WITHER)
{ {
if (TrySpawnWitherAround(a_World, a_Player, {placedX, placedY, placedZ})) if (TrySpawnWitherAround(a_World, a_Player, PlacePos))
{ {
return true; return true;
} }
// Wither not created, proceed with regular head placement // Wither not created, proceed with regular head placement
} }
cItem itemCopy(a_EquippedItem); // Make a copy in case this is the player's last head item and OnPlayerPlace removes it 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)) if (!Super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_ClickedBlockPos, a_ClickedBlockFace, a_CursorPos))
{ {
return false; return false;
} }
RegularHeadPlaced(a_World, a_Player, itemCopy, placedX, placedY, placedZ, a_BlockFace); RegularHeadPlaced(a_World, a_Player, ItemCopy, PlacePos, a_ClickedBlockFace);
return true; return true;
} }
/** Called after placing a regular head block with no mob spawning. /** Called after placing a regular head block with no mob spawning.
Adjusts the mob head entity based on the equipped item's data. */ Adjusts the mob head entity based on the equipped item's data. */
void RegularHeadPlaced( void RegularHeadPlaced(
cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem, cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace const Vector3i a_PlacePos, eBlockFace a_ClickedBlockFace
) )
{ {
auto HeadType = static_cast<eMobHeadType>(a_EquippedItem.m_ItemDamage); auto HeadType = static_cast<eMobHeadType>(a_EquippedItem.m_ItemDamage);
auto BlockMeta = static_cast<NIBBLETYPE>(a_BlockFace); auto BlockMeta = static_cast<NIBBLETYPE>(a_ClickedBlockFace);
// Use a callback to set the properties of the mob head block entity: // Use a callback to set the properties of the mob head block entity:
a_World.DoWithBlockEntityAt(a_BlockX, a_BlockY, a_BlockZ, [&](cBlockEntity & a_BlockEntity) a_World.DoWithBlockEntityAt(a_PlacePos.x, a_PlacePos.y, a_PlacePos.z, [&](cBlockEntity & a_BlockEntity)
{ {
if (a_BlockEntity.GetBlockType() != E_BLOCK_HEAD) if (a_BlockEntity.GetBlockType() != E_BLOCK_HEAD)
{ {
@ -90,11 +98,14 @@ public:
} }
/** Spawns a wither if the wither skull placed at the specified coords completes wither's spawning formula. /** Spawns a wither if the wither skull placed at the specified coords completes wither's spawning formula.
Returns true if the wither was created. */ Returns true if the wither was created. */
bool TrySpawnWitherAround( bool TrySpawnWitherAround(
cWorld & a_World, cPlayer & a_Player, cWorld & a_World, cPlayer & a_Player,
Vector3i a_BlockPos const Vector3i a_BlockPos
) )
{ {
// No wither can be created at Y < 2 - not enough space for the formula: // No wither can be created at Y < 2 - not enough space for the formula:
@ -183,6 +194,9 @@ public:
} }
/** Tries to spawn a wither from the specified image at the specified offset from the placed head block. /** Tries to spawn a wither from the specified image at the specified offset from the placed head block.
PlacedHead coords are used to override the block query - at those coords the block is not queried from the world, PlacedHead coords are used to override the block query - at those coords the block is not queried from the world,
but assumed to be a head instead. but assumed to be a head instead.
@ -265,6 +279,9 @@ public:
} }
/** Awards the achievement to all players close to the specified point. */ /** Awards the achievement to all players close to the specified point. */
void AwardSpawnWitherAchievement(cWorld & a_World, Vector3i a_BlockPos) void AwardSpawnWitherAchievement(cWorld & a_World, Vector3i a_BlockPos)
{ {
@ -283,6 +300,9 @@ public:
} }
/** Converts the block face of the placement (which face of the block was clicked to place the head) /** Converts the block face of the placement (which face of the block was clicked to place the head)
into the block's metadata value. */ into the block's metadata value. */
static NIBBLETYPE BlockFaceToBlockMeta(int a_BlockFace) static NIBBLETYPE BlockFaceToBlockMeta(int a_BlockFace)
@ -303,21 +323,28 @@ public:
} }
virtual bool IsPlaceable(void) override virtual bool IsPlaceable(void) override
{ {
return true; return true;
} }
virtual bool GetPlacementBlockTypeMeta( virtual bool GetPlacementBlockTypeMeta(
cWorld * a_World, cPlayer * a_Player, cWorld * a_World, cPlayer * a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, const Vector3i a_PlacedBlockPos,
int a_CursorX, int a_CursorY, int a_CursorZ, eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override ) override
{ {
a_BlockType = E_BLOCK_HEAD; a_BlockType = E_BLOCK_HEAD;
a_BlockMeta = BlockFaceToBlockMeta(a_BlockFace); a_BlockMeta = BlockFaceToBlockMeta(a_ClickedBlockFace);
return true; return true;
} }
} ; } ;

View File

@ -8,47 +8,53 @@
class cItemNetherWartHandler : class cItemNetherWartHandler:
public cItemHandler public cItemHandler
{ {
public: using Super = cItemHandler;
cItemNetherWartHandler(int a_ItemType) :
cItemHandler(a_ItemType)
{
public:
cItemNetherWartHandler(int a_ItemType):
Super(a_ItemType)
{
} }
virtual bool IsPlaceable(void) override virtual bool IsPlaceable(void) override
{ {
return true; return true;
} }
virtual bool GetPlacementBlockTypeMeta( virtual bool GetPlacementBlockTypeMeta(
cWorld * a_World, cPlayer * a_Player, cWorld * a_World, cPlayer * a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, const Vector3i a_PlacedBlockPos,
int a_CursorX, int a_CursorY, int a_CursorZ, eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override ) override
{ {
if (a_BlockFace != BLOCK_FACE_TOP) // Only allow planting nether wart onto the top side of the block:
if (a_ClickedBlockFace != BLOCK_FACE_TOP)
{ {
// Only allow planting nether wart from the top side of the block
return false; return false;
} }
// Only allow placement on farmland // Only allow placement on soulsand
int X = a_BlockX; if ((a_PlacedBlockPos.y < 1) || (a_World->GetBlock(a_PlacedBlockPos.addedY(-1)) != E_BLOCK_SOULSAND))
int Y = a_BlockY;
int Z = a_BlockZ;
AddFaceDirection(X, Y, Z, a_BlockFace, true);
if (a_World->GetBlock(X, Y, Z) != E_BLOCK_SOULSAND)
{ {
return false; return false;
} }
a_BlockMeta = 0; a_BlockMeta = 0;
a_BlockType = E_BLOCK_NETHER_WART; a_BlockType = E_BLOCK_NETHER_WART;
return true; return true;
} }
} ; } ;

View File

@ -10,82 +10,93 @@
class cItemPaintingHandler : class cItemPaintingHandler:
public cItemHandler public cItemHandler
{ {
using Super = cItemHandler;
public: public:
cItemPaintingHandler(int a_ItemType)
: cItemHandler(a_ItemType) cItemPaintingHandler(int a_ItemType):
Super(a_ItemType)
{ {
} }
virtual bool OnItemUse( virtual bool OnItemUse(
cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item, cWorld * a_World,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace cPlayer * a_Player,
cBlockPluginInterface & a_PluginInterface,
const cItem & a_HeldItem,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace
) override ) override
{ {
if ((a_BlockFace == BLOCK_FACE_NONE) || (a_BlockFace == BLOCK_FACE_YM) || (a_BlockFace == BLOCK_FACE_YP)) // Paintings can't be flatly placed:
if (
(a_ClickedBlockFace == BLOCK_FACE_NONE) ||
(a_ClickedBlockFace == BLOCK_FACE_YM) ||
(a_ClickedBlockFace == BLOCK_FACE_YP)
)
{ {
// Paintings can't be flatly placed
return false; return false;
} }
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); // Make sure block that will be occupied is free // Make sure block that will be occupied is free:
BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ); auto PlacePos = AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace);
BLOCKTYPE PlaceBlockType = a_World->GetBlock(PlacePos);
if (Block == E_BLOCK_AIR) if (PlaceBlockType != E_BLOCK_AIR)
{ {
static const struct // Define all the possible painting titles return false;
{
AString Title;
} gPaintingTitlesList[] =
{
{ "Kebab" },
{ "Aztec" },
{ "Alban" },
{ "Aztec2" },
{ "Bomb" },
{ "Plant" },
{ "Wasteland" },
{ "Wanderer" },
{ "Graham" },
{ "Pool" },
{ "Courbet" },
{ "Sunset" },
{ "Sea" },
{ "Creebet" },
{ "Match" },
{ "Bust" },
{ "Stage" },
{ "Void" },
{ "SkullAndRoses" },
{ "Wither" },
{ "Fighters" },
{ "Skeleton" },
{ "DonkeyKong" },
{ "Pointer" },
{ "Pigscene" },
{ "BurningSkull" }
};
auto Painting = cpp14::make_unique<cPainting>(gPaintingTitlesList[a_World->GetTickRandomNumber(ARRAYCOUNT(gPaintingTitlesList) - 1)].Title, a_BlockFace, Vector3i{a_BlockX, a_BlockY, a_BlockZ});
auto PaintingPtr = Painting.get();
if (!PaintingPtr->Initialize(std::move(Painting), *a_World))
{
return false;
}
if (!a_Player->IsGameModeCreative())
{
a_Player->GetInventory().RemoveOneEquippedItem();
}
return true;
} }
return false;
// Define all the possible painting titles
static const AString gPaintingTitlesList[] =
{
{ "Kebab" },
{ "Aztec" },
{ "Alban" },
{ "Aztec2" },
{ "Bomb" },
{ "Plant" },
{ "Wasteland" },
{ "Wanderer" },
{ "Graham" },
{ "Pool" },
{ "Courbet" },
{ "Sunset" },
{ "Sea" },
{ "Creebet" },
{ "Match" },
{ "Bust" },
{ "Stage" },
{ "Void" },
{ "SkullAndRoses" },
{ "Wither" },
{ "Fighters" },
{ "Skeleton" },
{ "DonkeyKong" },
{ "Pointer" },
{ "Pigscene" },
{ "BurningSkull" }
};
auto PaintingTitle = gPaintingTitlesList[a_World->GetTickRandomNumber(ARRAYCOUNT(gPaintingTitlesList) - 1)];
auto Painting = cpp14::make_unique<cPainting>(PaintingTitle, a_ClickedBlockFace, PlacePos);
auto PaintingPtr = Painting.get();
if (!PaintingPtr->Initialize(std::move(Painting), *a_World))
{
return false;
}
if (!a_Player->IsGameModeCreative())
{
a_Player->GetInventory().RemoveOneEquippedItem();
}
return true;
} }
}; };

View File

@ -17,6 +17,9 @@ public:
} }
// cItemHandler overrides: // cItemHandler overrides:
virtual bool IsDrinkable(short a_ItemDamage) override virtual bool IsDrinkable(short a_ItemDamage) override
{ {
@ -26,12 +29,19 @@ public:
} }
virtual bool OnItemUse( virtual bool OnItemUse(
cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item, cWorld * a_World,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace cPlayer * a_Player,
cBlockPluginInterface & a_PluginInterface,
const cItem & a_HeldItem,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace
) override ) override
{ {
short PotionDamage = a_Item.m_ItemDamage; short PotionDamage = a_HeldItem.m_ItemDamage;
// Do not throw non-splash potions: // Do not throw non-splash potions:
if (cEntityEffect::IsPotionDrinkable(PotionDamage)) if (cEntityEffect::IsPotionDrinkable(PotionDamage))
@ -59,6 +69,9 @@ public:
} }
virtual bool EatItem(cPlayer * a_Player, cItem * a_Item) override virtual bool EatItem(cPlayer * a_Player, cItem * a_Item) override
{ {
short PotionDamage = a_Item->m_ItemDamage; short PotionDamage = a_Item->m_ItemDamage;

View File

@ -20,44 +20,50 @@ public:
} }
virtual bool OnPlayerPlace( virtual bool OnPlayerPlace(
cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem, cWorld & a_World,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, cPlayer & a_Player,
int a_CursorX, int a_CursorY, int a_CursorZ const cItem & a_EquippedItem,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos
) override ) override
{ {
// First try spawning a snow golem or an iron golem: // First try spawning a snow golem or an iron golem:
int PlacedBlockX = a_BlockX; auto PlacePos = AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace);
int PlacedBlockY = a_BlockY; if (TrySpawnGolem(a_World, a_Player, PlacePos))
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: // The client thinks that they placed the pumpkin, let them know it's been replaced:
a_Player.SendBlocksAround(PlacedBlockX, PlacedBlockY, PlacedBlockZ); a_Player.SendBlocksAround(PlacePos.x, PlacePos.y, PlacePos.z);
return true; return true;
} }
// No golem at these coords, place the block normally: // 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); return Super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_ClickedBlockPos, a_ClickedBlockFace, a_CursorPos);
} }
/** Spawns a snow / iron golem if the shape matches the recipe, supposing that the block placed at the specified coords is a pumpkin. /** 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. */ 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) bool TrySpawnGolem(cWorld & a_World, cPlayer & a_Player, const Vector3i a_PumpkinPos)
{ {
// A golem can't form with a pumpkin below level 2 or above level 255 // A golem can't form with a pumpkin below level 2 or above level 255
if ((a_BlockY < 2) || (a_BlockY >= cChunkDef::Height)) if ((a_PumpkinPos.y < 2) || (a_PumpkinPos.y >= cChunkDef::Height))
{ {
return false; return false;
} }
// Decide which golem to try spawning based on the block below the placed pumpkin: // 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)) switch (a_World.GetBlock(a_PumpkinPos.addedY(-1)))
{ {
case E_BLOCK_SNOW_BLOCK: return TrySpawnSnowGolem(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ); case E_BLOCK_SNOW_BLOCK: return TrySpawnSnowGolem(a_World, a_Player, a_PumpkinPos);
case E_BLOCK_IRON_BLOCK: return TrySpawnIronGolem(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ); case E_BLOCK_IRON_BLOCK: return TrySpawnIronGolem(a_World, a_Player, a_PumpkinPos);
default: default:
{ {
// No golem here // No golem here
@ -67,45 +73,59 @@ public:
} }
/** Spawns a snow golem if the shape matches the recipe, supposing that the block placed at the specified coords is a pumpkin. /** 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. 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. */ 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) bool TrySpawnSnowGolem(cWorld & a_World, cPlayer & a_Player, const Vector3i a_PumpkinPos)
{ {
ASSERT(a_PumpkinPos.y > 1);
ASSERT(a_World.GetBlock(a_PumpkinPos.addedY(-1)) == E_BLOCK_SNOW);
// Need one more snow block 2 blocks below the pumpkin: // 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) if (a_World.GetBlock(a_PumpkinPos.addedY(-2)) != E_BLOCK_SNOW_BLOCK)
{ {
return false; return false;
} }
// Try to place air blocks where the original recipe blocks were: // Try to place air blocks where the original recipe blocks were:
sSetBlockVector AirBlocks; sSetBlockVector AirBlocks;
AirBlocks.emplace_back(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); // Head AirBlocks.emplace_back(a_PumpkinPos, E_BLOCK_AIR, 0); // Head
AirBlocks.emplace_back(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); // Torso AirBlocks.emplace_back(a_PumpkinPos.addedY(-1), E_BLOCK_AIR, 0); // Torso
AirBlocks.emplace_back(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); // Legs AirBlocks.emplace_back(a_PumpkinPos.addedY(-2), E_BLOCK_AIR, 0); // Legs
if (!a_Player.PlaceBlocks(AirBlocks)) if (!a_Player.PlaceBlocks(AirBlocks))
{ {
return false; return false;
} }
// Spawn the golem: // Spawn the golem:
a_World.SpawnMob(static_cast<double>(a_BlockX) + 0.5, a_BlockY - 2, static_cast<double>(a_BlockZ) + 0.5, mtSnowGolem, false); auto GolemPos = Vector3d(a_PumpkinPos) + Vector3d(0.5, -2, 0.5);
a_World.SpawnMob(GolemPos.x, GolemPos.y, GolemPos.z, mtSnowGolem, false);
return true; return true;
} }
/** Spawns an iron golem if the shape matches the recipe, supposing that the block placed at the specified coords is a pumpkin. /** 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. 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. */ 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) bool TrySpawnIronGolem(cWorld & a_World, cPlayer & a_Player, const Vector3i a_PumpkinPos)
{ {
ASSERT(a_PumpkinPos.y > 1);
ASSERT(a_World.GetBlock(a_PumpkinPos.addedY(-1)) == E_BLOCK_IRON_BLOCK);
// Need one more iron block 2 blocks below the pumpkin: // 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) if (a_World.GetBlock(a_PumpkinPos.addedY(-2)) != E_BLOCK_IRON_BLOCK)
{ {
return false; return false;
} }
// Check the two arm directions (X, Z) using a loop over two sets of offset vectors: // Check the two arm directions (X, Z) using a loop over two sets of offset vectors:
auto BodyPos = a_PumpkinPos.addedY(-1);
static const Vector3i ArmOffsets[] = static const Vector3i ArmOffsets[] =
{ {
{1, 0, 0}, {1, 0, 0},
@ -115,8 +135,8 @@ public:
{ {
// If the arm blocks don't match, bail out of this loop repetition: // If the arm blocks don't match, bail out of this loop repetition:
if ( if (
(a_World.GetBlock(a_BlockX + ArmOffsets[i].x, a_BlockY - 1, a_BlockZ + ArmOffsets[i].z) != E_BLOCK_IRON_BLOCK) || (a_World.GetBlock(BodyPos + ArmOffsets[i]) != E_BLOCK_IRON_BLOCK) ||
(a_World.GetBlock(a_BlockX - ArmOffsets[i].x, a_BlockY - 1, a_BlockZ - ArmOffsets[i].z) != E_BLOCK_IRON_BLOCK) (a_World.GetBlock(BodyPos - ArmOffsets[i]) != E_BLOCK_IRON_BLOCK)
) )
{ {
continue; continue;
@ -124,18 +144,19 @@ public:
// Try to place air blocks where the original recipe blocks were: // Try to place air blocks where the original recipe blocks were:
sSetBlockVector AirBlocks; sSetBlockVector AirBlocks;
AirBlocks.emplace_back(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); // Head AirBlocks.emplace_back(a_PumpkinPos, E_BLOCK_AIR, 0); // Head
AirBlocks.emplace_back(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); // Torso AirBlocks.emplace_back(BodyPos, E_BLOCK_AIR, 0); // Torso
AirBlocks.emplace_back(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); // Legs AirBlocks.emplace_back(BodyPos.addedY(-1), 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(BodyPos + ArmOffsets[i], 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 AirBlocks.emplace_back(BodyPos - ArmOffsets[i], E_BLOCK_AIR, 0); // Arm
if (!a_Player.PlaceBlocks(AirBlocks)) if (!a_Player.PlaceBlocks(AirBlocks))
{ {
return false; return false;
} }
// Spawn the golem: // Spawn the golem:
a_World.SpawnMob(static_cast<double>(a_BlockX) + 0.5, a_BlockY - 2, static_cast<double>(a_BlockZ) + 0.5, mtIronGolem, false); auto GolemPos = Vector3d(a_PumpkinPos) + Vector3d(0.5, -2, 0.5);
a_World.SpawnMob(GolemPos.x, GolemPos.y, GolemPos.z, mtIronGolem, false);
return true; return true;
} // for i - ArmOffsets[] } // for i - ArmOffsets[]

View File

@ -7,36 +7,48 @@
class cItemRedstoneDustHandler : public cItemHandler class cItemRedstoneDustHandler:
public cItemHandler
{ {
using Super = cItemHandler;
public: public:
cItemRedstoneDustHandler(int a_ItemType)
: cItemHandler(a_ItemType) cItemRedstoneDustHandler(int a_ItemType):
Super(a_ItemType)
{ {
} }
virtual bool IsPlaceable(void) override virtual bool IsPlaceable(void) override
{ {
return true; return true;
} }
virtual bool GetPlacementBlockTypeMeta( virtual bool GetPlacementBlockTypeMeta(
cWorld * a_World, cPlayer * a_Player, cWorld * a_World, cPlayer * a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, const Vector3i a_PlacedBlockPos,
int a_CursorX, int a_CursorY, int a_CursorZ, eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override ) override
{ {
// Check if coords are out of range: // Check the block below, if it supports dust on top of it:
if ((a_BlockY <= 0) || (a_BlockY >= cChunkDef::Height)) auto UnderPos = a_PlacedBlockPos.addedY(-1);
if (UnderPos.y < 0)
{ {
return false; return false;
} }
// Check the block below, if it supports dust on top of it:
BLOCKTYPE BlockType; BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta; NIBBLETYPE BlockMeta;
if (!a_World->GetBlockTypeMeta(a_BlockX, a_BlockY - 1, a_BlockZ, BlockType, BlockMeta)) if (!a_World->GetBlockTypeMeta(UnderPos, BlockType, BlockMeta))
{ {
return false; return false;
} }
@ -51,6 +63,9 @@ public:
} }
/** Returns true if the specified block type / meta is suitable to have redstone dust on top of it. */ /** Returns true if the specified block type / meta is suitable to have redstone dust on top of it. */
static bool IsBlockTypeUnderSuitable(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) static bool IsBlockTypeUnderSuitable(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
{ {

View File

@ -8,24 +8,36 @@
class cItemRedstoneRepeaterHandler : class cItemRedstoneRepeaterHandler:
public cItemHandler public cItemHandler
{ {
using Super = cItemHandler;
public: public:
cItemRedstoneRepeaterHandler(int a_ItemType)
: cItemHandler(a_ItemType) cItemRedstoneRepeaterHandler(int a_ItemType):
Super(a_ItemType)
{ {
} }
virtual bool IsPlaceable() override virtual bool IsPlaceable() override
{ {
return true; return true;
} }
virtual bool GetPlacementBlockTypeMeta( virtual bool GetPlacementBlockTypeMeta(
cWorld * a_World, cPlayer * a_Player, cWorld * a_World, cPlayer * a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, const Vector3i a_PlacedBlockPos,
int a_CursorX, int a_CursorY, int a_CursorZ, eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override ) override
{ {

View File

@ -17,24 +17,29 @@ public:
cItemSaplingHandler(int a_ItemType): cItemSaplingHandler(int a_ItemType):
Super(a_ItemType) Super(a_ItemType)
{ {
} }
virtual bool GetPlacementBlockTypeMeta( virtual bool GetPlacementBlockTypeMeta(
cWorld * a_World, cPlayer * a_Player, cWorld * a_World, cPlayer * a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, const Vector3i a_PlacedBlockPos,
int a_CursorX, int a_CursorY, int a_CursorZ, eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override ) override
{ {
bool res = Super::GetPlacementBlockTypeMeta( bool res = Super::GetPlacementBlockTypeMeta(
a_World, a_Player, a_World, a_Player,
a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_PlacedBlockPos, a_ClickedBlockFace,
a_CursorX, a_CursorY, a_CursorZ, a_CursorPos,
a_BlockType, a_BlockMeta a_BlockType, a_BlockMeta
); );
// Only the lowest 3 bits are important
a_BlockMeta = a_BlockMeta & 0x7; // Allow only the lowest 3 bits (top bit is for growth):
a_BlockMeta = a_BlockMeta & 0x07;
return res; return res;
} }
} ; } ;

View File

@ -8,44 +8,53 @@
class cItemSeedsHandler : class cItemSeedsHandler:
public cItemHandler public cItemHandler
{ {
using Super = cItemHandler;
public: public:
cItemSeedsHandler(int a_ItemType) :
cItemHandler(a_ItemType) cItemSeedsHandler(int a_ItemType):
Super(a_ItemType)
{ {
} }
virtual bool IsPlaceable(void) override virtual bool IsPlaceable(void) override
{ {
return true; return true;
} }
virtual bool GetPlacementBlockTypeMeta( virtual bool GetPlacementBlockTypeMeta(
cWorld * a_World, cPlayer * a_Player, cWorld * a_World, cPlayer * a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, const Vector3i a_PlacedBlockPos,
int a_CursorX, int a_CursorY, int a_CursorZ, eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override ) override
{ {
if (a_BlockFace != BLOCK_FACE_TOP) // Only allow planting seeds from the top side of the block:
if ((a_ClickedBlockFace != BLOCK_FACE_TOP) || (a_PlacedBlockPos.y <= 0))
{ {
// Only allow planting seeds from the top side of the block
return false; return false;
} }
// Only allow placement on farmland // Only allow placement on farmland
int X = a_BlockX; if (a_World->GetBlock(a_PlacedBlockPos.addedY(-1)) != E_BLOCK_FARMLAND)
int Y = a_BlockY;
int Z = a_BlockZ;
AddFaceDirection(X, Y, Z, a_BlockFace, true);
if (a_World->GetBlock(X, Y, Z) != E_BLOCK_FARMLAND)
{ {
return false; return false;
} }
// Get the produce block based on the seed item:
a_BlockMeta = 0; a_BlockMeta = 0;
switch (m_ItemType) switch (m_ItemType)
{ {

View File

@ -22,25 +22,33 @@ public:
} }
virtual bool IsTool(void) override virtual bool IsTool(void) override
{ {
return true; return true;
} }
virtual bool OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir) override
virtual bool OnDiggingBlock(
cWorld * a_World,
cPlayer * a_Player,
const cItem & a_HeldItem,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace
) override
{ {
BLOCKTYPE Block; BLOCKTYPE Block;
NIBBLETYPE BlockMeta; NIBBLETYPE BlockMeta;
a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, Block, BlockMeta); a_World->GetBlockTypeMeta(a_ClickedBlockPos, Block, BlockMeta);
if ((Block == E_BLOCK_LEAVES) || (Block == E_BLOCK_NEW_LEAVES)) if ((Block == E_BLOCK_LEAVES) || (Block == E_BLOCK_NEW_LEAVES))
{ {
cItems Drops; a_World->DropBlockAsPickups(a_ClickedBlockPos, a_Player, &a_HeldItem);
Drops.Add(Block, 1, BlockMeta & 3);
a_World->SpawnItemPickups(Drops, a_BlockX, a_BlockY, a_BlockZ);
a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0);
return true; return true;
} }
@ -48,6 +56,9 @@ public:
} }
virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType) override virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType) override
{ {
switch (a_BlockType) switch (a_BlockType)
@ -63,6 +74,9 @@ public:
} }
virtual short GetDurabilityLossByAction(eDurabilityLostAction a_Action) override virtual short GetDurabilityLossByAction(eDurabilityLostAction a_Action) override
{ {
switch (a_Action) switch (a_Action)
@ -93,5 +107,4 @@ public:
return Super::GetBlockBreakingStrength(a_Block); return Super::GetBlockBreakingStrength(a_Block);
} }
} }
} ; } ;

View File

@ -24,58 +24,68 @@ public:
} }
virtual bool OnPlayerPlace( virtual bool OnPlayerPlace(
cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem, cWorld & a_World,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, cPlayer & a_Player,
int a_CursorX, int a_CursorY, int a_CursorZ const cItem & a_EquippedItem,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos
) override ) override
{ {
// Check if placing on something ignoring build collision to edit the correct sign later on: // Check if placing on something ignoring build collision to edit the correct sign later on:
BLOCKTYPE ClickedBlock; BLOCKTYPE ClickedBlockType;
NIBBLETYPE ClickedBlockMeta; NIBBLETYPE ClickedBlockMeta;
a_World.GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlock, ClickedBlockMeta); a_World.GetBlockTypeMeta(a_ClickedBlockPos, ClickedBlockType, ClickedBlockMeta);
cChunkInterface ChunkInterface(a_World.GetChunkMap()); cChunkInterface ChunkInterface(a_World.GetChunkMap());
bool isReplacingClickedBlock = BlockHandler(ClickedBlock)->DoesIgnoreBuildCollision(ChunkInterface, { a_BlockX, a_BlockY, a_BlockZ }, a_Player, ClickedBlockMeta); bool IsReplacingClickedBlock = BlockHandler(ClickedBlockType)->DoesIgnoreBuildCollision(ChunkInterface, a_ClickedBlockPos, a_Player, ClickedBlockMeta);
// If the regular placement doesn't work, do no further processing: // If the regular placement doesn't work, do no further processing:
if (!Super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ)) if (!Super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_ClickedBlockPos, a_ClickedBlockFace, a_CursorPos))
{ {
return false; return false;
} }
// Use isReplacingClickedBlock to make sure we will edit the right sign: // Use IsReplacingClickedBlock to make sure we will edit the right sign:
if (!isReplacingClickedBlock) auto SignPos = IsReplacingClickedBlock ? a_ClickedBlockPos : AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace);
{
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
}
// After successfully placing the sign, open the sign editor for the player: // After successfully placing the sign, open the sign editor for the player:
a_Player.GetClientHandle()->SendEditSign(a_BlockX, a_BlockY, a_BlockZ); a_Player.GetClientHandle()->SendEditSign(SignPos.x, SignPos.y, SignPos.z);
return true; return true;
} }
virtual bool IsPlaceable(void) override virtual bool IsPlaceable(void) override
{ {
return true; return true;
} }
virtual bool GetPlacementBlockTypeMeta( virtual bool GetPlacementBlockTypeMeta(
cWorld * a_World, cPlayer * a_Player, cWorld * a_World, cPlayer * a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, const Vector3i a_PlacedBlockPos,
int a_CursorX, int a_CursorY, int a_CursorZ, eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override ) override
{ {
if (a_BlockFace == BLOCK_FACE_TOP) if (a_ClickedBlockFace == BLOCK_FACE_TOP)
{ {
a_BlockMeta = cBlockSignPostHandler::RotationToMetaData(a_Player->GetYaw()); a_BlockMeta = cBlockSignPostHandler::RotationToMetaData(a_Player->GetYaw());
a_BlockType = E_BLOCK_SIGN_POST; a_BlockType = E_BLOCK_SIGN_POST;
} }
else else
{ {
a_BlockMeta = cBlockWallSignHandler::DirectionToMetaData(a_BlockFace); a_BlockMeta = cBlockWallSignHandler::BlockFaceToMetaData(a_ClickedBlockFace);
a_BlockType = E_BLOCK_WALLSIGN; a_BlockType = E_BLOCK_WALLSIGN;
} }
return true; return true;

Some files were not shown because too many files have changed in this diff Show More