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 "../Chunk.h"
6 #include "../BlockInfo.h"
7 #include "../Defines.h"
8 #include "../World.h"
9 #include "../Entities/Boat.h"
10 #include "../Entities/ProjectileEntity.h"
11 #include "../Simulator/FluidSimulator.h"
12 #include "../Items/ItemSpawnEgg.h"
13 #include "../Items/ItemDye.h"
14 
15 
16 
17 cDispenserEntity::cDispenserEntity(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, Vector3i a_Pos, cWorld * a_World):
18  Super(a_BlockType, a_BlockMeta, a_Pos, a_World)
19 {
20  ASSERT(a_BlockType == E_BLOCK_DISPENSER);
21 }
22 
23 
24 
25 
26 
27 void cDispenserEntity::DropSpenseFromSlot(cChunk & a_Chunk, int a_SlotNum)
28 {
29  Vector3i DispRelCoord(GetRelPos());
30  auto Meta = a_Chunk.GetMeta(DispRelCoord);
31  AddDropSpenserDir(DispRelCoord, Meta);
32  auto DispChunk = a_Chunk.GetRelNeighborChunkAdjustCoords(DispRelCoord);
33  if (DispChunk == nullptr)
34  {
35  // Would dispense into / interact with a non-loaded chunk, ignore the tick
36  return;
37  }
38 
39  BLOCKTYPE DispBlock = DispChunk->GetBlock(DispRelCoord);
40  auto DispAbsCoord = DispChunk->RelativeToAbsolute(DispRelCoord);
41 
42  // Dispense the item:
43  const cItem & SlotItem = m_Contents.GetSlot(a_SlotNum);
44  if (ItemCategory::IsMinecart(SlotItem.m_ItemType) && IsBlockRail(DispBlock)) // only actually place the minecart if there are rails!
45  {
46  if (m_World->SpawnMinecart(DispAbsCoord.x + 0.5, DispAbsCoord.y + 0.5, DispAbsCoord.z + 0.5, SlotItem.m_ItemType) != cEntity::INVALID_ID)
47  {
48  m_Contents.ChangeSlotCount(a_SlotNum, -1);
49  }
50  return;
51  }
52  switch (SlotItem.m_ItemType)
53  {
54  case E_ITEM_BUCKET:
55  {
56  LOGD("Dispensing empty bucket in slot %d; DispBlock is \"%s\" (%d).", a_SlotNum, ItemTypeToString(DispBlock).c_str(), DispBlock);
57  switch (DispBlock)
58  {
60  case E_BLOCK_WATER:
61  {
62  if (ScoopUpLiquid(a_SlotNum, E_ITEM_WATER_BUCKET))
63  {
64  DispChunk->SetBlock(DispRelCoord, E_BLOCK_AIR, 0);
65  }
66  break;
67  }
69  case E_BLOCK_LAVA:
70  {
71  if (ScoopUpLiquid(a_SlotNum, E_ITEM_LAVA_BUCKET))
72  {
73  DispChunk->SetBlock(DispRelCoord, E_BLOCK_AIR, 0);
74  }
75  break;
76  }
77  default:
78  {
79  DropFromSlot(a_Chunk, a_SlotNum);
80  break;
81  }
82  }
83  break;
84  } // E_ITEM_BUCKET
85 
87  {
88  LOGD("Dispensing water bucket in slot %d; DispBlock is \"%s\" (%d).", a_SlotNum, ItemTypeToString(DispBlock).c_str(), DispBlock);
89  if (EmptyLiquidBucket(DispBlock, a_SlotNum))
90  {
91  DispChunk->SetBlock(DispRelCoord, E_BLOCK_WATER, 0);
92  }
93  else
94  {
95  DropFromSlot(a_Chunk, a_SlotNum);
96  }
97  break;
98  }
99 
100  case E_ITEM_LAVA_BUCKET:
101  {
102  LOGD("Dispensing lava bucket in slot %d; DispBlock is \"%s\" (%d).", a_SlotNum, ItemTypeToString(DispBlock).c_str(), DispBlock);
103  if (EmptyLiquidBucket(DispBlock, a_SlotNum))
104  {
105  DispChunk->SetBlock(DispRelCoord, E_BLOCK_LAVA, 0);
106  }
107  else
108  {
109  DropFromSlot(a_Chunk, a_SlotNum);
110  }
111  break;
112  }
113 
114  case E_ITEM_SPAWN_EGG:
115  {
116  double MobX = 0.5 + DispAbsCoord.x;
117  double MobZ = 0.5 + DispAbsCoord.z;
119  if (m_World->SpawnMob(MobX, DispAbsCoord.y, MobZ, MonsterType, false) != cEntity::INVALID_ID)
120  {
121  m_Contents.ChangeSlotCount(a_SlotNum, -1);
122  }
123  break;
124  }
125 
126  case E_BLOCK_TNT:
127  {
128  // Spawn a primed TNT entity, if space allows:
129  if (!cBlockInfo::IsSolid(DispBlock))
130  {
131  m_World->SpawnPrimedTNT(Vector3d(0.5, 0.5, 0.5) + DispAbsCoord, 80, 0); // 80 ticks fuse, no initial velocity
132  m_Contents.ChangeSlotCount(a_SlotNum, -1);
133  }
134  break;
135  }
136 
138  {
139  // Spawn fire if the block in front is air.
140  if (DispBlock == E_BLOCK_AIR)
141  {
142  DispChunk->SetBlock(DispRelCoord, E_BLOCK_FIRE, 0);
143 
144  bool ItemBroke = m_Contents.DamageItem(a_SlotNum, 1);
145 
146  if (ItemBroke)
147  {
148  m_Contents.ChangeSlotCount(a_SlotNum, -1);
149  }
150  }
151  break;
152  }
153 
154  case E_ITEM_FIRE_CHARGE:
155  {
157  {
158  m_Contents.ChangeSlotCount(a_SlotNum, -1);
159  }
160  break;
161  }
162 
163  case E_ITEM_ARROW:
164  {
166  {
167  m_Contents.ChangeSlotCount(a_SlotNum, -1);
168  }
169  break;
170  }
171 
172  case E_ITEM_SNOWBALL:
173  {
175  {
176  m_Contents.ChangeSlotCount(a_SlotNum, -1);
177  }
178  break;
179  }
180 
181  case E_ITEM_EGG:
182  {
184  {
185  m_Contents.ChangeSlotCount(a_SlotNum, -1);
186  }
187  break;
188  }
189 
191  {
193  {
194  m_Contents.ChangeSlotCount(a_SlotNum, -1);
195  }
196  break;
197  }
198 
199  case E_ITEM_POTION:
200  {
201  if (SpawnProjectileFromDispenser(DispAbsCoord, cProjectileEntity::pkSplashPotion, GetShootVector(Meta) * 20 + Vector3d(0, 1, 0), &SlotItem) != cEntity::INVALID_ID)
202  {
203  m_Contents.ChangeSlotCount(a_SlotNum, -1);
204  }
205  break;
206  }
207 
208  case E_ITEM_DYE:
209  {
210  if (SlotItem.m_ItemDamage != E_META_DYE_WHITE)
211  {
212  DropFromSlot(a_Chunk, a_SlotNum);
213  break;
214  }
215 
216  // Simulate a right-click with bonemeal:
217  if (cItemDyeHandler::FertilizePlant(*m_World, DispAbsCoord))
218  {
219  m_Contents.ChangeSlotCount(a_SlotNum, -1);
220  }
221  break;
222  }
223 
224  case E_ITEM_BOAT:
225  case E_ITEM_SPRUCE_BOAT:
226  case E_ITEM_BIRCH_BOAT:
227  case E_ITEM_JUNGLE_BOAT:
228  case E_ITEM_ACACIA_BOAT:
230  {
231  Vector3d spawnPos = DispAbsCoord;
232  if (IsBlockWater(DispBlock))
233  {
234  // Water next to the dispenser, spawn a boat above the water block
235  spawnPos.y += 1;
236  }
237  else if (IsBlockWater(DispChunk->GetBlock(DispRelCoord.addedY(-1))))
238  {
239  // Water one block below the dispenser, spawn a boat at the dispenser's Y level
240  // No adjustment needed
241  }
242  else
243  {
244  // There's no eligible water block, drop the boat as a pickup
245  DropFromSlot(a_Chunk, a_SlotNum);
246  break;
247  }
248 
249  spawnPos += GetShootVector(Meta) * 0.8; // A boat is bigger than one block. Add the shoot vector to put it outside the dispenser.
250  spawnPos += Vector3d(0.5, 0.5, 0.5);
251 
252  if (m_World->SpawnBoat(spawnPos, cBoat::ItemToMaterial(SlotItem)))
253  {
254  m_Contents.ChangeSlotCount(a_SlotNum, -1);
255  }
256  break;
257  }
258 
260  {
261  if (SpawnProjectileFromDispenser(DispAbsCoord, cProjectileEntity::pkFirework, GetShootVector(Meta) * 20 + Vector3d(0, 1, 0), &SlotItem) != cEntity::INVALID_ID)
262  {
263  m_Contents.ChangeSlotCount(a_SlotNum, -1);
264  }
265  break;
266  }
267 
268  default:
269  {
270  DropFromSlot(a_Chunk, a_SlotNum);
271  break;
272  }
273  } // switch (SlotItem.m_ItemType)
274 }
275 
276 
277 
278 
279 
281 {
282  return m_World->CreateProjectile(Vector3d(0.5, 0.5, 0.5) + a_BlockPos,
283  a_Kind, nullptr, a_Item, &a_ShootVector
284  );
285 }
286 
287 
288 
289 
290 
292 {
293  switch (a_Meta & E_META_DROPSPENSER_FACING_MASK)
294  {
295  case E_META_DROPSPENSER_FACING_YP: return Vector3d( 0, 1, 0);
296  case E_META_DROPSPENSER_FACING_YM: return Vector3d( 0, -1, 0);
297  case E_META_DROPSPENSER_FACING_XM: return Vector3d(-1, 0, 0);
298  case E_META_DROPSPENSER_FACING_XP: return Vector3d( 1, 0, 0);
299  case E_META_DROPSPENSER_FACING_ZM: return Vector3d( 0, 0, -1);
300  case E_META_DROPSPENSER_FACING_ZP: return Vector3d( 0, 0, 1);
301  }
302  LOGWARNING("Unhandled dispenser meta: %d", a_Meta);
303  ASSERT(!"Unhandled dispenser facing");
304  return Vector3d(0, 1, 0);
305 }
306 
307 
308 
309 
310 
311 bool cDispenserEntity::ScoopUpLiquid(int a_SlotNum, short a_ResultingBucketItemType)
312 {
313  cItem LiquidBucket(a_ResultingBucketItemType);
314  if (m_Contents.GetSlot(a_SlotNum).m_ItemCount == 1)
315  {
316  // Special case: replacing one empty bucket with one full bucket
317  m_Contents.SetSlot(a_SlotNum, LiquidBucket);
318  return true;
319  }
320 
321  // There are stacked buckets at the selected slot, see if a full bucket will fit somewhere else
322  if (m_Contents.HowManyCanFit(LiquidBucket) < 1)
323  {
324  // Cannot fit into m_Contents
325  return false;
326  }
327 
328  m_Contents.ChangeSlotCount(a_SlotNum, -1);
329  m_Contents.AddItem(LiquidBucket);
330  return true;
331 }
332 
333 
334 
335 
336 
337 bool cDispenserEntity::EmptyLiquidBucket(BLOCKTYPE a_BlockInFront, int a_SlotNum)
338 {
339  if (
340  (a_BlockInFront != E_BLOCK_AIR) &&
341  !IsBlockLiquid(a_BlockInFront) &&
342  !cFluidSimulator::CanWashAway(a_BlockInFront)
343  )
344  {
345  // Not a suitable block in front
346  return false;
347  }
348 
349  cItem EmptyBucket(E_ITEM_BUCKET, 1);
350  if (m_Contents.GetSlot(a_SlotNum).m_ItemCount == 1)
351  {
352  // Change the single full bucket present into a single empty bucket
353  m_Contents.SetSlot(a_SlotNum, EmptyBucket);
354  return true;
355  }
356 
357  // There are full buckets stacked at this slot, check if we can fit in the empty bucket
358  if (m_Contents.HowManyCanFit(EmptyBucket) < 1)
359  {
360  // The empty bucket wouldn't fit into m_Contents
361  return false;
362  }
363 
364  // The empty bucket fits in, remove one full bucket and add the empty one
365  m_Contents.ChangeSlotCount(a_SlotNum, -1);
366  m_Contents.AddItem(EmptyBucket);
367  return true;
368 }
bool IsBlockWater(BLOCKTYPE a_BlockType)
Definition: BlockInfo.cpp:10
bool IsBlockRail(BLOCKTYPE a_BlockType)
Definition: BlockInfo.cpp:67
bool IsBlockLiquid(BLOCKTYPE a_BlockType)
Definition: BlockInfo.cpp:58
AString ItemTypeToString(short a_ItemType)
Translates itemtype into a string.
Definition: BlockType.cpp:252
@ E_META_DROPSPENSER_FACING_ZM
Definition: BlockType.h:647
@ E_META_DROPSPENSER_FACING_XM
Definition: BlockType.h:649
@ E_META_DROPSPENSER_FACING_YP
Definition: BlockType.h:646
@ E_META_DROPSPENSER_FACING_YM
Definition: BlockType.h:645
@ E_META_DROPSPENSER_FACING_ZP
Definition: BlockType.h:648
@ E_META_DROPSPENSER_FACING_XP
Definition: BlockType.h:650
@ E_META_DROPSPENSER_FACING_MASK
Definition: BlockType.h:651
@ E_BLOCK_WATER
Definition: BlockType.h:18
@ E_BLOCK_STATIONARY_LAVA
Definition: BlockType.h:21
@ E_BLOCK_AIR
Definition: BlockType.h:10
@ E_BLOCK_FIRE
Definition: BlockType.h:61
@ E_BLOCK_TNT
Definition: BlockType.h:56
@ E_BLOCK_DISPENSER
Definition: BlockType.h:33
@ E_BLOCK_STATIONARY_WATER
Definition: BlockType.h:19
@ E_BLOCK_LAVA
Definition: BlockType.h:20
@ E_ITEM_POTION
Definition: BlockType.h:418
@ E_ITEM_SNOWBALL
Definition: BlockType.h:376
@ E_ITEM_FIRE_CHARGE
Definition: BlockType.h:431
@ E_ITEM_SPRUCE_BOAT
Definition: BlockType.h:491
@ E_ITEM_BOAT
Definition: BlockType.h:377
@ E_ITEM_DYE
Definition: BlockType.h:396
@ E_ITEM_LAVA_BUCKET
Definition: BlockType.h:371
@ E_ITEM_SPAWN_EGG
Definition: BlockType.h:429
@ E_ITEM_WATER_BUCKET
Definition: BlockType.h:370
@ E_ITEM_ACACIA_BOAT
Definition: BlockType.h:494
@ E_ITEM_JUNGLE_BOAT
Definition: BlockType.h:493
@ E_ITEM_FIREWORK_ROCKET
Definition: BlockType.h:447
@ E_ITEM_ARROW
Definition: BlockType.h:306
@ E_ITEM_DARK_OAK_BOAT
Definition: BlockType.h:495
@ E_ITEM_BUCKET
Definition: BlockType.h:369
@ E_ITEM_BOTTLE_O_ENCHANTING
Definition: BlockType.h:430
@ E_ITEM_FLINT_AND_STEEL
Definition: BlockType.h:303
@ E_ITEM_EGG
Definition: BlockType.h:389
@ E_ITEM_BIRCH_BOAT
Definition: BlockType.h:492
@ E_META_DYE_WHITE
Definition: BlockType.h:1060
unsigned char NIBBLETYPE
The datatype used by nibbledata (meta, light, skylight)
Definition: ChunkDef.h:44
unsigned char BLOCKTYPE
The datatype used by blockdata.
Definition: ChunkDef.h:41
unsigned int UInt32
Definition: Globals.h:157
#define ASSERT(x)
Definition: Globals.h:276
void LOGWARNING(std::string_view a_Format, const Args &... args)
Definition: LoggerSimple.h:67
#define LOGD
Definition: LoggerSimple.h:83
Vector3< double > Vector3d
Definition: Vector3.h:485
bool IsMinecart(short a_ItemType)
Definition: Defines.cpp:573
Vector3i GetRelPos() const
Definition: BlockEntity.h:95
cWorld * m_World
Definition: BlockEntity.h:126
cDispenserEntity(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta, Vector3i a_Pos, cWorld *a_World)
Constructor used for normal operation.
static Vector3d GetShootVector(NIBBLETYPE a_BlockMeta)
Returns a unit vector in the cardinal direction of where the dispenser with the specified meta would ...
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 ScoopUpLiquid(int a_SlotNum, short a_ResultingBucketItemType)
If such a bucket can fit, adds it to m_Contents and returns true.
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 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.
void DropFromSlot(cChunk &a_Chunk, int a_SlotNum)
Helper function, drops one item from the specified slot (like a dropper)
void AddDropSpenserDir(Vector3i &a_RelCoord, NIBBLETYPE a_Direction)
Modifies the block coords to match the dropspenser direction given (where the dropspensed pickups sho...
static bool IsSolid(BLOCKTYPE Block)
Is this block solid (player cannot walk through)?
Definition: BlockInfo.cpp:892
Definition: Chunk.h:36
NIBBLETYPE GetMeta(int a_RelX, int a_RelY, int a_RelZ) const
Definition: Chunk.h:279
BLOCKTYPE GetBlock(int a_RelX, int a_RelY, int a_RelZ) const
Definition: Chunk.h:146
cChunk * GetRelNeighborChunkAdjustCoords(Vector3i &a_RelPos) const
Returns the chunk into which the relatively-specified block belongs.
Definition: Chunk.cpp:1885
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:253
static const UInt32 INVALID_ID
Special ID that is considered an "invalid value", signifying no entity.
Definition: Entity.h:128
eKind
The kind of the projectile.
Definition: Item.h:37
char m_ItemCount
Definition: Item.h:164
short m_ItemType
Definition: Item.h:163
short m_ItemDamage
Definition: Item.h:165
char ChangeSlotCount(int a_SlotNum, char a_AddToCount)
Adds (or subtracts, if a_AddToCount is negative) to the count of items in the specified slot.
Definition: ItemGrid.cpp:468
void SetSlot(int a_X, int a_Y, const cItem &a_Item)
Definition: ItemGrid.cpp:121
const cItem & GetSlot(int a_X, int a_Y) const
Definition: ItemGrid.cpp:96
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
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:723
char 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
static bool FertilizePlant(cWorld &a_World, Vector3i a_BlockPos)
Attempts to use the bonemeal on the plant at the specified (absolute) position.
Definition: ItemDye.h:100
static eMonsterType ItemDamageToMonsterType(short a_ItemDamage)
Converts the Spawn egg item damage to the monster type to spawn.
Definition: ItemSpawnEgg.h:64
static bool CanWashAway(BLOCKTYPE a_BlockType)
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:314
T y
Definition: Vector3.h:17
Definition: World.h:53
UInt32 SpawnPrimedTNT(double a_X, double a_Y, double a_Z, int a_FuseTimeInSec=80, double a_InitialVelocityCoeff=1, bool a_ShouldPlayFuseSound=true)
Definition: World.h:519
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:2897
UInt32 SpawnBoat(double a_X, double a_Y, double a_Z, cBoat::eMaterial a_Material)
Definition: World.h:480
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:1958
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:2948