1
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);
return true;
}
virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
int a_CursorX, int a_CursorY, int a_CursorZ,
cChunkInterface & a_ChunkInterface,
cPlayer & a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) 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;
}
@ -58,11 +71,19 @@ public:
return true;
}
virtual bool IsUseable() override
{
return true;
}
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{
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);
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)
{
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");
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
{
NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta(Coords);
if ((Meta & 0x4) == 0x4)
// Is foot end
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 (
(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 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_BlockPos, Meta & 0x0b); // Clear the "occupied" bit of the bed's block
}
return true;
}

View File

@ -32,10 +32,30 @@ public:
// Overrides:
virtual void OnBroken(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, Vector3i a_BlockPos, BLOCKTYPE a_OldBlockType, NIBBLETYPE a_OldBlockMeta) override;
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 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;
virtual void OnBroken(
cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface,
const 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_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
{
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;
}
BLOCKTYPE BlockType;
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));
}
@ -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)
{

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(Pos);
NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta(a_BlockPos);
Vector3d SoundPos(Pos);
// If button is already on do nothing
// If button is already on, do nothing:
if (Meta & 0x08)
{
return false;
}
// Set p the ON bit to on
// Set the ON bit to on
Meta |= 0x08;
a_ChunkInterface.SetBlockMeta({a_BlockX, a_BlockY, a_BlockZ}, Meta, false);
a_WorldInterface.WakeUpSimulators(Pos);
a_WorldInterface.GetBroadcastManager().BroadcastSoundEffect("block.stone_button.click_on", SoundPos, 0.5f, 0.6f);
a_ChunkInterface.SetBlockMeta(a_BlockPos, Meta, false);
a_WorldInterface.WakeUpSimulators(a_BlockPos);
a_WorldInterface.GetBroadcastManager().BroadcastSoundEffect("block.stone_button.click_on", a_BlockPos, 0.5f, 0.6f);
// Queue a button reset (unpress)
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
a_World.SetBlockMeta(Pos.x, Pos.y, Pos.z, a_World.GetBlockMeta(Pos) & 0x07, false);
a_World.WakeUpSimulators(Pos);
a_World.BroadcastSoundEffect("block.stone_button.click_off", SoundPos, 0.5f, 0.5f);
a_World.SetBlockMeta(a_BlockPos, a_World.GetBlockMeta(a_BlockPos) & 0x07, false);
a_World.WakeUpSimulators(a_BlockPos);
a_World.BroadcastSoundEffect("block.stone_button.click_off", a_BlockPos, 0.5f, 0.5f);
}
}
);
@ -61,23 +65,38 @@ public:
return true;
}
virtual bool IsUseable(void) override
{
return true;
}
virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
int a_CursorX, int a_CursorY, int a_CursorZ,
cChunkInterface & a_ChunkInterface,
cPlayer & a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override
{
a_BlockType = m_BlockType;
a_BlockMeta = BlockFaceToMetaData(a_BlockFace);
a_BlockMeta = BlockFaceToMetaData(a_ClickedBlockFace);
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)
{
switch (a_BlockFace)
@ -97,6 +116,11 @@ public:
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)
{
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;
a_Chunk.UnboundedRelGetBlockMeta(a_RelX, a_RelY, a_RelZ, Meta);
auto Meta = a_Chunk.GetMeta(a_RelPos);
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);
BLOCKTYPE BlockIsOn; a_Chunk.UnboundedRelGetBlockType(a_RelX, a_RelY, a_RelZ, BlockIsOn);
return (a_RelY > 0) && (cBlockInfo::FullyOccupiesVoxel(BlockIsOn));
return cBlockInfo::FullyOccupiesVoxel(SupportBlockType);
}
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{
UNUSED(a_Meta);
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)
{
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;
}
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))
{
// 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
static const struct
static const Vector3i Coords[] =
{
int x, z;
} Coords[] =
{
{-1, 0},
{ 1, 0},
{ 0, -1},
{ 0, 1},
} ;
{-1, 0, 0},
{ 1, 0, 0},
{ 0, 0, -1},
{ 0, 0, 1},
};
for (size_t i = 0; i < ARRAYCOUNT(Coords); i++)
{
BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta;
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) ||
(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))
{
@ -31,11 +38,11 @@ public:
if (Meta >= 5)
{
a_ChunkInterface.DigBlock(a_WorldInterface, {a_BlockX, a_BlockY, a_BlockZ});
a_ChunkInterface.DigBlock(a_WorldInterface, a_BlockPos);
}
else
{
a_ChunkInterface.SetBlockMeta({a_BlockX, a_BlockY, a_BlockZ}, Meta + 1);
a_ChunkInterface.SetBlockMeta(a_BlockPos, Meta + 1);
}
return true;
}

View File

