diff options
-rw-r--r-- | source/Chunk.cpp | 64 | ||||
-rw-r--r-- | source/Chunk.h | 6 | ||||
-rw-r--r-- | source/ChunkMap.cpp | 33 | ||||
-rw-r--r-- | source/ChunkMap.h | 6 | ||||
-rw-r--r-- | source/DispenserEntity.cpp | 147 | ||||
-rw-r--r-- | source/DispenserEntity.h | 5 | ||||
-rw-r--r-- | source/Simulator/RedstoneSimulator.cpp | 37 | ||||
-rw-r--r-- | source/Simulator/RedstoneSimulator.h | 1 | ||||
-rw-r--r-- | source/World.cpp | 18 | ||||
-rw-r--r-- | source/World.h | 6 |
10 files changed, 320 insertions, 3 deletions
diff --git a/source/Chunk.cpp b/source/Chunk.cpp index 165eda831..96522d6bf 100644 --- a/source/Chunk.cpp +++ b/source/Chunk.cpp @@ -442,7 +442,15 @@ void cChunk::Tick(float a_Dt, MTRand & a_TickRandom) m_IsDirty = ((cFurnaceEntity *)(*itr))->Tick( a_Dt ) | m_IsDirty; } } - + + // Tick block entities (dispensers) + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) + { + if ((*itr)->GetBlockType() == E_BLOCK_DISPENSER) + { + m_IsDirty = ((cDispenserEntity *)(*itr))->Tick( a_Dt ) | m_IsDirty; + } + } ApplyWeatherToTop(a_TickRandom); } @@ -1588,6 +1596,28 @@ bool cChunk::ForEachChest(cChestCallback & a_Callback) +bool cChunk::ForEachDispenser(cDispenserCallback & a_Callback) +{ + // The blockentity list is locked by the parent chunkmap's CS + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) + { + ++itr2; + if ((*itr)->GetBlockType() != E_BLOCK_DISPENSER) + { + continue; + } + if (a_Callback.Item((cDispenserEntity *)*itr)) + { + return false; + } + } // for itr - m_BlockEntitites[] + return true; +} + + + + + bool cChunk::ForEachFurnace(cFurnaceCallback & a_Callback) { // The blockentity list is locked by the parent chunkmap's CS @@ -1650,6 +1680,38 @@ bool cChunk::DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallb +bool cChunk::DoWithDispenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDispenserCallback & a_Callback) +{ + // The blockentity list is locked by the parent chunkmap's CS + for (cBlockEntityList::iterator itr = m_BlockEntities.begin(), itr2 = itr; itr != m_BlockEntities.end(); itr = itr2) + { + ++itr2; + if (((*itr)->GetPosX() != a_BlockX) || ((*itr)->GetPosY() != a_BlockY) || ((*itr)->GetPosZ() != a_BlockZ)) + { + continue; + } + if ((*itr)->GetBlockType() != E_BLOCK_DISPENSER) + { + // There is a block entity here, but of different type. No other block entity can be here, so we can safely bail out + return false; + } + + // The correct block entity is here + if (a_Callback.Item((cDispenserEntity *)*itr)) + { + return false; + } + return true; + } // for itr - m_BlockEntitites[] + + // Not found: + return false; +} + + + + + bool cChunk::DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback) { // The blockentity list is locked by the parent chunkmap's CS diff --git a/source/Chunk.h b/source/Chunk.h index fbf65b4fd..b5dfd380d 100644 --- a/source/Chunk.h +++ b/source/Chunk.h @@ -168,12 +168,18 @@ public: /// Calls the callback for each chest; returns true if all chests processed, false if the callback aborted by returning true bool ForEachChest(cChestCallback & a_Callback); // Lua-accessible + /// Calls the callback for each dispenser; returns true if all dispensers processed, false if the callback aborted by returning true + bool ForEachDispenser(cDispenserCallback & a_Callback); // Lua-accessible + /// Calls the callback for each furnace; returns true if all furnaces processed, false if the callback aborted by returning true bool ForEachFurnace(cFurnaceCallback & a_Callback); // Lua-accessible /// Calls the callback for the chest at the specified coords; returns false if there's no chest at those coords, true if found bool DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback); // Lua-acessible + /// Calls the callback for the dispenser at the specified coords; returns false if there's no dispenser at those coords, true if found + bool DoWithDispenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDispenserCallback & a_Callback); // Lua-accessible + /// Calls the callback for the furnace at the specified coords; returns false if there's no furnace at those coords, true if found bool DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback); // Lua-accessible diff --git a/source/ChunkMap.cpp b/source/ChunkMap.cpp index 61ca51ab0..ede16ffa6 100644 --- a/source/ChunkMap.cpp +++ b/source/ChunkMap.cpp @@ -1354,6 +1354,21 @@ bool cChunkMap::ForEachChestInChunk(int a_ChunkX, int a_ChunkZ, cChestCallback & +bool cChunkMap::ForEachDispenserInChunk(int a_ChunkX, int a_ChunkZ, cDispenserCallback & a_Callback) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ); + if ((Chunk == NULL) && !Chunk->IsValid()) + { + return false; + } + return Chunk->ForEachDispenser(a_Callback); +} + + + + + bool cChunkMap::ForEachFurnaceInChunk(int a_ChunkX, int a_ChunkZ, cFurnaceCallback & a_Callback) { cCSLock Lock(m_CSLayers); @@ -1387,6 +1402,24 @@ bool cChunkMap::DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCa +bool cChunkMap::DoWithDispenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDispenserCallback & a_Callback) +{ + int ChunkX, ChunkZ; + int BlockX = a_BlockX, BlockY = a_BlockY, BlockZ = a_BlockZ; + cChunkDef::AbsoluteToRelative(BlockX, BlockY, BlockZ, ChunkX, ChunkZ); + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(ChunkX, ZERO_CHUNK_Y, ChunkZ); + if ((Chunk == NULL) && !Chunk->IsValid()) + { + return false; + } + return Chunk->DoWithDispenserAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); +} + + + + + bool cChunkMap::DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback) { int ChunkX, ChunkZ; diff --git a/source/ChunkMap.h b/source/ChunkMap.h index 6a1a26d83..ba037444a 100644 --- a/source/ChunkMap.h +++ b/source/ChunkMap.h @@ -184,12 +184,18 @@ public: /// Calls the callback for each chest in the specified chunk; returns true if all chests processed, false if the callback aborted by returning true bool ForEachChestInChunk (int a_ChunkX, int a_ChunkZ, cChestCallback & a_Callback); // Lua-accessible + /// Calls the callback for each dispenser in the specified chunk; returns true if all dispensers processed, false if the callback aborted by returning true + bool ForEachDispenserInChunk(int a_ChunkX, int a_ChunkZ, cDispenserCallback & a_Callback); // Lua-accessible + /// Calls the callback for each furnace in the specified chunk; returns true if all furnaces processed, false if the callback aborted by returning true bool ForEachFurnaceInChunk(int a_ChunkX, int a_ChunkZ, cFurnaceCallback & a_Callback); // Lua-accessible /// Calls the callback for the chest at the specified coords; returns false if there's no chest at those coords, true if found bool DoWithChestAt (int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback); // Lua-acessible + /// Calls the callback for the dispenser at the specified coords; returns false if there's no dispenser at those coords, true if found + bool DoWithDispenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDispenserCallback & a_Callback); // Lua-accessible + /// Calls the callback for the furnace at the specified coords; returns false if there's no furnace at those coords, true if found bool DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback); // Lua-accessible diff --git a/source/DispenserEntity.cpp b/source/DispenserEntity.cpp index dc13f30b8..45f9d58f3 100644 --- a/source/DispenserEntity.cpp +++ b/source/DispenserEntity.cpp @@ -11,15 +11,30 @@ #include "Server.h"
#include "Pickup.h"
#include "Root.h"
+#include "Simulator/FluidSimulator.h"
#include <json/json.h>
+#define AddDispenserDir(x, y, z, dir) \
+ switch (dir) \
+ { \
+ case 2: (z) --; break; \
+ case 3: (z) ++; break; \
+ case 4: (x) --; break; \
+ case 5: (x) ++; break; \
+ }
+
+
+
+
+
cDispenserEntity::cDispenserEntity(int a_X, int a_Y, int a_Z, cWorld * a_World)
: cBlockEntity( E_BLOCK_DISPENSER, a_X, a_Y, a_Z, a_World )
, m_Items( new cItem[9] )
+ , m_CanDispense( 0 )
{
SetBlockEntity(this); // cBlockEntityWindowOwner
}
@@ -51,7 +66,7 @@ void cDispenserEntity::Destroy() {
// Drop items
cItems Pickups;
- for( int i = 0; i < 3; i++)
+ for( int i = 0; i < 9; i++)
{
if( !m_Items[i].IsEmpty() )
{
@@ -66,6 +81,120 @@ void cDispenserEntity::Destroy() +void cDispenserEntity::Dispense()
+{
+ int Disp_X = m_PosX;
+ int Disp_Y = m_PosY;
+ int Disp_Z = m_PosZ;
+ NIBBLETYPE Meta = m_World->GetBlockMeta( m_PosX, m_PosY, m_PosZ );
+ AddDispenserDir( Disp_X, Disp_Y, Disp_Z, Meta );
+ char OccupiedSlots[9];
+ char SlotsCnt = 0;
+ for( int i = 0; i < 9; i++)
+ {
+ if( !m_Items[i].IsEmpty() )
+ {
+ OccupiedSlots[SlotsCnt] = i;
+ SlotsCnt++;
+ }
+ }
+ if(SlotsCnt > 0)
+ {
+ MTRand r1;
+ cItem Drop = m_Items[OccupiedSlots[r1.randInt() % SlotsCnt]];
+ switch( m_Items[OccupiedSlots[r1.randInt() % SlotsCnt]].m_ItemType )
+ {
+ case E_ITEM_BUCKET:
+ {
+ BLOCKTYPE DispBlock = m_World->GetBlock( Disp_X, Disp_Y, Disp_Z );
+ if( DispBlock == E_BLOCK_STATIONARY_WATER )
+ {
+ m_World->SetBlock( Disp_X, Disp_Y, Disp_Z, E_BLOCK_AIR, 0 );
+ m_Items[OccupiedSlots[r1.randInt() % SlotsCnt]].m_ItemType = E_ITEM_WATER_BUCKET;
+ }
+ else if( DispBlock == E_BLOCK_STATIONARY_LAVA )
+ {
+ m_World->SetBlock( Disp_X, Disp_Y, Disp_Z, E_BLOCK_AIR, 0 );
+ m_Items[OccupiedSlots[r1.randInt() % SlotsCnt]].m_ItemType = E_ITEM_LAVA_BUCKET;
+ }
+ else
+ {
+ cItems Pickups;
+ Pickups.push_back(cItem(Drop.m_ItemType, 1, Drop.m_ItemHealth));
+ m_World->SpawnItemPickups(Pickups, Disp_X, Disp_Y, Disp_Z);
+ m_Items[OccupiedSlots[r1.randInt() % SlotsCnt]].m_ItemCount--;
+ }
+ break;
+ }
+ case E_ITEM_WATER_BUCKET:
+ {
+ BLOCKTYPE DispBlock = m_World->GetBlock( Disp_X, Disp_Y, Disp_Z );
+ if( DispBlock == E_BLOCK_AIR || IsBlockLiquid(DispBlock) || cFluidSimulator::CanWashAway(DispBlock) )
+ {
+ m_World->SetBlock( Disp_X, Disp_Y, Disp_Z, E_BLOCK_STATIONARY_WATER, 0 );
+ m_Items[OccupiedSlots[r1.randInt() % SlotsCnt]].m_ItemType = E_ITEM_BUCKET;
+ }
+ else
+ {
+ cItems Pickups;
+ Pickups.push_back(cItem(Drop.m_ItemType, 1, Drop.m_ItemHealth));
+ m_World->SpawnItemPickups(Pickups, Disp_X, Disp_Y, Disp_Z);
+ m_Items[OccupiedSlots[r1.randInt() % SlotsCnt]].m_ItemCount--;
+ }
+ break;
+ }
+ case E_ITEM_LAVA_BUCKET:
+ {
+ BLOCKTYPE DispBlock = m_World->GetBlock( Disp_X, Disp_Y, Disp_Z );
+ if( DispBlock == E_BLOCK_AIR || IsBlockLiquid(DispBlock) || cFluidSimulator::CanWashAway(DispBlock) )
+ {
+ m_World->SetBlock( Disp_X, Disp_Y, Disp_Z, E_BLOCK_STATIONARY_LAVA, 0 );
+ m_Items[OccupiedSlots[r1.randInt() % SlotsCnt]].m_ItemType = E_ITEM_BUCKET;
+ }
+ else
+ {
+ cItems Pickups;
+ Pickups.push_back(cItem(Drop.m_ItemType, 1, Drop.m_ItemHealth));
+ m_World->SpawnItemPickups(Pickups, Disp_X, Disp_Y, Disp_Z);
+ m_Items[OccupiedSlots[r1.randInt() % SlotsCnt]].m_ItemCount--;
+ }
+ break;
+ }
+ default:
+ {
+ cItems Pickups;
+ Pickups.push_back(cItem(Drop.m_ItemType, 1, Drop.m_ItemHealth));
+ m_World->SpawnItemPickups(Pickups, Disp_X, Disp_Y, Disp_Z);
+ m_Items[OccupiedSlots[r1.randInt() % SlotsCnt]].m_ItemCount--;
+ break;
+ }
+ }
+ char SmokeDir;
+ switch( Meta )
+ {
+ case 2: SmokeDir = 1; break;
+ case 3: SmokeDir = 7; break;
+ case 4: SmokeDir = 3; break;
+ case 5: SmokeDir = 5; 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);
+ cWindow * Window = GetWindow();
+ if ( Window != NULL )
+ {
+ Window->BroadcastWholeWindow();
+ }
+ }
+ else
+ {
+ m_World->BroadcastSoundEffect("random.click", m_PosX * 8, m_PosY * 8, m_PosZ * 8, 1.0f, 1.2f);
+ }
+}
+
+
+
+
+
void cDispenserEntity::UsedBy(cPlayer * a_Player)
{
if (GetWindow() == NULL)
@@ -86,9 +215,23 @@ void cDispenserEntity::UsedBy(cPlayer * a_Player) +void cDispenserEntity::Activate()
+{
+ m_CanDispense = 1;
+}
+
+
+
+
+
bool cDispenserEntity::Tick( float a_Dt )
{
- return true;
+ if(m_CanDispense)
+ {
+ m_CanDispense = 0;
+ Dispense();
+ }
+ return false;
}
diff --git a/source/DispenserEntity.h b/source/DispenserEntity.h index 8f2e89833..9e69878b3 100644 --- a/source/DispenserEntity.h +++ b/source/DispenserEntity.h @@ -45,9 +45,14 @@ public: const cItem * GetSlot(int i) const { return &(m_Items[i]); }
void SetSlot(int a_Slot, const cItem & a_Item);
+
+ void Activate();
private:
cItem * m_Items;
+ bool m_CanDispense;
+
+ void Dispense();
};
diff --git a/source/Simulator/RedstoneSimulator.cpp b/source/Simulator/RedstoneSimulator.cpp index 2224d300d..1862537e0 100644 --- a/source/Simulator/RedstoneSimulator.cpp +++ b/source/Simulator/RedstoneSimulator.cpp @@ -2,6 +2,7 @@ #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules #include "RedstoneSimulator.h" +#include "../DispenserEntity.h" #include "../Piston.h" #include "../World.h" #include "../BlockID.h" @@ -387,6 +388,36 @@ void cRedstoneSimulator::HandleChange( const Vector3i & a_BlockPos ) } } // switch (BlockType) } // while (m_RefreshPistons[]) + + while (!m_RefreshDispensers.empty()) + { + Vector3i pos = m_RefreshDispensers.back(); + m_RefreshDispensers.pop_back(); + + BLOCKTYPE BlockType = m_World->GetBlock(pos); + if (BlockType == E_BLOCK_DISPENSER) + { + if (IsPowered(pos)) + { + class cActivateDispenser : + public cDispenserCallback + { + public: + cActivateDispenser() + { + } + + virtual bool Item(cDispenserEntity * a_Dispenser) override + { + a_Dispenser->Activate(); + return false; + } + } ; + cActivateDispenser DispAct; + m_World->DoWithDispenserAt(pos.x, pos.y, pos.z, DispAct); + } + } + } } @@ -416,6 +447,12 @@ bool cRedstoneSimulator::PowerBlock(const Vector3i & a_BlockPos, const Vector3i m_RefreshPistons.push_back(a_BlockPos); break; } + + case E_BLOCK_DISPENSER: + { + m_RefreshDispensers.push_back(a_BlockPos); + break; + } case E_BLOCK_REDSTONE_REPEATER_OFF: case E_BLOCK_REDSTONE_REPEATER_ON: diff --git a/source/Simulator/RedstoneSimulator.h b/source/Simulator/RedstoneSimulator.h index 089b84aeb..0b0333164 100644 --- a/source/Simulator/RedstoneSimulator.h +++ b/source/Simulator/RedstoneSimulator.h @@ -71,6 +71,7 @@ private: BlockList m_BlocksBuffer; BlockList m_RefreshPistons; + BlockList m_RefreshDispensers; BlockList m_RefreshTorchesAround; diff --git a/source/World.cpp b/source/World.cpp index 0d4080f88..d2c60690b 100644 --- a/source/World.cpp +++ b/source/World.cpp @@ -736,6 +736,15 @@ bool cWorld::ForEachChestInChunk(int a_ChunkX, int a_ChunkZ, cChestCallback & a_ +bool cWorld::ForEachDispenserInChunk(int a_ChunkX, int a_ChunkZ, cDispenserCallback & a_Callback) +{ + return m_ChunkMap->ForEachDispenserInChunk(a_ChunkX, a_ChunkZ, a_Callback); +} + + + + + bool cWorld::ForEachFurnaceInChunk(int a_ChunkX, int a_ChunkZ, cFurnaceCallback & a_Callback) { return m_ChunkMap->ForEachFurnaceInChunk(a_ChunkX, a_ChunkZ, a_Callback); @@ -754,6 +763,15 @@ bool cWorld::DoWithChestAt(int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallb +bool cWorld::DoWithDispenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDispenserCallback & a_Callback) +{ + return m_ChunkMap->DoWithDispenserAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); +} + + + + + bool cWorld::DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback) { return m_ChunkMap->DoWithFurnaceAt(a_BlockX, a_BlockY, a_BlockZ, a_Callback); diff --git a/source/World.h b/source/World.h index 9609d8b3f..cefb9abf2 100644 --- a/source/World.h +++ b/source/World.h @@ -318,12 +318,18 @@ public: /// Calls the callback for each chest in the specified chunk; returns true if all chests processed, false if the callback aborted by returning true bool ForEachChestInChunk (int a_ChunkX, int a_ChunkZ, cChestCallback & a_Callback); // Exported in ManualBindings.cpp + /// Calls the callback for each dispenser in the specified chunk; returns true if all dispensers processed, false if the callback aborted by returning true + bool ForEachDispenserInChunk(int a_ChunkX, int a_ChunkZ, cDispenserCallback & a_Callback); // Exported in ManualBindings.cpp + /// Calls the callback for each furnace in the specified chunk; returns true if all furnaces processed, false if the callback aborted by returning true bool ForEachFurnaceInChunk(int a_ChunkX, int a_ChunkZ, cFurnaceCallback & a_Callback); // Exported in ManualBindings.cpp /// Calls the callback for the chest at the specified coords; returns false if there's no chest at those coords, true if found bool DoWithChestAt (int a_BlockX, int a_BlockY, int a_BlockZ, cChestCallback & a_Callback); // Exported in ManualBindings.cpp + /// Calls the callback for the dispenser at the specified coords; returns false if there's no dispenser at those coords, true if found + bool DoWithDispenserAt(int a_BlockX, int a_BlockY, int a_BlockZ, cDispenserCallback & a_Callback); // Exported in ManualBindings.cpp + /// Calls the callback for the furnace at the specified coords; returns false if there's no furnace at those coords, true if found bool DoWithFurnaceAt(int a_BlockX, int a_BlockY, int a_BlockZ, cFurnaceCallback & a_Callback); // Exported in ManualBindings.cpp |