summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--source/Chunk.cpp64
-rw-r--r--source/Chunk.h6
-rw-r--r--source/ChunkMap.cpp33
-rw-r--r--source/ChunkMap.h6
-rw-r--r--source/DispenserEntity.cpp147
-rw-r--r--source/DispenserEntity.h5
-rw-r--r--source/Simulator/RedstoneSimulator.cpp37
-rw-r--r--source/Simulator/RedstoneSimulator.h1
-rw-r--r--source/World.cpp18
-rw-r--r--source/World.h6
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