@ -14,14 +14,14 @@
class cBlockCarpetHandler :
class cBlockCarpetHandler:
public cBlockHandler
{
using Super = cBlockHandler;
public:
cBlockCarpetHandler(BLOCKTYPE a_BlockType) :
cBlockCarpetHandler(BLOCKTYPE a_BlockType):
Super(a_BlockType)
{
}
@ -31,9 +31,11 @@ public:
virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
int a_CursorX, int a_CursorY, int a_CursorZ,
cChunkInterface & a_ChunkInterface,
cPlayer & a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) 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();
switch (EquippedItem.m_ItemType)
{
@ -42,7 +49,7 @@ public:
{
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:
if (!a_Player.IsGameModeCreative())
{
@ -55,7 +62,7 @@ public:
{
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:
if (!a_Player.IsGameModeCreative())
{
@ -68,7 +75,7 @@ public:
{
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:
if (!a_Player.IsGameModeCreative())
{
@ -82,8 +89,8 @@ public:
// Refill cauldron with water bottles.
if ((Meta < 3) && (EquippedItem.m_ItemDamage == 0))
{
a_ChunkInterface.SetBlockMeta(Vector3i(a_BlockX, a_BlockY, a_BlockZ), ++Meta);
// Give empty bottle when the gamemode is not creative:
a_ChunkInterface.SetBlockMeta(Vector3i(a_BlockPos), ++Meta);
// Give back an empty bottle when the gamemode is not creative:
if (!a_Player.IsGameModeCreative())
{
a_Player.ReplaceOneEquippedItemTossRest(cItem(E_ITEM_GLASS_BOTTLE));

View File

@ -27,14 +27,16 @@ public:
virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
int a_CursorX, int a_CursorY, int a_CursorZ,
cChunkInterface & a_ChunkInterface,
cPlayer & a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override
{
// Is there a doublechest already next to this block?
if (!CanBeAt(a_ChunkInterface, a_BlockX, a_BlockY, a_BlockZ))
// Cannot place right next to double-chest:
if (!CanBeAt(a_ChunkInterface, a_PlacedBlockPos))
{
// Yup, cannot form a triple-chest, refuse:
return false;
@ -42,13 +44,13 @@ public:
// Try to read double-chest information:
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;
}
// 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;
}
@ -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;
int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width;
return CanBeAt(a_ChunkInterface, BlockX, a_RelY, BlockZ);
auto BlockPos = a_Chunk.RelativeToAbsolute(a_RelPos);
return CanBeAt(a_ChunkInterface, BlockPos);
}
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;
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.
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));
AddFaceDirection(a_RelX, a_RelY, a_RelZ, BlockFace, true);
// Check that we're attached to a jungle log block:
eBlockFace BlockFace = MetaToBlockFace(a_Chunk.GetMeta(a_RelPos));
auto LogPos = AddFaceDirection(a_RelPos, BlockFace, true);
BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta;
a_Chunk.UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ, BlockType, BlockMeta);
return ((BlockType == E_BLOCK_LOG) && ((BlockMeta & 0x3) == E_META_LOG_JUNGLE));
a_Chunk.UnboundedRelGetBlock(LogPos, BlockType, BlockMeta);
return ((BlockType == E_BLOCK_LOG) && ((BlockMeta & 0x03) == 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});
Meta ^= 0x04; // Toggle 3rd (addition / subtraction) bit with XOR
a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta);
// Toggle the 3rd bit (addition / subtraction):
NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta(a_BlockPos);
Meta ^= 0x04;
a_ChunkInterface.SetBlockMeta(a_BlockPos, Meta);
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);
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
{
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
{
return cItem(E_ITEM_COMPARATOR, 1, 0);
}
inline static bool IsInSubtractionMode(NIBBLETYPE a_Meta)
{
return ((a_Meta & 0x4) == 0x4);
}
inline static bool IsOn(NIBBLETYPE a_Meta)
{
return ((a_Meta & 0x8) == 0x8);
}
inline static Vector3i GetSideCoordinate(Vector3i a_Position, NIBBLETYPE a_Meta, bool a_bInverse)
{
if (!a_bInverse)
@ -98,6 +167,10 @@ public:
return a_Position;
}
inline static Vector3i GetRearCoordinate(Vector3i a_Position, NIBBLETYPE a_Meta)
{
switch (a_Meta)
@ -117,6 +190,10 @@ public:
return a_Position;
}
inline static Vector3i GetFrontCoordinate(Vector3i a_Position, NIBBLETYPE a_Meta)
{
switch (a_Meta)
@ -136,6 +213,10 @@ public:
return a_Position;
}
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{
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
{
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;
}
BLOCKTYPE BelowBlock = a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ);
BLOCKTYPE BelowBlock = a_Chunk.GetBlock(a_RelPos.addedY(-1));
switch (BelowBlock)
{
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_BlockFace);
UNUSED(a_CursorX);
UNUSED(a_CursorY);
UNUSED(a_CursorZ);
UNUSED(a_CursorPos);
switch (a_ChunkInterface.GetBlock({a_BlockX, a_BlockY, a_BlockZ}))
switch (a_ChunkInterface.GetBlock(a_BlockPos))
{
default:
{
@ -61,14 +66,14 @@ bool cBlockDoorHandler::OnUse(cChunkInterface & a_ChunkInterface, cWorldInterfac
case E_BLOCK_SPRUCE_DOOR:
case E_BLOCK_OAK_DOOR:
{
ChangeDoor(a_ChunkInterface, a_BlockX, a_BlockY, a_BlockZ);
a_Player.GetWorld()->BroadcastSoundParticleEffect(EffectID::SFX_RANDOM_WOODEN_DOOR_OPEN, {a_BlockX, a_BlockY, a_BlockZ}, 0, a_Player.GetClientHandle());
ChangeDoor(a_ChunkInterface, a_BlockPos);
a_Player.GetWorld()->BroadcastSoundParticleEffect(EffectID::SFX_RANDOM_WOODEN_DOOR_OPEN, a_BlockPos, 0, a_Player.GetClientHandle());
break;
}
// Prevent iron door from opening on player click
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;
}
}
@ -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_BlockFace);
a_WorldInterface.SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, a_Player);
NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta({a_BlockX, a_BlockY, a_BlockZ});
a_WorldInterface.SendBlockTo(a_BlockPos, a_Player);
NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta(a_BlockPos);
if (Meta & 0x8)
if (Meta & 0x08)
{
// Current block is top of the door
a_WorldInterface.SendBlockTo(a_BlockX, a_BlockY - 1, a_BlockZ, a_Player);
// Current block is top of the door, send the bottom part:
a_WorldInterface.SendBlockTo(a_BlockPos.addedY(-1), a_Player);
}
else
{
// Current block is bottom of the door
a_WorldInterface.SendBlockTo(a_BlockX, a_BlockY + 1, a_BlockZ, a_Player);
// Current block is bottom of the door, send the top part:
a_WorldInterface.SendBlockTo(a_BlockPos.addedY(1), a_Player);
}
}

View File

@ -20,37 +20,63 @@ public:
cBlockDoorHandler(BLOCKTYPE a_BlockType);
virtual void OnBroken(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, Vector3i a_BlockPos, BLOCKTYPE a_OldBlockType, NIBBLETYPE a_OldBlockMeta) override;
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 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 OnBroken(
cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface,
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 MetaRotateCW(NIBBLETYPE a_Meta) override;
virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override;
virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override;
virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
int a_CursorX, int a_CursorY, int a_CursorZ,
cChunkInterface & a_ChunkInterface,
cPlayer & a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override
{
// 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 (
!CanReplaceBlock(a_ChunkInterface.GetBlock({a_BlockX, a_BlockY, a_BlockZ})) ||
!CanReplaceBlock(a_ChunkInterface.GetBlock({a_BlockX, a_BlockY + 1, a_BlockZ}))
!CanReplaceBlock(a_ChunkInterface.GetBlock(PlacedPos)) ||
!CanReplaceBlock(a_ChunkInterface.GetBlock(PlacedPos.addedY(1)))
)
{
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;
@ -93,11 +119,19 @@ public:
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. */
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.
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);
}
@ -201,17 +235,17 @@ public:
/** 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 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. */
static NIBBLETYPE GetCompleteDoorMeta(cChunkInterface & a_ChunkInterface, int a_BlockX, int a_BlockY, int a_BlockZ)
Fails silently for (invalid) doors on the world's top and bottom. */
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)
{
// 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));
}
// 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
{
// 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));
}
// 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. */
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))
{
return;
}
NIBBLETYPE Meta = GetCompleteDoorMeta(a_ChunkInterface, a_BlockX, a_BlockY, a_BlockZ);
NIBBLETYPE Meta = GetCompleteDoorMeta(a_ChunkInterface, a_BlockPos);
bool IsOpened = ((Meta & 0x04) != 0);
if (IsOpened == a_Open)
{
@ -255,14 +289,14 @@ public:
if ((Meta & 0x08) == 0)
{
// 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
{
// 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 */
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
{
using Super = cBlockHandler;
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);
return true;
}
virtual bool IsUseable(void) override
{
return true;
}
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{
UNUSED(a_Meta);

View File

@ -7,22 +7,26 @@
class cBlockEndPortalFrameHandler :
class cBlockEndPortalFrameHandler:
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_XP,
E_META_END_PORTAL_FRAME_ZP,
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(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
int a_CursorX, int a_CursorY, int a_CursorZ,
cChunkInterface & a_ChunkInterface,
cPlayer & a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) 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
{
using Super = cBlockHandler;
public:
// 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 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 MAX_COORD = 0.6;
cBlockFenceHandler(BLOCKTYPE a_BlockType)
: cBlockHandler(a_BlockType)
cBlockFenceHandler(BLOCKTYPE 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
{
bool XMSolid = cBlockInfo::IsSolid(a_XM);
@ -76,9 +83,20 @@ public:
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);
if (KnotAlreadyExists)
@ -89,7 +107,7 @@ public:
// New knot? needs to init and produce sound effect
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();
NewLeashKnotPtr->TiePlayersLeashedMobs(a_Player, KnotAlreadyExists);
@ -112,11 +130,25 @@ public:
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
{
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());
OldMetaData ^= 4; // Toggle the gate
if ((OldMetaData & 1) == (NewMetaData & 1))
{
// 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
{
// 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;
}
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
{
return true;
}
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{
UNUSED(a_Meta);

View File

@ -7,12 +7,15 @@
class cBlockFlowerHandler :
class cBlockFlowerHandler:
public cBlockHandler
{
using Super = cBlockHandler;
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
{
UNUSED(a_Meta);

View File

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

View File

@ -406,8 +406,9 @@ cBlockHandler::cBlockHandler(BLOCKTYPE a_BlockType)
bool cBlockHandler::GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
int a_CursorX, int a_CursorY, int a_CursorZ,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
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)
{
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;
}
@ -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(Vector3d a_Position, const BLOCKTYPE a_BlockType, const NIBBLETYPE a_BlockMeta)
bool cBlockHandler::IsInsideBlock(const Vector3d a_RelPosition, 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
)
{
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())
{

View File

@ -44,19 +44,28 @@ public:
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);
/** 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.
Also, the handler should set a_BlockType and a_BlockMeta to correct values for the newly placed block.
Called by cItemHandler::GetPlacementBlockTypeMeta() if the item is a block */
a_PlacedBlockPos is the coords of the block being placed
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(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
int a_CursorX, int a_CursorY, int a_CursorZ,
cChunkInterface & a_ChunkInterface,
cPlayer & a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
);
/** 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(). */
virtual void OnPlacedByPlayer(
@ -118,18 +127,37 @@ public:
cChunkInterface & a_ChunkInterface,
cWorldInterface & a_WorldInterface,
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
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, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) { return false; }
/** Called when the user right clicks the block and the block is useable.
a_CursorPos is the cursor position within the clicked block face.
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.
It forces the server to send the real state of a block to the client to prevent client assuming the operation is 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) {}
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,
const Vector3i a_BlockPos,
eBlockFace a_BlockFace
)
{
}
/** 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.
@ -145,41 +173,37 @@ public:
);
/** 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 */
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.
If it returns true, OnUse() is called */
virtual bool IsUseable(void);
/** 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);
/** 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
@param a_Pos Position of the block
@param a_Player Player trying to build on the block
@param a_Meta Meta value of the block currently at a_Pos
*/
virtual bool DoesIgnoreBuildCollision(cChunkInterface & ChunkInterface, Vector3i a_Pos, cPlayer & a_Player, NIBBLETYPE a_Meta);
@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);
/** Returns if this block drops if it gets destroyed by an unsuitable situation.
Default: true */
virtual bool DoesDropOnUnsuitable(void);
/** Tests if a_Position is inside the block where a_Position 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 */
virtual bool IsInsideBlock(Vector3d a_Position, const BLOCKTYPE a_BlockType, const NIBBLETYPE a_BlockMeta);
/** Tests if a_RelPosition is inside the block, where a_RelPosition is relative to the origin of the block.
Coords in a_RelPosition are guaranteed to be in the [0..1] range. */
virtual bool IsInsideBlock(const Vector3d a_RelPosition, const NIBBLETYPE a_BlockMeta);
/** 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()),

View File

@ -24,16 +24,18 @@ public:
virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
int a_CursorX, int a_CursorY, int a_CursorZ,
cChunkInterface & a_ChunkInterface,
cPlayer & a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override
{
a_BlockType = m_BlockType;
// 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_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> >
{
using Super = cClearMetaOnDrop<cMetaRotator<cBlockHandler, 0x07, 0x02, 0x05, 0x03, 0x04>>;
@ -25,24 +25,26 @@ public:
virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
int a_CursorX, int a_CursorY, int a_CursorZ,
cChunkInterface & a_ChunkInterface,
cPlayer & a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) 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);
if (a_BlockFace == BLOCK_FACE_BOTTOM)
a_ClickedBlockFace = FindSuitableBlockFace(a_ChunkInterface, a_PlacedBlockPos);
if (a_ClickedBlockFace == BLOCK_FACE_NONE)
{
return false;
}
}
a_BlockType = m_BlockType;
a_BlockMeta = DirectionToMetaData(a_BlockFace);
a_BlockMeta = BlockFaceToMetaData(a_ClickedBlockFace);
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_ZP: return 0x3;
@ -65,14 +68,15 @@ public:
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)
{
@ -88,47 +92,51 @@ public:
/** Finds a suitable Direction for the Ladder. Returns BLOCK_FACE_BOTTOM on failure */
static eBlockFace FindSuitableBlockFace(cChunkInterface & a_ChunkInterface, int a_BlockX, int a_BlockY, int a_BlockZ)
/** Finds a suitable block face value for the Ladder.
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++)
{
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 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;
}
AddFaceDirection( a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, true);
return cBlockInfo::IsSolid(a_ChunkInterface.GetBlock({a_BlockX, a_BlockY, a_BlockZ}));
auto NeighborPos = AddFaceDirection(a_LadderPos, a_NeighborBlockFace, true);
return cBlockInfo::IsSolid(a_ChunkInterface.GetBlock(NeighborPos));
}
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
eBlockFace BlockFace = MetaDataToDirection(a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ));
int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width;
int BlockZ = a_RelZ + a_Chunk.GetPosZ() * cChunkDef::Width;
return LadderCanBePlacedAt(a_ChunkInterface, BlockX, a_RelY, BlockZ, BlockFace);
auto NeighborBlockFace = MetaDataToBlockFace(a_Chunk.GetMeta(a_RelPos));
auto LadderAbsPos = a_Chunk.RelativeToAbsolute(a_RelPos);
return LadderCanBePlacedAt(a_ChunkInterface, LadderAbsPos, NeighborBlockFace);
}

View File

@ -6,14 +6,14 @@
#include "BlockSlab.h"
class cBlockLeverHandler :
class cBlockLeverHandler:
public cMetaRotator<cBlockHandler, 0x07, 0x04, 0x01, 0x03, 0x02, false>
{
using Super = cMetaRotator<cBlockHandler, 0x07, 0x04, 0x01, 0x03, 0x02, false>;
public:
cBlockLeverHandler(BLOCKTYPE a_BlockType) :
cBlockLeverHandler(BLOCKTYPE 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
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_WorldInterface.WakeUpSimulators(Coords);
a_WorldInterface.GetBroadcastManager().BroadcastSoundEffect("block.lever.click", Vector3d(Coords), 0.5f, (Meta & 0x08) ? 0.6f : 0.5f);
a_ChunkInterface.SetBlockMeta(a_BlockPos, Meta);
a_WorldInterface.WakeUpSimulators(a_BlockPos);
a_WorldInterface.GetBroadcastManager().BroadcastSoundEffect("block.lever.click", a_BlockPos, 0.5f, (Meta & 0x08) ? 0.6f : 0.5f);
return true;
}
@ -58,14 +64,16 @@ public:
virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
int a_CursorX, int a_CursorY, int a_CursorZ,
cChunkInterface & a_ChunkInterface,
cPlayer & a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override
{
a_BlockType = m_BlockType;
a_BlockMeta = LeverDirectionToMetaData(a_BlockFace);
a_BlockMeta = LeverDirectionToMetaData(a_ClickedBlockFace);
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)
{
// 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)
{
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);
eBlockFace Face = BlockMetaDataToBlockFace(Meta);
AddFaceDirection(a_RelX, a_RelY, a_RelZ, Face, true);
if ((a_RelY < 0) || (a_RelY >= cChunkDef::Height -1))
// Find the type of block the lever is attached to:
auto Meta = a_Chunk.GetMeta(a_RelPos);
auto NeighborFace = BlockMetaDataToBlockFace(Meta);
auto NeighborPos = AddFaceDirection(a_RelPos, NeighborFace, true);
if (!cChunkDef::IsValidHeight(NeighborPos.y))
{
return false;
}
BLOCKTYPE NeighborBlockType;
if (!a_Chunk.UnboundedRelGetBlock(NeighborPos, NeighborBlockType, Meta))
{
return false;
}
BLOCKTYPE BlockIsOn;
a_Chunk.UnboundedRelGetBlock(a_RelX, a_RelY, a_RelZ, BlockIsOn, Meta);
if (cBlockInfo::FullyOccupiesVoxel(BlockIsOn))
// Allow any full block or the "good" side of a half-slab:
if (cBlockInfo::FullyOccupiesVoxel(NeighborBlockType))
{
return true;
}
else if (cBlockSlabHandler::IsAnySlabType(BlockIsOn))
else if (cBlockSlabHandler::IsAnySlabType(NeighborBlockType))
{
// Check if the slab is turned up side down
if (((Meta & 0x08) == 0x08) && (Face == BLOCK_FACE_TOP))
{
return true;
}
return (
(((Meta & 0x08) == 0x08) && (NeighborFace == BLOCK_FACE_TOP)) ||
(((Meta & 0x08) == 0) && (NeighborFace == BLOCK_FACE_BOTTOM))
);
}
return false;

View File

@ -19,6 +19,10 @@ public:
{
}
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{
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;
}
BLOCKTYPE UnderType;
NIBBLETYPE UnderMeta;
a_Chunk.GetBlockTypeMeta(a_RelX, a_RelY - 1, a_RelZ, UnderType, UnderMeta);
a_Chunk.GetBlockTypeMeta(UnderPos, UnderType, UnderMeta);
return (
(((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

View File

@ -8,22 +8,38 @@
class cBlockMobSpawnerHandler :
class cBlockMobSpawnerHandler:
public cBlockHandler
{
using Super = cBlockHandler;
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
{
return true;

View File

@ -7,6 +7,7 @@
/** Handler for the small (singleblock) mushrooms. */
class cBlockMushroomHandler:
public cClearMetaOnDrop<cBlockHandler>
{
@ -19,18 +20,26 @@ public:
{
}
// 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;
}
// 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_CACTUS:
@ -45,6 +54,10 @@ public:
return true;
}
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{
UNUSED(a_Meta);

View File

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

View File

@ -7,25 +7,33 @@
class cBlockPortalHandler :
class cBlockPortalHandler:
public cBlockHandler
{
using Super = cBlockHandler;
public:
cBlockPortalHandler(BLOCKTYPE a_BlockType)
: cBlockHandler(a_BlockType)
cBlockPortalHandler(BLOCKTYPE a_BlockType):
Super(a_BlockType)
{
}
virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
int a_CursorX, int a_CursorY, int a_CursorZ,
cChunkInterface & a_ChunkInterface,
cPlayer & a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) 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
// 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_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
}
switch (a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ))
switch (a_Chunk.GetMeta(a_RelPos))
{
case 0x1:
{
@ -91,8 +99,7 @@ public:
for (const auto & Direction : PortalCheck)
{
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))
{
return false;
@ -115,8 +122,7 @@ public:
for (const auto & Direction : PortalCheck)
{
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))
{
return false;
@ -128,6 +134,10 @@ public:
return true;
}
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{
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;
}
// 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)
{
case E_BLOCK_ACACIA_FENCE:
@ -47,6 +51,10 @@ public:
}
}
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{
UNUSED(a_Meta);

View File

@ -7,42 +7,57 @@
class cBlockQuartzHandler : public cBlockHandler
class cBlockQuartzHandler:
public cBlockHandler
{
using Super = cBlockHandler;
public:
cBlockQuartzHandler(BLOCKTYPE a_BlockType)
: cBlockHandler(a_BlockType)
cBlockQuartzHandler(BLOCKTYPE a_BlockType):
Super(a_BlockType)
{
}
virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
int a_CursorX, int a_CursorY, int a_CursorZ,
cChunkInterface & a_ChunkInterface,
cPlayer & a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override
{
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;
return true;
}
a_BlockMeta = BlockFaceToMetaData(a_BlockFace, Meta);
a_BlockMeta = BlockFaceToMetaData(a_ClickedBlockFace);
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)
{
case BLOCK_FACE_YM:
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:
@ -57,15 +72,17 @@ public:
return 0x3; // East or west
}
case BLOCK_FACE_NONE:
default:
{
ASSERT(!"Unhandled block face!");
return a_QuartzMeta; // No idea, give a special meta (all sides the same)
return E_META_QUARTZ_PILLAR;
}
}
UNREACHABLE("Unsupported block face");
}
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{
UNUSED(a_Meta);

View File

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

View File

@ -8,7 +8,7 @@
class cBlockRedstoneHandler :
class cBlockRedstoneHandler:
public 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;
}
BLOCKTYPE BelowBlock;
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))
{
@ -46,11 +50,19 @@ public:
return false;
}
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);
}
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{
UNUSED(a_Meta);

View File

@ -17,31 +17,39 @@ public:
using Super::Super; // Inherit constructor from base
virtual bool OnUse(
cChunkInterface & a_ChunkInterface,
cWorldInterface & a_WorldInterface,
cPlayer & a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ,
cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer & a_Player,
const Vector3i a_BlockPos,
eBlockFace a_BlockFace,
int a_CursorX, int a_CursorY, int a_CursorZ
const Vector3i a_CursorPos
) override
{
Vector3i BlockPos{a_BlockX, a_BlockY, a_BlockZ};
a_ChunkInterface.SetBlock(BlockPos, E_BLOCK_REDSTONE_ORE_GLOWING, 0);
a_ChunkInterface.SetBlock(a_BlockPos, E_BLOCK_REDSTONE_ORE_GLOWING, 0);
return false;
}
virtual void OnDigging(
cChunkInterface & a_ChunkInterface,
cWorldInterface & a_WorldInterface,
cPlayer & a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ
const Vector3i a_BlockPos
) override
{
Vector3i BlockPos{a_BlockX, a_BlockY, a_BlockZ};
a_ChunkInterface.SetBlock(BlockPos, E_BLOCK_REDSTONE_ORE_GLOWING, 0);
a_ChunkInterface.SetBlock(a_BlockPos, E_BLOCK_REDSTONE_ORE_GLOWING, 0);
}
virtual bool IsUseable() override
{
return true;
@ -61,6 +69,10 @@ public:
using Super::Super; // Inherit constructor from base
virtual void OnUpdate(
cChunkInterface & a_ChunkInterface,
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;
}
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);
a_WorldInterface.SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, a_Player);
a_WorldInterface.SendBlockTo(a_BlockPos, a_Player);
}
virtual bool IsUseable(void) override
{
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;
}
BLOCKTYPE BelowBlock;
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))
{
@ -66,17 +96,29 @@ public:
return false;
}
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);
}
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{
UNUSED(a_Meta);
return 11;
}
inline static Vector3i GetLeftCoordinateOffset(NIBBLETYPE a_Meta)
{
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)
{
return -GetRearCoordinateOffset(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

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(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
int a_CursorX, int a_CursorY, int a_CursorZ,
cChunkInterface & a_ChunkInterface,
cPlayer & a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override
{
a_BlockType = m_BlockType;
NIBBLETYPE Meta = static_cast<NIBBLETYPE>(a_Player.GetEquippedItem().m_ItemDamage);
a_BlockMeta = BlockFaceToMetaData(a_BlockFace, Meta);
a_BlockMeta = BlockFaceToMetaData(a_ClickedBlockFace, Meta);
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;
}
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));
}
/** Converts the (player) rotation to placed-signpost block meta. */
static NIBBLETYPE RotationToMetaData(double a_Rotation)
{
a_Rotation += 180 + (180 / 16); // So it's not aligned with axis
@ -56,16 +60,28 @@ public:
return (static_cast<char>(a_Rotation)) % 16;
}
virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override
{
return (a_Meta + 4) & 0x0f;
}
virtual NIBBLETYPE MetaRotateCCW(NIBBLETYPE a_Meta) override
{
return (a_Meta + 12) & 0x0f;
}
virtual NIBBLETYPE MetaMirrorXY(NIBBLETYPE a_Meta) override
{
// Mirrors signs over the XY plane (North-South Mirroring)
@ -75,6 +91,10 @@ public:
return (a_Meta < 0x08) ? (0x08 - a_Meta) : (0x18 - a_Meta);
}
virtual NIBBLETYPE MetaMirrorYZ(NIBBLETYPE a_Meta) override
{
// Mirrors signs over the YZ plane (East-West Mirroring)
@ -84,6 +104,10 @@ public:
return 0x0f - a_Meta;
}
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{
UNUSED(a_Meta);

View File

@ -44,9 +44,11 @@ public:
virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
int a_CursorX, int a_CursorY, int a_CursorZ,
cChunkInterface & a_ChunkInterface,
cPlayer & a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override
{
@ -54,18 +56,18 @@ public:
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)
switch (a_BlockFace)
switch (a_ClickedBlockFace)
{
case BLOCK_FACE_TOP:
{
// Bottom half slab block
a_BlockMeta = Meta & 0x7;
a_BlockMeta = Meta & 0x07;
break;
}
case BLOCK_FACE_BOTTOM:
{
// Top half slab block
a_BlockMeta = Meta | 0x8;
a_BlockMeta = Meta | 0x08;
break;
}
case BLOCK_FACE_EAST:
@ -73,15 +75,15 @@ public:
case BLOCK_FACE_SOUTH:
case BLOCK_FACE_WEST:
{
if (a_CursorY > 7)
if (a_CursorPos.y > 7)
{
// Cursor at top half of block, place top slab
a_BlockMeta = Meta | 0x8; break;
a_BlockMeta = Meta | 0x08; break;
}
else
{
// 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;
@ -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
// 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_BlockMeta = a_BlockMeta & 0x7;
a_BlockMeta = a_BlockMeta & 0x07;
}
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)))
{
@ -125,7 +133,7 @@ public:
}
// 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 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
{
using Super = cBlockHandler;

View File

@ -10,30 +10,40 @@
class cBlockSnowHandler :
public cBlockHandler
{
using Super = cBlockHandler;
public:
enum
{
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(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
int a_CursorX, int a_CursorY, int a_CursorZ,
cChunkInterface & a_ChunkInterface,
cPlayer & a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override
{
a_BlockType = m_BlockType;
// Check if incrementing existing snow height:
BLOCKTYPE BlockBeforePlacement;
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))
{
// Only increment if:
@ -45,16 +55,19 @@ public:
// First time placement, check placement is valid
a_BlockMeta = 0;
BLOCKTYPE BlockBelow;
NIBBLETYPE MetaBelow;
return (
(a_BlockY > 0) &&
a_ChunkInterface.GetBlockTypeMeta({a_BlockX, a_BlockY - 1, a_BlockZ}, BlockBelow, MetaBelow) &&
(a_PlacedBlockPos.y > 0) &&
a_ChunkInterface.GetBlockTypeMeta(a_PlacedBlockPos.addedY(-1), BlockBelow, MetaBelow) &&
CanBeOn(BlockBelow, MetaBelow)
);
}
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))
@ -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);
NIBBLETYPE MetaBelow = a_Chunk.GetMeta(a_RelX, a_RelY - 1, a_RelZ);
return CanBeOn(BlockBelow, MetaBelow);
return false;
}
return false;
auto BelowPos = a_RelPos.addedY(-1);
auto BlockBelow = a_Chunk.GetBlock(BelowPos);
auto MetaBelow = a_Chunk.GetMeta(BelowPos);
return CanBeOn(BlockBelow, MetaBelow);
}
virtual bool DoesDropOnUnsuitable(void) override
{
return false;
}
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{
UNUSED(a_Meta);
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:
/** 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):
Super(a_BlockType)
{
}
virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
int a_CursorX, int a_CursorY, int a_CursorZ,
cChunkInterface & a_ChunkInterface,
cPlayer & a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override
{
UNUSED(a_ChunkInterface);
UNUSED(a_BlockX);
UNUSED(a_BlockY);
UNUSED(a_BlockZ);
UNUSED(a_CursorX);
UNUSED(a_CursorZ);
UNUSED(a_PlacedBlockPos);
UNUSED(a_CursorPos);
a_BlockType = m_BlockType;
a_BlockMeta = RotationToMetaData(a_Player.GetYaw());
switch (a_BlockFace)
switch (a_ClickedBlockFace)
{
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
@ -45,7 +47,7 @@ public:
case BLOCK_FACE_WEST:
{
// 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;
}
@ -56,6 +58,10 @@ public:
return true;
}
static NIBBLETYPE RotationToMetaData(double a_Rotation)
{
a_Rotation += 90 + 45; // So its not aligned with axis
@ -81,12 +87,20 @@ public:
}
}
virtual NIBBLETYPE MetaMirrorXZ(NIBBLETYPE a_Meta) override
{
// Toggle bit 3:
return (a_Meta & 0x0b) | ((~a_Meta) & 0x04);
}
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{
UNUSED(a_Meta);
@ -114,12 +128,16 @@ public:
}
}
/** EXCEPTION a.k.a. why is this removed:
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
two in sync by assuming that if a player hit ANY of the stair's bounding cube, it counts as the ground. */
#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
{
@ -127,19 +145,19 @@ public:
}
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)
{
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)
{
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)
{
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;
}

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;
}
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_GRASS:
case E_BLOCK_FARMLAND:
case E_BLOCK_SAND:
{
static const struct
static const Vector3i Coords[] =
{
int x, z;
} Coords[] =
{
{-1, 0},
{ 1, 0},
{ 0, -1},
{ 0, 1},
{-1, -1, 0},
{ 1, -1, 0},
{ 0, -1, -1},
{ 0, -1, 1},
} ;
a_RelY -= 1;
for (size_t i = 0; i < ARRAYCOUNT(Coords); i++)
{
BLOCKTYPE BlockType;
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
return true;

View File

@ -10,17 +10,34 @@
class cBlockTNTHandler :
public cBlockHandler
{
using Super = cBlockHandler;
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
{
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;
}
BLOCKTYPE BelowBlock = a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ);
BLOCKTYPE BelowBlock = a_Chunk.GetBlock(a_RelPos.addedY(-1));
return IsBlockTypeOfDirt(BelowBlock);
}

View File

@ -8,7 +8,7 @@
class cBlockTorchHandler :
class cBlockTorchHandler:
public 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(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
int a_CursorX, int a_CursorY, int a_CursorZ,
cChunkInterface & a_ChunkInterface,
cPlayer & a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override
{
BLOCKTYPE Block;
NIBBLETYPE Meta;
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, true); // Set to clicked block
a_ChunkInterface.GetBlockTypeMeta({a_BlockX, a_BlockY, a_BlockZ}, Block, Meta);
if (!CanBePlacedOn(Block, Meta, a_BlockFace)) // Try to preserve original direction
BLOCKTYPE ClickedBlockType;
NIBBLETYPE ClickedBlockMeta;
auto ClickedBlockPos = AddFaceDirection(a_PlacedBlockPos, a_ClickedBlockFace, true);
a_ChunkInterface.GetBlockTypeMeta(ClickedBlockPos, ClickedBlockType, ClickedBlockMeta);
if (!CanBePlacedOn(ClickedBlockType, ClickedBlockMeta, a_ClickedBlockFace))
{
// Torch couldn't be placed on whatever face was clicked, last ditch resort - find another face
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, false); // Reset to torch block
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)
// 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
if (a_ClickedBlockFace == BLOCK_FACE_NONE)
{
// No attachable face found - don't place the torch
return false;
}
}
a_BlockType = m_BlockType;
a_BlockMeta = DirectionToMetaData(a_BlockFace);
a_BlockMeta = BlockFaceToMetaData(a_ClickedBlockFace);
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_TOP: return E_META_TORCH_FLOOR;
@ -69,7 +76,12 @@ public:
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)
{
@ -88,6 +100,11 @@ public:
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)
{
switch (a_BlockType)
@ -115,7 +132,7 @@ public:
case E_BLOCK_STONE_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));
}
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;
}
else
{
// Reset coords in preparation for next iteration
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, Face, false);
}
}
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 CanBePlacedOn(BlockInQuestion, BlockInQuestionMeta, Face);
return CanBePlacedOn(NeighborBlockType, NeighborBlockMeta, Face);
}
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{
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)
{
@ -41,36 +48,56 @@ public:
}
// Flip the ON bit on / off using the XOR bitwise operation
NIBBLETYPE Meta = (a_ChunkInterface.GetBlockMeta({a_BlockX, a_BlockY, a_BlockZ}) ^ 0x04);
a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta);
a_WorldInterface.GetBroadcastManager().BroadcastSoundParticleEffect(EffectID::SFX_RANDOM_FENCE_GATE_OPEN, { a_BlockX, a_BlockY, a_BlockZ }, 0, a_Player.GetClientHandle());
NIBBLETYPE Meta = (a_ChunkInterface.GetBlockMeta(a_BlockPos) ^ 0x04);
a_ChunkInterface.SetBlockMeta(a_BlockPos, Meta);
a_WorldInterface.GetBroadcastManager().BroadcastSoundParticleEffect(EffectID::SFX_RANDOM_FENCE_GATE_OPEN, a_BlockPos, 0, a_Player.GetClientHandle());
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);
a_WorldInterface.SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, a_Player);
a_WorldInterface.SendBlockTo(a_BlockPos, a_Player);
}
virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
int a_CursorX, int a_CursorY, int a_CursorZ,
cChunkInterface & a_ChunkInterface,
cPlayer & a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override
{
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;
}
return true;
}
inline static NIBBLETYPE BlockFaceToMetaData(eBlockFace a_BlockFace)
{
switch (a_BlockFace)
@ -90,6 +117,10 @@ public:
UNREACHABLE("Unsupported block face");
}
inline static eBlockFace BlockMetaDataToBlockFace(NIBBLETYPE a_Meta)
{
switch (a_Meta & 0x3)
@ -106,6 +137,10 @@ public:
}
}
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{
UNUSED(a_Meta);

View File

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

View File

@ -23,23 +23,25 @@ public:
virtual bool GetPlacementBlockTypeMeta(
cChunkInterface & a_ChunkInterface, cPlayer & a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
int a_CursorX, int a_CursorY, int a_CursorZ,
cChunkInterface & a_ChunkInterface,
cPlayer & a_Player,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
) override
{
// TODO: Disallow placement where the vine doesn't attach to something properly
BLOCKTYPE BlockType = 0;
NIBBLETYPE BlockMeta;
a_ChunkInterface.GetBlockTypeMeta({a_BlockX, a_BlockY, a_BlockZ}, BlockType, BlockMeta);
a_ChunkInterface.GetBlockTypeMeta(a_PlacedBlockPos, BlockType, BlockMeta);
if (BlockType == m_BlockType)
{
a_BlockMeta = BlockMeta | DirectionToMetaData(a_BlockFace);
a_BlockMeta = BlockMeta | DirectionToMetaData(a_ClickedBlockFace);
}
else
{
a_BlockMeta = DirectionToMetaData(a_BlockFace);
a_BlockMeta = DirectionToMetaData(a_ClickedBlockFace);
}
a_BlockType = m_BlockType;
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;
int BlockZ = (a_Chunk.GetPosZ() * cChunkDef::Width) + a_RelZ;
GetBlockCoordsBehindTheSign(a_Chunk.GetMeta(a_RelX, a_RelY, a_RelZ), BlockX, BlockZ);
BLOCKTYPE Type = a_ChunkInterface.GetBlock({BlockX, a_RelY, BlockZ});
return ((Type == E_BLOCK_WALLSIGN) || (Type == E_BLOCK_SIGN_POST) || cBlockInfo::IsSolid(Type));
auto NeighborPos = a_RelPos + GetOffsetBehindTheSign(a_Chunk.GetMeta(a_RelPos));
BLOCKTYPE NeighborType;
if (!a_Chunk.UnboundedRelGetBlockType(NeighborPos, NeighborType))
{
// The neighbor is not accessible (unloaded chunk), bail out without changing this
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)
{
case 2: a_BlockZ++; break;
case 3: a_BlockZ--; break;
case 4: a_BlockX++; break;
case 5: a_BlockX--; break;
default: break;
case 2: return Vector3i( 0, 0, 1);
case 3: return Vector3i( 0, 0, -1);
case 4: return Vector3i( 1, 0, 0);
case 5: return Vector3i(-1, 0, 0);
}
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_ZP: return 0x03;

View File

@ -12,24 +12,46 @@
class cBlockWorkbenchHandler:
public cBlockHandler
{
using Super = cBlockHandler;
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);
return true;
}
virtual bool IsUseable(void) override
{
return true;
}
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) override
{
UNUSED(a_Meta);

View File

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

View File

@ -60,6 +60,12 @@ public:
/** 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;
/** 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 */
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.
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});
}

