Moved BlockEntities to a separate folder

git-svn-id: http://mc-server.googlecode.com/svn/trunk@1527 0a769ca7-a7f5-676a-18bf-c427514a06d6
This commit is contained in:
madmaxoft@gmail.com
2013-05-28 19:12:47 +00:00
parent 3138daa1f8
commit 9be27992e2
35 changed files with 175 additions and 127 deletions
+96
View File
@@ -0,0 +1,96 @@
#pragma once
#include "../ClientHandle.h"
#include "../World.h"
namespace Json
{
class Value;
};
class cPlayer;
class cPacket;
// tolua_begin
class cBlockEntity
{
protected:
cBlockEntity(BLOCKTYPE a_BlockType, int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) :
m_PosX(a_BlockX),
m_PosY(a_BlockY),
m_PosZ(a_BlockZ),
m_RelX(a_BlockX - cChunkDef::Width * FAST_FLOOR_DIV(a_BlockX, cChunkDef::Width)),
m_RelZ(a_BlockZ - cChunkDef::Width * FAST_FLOOR_DIV(a_BlockZ, cChunkDef::Width)),
m_BlockType(a_BlockType),
m_World(a_World)
{
}
public:
// tolua_end
virtual ~cBlockEntity() {}; // force a virtual destructor in all descendants
virtual void Destroy(void) {};
void SetWorld(cWorld * a_World)
{
m_World = a_World;
}
// tolua_begin
// Position, in absolute block coordinates:
int GetPosX(void) const { return m_PosX; }
int GetPosY(void) const { return m_PosY; }
int GetPosZ(void) const { return m_PosZ; }
BLOCKTYPE GetBlockType(void) const { return m_BlockType; }
cWorld * GetWorld(void) const {return m_World; }
int GetChunkX(void) const { return FAST_FLOOR_DIV(m_PosX, cChunkDef::Width); }
int GetChunkZ(void) const { return FAST_FLOOR_DIV(m_PosZ, cChunkDef::Width); }
int GetRelX(void) const { return m_RelX; }
int GetRelZ(void) const { return m_RelZ; }
// tolua_end
virtual void SaveToJson (Json::Value & a_Value) = 0;
/// Called when a player uses this entity; should open the UI window
virtual void UsedBy( cPlayer * a_Player ) = 0;
/** Sends the packet defining the block entity to the client specified.
To send to all eligible clients, use cWorld::BroadcastBlockEntity()
*/
virtual void SendTo(cClientHandle & a_Client) = 0;
/// Ticks the entity; returns true if the chunk should be marked as dirty as a result of this ticking. By default does nothing.
virtual bool Tick(float a_Dt, cChunk & a_Chunk) { return false; }
protected:
/// Position in absolute block coordinates
int m_PosX, m_PosY, m_PosZ;
/// Position relative to the chunk, used to speed up ticking
int m_RelX, m_RelZ;
BLOCKTYPE m_BlockType;
cWorld * m_World;
} ; // tolua_export
@@ -0,0 +1,84 @@
// BlockEntityWithItems.h
// Declares the cBlockEntityWithItems class representing a common ancestor for all block entities that have an ItemGrid
#pragma once
#include "BlockEntity.h"
#include "../ItemGrid.h"
// tolua_begin
class cBlockEntityWithItems :
public cBlockEntity
// tolua_end
// tolua doesn't seem to support multiple inheritance?
, public cItemGrid::cListener
// tolua_begin
{
typedef cBlockEntity super;
public:
// tolua_end
cBlockEntityWithItems(
BLOCKTYPE a_BlockType, // Type of the block that the entity represents
int a_BlockX, int a_BlockY, int a_BlockZ, // Position of the block entity
int a_ItemGridWidth, int a_ItemGridHeight, // Dimensions of the ItemGrid
cWorld * a_World // Optional world to assign to the entity
) :
super(a_BlockType, a_BlockX, a_BlockY, a_BlockZ, a_World),
m_Contents(a_ItemGridWidth, a_ItemGridHeight)
{
m_Contents.AddListener(*this);
}
virtual void Destroy(void) override
{
// Drop the contents as pickups:
ASSERT(m_World != NULL);
cItems Pickups;
m_Contents.CopyToItems(Pickups);
m_Contents.Clear();
m_World->SpawnItemPickups(Pickups, m_PosX, m_PosY, m_PosZ);
}
// tolua_begin
const cItem & GetSlot(int a_SlotNum) const { return m_Contents.GetSlot(a_SlotNum); }
const cItem & GetSlot(int a_X, int a_Y) const { return m_Contents.GetSlot(a_X, a_Y); }
void SetSlot(int a_SlotNum, const cItem & a_Item) { m_Contents.SetSlot(a_SlotNum, a_Item); }
void SetSlot(int a_X, int a_Y, const cItem & a_Item) { m_Contents.SetSlot(a_X, a_Y, a_Item); }
/// Returns the ItemGrid used for storing the contents
cItemGrid & GetContents(void) { return m_Contents; }
// tolua_end
/// Const version of the GetContents() function for C++ type-safety
const cItemGrid & GetContents(void) const { return m_Contents; }
protected:
cItemGrid m_Contents;
// cItemGrid::cListener overrides:
virtual void OnSlotChanged(cItemGrid * a_Grid, int a_SlotNum)
{
ASSERT(a_Grid == &m_Contents);
ASSERT(m_World != NULL);
m_World->MarkChunkDirty(GetChunkX(), GetChunkZ());
}
} ; // tolua_export
+179
View File
@@ -0,0 +1,179 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "ChestEntity.h"
#include "../Item.h"
#include "../Player.h"
#include "../UI/Window.h"
#include "../Noise.h"
#include <json/json.h>
cChestEntity::cChestEntity(int a_BlockX, int a_BlockY, int a_BlockZ) :
super(E_BLOCK_CHEST, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, NULL)
{
cBlockEntityWindowOwner::SetBlockEntity(this);
}
cChestEntity::cChestEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) :
super(E_BLOCK_CHEST, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, a_World)
{
cBlockEntityWindowOwner::SetBlockEntity(this);
}
cChestEntity::~cChestEntity()
{
cWindow * Window = GetWindow();
if (Window != NULL)
{
Window->OwnerDestroyed();
}
}
bool cChestEntity::LoadFromJson(const Json::Value & a_Value)
{
m_PosX = a_Value.get("x", 0).asInt();
m_PosY = a_Value.get("y", 0).asInt();
m_PosZ = a_Value.get("z", 0).asInt();
Json::Value AllSlots = a_Value.get("Slots", 0);
int SlotIdx = 0;
for (Json::Value::iterator itr = AllSlots.begin(); itr != AllSlots.end(); ++itr)
{
cItem Item;
Item.FromJson( *itr );
SetSlot( SlotIdx, Item );
SlotIdx++;
}
return true;
}
void cChestEntity::SaveToJson(Json::Value & a_Value)
{
a_Value["x"] = m_PosX;
a_Value["y"] = m_PosY;
a_Value["z"] = m_PosZ;
Json::Value AllSlots;
for (int i = m_Contents.GetNumSlots() - 1; i >= 0; i--)
{
Json::Value Slot;
m_Contents.GetSlot(i).GetJson(Slot);
AllSlots.append(Slot);
}
a_Value["Slots"] = AllSlots;
}
void cChestEntity::SendTo(cClientHandle & a_Client)
{
// The chest entity doesn't need anything sent to the client when it's created / gets in the viewdistance
// All the actual handling is in the cWindow UI code that gets called when the chest is rclked
UNUSED(a_Client);
}
void cChestEntity::UsedBy(cPlayer * a_Player)
{
if (GetWindow() == NULL)
{
OpenNewWindow();
}
if (GetWindow())
{
if( a_Player->GetWindow() != GetWindow() )
{
a_Player->OpenWindow( GetWindow() );
GetWindow()->SendWholeWindow(*a_Player->GetClientHandle());
}
}
// This is rather a hack
// Instead of marking the chunk as dirty upon chest contents change, we mark it dirty now
// We cannot properly detect contents change, but such a change doesn't happen without a player opening the chest first.
// The few false positives aren't much to worry about
int ChunkX, ChunkZ;
cChunkDef::BlockToChunk(m_PosX, m_PosY, m_PosZ, ChunkX, ChunkZ);
m_World->MarkChunkDirty(ChunkX, ChunkZ);
}
void cChestEntity::OpenNewWindow(void)
{
// Callback for opening together with neighbor chest:
class cOpenDouble :
public cChestCallback
{
cChestEntity * m_ThisChest;
public:
cOpenDouble(cChestEntity * a_ThisChest) :
m_ThisChest(a_ThisChest)
{
}
virtual bool Item(cChestEntity * a_Chest) override
{
// The primary chest should eb the one with lesser X or Z coord:
cChestEntity * Primary = a_Chest;
cChestEntity * Secondary = m_ThisChest;
if (
(Primary->GetPosX() > Secondary->GetPosX()) ||
(Primary->GetPosZ() > Secondary->GetPosZ())
)
{
std::swap(Primary, Secondary);
}
m_ThisChest->OpenWindow(new cChestWindow(Primary, Secondary));
return false;
}
} ;
// Scan neighbors for adjacent chests:
cOpenDouble OpenDbl(this);
if (
m_World->DoWithChestAt(m_PosX - 1, m_PosY, m_PosZ, OpenDbl) ||
m_World->DoWithChestAt(m_PosX + 1, m_PosY, m_PosZ, OpenDbl) ||
m_World->DoWithChestAt(m_PosX , m_PosY, m_PosZ - 1, OpenDbl) ||
m_World->DoWithChestAt(m_PosX , m_PosY, m_PosZ + 1, OpenDbl)
)
{
// The double-chest window has been opened in the callback
return;
}
// There is no chest neighbor, open a single-chest window:
OpenWindow(new cChestWindow(this));
}
+63
View File
@@ -0,0 +1,63 @@
#pragma once
#include "BlockEntityWithItems.h"
#include "../UI/WindowOwner.h"
namespace Json
{
class Value;
};
class cClientHandle;
class cServer;
class cNBTData;
// tolua_begin
class cChestEntity :
public cBlockEntityWithItems,
public cBlockEntityWindowOwner
{
typedef cBlockEntityWithItems super;
public:
enum {
ContentsHeight = 3,
ContentsWidth = 9,
} ;
/// Constructor used while generating a chunk; sets m_World to NULL
cChestEntity(int a_BlockX, int a_BlockY, int a_BlockZ);
// tolua_end
/// Constructor used for normal operation
cChestEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
virtual ~cChestEntity();
static const char * GetClassStatic(void) { return "cChestEntity"; }
bool LoadFromJson(const Json::Value& a_Value);
// cBlockEntity overrides:
virtual void SaveToJson(Json::Value & a_Value ) override;
virtual void SendTo(cClientHandle & a_Client) override;
virtual void UsedBy(cPlayer * a_Player);
/// Opens a new chest window for this chests. Scans for neighbors to open a double chest window, if appropriate.
void OpenNewWindow(void);
} ; // tolua_export
+196
View File
@@ -0,0 +1,196 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "DispenserEntity.h"
#include "../Player.h"
#include "../Simulator/FluidSimulator.h"
#include "../Chunk.h"
cDispenserEntity::cDispenserEntity(int a_BlockX, int a_BlockY, int a_BlockZ) :
super(E_BLOCK_DISPENSER, a_BlockX, a_BlockY, a_BlockZ, NULL)
{
SetBlockEntity(this); // cBlockEntityWindowOwner
}
cDispenserEntity::cDispenserEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) :
super(E_BLOCK_DISPENSER, a_BlockX, a_BlockY, a_BlockZ, a_World)
{
SetBlockEntity(this); // cBlockEntityWindowOwner
}
void cDispenserEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum)
{
int DispX = m_RelX;
int DispY = m_PosY;
int DispZ = m_RelZ;
NIBBLETYPE Meta = a_Chunk.GetMeta(m_RelX, m_PosY, m_RelZ);
AddDropSpenserDir(DispX, DispY, DispZ, Meta);
cChunk * DispChunk = a_Chunk.GetRelNeighborChunkAdjustCoords(DispX, DispZ);
if (DispChunk == NULL)
{
// Would dispense into / interact with a non-loaded chunk, ignore the tick
return;
}
BLOCKTYPE DispBlock = DispChunk->GetBlock(DispX, DispY, DispZ);
// Dispense the item:
switch (m_Contents.GetSlot(a_SlotNum).m_ItemType)
{
case E_ITEM_BUCKET:
{
LOGD("Dispensing empty bucket in slot %d; DispBlock is \"%s\" (%d).", a_SlotNum, ItemTypeToString(DispBlock).c_str(), DispBlock);
switch (DispBlock)
{
case E_BLOCK_STATIONARY_WATER:
case E_BLOCK_WATER:
{
if (ScoopUpLiquid(a_SlotNum, E_ITEM_WATER_BUCKET))
{
DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_AIR, 0);
}
break;
}
case E_BLOCK_STATIONARY_LAVA:
case E_BLOCK_LAVA:
{
if (ScoopUpLiquid(a_SlotNum, E_ITEM_LAVA_BUCKET))
{
DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_AIR, 0);
}
break;
}
default:
{
DropFromSlot(a_Chunk, a_SlotNum);
break;
}
}
break;
} // E_ITEM_BUCKET
case E_ITEM_WATER_BUCKET:
{
LOGD("Dispensing water bucket in slot %d; DispBlock is \"%s\" (%d).", a_SlotNum, ItemTypeToString(DispBlock).c_str(), DispBlock);
if (EmptyLiquidBucket(DispBlock, a_SlotNum))
{
DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_WATER, 0);
}
else
{
DropFromSlot(a_Chunk, a_SlotNum);
}
break;
}
case E_ITEM_LAVA_BUCKET:
{
LOGD("Dispensing lava bucket in slot %d; DispBlock is \"%s\" (%d).", a_SlotNum, ItemTypeToString(DispBlock).c_str(), DispBlock);
if (EmptyLiquidBucket(DispBlock, a_SlotNum))
{
DispChunk->SetBlock(DispX, DispY, DispZ, E_BLOCK_LAVA, 0);
}
else
{
DropFromSlot(a_Chunk, a_SlotNum);
}
break;
}
case E_ITEM_SPAWN_EGG:
{
double MobX = 0.5 + (DispX + DispChunk->GetPosX() * cChunkDef::Width);
double MobZ = 0.5 + (DispZ + DispChunk->GetPosZ() * cChunkDef::Width);
if (m_World->SpawnMob(MobX, DispY, MobZ, m_Contents.GetSlot(a_SlotNum).m_ItemDamage) >= 0)
{
m_Contents.ChangeSlotCount(a_SlotNum, -1);
}
break;
}
default:
{
DropFromSlot(a_Chunk, a_SlotNum);
break;
}
} // switch (ItemType)
}
bool cDispenserEntity::ScoopUpLiquid(int a_SlotNum, short a_BucketItemType)
{
cItem LiquidBucket(a_BucketItemType, 1);
if (m_Contents.GetSlot(a_SlotNum).m_ItemCount == 1)
{
// Special case: replacing one empty bucket with one full bucket
m_Contents.SetSlot(a_SlotNum, LiquidBucket);
return true;
}
// There are stacked buckets at the selected slot, see if a full bucket will fit somewhere else
if (m_Contents.HowManyCanFit(LiquidBucket) < 1)
{
// Cannot fit into m_Contents
return false;
}
m_Contents.ChangeSlotCount(a_SlotNum, -1);
m_Contents.AddItem(LiquidBucket);
return true;
}
bool cDispenserEntity::EmptyLiquidBucket(BLOCKTYPE a_BlockInFront, int a_SlotNum)
{
if (
(a_BlockInFront != E_BLOCK_AIR) &&
!IsBlockLiquid(a_BlockInFront) &&
!cFluidSimulator::CanWashAway(a_BlockInFront)
)
{
// Not a suitable block in front
return false;
}
cItem EmptyBucket(E_ITEM_BUCKET, 1);
if (m_Contents.GetSlot(a_SlotNum).m_ItemCount == 1)
{
// Change the single full bucket present into a single empty bucket
m_Contents.SetSlot(a_SlotNum, EmptyBucket);
return true;
}
// There are full buckets stacked at this slot, check if we can fit in the empty bucket
if (m_Contents.HowManyCanFit(EmptyBucket) < 1)
{
// The empty bucket wouldn't fit into m_Contents
return false;
}
// The empty bucket fits in, remove one full bucket and add the empty one
m_Contents.ChangeSlotCount(a_SlotNum, -1);
m_Contents.AddItem(EmptyBucket);
return true;
}
+41
View File
@@ -0,0 +1,41 @@
#pragma once
#include "DropSpenserEntity.h"
// tolua_begin
class cDispenserEntity :
public cDropSpenserEntity
{
typedef cDropSpenserEntity super;
public:
/// Constructor used while generating a chunk; sets m_World to NULL
cDispenserEntity(int a_BlockX, int a_BlockY, int a_BlockZ);
// tolua_end
/// Constructor used for normal operation
cDispenserEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
static const char * GetClassStatic(void) { return "cDispenserEntity"; }
private:
// cDropSpenser overrides:
virtual void DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) override;
/// If such a bucket can fit, adds it to m_Contents and returns true
bool ScoopUpLiquid(int a_SlotNum, short a_BucketItemType);
/// If the a_BlockInFront is liquidable and the empty bucket can fit, does the m_Contents processing and returns true
bool EmptyLiquidBucket(BLOCKTYPE a_BlockInFront, int a_SlotNum);
} ; // tolua_export
+246
View File
@@ -0,0 +1,246 @@
// DropSpenserEntity.cpp
// Declares the cDropSpenserEntity class representing a common ancestor to the cDispenserEntity and cDropperEntity
// The dropper and dispenser only needs to override the DropSpenseFromSlot() function to provide the specific item behavior
#include "Globals.h"
#include "DropSpenserEntity.h"
#include "../Player.h"
#include "../Chunk.h"
cDropSpenserEntity::cDropSpenserEntity(BLOCKTYPE a_BlockType, int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) :
super(a_BlockType, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, a_World),
m_ShouldDropSpense(false),
m_IsPowered(false)
{
SetBlockEntity(this); // cBlockEntityWindowOwner
}
cDropSpenserEntity::~cDropSpenserEntity()
{
// Tell window its owner is destroyed
cWindow * Window = GetWindow();
if (Window != NULL)
{
Window->OwnerDestroyed();
}
}
void cDropSpenserEntity::AddDropSpenserDir(int & a_BlockX, int & a_BlockY, int & a_BlockZ, NIBBLETYPE a_Direction)
{
switch (a_Direction)
{
case E_META_DISPENSER_FACING_YM: a_BlockY--; return;
case E_META_DISPENSER_FACING_YP: a_BlockY++; return;
case E_META_DISPENSER_FACING_ZM: a_BlockZ--; return;
case E_META_DISPENSER_FACING_ZP: a_BlockZ++; return;
case E_META_DISPENSER_FACING_XM: a_BlockX--; return;
case E_META_DISPENSER_FACING_XP: a_BlockX++; return;
}
LOGWARNING("%s: Unhandled direction: %d", __FUNCTION__, a_Direction);
return;
}
void cDropSpenserEntity::DropSpense(cChunk & a_Chunk)
{
// Pick one of the occupied slots:
int OccupiedSlots[9];
int SlotsCnt = 0;
for (int i = m_Contents.GetNumSlots() - 1; i >= 0; i--)
{
if (!m_Contents.GetSlot(i).IsEmpty())
{
OccupiedSlots[SlotsCnt] = i;
SlotsCnt++;
}
} // for i - m_Contents[]
if (SlotsCnt == 0)
{
// Nothing in the dropspenser, play the click sound
m_World->BroadcastSoundEffect("random.click", m_PosX * 8, m_PosY * 8, m_PosZ * 8, 1.0f, 1.2f);
return;
}
int RandomSlot = m_World->GetTickRandomNumber(SlotsCnt - 1);
// DropSpense the item, using the specialized behavior in the subclasses:
DropSpenseFromSlot(a_Chunk, OccupiedSlots[RandomSlot]);
// Broadcast a smoke and click effects:
NIBBLETYPE Meta = a_Chunk.GetMeta(m_RelX, m_PosY, m_RelZ);
int SmokeDir = 0;
switch (Meta)
{
case E_META_DISPENSER_FACING_XM: SmokeDir = 3; break;
case E_META_DISPENSER_FACING_XP: SmokeDir = 5; break;
case E_META_DISPENSER_FACING_ZM: SmokeDir = 1; break;
case E_META_DISPENSER_FACING_ZP: SmokeDir = 7; break;
}
m_World->BroadcastSoundParticleEffect(2000, m_PosX * 8, m_PosY * 8, m_PosZ * 8, SmokeDir);
m_World->BroadcastSoundEffect("random.click", m_PosX * 8, m_PosY * 8, m_PosZ * 8, 1.0f, 1.0f);
// Update the UI window, if open:
cWindow * Window = GetWindow();
if (Window != NULL)
{
Window->BroadcastWholeWindow();
}
}
void cDropSpenserEntity::Activate(void)
{
m_ShouldDropSpense = true;
}
void cDropSpenserEntity::SetRedstonePower(bool a_IsPowered)
{
if (a_IsPowered && !m_IsPowered)
{
Activate();
}
m_IsPowered = a_IsPowered;
}
bool cDropSpenserEntity::Tick(float a_Dt, cChunk & a_Chunk)
{
if (!m_ShouldDropSpense)
{
return false;
}
m_ShouldDropSpense = false;
DropSpense(a_Chunk);
return true;
}
bool cDropSpenserEntity::LoadFromJson(const Json::Value & a_Value)
{
m_PosX = a_Value.get("x", 0).asInt();
m_PosY = a_Value.get("y", 0).asInt();
m_PosZ = a_Value.get("z", 0).asInt();
Json::Value AllSlots = a_Value.get("Slots", 0);
int SlotIdx = 0;
for (Json::Value::iterator itr = AllSlots.begin(); itr != AllSlots.end(); ++itr)
{
cItem Contents;
Contents.FromJson(*itr);
m_Contents.SetSlot(SlotIdx, Contents);
SlotIdx++;
if (SlotIdx >= m_Contents.GetNumSlots())
{
return true;
}
}
return true;
}
void cDropSpenserEntity::SaveToJson(Json::Value & a_Value)
{
a_Value["x"] = m_PosX;
a_Value["y"] = m_PosY;
a_Value["z"] = m_PosZ;
Json::Value AllSlots;
int NumSlots = m_Contents.GetNumSlots();
for (int i = 0; i < NumSlots; i++)
{
Json::Value Slot;
m_Contents.GetSlot(i).GetJson(Slot);
AllSlots.append(Slot);
}
a_Value["Slots"] = AllSlots;
}
void cDropSpenserEntity::SendTo(cClientHandle & a_Client)
{
// Nothing needs to be sent
UNUSED(a_Client);
}
void cDropSpenserEntity::UsedBy(cPlayer * a_Player)
{
cWindow * Window = GetWindow();
if (Window == NULL)
{
OpenWindow(new cDropSpenserWindow(m_PosX, m_PosY, m_PosZ, this));
Window = GetWindow();
}
if (Window != NULL)
{
if (a_Player->GetWindow() != Window)
{
a_Player->OpenWindow(Window);
Window->SendWholeWindow(*a_Player->GetClientHandle());
}
}
}
void cDropSpenserEntity::DropFromSlot(cChunk & a_Chunk, int a_SlotNum)
{
int DispX = m_PosX;
int DispY = m_PosY;
int DispZ = m_PosZ;
NIBBLETYPE Meta = a_Chunk.GetMeta(m_RelX, m_PosY, m_RelZ);
AddDropSpenserDir(DispX, DispY, DispZ, Meta);
cItems Pickups;
Pickups.push_back(m_Contents.RemoveOneItem(a_SlotNum));
m_World->SpawnItemPickups(Pickups, DispX, DispY, DispZ);
}
+89
View File
@@ -0,0 +1,89 @@
// DropSpenser.h
// Declares the cDropSpenser class representing a common ancestor to the cDispenserEntity and cDropperEntity
// The dropper and dispenser only needs to override the DropSpenseFromSlot() function to provide the specific item behavior
#pragma once
#include "BlockEntityWithItems.h"
#include "../UI/WindowOwner.h"
namespace Json
{
class Value;
}
class cClientHandle;
class cServer;
// tolua_begin
class cDropSpenserEntity :
public cBlockEntityWithItems,
public cBlockEntityWindowOwner
{
typedef cBlockEntityWithItems super;
public:
enum {
ContentsHeight = 3,
ContentsWidth = 3,
} ;
// tolua_end
cDropSpenserEntity(BLOCKTYPE a_BlockType, int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
virtual ~cDropSpenserEntity();
static const char * GetClassStatic(void) { return "cDropSpenserEntity"; }
bool LoadFromJson(const Json::Value & a_Value);
// cBlockEntity overrides:
virtual void SaveToJson(Json::Value & a_Value) override;
virtual bool Tick(float a_Dt, cChunk & a_Chunk) override;
virtual void SendTo(cClientHandle & a_Client) override;
virtual void UsedBy(cPlayer * a_Player) override;
// tolua_begin
/// Modifies the block coords to match the dropspenser direction given (where the dropspensed pickups should materialize)
void AddDropSpenserDir(int & a_BlockX, int & a_BlockY, int & a_BlockZ, NIBBLETYPE a_Direction);
/// Sets the dropspenser to dropspense an item in the next tick
void Activate(void);
/// Sets the internal redstone power flag to "on" or "off", depending on the parameter. Calls Activate() if appropriate
void SetRedstonePower(bool a_IsPowered);
// tolua_end
protected:
bool m_ShouldDropSpense; ///< If true, the dropspenser will dropspense an item in the next tick
bool m_IsPowered; ///< Set to true when the dropspenser receives redstone power.
/// Does the actual work on dropspensing an item. Chooses the slot, calls DropSpenseFromSlot() and handles smoke / sound effects
void DropSpense(cChunk & a_Chunk);
/// Override this function to provide the specific behavior for item dropspensing (drop / shoot / pour / ...)
virtual void DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) = 0;
/// Helper function, drops one item from the specified slot (like a dropper)
void DropFromSlot(cChunk & a_Chunk, int a_SlotNum);
} ; // tolua_export
+42
View File
@@ -0,0 +1,42 @@
// DropperEntity.cpp
// Implements the cRtopperEntity class representing a Dropper block entity
#include "Globals.h"
#include "DropperEntity.h"
#include "../Player.h"
#include "../Simulator/FluidSimulator.h"
cDropperEntity::cDropperEntity(int a_BlockX, int a_BlockY, int a_BlockZ) :
super(E_BLOCK_DROPPER, a_BlockX, a_BlockY, a_BlockZ, NULL)
{
SetBlockEntity(this); // cBlockEntityWindowOwner
}
cDropperEntity::cDropperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World) :
super(E_BLOCK_DROPPER, a_BlockX, a_BlockY, a_BlockZ, a_World)
{
SetBlockEntity(this); // cBlockEntityWindowOwner
}
void cDropperEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum)
{
DropFromSlot(a_Chunk, a_SlotNum);
}
+49
View File
@@ -0,0 +1,49 @@
// DropperEntity.h
// Declares the cDropperEntity class representing a dropper block entity
#pragma once
#include "DropSpenserEntity.h"
// tolua_begin
class cDropperEntity :
public cDropSpenserEntity
{
typedef cDropSpenserEntity super;
public:
/// Constructor used while generating a chunk; sets m_World to NULL
cDropperEntity(int a_BlockX, int a_BlockY, int a_BlockZ);
// tolua_end
/// Constructor used for normal operation
cDropperEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
static const char * GetClassStatic(void) { return "cDropperEntity"; }
protected:
// cDropSpenserEntity overrides:
virtual void DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum) override;
/** Takes an item from slot a_SlotNum and puts it into the container in front of the dropper.
Called when there's a container directly in front of the dropper,
so the dropper should store items there, rather than dropping.
*/
void PutIntoContainer(cChunk & a_Chunk, int a_SlotNum, BLOCKTYPE a_ContainerBlock, int a_ContainerX, int a_ContainerY, int a_ContainerZ);
} ; // tolua_export
+369
View File
@@ -0,0 +1,369 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "FurnaceEntity.h"
#include "../UI/Window.h"
#include "../Player.h"
#include "../FurnaceRecipe.h"
#include "../Root.h"
#include <json/json.h>
enum
{
PROGRESSBAR_SMELTING = 0,
PROGRESSBAR_FUEL = 1,
} ;
cFurnaceEntity::cFurnaceEntity(int a_X, int a_Y, int a_Z, cWorld * a_World)
: cBlockEntity( E_BLOCK_FURNACE, a_X, a_Y, a_Z, a_World )
, m_Items( new cItem[3] )
, m_CookingItem( 0 )
, m_CookTime( 0 )
, m_TimeCooked( 0 )
, m_BurnTime( 0 )
, m_TimeBurned( 0 )
{
SetBlockEntity(this); // cBlockEntityWindowOwner
}
cFurnaceEntity::~cFurnaceEntity()
{
// Tell window its owner is destroyed
if( GetWindow() )
{
GetWindow()->OwnerDestroyed();
}
// Clean up items
if( m_Items )
{
delete [] m_Items;
}
}
void cFurnaceEntity::Destroy()
{
// Drop items
cItems Pickups;
for( int i = 0; i < 3; i++)
{
if( !m_Items[i].IsEmpty() )
{
Pickups.push_back(m_Items[i]);
m_Items[i].Empty();
}
}
m_World->SpawnItemPickups(Pickups, m_PosX, m_PosY, m_PosZ);
}
void cFurnaceEntity::UsedBy(cPlayer * a_Player)
{
if (GetWindow() == NULL)
{
OpenWindow(new cFurnaceWindow(m_PosX, m_PosY, m_PosZ, this));
}
if (GetWindow() != NULL)
{
if (a_Player->GetWindow() != GetWindow())
{
a_Player->OpenWindow(GetWindow());
GetWindow()->SendWholeWindow(*a_Player->GetClientHandle());
}
}
}
bool cFurnaceEntity::Tick(float a_Dt, cChunk & a_Chunk)
{
/*
// DEBUG:
Int16 BurnTime = (Int16)(GetTimeToBurn() / 50.0);
Int16 CookTime = (Int16)(GetTimeCooked() / 50.0);
LOGD("Furnace: BurnTime %d, CookTime %d", BurnTime, CookTime);
*/
if (m_BurnTime <= 0)
{
if (m_TimeCooked > 0)
{
// We have just finished smelting, reset the progress bar:
BroadcastProgress(PROGRESSBAR_SMELTING, 0);
m_TimeCooked = 0;
m_World->FastSetBlock(m_PosX, m_PosY, m_PosZ, E_BLOCK_FURNACE, m_World->GetBlockMeta(m_PosX, m_PosY, m_PosZ));
}
// There is no fuel and no flame, no need to tick at all
return false;
}
// DEBUG: LOGD("Furnace: Left: %0.1f Burned: %0.1f Burn time: %0.1f", m_CookTime - m_TimeCooked, m_TimeBurned, m_BurnTime );
short SmeltingProgress = 0;
if ((m_CookingItem != NULL) && ((m_TimeBurned < m_BurnTime) || (m_TimeCooked + a_Dt >= m_CookTime)))
{
if (m_CookingItem->IsEqual(m_Items[2]) || m_Items[2].IsEmpty())
{
m_TimeCooked += a_Dt;
if ( m_TimeCooked >= m_CookTime )
{
m_Items[0].m_ItemCount--;
if( m_Items[0].IsEmpty() ) m_Items[0].Empty();
m_Items[2].m_ItemDamage = m_CookingItem->m_ItemDamage;
m_Items[2].m_ItemType = m_CookingItem->m_ItemType;
m_Items[2].m_ItemCount += m_CookingItem->m_ItemCount;
delete m_CookingItem;
m_CookingItem = NULL;
cWindow * Window = GetWindow();
if (Window != NULL)
{
Window->BroadcastWholeWindow();
}
m_TimeCooked -= m_CookTime;
StartCooking();
}
SmeltingProgress = (short)( m_TimeCooked * (180.f / m_CookTime));
if (SmeltingProgress > 180) SmeltingProgress = 180;
if (SmeltingProgress < 0) SmeltingProgress = 0;
}
}
BroadcastProgress(PROGRESSBAR_SMELTING, SmeltingProgress);
m_TimeBurned += a_Dt;
cWindow * Window = GetWindow();
if (m_TimeBurned >= m_BurnTime)
{
m_TimeBurned -= m_BurnTime;
m_BurnTime = 0;
if (StartCooking() && (Window != NULL))
{
Window->BroadcastWholeWindow();
}
}
short Value = 0;
if (m_BurnTime > 0.f)
{
Value = 250 - (short)( m_TimeBurned * (250.f / m_BurnTime));
if (Value > 250) Value = 250;
if (Value < 0) Value = 0;
}
BroadcastProgress(PROGRESSBAR_FUEL, Value);
return ((m_CookingItem != NULL) || (m_TimeBurned < m_BurnTime)) && (m_BurnTime > 0.0); // Keep on ticking, if there's more to cook, or if it's cooking
}
bool cFurnaceEntity::StartCooking(void)
{
cFurnaceRecipe* FR = cRoot::Get()->GetFurnaceRecipe();
float BurnTime = FR->GetBurnTime( m_Items[1] );
if( (m_TimeBurned < m_BurnTime) || BurnTime > 0.f ) // burnable material
{
const cFurnaceRecipe::Recipe* R = FR->GetRecipeFrom( m_Items[0] );
if (R != NULL) // cook able ingredient
{
if (m_Items[2].IsEqual(*R->Out) || m_Items[2].IsEmpty())
{
// good to go
m_World->FastSetBlock(m_PosX, m_PosY, m_PosZ, E_BLOCK_LIT_FURNACE, m_World->GetBlockMeta(m_PosX, m_PosY, m_PosZ));
if( m_TimeBurned >= m_BurnTime ) // burn new material
{
m_Items[1].m_ItemCount--;
if( m_Items[1].m_ItemCount <= 0 ) m_Items[1].Empty();
m_TimeBurned = 0;
m_BurnTime = BurnTime;
}
if( !m_CookingItem ) // Only cook new item if not already cooking
{
m_CookingItem = new cItem( *R->Out ); // Resulting item
m_TimeCooked = 0.f;
m_CookTime = R->CookTime;
}
return true;
}
}
}
return false;
}
bool cFurnaceEntity::ContinueCooking(void)
{
cFurnaceRecipe * FR = cRoot::Get()->GetFurnaceRecipe();
float BurnTime = FR->GetBurnTime( m_Items[1] );
if( (m_TimeBurned < m_BurnTime) || BurnTime > 0.f ) // burnable material
{
const cFurnaceRecipe::Recipe * R = FR->GetRecipeFrom( m_Items[0] );
if (R != NULL) // cook able ingredient
{
if (m_Items[2].IsEqual(*R->Out) || m_Items[2].IsEmpty())
{
// good to go
if (m_CookingItem == NULL) // Only cook new item if not already cooking
{
m_CookingItem = new cItem( *R->Out ); // Resulting item
}
return true;
}
}
}
return false;
}
void cFurnaceEntity::ResetCookTimer()
{
delete m_CookingItem;
m_CookingItem = NULL;
m_TimeCooked = 0.f;
m_CookTime = 0.f;
}
void cFurnaceEntity::SetSlot(int a_Slot, const cItem & a_Item)
{
if ((a_Slot < 0) || (a_Slot >= 3))
{
ASSERT(!"Furnace: slot number out of range");
return;
}
m_Items[a_Slot] = a_Item;
}
bool cFurnaceEntity::LoadFromJson(const Json::Value & a_Value)
{
m_PosX = a_Value.get("x", 0).asInt();
m_PosY = a_Value.get("y", 0).asInt();
m_PosZ = a_Value.get("z", 0).asInt();
Json::Value AllSlots = a_Value.get("Slots", 0);
int SlotIdx = 0;
for( Json::Value::iterator itr = AllSlots.begin(); itr != AllSlots.end(); ++itr )
{
m_Items[ SlotIdx ].FromJson( *itr );
SlotIdx++;
}
// Get currently cooking item
Json::Value JsonItem = a_Value.get("Cooking", Json::nullValue );
if( !JsonItem.empty() )
{
cItem Item;
Item.FromJson( JsonItem );
if( !Item.IsEmpty() )
{
m_CookingItem = new cItem( Item );
}
}
m_CookTime = (float)a_Value.get("CookTime", 0).asDouble();
m_TimeCooked = (float)a_Value.get("TimeCooked", 0).asDouble();
m_BurnTime = (float)a_Value.get("BurnTime", 0).asDouble();
m_TimeBurned = (float)a_Value.get("TimeBurned", 0).asDouble();
return true;
}
void cFurnaceEntity::SaveToJson( Json::Value& a_Value )
{
a_Value["x"] = m_PosX;
a_Value["y"] = m_PosY;
a_Value["z"] = m_PosZ;
Json::Value AllSlots;
for(unsigned int i = 0; i < 3; i++)
{
Json::Value Slot;
m_Items[ i ].GetJson( Slot );
AllSlots.append( Slot );
}
a_Value["Slots"] = AllSlots;
// Currently cooking item
if( m_CookingItem )
{
Json::Value JsonItem;
m_CookingItem->GetJson( JsonItem );
a_Value["Cooking"] = JsonItem;
}
a_Value["CookTime"] = m_CookTime;
a_Value["TimeCooked"] = m_TimeCooked;
a_Value["BurnTime"] = m_BurnTime;
a_Value["TimeBurned"] = m_TimeBurned;
}
void cFurnaceEntity::SendTo(cClientHandle & a_Client)
{
// Nothing needs to be sent
UNUSED(a_Client);
}
void cFurnaceEntity::BroadcastProgress(int a_ProgressbarID, short a_Value)
{
cWindow * Window = GetWindow();
if (Window != NULL)
{
Window->BroadcastInventoryProgress(a_ProgressbarID, a_Value);
}
}
+76
View File
@@ -0,0 +1,76 @@
#pragma once
#include "BlockEntity.h"
#include "../UI/WindowOwner.h"
#include "../Item.h"
namespace Json
{
class Value;
}
class cClientHandle;
class cServer;
class cFurnaceEntity :
public cBlockEntity,
public cBlockEntityWindowOwner
{
public:
cFurnaceEntity(int a_X, int a_Y, int a_Z, cWorld * a_World);
virtual ~cFurnaceEntity();
virtual void Destroy();
static const char * GetClassStatic() { return "cFurnaceEntity"; }
bool LoadFromJson(const Json::Value & a_Value);
// cBlockEntity overrides:
virtual void SaveToJson(Json::Value & a_Value) override;
virtual void SendTo(cClientHandle & a_Client) override;
virtual bool Tick(float a_Dt, cChunk & a_Chunk) override;
virtual void UsedBy(cPlayer * a_Player) override;
bool StartCooking(void);
/// Restarts cooking. Used after the furnace is loaded from storage to set up the internal variables so that cooking continues, if it was active. Returns true if cooking.
bool ContinueCooking(void);
void ResetCookTimer();
const cItem * GetSlot(int i) const { return &(m_Items[i]); }
void SetSlot(int a_Slot, const cItem & a_Item);
float GetTimeCooked(void) const {return m_TimeCooked; }
float GetTimeToBurn(void) const {return m_BurnTime - m_TimeBurned; }
void SetBurnTimes(float a_BurnTime, float a_TimeBurned) {m_BurnTime = a_BurnTime; m_TimeBurned = 0; }
void SetCookTimes(float a_CookTime, float a_TimeCooked) {m_CookTime = a_CookTime; m_TimeCooked = a_TimeCooked; }
private:
cItem * m_Items;
cItem * m_CookingItem;
// All timers are in 1 ms
float m_CookTime; // Amount of time needed to fully cook current item
float m_TimeCooked; // Amount of time that the current item has been cooking
float m_BurnTime; // Amount of time that the current fuel can burn (in total); zero if no fuel burning
float m_TimeBurned; // Amount of time that the current fuel has been burning
void BroadcastProgress(int a_ProgressbarID, short a_Value);
};
+123
View File
@@ -0,0 +1,123 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "JukeboxEntity.h"
#include "../World.h"
#include <json/json.h>
cJukeboxEntity::cJukeboxEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World)
: cBlockEntity(E_BLOCK_JUKEBOX, a_BlockX, a_BlockY, a_BlockZ, a_World)
, m_Record( 0 )
{
}
cJukeboxEntity::~cJukeboxEntity()
{
if (m_Record >= 2256 && m_Record <= 2267)
{
EjectRecord();
m_Record = 0;
}
}
void cJukeboxEntity::UsedBy(cPlayer * a_Player)
{
if (m_Record == 0)
{
const cItem & HeldItem = a_Player->GetEquippedItem();
if (HeldItem.m_ItemType >= 2256 && HeldItem.m_ItemType <= 2267)
{
m_Record = HeldItem.m_ItemType;
a_Player->GetInventory().RemoveOneEquippedItem();
PlayRecord();
}
}
else if (m_Record >= 2256 && m_Record <= 2267)
{
EjectRecord();
m_Record = 0;
}
}
void cJukeboxEntity::PlayRecord( void )
{
m_World->BroadcastSoundParticleEffect(1005, m_PosX * 8, m_PosY * 8, m_PosZ * 8, m_Record);
}
void cJukeboxEntity::EjectRecord( void )
{
cItems Drops;
Drops.push_back(cItem(m_Record, 1, 0));
m_World->SpawnItemPickups(Drops, m_PosX, m_PosY+1, m_PosZ);
m_World->BroadcastSoundParticleEffect(1005, m_PosX * 8, m_PosY * 8, m_PosZ * 8, 0);
}
int cJukeboxEntity::GetRecord( void )
{
return m_Record;
}
void cJukeboxEntity::SetRecord( int a_Record )
{
m_Record = a_Record;
}
bool cJukeboxEntity::LoadFromJson( const Json::Value & a_Value )
{
m_PosX = a_Value.get("x", 0).asInt();
m_PosY = a_Value.get("y", 0).asInt();
m_PosZ = a_Value.get("z", 0).asInt();
m_Record = a_Value.get("Record", 0).asInt();
return true;
}
void cJukeboxEntity::SaveToJson( Json::Value & a_Value )
{
a_Value["x"] = m_PosX;
a_Value["y"] = m_PosY;
a_Value["z"] = m_PosZ;
a_Value["Record"] = m_Record;
}
+43
View File
@@ -0,0 +1,43 @@
#pragma once
#include "BlockEntity.h"
#include "../Player.h"
namespace Json
{
class Value;
}
class cJukeboxEntity :
public cBlockEntity
{
public:
cJukeboxEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World);
virtual ~cJukeboxEntity();
bool LoadFromJson( const Json::Value& a_Value );
virtual void SaveToJson( Json::Value& a_Value ) override;
int GetRecord( void );
void SetRecord( int a_Record );
void PlayRecord( void );
void EjectRecord( void );
virtual void UsedBy( cPlayer * a_Player ) override;
virtual void SendTo(cClientHandle & a_Client) override { };
private:
int m_Record;
};
+159
View File
@@ -0,0 +1,159 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "NoteEntity.h"
#include "../World.h"
#include <json/json.h>
cNoteEntity::cNoteEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cWorld * a_World)
: cBlockEntity(E_BLOCK_NOTE_BLOCK, a_BlockX, a_BlockY, a_BlockZ, a_World)
, m_Pitch( 0 )
{
}
cNoteEntity::~cNoteEntity()
{
}
void cNoteEntity::UsedBy( cPlayer * a_Player )
{
IncrementPitch();
MakeSound();
}
void cNoteEntity::MakeSound( void )
{
char instrument;
AString sampleName;
switch (m_World->GetBlock(m_PosX, m_PosY - 1, m_PosZ))
{
case E_BLOCK_PLANKS:
case E_BLOCK_LOG:
case E_BLOCK_NOTE_BLOCK:
{
// TODO: add other wood-based blocks if needed
instrument = E_INST_DOUBLE_BASS;
sampleName = "note.db";
break;
}
case E_BLOCK_SAND:
case E_BLOCK_GRAVEL:
case E_BLOCK_SOULSAND:
{
instrument = E_INST_SNARE_DRUM;
sampleName = "note.snare";
break;
}
case E_BLOCK_GLASS:
case E_BLOCK_GLASS_PANE:
case E_BLOCK_GLOWSTONE:
{
instrument = E_INST_CLICKS;
sampleName = "note.hat";
break;
}
case E_BLOCK_STONE:
case E_BLOCK_STONE_BRICKS:
case E_BLOCK_COBBLESTONE:
case E_BLOCK_OBSIDIAN:
case E_BLOCK_NETHERRACK:
case E_BLOCK_BRICK:
case E_BLOCK_NETHER_BRICK:
{
// TODO: add other stone-based blocks if needed
instrument = E_INST_BASS_DRUM;
sampleName = "note.bassattack";
break;
}
default:
{
instrument = E_INST_HARP_PIANO;
sampleName = "note.harp";
break;
}
}
m_World->BroadcastBlockAction(m_PosX, m_PosY, m_PosZ, instrument, m_Pitch, E_BLOCK_NOTE_BLOCK);
// TODO: instead of calculating the power function over and over, make a precalculated table - there's only 24 pitches after all
float calcPitch = pow(2.0f, ((float)m_Pitch - 12.0f) / 12.0f);
m_World->BroadcastSoundEffect(sampleName, m_PosX * 8, m_PosY * 8, m_PosZ * 8, 3.0f, calcPitch);
}
char cNoteEntity::GetPitch( void )
{
return m_Pitch;
}
void cNoteEntity::SetPitch( char a_Pitch )
{
m_Pitch = a_Pitch % 25;
}
void cNoteEntity::IncrementPitch( void )
{
SetPitch( m_Pitch + 1 );
}
bool cNoteEntity::LoadFromJson( const Json::Value & a_Value )
{
m_PosX = a_Value.get("x", 0).asInt();
m_PosY = a_Value.get("y", 0).asInt();
m_PosZ = a_Value.get("z", 0).asInt();
m_Pitch = (char)a_Value.get("p", 0).asInt();
return true;
}
void cNoteEntity::SaveToJson( Json::Value & a_Value )
{
a_Value["x"] = m_PosX;
a_Value["y"] = m_PosY;
a_Value["z"] = m_PosZ;
a_Value["p"] = m_Pitch;
}
+52
View File
@@ -0,0 +1,52 @@
#pragma once
#include "BlockEntity.h"
namespace Json
{
class Value;
}
enum ENUM_NOTE_INSTRUMENTS
{
E_INST_HARP_PIANO = 0,
E_INST_DOUBLE_BASS = 1,
E_INST_SNARE_DRUM = 2,
E_INST_CLICKS = 3,
E_INST_BASS_DRUM = 4
};
class cNoteEntity :
public cBlockEntity
{
public:
cNoteEntity(int a_X, int a_Y, int a_Z, cWorld * a_World);
virtual ~cNoteEntity();
bool LoadFromJson( const Json::Value& a_Value );
virtual void SaveToJson( Json::Value& a_Value ) override;
char GetPitch( void );
void SetPitch( char a_Pitch );
void IncrementPitch( void );
void MakeSound( void );
virtual void UsedBy( cPlayer * a_Player ) override;
virtual void SendTo(cClientHandle & a_Client) override { };
private:
unsigned char m_Pitch;
};
+130
View File
@@ -0,0 +1,130 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
#include "SignEntity.h"
#include "../Player.h"
// #include "ClientHandle.h"
// #include "World.h"
// #include "Root.h"
#include <json/json.h>
cSignEntity::cSignEntity(BLOCKTYPE a_BlockType, int a_X, int a_Y, int a_Z, cWorld * a_World)
: cBlockEntity(a_BlockType, a_X, a_Y, a_Z, a_World)
{
}
cSignEntity::~cSignEntity()
{
}
// It don't do anything when 'used'
void cSignEntity::UsedBy( cPlayer * a_Player )
{
(void)a_Player;
}
void cSignEntity::SetLines( const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4 )
{
m_Line[0] = a_Line1;
m_Line[1] = a_Line2;
m_Line[2] = a_Line3;
m_Line[3] = a_Line4;
}
void cSignEntity::SetLine( int a_Index, const AString & a_Line )
{
if( a_Index < 4 && a_Index > -1 )
{
m_Line[a_Index] = a_Line;
}
}
AString cSignEntity::GetLine( int a_Index ) const
{
if( a_Index < 4 && a_Index > -1 )
{
return m_Line[a_Index];
}
return "";
}
void cSignEntity::SendTo(cClientHandle & a_Client)
{
a_Client.SendUpdateSign(m_PosX, m_PosY, m_PosZ, m_Line[0], m_Line[1], m_Line[2], m_Line[3]);
}
#define READ(File, Var) \
if (File.Read(&Var, sizeof(Var)) != sizeof(Var)) \
{ \
LOGERROR("ERROR READING cSignEntity %s FROM FILE (line %d)", #Var, __LINE__); \
return false; \
}
bool cSignEntity::LoadFromJson( const Json::Value & a_Value )
{
m_PosX = a_Value.get("x", 0).asInt();
m_PosY = a_Value.get("y", 0).asInt();
m_PosZ = a_Value.get("z", 0).asInt();
m_Line[0] = a_Value.get("Line1", "").asString();
m_Line[1] = a_Value.get("Line2", "").asString();
m_Line[2] = a_Value.get("Line3", "").asString();
m_Line[3] = a_Value.get("Line4", "").asString();
return true;
}
void cSignEntity::SaveToJson( Json::Value & a_Value )
{
a_Value["x"] = m_PosX;
a_Value["y"] = m_PosY;
a_Value["z"] = m_PosZ;
a_Value["Line1"] = m_Line[0];
a_Value["Line2"] = m_Line[1];
a_Value["Line3"] = m_Line[2];
a_Value["Line4"] = m_Line[3];
}
+41
View File
@@ -0,0 +1,41 @@
#pragma once
#include "BlockEntity.h"
namespace Json
{
class Value;
}
class cSignEntity :
public cBlockEntity
{
public:
cSignEntity(BLOCKTYPE a_BlockType, int a_X, int a_Y, int a_Z, cWorld * a_World);
virtual ~cSignEntity();
bool LoadFromJson( const Json::Value& a_Value );
virtual void SaveToJson(Json::Value& a_Value ) override;
void SetLines( const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4 );
void SetLine( int a_Index, const AString & a_Line );
AString GetLine( int a_Index ) const;
virtual void UsedBy( cPlayer * a_Player ) override;
virtual void SendTo(cClientHandle & a_Client) override;
private:
AString m_Line[4];
};