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:
@@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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];
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user