View File

@ -555,8 +555,17 @@ struct sSetBlock
/** Returns the absolute Z coord of the stored block. */
int GetZ(void) const { return m_RelZ + cChunkDef::Width * m_ChunkZ; }
/** Returns the absolute position of the stored block. */
Vector3i GetPos(void) const { return Vector3i(GetX(), GetY(), GetZ()); }
/** Returns the absolute coords of the stored block. */
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;

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->OnItemShoot(m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
ItemHandler->OnItemShoot(m_Player, {a_BlockX, a_BlockY, a_BlockZ}, a_BlockFace);
}
return;
}
@ -1294,10 +1294,10 @@ void cClientHandle::HandleBlockDigStarted(int a_BlockX, int a_BlockY, int a_Bloc
cWorld * World = m_Player->GetWorld();
cChunkInterface ChunkInterface(World->GetChunkMap());
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());
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
// 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();
FLOGD("HandleRightClick: {0}, face {1}, Hand: {2}, HeldItem: {3}; Dist: {4:.02f}",
Vector3i{a_BlockX, a_BlockY, a_BlockZ}, a_BlockFace, a_Hand, ItemToFullString(HeldItem), Dist
auto ClickedBlockPos = Vector3i(a_BlockX, a_BlockY, a_BlockZ);
auto CursorPos = Vector3i(a_CursorX, a_CursorY, a_CursorZ);
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:
@ -1464,7 +1466,7 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
{
BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta;
World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta);
World->GetBlockTypeMeta(ClickedBlockPos, BlockType, BlockMeta);
cBlockHandler * BlockHandler = cBlockInfo::GetHandler(BlockType);
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());
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
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)
{
// 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
{
// 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:
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)
@ -1507,7 +1509,7 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
return;
}
// 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
{
@ -1516,7 +1518,7 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
{
// All plugins agree with using the item
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);
Success = true;
}
@ -1827,7 +1829,7 @@ void cClientHandle::HandleUseItem(eHand a_Hand)
{
// All plugins agree with using the item
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);
}
}
@ -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)
{
@ -2652,7 +2654,7 @@ void cClientHandle::SendExplosion(double a_BlockX, double a_BlockY, double a_Blo
// Update the statistics:
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 SendExperience (void);
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 SendHealth (void);
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.
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) */
static const struct
{
int x, y, z;
} BlockSampleOffsets[] =
static const Vector3i BlockSampleOffsets[] =
{
{ 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)
@ -354,26 +351,26 @@ void cPawn::HandleFalling(void)
/* We go through the blocks that we consider "relevant" */
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))
{
continue;
}
BLOCKTYPE Block = GetWorld()->GetBlock(BlockTestPosition);
BLOCKTYPE BlockType = GetWorld()->GetBlock(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 */
if (j == 0)
{
IsFootInWater |= IsBlockWater(Block);
IsFootInLiquid |= IsFootInWater || IsBlockLava(Block) || (Block == E_BLOCK_COBWEB); // okay so cobweb is not _technically_ a liquid...
IsFootOnSlimeBlock |= (Block == E_BLOCK_SLIME_BLOCK);
IsFootInWater |= IsBlockWater(BlockType);
IsFootInLiquid |= IsFootInWater || IsBlockLava(BlockType) || (BlockType == E_BLOCK_COBWEB); // okay so cobweb is not _technically_ a liquid...
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 ((cBlockInfo::IsSolid(Block)) && (cBlockInfo::GetHandler(Block)->IsInsideBlock(CrossTestPosition - BlockTestPosition, Block, BlockMeta)))
if ((cBlockInfo::IsSolid(BlockType)) && (cBlockInfo::GetHandler(BlockType)->IsInsideBlock(CrossTestPosition - BlockTestPosition, BlockMeta)))
{
OnGround = true;
}

View File

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

View File

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

View File

@ -15,43 +15,50 @@ class cItemBigFlowerHandler:
public:
cItemBigFlowerHandler(void):
cItemBigFlowerHandler():
Super(E_BLOCK_BIG_FLOWER)
{
}
virtual bool GetBlocksToPlace(
cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
int a_CursorX, int a_CursorY, int a_CursorZ,
sSetBlockVector & a_BlocksToSet
cWorld & a_World,
cPlayer & a_Player,
const cItem & a_EquippedItem,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
sSetBlockVector & a_BlocksToPlace
) override
{
// 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;
}
// Needs at least two free blocks to build in
if (a_BlockY >= cChunkDef::Height - 1)
if (a_PlacedBlockPos.y >= cChunkDef::Height - 1)
{
return false;
}
auto TopPos = a_PlacedBlockPos.addedY(1);
BLOCKTYPE TopType;
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());
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;
}
a_BlocksToSet.emplace_back(a_BlockX, a_BlockY, a_BlockZ, 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(a_PlacedBlockPos, E_BLOCK_BIG_FLOWER, a_EquippedItem.m_ItemDamage & 0x07);
a_BlocksToPlace.emplace_back(TopPos, E_BLOCK_BIG_FLOWER, E_META_BIG_FLOWER_TOP);
return true;
}
};

