Merge pull request #1686 from mc-server/PlaceBlockRefactor
Place block refactor
This commit is contained in:
commit
1d593134da
@ -1840,7 +1840,9 @@ a_Player:OpenWindow(Window);
|
||||
MoveToWorld = { Params = "WorldName", Return = "bool", Return = "Moves the player to the specified world. Returns true if successful." },
|
||||
OpenWindow = { Params = "{{cWindow|Window}}", Return = "", Notes = "Opens the specified UI window for the player." },
|
||||
PermissionMatches = { Params = "Permission, Template", Return = "bool", Notes = "(STATIC) Returns true if the specified permission matches the specified template. The template may contain wildcards." },
|
||||
PlaceBlock = { Params = "BlockX, BlockY, BlockZ, BlockType, BlockMeta", Return = "bool", Notes = "Places a block while impersonating the player. The {{OnPlayerPlacingBlock|HOOK_PLAYER_PLACING_BLOCK}} hook is called before the placement, and if it succeeds, the block is placed and the {{OnPlayerPlacedBlock|HOOK_PLAYER_PLACED_BLOCK}} hook is called. Returns true iff the block is successfully placed. Assumes that the block is in a currently loaded chunk." },
|
||||
Respawn = { Params = "", Return = "", Notes = "Restores the health, extinguishes fire, makes visible and sends the Respawn packet." },
|
||||
SendBlocksAround = { Params = "BlockX, BlockY, BlockZ, [Range]", Return = "", Notes = "Sends all the world's blocks in Range from the specified coords to the player, as a BlockChange packet. Range defaults to 1 (only one block sent)." },
|
||||
SendMessage = { Params = "Message", Return = "", Notes = "Sends the specified message to the player." },
|
||||
SendMessageFailure = { Params = "Message", Return = "", Notes = "Prepends Rose [INFO] / colours entire text (depending on ShouldUseChatPrefixes()) and sends message to player. For a command that failed to run because of insufficient permissions, etc." },
|
||||
SendMessageFatal = { Params = "Message", Return = "", Notes = "Prepends Red [FATAL] / colours entire text (depending on ShouldUseChatPrefixes()) and sends message to player. For something serious, such as a plugin crash, etc." },
|
||||
|
@ -12,7 +12,11 @@ return
|
||||
Use the {{cPlayer}}:GetWorld() function to get the world to which the block belongs.</p>
|
||||
<p>
|
||||
See also the {{OnPlayerPlacingBlock|HOOK_PLAYER_PLACING_BLOCK}} hook for a similar hook called
|
||||
before the placement.
|
||||
before the placement.</p>
|
||||
<p>
|
||||
If the client action results in multiple blocks being placed (such as a bed or a door), each separate
|
||||
block is reported through this hook. All the blocks are already present in the world before the first
|
||||
instance of this hook is called.
|
||||
]],
|
||||
Params =
|
||||
{
|
||||
@ -20,10 +24,6 @@ return
|
||||
{ Name = "BlockX", Type = "number", Notes = "X-coord of the block" },
|
||||
{ Name = "BlockY", Type = "number", Notes = "Y-coord of the block" },
|
||||
{ Name = "BlockZ", Type = "number", Notes = "Z-coord of the block" },
|
||||
{ Name = "BlockFace", Type = "number", Notes = "Face of the existing block upon which the player interacted. One of the BLOCK_FACE_ constants" },
|
||||
{ Name = "CursorX", Type = "number", Notes = "X-coord of the cursor within the block face (0 .. 15)" },
|
||||
{ Name = "CursorY", Type = "number", Notes = "Y-coord of the cursor within the block face (0 .. 15)" },
|
||||
{ Name = "CursorZ", Type = "number", Notes = "Z-coord of the cursor within the block face (0 .. 15)" },
|
||||
{ Name = "BlockType", Type = "BLOCKTYPE", Notes = "The block type of the block" },
|
||||
{ Name = "BlockMeta", Type = "NIBBLETYPE", Notes = "The block meta of the block" },
|
||||
},
|
||||
|
@ -15,7 +15,11 @@ return
|
||||
Use the {{cPlayer}}:GetWorld() function to get the world to which the block belongs.</p>
|
||||
<p>
|
||||
See also the {{OnPlayerPlacedBlock|HOOK_PLAYER_PLACED_BLOCK}} hook for a similar hook called after
|
||||
the placement.
|
||||
the placement.</p>
|
||||
<p>
|
||||
If the client action results in multiple blocks being placed (such as a bed or a door), each separate
|
||||
block is reported through this hook and only if all of them succeed, all the blocks are placed. If
|
||||
any one of the calls are refused by the plugin, all the blocks are refused and reverted on the client.
|
||||
]],
|
||||
Params =
|
||||
{
|
||||
@ -23,10 +27,6 @@ return
|
||||
{ Name = "BlockX", Type = "number", Notes = "X-coord of the block" },
|
||||
{ Name = "BlockY", Type = "number", Notes = "Y-coord of the block" },
|
||||
{ Name = "BlockZ", Type = "number", Notes = "Z-coord of the block" },
|
||||
{ Name = "BlockFace", Type = "number", Notes = "Face of the existing block upon which the player is interacting. One of the BLOCK_FACE_ constants" },
|
||||
{ Name = "CursorX", Type = "number", Notes = "X-coord of the cursor within the block face (0 .. 15)" },
|
||||
{ Name = "CursorY", Type = "number", Notes = "Y-coord of the cursor within the block face (0 .. 15)" },
|
||||
{ Name = "CursorZ", Type = "number", Notes = "Z-coord of the cursor within the block face (0 .. 15)" },
|
||||
{ Name = "BlockType", Type = "BLOCKTYPE", Notes = "The block type of the block" },
|
||||
{ Name = "BlockMeta", Type = "NIBBLETYPE", Notes = "The block meta of the block" },
|
||||
},
|
||||
|
@ -76,8 +76,8 @@ public:
|
||||
virtual bool OnPlayerJoined (cPlayer & a_Player) = 0;
|
||||
virtual bool OnPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status) = 0;
|
||||
virtual bool OnPlayerMoving (cPlayer & a_Player, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) = 0;
|
||||
virtual bool OnPlayerPlacedBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0;
|
||||
virtual bool OnPlayerPlacingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) = 0;
|
||||
virtual bool OnPlayerPlacedBlock (cPlayer & a_Player, const sSetBlock & a_BlockChange) = 0;
|
||||
virtual bool OnPlayerPlacingBlock (cPlayer & a_Player, const sSetBlock & a_BlockChange) = 0;
|
||||
virtual bool OnPlayerRightClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) = 0;
|
||||
virtual bool OnPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity) = 0;
|
||||
virtual bool OnPlayerShooting (cPlayer & a_Player) = 0;
|
||||
|
@ -857,14 +857,19 @@ bool cPluginLua::OnPlayerMoving(cPlayer & a_Player, const Vector3d & a_OldPositi
|
||||
|
||||
|
||||
|
||||
bool cPluginLua::OnPlayerPlacedBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
|
||||
bool cPluginLua::OnPlayerPlacedBlock(cPlayer & a_Player, const sSetBlock & a_BlockChange)
|
||||
{
|
||||
cCSLock Lock(m_CriticalSection);
|
||||
bool res = false;
|
||||
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_PLACED_BLOCK];
|
||||
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
|
||||
{
|
||||
m_LuaState.Call((int)(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta, cLuaState::Return, res);
|
||||
m_LuaState.Call((int)(**itr), &a_Player,
|
||||
a_BlockChange.GetX(), a_BlockChange.GetY(), a_BlockChange.GetZ(),
|
||||
a_BlockChange.m_BlockType, a_BlockChange.m_BlockMeta,
|
||||
cLuaState::Return,
|
||||
res
|
||||
);
|
||||
if (res)
|
||||
{
|
||||
return true;
|
||||
@ -877,14 +882,19 @@ bool cPluginLua::OnPlayerPlacedBlock(cPlayer & a_Player, int a_BlockX, int a_Blo
|
||||
|
||||
|
||||
|
||||
bool cPluginLua::OnPlayerPlacingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
|
||||
bool cPluginLua::OnPlayerPlacingBlock(cPlayer & a_Player, const sSetBlock & a_BlockChange)
|
||||
{
|
||||
cCSLock Lock(m_CriticalSection);
|
||||
bool res = false;
|
||||
cLuaRefs & Refs = m_HookMap[cPluginManager::HOOK_PLAYER_PLACING_BLOCK];
|
||||
for (cLuaRefs::iterator itr = Refs.begin(), end = Refs.end(); itr != end; ++itr)
|
||||
{
|
||||
m_LuaState.Call((int)(**itr), &a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta, cLuaState::Return, res);
|
||||
m_LuaState.Call((int)(**itr), &a_Player,
|
||||
a_BlockChange.GetX(), a_BlockChange.GetY(), a_BlockChange.GetZ(),
|
||||
a_BlockChange.m_BlockType, a_BlockChange.m_BlockMeta,
|
||||
cLuaState::Return,
|
||||
res
|
||||
);
|
||||
if (res)
|
||||
{
|
||||
return true;
|
||||
|
@ -100,8 +100,8 @@ public:
|
||||
virtual bool OnPlayerJoined (cPlayer & a_Player) override;
|
||||
virtual bool OnPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status) override;
|
||||
virtual bool OnPlayerMoving (cPlayer & a_Player, const Vector3d & a_OldPosition, const Vector3d & a_NewPosition) override;
|
||||
virtual bool OnPlayerPlacedBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
|
||||
virtual bool OnPlayerPlacingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
|
||||
virtual bool OnPlayerPlacedBlock (cPlayer & a_Player, const sSetBlock & a_BlockChange) override;
|
||||
virtual bool OnPlayerPlacingBlock (cPlayer & a_Player, const sSetBlock & a_BlockChange) override;
|
||||
virtual bool OnPlayerRightClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ) override;
|
||||
virtual bool OnPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity) override;
|
||||
virtual bool OnPlayerShooting (cPlayer & a_Player) override;
|
||||
|
@ -866,14 +866,14 @@ bool cPluginManager::CallHookPlayerMoving(cPlayer & a_Player, const Vector3d a_O
|
||||
|
||||
|
||||
|
||||
bool cPluginManager::CallHookPlayerPlacedBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
|
||||
bool cPluginManager::CallHookPlayerPlacedBlock(cPlayer & a_Player, const sSetBlock & a_BlockChange)
|
||||
{
|
||||
FIND_HOOK(HOOK_PLAYER_PLACED_BLOCK);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnPlayerPlacedBlock(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta))
|
||||
if ((*itr)->OnPlayerPlacedBlock(a_Player, a_BlockChange))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -885,14 +885,14 @@ bool cPluginManager::CallHookPlayerPlacedBlock(cPlayer & a_Player, int a_BlockX,
|
||||
|
||||
|
||||
|
||||
bool cPluginManager::CallHookPlayerPlacingBlock(cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
|
||||
bool cPluginManager::CallHookPlayerPlacingBlock(cPlayer & a_Player, const sSetBlock & a_BlockChange)
|
||||
{
|
||||
FIND_HOOK(HOOK_PLAYER_PLACING_BLOCK);
|
||||
VERIFY_HOOK;
|
||||
|
||||
for (PluginList::iterator itr = Plugins->second.begin(); itr != Plugins->second.end(); ++itr)
|
||||
{
|
||||
if ((*itr)->OnPlayerPlacingBlock(a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, a_BlockType, a_BlockMeta))
|
||||
if ((*itr)->OnPlayerPlacingBlock(a_Player, a_BlockChange))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
@ -207,10 +207,10 @@ public:
|
||||
bool CallHookPlayerFishing (cPlayer & a_Player, cItems a_Reward);
|
||||
bool CallHookPlayerFoodLevelChange (cPlayer & a_Player, int a_NewFoodLevel);
|
||||
bool CallHookPlayerJoined (cPlayer & a_Player);
|
||||
bool CallHookPlayerMoving (cPlayer & a_Player, const Vector3d a_OldPosition, const Vector3d a_NewPosition);
|
||||
bool CallHookPlayerLeftClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status);
|
||||
bool CallHookPlayerPlacedBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
|
||||
bool CallHookPlayerPlacingBlock (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
|
||||
bool CallHookPlayerMoving (cPlayer & a_Player, const Vector3d a_OldPosition, const Vector3d a_NewPosition);
|
||||
bool CallHookPlayerPlacedBlock (cPlayer & a_Player, const sSetBlock & a_BlockChange);
|
||||
bool CallHookPlayerPlacingBlock (cPlayer & a_Player, const sSetBlock & a_BlockChange);
|
||||
bool CallHookPlayerRightClick (cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ);
|
||||
bool CallHookPlayerRightClickingEntity(cPlayer & a_Player, cEntity & a_Entity);
|
||||
bool CallHookPlayerShooting (cPlayer & a_Player);
|
||||
|
@ -14,24 +14,6 @@
|
||||
|
||||
|
||||
|
||||
void cBlockBedHandler::OnPlacedByPlayer(
|
||||
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,
|
||||
BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta
|
||||
)
|
||||
{
|
||||
if (a_BlockMeta < 8)
|
||||
{
|
||||
Vector3i Direction = MetaDataToDirection(a_BlockMeta);
|
||||
a_ChunkInterface.SetBlock(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z, E_BLOCK_BED, a_BlockMeta | 0x8);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cBlockBedHandler::OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ)
|
||||
{
|
||||
NIBBLETYPE OldMeta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
|
||||
@ -151,7 +133,7 @@ void cBlockBedHandler::OnUse(cChunkInterface & a_ChunkInterface, cWorldInterface
|
||||
cPlayerBedStateUnsetter Unsetter(Vector3i(a_BlockX + PillowDirection.x, a_BlockY, a_BlockZ + PillowDirection.z), a_WorldInterface);
|
||||
a_WorldInterface.ForEachPlayer(Unsetter);
|
||||
a_WorldInterface.SetTimeOfDay(0);
|
||||
a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta & 0xB); // Where 0xB = 1011, and zero is to make sure 'occupied' bit is always unset
|
||||
a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta & 0x0b); // Clear the "occupied" bit of the bed's block
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,6 @@ public:
|
||||
}
|
||||
|
||||
|
||||
virtual void OnPlacedByPlayer(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, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override;
|
||||
virtual void OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ) override;
|
||||
virtual void 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;
|
||||
|
||||
|
@ -85,18 +85,6 @@ public:
|
||||
}
|
||||
|
||||
|
||||
virtual void OnPlacedByPlayer(
|
||||
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,
|
||||
BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta
|
||||
) override
|
||||
{
|
||||
int Meta = (((int)floor(a_Player->GetYaw() * 4.0 / 360.0 + 0.5) & 0x3) + 2) % 4;
|
||||
a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY + 1, a_BlockZ, m_BlockType, 0x8 | Meta);
|
||||
}
|
||||
|
||||
|
||||
virtual void OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ) override
|
||||
{
|
||||
NIBBLETYPE OldMeta = a_ChunkInterface.GetBlockMeta(a_BlockX, a_BlockY, a_BlockZ);
|
||||
|
@ -62,50 +62,11 @@ public:
|
||||
}
|
||||
|
||||
// Single chest, get meta from rotation only
|
||||
a_BlockMeta = RotationToMetaData(yaw);
|
||||
a_BlockMeta = PlayerYawToMetaData(yaw);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
virtual void OnPlacedByPlayer(
|
||||
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,
|
||||
BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta
|
||||
) override
|
||||
{
|
||||
// Check if this forms a doublechest, if so, need to adjust the meta:
|
||||
cBlockArea Area;
|
||||
if (!Area.Read(&a_ChunkInterface, a_BlockX - 1, a_BlockX + 1, a_BlockY, a_BlockY, a_BlockZ - 1, a_BlockZ + 1))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
double rot = a_Player->GetYaw(); // FIXME: Rename rot to yaw
|
||||
// Choose meta from player rotation, choose only between 2 or 3
|
||||
NIBBLETYPE NewMeta = ((rot >= -90) && (rot < 90)) ? 2 : 3;
|
||||
if (
|
||||
CheckAndAdjustNeighbor(a_ChunkInterface, Area, 0, 1, NewMeta) ||
|
||||
CheckAndAdjustNeighbor(a_ChunkInterface, Area, 2, 1, NewMeta)
|
||||
)
|
||||
{
|
||||
// Forming a double chest in the X direction
|
||||
return;
|
||||
}
|
||||
// Choose meta from player rotation, choose only between 4 or 5
|
||||
NewMeta = (rot < 0) ? 4 : 5;
|
||||
if (
|
||||
CheckAndAdjustNeighbor(a_ChunkInterface, Area, 1, 0, NewMeta) ||
|
||||
CheckAndAdjustNeighbor(a_ChunkInterface, Area, 2, 2, NewMeta)
|
||||
)
|
||||
{
|
||||
// Forming a double chest in the Z direction
|
||||
return;
|
||||
}
|
||||
|
||||
// Single chest, no further processing needed
|
||||
}
|
||||
|
||||
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
|
||||
{
|
||||
int BlockX = a_RelX + a_Chunk.GetPosX() * cChunkDef::Width;
|
||||
@ -180,30 +141,30 @@ public:
|
||||
}
|
||||
|
||||
|
||||
/// Translates player rotation when placing a chest into the chest block metadata. Valid for single chests only
|
||||
static NIBBLETYPE RotationToMetaData(double a_Rotation)
|
||||
/** Translates player yaw when placing a chest into the chest block metadata. Valid for single chests only */
|
||||
static NIBBLETYPE PlayerYawToMetaData(double a_Yaw)
|
||||
{
|
||||
a_Rotation += 90 + 45; // So its not aligned with axis
|
||||
a_Yaw += 90 + 45; // So its not aligned with axis
|
||||
|
||||
if (a_Rotation > 360.f)
|
||||
if (a_Yaw > 360.f)
|
||||
{
|
||||
a_Rotation -= 360.f;
|
||||
a_Yaw -= 360.f;
|
||||
}
|
||||
if ((a_Rotation >= 0.f) && (a_Rotation < 90.f))
|
||||
if ((a_Yaw >= 0.f) && (a_Yaw < 90.f))
|
||||
{
|
||||
return 0x4;
|
||||
return 0x04;
|
||||
}
|
||||
else if ((a_Rotation >= 180) && (a_Rotation < 270))
|
||||
else if ((a_Yaw >= 180) && (a_Yaw < 270))
|
||||
{
|
||||
return 0x5;
|
||||
return 0x05;
|
||||
}
|
||||
else if ((a_Rotation >= 90) && (a_Rotation < 180))
|
||||
else if ((a_Yaw >= 90) && (a_Yaw < 180))
|
||||
{
|
||||
return 0x2;
|
||||
return 0x02;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0x3;
|
||||
return 0x03;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,7 @@ void cBlockDoorHandler::OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldIn
|
||||
if (OldMeta & 8)
|
||||
{
|
||||
// Was upper part of door
|
||||
if (IsDoor(a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ)))
|
||||
if (IsDoorBlockType(a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ)))
|
||||
{
|
||||
a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0);
|
||||
}
|
||||
@ -31,7 +31,7 @@ void cBlockDoorHandler::OnDestroyed(cChunkInterface & a_ChunkInterface, cWorldIn
|
||||
else
|
||||
{
|
||||
// Was lower part
|
||||
if (IsDoor(a_ChunkInterface.GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ)))
|
||||
if (IsDoorBlockType(a_ChunkInterface.GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ)))
|
||||
{
|
||||
a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY + 1, a_BlockZ, E_BLOCK_AIR, 0);
|
||||
}
|
||||
@ -84,52 +84,34 @@ void cBlockDoorHandler::OnCancelRightClick(cChunkInterface & a_ChunkInterface, c
|
||||
|
||||
|
||||
|
||||
void cBlockDoorHandler::OnPlacedByPlayer(
|
||||
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,
|
||||
BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta
|
||||
)
|
||||
{
|
||||
NIBBLETYPE a_TopBlockMeta = 8;
|
||||
if (
|
||||
((a_BlockMeta == 0) && (a_ChunkInterface.GetBlock(a_BlockX, a_BlockY, a_BlockZ - 1) == m_BlockType)) ||
|
||||
((a_BlockMeta == 1) && (a_ChunkInterface.GetBlock(a_BlockX + 1, a_BlockY, a_BlockZ) == m_BlockType)) ||
|
||||
((a_BlockMeta == 2) && (a_ChunkInterface.GetBlock(a_BlockX, a_BlockY, a_BlockZ + 1) == m_BlockType)) ||
|
||||
((a_BlockMeta == 3) && (a_ChunkInterface.GetBlock(a_BlockX - 1, a_BlockY, a_BlockZ) == m_BlockType))
|
||||
)
|
||||
{
|
||||
a_TopBlockMeta = 9;
|
||||
}
|
||||
a_ChunkInterface.SetBlock(a_BlockX, a_BlockY + 1, a_BlockZ, m_BlockType, a_TopBlockMeta);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
NIBBLETYPE cBlockDoorHandler::MetaRotateCCW(NIBBLETYPE a_Meta)
|
||||
{
|
||||
if (a_Meta & 0x08)
|
||||
{
|
||||
// The meta doesn't change for the top block
|
||||
return a_Meta;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Rotate the bottom block
|
||||
return super::MetaRotateCCW(a_Meta);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
NIBBLETYPE cBlockDoorHandler::MetaRotateCW(NIBBLETYPE a_Meta)
|
||||
{
|
||||
if (a_Meta & 0x08)
|
||||
{
|
||||
// The meta doesn't change for the top block
|
||||
return a_Meta;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Rotate the bottom block
|
||||
return super::MetaRotateCW(a_Meta);
|
||||
}
|
||||
}
|
||||
@ -138,8 +120,10 @@ NIBBLETYPE cBlockDoorHandler::MetaRotateCW(NIBBLETYPE a_Meta)
|
||||
|
||||
NIBBLETYPE cBlockDoorHandler::MetaMirrorXY(NIBBLETYPE a_Meta)
|
||||
{
|
||||
// Top bit (0x08) contains door panel type (Top/Bottom panel) Only Bottom panels contain position data
|
||||
// Return a_Meta if panel is a top panel (0x08 bit is set to 1)
|
||||
/*
|
||||
Top bit (0x08) contains door block position (Top / Bottom). Only Bottom blocks contain position data
|
||||
Return a_Meta if panel is a top panel (0x08 bit is set to 1)
|
||||
*/
|
||||
|
||||
// Note: Currently, you can not properly mirror the hinges on a double door. The orientation of the door is stored
|
||||
// in only the bottom tile while the hinge position is in the top tile. This function only operates on one tile at a time,
|
||||
|
@ -101,14 +101,6 @@ public:
|
||||
}
|
||||
|
||||
|
||||
virtual void OnPlacedByPlayer(
|
||||
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,
|
||||
BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta
|
||||
) override;
|
||||
|
||||
|
||||
virtual bool IsUseable(void) override
|
||||
{
|
||||
return true;
|
||||
@ -117,11 +109,19 @@ public:
|
||||
|
||||
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
|
||||
{
|
||||
return ((a_RelY > 0) && (a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ) != E_BLOCK_AIR));
|
||||
return ((a_RelY > 0) && CanBeOn(a_Chunk.GetBlock(a_RelX, a_RelY - 1, a_RelZ)));
|
||||
}
|
||||
|
||||
|
||||
bool CanReplaceBlock(BLOCKTYPE a_BlockType)
|
||||
/** Returns true if door can be placed on the specified block type. */
|
||||
static bool CanBeOn(BLOCKTYPE a_BlockType)
|
||||
{
|
||||
// Vanilla refuses to place doors on transparent blocks
|
||||
return !cBlockInfo::IsTransparent(a_BlockType);
|
||||
}
|
||||
|
||||
|
||||
static bool CanReplaceBlock(BLOCKTYPE a_BlockType)
|
||||
{
|
||||
switch (a_BlockType)
|
||||
{
|
||||
@ -170,8 +170,21 @@ public:
|
||||
}
|
||||
|
||||
|
||||
/** Returns a vector pointing one block in the direction the door is facing (where the outside is). */
|
||||
inline static Vector3i GetRelativeDirectionToOutside(NIBBLETYPE a_BlockMeta)
|
||||
{
|
||||
switch (a_BlockMeta & 0x03)
|
||||
{
|
||||
case 0: return Vector3i(-1, 0, 0); // Facing West / XM
|
||||
case 1: return Vector3i( 0, 0, -1); // Facing North / ZM
|
||||
case 2: return Vector3i( 1, 0, 0); // Facing East / XP
|
||||
default: return Vector3i( 0, 0, 1); // Facing South / ZP
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Returns true if the specified blocktype is any kind of door */
|
||||
inline static bool IsDoor(BLOCKTYPE a_Block)
|
||||
inline static bool IsDoorBlockType(BLOCKTYPE a_Block)
|
||||
{
|
||||
switch (a_Block)
|
||||
{
|
||||
@ -193,6 +206,8 @@ 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)
|
||||
{
|
||||
NIBBLETYPE Meta = GetCompleteDoorMeta(a_ChunkInterface, a_BlockX, a_BlockY, a_BlockZ);
|
||||
@ -237,7 +252,7 @@ public:
|
||||
static void SetOpen(cChunkInterface & a_ChunkInterface, int a_BlockX, int a_BlockY, int a_BlockZ, bool a_Open)
|
||||
{
|
||||
BLOCKTYPE Block = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY, a_BlockZ);
|
||||
if (!IsDoor(Block))
|
||||
if (!IsDoorBlockType(Block))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -369,7 +369,7 @@ void cBlockHandler::OnUpdate(cChunkInterface & cChunkInterface, cWorldInterface
|
||||
|
||||
|
||||
|
||||
void cBlockHandler::OnPlacedByPlayer(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, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
|
||||
void cBlockHandler::OnPlacedByPlayer(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, const sSetBlock & a_BlockChange)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -42,15 +42,12 @@ public:
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
);
|
||||
|
||||
/// Called by cWorld::SetBlock() after the block has been set
|
||||
/** Called by cWorld::SetBlock() after the block has been set */
|
||||
virtual void OnPlaced(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
|
||||
|
||||
/// Called by cClientHandle::HandlePlaceBlock() after the player has placed a new block. Called after OnPlaced().
|
||||
/** Called by cPlayer::PlaceBlocks() for each block after it has been set to the world. Called after OnPlaced(). */
|
||||
virtual void OnPlacedByPlayer(
|
||||
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,
|
||||
BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta
|
||||
cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, const sSetBlock & a_BlockChange
|
||||
);
|
||||
|
||||
/// Called before the player has destroyed a block
|
||||
@ -96,7 +93,8 @@ public:
|
||||
*/
|
||||
// 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
|
||||
/** 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.
|
||||
@ -109,20 +107,21 @@ public:
|
||||
*/
|
||||
virtual bool DoesIgnoreBuildCollision(void);
|
||||
|
||||
/// <summary>Similar to DoesIgnoreBuildCollision(void), but is used for cases where block meta/player item-in-hand is needed to determine collision (thin snow)</summary>
|
||||
/** Similar to DoesIgnoreBuildCollision(void), but is used for cases where block's meta or
|
||||
player's item-in-hand is needed to determine collision (thin snow) */
|
||||
virtual bool DoesIgnoreBuildCollision(cPlayer *, NIBBLETYPE a_Meta)
|
||||
{
|
||||
UNUSED(a_Meta);
|
||||
return DoesIgnoreBuildCollision();
|
||||
}
|
||||
|
||||
/// <summary>Returns if this block drops if it gets destroyed by an unsuitable situation. Default: true</summary>
|
||||
/** Returns if this block drops if it gets destroyed by an unsuitable situation.
|
||||
Default: true */
|
||||
virtual bool DoesDropOnUnsuitable(void);
|
||||
|
||||
/** Called when one of the neighbors gets set; equivalent to MC block update.
|
||||
By default drops if position no more suitable (CanBeAt(), DoesDropOnUnsuitable(), Drop()),
|
||||
and wakes up all simulators on the block.
|
||||
*/
|
||||
and wakes up all simulators on the block. */
|
||||
virtual void Check(cChunkInterface & ChunkInterface, cBlockPluginInterface & a_PluginInterface, int a_RelX, int a_RelY, int a_RelZ, cChunk & a_Chunk);
|
||||
|
||||
/// <summary>Rotates a given block meta counter-clockwise. Default: no change</summary>
|
||||
|
@ -12,16 +12,18 @@ class cBlockMobHeadHandler :
|
||||
public cBlockEntityHandler
|
||||
{
|
||||
public:
|
||||
cBlockMobHeadHandler(BLOCKTYPE a_BlockType)
|
||||
: cBlockEntityHandler(a_BlockType)
|
||||
cBlockMobHeadHandler(BLOCKTYPE a_BlockType):
|
||||
cBlockEntityHandler(a_BlockType)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
virtual void ConvertToPickups(cItems & a_Pickups, NIBBLETYPE a_BlockMeta) override
|
||||
{
|
||||
// The drop spawn is in OnDestroyed method
|
||||
// The drop spawn is in the OnDestroyedByPlayer method
|
||||
}
|
||||
|
||||
|
||||
virtual void OnDestroyedByPlayer(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, cPlayer * a_Player, int a_BlockX, int a_BlockY, int a_BlockZ) override
|
||||
{
|
||||
if (a_Player->IsGameModeCreative())
|
||||
@ -61,202 +63,6 @@ public:
|
||||
|
||||
a_WorldInterface.DoWithBlockEntityAt(a_BlockX, a_BlockY, a_BlockZ, Callback);
|
||||
}
|
||||
|
||||
bool TrySpawnWither(cChunkInterface & a_ChunkInterface, cWorldInterface & a_WorldInterface, int a_BlockX, int a_BlockY, int a_BlockZ)
|
||||
{
|
||||
if (a_BlockY < 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
class cCallback : public cBlockEntityCallback
|
||||
{
|
||||
bool m_IsWither;
|
||||
|
||||
virtual bool Item(cBlockEntity * a_BlockEntity)
|
||||
{
|
||||
if (a_BlockEntity->GetBlockType() != E_BLOCK_HEAD)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
cMobHeadEntity * MobHeadEntity = static_cast<cMobHeadEntity*>(a_BlockEntity);
|
||||
|
||||
m_IsWither = (MobHeadEntity->GetType() == SKULL_TYPE_WITHER);
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
cCallback () : m_IsWither(false) {}
|
||||
|
||||
bool IsWither(void) const { return m_IsWither; }
|
||||
|
||||
void Reset(void) { m_IsWither = false; }
|
||||
|
||||
} CallbackA, CallbackB;
|
||||
|
||||
class cPlayerCallback : public cPlayerListCallback
|
||||
{
|
||||
Vector3f m_Pos;
|
||||
|
||||
virtual bool Item(cPlayer * a_Player)
|
||||
{
|
||||
// TODO 2014-05-21 xdot: Vanilla minecraft uses an AABB check instead of a radius one
|
||||
double Dist = (a_Player->GetPosition() - m_Pos).Length();
|
||||
if (Dist < 50.0)
|
||||
{
|
||||
// If player is close, award achievement
|
||||
a_Player->AwardAchievement(achSpawnWither);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
cPlayerCallback(const Vector3f & a_Pos) : m_Pos(a_Pos) {}
|
||||
|
||||
} PlayerCallback(Vector3f((float)a_BlockX, (float)a_BlockY, (float)a_BlockZ));
|
||||
|
||||
a_WorldInterface.DoWithBlockEntityAt(a_BlockX, a_BlockY, a_BlockZ, CallbackA);
|
||||
|
||||
if (!CallbackA.IsWither())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
CallbackA.Reset();
|
||||
|
||||
BLOCKTYPE BlockY1 = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ);
|
||||
BLOCKTYPE BlockY2 = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ);
|
||||
|
||||
if ((BlockY1 != E_BLOCK_SOULSAND) || (BlockY2 != E_BLOCK_SOULSAND))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
a_WorldInterface.DoWithBlockEntityAt(a_BlockX - 1, a_BlockY, a_BlockZ, CallbackA);
|
||||
a_WorldInterface.DoWithBlockEntityAt(a_BlockX + 1, a_BlockY, a_BlockZ, CallbackB);
|
||||
|
||||
BLOCKTYPE Block1 = a_ChunkInterface.GetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ);
|
||||
BLOCKTYPE Block2 = a_ChunkInterface.GetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ);
|
||||
|
||||
if ((Block1 == E_BLOCK_SOULSAND) && (Block2 == E_BLOCK_SOULSAND) && CallbackA.IsWither() && CallbackB.IsWither())
|
||||
{
|
||||
a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0);
|
||||
a_ChunkInterface.FastSetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0);
|
||||
a_ChunkInterface.FastSetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0);
|
||||
a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0);
|
||||
|
||||
// Block entities
|
||||
a_ChunkInterface.SetBlock(a_BlockX + 1, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0);
|
||||
a_ChunkInterface.SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0);
|
||||
a_ChunkInterface.SetBlock(a_BlockX - 1, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0);
|
||||
|
||||
// Spawn the wither:
|
||||
a_WorldInterface.SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, mtWither);
|
||||
|
||||
// Award Achievement
|
||||
a_WorldInterface.ForEachPlayer(PlayerCallback);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CallbackA.Reset();
|
||||
CallbackB.Reset();
|
||||
|
||||
a_WorldInterface.DoWithBlockEntityAt(a_BlockX, a_BlockY, a_BlockZ - 1, CallbackA);
|
||||
a_WorldInterface.DoWithBlockEntityAt(a_BlockX, a_BlockY, a_BlockZ + 1, CallbackB);
|
||||
|
||||
Block1 = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1);
|
||||
Block2 = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1);
|
||||
|
||||
if ((Block1 == E_BLOCK_SOULSAND) && (Block2 == E_BLOCK_SOULSAND) && CallbackA.IsWither() && CallbackB.IsWither())
|
||||
{
|
||||
a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0);
|
||||
a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1, E_BLOCK_AIR, 0);
|
||||
a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1, E_BLOCK_AIR, 0);
|
||||
a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0);
|
||||
|
||||
// Block entities
|
||||
a_ChunkInterface.SetBlock(a_BlockX, a_BlockY, a_BlockZ + 1, E_BLOCK_AIR, 0);
|
||||
a_ChunkInterface.SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0);
|
||||
a_ChunkInterface.SetBlock(a_BlockX, a_BlockY, a_BlockZ - 1, E_BLOCK_AIR, 0);
|
||||
|
||||
// Spawn the wither:
|
||||
a_WorldInterface.SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, mtWither);
|
||||
|
||||
// Award Achievement
|
||||
a_WorldInterface.ForEachPlayer(PlayerCallback);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void OnPlacedByPlayer(
|
||||
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,
|
||||
BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta
|
||||
) override
|
||||
{
|
||||
class cCallback : public cBlockEntityCallback
|
||||
{
|
||||
cPlayer * m_Player;
|
||||
NIBBLETYPE m_OldBlockMeta;
|
||||
NIBBLETYPE m_NewBlockMeta;
|
||||
|
||||
virtual bool Item(cBlockEntity * a_BlockEntity)
|
||||
{
|
||||
if (a_BlockEntity->GetBlockType() != E_BLOCK_HEAD)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
cMobHeadEntity * MobHeadEntity = static_cast<cMobHeadEntity*>(a_BlockEntity);
|
||||
|
||||
int Rotation = 0;
|
||||
if (m_NewBlockMeta == 1)
|
||||
{
|
||||
Rotation = (int) floor(m_Player->GetYaw() * 16.0F / 360.0F + 0.5) & 0xF;
|
||||
}
|
||||
|
||||
MobHeadEntity->SetType(static_cast<eMobHeadType>(m_OldBlockMeta));
|
||||
MobHeadEntity->SetRotation(static_cast<eMobHeadRotation>(Rotation));
|
||||
MobHeadEntity->GetWorld()->BroadcastBlockEntity(MobHeadEntity->GetPosX(), MobHeadEntity->GetPosY(), MobHeadEntity->GetPosZ());
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
cCallback (cPlayer * a_CBPlayer, NIBBLETYPE a_OldBlockMeta, NIBBLETYPE a_NewBlockMeta) :
|
||||
m_Player(a_CBPlayer),
|
||||
m_OldBlockMeta(a_OldBlockMeta),
|
||||
m_NewBlockMeta(a_NewBlockMeta)
|
||||
{}
|
||||
};
|
||||
cCallback Callback(a_Player, a_BlockMeta, static_cast<NIBBLETYPE>(a_BlockFace));
|
||||
|
||||
a_BlockMeta = (NIBBLETYPE)a_BlockFace;
|
||||
a_WorldInterface.DoWithBlockEntityAt(a_BlockX, a_BlockY, a_BlockZ, Callback);
|
||||
a_ChunkInterface.SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, a_BlockMeta);
|
||||
|
||||
if (a_BlockMeta == SKULL_TYPE_WITHER)
|
||||
{
|
||||
static const Vector3i Coords[] =
|
||||
{
|
||||
Vector3i( 0, 0, 0),
|
||||
Vector3i( 1, 0, 0),
|
||||
Vector3i(-1, 0, 0),
|
||||
Vector3i( 0, 0, 1),
|
||||
Vector3i( 0, 0, -1),
|
||||
};
|
||||
for (size_t i = 0; i < ARRAYCOUNT(Coords); ++i)
|
||||
{
|
||||
if (TrySpawnWither(a_ChunkInterface, a_WorldInterface, a_BlockX + Coords[i].x, a_BlockY, a_BlockZ + Coords[i].z))
|
||||
{
|
||||
break;
|
||||
}
|
||||
} // for i - Coords[]
|
||||
}
|
||||
}
|
||||
} ;
|
||||
|
||||
|
||||
|
@ -17,70 +17,6 @@ public:
|
||||
}
|
||||
|
||||
|
||||
virtual void OnPlacedByPlayer(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, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) override
|
||||
{
|
||||
// Check whether the pumpkin is a part of a golem or a snowman
|
||||
|
||||
if (a_BlockY < 2)
|
||||
{
|
||||
// The pumpkin is too low for a golem / snowman
|
||||
return;
|
||||
}
|
||||
|
||||
BLOCKTYPE BlockY1 = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ);
|
||||
BLOCKTYPE BlockY2 = a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ);
|
||||
|
||||
// Check for a snow golem:
|
||||
if ((BlockY1 == E_BLOCK_SNOW_BLOCK) && (BlockY2 == E_BLOCK_SNOW_BLOCK))
|
||||
{
|
||||
a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0);
|
||||
a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0);
|
||||
a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0);
|
||||
a_WorldInterface.SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, mtSnowGolem);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for an iron golem. First check only the body and legs, since those are the same for both orientations:
|
||||
if ((BlockY1 != E_BLOCK_IRON_BLOCK) || (BlockY2 != E_BLOCK_IRON_BLOCK))
|
||||
{
|
||||
// One of the blocks is not an iron, no chance of a golem here
|
||||
return;
|
||||
}
|
||||
|
||||
// Now check both orientations for hands:
|
||||
if (
|
||||
(a_ChunkInterface.GetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK) &&
|
||||
(a_ChunkInterface.GetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ) == E_BLOCK_IRON_BLOCK)
|
||||
)
|
||||
{
|
||||
// Remove the iron blocks:
|
||||
a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0);
|
||||
a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0);
|
||||
a_ChunkInterface.FastSetBlock(a_BlockX + 1, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0);
|
||||
a_ChunkInterface.FastSetBlock(a_BlockX - 1, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0);
|
||||
a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0);
|
||||
|
||||
// Spawn the golem:
|
||||
a_WorldInterface.SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, mtIronGolem);
|
||||
}
|
||||
else if (
|
||||
(a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1) == E_BLOCK_IRON_BLOCK) &&
|
||||
(a_ChunkInterface.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1) == E_BLOCK_IRON_BLOCK)
|
||||
)
|
||||
{
|
||||
// Remove the iron blocks:
|
||||
a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0);
|
||||
a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0);
|
||||
a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ + 1, E_BLOCK_AIR, 0);
|
||||
a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 1, a_BlockZ - 1, E_BLOCK_AIR, 0);
|
||||
a_ChunkInterface.FastSetBlock(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0);
|
||||
|
||||
// Spawn the golem:
|
||||
a_WorldInterface.SpawnMob(a_BlockX + 0.5, a_BlockY - 2, a_BlockZ + 0.5, mtIronGolem);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
virtual bool GetPlacementBlockTypeMeta(
|
||||
cChunkInterface & a_ChunkInterface, cPlayer * a_Player,
|
||||
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
|
||||
|
@ -53,17 +53,6 @@ public:
|
||||
}
|
||||
|
||||
|
||||
virtual void OnPlacedByPlayer(
|
||||
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,
|
||||
BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta
|
||||
) override
|
||||
{
|
||||
a_Player->GetClientHandle()->SendEditSign(a_BlockX, a_BlockY, a_BlockZ);
|
||||
}
|
||||
|
||||
|
||||
virtual NIBBLETYPE MetaRotateCW(NIBBLETYPE a_Meta) override
|
||||
{
|
||||
return (a_Meta + 4) & 0x0f;
|
||||
|
@ -27,17 +27,6 @@ public:
|
||||
}
|
||||
|
||||
|
||||
virtual void OnPlacedByPlayer(
|
||||
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,
|
||||
BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta
|
||||
) override
|
||||
{
|
||||
a_Player->GetClientHandle()->SendEditSign(a_BlockX, a_BlockY, a_BlockZ);
|
||||
}
|
||||
|
||||
|
||||
virtual bool CanBeAt(cChunkInterface & a_ChunkInterface, int a_RelX, int a_RelY, int a_RelZ, const cChunk & a_Chunk) override
|
||||
{
|
||||
int BlockX = (a_Chunk.GetPosX() * cChunkDef::Width) + a_RelX;
|
||||
|
@ -49,14 +49,14 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// sSetBlock:
|
||||
|
||||
sSetBlock::sSetBlock( int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) // absolute block position
|
||||
: x( a_BlockX)
|
||||
, y( a_BlockY)
|
||||
, z( a_BlockZ)
|
||||
, BlockType( a_BlockType)
|
||||
, BlockMeta( a_BlockMeta)
|
||||
sSetBlock::sSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta):
|
||||
m_RelX(a_BlockX),
|
||||
m_RelY(a_BlockY),
|
||||
m_RelZ(a_BlockZ),
|
||||
m_BlockType(a_BlockType),
|
||||
m_BlockMeta(a_BlockMeta)
|
||||
{
|
||||
cChunkDef::AbsoluteToRelative(x, y, z, ChunkX, ChunkZ);
|
||||
cChunkDef::AbsoluteToRelative(m_RelX, m_RelY, m_RelZ, m_ChunkX, m_ChunkZ);
|
||||
}
|
||||
|
||||
|
||||
|
@ -347,18 +347,32 @@ public:
|
||||
|
||||
struct sSetBlock
|
||||
{
|
||||
int x, y, z;
|
||||
int ChunkX, ChunkZ;
|
||||
BLOCKTYPE BlockType;
|
||||
NIBBLETYPE BlockMeta;
|
||||
int m_RelX, m_RelY, m_RelZ;
|
||||
int m_ChunkX, m_ChunkZ;
|
||||
BLOCKTYPE m_BlockType;
|
||||
NIBBLETYPE m_BlockMeta;
|
||||
|
||||
sSetBlock( int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); // absolute block position
|
||||
sSetBlock(int a_ChunkX, int a_ChunkZ, int a_X, int a_Y, int a_Z, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) :
|
||||
x(a_X), y(a_Y), z(a_Z),
|
||||
ChunkX(a_ChunkX), ChunkZ(a_ChunkZ),
|
||||
BlockType(a_BlockType),
|
||||
BlockMeta(a_BlockMeta)
|
||||
{}
|
||||
sSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
|
||||
|
||||
sSetBlock(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) :
|
||||
m_RelX(a_RelX), m_RelY(a_RelY), m_RelZ(a_RelZ),
|
||||
m_ChunkX(a_ChunkX), m_ChunkZ(a_ChunkZ),
|
||||
m_BlockType(a_BlockType),
|
||||
m_BlockMeta(a_BlockMeta)
|
||||
{
|
||||
ASSERT((a_RelX >= 0) && (a_RelX < cChunkDef::Width));
|
||||
ASSERT((a_RelZ >= 0) && (a_RelZ < cChunkDef::Width));
|
||||
}
|
||||
|
||||
/** Returns the absolute X coord of the stored block. */
|
||||
int GetX(void) const { return m_RelX + cChunkDef::Width * m_ChunkX; }
|
||||
|
||||
/** Returns the absolute Y coord of the stored block.
|
||||
Is the same as relative Y coords, because there's no Y relativization. */
|
||||
int GetY(void) const { return m_RelY; }
|
||||
|
||||
/** Returns the absolute Z coord of the stored block. */
|
||||
int GetZ(void) const { return m_RelZ + cChunkDef::Width * m_ChunkZ; }
|
||||
};
|
||||
|
||||
typedef std::list<sSetBlock> sSetBlockList;
|
||||
@ -385,6 +399,17 @@ public:
|
||||
typedef std::list<cChunkCoords> cChunkCoordsList;
|
||||
typedef std::vector<cChunkCoords> cChunkCoordsVector;
|
||||
|
||||
/** A simple hash function for chunk coords, we assume that chunk coords won't use more than 16 bits, so the hash is almost an identity.
|
||||
Used for std::unordered_map<cChunkCoords, ...> */
|
||||
class cChunkCoordsHash
|
||||
{
|
||||
public:
|
||||
size_t operator () (const cChunkCoords & a_Coords) const
|
||||
{
|
||||
return (static_cast<size_t>(a_Coords.m_ChunkX) << 16) ^ static_cast<size_t>(a_Coords.m_ChunkZ);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -1101,17 +1101,17 @@ void cChunkMap::FastSetBlocks(sSetBlockList & a_BlockList)
|
||||
// Process all items from a_BlockList, either successfully or by placing into Failed
|
||||
while (!a_BlockList.empty())
|
||||
{
|
||||
int ChunkX = a_BlockList.front().ChunkX;
|
||||
int ChunkZ = a_BlockList.front().ChunkZ;
|
||||
int ChunkX = a_BlockList.front().m_ChunkX;
|
||||
int ChunkZ = a_BlockList.front().m_ChunkZ;
|
||||
cCSLock Lock(m_CSLayers);
|
||||
cChunkPtr Chunk = GetChunkNoGen(ChunkX, ChunkZ);
|
||||
if ((Chunk != nullptr) && Chunk->IsValid())
|
||||
{
|
||||
for (sSetBlockList::iterator itr = a_BlockList.begin(); itr != a_BlockList.end();)
|
||||
{
|
||||
if ((itr->ChunkX == ChunkX) && (itr->ChunkZ == ChunkZ))
|
||||
if ((itr->m_ChunkX == ChunkX) && (itr->m_ChunkZ == ChunkZ))
|
||||
{
|
||||
Chunk->FastSetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta);
|
||||
Chunk->FastSetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ, itr->m_BlockType, itr->m_BlockMeta);
|
||||
itr = a_BlockList.erase(itr);
|
||||
}
|
||||
else
|
||||
@ -1125,7 +1125,7 @@ void cChunkMap::FastSetBlocks(sSetBlockList & a_BlockList)
|
||||
// The chunk is not valid, move all blocks within this chunk to Failed
|
||||
for (sSetBlockList::iterator itr = a_BlockList.begin(); itr != a_BlockList.end();)
|
||||
{
|
||||
if ((itr->ChunkX == ChunkX) && (itr->ChunkZ == ChunkZ))
|
||||
if ((itr->m_ChunkX == ChunkX) && (itr->m_ChunkZ == ChunkZ))
|
||||
{
|
||||
Failed.push_back(*itr);
|
||||
itr = a_BlockList.erase(itr);
|
||||
@ -1146,6 +1146,34 @@ void cChunkMap::FastSetBlocks(sSetBlockList & a_BlockList)
|
||||
|
||||
|
||||
|
||||
void cChunkMap::SetBlocks(const sSetBlockVector & a_Blocks)
|
||||
{
|
||||
cCSLock lock(m_CSLayers);
|
||||
cChunkPtr chunk = nullptr;
|
||||
int lastChunkX = 0x7fffffff; // Bogus coords so that chunk is updated on first pass
|
||||
int lastChunkZ = 0x7fffffff;
|
||||
for (auto block: a_Blocks)
|
||||
{
|
||||
// Update the chunk, if different from last time:
|
||||
if ((block.m_ChunkX != lastChunkX) || (block.m_ChunkZ != lastChunkZ))
|
||||
{
|
||||
lastChunkX = block.m_ChunkX;
|
||||
lastChunkZ = block.m_ChunkZ;
|
||||
chunk = GetChunk(lastChunkX, lastChunkZ);
|
||||
}
|
||||
|
||||
// If the chunk is valid, set the block:
|
||||
if (chunk != nullptr)
|
||||
{
|
||||
chunk->SetBlock(block.m_RelX, block.m_RelY, block.m_RelZ, block.m_BlockType, block.m_BlockMeta);
|
||||
}
|
||||
} // for block - a_Blocks[]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cChunkMap::CollectPickupsByPlayer(cPlayer & a_Player)
|
||||
{
|
||||
int BlockX = (int)(a_Player.GetPosX()); // Truncating doesn't matter much; we're scanning entire chunks anyway
|
||||
@ -1174,29 +1202,29 @@ void cChunkMap::CollectPickupsByPlayer(cPlayer & a_Player)
|
||||
|
||||
|
||||
BLOCKTYPE cChunkMap::GetBlock(int a_BlockX, int a_BlockY, int a_BlockZ)
|
||||
{
|
||||
// First check if it isn't queued in the m_FastSetBlockQueue:
|
||||
{
|
||||
int X = a_BlockX, Y = a_BlockY, Z = a_BlockZ;
|
||||
int ChunkX, ChunkZ;
|
||||
cChunkDef::AbsoluteToRelative(X, Y, Z, ChunkX, ChunkZ);
|
||||
|
||||
// First check if it isn't queued in the m_FastSetBlockQueue:
|
||||
{
|
||||
cCSLock Lock(m_CSFastSetBlock);
|
||||
for (sSetBlockList::iterator itr = m_FastSetBlockQueue.begin(); itr != m_FastSetBlockQueue.end(); ++itr)
|
||||
{
|
||||
if ((itr->x == X) && (itr->y == Y) && (itr->z == Z) && (itr->ChunkX == ChunkX) && (itr->ChunkZ == ChunkZ))
|
||||
if ((itr->m_RelX == X) && (itr->m_RelY == Y) && (itr->m_RelZ == Z) && (itr->m_ChunkX == ChunkX) && (itr->m_ChunkZ == ChunkZ))
|
||||
{
|
||||
return itr->BlockType;
|
||||
return itr->m_BlockType;
|
||||
}
|
||||
} // for itr - m_FastSetBlockQueue[]
|
||||
}
|
||||
int ChunkX, ChunkZ;
|
||||
cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ);
|
||||
|
||||
// Not in the queue, query the chunk, if loaded:
|
||||
cCSLock Lock(m_CSLayers);
|
||||
cChunkPtr Chunk = GetChunk(ChunkX, ChunkZ);
|
||||
if ((Chunk != nullptr) && Chunk->IsValid())
|
||||
{
|
||||
return Chunk->GetBlock(a_BlockX, a_BlockY, a_BlockZ);
|
||||
return Chunk->GetBlock(X, Y, Z);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -1207,25 +1235,28 @@ BLOCKTYPE cChunkMap::GetBlock(int a_BlockX, int a_BlockY, int a_BlockZ)
|
||||
|
||||
NIBBLETYPE cChunkMap::GetBlockMeta(int a_BlockX, int a_BlockY, int a_BlockZ)
|
||||
{
|
||||
int X = a_BlockX, Y = a_BlockY, Z = a_BlockZ;
|
||||
int ChunkX, ChunkZ;
|
||||
cChunkDef::AbsoluteToRelative(X, Y, Z, ChunkX, ChunkZ);
|
||||
|
||||
// First check if it isn't queued in the m_FastSetBlockQueue:
|
||||
{
|
||||
cCSLock Lock(m_CSFastSetBlock);
|
||||
for (sSetBlockList::iterator itr = m_FastSetBlockQueue.begin(); itr != m_FastSetBlockQueue.end(); ++itr)
|
||||
{
|
||||
if ((itr->x == a_BlockX) && (itr->y == a_BlockY) && (itr->z == a_BlockZ))
|
||||
if ((itr->m_RelX == X) && (itr->m_RelY == Y) && (itr->m_RelZ == Z) && (itr->m_ChunkX == ChunkX) && (itr->m_ChunkZ == ChunkZ))
|
||||
{
|
||||
return itr->BlockMeta;
|
||||
return itr->m_BlockMeta;
|
||||
}
|
||||
} // for itr - m_FastSetBlockQueue[]
|
||||
}
|
||||
int ChunkX, ChunkZ;
|
||||
cChunkDef::AbsoluteToRelative(a_BlockX, a_BlockY, a_BlockZ, ChunkX, ChunkZ);
|
||||
|
||||
// Not in the queue, query the chunk, if loaded:
|
||||
cCSLock Lock(m_CSLayers);
|
||||
cChunkPtr Chunk = GetChunk(ChunkX, ChunkZ);
|
||||
if ((Chunk != nullptr) && Chunk->IsValid())
|
||||
{
|
||||
return Chunk->GetMeta(a_BlockX, a_BlockY, a_BlockZ);
|
||||
return Chunk->GetMeta(X, Y, Z);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -1373,14 +1404,14 @@ void cChunkMap::ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_Filt
|
||||
cCSLock Lock(m_CSLayers);
|
||||
for (sSetBlockVector::const_iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr)
|
||||
{
|
||||
cChunkPtr Chunk = GetChunk(itr->ChunkX, itr->ChunkZ);
|
||||
cChunkPtr Chunk = GetChunk(itr->m_ChunkX, itr->m_ChunkZ);
|
||||
if ((Chunk == nullptr) || !Chunk->IsValid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (Chunk->GetBlock(itr->x, itr->y, itr->z) == a_FilterBlockType)
|
||||
if (Chunk->GetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ) == a_FilterBlockType)
|
||||
{
|
||||
Chunk->SetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta);
|
||||
Chunk->SetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ, itr->m_BlockType, itr->m_BlockMeta);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1394,24 +1425,24 @@ void cChunkMap::ReplaceTreeBlocks(const sSetBlockVector & a_Blocks)
|
||||
cCSLock Lock(m_CSLayers);
|
||||
for (sSetBlockVector::const_iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr)
|
||||
{
|
||||
cChunkPtr Chunk = GetChunk(itr->ChunkX, itr->ChunkZ);
|
||||
cChunkPtr Chunk = GetChunk(itr->m_ChunkX, itr->m_ChunkZ);
|
||||
if ((Chunk == nullptr) || !Chunk->IsValid())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
switch (Chunk->GetBlock(itr->x, itr->y, itr->z))
|
||||
switch (Chunk->GetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ))
|
||||
{
|
||||
CASE_TREE_OVERWRITTEN_BLOCKS:
|
||||
{
|
||||
Chunk->SetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta);
|
||||
Chunk->SetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ, itr->m_BlockType, itr->m_BlockMeta);
|
||||
break;
|
||||
}
|
||||
case E_BLOCK_LEAVES:
|
||||
case E_BLOCK_NEW_LEAVES:
|
||||
{
|
||||
if ((itr->BlockType == E_BLOCK_LOG) || (itr->BlockType == E_BLOCK_NEW_LOG))
|
||||
if ((itr->m_BlockType == E_BLOCK_LOG) || (itr->m_BlockType == E_BLOCK_NEW_LOG))
|
||||
{
|
||||
Chunk->SetBlock(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta);
|
||||
Chunk->SetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ, itr->m_BlockType, itr->m_BlockMeta);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -1507,7 +1538,7 @@ bool cChunkMap::GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure)
|
||||
cCSLock Lock(m_CSLayers);
|
||||
for (sSetBlockVector::iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr)
|
||||
{
|
||||
cChunkPtr Chunk = GetChunk(itr->ChunkX, itr->ChunkZ);
|
||||
cChunkPtr Chunk = GetChunk(itr->m_ChunkX, itr->m_ChunkZ);
|
||||
if ((Chunk == nullptr) || !Chunk->IsValid())
|
||||
{
|
||||
if (!a_ContinueOnFailure)
|
||||
@ -1517,8 +1548,8 @@ bool cChunkMap::GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure)
|
||||
res = false;
|
||||
continue;
|
||||
}
|
||||
itr->BlockType = Chunk->GetBlock(itr->x, itr->y, itr->z);
|
||||
itr->BlockMeta = Chunk->GetMeta(itr->x, itr->y, itr->z);
|
||||
itr->m_BlockType = Chunk->GetBlock(itr->m_RelX, itr->m_RelY, itr->m_RelZ);
|
||||
itr->m_BlockMeta = Chunk->GetMeta(itr->m_RelX, itr->m_RelY, itr->m_RelZ);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@ -1527,9 +1558,9 @@ bool cChunkMap::GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure)
|
||||
|
||||
|
||||
|
||||
bool cChunkMap::DigBlock(int a_X, int a_Y, int a_Z)
|
||||
bool cChunkMap::DigBlock(int a_BlockX, int a_BlockY, int a_BlockZ)
|
||||
{
|
||||
int PosX = a_X, PosY = a_Y, PosZ = a_Z, ChunkX, ChunkZ;
|
||||
int PosX = a_BlockX, PosY = a_BlockY, PosZ = a_BlockZ, ChunkX, ChunkZ;
|
||||
|
||||
cChunkDef::AbsoluteToRelative(PosX, PosY, PosZ, ChunkX, ChunkZ);
|
||||
|
||||
@ -1542,7 +1573,7 @@ bool cChunkMap::DigBlock(int a_X, int a_Y, int a_Z)
|
||||
}
|
||||
|
||||
DestChunk->SetBlock(PosX, PosY, PosZ, E_BLOCK_AIR, 0);
|
||||
m_World->GetSimulatorManager()->WakeUp(a_X, a_Y, a_Z, DestChunk);
|
||||
m_World->GetSimulatorManager()->WakeUp(a_BlockX, a_BlockY, a_BlockZ, DestChunk);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -144,6 +144,12 @@ public:
|
||||
|
||||
void FastSetQueuedBlocks();
|
||||
void FastSetBlocks(sSetBlockList & a_BlockList);
|
||||
|
||||
/** Performs the specified single-block set operations simultaneously, as if SetBlock() was called for each item.
|
||||
Is more efficient than calling SetBlock() multiple times.
|
||||
If the chunk for any of the blocks is not loaded, the set operation is ignored silently. */
|
||||
void SetBlocks(const sSetBlockVector & a_Blocks);
|
||||
|
||||
void CollectPickupsByPlayer(cPlayer & a_Player);
|
||||
|
||||
BLOCKTYPE GetBlock (int a_BlockX, int a_BlockY, int a_BlockZ);
|
||||
@ -173,11 +179,20 @@ public:
|
||||
(Re)sends the chunks to their relevant clients if successful. */
|
||||
bool SetAreaBiome(int a_MinX, int a_MaxX, int a_MinZ, int a_MaxZ, EMCSBiome a_Biome);
|
||||
|
||||
/** Retrieves block types of the specified blocks. If a chunk is not loaded, doesn't modify the block. Returns true if all blocks were read. */
|
||||
/** Retrieves block types and metas of the specified blocks.
|
||||
If a chunk is not loaded, doesn't modify the block and consults a_ContinueOnFailure whether to process the rest of the array.
|
||||
Returns true if all blocks were read, false if any one failed. */
|
||||
bool GetBlocks(sSetBlockVector & a_Blocks, bool a_ContinueOnFailure);
|
||||
|
||||
bool DigBlock (int a_X, int a_Y, int a_Z);
|
||||
void SendBlockTo(int a_X, int a_Y, int a_Z, cPlayer * a_Player);
|
||||
/** Removes the block at the specified coords and wakes up simulators.
|
||||
Returns false if the chunk is not loaded (and the block is not dug).
|
||||
Returns true if successful. */
|
||||
bool DigBlock(int a_BlockX, int a_BlockY, int a_BlockZ);
|
||||
|
||||
/** Sends the block at the specified coords to the specified player.
|
||||
Uses a blockchange packet to send the block.
|
||||
If the relevant chunk isn't loaded, doesn't do anything. */
|
||||
void SendBlockTo(int a_BlockX, int a_BlockY, int a_BlockZ, cPlayer * a_Player);
|
||||
|
||||
/** Compares clients of two chunks, calls the callback accordingly */
|
||||
void CompareChunkClients(int a_ChunkX1, int a_ChunkZ1, int a_ChunkX2, int a_ChunkZ2, cClientDiffCallback & a_Callback);
|
||||
|
@ -1346,12 +1346,19 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
|
||||
|
||||
if (ItemHandler->IsPlaceable() && (a_BlockFace != BLOCK_FACE_NONE))
|
||||
{
|
||||
HandlePlaceBlock(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, *ItemHandler);
|
||||
if (!ItemHandler->OnPlayerPlace(*World, *m_Player, Equipped, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ))
|
||||
{
|
||||
// Placement failed, bail out
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if ((ItemHandler->IsFood() || ItemHandler->IsDrinkable(EquippedDamage)))
|
||||
{
|
||||
if ((m_Player->IsSatiated() || m_Player->IsGameModeCreative()) &&
|
||||
ItemHandler->IsFood() && (Equipped.m_ItemType != E_ITEM_GOLDEN_APPLE))
|
||||
if (
|
||||
(m_Player->IsSatiated() || m_Player->IsGameModeCreative()) && // Only creative or hungry players can eat
|
||||
ItemHandler->IsFood() &&
|
||||
(Equipped.m_ItemType != E_ITEM_GOLDEN_APPLE) // Golden apple is a special case, it is used instead of eaten
|
||||
)
|
||||
{
|
||||
// The player is satiated or in creative, and trying to eat
|
||||
return;
|
||||
@ -1379,151 +1386,6 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
|
||||
|
||||
|
||||
|
||||
void cClientHandle::HandlePlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, cItemHandler & a_ItemHandler)
|
||||
{
|
||||
BLOCKTYPE EquippedBlock = (BLOCKTYPE)(m_Player->GetEquippedItem().m_ItemType);
|
||||
if (a_BlockFace < 0)
|
||||
{
|
||||
// Clicked in air
|
||||
return;
|
||||
}
|
||||
|
||||
cWorld * World = m_Player->GetWorld();
|
||||
|
||||
BLOCKTYPE ClickedBlock;
|
||||
NIBBLETYPE ClickedBlockMeta;
|
||||
NIBBLETYPE EquippedBlockDamage = (NIBBLETYPE)(m_Player->GetEquippedItem().m_ItemDamage);
|
||||
|
||||
if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height))
|
||||
{
|
||||
// The block is being placed outside the world, ignore this packet altogether (#128)
|
||||
return;
|
||||
}
|
||||
|
||||
World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlock, ClickedBlockMeta);
|
||||
|
||||
// Special slab handling - placing a slab onto another slab produces a dblslab instead:
|
||||
if (
|
||||
cBlockSlabHandler::IsAnySlabType(ClickedBlock) && // Is there a slab already?
|
||||
cBlockSlabHandler::IsAnySlabType(EquippedBlock) && // Is the player placing another slab?
|
||||
((ClickedBlockMeta & 0x07) == EquippedBlockDamage) && // Is it the same slab type?
|
||||
(
|
||||
(a_BlockFace == BLOCK_FACE_TOP) || // Clicking the top of a bottom slab
|
||||
(a_BlockFace == BLOCK_FACE_BOTTOM) // Clicking the bottom of a top slab
|
||||
)
|
||||
)
|
||||
{
|
||||
// Coordinates at clicked block, which was an eligible slab, and either top or bottom faces were clicked
|
||||
// If clicked top face and slab occupies the top voxel, we want a slab to be placed above it (therefore increment Y)
|
||||
// Else if clicked bottom face and slab occupies the bottom voxel, decrement Y for the same reason
|
||||
// Don't touch coordinates if anything else because a dblslab opportunity is present
|
||||
if ((ClickedBlockMeta & 0x08) && (a_BlockFace == BLOCK_FACE_TOP))
|
||||
{
|
||||
++a_BlockY;
|
||||
}
|
||||
else if (!(ClickedBlockMeta & 0x08) && (a_BlockFace == BLOCK_FACE_BOTTOM))
|
||||
{
|
||||
--a_BlockY;
|
||||
}
|
||||
World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlock, ClickedBlockMeta);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check if the block ignores build collision (water, grass etc.):
|
||||
if (
|
||||
BlockHandler(ClickedBlock)->DoesIgnoreBuildCollision() ||
|
||||
BlockHandler(ClickedBlock)->DoesIgnoreBuildCollision(m_Player, ClickedBlockMeta)
|
||||
)
|
||||
{
|
||||
cChunkInterface ChunkInterface(World->GetChunkMap());
|
||||
BlockHandler(ClickedBlock)->OnDestroyedByPlayer(ChunkInterface, *World, m_Player, a_BlockX, a_BlockY, a_BlockZ);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
|
||||
|
||||
if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height))
|
||||
{
|
||||
// The block is being placed outside the world, ignore this packet altogether (#128)
|
||||
return;
|
||||
}
|
||||
|
||||
NIBBLETYPE PlaceMeta;
|
||||
BLOCKTYPE PlaceBlock;
|
||||
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 (cBlockSlabHandler::IsAnySlabType(PlaceBlock))
|
||||
{
|
||||
// It's a slab, don't do checks and proceed to double-slabbing
|
||||
}
|
||||
else
|
||||
{
|
||||
if (
|
||||
!BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision() &&
|
||||
!BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision(m_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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BLOCKTYPE BlockType;
|
||||
NIBBLETYPE BlockMeta;
|
||||
if (!a_ItemHandler.GetPlacementBlockTypeMeta(World, m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta))
|
||||
{
|
||||
// Handler refused the placement, send that information back to the client:
|
||||
World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player);
|
||||
m_Player->GetInventory().SendEquippedSlot();
|
||||
return;
|
||||
}
|
||||
|
||||
cBlockHandler * NewBlock = BlockHandler(BlockType);
|
||||
|
||||
if (cRoot::Get()->GetPluginManager()->CallHookPlayerPlacingBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta))
|
||||
{
|
||||
// A plugin doesn't agree with placing the block, revert the block on the client:
|
||||
World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, m_Player);
|
||||
m_Player->GetInventory().SendEquippedSlot();
|
||||
return;
|
||||
}
|
||||
|
||||
// The actual block placement:
|
||||
World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta);
|
||||
if (!m_Player->IsGameModeCreative())
|
||||
{
|
||||
m_Player->GetInventory().RemoveOneEquippedItem();
|
||||
}
|
||||
|
||||
cChunkInterface ChunkInterface(World->GetChunkMap());
|
||||
NewBlock->OnPlacedByPlayer(ChunkInterface, *World, m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta);
|
||||
|
||||
AString PlaceSound = cBlockInfo::GetPlaceSound(BlockType);
|
||||
float Volume = 1.0f, Pitch = 0.8f;
|
||||
if (PlaceSound == "dig.metal")
|
||||
{
|
||||
Pitch = 1.2f;
|
||||
PlaceSound = "dig.stone";
|
||||
}
|
||||
else if (PlaceSound == "random.anvil_land")
|
||||
{
|
||||
Volume = 0.65f;
|
||||
}
|
||||
|
||||
World->BroadcastSoundEffect(PlaceSound, a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, Volume, Pitch);
|
||||
|
||||
cRoot::Get()->GetPluginManager()->CallHookPlayerPlacedBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cClientHandle::HandleChat(const AString & a_Message)
|
||||
{
|
||||
// We no longer need to postpone message processing, because the messages already arrive in the Tick thread
|
||||
|
@ -459,9 +459,6 @@ private:
|
||||
UInt32 m_ProtocolVersion;
|
||||
|
||||
|
||||
/** Handles the block placing packet when it is a real block placement (not block-using, item-using or eating) */
|
||||
void HandlePlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, cItemHandler & a_ItemHandler);
|
||||
|
||||
/** Returns true if the rate block interactions is within a reasonable limit (bot protection) */
|
||||
bool CheckBlockInteractionsRate(void);
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
|
||||
|
||||
#include "Player.h"
|
||||
#include <unordered_map>
|
||||
#include "../ChatColor.h"
|
||||
#include "../Server.h"
|
||||
#include "../UI/Window.h"
|
||||
@ -19,6 +20,10 @@
|
||||
#include "../WorldStorage/StatSerializer.h"
|
||||
#include "../CompositeChat.h"
|
||||
|
||||
#include "../Blocks/BlockHandler.h"
|
||||
#include "../Blocks/BlockSlab.h"
|
||||
#include "../Blocks/ChunkInterface.h"
|
||||
|
||||
#include "../IniFile.h"
|
||||
#include "json/json.h"
|
||||
|
||||
@ -2168,6 +2173,97 @@ void cPlayer::LoadRank(void)
|
||||
|
||||
|
||||
|
||||
bool cPlayer::PlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
|
||||
{
|
||||
sSetBlockVector blk{{a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta}};
|
||||
return PlaceBlocks(blk);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cPlayer::SendBlocksAround(int a_BlockX, int a_BlockY, int a_BlockZ, int a_Range)
|
||||
{
|
||||
// Collect the coords of all the blocks to send:
|
||||
sSetBlockVector blks;
|
||||
for (int y = a_BlockY - a_Range + 1; y < a_BlockY + a_Range; y++)
|
||||
{
|
||||
for (int z = a_BlockZ - a_Range + 1; z < a_BlockZ + a_Range; z++)
|
||||
{
|
||||
for (int x = a_BlockX - a_Range + 1; x < a_BlockX + a_Range; x++)
|
||||
{
|
||||
blks.emplace_back(x, y, z, E_BLOCK_AIR, 0); // Use fake blocktype, it will get set later on.
|
||||
};
|
||||
};
|
||||
} // for y
|
||||
|
||||
// Get the values of all the blocks:
|
||||
if (!m_World->GetBlocks(blks, false))
|
||||
{
|
||||
LOGD("%s: Cannot query all blocks, not sending an update", __FUNCTION__);
|
||||
return;
|
||||
}
|
||||
|
||||
// Divide the block changes by their respective chunks:
|
||||
std::unordered_map<cChunkCoords, sSetBlockVector, cChunkCoordsHash> Changes;
|
||||
for (const auto & blk: blks)
|
||||
{
|
||||
Changes[cChunkCoords(blk.m_ChunkX, blk.m_ChunkZ)].push_back(blk);
|
||||
} // for blk - blks[]
|
||||
blks.clear();
|
||||
|
||||
// Send the blocks for each affected chunk:
|
||||
for (auto itr = Changes.cbegin(), end = Changes.cend(); itr != end; ++itr)
|
||||
{
|
||||
m_ClientHandle->SendBlockChanges(itr->first.m_ChunkX, itr->first.m_ChunkZ, itr->second);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cPlayer::PlaceBlocks(const sSetBlockVector & a_Blocks)
|
||||
{
|
||||
// Call the "placing" hooks; if any fail, abort:
|
||||
cPluginManager * pm = cPluginManager::Get();
|
||||
for (auto blk: a_Blocks)
|
||||
{
|
||||
if (pm->CallHookPlayerPlacingBlock(*this, blk))
|
||||
{
|
||||
// Abort - re-send all the current blocks in the a_Blocks' coords to the client:
|
||||
for (auto blk2: a_Blocks)
|
||||
{
|
||||
m_World->SendBlockTo(blk2.GetX(), blk2.GetY(), blk2.GetZ(), this);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} // for blk - a_Blocks[]
|
||||
|
||||
// Set the blocks:
|
||||
m_World->SetBlocks(a_Blocks);
|
||||
|
||||
// Notify the blockhandlers:
|
||||
cChunkInterface ChunkInterface(m_World->GetChunkMap());
|
||||
for (auto blk: a_Blocks)
|
||||
{
|
||||
cBlockHandler * newBlock = BlockHandler(blk.m_BlockType);
|
||||
newBlock->OnPlacedByPlayer(ChunkInterface, *m_World, this, blk);
|
||||
}
|
||||
|
||||
// Call the "placed" hooks:
|
||||
for (auto blk: a_Blocks)
|
||||
{
|
||||
pm->CallHookPlayerPlacedBlock(*this, blk);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cPlayer::Detach()
|
||||
{
|
||||
super::Detach();
|
||||
|
@ -440,8 +440,27 @@ public:
|
||||
Loads the m_Rank, m_Permissions, m_MsgPrefix, m_MsgSuffix and m_MsgNameColorCode members. */
|
||||
void LoadRank(void);
|
||||
|
||||
/** Calls the block-placement hook and places the block in the world, unless refused by the hook.
|
||||
If the hook prevents the placement, sends the current block at the specified coords back to the client.
|
||||
Assumes that the block is in a currently loaded chunk.
|
||||
Returns true if the block is successfully placed. */
|
||||
bool PlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
|
||||
|
||||
/** Sends the block in the specified range around the specified coord to the client
|
||||
as a block change packet.
|
||||
The blocks in range (a_BlockX - a_Range, a_BlockX + a_Range) are sent (NY-metric). */
|
||||
void SendBlocksAround(int a_BlockX, int a_BlockY, int a_BlockZ, int a_Range = 1);
|
||||
|
||||
// tolua_end
|
||||
|
||||
/** Calls the block placement hooks and places the blocks in the world.
|
||||
First the "placing" hooks for all the blocks are called, then the blocks are placed, and finally
|
||||
the "placed" hooks are called.
|
||||
If the any of the "placing" hooks aborts, none of the blocks are placed and the function returns false.
|
||||
Returns true if all the blocks are placed.
|
||||
Assumes that all the blocks are in currently loaded chunks. */
|
||||
bool PlaceBlocks(const sSetBlockVector & a_Blocks);
|
||||
|
||||
// cEntity overrides:
|
||||
virtual bool IsCrouched (void) const { return m_IsCrouched; }
|
||||
virtual bool IsSprinting(void) const { return m_IsSprinting; }
|
||||
|
@ -123,18 +123,18 @@ void cStructGenTrees::GenerateSingleTree(
|
||||
// Check if the generated image fits the terrain. Only the logs are checked:
|
||||
for (sSetBlockVector::const_iterator itr = TreeLogs.begin(); itr != TreeLogs.end(); ++itr)
|
||||
{
|
||||
if ((itr->ChunkX != a_ChunkX) || (itr->ChunkZ != a_ChunkZ))
|
||||
if ((itr->m_ChunkX != a_ChunkX) || (itr->m_ChunkZ != a_ChunkZ))
|
||||
{
|
||||
// Outside the chunk
|
||||
continue;
|
||||
}
|
||||
if (itr->y >= cChunkDef::Height)
|
||||
if (itr->m_RelY >= cChunkDef::Height)
|
||||
{
|
||||
// Above the chunk, cut off (this shouldn't happen too often, we're limiting trees to y < 230)
|
||||
continue;
|
||||
}
|
||||
|
||||
BLOCKTYPE Block = a_ChunkDesc.GetBlockType(itr->x, itr->y, itr->z);
|
||||
BLOCKTYPE Block = a_ChunkDesc.GetBlockType(itr->m_RelX, itr->m_RelY, itr->m_RelZ);
|
||||
switch (Block)
|
||||
{
|
||||
CASE_TREE_ALLOWED_BLOCKS:
|
||||
@ -167,14 +167,14 @@ void cStructGenTrees::ApplyTreeImage(
|
||||
// Put the generated image into a_BlockTypes, push things outside this chunk into a_Blocks
|
||||
for (sSetBlockVector::const_iterator itr = a_Image.begin(), end = a_Image.end(); itr != end; ++itr)
|
||||
{
|
||||
if ((itr->ChunkX == a_ChunkX) && (itr->ChunkZ == a_ChunkZ) && (itr->y < cChunkDef::Height))
|
||||
if ((itr->m_ChunkX == a_ChunkX) && (itr->m_ChunkZ == a_ChunkZ) && (itr->m_RelY < cChunkDef::Height))
|
||||
{
|
||||
// Inside this chunk, integrate into a_ChunkDesc:
|
||||
switch (a_ChunkDesc.GetBlockType(itr->x, itr->y, itr->z))
|
||||
switch (a_ChunkDesc.GetBlockType(itr->m_RelX, itr->m_RelY, itr->m_RelZ))
|
||||
{
|
||||
case E_BLOCK_LEAVES:
|
||||
{
|
||||
if (itr->BlockType != E_BLOCK_LOG)
|
||||
if (itr->m_BlockType != E_BLOCK_LOG)
|
||||
{
|
||||
break;
|
||||
}
|
||||
@ -182,7 +182,7 @@ void cStructGenTrees::ApplyTreeImage(
|
||||
}
|
||||
CASE_TREE_OVERWRITTEN_BLOCKS:
|
||||
{
|
||||
a_ChunkDesc.SetBlockTypeMeta(itr->x, itr->y, itr->z, itr->BlockType, itr->BlockMeta);
|
||||
a_ChunkDesc.SetBlockTypeMeta(itr->m_RelX, itr->m_RelY, itr->m_RelZ, itr->m_BlockType, itr->m_BlockMeta);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -403,17 +403,17 @@ void GetLargeAppleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a
|
||||
for (auto itr : a_LogBlocks)
|
||||
{
|
||||
// Get the log's X and Z coordinates
|
||||
int X = itr.ChunkX * 16 + itr.x;
|
||||
int Z = itr.ChunkZ * 16 + itr.z;
|
||||
int X = itr.GetX();
|
||||
int Z = itr.GetZ();
|
||||
|
||||
a_OtherBlocks.push_back(sSetBlock(X, itr.y - 2, Z, E_BLOCK_LEAVES, E_META_LEAVES_APPLE));
|
||||
PushCoordBlocks(X, itr.y - 2, Z, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
|
||||
a_OtherBlocks.push_back(sSetBlock(X, itr.m_RelY - 2, Z, E_BLOCK_LEAVES, E_META_LEAVES_APPLE));
|
||||
PushCoordBlocks(X, itr.m_RelY - 2, Z, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
|
||||
for (int y = -1; y <= 1; y++)
|
||||
{
|
||||
PushCoordBlocks (X, itr.y + y, Z, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
|
||||
PushCoordBlocks (X, itr.m_RelY + y, Z, a_OtherBlocks, BigO2, ARRAYCOUNT(BigO2), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
|
||||
}
|
||||
PushCoordBlocks(X, itr.y + 2, Z, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
|
||||
a_OtherBlocks.push_back(sSetBlock(X, itr.y + 2, Z, E_BLOCK_LEAVES, E_META_LEAVES_APPLE));
|
||||
PushCoordBlocks(X, itr.m_RelY + 2, Z, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_APPLE);
|
||||
a_OtherBlocks.push_back(sSetBlock(X, itr.m_RelY + 2, Z, E_BLOCK_LEAVES, E_META_LEAVES_APPLE));
|
||||
}
|
||||
|
||||
// Trunk:
|
||||
|
@ -10,12 +10,14 @@ SET (SRCS
|
||||
SET (HDRS
|
||||
ItemArmor.h
|
||||
ItemBed.h
|
||||
ItemBigFlower.h
|
||||
ItemBoat.h
|
||||
ItemBow.h
|
||||
ItemBrewingStand.h
|
||||
ItemBucket.h
|
||||
ItemCake.h
|
||||
ItemCauldron.h
|
||||
ItemChest.h
|
||||
ItemCloth.h
|
||||
ItemComparator.h
|
||||
ItemDoor.h
|
||||
@ -38,18 +40,21 @@ SET (HDRS
|
||||
ItemPainting.h
|
||||
ItemPickaxe.h
|
||||
ItemPotion.h
|
||||
ItemPumpkin.h
|
||||
ItemRedstoneDust.h
|
||||
ItemRedstoneRepeater.h
|
||||
ItemSapling.h
|
||||
ItemSeeds.h
|
||||
ItemShears.h
|
||||
ItemShovel.h
|
||||
ItemSlab.h
|
||||
ItemSign.h
|
||||
ItemSpawnEgg.h
|
||||
ItemString.h
|
||||
ItemSugarcane.h
|
||||
ItemSword.h
|
||||
ItemThrowable.h)
|
||||
ItemThrowable.h
|
||||
)
|
||||
|
||||
if(NOT MSVC)
|
||||
add_library(Items ${SRCS} ${HDRS})
|
||||
|
@ -24,30 +24,36 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool GetPlacementBlockTypeMeta(
|
||||
cWorld * a_World, cPlayer * a_Player,
|
||||
|
||||
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,
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
int a_CursorX, int a_CursorY, int a_CursorZ
|
||||
) override
|
||||
{
|
||||
// Can only be placed on the floor:
|
||||
if (a_BlockFace != BLOCK_FACE_TOP)
|
||||
{
|
||||
// Can only be placed on the floor
|
||||
return false;
|
||||
}
|
||||
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
|
||||
|
||||
a_BlockMeta = cBlockBedHandler::RotationToMetaData(a_Player->GetYaw());
|
||||
// The "foot" block:
|
||||
sSetBlockVector blks;
|
||||
NIBBLETYPE BlockMeta = cBlockBedHandler::RotationToMetaData(a_Player.GetYaw());
|
||||
blks.emplace_back(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_BED, BlockMeta);
|
||||
|
||||
// Check if there is empty space for the foot section:
|
||||
Vector3i Direction = cBlockBedHandler::MetaDataToDirection(a_BlockMeta);
|
||||
if (a_World->GetBlock(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z) != E_BLOCK_AIR)
|
||||
// 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)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
blks.emplace_back(a_BlockX + Direction.x, a_BlockY, a_BlockZ + Direction.z, E_BLOCK_BED, BlockMeta | 0x08);
|
||||
|
||||
a_BlockType = E_BLOCK_BED;
|
||||
return true;
|
||||
// Place both bed blocks:
|
||||
return a_Player.PlaceBlocks(blks);
|
||||
}
|
||||
} ;
|
||||
|
||||
|
56
src/Items/ItemBigFlower.h
Normal file
56
src/Items/ItemBigFlower.h
Normal file
@ -0,0 +1,56 @@
|
||||
|
||||
// ItemBigFlower.h
|
||||
|
||||
// Declares the cItemBigFlower class representing the cItemHandler for big flowers
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ItemHandler.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cItemBigFlowerHandler:
|
||||
public cItemHandler
|
||||
{
|
||||
typedef cItemHandler super;
|
||||
|
||||
public:
|
||||
cItemBigFlowerHandler(void):
|
||||
super(E_BLOCK_BIG_FLOWER)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
virtual bool OnPlayerPlace(
|
||||
cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem,
|
||||
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
|
||||
int a_CursorX, int a_CursorY, int a_CursorZ
|
||||
) override
|
||||
{
|
||||
// Can only be placed on the floor:
|
||||
if (a_BlockFace != BLOCK_FACE_TOP)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
|
||||
|
||||
// Place both blocks atomically:
|
||||
sSetBlockVector blks;
|
||||
blks.emplace_back(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_BIG_FLOWER, a_EquippedItem.m_ItemDamage & 0x07);
|
||||
if (a_BlockY < cChunkDef::Height - 1)
|
||||
{
|
||||
blks.emplace_back(a_BlockX, a_BlockY + 1, a_BlockZ, E_BLOCK_BIG_FLOWER, (a_EquippedItem.m_ItemDamage & 0x07) | 0x08);
|
||||
}
|
||||
return a_Player.PlaceBlocks(blks);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
167
src/Items/ItemChest.h
Normal file
167
src/Items/ItemChest.h
Normal file
@ -0,0 +1,167 @@
|
||||
|
||||
// ItemChest.h
|
||||
|
||||
// Declares the cItemChestHandler class representing the cItemHandler descendant responsible for chests
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ItemHandler.h"
|
||||
#include "../Blocks/BlockChest.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cItemChestHandler:
|
||||
public cItemHandler
|
||||
{
|
||||
typedef cItemHandler super;
|
||||
public:
|
||||
cItemChestHandler(int a_ItemType):
|
||||
super(a_ItemType)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
virtual bool OnPlayerPlace(
|
||||
cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem,
|
||||
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
|
||||
int a_CursorX, int a_CursorY, int a_CursorZ
|
||||
) override
|
||||
{
|
||||
if (a_BlockFace < 0)
|
||||
{
|
||||
// Clicked in air
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height))
|
||||
{
|
||||
// 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;
|
||||
a_World.GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlock, ClickedBlockMeta);
|
||||
if (
|
||||
BlockHandler(ClickedBlock)->DoesIgnoreBuildCollision() ||
|
||||
BlockHandler(ClickedBlock)->DoesIgnoreBuildCollision(&a_Player, ClickedBlockMeta)
|
||||
)
|
||||
{
|
||||
cChunkInterface ChunkInterface(a_World.GetChunkMap());
|
||||
BlockHandler(ClickedBlock)->OnDestroyedByPlayer(ChunkInterface, a_World, &a_Player, a_BlockX, a_BlockY, a_BlockZ);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
|
||||
|
||||
if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height))
|
||||
{
|
||||
// The block is being placed outside the world, ignore this packet altogether (#128)
|
||||
return false;
|
||||
}
|
||||
|
||||
NIBBLETYPE PlaceMeta;
|
||||
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(PlaceBlock)->DoesIgnoreBuildCollision() &&
|
||||
!BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision(&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;
|
||||
}
|
||||
}
|
||||
|
||||
// Check that there is at most one single neighbor of the same chest type:
|
||||
static const Vector3i CrossCoords[] =
|
||||
{
|
||||
{-1, 0, 0},
|
||||
{ 0, 0, -1},
|
||||
{ 1, 0, 0},
|
||||
{ 0, 0, 1},
|
||||
};
|
||||
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)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (NeighborIdx >= 0)
|
||||
{
|
||||
// Can't place here, there are already two neighbors, this would form a 3-block chest
|
||||
return false;
|
||||
}
|
||||
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)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
} // for j
|
||||
} // for i
|
||||
|
||||
// If there's no chest neighbor, place the single block chest and bail out:
|
||||
BLOCKTYPE ChestBlockType = static_cast<BLOCKTYPE>(m_ItemType);
|
||||
if (NeighborIdx < 0)
|
||||
{
|
||||
NIBBLETYPE Meta = cBlockChestHandler::PlayerYawToMetaData(a_Player.GetYaw());
|
||||
return a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, ChestBlockType, Meta);
|
||||
}
|
||||
|
||||
// There is a neighbor to which we need to adjust
|
||||
double yaw = a_Player.GetYaw();
|
||||
if ((NeighborIdx == 0) || (NeighborIdx == 2))
|
||||
{
|
||||
// The neighbor is in the X axis, form a X-axis-aligned dblchest:
|
||||
NIBBLETYPE Meta = ((yaw >= -90) && (yaw < 90)) ? E_META_CHEST_FACING_ZM : E_META_CHEST_FACING_ZP;
|
||||
|
||||
// Place the new chest:
|
||||
if (!a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, ChestBlockType, Meta))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Adjust the existing chest:
|
||||
a_World.FastSetBlock(a_BlockX + CrossCoords[NeighborIdx].x, a_BlockY, a_BlockZ + CrossCoords[NeighborIdx].z, ChestBlockType, Meta);
|
||||
return true;
|
||||
}
|
||||
|
||||
// The neighbor is in the Z axis, form a Z-axis-aligned dblchest:
|
||||
NIBBLETYPE Meta = (yaw < 0) ? E_META_CHEST_FACING_XM : E_META_CHEST_FACING_XP;
|
||||
|
||||
// Place the new chest:
|
||||
if (!a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, ChestBlockType, Meta))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Adjust the existing chest:
|
||||
a_World.FastSetBlock(a_BlockX + CrossCoords[NeighborIdx].x, a_BlockY, a_BlockZ + CrossCoords[NeighborIdx].z, ChestBlockType, Meta);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
cItemChestHandler(const cItemChestHandler &) = delete;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include "ItemHandler.h"
|
||||
#include "../World.h"
|
||||
#include "../Blocks/BlockDoor.h"
|
||||
|
||||
|
||||
|
||||
@ -18,27 +19,43 @@ public:
|
||||
|
||||
}
|
||||
|
||||
virtual bool IsPlaceable(void) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool GetPlacementBlockTypeMeta(
|
||||
cWorld * a_World, cPlayer * a_Player,
|
||||
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,
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
int a_CursorX, int a_CursorY, int a_CursorZ
|
||||
) override
|
||||
{
|
||||
// Vanilla only allows door placement while clicking on the top face of the block below the door:
|
||||
if (a_BlockFace != BLOCK_FACE_NONE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
|
||||
|
||||
// Door (bottom block) can be placed in Y range of [1, 254]:
|
||||
if ((a_BlockY < 1) || (a_BlockY + 2 >= cChunkDef::Height))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// The door needs a compatible block below it:
|
||||
if ((a_BlockY > 0) && cBlockDoorHandler::CanBeOn(a_World.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the block type of the door to place:
|
||||
BLOCKTYPE BlockType;
|
||||
switch (m_ItemType)
|
||||
{
|
||||
case E_ITEM_WOODEN_DOOR: a_BlockType = E_BLOCK_WOODEN_DOOR; break;
|
||||
case E_ITEM_IRON_DOOR: a_BlockType = E_BLOCK_IRON_DOOR; break;
|
||||
case E_ITEM_SPRUCE_DOOR: a_BlockType = E_BLOCK_SPRUCE_DOOR; break;
|
||||
case E_ITEM_BIRCH_DOOR: a_BlockType = E_BLOCK_BIRCH_DOOR; break;
|
||||
case E_ITEM_JUNGLE_DOOR: a_BlockType = E_BLOCK_JUNGLE_DOOR; break;
|
||||
case E_ITEM_DARK_OAK_DOOR: a_BlockType = E_BLOCK_DARK_OAK_DOOR; break;
|
||||
case E_ITEM_ACACIA_DOOR: a_BlockType = E_BLOCK_ACACIA_DOOR; break;
|
||||
case E_ITEM_WOODEN_DOOR: BlockType = E_BLOCK_WOODEN_DOOR; break;
|
||||
case E_ITEM_IRON_DOOR: BlockType = E_BLOCK_IRON_DOOR; break;
|
||||
case E_ITEM_SPRUCE_DOOR: BlockType = E_BLOCK_SPRUCE_DOOR; break;
|
||||
case E_ITEM_BIRCH_DOOR: BlockType = E_BLOCK_BIRCH_DOOR; break;
|
||||
case E_ITEM_JUNGLE_DOOR: BlockType = E_BLOCK_JUNGLE_DOOR; break;
|
||||
case E_ITEM_DARK_OAK_DOOR: BlockType = E_BLOCK_DARK_OAK_DOOR; break;
|
||||
case E_ITEM_ACACIA_DOOR: BlockType = E_BLOCK_ACACIA_DOOR; break;
|
||||
default:
|
||||
{
|
||||
ASSERT(!"Unhandled door type");
|
||||
@ -46,14 +63,47 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
cChunkInterface ChunkInterface(a_World->GetChunkMap());
|
||||
bool Meta = BlockHandler(a_BlockType)->GetPlacementBlockTypeMeta(
|
||||
ChunkInterface, a_Player,
|
||||
a_BlockX, a_BlockY, a_BlockZ, a_BlockFace,
|
||||
a_CursorX, a_CursorY, a_CursorZ,
|
||||
a_BlockType, a_BlockMeta
|
||||
);
|
||||
return Meta;
|
||||
// Check the two blocks that will get replaced by the door:
|
||||
BLOCKTYPE LowerBlockType = a_World.GetBlock(a_BlockX, a_BlockY + 1, a_BlockZ);
|
||||
BLOCKTYPE UpperBlockType = a_World.GetBlock(a_BlockX, a_BlockY + 2, a_BlockZ);
|
||||
if (
|
||||
!cBlockDoorHandler::CanReplaceBlock(LowerBlockType) ||
|
||||
!cBlockDoorHandler::CanReplaceBlock(UpperBlockType))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the coords of the neighboring blocks:
|
||||
NIBBLETYPE LowerBlockMeta = cBlockDoorHandler::PlayerYawToMetaData(a_Player.GetYaw());
|
||||
Vector3i RelDirToOutside = cBlockDoorHandler::GetRelativeDirectionToOutside(LowerBlockMeta);
|
||||
Vector3i LeftNeighborPos = RelDirToOutside;
|
||||
LeftNeighborPos.TurnCCW();
|
||||
LeftNeighborPos.Move(a_BlockX, a_BlockY, a_BlockZ);
|
||||
Vector3i RightNeighborPos = RelDirToOutside;
|
||||
RightNeighborPos.TurnCW();
|
||||
RightNeighborPos.Move(a_BlockX, a_BlockY, a_BlockZ);
|
||||
|
||||
// Decide whether the hinge is on the left (default) or on the right:
|
||||
NIBBLETYPE UpperBlockMeta = 0x08;
|
||||
if (
|
||||
cBlockDoorHandler::IsDoorBlockType(a_World.GetBlock(LeftNeighborPos)) || // The block to the left is a door block
|
||||
cBlockInfo::IsSolid(a_World.GetBlock(RightNeighborPos)) // The block to the right is solid
|
||||
)
|
||||
{
|
||||
UpperBlockMeta = 0x09; // Upper block | hinge on right
|
||||
}
|
||||
|
||||
// Set the blocks:
|
||||
sSetBlockVector blks;
|
||||
blks.emplace_back(a_BlockX, a_BlockY, a_BlockZ, BlockType, LowerBlockMeta);
|
||||
blks.emplace_back(a_BlockX, a_BlockY + 1, a_BlockZ, BlockType, UpperBlockMeta);
|
||||
return a_Player.PlaceBlocks(blks);
|
||||
}
|
||||
|
||||
|
||||
virtual bool IsPlaceable(void) override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
} ;
|
||||
|
||||
|
@ -55,26 +55,17 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check plugins
|
||||
if (cRoot::Get()->GetPluginManager()->CallHookPlayerPlacingBlock(*a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, 0, 0, 0, E_BLOCK_COCOA_POD, BlockMeta))
|
||||
// Place the cocoa pod:
|
||||
if (a_Player->PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_COCOA_POD, BlockMeta))
|
||||
{
|
||||
a_World->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, a_Player);
|
||||
a_Player->GetInventory().SendEquippedSlot();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set block and broadcast place sound
|
||||
a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_COCOA_POD, BlockMeta);
|
||||
a_World->BroadcastSoundEffect("dig.stone", a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, 1.0f, 0.8f);
|
||||
|
||||
// Remove one cocoa pod from the inventory
|
||||
if (!a_Player->IsGameModeCreative())
|
||||
if (a_Player->IsGameModeSurvival())
|
||||
{
|
||||
a_Player->GetInventory().RemoveOneEquippedItem();
|
||||
}
|
||||
cRoot::Get()->GetPluginManager()->CallHookPlayerPlacedBlock(*a_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, 0, 0, 0, E_BLOCK_COCOA_POD, BlockMeta);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -6,39 +6,43 @@
|
||||
#include "../Entities/Player.h"
|
||||
#include "../FastRandom.h"
|
||||
#include "../BlockInServerPluginInterface.h"
|
||||
#include "../Chunk.h"
|
||||
|
||||
// Handlers:
|
||||
#include "ItemArmor.h"
|
||||
#include "ItemBed.h"
|
||||
#include "ItemBigFlower.h"
|
||||
#include "ItemBoat.h"
|
||||
#include "ItemBow.h"
|
||||
#include "ItemBrewingStand.h"
|
||||
#include "ItemBucket.h"
|
||||
#include "ItemCake.h"
|
||||
#include "ItemCauldron.h"
|
||||
#include "ItemChest.h"
|
||||
#include "ItemCloth.h"
|
||||
#include "ItemComparator.h"
|
||||
#include "ItemDoor.h"
|
||||
#include "ItemMilk.h"
|
||||
#include "ItemDye.h"
|
||||
#include "ItemEmptyMap.h"
|
||||
#include "ItemFishingRod.h"
|
||||
#include "ItemFlowerPot.h"
|
||||
#include "ItemFood.h"
|
||||
#include "ItemGoldenApple.h"
|
||||
#include "ItemItemFrame.h"
|
||||
#include "ItemHoe.h"
|
||||
#include "ItemItemFrame.h"
|
||||
#include "ItemLeaves.h"
|
||||
#include "ItemLighter.h"
|
||||
#include "ItemLilypad.h"
|
||||
#include "ItemMap.h"
|
||||
#include "ItemMilk.h"
|
||||
#include "ItemMinecart.h"
|
||||
#include "ItemMobHead.h"
|
||||
#include "ItemMushroomSoup.h"
|
||||
#include "ItemNetherWart.h"
|
||||
#include "ItemPainting.h"
|
||||
#include "ItemPickaxe.h"
|
||||
#include "ItemPotion.h"
|
||||
#include "ItemThrowable.h"
|
||||
#include "ItemPumpkin.h"
|
||||
#include "ItemRedstoneDust.h"
|
||||
#include "ItemRedstoneRepeater.h"
|
||||
#include "ItemSapling.h"
|
||||
@ -46,11 +50,12 @@
|
||||
#include "ItemShears.h"
|
||||
#include "ItemShovel.h"
|
||||
#include "ItemSign.h"
|
||||
#include "ItemMobHead.h"
|
||||
#include "ItemSlab.h"
|
||||
#include "ItemSpawnEgg.h"
|
||||
#include "ItemString.h"
|
||||
#include "ItemSugarcane.h"
|
||||
#include "ItemSword.h"
|
||||
#include "ItemThrowable.h"
|
||||
|
||||
#include "../Blocks/BlockHandler.h"
|
||||
|
||||
@ -101,9 +106,15 @@ cItemHandler *cItemHandler::CreateItemHandler(int a_ItemType)
|
||||
default: return new cItemHandler(a_ItemType);
|
||||
|
||||
// Single item per handler, alphabetically sorted:
|
||||
case E_BLOCK_CHEST: return new cItemChestHandler(a_ItemType);
|
||||
case E_BLOCK_LEAVES: return new cItemLeavesHandler(a_ItemType);
|
||||
case E_BLOCK_HEAD: return new cItemMobHeadHandler(a_ItemType);
|
||||
case E_BLOCK_NEW_LEAVES: return new cItemLeavesHandler(a_ItemType);
|
||||
case E_BLOCK_PUMPKIN: return new cItemPumpkinHandler;
|
||||
case E_BLOCK_SAPLING: return new cItemSaplingHandler(a_ItemType);
|
||||
case E_BLOCK_STONE_SLAB: return new cItemSlabHandler(E_BLOCK_STONE_SLAB, E_BLOCK_DOUBLE_STONE_SLAB);
|
||||
case E_BLOCK_TRAPPED_CHEST: return new cItemChestHandler(a_ItemType);
|
||||
case E_BLOCK_WOODEN_SLAB: return new cItemSlabHandler(E_BLOCK_WOODEN_SLAB, E_BLOCK_DOUBLE_WOODEN_SLAB);
|
||||
case E_BLOCK_WOOL: return new cItemClothHandler(a_ItemType);
|
||||
case E_ITEM_BED: return new cItemBedHandler(a_ItemType);
|
||||
case E_ITEM_BOAT: return new cItemBoatHandler(a_ItemType);
|
||||
@ -297,6 +308,108 @@ 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
|
||||
)
|
||||
{
|
||||
if (a_BlockFace < 0)
|
||||
{
|
||||
// Clicked in air
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height))
|
||||
{
|
||||
// The clicked block is outside the world, ignore this call altogether (#128)
|
||||
return false;
|
||||
}
|
||||
|
||||
BLOCKTYPE ClickedBlock;
|
||||
NIBBLETYPE ClickedBlockMeta;
|
||||
|
||||
a_World.GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlock, ClickedBlockMeta);
|
||||
|
||||
// Check if the block ignores build collision (water, grass etc.):
|
||||
if (
|
||||
BlockHandler(ClickedBlock)->DoesIgnoreBuildCollision() ||
|
||||
BlockHandler(ClickedBlock)->DoesIgnoreBuildCollision(&a_Player, ClickedBlockMeta)
|
||||
)
|
||||
{
|
||||
cChunkInterface ChunkInterface(a_World.GetChunkMap());
|
||||
BlockHandler(ClickedBlock)->OnDestroyedByPlayer(ChunkInterface, a_World, &a_Player, a_BlockX, a_BlockY, a_BlockZ);
|
||||
}
|
||||
else
|
||||
{
|
||||
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
|
||||
|
||||
if ((a_BlockY < 0) || (a_BlockY >= cChunkDef::Height))
|
||||
{
|
||||
// The block is being placed outside the world, ignore this packet altogether (#128)
|
||||
return false;
|
||||
}
|
||||
|
||||
NIBBLETYPE PlaceMeta;
|
||||
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(PlaceBlock)->DoesIgnoreBuildCollision() &&
|
||||
!BlockHandler(PlaceBlock)->DoesIgnoreBuildCollision(&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;
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
{
|
||||
// Handler refused the placement, send that information back to the client:
|
||||
a_World.SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, &a_Player);
|
||||
a_Player.GetInventory().SendEquippedSlot();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta))
|
||||
{
|
||||
// The placement failed, the block has already been re-sent, re-send inventory:
|
||||
a_Player.GetInventory().SendEquippedSlot();
|
||||
return false;
|
||||
}
|
||||
|
||||
AString PlaceSound = cBlockInfo::GetPlaceSound(BlockType);
|
||||
float Volume = 1.0f, Pitch = 0.8f;
|
||||
if (PlaceSound == "dig.metal")
|
||||
{
|
||||
Pitch = 1.2f;
|
||||
PlaceSound = "dig.stone";
|
||||
}
|
||||
else if (PlaceSound == "random.anvil_land")
|
||||
{
|
||||
Volume = 0.65f;
|
||||
}
|
||||
|
||||
a_World.BroadcastSoundEffect(PlaceSound, a_BlockX + 0.5, a_BlockY + 0.5, a_BlockZ + 0.5, Volume, Pitch);
|
||||
|
||||
// Remove the "placed" item:
|
||||
if (a_Player.IsGameModeSurvival())
|
||||
{
|
||||
a_Player.GetInventory().RemoveOneEquippedItem();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
bool cItemHandler::OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir)
|
||||
{
|
||||
UNUSED(a_World);
|
||||
|
@ -32,9 +32,34 @@ public:
|
||||
/** Force virtual destructor */
|
||||
virtual ~cItemHandler() {}
|
||||
|
||||
|
||||
/** Called when the player tries to place the item (right mouse button, IsPlaceable() == true).
|
||||
The default handler uses GetPlacementBlockTypeMeta and places the returned block.
|
||||
Override this function for advanced behavior such as placing multiple blocks.
|
||||
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
|
||||
);
|
||||
|
||||
|
||||
/** Called when the player right-clicks with this item and IsPlaceable() == true, and OnPlace() is not overridden.
|
||||
This function should provide the block type and meta for the placed block, or refuse the placement.
|
||||
Returns true to allow placement, false to refuse. */
|
||||
virtual bool GetPlacementBlockTypeMeta(
|
||||
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,
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
);
|
||||
|
||||
|
||||
/** Called when the player tries to use the item (right mouse button). Return false to make the item unusable. DEFAULT: False */
|
||||
virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir);
|
||||
|
||||
|
||||
/** 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)
|
||||
{
|
||||
@ -106,18 +131,8 @@ public:
|
||||
/** Can the anvil repair this item, when a_Item is the second input? */
|
||||
virtual bool CanRepairWithRawMaterial(short a_ItemType);
|
||||
|
||||
/** Called before a block is placed into a world.
|
||||
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.
|
||||
*/
|
||||
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,
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
);
|
||||
|
||||
/** Returns whether this tool/item can harvest a specific block (e.g. wooden pickaxe can harvest stone, but wood can't) DEFAULT: False */
|
||||
/** Returns whether this tool / item can harvest a specific block (e.g. iron pickaxe can harvest diamond ore, but wooden one can't).
|
||||
Defaults to false unless overridden. */
|
||||
virtual bool CanHarvestBlock(BLOCKTYPE a_BlockType);
|
||||
|
||||
static cItemHandler * GetItemHandler(int a_ItemType);
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include "ItemHandler.h"
|
||||
#include "../World.h"
|
||||
#include "../BlockEntities/MobHeadEntity.h"
|
||||
|
||||
|
||||
|
||||
@ -18,6 +19,266 @@ 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
|
||||
) override
|
||||
{
|
||||
// Cannot place a head at "no face" and from the bottom:
|
||||
if ((a_BlockFace == BLOCK_FACE_NONE) || (a_BlockFace == BLOCK_FACE_BOTTOM))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
|
||||
|
||||
// If the placed head is a wither, try to spawn the wither first:
|
||||
if (a_EquippedItem.m_ItemDamage == E_META_HEAD_WITHER)
|
||||
{
|
||||
if (TrySpawnWitherAround(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// Wither not created, proceed with regular head placement
|
||||
}
|
||||
|
||||
return PlaceRegularHead(a_World, a_Player, a_EquippedItem, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
|
||||
}
|
||||
|
||||
|
||||
/** Places a regular head block with no mob spawning checking. */
|
||||
bool PlaceRegularHead(
|
||||
cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem,
|
||||
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace
|
||||
)
|
||||
{
|
||||
// Place the block:
|
||||
if (!a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_HEAD, static_cast<NIBBLETYPE>(a_EquippedItem.m_ItemType)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Use a callback to set the properties of the mob head block entity:
|
||||
class cCallback : public cBlockEntityCallback
|
||||
{
|
||||
cPlayer & m_Player;
|
||||
eMobHeadType m_HeadType;
|
||||
NIBBLETYPE m_BlockMeta;
|
||||
|
||||
virtual bool Item(cBlockEntity * a_BlockEntity)
|
||||
{
|
||||
if (a_BlockEntity->GetBlockType() != E_BLOCK_HEAD)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
cMobHeadEntity * MobHeadEntity = static_cast<cMobHeadEntity *>(a_BlockEntity);
|
||||
|
||||
int Rotation = 0;
|
||||
if (m_BlockMeta == 1)
|
||||
{
|
||||
Rotation = FloorC(m_Player.GetYaw() * 16.0f / 360.0f + 0.5f) & 0x0f;
|
||||
}
|
||||
|
||||
MobHeadEntity->SetType(m_HeadType);
|
||||
MobHeadEntity->SetRotation(static_cast<eMobHeadRotation>(Rotation));
|
||||
MobHeadEntity->GetWorld()->BroadcastBlockEntity(MobHeadEntity->GetPosX(), MobHeadEntity->GetPosY(), MobHeadEntity->GetPosZ());
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
cCallback (cPlayer & a_CBPlayer, eMobHeadType a_HeadType, NIBBLETYPE a_BlockMeta) :
|
||||
m_Player(a_CBPlayer),
|
||||
m_HeadType(a_HeadType),
|
||||
m_BlockMeta(a_BlockMeta)
|
||||
{}
|
||||
};
|
||||
cCallback Callback(a_Player, static_cast<eMobHeadType>(a_EquippedItem.m_ItemType), static_cast<NIBBLETYPE>(a_BlockFace));
|
||||
a_World.DoWithBlockEntityAt(a_BlockX, a_BlockY, a_BlockZ, Callback);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/** 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,
|
||||
int a_BlockX, int a_BlockY, int a_BlockZ
|
||||
)
|
||||
{
|
||||
// No wither can be created at Y < 2 - not enough space for the formula:
|
||||
if (a_BlockY < 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for all relevant wither locations:
|
||||
static const Vector3i RelCoords[] =
|
||||
{
|
||||
{ 0, 0, 0},
|
||||
{ 1, 0, 0},
|
||||
{-1, 0, 0},
|
||||
{ 0, 0, 1},
|
||||
{ 0, 0, -1},
|
||||
};
|
||||
for (size_t i = 0; i < ARRAYCOUNT(RelCoords); ++i)
|
||||
{
|
||||
if (TrySpawnWitherAt(
|
||||
a_World, a_Player,
|
||||
a_BlockX, a_BlockY, a_BlockZ,
|
||||
RelCoords[i].x, RelCoords[i].z
|
||||
))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
} // for i - Coords[]
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/** Tries to spawn a wither 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.
|
||||
Offset is used to shift the image around the X and Z axis.
|
||||
Returns true iff the wither was created successfully. */
|
||||
bool TrySpawnWitherAt(
|
||||
cWorld & a_World, cPlayer & a_Player,
|
||||
int a_PlacedHeadX, int a_PlacedHeadY, int a_PlacedHeadZ,
|
||||
int a_OffsetX, int a_OffsetZ
|
||||
)
|
||||
{
|
||||
// Image for the wither at the X axis:
|
||||
static const sSetBlock ImageWitherX[] =
|
||||
{
|
||||
{-1, 0, 0, E_BLOCK_HEAD, E_META_HEAD_WITHER},
|
||||
{ 0, 0, 0, E_BLOCK_HEAD, E_META_HEAD_WITHER},
|
||||
{ 1, 0, 0, E_BLOCK_HEAD, E_META_HEAD_WITHER},
|
||||
{-1, -1, 0, E_BLOCK_SOULSAND, 0},
|
||||
{ 0, -1, 0, E_BLOCK_SOULSAND, 0},
|
||||
{ 1, -1, 0, E_BLOCK_SOULSAND, 0},
|
||||
{-1, -2, 0, E_BLOCK_AIR, 0},
|
||||
{ 0, -2, 0, E_BLOCK_SOULSAND, 0},
|
||||
{ 1, -2, 0, E_BLOCK_AIR, 0},
|
||||
};
|
||||
|
||||
// Image for the wither at the Z axis:
|
||||
static const sSetBlock ImageWitherZ[] =
|
||||
{
|
||||
{ 0, 0, -1, E_BLOCK_HEAD, E_META_HEAD_WITHER},
|
||||
{ 0, 0, 0, E_BLOCK_HEAD, E_META_HEAD_WITHER},
|
||||
{ 0, 0, 1, E_BLOCK_HEAD, E_META_HEAD_WITHER},
|
||||
{ 0, -1, -1, E_BLOCK_SOULSAND, 0},
|
||||
{ 0, -1, 0, E_BLOCK_SOULSAND, 0},
|
||||
{ 0, -1, 1, E_BLOCK_SOULSAND, 0},
|
||||
{ 0, -2, -1, E_BLOCK_AIR, 0},
|
||||
{ 0, -2, 0, E_BLOCK_SOULSAND, 0},
|
||||
{ 0, -2, 1, E_BLOCK_AIR, 0},
|
||||
};
|
||||
|
||||
// Try to spawn the wither from each image:
|
||||
return (
|
||||
TrySpawnWitherFromImage(
|
||||
a_World, a_Player, ImageWitherX, ARRAYCOUNT(ImageWitherX),
|
||||
a_PlacedHeadX, a_PlacedHeadY, a_PlacedHeadZ,
|
||||
a_OffsetX, a_OffsetZ
|
||||
) ||
|
||||
TrySpawnWitherFromImage(
|
||||
a_World, a_Player, ImageWitherZ, ARRAYCOUNT(ImageWitherZ),
|
||||
a_PlacedHeadX, a_PlacedHeadY, a_PlacedHeadZ,
|
||||
a_OffsetX, a_OffsetZ
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/** 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.
|
||||
Offset is used to shift the image around the X and Z axis.
|
||||
Returns true iff the wither was created successfully. */
|
||||
bool TrySpawnWitherFromImage(
|
||||
cWorld & a_World, cPlayer & a_Player, const sSetBlock * a_Image, size_t a_ImageCount,
|
||||
int a_PlacedHeadX, int a_PlacedHeadY, int a_PlacedHeadZ,
|
||||
int a_OffsetX, int a_OffsetZ
|
||||
)
|
||||
{
|
||||
// Check each block individually; simultaneously build the SetBlockVector for clearing the blocks:
|
||||
sSetBlockVector AirBlocks;
|
||||
AirBlocks.reserve(a_ImageCount);
|
||||
for (size_t i = 0; i < a_ImageCount; i++)
|
||||
{
|
||||
// Get the absolute coords of the image:
|
||||
int BlockX = a_PlacedHeadX + a_OffsetX + a_Image[i].m_RelX;
|
||||
int BlockY = a_PlacedHeadY + a_Image[i].m_RelY;
|
||||
int BlockZ = a_PlacedHeadZ + a_OffsetZ + a_Image[i].m_RelZ;
|
||||
|
||||
// If the query is for the placed head, short-circuit-evaluate it:
|
||||
if ((BlockX == a_PlacedHeadX) && (BlockY == a_PlacedHeadY) && (BlockZ == a_PlacedHeadZ))
|
||||
{
|
||||
if ((a_Image[i].m_BlockType != E_BLOCK_HEAD) || (a_Image[i].m_BlockMeta != E_META_HEAD_WITHER))
|
||||
{
|
||||
return false; // Didn't match
|
||||
}
|
||||
continue; // Matched, continue checking the rest of the image
|
||||
}
|
||||
|
||||
// Query the world block:
|
||||
BLOCKTYPE BlockType;
|
||||
NIBBLETYPE BlockMeta;
|
||||
if (!a_World.GetBlockTypeMeta(BlockX, BlockY, BlockZ, BlockType, BlockMeta))
|
||||
{
|
||||
// Cannot query block, assume unloaded chunk, fail to spawn the wither
|
||||
return false;
|
||||
}
|
||||
|
||||
// Compare the world block:
|
||||
if ((BlockType != a_Image[i].m_BlockType) || (BlockMeta != a_Image[i].m_BlockMeta))
|
||||
{
|
||||
return false; // Didn't match
|
||||
}
|
||||
// Matched, continue checking
|
||||
} // for i - a_Image
|
||||
|
||||
// All image blocks matched, try place the wither:
|
||||
if (!a_Player.PlaceBlocks(AirBlocks))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Spawn the wither:
|
||||
int BlockX = a_PlacedHeadX + a_OffsetX;
|
||||
int BlockZ = a_PlacedHeadZ + a_OffsetZ;
|
||||
a_World.SpawnMob(static_cast<double>(BlockX) + 0.5, a_PlacedHeadY - 2, static_cast<double>(BlockZ) + 0.5, mtWither);
|
||||
AwardSpawnWitherAchievement(a_World, BlockX, a_PlacedHeadY - 2, BlockZ);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/** Awards the achievement to all players close to the specified point. */
|
||||
void AwardSpawnWitherAchievement(cWorld & a_World, int a_BlockX, int a_BlockY, int a_BlockZ)
|
||||
{
|
||||
class cPlayerCallback : public cPlayerListCallback
|
||||
{
|
||||
Vector3f m_Pos;
|
||||
|
||||
virtual bool Item(cPlayer * a_Player)
|
||||
{
|
||||
// If player is close, award achievement:
|
||||
double Dist = (a_Player->GetPosition() - m_Pos).Length();
|
||||
if (Dist < 50.0)
|
||||
{
|
||||
a_Player->AwardAchievement(achSpawnWither);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public:
|
||||
cPlayerCallback(const Vector3f & a_Pos) : m_Pos(a_Pos) {}
|
||||
} PlayerCallback(Vector3f(static_cast<float>(a_BlockX), static_cast<float>(a_BlockY), static_cast<float>(a_BlockZ)));
|
||||
a_World.ForEachPlayer(PlayerCallback);
|
||||
}
|
||||
|
||||
|
||||
virtual bool IsPlaceable(void) override
|
||||
{
|
||||
return true;
|
||||
|
156
src/Items/ItemPumpkin.h
Normal file
156
src/Items/ItemPumpkin.h
Normal file
@ -0,0 +1,156 @@
|
||||
|
||||
// ItemPumpkin.h
|
||||
|
||||
// Declares the cItemPumpkinHandler class representing the pumpkin block in its item form
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ItemHandler.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cItemPumpkinHandler:
|
||||
public cItemHandler
|
||||
{
|
||||
typedef cItemHandler super;
|
||||
|
||||
public:
|
||||
cItemPumpkinHandler(void):
|
||||
super(E_BLOCK_PUMPKIN)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
virtual bool OnPlayerPlace(
|
||||
cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem,
|
||||
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
|
||||
int a_CursorX, int a_CursorY, int a_CursorZ
|
||||
) override
|
||||
{
|
||||
// First try spawning a snow golem or an iron golem:
|
||||
int PlacedBlockX = a_BlockX;
|
||||
int PlacedBlockY = a_BlockY;
|
||||
int PlacedBlockZ = a_BlockZ;
|
||||
AddFaceDirection(PlacedBlockX, PlacedBlockY, PlacedBlockZ, a_BlockFace);
|
||||
if (TrySpawnGolem(a_World, a_Player, PlacedBlockX, PlacedBlockY, PlacedBlockZ))
|
||||
{
|
||||
// The client thinks that they placed the pumpkin, let them know it's been replaced:
|
||||
a_Player.SendBlocksAround(PlacedBlockX, PlacedBlockY, PlacedBlockZ);
|
||||
return true;
|
||||
}
|
||||
|
||||
// No golem at these coords, place the block normally:
|
||||
return super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ);
|
||||
}
|
||||
|
||||
|
||||
/** Spawns a snow / iron golem if the shape matches the recipe, supposing that the block placed at the specified coords is a pumpkin.
|
||||
Returns true if the golem blocks are removed (for spawning), false if the recipe is not matched. */
|
||||
bool TrySpawnGolem(cWorld & a_World, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ)
|
||||
{
|
||||
// A golem can't form with a pumpkin below level 2 or above level 255
|
||||
if ((a_BlockY < 2) || (a_BlockY >= cChunkDef::Height))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Decide which golem to try spawning based on the block below the placed pumpkin:
|
||||
switch (a_World.GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ))
|
||||
{
|
||||
case E_BLOCK_SNOW_BLOCK: return TrySpawnSnowGolem(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ);
|
||||
case E_BLOCK_IRON_BLOCK: return TrySpawnIronGolem(a_World, a_Player, a_BlockX, a_BlockY, a_BlockZ);
|
||||
default:
|
||||
{
|
||||
// No golem here
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Spawns a snow golem if the shape matches the recipe, supposing that the block placed at the specified coords is a pumpkin.
|
||||
Returns true if the golem blocks are removed (for spawning), false if the recipe is not matched.
|
||||
Assumes that the block below the specified block has already been checked and is a snow block. */
|
||||
bool TrySpawnSnowGolem(cWorld & a_World, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ)
|
||||
{
|
||||
// Need one more snow block 2 blocks below the pumpkin:
|
||||
if (a_World.GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ) != E_BLOCK_SNOW_BLOCK)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try to place air blocks where the original recipe blocks were:
|
||||
sSetBlockVector AirBlocks;
|
||||
AirBlocks.emplace_back(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); // Head
|
||||
AirBlocks.emplace_back(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); // Torso
|
||||
AirBlocks.emplace_back(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); // Legs
|
||||
if (!a_Player.PlaceBlocks(AirBlocks))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Spawn the golem:
|
||||
a_World.SpawnMob(static_cast<double>(a_BlockX) + 0.5, a_BlockY - 2, static_cast<double>(a_BlockZ) + 0.5, mtSnowGolem);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/** Spawns an iron golem if the shape matches the recipe, supposing that the block placed at the specified coords is a pumpkin.
|
||||
Returns true if the golem blocks are removed (for spawning), false if the recipe is not matched.
|
||||
Assumes that the block below the specified block has already been checked and is an iron block. */
|
||||
bool TrySpawnIronGolem(cWorld & a_World, cPlayer & a_Player, int a_BlockX, int a_BlockY, int a_BlockZ)
|
||||
{
|
||||
// Need one more iron block 2 blocks below the pumpkin:
|
||||
if (a_World.GetBlock(a_BlockX, a_BlockY - 2, a_BlockZ) != E_BLOCK_IRON_BLOCK)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check the two arm directions (X, Z) using a loop over two sets of offset vectors:
|
||||
static const Vector3i ArmOffsets[] =
|
||||
{
|
||||
{1, 0, 0},
|
||||
{0, 0, 1},
|
||||
};
|
||||
for (size_t i = 0; i < ARRAYCOUNT(ArmOffsets); i++)
|
||||
{
|
||||
// If the arm blocks don't match, bail out of this loop repetition:
|
||||
if (
|
||||
(a_World.GetBlock(a_BlockX + ArmOffsets[i].x, a_BlockY - 1, a_BlockZ + ArmOffsets[i].z) != E_BLOCK_IRON_BLOCK) ||
|
||||
(a_World.GetBlock(a_BlockX - ArmOffsets[i].x, a_BlockY - 1, a_BlockZ - ArmOffsets[i].z) != E_BLOCK_IRON_BLOCK)
|
||||
)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try to place air blocks where the original recipe blocks were:
|
||||
sSetBlockVector AirBlocks;
|
||||
AirBlocks.emplace_back(a_BlockX, a_BlockY, a_BlockZ, E_BLOCK_AIR, 0); // Head
|
||||
AirBlocks.emplace_back(a_BlockX, a_BlockY - 1, a_BlockZ, E_BLOCK_AIR, 0); // Torso
|
||||
AirBlocks.emplace_back(a_BlockX, a_BlockY - 2, a_BlockZ, E_BLOCK_AIR, 0); // Legs
|
||||
AirBlocks.emplace_back(a_BlockX + ArmOffsets[i].x, a_BlockY - 1, a_BlockZ + ArmOffsets[i].z, E_BLOCK_AIR, 0); // Arm
|
||||
AirBlocks.emplace_back(a_BlockX - ArmOffsets[i].x, a_BlockY - 1, a_BlockZ - ArmOffsets[i].z, E_BLOCK_AIR, 0); // Arm
|
||||
if (!a_Player.PlaceBlocks(AirBlocks))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Spawn the golem:
|
||||
a_World.SpawnMob(static_cast<double>(a_BlockX) + 0.5, a_BlockY - 2, static_cast<double>(a_BlockZ) + 0.5, mtIronGolem);
|
||||
return true;
|
||||
} // for i - ArmOffsets[]
|
||||
|
||||
// Neither arm offset matched, this thing is not a complete golem
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
@ -27,7 +27,20 @@ public:
|
||||
BLOCKTYPE & a_BlockType, NIBBLETYPE & a_BlockMeta
|
||||
) override
|
||||
{
|
||||
if (!cBlockInfo::FullyOccupiesVoxel(a_World->GetBlock(a_BlockX, a_BlockY - 1, a_BlockZ))) // Some solid blocks, such as cocoa beans, are not suitable for dust
|
||||
// Check if coords are out of range:
|
||||
if ((a_BlockY <= 0) || (a_BlockY >= cChunkDef::Height))
|
||||
{
|
||||
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))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!IsBlockTypeUnderSuitable(BlockType, BlockMeta))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -36,6 +49,28 @@ public:
|
||||
a_BlockMeta = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/** 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)
|
||||
{
|
||||
if (cBlockInfo::FullyOccupiesVoxel(a_BlockType))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (a_BlockType)
|
||||
{
|
||||
case E_BLOCK_NEW_STONE_SLAB:
|
||||
case E_BLOCK_WOODEN_SLAB:
|
||||
case E_BLOCK_STONE_SLAB:
|
||||
{
|
||||
// Slabs can support redstone if they're upside down:
|
||||
return ((a_BlockMeta & 0x08) != 0);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} ;
|
||||
|
||||
|
||||
|
@ -13,13 +13,33 @@
|
||||
class cItemSignHandler :
|
||||
public cItemHandler
|
||||
{
|
||||
typedef cItemHandler super;
|
||||
public:
|
||||
cItemSignHandler(int a_ItemType) :
|
||||
cItemHandler(a_ItemType)
|
||||
super(a_ItemType)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
)
|
||||
{
|
||||
// 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))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// After successfully placing the sign, open the sign editor for the player:
|
||||
AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
|
||||
a_Player.GetClientHandle()->SendEditSign(a_BlockX, a_BlockY, a_BlockZ);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
virtual bool IsPlaceable(void) override
|
||||
{
|
||||
return true;
|
||||
|
93
src/Items/ItemSlab.h
Normal file
93
src/Items/ItemSlab.h
Normal file
@ -0,0 +1,93 @@
|
||||
|
||||
// ItemSlab.h
|
||||
|
||||
// Declares the cItemSlabHandler responsible for handling slabs, when in their item form.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ItemHandler.h"
|
||||
#include "../Blocks/BlockSlab.h"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class cItemSlabHandler:
|
||||
public cItemHandler
|
||||
{
|
||||
typedef cItemHandler super;
|
||||
|
||||
public:
|
||||
|
||||
/** Creates a new handler for the specified slab item type.
|
||||
Sets the handler to use the specified doubleslab block type for combining self into doubleslabs. */
|
||||
cItemSlabHandler(int a_ItemType, BLOCKTYPE a_DoubleSlabBlockType):
|
||||
super(a_ItemType),
|
||||
m_DoubleSlabBlockType(a_DoubleSlabBlockType)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
// cItemHandler overrides:
|
||||
virtual bool OnPlayerPlace(
|
||||
cWorld & a_World, cPlayer & a_Player, const cItem & a_EquippedItem,
|
||||
int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace,
|
||||
int a_CursorX, int a_CursorY, int a_CursorZ
|
||||
) override
|
||||
{
|
||||
// Special slab handling - placing a slab onto another slab produces a dblslab instead:
|
||||
BLOCKTYPE ClickedBlockType;
|
||||
NIBBLETYPE ClickedBlockMeta;
|
||||
a_World.GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, ClickedBlockType, ClickedBlockMeta);
|
||||
if (
|
||||
(ClickedBlockType == m_ItemType) && // Placing the same slab material
|
||||
(ClickedBlockMeta == a_EquippedItem.m_ItemDamage) // Placing the same slab sub-kind (and existing slab is single)
|
||||
)
|
||||
{
|
||||
// If clicking the top side of a bottom-half slab, combine into a doubleslab:
|
||||
if (
|
||||
(a_BlockFace == BLOCK_FACE_TOP) &&
|
||||
((ClickedBlockMeta & 0x08) == 0)
|
||||
)
|
||||
{
|
||||
return a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, m_DoubleSlabBlockType, ClickedBlockMeta & 0x07);
|
||||
}
|
||||
|
||||
// If clicking the bottom side of a top-half slab, combine into a doubleslab:
|
||||
if (
|
||||
(a_BlockFace == BLOCK_FACE_BOTTOM) &&
|
||||
((ClickedBlockMeta & 0x08) != 0)
|
||||
)
|
||||
{
|
||||
return a_Player.PlaceBlock(a_BlockX, a_BlockY, a_BlockZ, m_DoubleSlabBlockType, ClickedBlockMeta & 0x07);
|
||||
}
|
||||
}
|
||||
|
||||
// The slabs didn't combine, use the default handler to place the slab:
|
||||
bool res = super::OnPlayerPlace(a_World, a_Player, a_EquippedItem, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ);
|
||||
|
||||
/*
|
||||
The client has a bug when a slab replaces snow and there's a slab above it.
|
||||
The client then combines the slab above, rather than replacing the snow.
|
||||
We send the block above the currently placed block back to the client to fix the bug.
|
||||
Ref.: http://forum.mc-server.org/showthread.php?tid=434&pid=17388#pid17388
|
||||
*/
|
||||
if ((a_BlockFace == BLOCK_FACE_TOP) && (a_BlockY < cChunkDef::Height - 1))
|
||||
{
|
||||
a_Player.SendBlocksAround(a_BlockX, a_BlockY + 1, a_BlockZ, 1);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
protected:
|
||||
/** The block type to use when the slab combines into a doubleslab block. */
|
||||
BLOCKTYPE m_DoubleSlabBlockType;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
@ -228,8 +228,8 @@ void cProtocol172::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockV
|
||||
Pkt.WriteInt((int)a_Changes.size() * 4);
|
||||
for (sSetBlockVector::const_iterator itr = a_Changes.begin(), end = a_Changes.end(); itr != end; ++itr)
|
||||
{
|
||||
unsigned int Coords = itr->y | (itr->z << 8) | (itr->x << 12);
|
||||
unsigned int Blocks = itr->BlockMeta | (itr->BlockType << 4);
|
||||
unsigned int Coords = itr->m_RelY | (itr->m_RelZ << 8) | (itr->m_RelX << 12);
|
||||
unsigned int Blocks = itr->m_BlockMeta | (itr->m_BlockType << 4);
|
||||
Pkt.WriteInt((Coords << 16) | Blocks);
|
||||
} // for itr - a_Changes[]
|
||||
}
|
||||
|
@ -207,9 +207,9 @@ void cProtocol180::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockV
|
||||
Pkt.WriteVarInt((UInt32)a_Changes.size());
|
||||
for (sSetBlockVector::const_iterator itr = a_Changes.begin(), end = a_Changes.end(); itr != end; ++itr)
|
||||
{
|
||||
short Coords = (short) (itr->y | (itr->z << 8) | (itr->x << 12));
|
||||
short Coords = (short) (itr->m_RelY | (itr->m_RelZ << 8) | (itr->m_RelX << 12));
|
||||
Pkt.WriteShort(Coords);
|
||||
Pkt.WriteVarInt((itr->BlockType & 0xFFF) << 4 | (itr->BlockMeta & 0xF));
|
||||
Pkt.WriteVarInt((itr->m_BlockType & 0xFFF) << 4 | (itr->m_BlockMeta & 0xF));
|
||||
} // for itr - a_Changes[]
|
||||
}
|
||||
|
||||
|
@ -307,6 +307,22 @@ public:
|
||||
return (a_X - x) / (a_OtherEnd.x - x);
|
||||
}
|
||||
|
||||
/** Rotates the vector 90 degrees clockwise around the vertical axis.
|
||||
Note that this is specific to minecraft's axis ordering, which is X+ left, Z+ down. */
|
||||
inline void TurnCW(void)
|
||||
{
|
||||
std::swap(x, z);
|
||||
x = -x;
|
||||
}
|
||||
|
||||
/** Rotates the vector 90 degrees counterclockwise around the vertical axis.
|
||||
Note that this is specific to minecraft's axis ordering, which is X+ left, Z+ down. */
|
||||
inline void TurnCCW(void)
|
||||
{
|
||||
std::swap(x, z);
|
||||
z = -z;
|
||||
}
|
||||
|
||||
/** The max difference between two coords for which the coords are assumed equal. */
|
||||
static const double EPS;
|
||||
|
||||
|
@ -1463,7 +1463,7 @@ void cWorld::GrowTreeImage(const sSetBlockVector & a_Blocks)
|
||||
sSetBlockVector b2;
|
||||
for (sSetBlockVector::const_iterator itr = a_Blocks.begin(); itr != a_Blocks.end(); ++itr)
|
||||
{
|
||||
if (itr->BlockType == E_BLOCK_LOG)
|
||||
if (itr->m_BlockType == E_BLOCK_LOG)
|
||||
{
|
||||
b2.push_back(*itr);
|
||||
}
|
||||
@ -1478,7 +1478,7 @@ void cWorld::GrowTreeImage(const sSetBlockVector & a_Blocks)
|
||||
// Check that at each log's coord there's an block allowed to be overwritten:
|
||||
for (sSetBlockVector::const_iterator itr = b2.begin(); itr != b2.end(); ++itr)
|
||||
{
|
||||
switch (itr->BlockType)
|
||||
switch (itr->m_BlockType)
|
||||
{
|
||||
CASE_TREE_ALLOWED_BLOCKS:
|
||||
{
|
||||
@ -1926,6 +1926,15 @@ void cWorld::SpawnPrimedTNT(double a_X, double a_Y, double a_Z, int a_FuseTicks,
|
||||
|
||||
|
||||
|
||||
void cWorld::SetBlocks(const sSetBlockVector & a_Blocks)
|
||||
{
|
||||
m_ChunkMap->SetBlocks(a_Blocks);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void cWorld::ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_FilterBlockType)
|
||||
{
|
||||
m_ChunkMap->ReplaceBlocks(a_Blocks, a_FilterBlockType);
|
||||
|
@ -491,6 +491,11 @@ public:
|
||||
|
||||
// tolua_end
|
||||
|
||||
/** Performs the specified single-block set operations simultaneously, as if SetBlock() was called for each item.
|
||||
Is more efficient than calling SetBlock() multiple times.
|
||||
If the chunk for any of the blocks is not loaded, the set operation is ignored silently. */
|
||||
void SetBlocks(const sSetBlockVector & a_Blocks);
|
||||
|
||||
/** Replaces world blocks with a_Blocks, if they are of type a_FilterBlockType */
|
||||
void ReplaceBlocks(const sSetBlockVector & a_Blocks, BLOCKTYPE a_FilterBlockType);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user