From 575abe8691bf2f615f17e6212ea5ec6265ffd4ed Mon Sep 17 00:00:00 2001 From: "luksor111@gmail.com" Date: Wed, 26 Dec 2012 17:16:33 +0000 Subject: Dispensers can dispense items and liquids now git-svn-id: http://mc-server.googlecode.com/svn/trunk@1105 0a769ca7-a7f5-676a-18bf-c427514a06d6 --- source/Chunk.cpp | 64 +++++++++++++- source/Chunk.h | 6 ++ source/ChunkMap.cpp | 33 ++++++++ source/ChunkMap.h | 6 ++ source/DispenserEntity.cpp | 147 ++++++++++++++++++++++++++++++++- source/DispenserEntity.h | 5 ++ source/Simulator/RedstoneSimulator.cpp | 37 +++++++++ source/Simulator/RedstoneSimulator.h | 1 + source/World.cpp | 18 ++++ source/World.h | 6 ++ 10 files changed, 320 insertions(+), 3 deletions(-) (limited to 'source') 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 +#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 -- cgit v1.2.3