View File

@ -22,24 +22,32 @@ public:
virtual bool OnItemUse(
cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace
cWorld * a_World,
cPlayer * a_Player,
cBlockPluginInterface & a_PluginInterface,
const cItem & a_HeldItem,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace
) 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;
}
class cCallbacks :
// Find the actual placement position by tracing line of sight until non-air block:
class cCallbacks:
public cBlockTracer::cCallbacks
{
public:
Vector3d m_Pos;
bool m_HasFound;
cCallbacks(void) :
cCallbacks():
m_HasFound(false)
{
}
@ -55,27 +63,21 @@ public:
return false;
}
} Callbacks;
cLineBlockTracer Tracer(*a_World, Callbacks);
Vector3d Start(a_Player->GetEyePosition() + a_Player->GetLookVector());
Vector3d End(a_Player->GetEyePosition() + a_Player->GetLookVector() * 5);
Tracer.Trace(Start.x, Start.y, Start.z, End.x, End.y, End.z);
auto Start = a_Player->GetEyePosition() + a_Player->GetLookVector();
auto End = a_Player->GetEyePosition() + a_Player->GetLookVector() * 5;
cLineBlockTracer::Trace(*a_World, Callbacks, Start, End);
if (!Callbacks.m_HasFound)
{
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)
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)
{
return false;

View File

@ -9,19 +9,28 @@
class cItemBottleHandler :
class cItemBottleHandler:
public cItemHandler
{
using Super = cItemHandler;
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)
{
class cCallbacks :
class cCallbacks:
public cBlockTracer::cCallbacks
{
public:
@ -29,7 +38,7 @@ public:
bool m_HasHitFluid;
cCallbacks(void) :
cCallbacks():
m_HasHitFluid(false)
{
}
@ -49,31 +58,32 @@ public:
return false;
}
} Callbacks;
cLineBlockTracer Tracer(*a_World, Callbacks);
Vector3d Start(a_Player->GetEyePosition() + a_Player->GetLookVector());
Vector3d End(a_Player->GetEyePosition() + a_Player->GetLookVector() * 5);
Tracer.Trace(Start.x, Start.y, Start.z, End.x, End.y, End.z);
auto Start = a_Player->GetEyePosition() + a_Player->GetLookVector();
auto End = a_Player->GetEyePosition() + a_Player->GetLookVector() * 5;
cLineBlockTracer::Trace(*a_World, Callbacks, Start, End);
if (!Callbacks.m_HasHitFluid)
{
return false;
}
a_BlockPos = Callbacks.m_Pos;
return true;
}
virtual bool OnItemUse(
cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace
) override
cWorld * a_World,
cPlayer * a_Player,
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;
}

