diff options
Diffstat (limited to '')
-rw-r--r-- | src/BlockEntities/BlockEntity.cpp (renamed from source/BlockEntities/BlockEntity.cpp) | 0 | ||||
-rw-r--r-- | src/BlockEntities/BlockEntity.h (renamed from source/BlockEntities/BlockEntity.h) | 0 | ||||
-rw-r--r-- | src/BlockEntities/BlockEntityWithItems.h (renamed from source/BlockEntities/BlockEntityWithItems.h) | 0 | ||||
-rw-r--r-- | src/BlockEntities/ChestEntity.cpp | 172 | ||||
-rw-r--r-- | src/BlockEntities/ChestEntity.h (renamed from source/BlockEntities/ChestEntity.h) | 0 | ||||
-rw-r--r-- | src/BlockEntities/DispenserEntity.cpp (renamed from source/BlockEntities/DispenserEntity.cpp) | 0 | ||||
-rw-r--r-- | src/BlockEntities/DispenserEntity.h (renamed from source/BlockEntities/DispenserEntity.h) | 0 | ||||
-rw-r--r-- | src/BlockEntities/DropSpenserEntity.cpp (renamed from source/BlockEntities/DropSpenserEntity.cpp) | 0 | ||||
-rw-r--r-- | src/BlockEntities/DropSpenserEntity.h (renamed from source/BlockEntities/DropSpenserEntity.h) | 0 | ||||
-rw-r--r-- | src/BlockEntities/DropperEntity.cpp (renamed from source/BlockEntities/DropperEntity.cpp) | 0 | ||||
-rw-r--r-- | src/BlockEntities/DropperEntity.h (renamed from source/BlockEntities/DropperEntity.h) | 0 | ||||
-rw-r--r-- | src/BlockEntities/FurnaceEntity.cpp | 479 | ||||
-rw-r--r-- | src/BlockEntities/FurnaceEntity.h (renamed from source/BlockEntities/FurnaceEntity.h) | 0 | ||||
-rw-r--r-- | src/BlockEntities/HopperEntity.cpp (renamed from source/BlockEntities/HopperEntity.cpp) | 0 | ||||
-rw-r--r-- | src/BlockEntities/HopperEntity.h (renamed from source/BlockEntities/HopperEntity.h) | 0 | ||||
-rw-r--r-- | src/BlockEntities/JukeboxEntity.cpp | 125 | ||||
-rw-r--r-- | src/BlockEntities/JukeboxEntity.h (renamed from source/BlockEntities/JukeboxEntity.h) | 0 | ||||
-rw-r--r-- | src/BlockEntities/NoteEntity.cpp | 154 | ||||
-rw-r--r-- | src/BlockEntities/NoteEntity.h (renamed from source/BlockEntities/NoteEntity.h) | 0 | ||||
-rw-r--r-- | src/BlockEntities/SignEntity.cpp | 115 | ||||
-rw-r--r-- | src/BlockEntities/SignEntity.h (renamed from source/BlockEntities/SignEntity.h) | 0 |
21 files changed, 1045 insertions, 0 deletions
diff --git a/source/BlockEntities/BlockEntity.cpp b/src/BlockEntities/BlockEntity.cpp index 41a488717..41a488717 100644 --- a/source/BlockEntities/BlockEntity.cpp +++ b/src/BlockEntities/BlockEntity.cpp diff --git a/source/BlockEntities/BlockEntity.h b/src/BlockEntities/BlockEntity.h index 0d358b556..0d358b556 100644 --- a/source/BlockEntities/BlockEntity.h +++ b/src/BlockEntities/BlockEntity.h diff --git a/source/BlockEntities/BlockEntityWithItems.h b/src/BlockEntities/BlockEntityWithItems.h index 0846ae17e..0846ae17e 100644 --- a/source/BlockEntities/BlockEntityWithItems.h +++ b/src/BlockEntities/BlockEntityWithItems.h diff --git a/src/BlockEntities/ChestEntity.cpp b/src/BlockEntities/ChestEntity.cpp new file mode 100644 index 000000000..dfbe6ae87 --- /dev/null +++ b/src/BlockEntities/ChestEntity.cpp @@ -0,0 +1,172 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "ChestEntity.h" +#include "../Item.h" +#include "../Entities/Player.h" +#include "../UI/Window.h" +#include "json/json.h" + + + + + +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 the window is not created, open it anew: + cWindow * Window = GetWindow(); + if (Window == NULL) + { + OpenNewWindow(); + Window = GetWindow(); + } + + // Open the window for the player: + if (Window != NULL) + { + if (a_Player->GetWindow() != Window) + { + a_Player->OpenWindow(Window); + } + } + + // 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_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)); +} + + + + diff --git a/source/BlockEntities/ChestEntity.h b/src/BlockEntities/ChestEntity.h index 4f2c21e91..4f2c21e91 100644 --- a/source/BlockEntities/ChestEntity.h +++ b/src/BlockEntities/ChestEntity.h diff --git a/source/BlockEntities/DispenserEntity.cpp b/src/BlockEntities/DispenserEntity.cpp index 374f3d6e3..374f3d6e3 100644 --- a/source/BlockEntities/DispenserEntity.cpp +++ b/src/BlockEntities/DispenserEntity.cpp diff --git a/source/BlockEntities/DispenserEntity.h b/src/BlockEntities/DispenserEntity.h index fdfe4e5b4..fdfe4e5b4 100644 --- a/source/BlockEntities/DispenserEntity.h +++ b/src/BlockEntities/DispenserEntity.h diff --git a/source/BlockEntities/DropSpenserEntity.cpp b/src/BlockEntities/DropSpenserEntity.cpp index 823ed598f..823ed598f 100644 --- a/source/BlockEntities/DropSpenserEntity.cpp +++ b/src/BlockEntities/DropSpenserEntity.cpp diff --git a/source/BlockEntities/DropSpenserEntity.h b/src/BlockEntities/DropSpenserEntity.h index 0e9039915..0e9039915 100644 --- a/source/BlockEntities/DropSpenserEntity.h +++ b/src/BlockEntities/DropSpenserEntity.h diff --git a/source/BlockEntities/DropperEntity.cpp b/src/BlockEntities/DropperEntity.cpp index 5d4a8ad97..5d4a8ad97 100644 --- a/source/BlockEntities/DropperEntity.cpp +++ b/src/BlockEntities/DropperEntity.cpp diff --git a/source/BlockEntities/DropperEntity.h b/src/BlockEntities/DropperEntity.h index 8e07bc6f8..8e07bc6f8 100644 --- a/source/BlockEntities/DropperEntity.h +++ b/src/BlockEntities/DropperEntity.h diff --git a/src/BlockEntities/FurnaceEntity.cpp b/src/BlockEntities/FurnaceEntity.cpp new file mode 100644 index 000000000..b1409f5cc --- /dev/null +++ b/src/BlockEntities/FurnaceEntity.cpp @@ -0,0 +1,479 @@ + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "FurnaceEntity.h" +#include "../UI/Window.h" +#include "../Entities/Player.h" +#include "../Root.h" +#include "../Chunk.h" +#include "json/json.h" + + + + + + +enum +{ + PROGRESSBAR_SMELTING = 0, + PROGRESSBAR_FUEL = 1, +} ; + + + + + +cFurnaceEntity::cFurnaceEntity(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, cWorld * a_World) : + super(E_BLOCK_FURNACE, a_BlockX, a_BlockY, a_BlockZ, ContentsWidth, ContentsHeight, a_World), + m_BlockType(a_BlockType), + m_BlockMeta(a_BlockMeta), + m_CurrentRecipe(NULL), + m_IsCooking((a_World->GetBlock(a_BlockX, a_BlockY, a_BlockZ) == E_BLOCK_LIT_FURNACE)), + m_NeedCookTime(0), + m_TimeCooked(0), + m_FuelBurnTime(0), + m_TimeBurned(0), + m_LastProgressFuel(0), + m_LastProgressCook(0) +{ + cBlockEntityWindowOwner::SetBlockEntity(this); + m_Contents.AddListener(*this); +} + + + + + +cFurnaceEntity::~cFurnaceEntity() +{ + // Tell window its owner is destroyed + cWindow * Window = GetWindow(); + if (Window != NULL) + { + Window->OwnerDestroyed(); + } +} + + + + + +void cFurnaceEntity::UsedBy(cPlayer * a_Player) +{ + if (GetWindow() == NULL) + { + OpenWindow(new cFurnaceWindow(m_PosX, m_PosY, m_PosZ, this)); + } + cWindow * Window = GetWindow(); + if (Window != NULL) + { + if (a_Player->GetWindow() != Window) + { + a_Player->OpenWindow(Window); + BroadcastProgress(PROGRESSBAR_FUEL, m_LastProgressFuel); + BroadcastProgress(PROGRESSBAR_SMELTING, m_LastProgressCook); + } + } +} + + + + + +/// 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 cFurnaceEntity::ContinueCooking(void) +{ + UpdateInput(); + UpdateFuel(); + return m_IsCooking; +} + + + + + +bool cFurnaceEntity::Tick(float a_Dt, cChunk & a_Chunk) +{ + if (m_FuelBurnTime <= 0) + { + // No fuel is burning, reset progressbars and bail out + if ((m_LastProgressCook > 0) || (m_LastProgressFuel > 0)) + { + UpdateProgressBars(); + } + return false; + } + + if (m_IsCooking) + { + m_TimeCooked++; + if (m_TimeCooked >= m_NeedCookTime) + { + // Finished smelting one item + FinishOne(a_Chunk); + } + } + + m_TimeBurned++; + if (m_TimeBurned >= m_FuelBurnTime) + { + // The current fuel has been exhausted, use another one, if possible + BurnNewFuel(); + } + + UpdateProgressBars(); + + return true; +} + + + + + +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) + { + cItem Item; + Item.FromJson(*itr); + SetSlot(SlotIdx, Item); + SlotIdx++; + } + + m_NeedCookTime = (int)(a_Value.get("CookTime", 0).asDouble() / 50); + m_TimeCooked = (int)(a_Value.get("TimeCooked", 0).asDouble() / 50); + m_FuelBurnTime = (int)(a_Value.get("BurnTime", 0).asDouble() / 50); + m_TimeBurned = (int)(a_Value.get("TimeBurned", 0).asDouble() / 50); + + 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; + 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; + + a_Value["CookTime"] = m_NeedCookTime * 50; + a_Value["TimeCooked"] = m_TimeCooked * 50; + a_Value["BurnTime"] = m_FuelBurnTime * 50; + a_Value["TimeBurned"] = m_TimeBurned * 50; +} + + + + + +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->BroadcastProgress(a_ProgressbarID, a_Value); + } +} + + + + + +/// One item finished cooking +void cFurnaceEntity::FinishOne(cChunk & a_Chunk) +{ + m_TimeCooked = 0; + + if (m_Contents.GetSlot(fsOutput).IsEmpty()) + { + m_Contents.SetSlot(fsOutput, *m_CurrentRecipe->Out); + } + else + { + m_Contents.ChangeSlotCount(fsOutput, m_CurrentRecipe->Out->m_ItemCount); + } + m_Contents.ChangeSlotCount(fsInput, -m_CurrentRecipe->In->m_ItemCount); + + UpdateIsCooking(); +} + + + + + +void cFurnaceEntity::BurnNewFuel(void) +{ + cFurnaceRecipe * FR = cRoot::Get()->GetFurnaceRecipe(); + int NewTime = FR->GetBurnTime(m_Contents.GetSlot(fsFuel)); + if (NewTime == 0) + { + // The item in the fuel slot is not suitable + m_FuelBurnTime = 0; + m_TimeBurned = 0; + SetIsCooking(false); + return; + } + + // Is the input and output ready for cooking? + if (!CanCookInputToOutput()) + { + return; + } + + // Burn one new fuel: + m_FuelBurnTime = NewTime; + m_TimeBurned = 0; + SetIsCooking(true); + if (m_Contents.GetSlot(fsFuel).m_ItemType == E_ITEM_LAVA_BUCKET) + { + m_Contents.SetSlot(fsFuel, cItem(E_ITEM_BUCKET)); + } + else + { + m_Contents.ChangeSlotCount(fsFuel, -1); + } +} + + + + + +void cFurnaceEntity::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) +{ + super::OnSlotChanged(a_ItemGrid, a_SlotNum); + + if (m_World == NULL) + { + // The furnace isn't initialized yet, do no processing + return; + } + + ASSERT(a_ItemGrid == &m_Contents); + switch (a_SlotNum) + { + case fsInput: + { + UpdateInput(); + break; + } + + case fsFuel: + { + UpdateFuel(); + break; + } + + case fsOutput: + { + UpdateOutput(); + break; + } + } +} + + + + + + +/// Updates the current recipe, based on the current input +void cFurnaceEntity::UpdateInput(void) +{ + if (!m_Contents.GetSlot(fsInput).IsStackableWith(m_LastInput)) + { + // The input is different from what we had before, reset the cooking time + m_TimeCooked = 0; + } + m_LastInput = m_Contents.GetSlot(fsInput); + + cFurnaceRecipe * FR = cRoot::Get()->GetFurnaceRecipe(); + m_CurrentRecipe = FR->GetRecipeFrom(m_Contents.GetSlot(fsInput)); + if (!CanCookInputToOutput()) + { + // This input cannot be cooked + m_NeedCookTime = 0; + SetIsCooking(false); + } + else + { + m_NeedCookTime = m_CurrentRecipe->CookTime; + SetIsCooking(true); + + // Start burning new fuel if there's no flame now: + if (GetFuelBurnTimeLeft() <= 0) + { + BurnNewFuel(); + } + } +} + + + + + +/// Called when the fuel slot changes or when the fuel is spent, burns another piece of fuel if appropriate +void cFurnaceEntity::UpdateFuel(void) +{ + if (m_FuelBurnTime > m_TimeBurned) + { + // The current fuel is still burning, don't modify anything: + return; + } + + // The current fuel is spent, try to burn some more: + BurnNewFuel(); +} + + + + + +/// Called when the output slot changes; starts burning if space became available +void cFurnaceEntity::UpdateOutput(void) +{ + if (!CanCookInputToOutput()) + { + // Cannot cook anymore: + m_TimeCooked = 0; + m_NeedCookTime = 0; + SetIsCooking(false); + return; + } + + // No need to burn new fuel, the Tick() function will take care of that + + // Can cook, start cooking if not already underway: + m_NeedCookTime = m_CurrentRecipe->CookTime; + SetIsCooking(m_FuelBurnTime > 0); +} + + + + + +/// Updates the m_IsCooking, based on the input slot, output slot and m_FuelBurnTime / m_TimeBurned +void cFurnaceEntity::UpdateIsCooking(void) +{ + if ( + !CanCookInputToOutput() || // Cannot cook this + (m_FuelBurnTime <= 0) || // No fuel + (m_TimeBurned >= m_FuelBurnTime) // Fuel burnt out + ) + { + // Reset everything + SetIsCooking(false); + m_TimeCooked = 0; + m_NeedCookTime = 0; + return; + } + + SetIsCooking(true); +} + + + + + +/// Returns true if the input can be cooked into output and the item counts allow for another cooking operation +bool cFurnaceEntity::CanCookInputToOutput(void) const +{ + if (m_CurrentRecipe == NULL) + { + // This input cannot be cooked + return false; + } + + if (m_Contents.GetSlot(fsOutput).IsEmpty()) + { + // The output is empty, can cook + return true; + } + + if (!m_Contents.GetSlot(fsOutput).IsStackableWith(*m_CurrentRecipe->Out)) + { + // The output slot is blocked with something that cannot be stacked with the recipe's output + return false; + } + + if (m_Contents.GetSlot(fsOutput).IsFullStack()) + { + // Cannot add any more items to the output slot + return false; + } + + return true; +} + + + + + +/// Broadcasts progressbar updates, if needed +void cFurnaceEntity::UpdateProgressBars(void) +{ + // In order to preserve bandwidth, an update is sent only every 10th tick + // That's why the comparisons use the division by eight + + int CurFuel = (m_FuelBurnTime > 0) ? (200 - 200 * m_TimeBurned / m_FuelBurnTime) : 0; + if ((CurFuel / 8) != (m_LastProgressFuel / 8)) + { + BroadcastProgress(PROGRESSBAR_FUEL, CurFuel); + m_LastProgressFuel = CurFuel; + } + + int CurCook = (m_NeedCookTime > 0) ? (200 * m_TimeCooked / m_NeedCookTime) : 0; + if ((CurCook / 8) != (m_LastProgressCook / 8)) + { + BroadcastProgress(PROGRESSBAR_SMELTING, CurCook); + m_LastProgressCook = CurCook; + } +} + + + + + +void cFurnaceEntity::SetIsCooking(bool a_IsCooking) +{ + if (a_IsCooking == m_IsCooking) + { + return; + } + + m_IsCooking = a_IsCooking; + + // Light or extinguish the furnace: + m_World->FastSetBlock(m_PosX, m_PosY, m_PosZ, m_IsCooking ? E_BLOCK_LIT_FURNACE : E_BLOCK_FURNACE, m_BlockMeta); +} + + + + diff --git a/source/BlockEntities/FurnaceEntity.h b/src/BlockEntities/FurnaceEntity.h index 9464fd175..9464fd175 100644 --- a/source/BlockEntities/FurnaceEntity.h +++ b/src/BlockEntities/FurnaceEntity.h diff --git a/source/BlockEntities/HopperEntity.cpp b/src/BlockEntities/HopperEntity.cpp index 41849b1b3..41849b1b3 100644 --- a/source/BlockEntities/HopperEntity.cpp +++ b/src/BlockEntities/HopperEntity.cpp diff --git a/source/BlockEntities/HopperEntity.h b/src/BlockEntities/HopperEntity.h index 3eaa05b7c..3eaa05b7c 100644 --- a/source/BlockEntities/HopperEntity.h +++ b/src/BlockEntities/HopperEntity.h diff --git a/src/BlockEntities/JukeboxEntity.cpp b/src/BlockEntities/JukeboxEntity.cpp new file mode 100644 index 000000000..33042179d --- /dev/null +++ b/src/BlockEntities/JukeboxEntity.cpp @@ -0,0 +1,125 @@ + +#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) : + super(E_BLOCK_JUKEBOX, a_BlockX, a_BlockY, a_BlockZ, a_World), + m_Record(0) +{ +} + + + + + +cJukeboxEntity::~cJukeboxEntity() +{ + EjectRecord(); +} + + + + + +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 + { + EjectRecord(); + } +} + + + + + +void cJukeboxEntity::PlayRecord(void) +{ + m_World->BroadcastSoundParticleEffect(1005, m_PosX, m_PosY, m_PosZ, m_Record); +} + + + + + +void cJukeboxEntity::EjectRecord(void) +{ + if ((m_Record < E_ITEM_FIRST_DISC) || (m_Record > E_ITEM_LAST_DISC)) + { + // There's no record here + return; + } + + cItems Drops; + Drops.push_back(cItem(m_Record, 1, 0)); + m_World->SpawnItemPickups(Drops, m_PosX + 0.5, m_PosY + 1, m_PosZ + 0.5, 8); + m_World->BroadcastSoundParticleEffect(1005, m_PosX, m_PosY, m_PosZ, 0); + m_Record = 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; +} + + + + diff --git a/source/BlockEntities/JukeboxEntity.h b/src/BlockEntities/JukeboxEntity.h index fcafdc479..fcafdc479 100644 --- a/source/BlockEntities/JukeboxEntity.h +++ b/src/BlockEntities/JukeboxEntity.h diff --git a/src/BlockEntities/NoteEntity.cpp b/src/BlockEntities/NoteEntity.cpp new file mode 100644 index 000000000..9a33ead62 --- /dev/null +++ b/src/BlockEntities/NoteEntity.cpp @@ -0,0 +1,154 @@ + +#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) : + super(E_BLOCK_NOTE_BLOCK, a_BlockX, a_BlockY, a_BlockZ, a_World), + m_Pitch(0) +{ +} + + + + + +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; +} + + + + diff --git a/source/BlockEntities/NoteEntity.h b/src/BlockEntities/NoteEntity.h index e2d088f44..e2d088f44 100644 --- a/source/BlockEntities/NoteEntity.h +++ b/src/BlockEntities/NoteEntity.h diff --git a/src/BlockEntities/SignEntity.cpp b/src/BlockEntities/SignEntity.cpp new file mode 100644 index 000000000..df8774377 --- /dev/null +++ b/src/BlockEntities/SignEntity.cpp @@ -0,0 +1,115 @@ + +// SignEntity.cpp + +// Implements the cSignEntity class representing a single sign in the world + +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules +#include "json/json.h" +#include "SignEntity.h" +#include "../Entities/Player.h" + + + + + +cSignEntity::cSignEntity(BLOCKTYPE a_BlockType, int a_X, int a_Y, int a_Z, cWorld * a_World) : + super(a_BlockType, a_X, a_Y, a_Z, a_World) +{ +} + + + + + +// It don't do anything when 'used' +void cSignEntity::UsedBy(cPlayer * a_Player) +{ + UNUSED(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 < 0) || (a_Index >= ARRAYCOUNT(m_Line))) + { + LOGWARNING("%s: setting a non-existent line %d (value \"%s\"", __FUNCTION__, a_Index, a_Line.c_str()); + return; + } + m_Line[a_Index] = a_Line; +} + + + + + +AString cSignEntity::GetLine(int a_Index) const +{ + if ((a_Index < 0) || (a_Index >= ARRAYCOUNT(m_Line))) + { + LOGWARNING("%s: requesting a non-existent line %d", __FUNCTION__, a_Index); + return ""; + } + return m_Line[a_Index]; +} + + + + + +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]); +} + + + + + +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]; +} + + + + diff --git a/source/BlockEntities/SignEntity.h b/src/BlockEntities/SignEntity.h index d998ff1e8..d998ff1e8 100644 --- a/source/BlockEntities/SignEntity.h +++ b/src/BlockEntities/SignEntity.h |