Cuberite
A lightweight, fast and extensible game server for Minecraft
DispenserEntity.cpp
Go to the documentation of this file.
1 
2 #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
3 
4 #include "DispenserEntity.h"
5 #include "../Simulator/FluidSimulator.h"
6 #include "../Entities/Boat.h"
7 #include "../Chunk.h"
8 
9 #include "../Defines.h"
10 #include "../World.h"
11 #include "../Entities/ProjectileEntity.h"
12 
13 
14 
15 
16 cDispenserEntity::cDispenserEntity(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, Vector3i a_Pos, cWorld * a_World):
17  super(a_BlockType, a_BlockMeta, a_Pos, a_World)
18 {
19  ASSERT(a_BlockType == E_BLOCK_DISPENSER);
20 }
21 
22 
23 
24 
25 
26 void cDispenserEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum)
27 {
28  Vector3i dispRelCoord(GetRelPos());
29  auto meta = a_Chunk.GetMeta(dispRelCoord);
30  AddDropSpenserDir(dispRelCoord, meta);
31  auto dispChunk = a_Chunk.GetRelNeighborChunkAdjustCoords(dispRelCoord);
32  if (dispChunk == nullptr)
33  {
34  // Would dispense into / interact with a non-loaded chunk, ignore the tick
35  return;
36  }
37 
38  BLOCKTYPE dispBlock = dispChunk->GetBlock(dispRelCoord);
39  auto dispAbsCoord = dispChunk->RelativeToAbsolute(dispRelCoord);
40 
41  // Dispense the item:
42  const cItem & SlotItem = m_Contents.GetSlot(a_SlotNum);
43  if (ItemCategory::IsMinecart(SlotItem.m_ItemType) && IsBlockRail(dispBlock)) // only actually place the minecart if there are rails!
44  {
45  if (m_World->SpawnMinecart(dispAbsCoord.x + 0.5, dispAbsCoord.y + 0.5, dispAbsCoord.z + 0.5, SlotItem.m_ItemType) != cEntity::INVALID_ID)
46  {
47  m_Contents.ChangeSlotCount(a_SlotNum, -1);
48  }
49  return;
50  }
51  switch (SlotItem.m_ItemType)
52  {
53  case E_ITEM_BUCKET:
54  {
55  LOGD("Dispensing empty bucket in slot %d; dispBlock is \"%s\" (%d).", a_SlotNum, ItemTypeToString(dispBlock).c_str(), dispBlock);
56  switch (dispBlock)
57  {
59  case E_BLOCK_WATER:
60  {
61  if (ScoopUpLiquid(a_SlotNum, E_ITEM_WATER_BUCKET))
62  {
63  dispChunk->SetBlock(dispRelCoord, E_BLOCK_AIR, 0);
64  }
65  break;
66  }
68  case E_BLOCK_LAVA:
69  {
70  if (ScoopUpLiquid(a_SlotNum, E_ITEM_LAVA_BUCKET))
71  {
72  dispChunk->SetBlock(dispRelCoord, E_BLOCK_AIR, 0);
73  }
74  break;
75  }
76  default:
77  {
78  DropFromSlot(a_Chunk, a_SlotNum);
79  break;
80  }
81  }
82  break;
83  } // E_ITEM_BUCKET
84 
86  {
87  LOGD("Dispensing water bucket in slot %d; dispBlock is \"%s\" (%d).", a_SlotNum, ItemTypeToString(dispBlock).c_str(), dispBlock);
88  if (EmptyLiquidBucket(dispBlock, a_SlotNum))
89  {
90  dispChunk->SetBlock(dispRelCoord, E_BLOCK_WATER, 0);
91  }
92  else
93  {
94  DropFromSlot(a_Chunk, a_SlotNum);
95  }
96  break;
97  }
98 
99  case E_ITEM_LAVA_BUCKET:
100  {
101  LOGD("Dispensing lava bucket in slot %d; dispBlock is \"%s\" (%d).", a_SlotNum, ItemTypeToString(dispBlock).c_str(), dispBlock);
102  if (EmptyLiquidBucket(dispBlock, a_SlotNum))
103  {
104  dispChunk->SetBlock(dispRelCoord, E_BLOCK_LAVA, 0);
105  }
106  else
107  {
108  DropFromSlot(a_Chunk, a_SlotNum);
109  }
110  break;
111  }
112 
113  case E_ITEM_SPAWN_EGG:
114  {
115  double MobX = 0.5 + dispAbsCoord.x;
116  double MobZ = 0.5 + dispAbsCoord.z;
117  if (m_World->SpawnMob(MobX, dispAbsCoord.y, MobZ, static_cast<eMonsterType>(m_Contents.GetSlot(a_SlotNum).m_ItemDamage), false) != cEntity::INVALID_ID)
118  {
119  m_Contents.ChangeSlotCount(a_SlotNum, -1);
120  }
121  break;
122  }
123 
124  case E_BLOCK_TNT:
125  {
126  // Spawn a primed TNT entity, if space allows:
127  if (!cBlockInfo::IsSolid(dispBlock))
128  {
129  m_World->SpawnPrimedTNT(Vector3d(0.5, 0.5, 0.5) + dispAbsCoord, 80, 0); // 80 ticks fuse, no initial velocity
130  m_Contents.ChangeSlotCount(a_SlotNum, -1);
131  }
132  break;
133  }
134 
136  {
137  // Spawn fire if the block in front is air.
138  if (dispBlock == E_BLOCK_AIR)
139  {
140  dispChunk->SetBlock(dispRelCoord, E_BLOCK_FIRE, 0);
141 
142  bool ItemBroke = m_Contents.DamageItem(a_SlotNum, 1);
143 
144  if (ItemBroke)
145  {
146  m_Contents.ChangeSlotCount(a_SlotNum, -1);
147  }
148  }
149  break;
150  }
151 
152  case E_ITEM_FIRE_CHARGE:
153  {
155  {
156  m_Contents.ChangeSlotCount(a_SlotNum, -1);
157  }
158  break;
159  }
160 
161  case E_ITEM_ARROW:
162  {
164  {
165  m_Contents.ChangeSlotCount(a_SlotNum, -1);
166  }
167  break;
168  }
169 
170  case E_ITEM_SNOWBALL:
171  {
173  {
174  m_Contents.ChangeSlotCount(a_SlotNum, -1);
175  }
176  break;
177  }
178 
179  case E_ITEM_EGG:
180  {
182  {
183  m_Contents.ChangeSlotCount(a_SlotNum, -1);
184  }
185  break;
186  }
187 
189  {
191  {
192  m_Contents.ChangeSlotCount(a_SlotNum, -1);
193  }
194  break;
195  }
196 
197  case E_ITEM_POTION:
198  {
199  if (SpawnProjectileFromDispenser(dispAbsCoord, cProjectileEntity::pkSplashPotion, GetShootVector(meta) * 20 + Vector3d(0, 1, 0), &SlotItem) != cEntity::INVALID_ID)
200  {
201  m_Contents.ChangeSlotCount(a_SlotNum, -1);
202  }
203  break;
204  }
205 
206  case E_ITEM_DYE:
207  {
208  if (SlotItem.m_ItemDamage != E_META_DYE_WHITE)
209  {
210  DropFromSlot(a_Chunk, a_SlotNum);
211  break;
212  }
213  if (m_World->GrowRipePlant(dispAbsCoord.x, dispAbsCoord.y, dispAbsCoord.z, true))
214  {
215  m_Contents.ChangeSlotCount(a_SlotNum, -1);
216  }
217  break;
218  }
219 
220  case E_ITEM_BOAT:
221  case E_ITEM_SPRUCE_BOAT:
222  case E_ITEM_BIRCH_BOAT:
223  case E_ITEM_JUNGLE_BOAT:
224  case E_ITEM_ACACIA_BOAT:
226  {
227  Vector3d spawnPos = dispAbsCoord;
228  if (IsBlockWater(dispBlock))
229  {
230  // Water next to the dispenser, spawn a boat above the water block
231  spawnPos.y += 1;
232  }
233  else if (IsBlockWater(dispChunk->GetBlock(dispRelCoord.addedY(-1))))
234  {
235  // Water one block below the dispenser, spawn a boat at the dispenser's Y level
236  // No adjustment needed
237  }
238  else
239  {
240  // There's no eligible water block, drop the boat as a pickup
241  DropFromSlot(a_Chunk, a_SlotNum);
242  break;
243  }
244 
245  spawnPos += GetShootVector(meta) * 0.8; // A boat is bigger than one block. Add the shoot vector to put it outside the dispenser.
246  spawnPos += Vector3d(0.5, 0.5, 0.5);
247 
248  if (m_World->SpawnBoat(spawnPos, cBoat::ItemToMaterial(SlotItem)))
249  {
250  m_Contents.ChangeSlotCount(a_SlotNum, -1);
251  }
252  break;
253  }
254 
256  {
257  if (SpawnProjectileFromDispenser(dispAbsCoord, cProjectileEntity::pkFirework, GetShootVector(meta) * 20 + Vector3d(0, 1, 0), &SlotItem) != cEntity::INVALID_ID)
258  {
259  m_Contents.ChangeSlotCount(a_SlotNum, -1);
260  }
261  break;
262  }
263 
264  default:
265  {
266  DropFromSlot(a_Chunk, a_SlotNum);
267  break;
268  }
269  } // switch (SlotItem.m_ItemType)
270 }
271 
272 
273 
274 
275 
277 {
278  return m_World->CreateProjectile(Vector3d(0.5, 0.5, 0.5) + a_BlockPos,
279  a_Kind, nullptr, a_Item, &a_ShootVector
280  );
281 }
282 
283 
284 
285 
286 
288 {
289  switch (a_Meta & E_META_DROPSPENSER_FACING_MASK)
290  {
291  case E_META_DROPSPENSER_FACING_YP: return Vector3d( 0, 1, 0);
292  case E_META_DROPSPENSER_FACING_YM: return Vector3d( 0, -1, 0);
293  case E_META_DROPSPENSER_FACING_XM: return Vector3d(-1, 0, 0);
294  case E_META_DROPSPENSER_FACING_XP: return Vector3d( 1, 0, 0);
295  case E_META_DROPSPENSER_FACING_ZM: return Vector3d( 0, 0, -1);
296  case E_META_DROPSPENSER_FACING_ZP: return Vector3d( 0, 0, 1);
297  }
298  LOGWARNING("Unhandled dispenser meta: %d", a_Meta);
299  ASSERT(!"Unhandled dispenser facing");
300  return Vector3d(0, 1, 0);
301 }
302 
303 
304 
305 
306 
307 bool cDispenserEntity::ScoopUpLiquid(int a_SlotNum, short a_ResultingBucketItemType)
308 {
309  cItem LiquidBucket(a_ResultingBucketItemType, 1);
310  if (m_Contents.GetSlot(a_SlotNum).m_ItemCount == 1)
311  {
312  // Special case: replacing one empty bucket with one full bucket
313  m_Contents.SetSlot(a_SlotNum, LiquidBucket);
314  return true;
315  }
316 
317  // There are stacked buckets at the selected slot, see if a full bucket will fit somewhere else
318  if (m_Contents.HowManyCanFit(LiquidBucket) < 1)
319  {
320  // Cannot fit into m_Contents
321  return false;
322  }
323 
324  m_Contents.ChangeSlotCount(a_SlotNum, -1);
325  m_Contents.AddItem(LiquidBucket);
326  return true;
327 }
328 
329 
330 
331 
332 
333 bool cDispenserEntity::EmptyLiquidBucket(BLOCKTYPE a_BlockInFront, int a_SlotNum)
334 {
335  if (
336  (a_BlockInFront != E_BLOCK_AIR) &&
337  !IsBlockLiquid(a_BlockInFront) &&
338  !cFluidSimulator::CanWashAway(a_BlockInFront)
339  )
340  {
341  // Not a suitable block in front
342  return false;
343  }
344 
345  cItem EmptyBucket(E_ITEM_BUCKET, 1);
346  if (m_Contents.GetSlot(a_SlotNum).m_ItemCount == 1)
347  {
348  // Change the single full bucket present into a single empty bucket
349  m_Contents.SetSlot(a_SlotNum, EmptyBucket);
350  return true;
351  }
352 
353  // There are full buckets stacked at this slot, check if we can fit in the empty bucket
354  if (m_Contents.HowManyCanFit(EmptyBucket) < 1)
355  {
356  // The empty bucket wouldn't fit into m_Contents
357  return false;
358  }
359 
360  // The empty bucket fits in, remove one full bucket and add the empty one
361  m_Contents.ChangeSlotCount(a_SlotNum, -1);
362  m_Contents.AddItem(EmptyBucket);
363  return true;
364 }
void AddDropSpenserDir(Vector3i &a_RelCoord, NIBBLETYPE a_Direction)
Modifies the block coords to match the dropspenser direction given (where the dropspensed pickups sho...
virtual UInt32 SpawnMob(double a_PosX, double a_PosY, double a_PosZ, eMonsterType a_MonsterType, bool a_Baby=false) override
Spawns a mob of the specified type.
Definition: World.cpp:3190
bool EmptyLiquidBucket(BLOCKTYPE a_BlockInFront, int a_SlotNum)
If the a_BlockInFront can be washed away by liquid and the empty bucket can fit, does the m_Contents ...
bool IsBlockWater(BLOCKTYPE a_BlockType)
Definition: Defines.h:436
cChunk * GetRelNeighborChunkAdjustCoords(Vector3i &a_RelPos) const
Returns the chunk into which the relatively-specified block belongs.
Definition: Chunk.cpp:2358
short m_ItemDamage
Definition: Item.h:211
static bool IsSolid(BLOCKTYPE a_Type)
Definition: BlockInfo.h:48
unsigned char BLOCKTYPE
The datatype used by blockdata.
Definition: ChunkDef.h:42
cDispenserEntity(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, Vector3i a_Pos, cWorld *a_World)
Constructor used for normal operation.
int ChangeSlotCount(int a_SlotNum, int a_AddToCount)
Adds (or subtracts, if a_AddToCount is negative) to the count of items in the specified slot...
Definition: ItemGrid.cpp:443
void DropFromSlot(cChunk &a_Chunk, int a_SlotNum)
Helper function, drops one item from the specified slot (like a dropper)
unsigned char NIBBLETYPE
The datatype used by nibbledata (meta, light, skylight)
Definition: ChunkDef.h:45
eKind
The kind of the projectile.
Definition: Chunk.h:49
T y
Definition: Vector3.h:17
bool ScoopUpLiquid(int a_SlotNum, short a_ResultingBucketItemType)
If such a bucket can fit, adds it to m_Contents and returns true.
UInt32 SpawnBoat(double a_X, double a_Y, double a_Z, cBoat::eMaterial a_Material)
Definition: World.h:590
cWorld * m_World
Definition: BlockEntity.h:155
Vector3< T > addedY(T a_AddY) const
Returns a copy of this vector moved by the specified amount on the y axis.
Definition: Vector3.h:299
bool IsMinecart(short a_ItemType)
Definition: Defines.h:1139
static bool CanWashAway(BLOCKTYPE a_BlockType)
bool IsBlockLiquid(BLOCKTYPE a_BlockType)
Definition: Defines.h:484
Vector3i GetRelPos() const
Definition: BlockEntity.h:109
static eMaterial ItemToMaterial(const cItem &a_Item)
Returns the eMaterial that should be used for a boat created from the specified item.
Definition: Boat.cpp:179
Definition: World.h:65
NIBBLETYPE GetMeta(int a_RelX, int a_RelY, int a_RelZ) const
Definition: Chunk.h:380
bool GrowRipePlant(Vector3i a_BlockPos)
Grows the plant at the specified block to its ripe stage.
Definition: World.cpp:1785
char m_ItemCount
Definition: Item.h:210
Vector3< double > Vector3d
Definition: Vector3.h:445
int AddItem(cItem &a_ItemStack, bool a_AllowNewStacks=true, int a_PrioritySlot=-1)
Adds as many items out of a_ItemStack as can fit.
Definition: ItemGrid.cpp:313
UInt32 CreateProjectile(Vector3d a_Pos, cProjectileEntity::eKind a_Kind, cEntity *a_Creator, const cItem *a_Item, const Vector3d *a_Speed=nullptr)
Creates a projectile of the specified type.
Definition: World.cpp:3241
#define ASSERT(x)
Definition: Globals.h:335
void LOGWARNING(const char *a_Format, fmt::ArgList a_ArgList)
Definition: Logger.cpp:174
#define LOGD(...)
Definition: LoggerSimple.h:40
UInt32 SpawnMinecart(Vector3d a_Pos, int a_MinecartType, const cItem &a_Content=cItem(), int a_BlockHeight=1)
Spawns an minecart at the given coordinates.
Definition: World.cpp:2088
bool IsBlockRail(BLOCKTYPE a_BlockType)
Definition: Defines.h:492
int HowManyCanFit(const cItem &a_ItemStack, bool a_AllowNewStacks=true)
Returns number of items out of a_ItemStack that can fit in the storage.
Definition: ItemGrid.cpp:246
BLOCKTYPE GetBlock(int a_RelX, int a_RelY, int a_RelZ) const
Definition: Chunk.h:177
UInt32 SpawnProjectileFromDispenser(Vector3i a_BlockPos, cProjectileEntity::eKind a_Kind, const Vector3d &a_Speed, const cItem *a_Item=nullptr)
Spawns a projectile of the given kind in front of the dispenser with the specified speed...
virtual void DropSpenseFromSlot(cChunk &a_Chunk, int a_SlotNum) override
Override this function to provide the specific behavior for item dropspensing (drop / shoot / pour / ...
UInt32 SpawnPrimedTNT(double a_X, double a_Y, double a_Z, int a_FuseTimeInSec=80, double a_InitialVelocityCoeff=1)
Definition: World.h:629
unsigned int UInt32
Definition: Globals.h:113
short m_ItemType
Definition: Item.h:209
const cItem & GetSlot(int a_X, int a_Y) const
Definition: ItemGrid.cpp:96
static const UInt32 INVALID_ID
Special ID that is considered an "invalid value", signifying no entity.
Definition: Entity.h:156
Definition: Item.h:36
AString ItemTypeToString(short a_ItemType)
Translates itemtype into a string.
Definition: BlockID.cpp:270
static Vector3d GetShootVector(NIBBLETYPE a_BlockMeta)
Returns a unit vector in the cardinal direction of where the dispenser with the specified meta would ...
bool DamageItem(int a_SlotNum, short a_Amount)
Adds the specified damage to the specified item; returns true if the item broke (but the item is left...
Definition: ItemGrid.cpp:699
void SetSlot(int a_X, int a_Y, const cItem &a_Item)
Definition: ItemGrid.cpp:121