View File

@ -22,9 +22,15 @@ public:
virtual bool OnItemUse(
cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace
cWorld * a_World,
cPlayer * a_Player,
cBlockPluginInterface & a_PluginInterface,
const cItem & a_HeldItem,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace
) override
{
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);
int BowCharge = a_Player->FinishChargingBow();
@ -88,7 +95,6 @@ public:
a_Player->UseEquippedItem();
}
if (a_Player->GetEquippedItem().m_Enchantments.GetLevel(cEnchantments::enchFlame) > 0)
{
ArrowPtr->StartBurning(100);

View File

@ -7,12 +7,15 @@
class cItemBrewingStandHandler :
class cItemBrewingStandHandler:
public cItemHandler
{
using Super = cItemHandler;
public:
cItemBrewingStandHandler(int a_ItemType) :
cItemHandler(a_ItemType)
cItemBrewingStandHandler(int a_ItemType):
Super(a_ItemType)
{
}
@ -25,8 +28,9 @@ public:
virtual bool GetPlacementBlockTypeMeta(
cWorld * a_World, cPlayer * a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
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
) override
{

View File

@ -16,25 +16,34 @@
class cItemBucketHandler :
public cItemHandler
{
using Super = cItemHandler;
public:
cItemBucketHandler(int a_ItemType) :
cItemHandler(a_ItemType)
cItemBucketHandler(int a_ItemType):
Super(a_ItemType)
{
}
virtual bool OnItemUse(
cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace
cWorld * a_World,
cPlayer * a_Player,
cBlockPluginInterface & a_PluginInterface,
const cItem & a_HeldItem,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace
) override
{
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_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_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_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_HeldItem, a_ClickedBlockPos, a_ClickedBlockFace, E_BLOCK_LAVA);
case E_ITEM_WATER_BUCKET: return PlaceFluid (a_World, a_Player, a_PluginInterface, a_HeldItem, a_ClickedBlockPos, a_ClickedBlockFace, E_BLOCK_WATER);
default:
{
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.
if (a_Player->IsGameModeAdventure())
@ -53,7 +64,8 @@ public:
return false;
}
if (a_BlockFace != BLOCK_FACE_NONE)
// Needs a valid clicked block:
if (a_ClickedBlockFace != BLOCK_FACE_NONE)
{
return false;
}
@ -114,7 +126,7 @@ public:
bool PlaceFluid(
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.
@ -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)
{
class cCallbacks :

View File

@ -7,12 +7,15 @@
class cItemCakeHandler :
class cItemCakeHandler:
public cItemHandler
{
using Super = cItemHandler;
public:
cItemCakeHandler(int a_ItemType) :
cItemHandler(a_ItemType)
cItemCakeHandler(int a_ItemType):
Super(a_ItemType)
{
}
@ -25,8 +28,9 @@ public:
virtual bool GetPlacementBlockTypeMeta(
cWorld * a_World, cPlayer * a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
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
) override
{

View File

@ -7,12 +7,15 @@
class cItemCauldronHandler :
class cItemCauldronHandler:
public cItemHandler
{
using Super = cItemHandler;
public:
cItemCauldronHandler(int a_ItemType) :
cItemHandler(a_ItemType)
cItemCauldronHandler(int a_ItemType):
Super(a_ItemType)
{
}
@ -25,8 +28,9 @@ public:
virtual bool GetPlacementBlockTypeMeta(
cWorld * a_World, cPlayer * a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
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
) override
{

View File

@ -25,56 +25,57 @@ public:
/** We need an OnPlayerPlace override because we're processing neighbor chests and changing their metas,
the parent class cannot do that. */
virtual bool OnPlayerPlace(
cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
int a_CursorX, int a_CursorY, int a_CursorZ
cWorld & a_World,
cPlayer & a_Player,
const cItem & a_EquippedItem,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos
) override
{
if (a_BlockFace < 0)
if (a_ClickedBlockFace < 0)
{
// Clicked in air
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)
return false;
}
// Check if the block ignores build collision (water, grass etc.):
BLOCKTYPE clickedBlock;
NIBBLETYPE clickedBlockMeta;
Vector3i blockPos(a_BlockX, a_BlockY, a_BlockZ);
a_World.GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, clickedBlock, clickedBlockMeta);
BLOCKTYPE ClickedBlockType;
NIBBLETYPE ClickedBlockMeta;
a_World.GetBlockTypeMeta(a_ClickedBlockPos, ClickedBlockType, ClickedBlockMeta);
cChunkInterface ChunkInterface(a_World.GetChunkMap());
auto blockHandler = BlockHandler(clickedBlock);
if (blockHandler->DoesIgnoreBuildCollision(ChunkInterface, blockPos, a_Player, clickedBlockMeta))
auto blockHandler = BlockHandler(ClickedBlockType);
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
{
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height))
PlacePos = AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace);
if (!cChunkDef::IsValidHeight(PlacePos.y))
{
// The block is being placed outside the world, ignore this packet altogether (#128)
return false;
}
NIBBLETYPE PlaceMeta;
// Check if the chest can overwrite the block at PlacePos:
BLOCKTYPE PlaceBlock;
a_World.GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, 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.
// No need to do combinability (dblslab) checks, client will do that here.
if (blockHandler->DoesIgnoreBuildCollision(ChunkInterface, blockPos, a_Player, clickedBlockMeta))
NIBBLETYPE PlaceMeta;
a_World.GetBlockTypeMeta(PlacePos, PlaceBlock, PlaceMeta);
blockHandler = BlockHandler(PlaceBlock);
if (!blockHandler->DoesIgnoreBuildCollision(ChunkInterface, PlacePos, a_Player, PlaceMeta))
{
// 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;
}
blockHandler->OnPlayerBreakingBlock(ChunkInterface, a_World, a_Player, PlacePos);
}
// Check that there is at most one single neighbor of the same chest type:
@ -88,7 +89,8 @@ public:
int NeighborIdx = -1;
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;
}
@ -100,12 +102,11 @@ public:
NeighborIdx = static_cast<int>(i);
// 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++)
{
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;
}
} // for j
@ -133,13 +134,14 @@ public:
}
default:
{
// No neighbor, place based on yaw:
Meta = cBlockChestHandler::PlayerYawToMetaData(yaw);
break;
}
} // switch (NeighborIdx)
// 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;
}
@ -147,10 +149,10 @@ public:
// Adjust the existing chest, if any:
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())
{
a_Player.GetInventory().RemoveOneEquippedItem();

View File

@ -8,24 +8,36 @@
class cItemComparatorHandler :
class cItemComparatorHandler:
public cItemHandler
{
using Super = cItemHandler;
public:
cItemComparatorHandler(int a_ItemType) :
cItemComparatorHandler(int a_ItemType):
cItemHandler(a_ItemType)
{
}
virtual bool IsPlaceable(void) override
{
return true;
}
virtual bool GetPlacementBlockTypeMeta(
cWorld * a_World, cPlayer * a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
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
) override
{

View File

@ -9,38 +9,45 @@
class cItemDoorHandler :
class cItemDoorHandler:
public cItemHandler
{
using Super = cItemHandler;
public:
cItemDoorHandler(int a_ItemType) :
cItemHandler(a_ItemType)
cItemDoorHandler(int a_ItemType):
Super(a_ItemType)
{
}
virtual bool GetBlocksToPlace(
cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
int a_CursorX, int a_CursorY, int a_CursorZ,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
sSetBlockVector & a_BlocksToSet
) override
{
// 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;
}
// 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;
}
// 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;
}
@ -64,8 +71,9 @@ public:
}
// Check the two blocks that will get replaced by the door:
BLOCKTYPE LowerBlockType = a_World.GetBlock(a_BlockX, a_BlockY, a_BlockZ);
BLOCKTYPE UpperBlockType = a_World.GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ);
auto UpperBlockPos = a_PlacedBlockPos.addedY(1);
BLOCKTYPE LowerBlockType = a_World.GetBlock(a_PlacedBlockPos);
BLOCKTYPE UpperBlockType = a_World.GetBlock(UpperBlockPos);
if (
!cBlockDoorHandler::CanReplaceBlock(LowerBlockType) ||
!cBlockDoorHandler::CanReplaceBlock(UpperBlockType))
@ -78,10 +86,10 @@ public:
Vector3i RelDirToOutside = cBlockDoorHandler::GetRelativeDirectionToOutside(LowerBlockMeta);
Vector3i LeftNeighborPos = RelDirToOutside;
LeftNeighborPos.TurnCW();
LeftNeighborPos.Move(a_BlockX, a_BlockY, a_BlockZ);
LeftNeighborPos.Move(a_PlacedBlockPos);
Vector3i RightNeighborPos = RelDirToOutside;
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:
NIBBLETYPE UpperBlockMeta = 0x08;
@ -89,7 +97,7 @@ public:
BLOCKTYPE RightNeighborBlock = a_World.GetBlock(RightNeighborPos);
/*
// 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("Left neighbor at {0}: {1} ({2})", LeftNeighborPos, LeftNeighborBlock, ItemTypeToString(LeftNeighborBlock));
FLOGD("Right neighbor at {0}: {1} ({2})", RightNeighborPos, RightNeighborBlock, ItemTypeToString(RightNeighborBlock));
@ -108,12 +116,14 @@ public:
}
// Set the blocks:
a_BlocksToSet.emplace_back(a_BlockX, a_BlockY, a_BlockZ, BlockType, LowerBlockMeta);
a_BlocksToSet.emplace_back(a_BlockX, a_BlockY + 1, a_BlockZ, BlockType, UpperBlockMeta);
a_BlocksToSet.emplace_back(a_PlacedBlockPos, BlockType, LowerBlockMeta);
a_BlocksToSet.emplace_back(UpperBlockPos, BlockType, UpperBlockMeta);
return true;
}
virtual bool IsPlaceable(void) override
{
return true;

View File

@ -26,14 +26,18 @@ public:
virtual bool OnItemUse(
cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace
cWorld * a_World,
cPlayer * a_Player,
cBlockPluginInterface & a_PluginInterface,
const cItem & a_HeldItem,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace
) 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:
if (fertilizePlant(*a_World, {a_BlockX, a_BlockY, a_BlockZ}))
if (FertilizePlant(*a_World, a_ClickedBlockPos))
{
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.
if (a_Player->IsGameModeAdventure())
@ -53,25 +57,24 @@ public:
// Cocoa (brown dye) can be planted on jungle logs:
BLOCKTYPE BlockType;
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.
if ((BlockType != E_BLOCK_LOG) || ((BlockMeta & 0x3) != E_META_LOG_JUNGLE))
if ((BlockType != E_BLOCK_LOG) || ((BlockMeta & 0x03) != E_META_LOG_JUNGLE))
{
return false;
}
// Get the location from the new cocoa pod.
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, false);
BlockMeta = cBlockCocoaPodHandler::BlockFaceToMeta(a_BlockFace);
auto CocoaPos = AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace, false);
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;
}
// Place the cocoa pod:
if (a_Player->PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_COCOA_POD, BlockMeta))
if (a_Player->PlaceBlock(CocoaPos.x, CocoaPos.y, CocoaPos.z, E_BLOCK_COCOA_POD, BlockMeta))
{
if (a_Player->IsGameModeSurvival())
{
@ -97,7 +100,7 @@ public:
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,
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;
NIBBLETYPE blockMeta;

View File

@ -23,15 +23,19 @@ public:
virtual bool OnItemUse(
cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace
cWorld * a_World,
cPlayer * a_Player,
cBlockPluginInterface & a_PluginInterface,
const cItem & a_HeldItem,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace
) override
{
UNUSED(a_Item);
UNUSED(a_BlockX);
UNUSED(a_BlockZ);
UNUSED(a_BlockFace);
UNUSED(a_HeldItem);
UNUSED(a_ClickedBlockFace);
// 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 CenterZ = FloorC(a_Player->GetPosZ() / RegionWidth) * RegionWidth + (RegionWidth / 2);
cMap * NewMap = a_World->GetMapManager().CreateMap(CenterX, CenterZ, DEFAULT_SCALE);
// Remove empty map from inventory
if (!a_Player->GetInventory().RemoveOneEquippedItem())
{
ASSERT(!"Inventory mismatch");
return true;
}
auto NewMap = a_World->GetMapManager().CreateMap(CenterX, CenterZ, DEFAULT_SCALE);
if (NewMap == nullptr)
{
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;
}
} ;

View File

@ -8,46 +8,53 @@
class cItemEyeOfEnderHandler :
class cItemEyeOfEnderHandler:
public cItemThrowableHandler
{
typedef cItemThrowableHandler super;
using Super = cItemThrowableHandler;
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(
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
{
BLOCKTYPE FacingBlock;
NIBBLETYPE FacingMeta;
a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, FacingBlock, FacingMeta);
switch (FacingBlock)
// Try to fill an End Portal Frame block:
if (a_ClickedBlockFace != BLOCK_FACE_NONE)
{
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.
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())
{
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;
}

View File

@ -63,190 +63,36 @@ public:
virtual bool OnItemUse(
cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace
cWorld * a_World,
cPlayer * a_Player,
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;
}
auto & Random = GetRandomProvider();
if (a_Player->IsFishing())
{
cFloaterCallback FloaterInfo;
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);
}
}
ReelIn(*a_World, *a_Player);
}
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();
if (!FloaterPtr->Initialize(std::move(Floater), *a_World))
{
@ -256,4 +102,206 @@ public:
}
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
{
using Super = cItemHandler;
public:
cItemFlowerPotHandler(int a_ItemType) :
cItemHandler(a_ItemType)
cItemFlowerPotHandler(int a_ItemType):
Super(a_ItemType)
{
}
virtual bool IsPlaceable(void) override
{
return true;
}
virtual bool GetPlacementBlockTypeMeta(
cWorld * a_World, cPlayer * a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
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
) override
{

View File

@ -346,41 +346,44 @@ cItemHandler::cItemHandler(int a_ItemType)
bool cItemHandler::OnPlayerPlace(
cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
int a_CursorX, int a_CursorY, int a_CursorZ
cWorld & a_World,
cPlayer & a_Player,
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;
}
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)
return false;
}
BLOCKTYPE ClickedBlock;
BLOCKTYPE ClickedBlockType;
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());
// Check if the block ignores build collision (water, grass etc.):
auto blockHandler = BlockHandler(ClickedBlock);
Vector3i absPos(a_BlockX, a_BlockY, a_BlockZ);
if (blockHandler->DoesIgnoreBuildCollision(ChunkInterface, absPos, a_Player, ClickedBlockMeta))
auto HandlerB = BlockHandler(ClickedBlockType);
auto PlacedBlockPos = AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace);
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
{
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height))
if (!cChunkDef::IsValidHeight(PlacedBlockPos.y))
{
// The block is being placed outside the world, ignore this packet altogether (#128)
return false;
@ -388,11 +391,11 @@ bool cItemHandler::OnPlayerPlace(
NIBBLETYPE PlaceMeta;
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.
// 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?
// 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:
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:
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();
return false;
}
@ -436,18 +440,19 @@ bool cItemHandler::OnPlayerPlace(
bool cItemHandler::GetBlocksToPlace(
cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
int a_CursorX, int a_CursorY, int a_CursorZ,
const Vector3i a_PlacedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos,
sSetBlockVector & a_BlocksToSet
)
{
BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta;
if (!GetPlacementBlockTypeMeta(&a_World, &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta))
if (!GetPlacementBlockTypeMeta(&a_World, &a_Player, a_PlacedBlockPos, a_ClickedBlockFace, a_CursorPos, BlockType, BlockMeta))
{
return false;
}
a_BlocksToSet.emplace_back(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta);
a_BlocksToSet.emplace_back(a_PlacedBlockPos, BlockType, BlockMeta);
return true;
}
@ -457,17 +462,15 @@ bool cItemHandler::GetBlocksToPlace(
bool cItemHandler::OnItemUse(
cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace
const Vector3i a_ClickedBlockPos, eBlockFace a_ClickedBlockFace
)
{
UNUSED(a_World);
UNUSED(a_Player);
UNUSED(a_PluginInterface);
UNUSED(a_Item);
UNUSED(a_BlockX);
UNUSED(a_BlockY);
UNUSED(a_BlockZ);
UNUSED(a_BlockFace);
UNUSED(a_ClickedBlockPos);
UNUSED(a_ClickedBlockFace);
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_Player);
UNUSED(a_Item);
UNUSED(a_BlockX);
UNUSED(a_BlockY);
UNUSED(a_BlockZ);
UNUSED(a_Dir);
UNUSED(a_HeldItem);
UNUSED(a_ClickedBlockPos);
UNUSED(a_ClickedBlockFace);
return false;
}
@ -815,8 +822,8 @@ bool cItemHandler::CanHarvestBlock(BLOCKTYPE a_BlockType)
bool cItemHandler::GetPlacementBlockTypeMeta(
cWorld * a_World, cPlayer * a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
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
)
{
@ -832,8 +839,8 @@ bool cItemHandler::GetPlacementBlockTypeMeta(
cChunkInterface ChunkInterface(a_World->GetChunkMap());
return BlockH->GetPlacementBlockTypeMeta(
ChunkInterface, *a_Player,
a_BlockX, a_BlockY, a_BlockZ, a_BlockFace,
a_CursorX, a_CursorY, a_CursorZ,
a_PlacedBlockPos, a_ClickedBlockFace,
a_CursorPos,
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).
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.
Override if the item needs advanced processing, such as spawning a mob based on the blocks being placed.
If the block placement is refused inside this call, it will automatically revert the client-side changes.
Returns true if the placement succeeded, false if the placement was aborted for any reason. */
virtual bool OnPlayerPlace(
cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
int a_CursorX, int a_CursorY, int a_CursorZ
cWorld & a_World,
cPlayer & a_Player,
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.
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.
Returns true if the placement succeeded, false if the placement was aborted for any reason.
If aborted, the server then sends all original blocks in the coords provided in a_BlocksToSet to the client. */
virtual bool GetBlocksToPlace(
cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
int a_CursorX, int a_CursorY, int a_CursorZ,
sSetBlockVector & a_BlocksToSet
cWorld & a_World,
cPlayer & a_Player,
const cItem & a_EquippedItem,
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. */
virtual bool GetPlacementBlockTypeMeta(
cWorld * a_World, cPlayer * a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
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
);
/** 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(
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
cWorld * a_World,
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 */
virtual void OnItemShoot(cPlayer *, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace)
/** Called when the client sends the SHOOT status in the lclk packet (releasing the bow). */
virtual void OnItemShoot(cPlayer *, const Vector3i a_BlockPos, eBlockFace a_BlockFace)
{
UNUSED(a_BlockX);
UNUSED(a_BlockY);
UNUSED(a_BlockZ);
UNUSED(a_BlockPos);
UNUSED(a_BlockFace);
}
@ -100,9 +114,13 @@ public:
}
/** 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);
/** Called after the player has eaten this item. */

View File

@ -9,64 +9,68 @@
class cItemHoeHandler :
class cItemHoeHandler:
public cItemHandler
{
using Super = cItemHandler;
public:
cItemHoeHandler(int a_ItemType)
: cItemHandler(a_ItemType)
cItemHoeHandler(int a_ItemType):
Super(a_ItemType)
{
}
virtual bool OnItemUse(
cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace
cWorld * a_World,
cPlayer * a_Player,
cBlockPluginInterface & a_PluginInterface,
const cItem & a_HeldItem,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace
) override
{
if ((a_BlockFace == BLOCK_FACE_NONE) || (a_BlockY >= cChunkDef::Height))
if ((a_ClickedBlockFace == BLOCK_FACE_NONE) || (a_ClickedBlockPos.y >= cChunkDef::Height))
{
return false;
}
BLOCKTYPE UpperBlock = a_World->GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ);
BLOCKTYPE Block;
NIBBLETYPE BlockMeta;
a_World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, Block, BlockMeta);
if (((Block == E_BLOCK_DIRT) || (Block == E_BLOCK_GRASS)) && (UpperBlock == E_BLOCK_AIR))
// Need air above the hoe-d block to transform it:
BLOCKTYPE UpperBlockType = a_World->GetBlock(a_ClickedBlockPos.addedY(1));
if (UpperBlockType != E_BLOCK_AIR)
{
BLOCKTYPE NewBlock = E_BLOCK_FARMLAND;
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;
}
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
{
switch (a_Action)

View File

@ -9,51 +9,59 @@
class cItemItemFrameHandler :
class cItemItemFrameHandler:
public cItemHandler
{
public:
cItemItemFrameHandler(int a_ItemType)
: cItemHandler(a_ItemType)
{
using Super = cItemHandler;
public:
cItemItemFrameHandler(int a_ItemType):
Super(a_ItemType)
{
}
virtual bool OnItemUse(
cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace
cWorld * a_World,
cPlayer * a_Player,
cBlockPluginInterface & a_PluginInterface,
const cItem & a_HeldItem,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace
) 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;
}
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); // Make sure block that will be occupied is free
BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, true); // We want the clicked block, so go back again
if (Block == E_BLOCK_AIR)
// Make sure block that will be occupied by the item frame is free now:
auto PlacePos = AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace);
BLOCKTYPE Block = a_World->GetBlock(PlacePos);
if (Block != E_BLOCK_AIR)
{
auto ItemFrame = cpp14::make_unique<cItemFrame>(a_BlockFace, Vector3i{a_BlockX, a_BlockY, a_BlockZ});
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;
}
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(
cWorld * a_World, cPlayer * a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
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
) override
{
bool res = Super::GetPlacementBlockTypeMeta(
a_World, a_Player,
a_BlockX, a_BlockY, a_BlockZ, a_BlockFace,
a_CursorX, a_CursorY, a_CursorZ,
a_PlacedBlockPos,
a_ClickedBlockFace,
a_CursorPos,
a_BlockType, a_BlockMeta
);
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
{
using Super = cItemHandler;
public:
cItemLighterHandler(int a_ItemType) :
cItemHandler(a_ItemType)
cItemLighterHandler(int a_ItemType):
Super(a_ItemType)
{
}
virtual bool OnItemUse(
cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace
cWorld * a_World,
cPlayer * a_Player,
cBlockPluginInterface & a_PluginInterface,
const cItem & a_HeldItem,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace
) override
{
if (a_BlockFace < 0)
if (a_ClickedBlockFace < 0)
{
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:
{
// Activate the TNT:
a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, 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->SetBlock(a_ClickedBlockPos, E_BLOCK_AIR, 0);
a_World->SpawnPrimedTNT(Vector3d(a_ClickedBlockPos) + Vector3d(0.5, 0.5, 0.5)); // 80 ticks to boom
break;
}
default:
{
// Light a fire next to / on top of the block if air:
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height))
auto FirePos = AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace);
if (!cChunkDef::IsValidHeight(FirePos.y))
{
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->BroadcastSoundEffect("item.flintandsteel.use", Vector3d(a_BlockX, a_BlockY, a_BlockZ), 1.0F, 1.04F);
a_World->SetBlock(FirePos, E_BLOCK_FIRE, 0);
a_World->BroadcastSoundEffect("item.flintandsteel.use", FirePos, 1.0f, 1.04f);
break;
}
}

View File

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

View File

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

View File

@ -22,52 +22,60 @@ public:
}
virtual bool OnPlayerPlace(
cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
int a_CursorX, int a_CursorY, int a_CursorZ
cWorld & a_World,
cPlayer & a_Player,
const cItem & a_EquippedItem,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos
) override
{
// 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;
}
auto placedX = a_BlockX, placedY = a_BlockY, placedZ = a_BlockZ;
AddFaceDirection(placedY, placedY, placedZ, a_BlockFace);
const auto PlacePos = AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace);
// If the placed head is a wither, try to spawn the wither first:
if (a_EquippedItem.m_ItemDamage == E_META_HEAD_WITHER)
{
if (TrySpawnWitherAround(a_World, a_Player, {placedX, placedY, placedZ}))
if (TrySpawnWitherAround(a_World, a_Player, PlacePos))
{
return true;
}
// 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
if (!Super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ))
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_ClickedBlockPos, a_ClickedBlockFace, a_CursorPos))
{
return false;
}
RegularHeadPlaced(a_World, a_Player, itemCopy, placedX, placedY, placedZ, a_BlockFace);
RegularHeadPlaced(a_World, a_Player, ItemCopy, PlacePos, a_ClickedBlockFace);
return true;
}
/** Called after placing a regular head block with no mob spawning.
Adjusts the mob head entity based on the equipped item's data. */
void RegularHeadPlaced(
cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace
const Vector3i a_PlacePos, eBlockFace a_ClickedBlockFace
)
{
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:
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)
{
@ -90,11 +98,14 @@ public:
}
/** Spawns a wither if the wither skull placed at the specified coords completes wither's spawning formula.
Returns true if the wither was created. */
bool TrySpawnWitherAround(
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:
@ -183,6 +194,9 @@ public:
}
/** 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,
but assumed to be a head instead.
@ -265,6 +279,9 @@ public:
}
/** Awards the achievement to all players close to the specified point. */
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)
into the block's metadata value. */
static NIBBLETYPE BlockFaceToBlockMeta(int a_BlockFace)
@ -303,21 +323,28 @@ public:
}
virtual bool IsPlaceable(void) override
{
return true;
}
virtual bool GetPlacementBlockTypeMeta(
cWorld * a_World, cPlayer * a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
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
) override
{
a_BlockType = E_BLOCK_HEAD;
a_BlockMeta = BlockFaceToBlockMeta(a_BlockFace);
a_BlockMeta = BlockFaceToBlockMeta(a_ClickedBlockFace);
return true;
}
} ;

View File

@ -8,47 +8,53 @@
class cItemNetherWartHandler :
class cItemNetherWartHandler:
public cItemHandler
{
public:
cItemNetherWartHandler(int a_ItemType) :
cItemHandler(a_ItemType)
{
using Super = cItemHandler;
public:
cItemNetherWartHandler(int a_ItemType):
Super(a_ItemType)
{
}
virtual bool IsPlaceable(void) override
{
return true;
}
virtual bool GetPlacementBlockTypeMeta(
cWorld * a_World, cPlayer * a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
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
) 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;
}
// Only allow placement on farmland
int X = a_BlockX;
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)
// Only allow placement on soulsand
if ((a_PlacedBlockPos.y < 1) || (a_World->GetBlock(a_PlacedBlockPos.addedY(-1)) != E_BLOCK_SOULSAND))
{
return false;
}
a_BlockMeta = 0;
a_BlockType = E_BLOCK_NETHER_WART;
return true;
}
} ;

View File

@ -10,82 +10,93 @@
class cItemPaintingHandler :
class cItemPaintingHandler:
public cItemHandler
{
using Super = cItemHandler;
public:
cItemPaintingHandler(int a_ItemType)
: cItemHandler(a_ItemType)
cItemPaintingHandler(int a_ItemType):
Super(a_ItemType)
{
}
virtual bool OnItemUse(
cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace
cWorld * a_World,
cPlayer * a_Player,
cBlockPluginInterface & a_PluginInterface,
const cItem & a_HeldItem,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace
) 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;
}
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace); // Make sure block that will be occupied is free
BLOCKTYPE Block = a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
if (Block == E_BLOCK_AIR)
// Make sure block that will be occupied is free:
auto PlacePos = AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace);
BLOCKTYPE PlaceBlockType = a_World->GetBlock(PlacePos);
if (PlaceBlockType != E_BLOCK_AIR)
{
static const struct // Define all the possible painting titles
{
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;
}
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:
virtual bool IsDrinkable(short a_ItemDamage) override
{
@ -26,12 +29,19 @@ public:
}
virtual bool OnItemUse(
cWorld * a_World, cPlayer * a_Player, cBlockPluginInterface & a_PluginInterface, const cItem & a_Item,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace
cWorld * a_World,
cPlayer * a_Player,
cBlockPluginInterface & a_PluginInterface,
const cItem & a_HeldItem,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace
) override
{
short PotionDamage = a_Item.m_ItemDamage;
short PotionDamage = a_HeldItem.m_ItemDamage;
// Do not throw non-splash potions:
if (cEntityEffect::IsPotionDrinkable(PotionDamage))
@ -59,6 +69,9 @@ public:
}
virtual bool EatItem(cPlayer * a_Player, cItem * a_Item) override
{
short PotionDamage = a_Item->m_ItemDamage;

View File

@ -20,44 +20,50 @@ public:
}
virtual bool OnPlayerPlace(
cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
int a_CursorX, int a_CursorY, int a_CursorZ
cWorld & a_World,
cPlayer & a_Player,
const cItem & a_EquippedItem,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos
) override
{
// First try spawning a snow golem or an iron golem:
int PlacedBlockX = a_BlockX;
int PlacedBlockY = a_BlockY;
int PlacedBlockZ = a_BlockZ;
AddFaceDirection(PlacedBlockX, PlacedBlockY, PlacedBlockZ, a_BlockFace);
if (TrySpawnGolem(a_World, a_Player, PlacedBlockX, PlacedBlockY, PlacedBlockZ))
auto PlacePos = AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace);
if (TrySpawnGolem(a_World, a_Player, PlacePos))
{
// 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;
}
// 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.
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
if ((a_BlockY < 2) || (a_BlockY >= cChunkDef::Height))
if ((a_PumpkinPos.y < 2) || (a_PumpkinPos.y >= cChunkDef::Height))
{
return false;
}
// Decide which golem to try spawning based on the block below the placed pumpkin:
switch (a_World.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ))
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_IRON_BLOCK: return TrySpawnIronGolem(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_PumpkinPos);
default:
{
// 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.
Returns true if the golem blocks are removed (for spawning), false if the recipe is not matched.
Assumes that the block below the specified block has already been checked and is a snow block. */
bool TrySpawnSnowGolem(cWorld & a_World, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ)
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:
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;
}
// Try to place air blocks where the original recipe blocks were:
sSetBlockVector AirBlocks;
AirBlocks.emplace_back(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); // Head
AirBlocks.emplace_back(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); // Torso
AirBlocks.emplace_back(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); // Legs
AirBlocks.emplace_back(a_PumpkinPos, E_BLOCK_AIR, 0); // Head
AirBlocks.emplace_back(a_PumpkinPos.addedY(-1), E_BLOCK_AIR, 0); // Torso
AirBlocks.emplace_back(a_PumpkinPos.addedY(-2), E_BLOCK_AIR, 0); // Legs
if (!a_Player.PlaceBlocks(AirBlocks))
{
return false;
}
// 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;
}
/** Spawns an iron golem if the shape matches the recipe, supposing that the block placed at the specified coords is a pumpkin.
Returns true if the golem blocks are removed (for spawning), false if the recipe is not matched.
Assumes that the block below the specified block has already been checked and is an iron block. */
bool TrySpawnIronGolem(cWorld & a_World, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ)
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:
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;
}
// 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[] =
{
{1, 0, 0},
@ -115,8 +135,8 @@ public:
{
// If the arm blocks don't match, bail out of this loop repetition:
if (
(a_World.GetBlock(a_BlockX + ArmOffsets[i].x, a_BlockY - 1, a_BlockZ + ArmOffsets[i].z) != E_BLOCK_IRON_BLOCK) ||
(a_World.GetBlock(a_BlockX - ArmOffsets[i].x, a_BlockY - 1, a_BlockZ - ArmOffsets[i].z) != E_BLOCK_IRON_BLOCK)
(a_World.GetBlock(BodyPos + ArmOffsets[i]) != E_BLOCK_IRON_BLOCK) ||
(a_World.GetBlock(BodyPos - ArmOffsets[i]) != E_BLOCK_IRON_BLOCK)
)
{
continue;
@ -124,18 +144,19 @@ public:
// Try to place air blocks where the original recipe blocks were:
sSetBlockVector AirBlocks;
AirBlocks.emplace_back(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); // Head
AirBlocks.emplace_back(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); // Torso
AirBlocks.emplace_back(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); // Legs
AirBlocks.emplace_back(a_BlockX + ArmOffsets[i].x, a_BlockY - 1, a_BlockZ + ArmOffsets[i].z, E_BLOCK_AIR, 0); // Arm
AirBlocks.emplace_back(a_BlockX - ArmOffsets[i].x, a_BlockY - 1, a_BlockZ - ArmOffsets[i].z, E_BLOCK_AIR, 0); // Arm
AirBlocks.emplace_back(a_PumpkinPos, E_BLOCK_AIR, 0); // Head
AirBlocks.emplace_back(BodyPos, E_BLOCK_AIR, 0); // Torso
AirBlocks.emplace_back(BodyPos.addedY(-1), E_BLOCK_AIR, 0); // Legs
AirBlocks.emplace_back(BodyPos + ArmOffsets[i], E_BLOCK_AIR, 0); // Arm
AirBlocks.emplace_back(BodyPos - ArmOffsets[i], E_BLOCK_AIR, 0); // Arm
if (!a_Player.PlaceBlocks(AirBlocks))
{
return false;
}
// 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;
} // for i - ArmOffsets[]

View File

@ -7,36 +7,48 @@
class cItemRedstoneDustHandler : public cItemHandler
class cItemRedstoneDustHandler:
public cItemHandler
{
using Super = cItemHandler;
public:
cItemRedstoneDustHandler(int a_ItemType)
: cItemHandler(a_ItemType)
cItemRedstoneDustHandler(int a_ItemType):
Super(a_ItemType)
{
}
virtual bool IsPlaceable(void) override
{
return true;
}
virtual bool GetPlacementBlockTypeMeta(
cWorld * a_World, cPlayer * a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
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
) override
{
// Check if coords are out of range:
if ((a_BlockY <= 0) || (a_BlockY >= cChunkDef::Height))
// Check the block below, if it supports dust on top of it:
auto UnderPos = a_PlacedBlockPos.addedY(-1);
if (UnderPos.y < 0)
{
return false;
}
// Check the block below, if it supports dust on top of it:
BLOCKTYPE BlockType;
NIBBLETYPE BlockMeta;
if (!a_World->GetBlockTypeMeta(a_BlockX, a_BlockY - 1, a_BlockZ, BlockType, BlockMeta))
if (!a_World->GetBlockTypeMeta(UnderPos, BlockType, BlockMeta))
{
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. */
static bool IsBlockTypeUnderSuitable(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
{

View File

@ -8,24 +8,36 @@
class cItemRedstoneRepeaterHandler :
class cItemRedstoneRepeaterHandler:
public cItemHandler
{
using Super = cItemHandler;
public:
cItemRedstoneRepeaterHandler(int a_ItemType)
: cItemHandler(a_ItemType)
cItemRedstoneRepeaterHandler(int a_ItemType):
Super(a_ItemType)
{
}
virtual bool IsPlaceable() override
{
return true;
}
virtual bool GetPlacementBlockTypeMeta(
cWorld * a_World, cPlayer * a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
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
) override
{

View File

@ -17,24 +17,29 @@ public:
cItemSaplingHandler(int a_ItemType):
Super(a_ItemType)
{
}
virtual bool GetPlacementBlockTypeMeta(
cWorld * a_World, cPlayer * a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
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
) override
{
bool res = Super::GetPlacementBlockTypeMeta(
a_World, a_Player,
a_BlockX, a_BlockY, a_BlockZ, a_BlockFace,
a_CursorX, a_CursorY, a_CursorZ,
a_PlacedBlockPos, a_ClickedBlockFace,
a_CursorPos,
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;
}
} ;

View File

@ -8,44 +8,53 @@
class cItemSeedsHandler :
class cItemSeedsHandler:
public cItemHandler
{
using Super = cItemHandler;
public:
cItemSeedsHandler(int a_ItemType) :
cItemHandler(a_ItemType)
cItemSeedsHandler(int a_ItemType):
Super(a_ItemType)
{
}
virtual bool IsPlaceable(void) override
{
return true;
}
virtual bool GetPlacementBlockTypeMeta(
cWorld * a_World, cPlayer * a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
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
) 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;
}
// Only allow placement on farmland
int X = a_BlockX;
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)
if (a_World->GetBlock(a_PlacedBlockPos.addedY(-1)) != E_BLOCK_FARMLAND)
{
return false;
}
// Get the produce block based on the seed item:
a_BlockMeta = 0;
switch (m_ItemType)
{

View File

@ -22,25 +22,33 @@ public:
}
virtual bool IsTool(void) override
{
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;
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))
{
cItems Drops;
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);
a_World->DropBlockAsPickups(a_ClickedBlockPos, a_Player, &a_HeldItem);
return true;
}
@ -48,6 +56,9 @@ public:
}
virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType) override
{
switch (a_BlockType)
@ -63,6 +74,9 @@ public:
}
virtual short GetDurabilityLossByAction(eDurabilityLostAction a_Action) override
{
switch (a_Action)
@ -93,5 +107,4 @@ public:
return Super::GetBlockBreakingStrength(a_Block);
}
}
} ;

View File

@ -24,58 +24,68 @@ public:
}
virtual bool OnPlayerPlace(
cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
int a_CursorX, int a_CursorY, int a_CursorZ
cWorld & a_World,
cPlayer & a_Player,
const cItem & a_EquippedItem,
const Vector3i a_ClickedBlockPos,
eBlockFace a_ClickedBlockFace,
const Vector3i a_CursorPos
) override
{
// Check if placing on something ignoring build collision to edit the correct sign later on:
BLOCKTYPE ClickedBlock;
BLOCKTYPE ClickedBlockType;
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());
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 (!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;
}
// Use isReplacingClickedBlock to make sure we will edit the right sign:
if (!isReplacingClickedBlock)
{
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
}
// Use IsReplacingClickedBlock to make sure we will edit the right sign:
auto SignPos = IsReplacingClickedBlock ? a_ClickedBlockPos : AddFaceDirection(a_ClickedBlockPos, a_ClickedBlockFace);
// 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;
}
virtual bool IsPlaceable(void) override
{
return true;
}
virtual bool GetPlacementBlockTypeMeta(
cWorld * a_World, cPlayer * a_Player,
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
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
) override
{
if (a_BlockFace == BLOCK_FACE_TOP)
if (a_ClickedBlockFace == BLOCK_FACE_TOP)
{
a_BlockMeta = cBlockSignPostHandler::RotationToMetaData(a_Player->GetYaw());
a_BlockType = E_BLOCK_SIGN_POST;
}
else
{
a_BlockMeta = cBlockWallSignHandler::DirectionToMetaData(a_BlockFace);
a_BlockMeta = cBlockWallSignHandler::BlockFaceToMetaData(a_ClickedBlockFace);
a_BlockType = E_BLOCK_WALLSIGN;
}
return true;

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