Cuberite
A lightweight, fast and extensible game server for Minecraft
ItemMobHead.h
Go to the documentation of this file.
1 
2 #pragma once
3 
4 #include "ItemHandler.h"
5 #include "../World.h"
6 #include "../BlockEntities/MobHeadEntity.h"
7 
8 
9 
10 
11 
12 class cItemMobHeadHandler final :
13  public cItemHandler
14 {
16 
17 public:
18 
19  using Super::Super;
20 
21 
22 
23 
24 
25  virtual bool CommitPlacement(cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) const override
26  {
27  // Cannot place a head at "no face" and from the bottom:
28  if ((a_ClickedBlockFace == BLOCK_FACE_NONE) || (a_ClickedBlockFace == BLOCK_FACE_BOTTOM))
29  {
30  return false;
31  }
32 
33  // If the placed head is a wither, try to spawn the wither first:
34  if (a_HeldItem.m_ItemDamage == E_META_HEAD_WITHER)
35  {
36  if (TrySpawnWitherAround(a_Player, a_PlacePosition))
37  {
38  return true;
39  }
40  // Wither not created, proceed with regular head placement
41  }
42 
43  if (!a_Player.PlaceBlock(a_PlacePosition, E_BLOCK_HEAD, BlockFaceToBlockMeta(a_ClickedBlockFace)))
44  {
45  return false;
46  }
47 
48  RegularHeadPlaced(a_Player, a_HeldItem, a_PlacePosition, a_ClickedBlockFace);
49  return true;
50  }
51 
52 
53 
54 
55 
58  void RegularHeadPlaced(const cPlayer & a_Player, const cItem & a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace) const
59  {
60  const auto HeadType = static_cast<eMobHeadType>(a_HeldItem.m_ItemDamage);
61  const auto BlockMeta = static_cast<NIBBLETYPE>(a_ClickedBlockFace);
62 
63  // Use a callback to set the properties of the mob head block entity:
64  a_Player.GetWorld()->DoWithBlockEntityAt(a_PlacePosition, [&a_Player, HeadType, BlockMeta](cBlockEntity & a_BlockEntity)
65  {
66  ASSERT(a_BlockEntity.GetBlockType() == E_BLOCK_HEAD);
67 
68  auto & MobHeadEntity = static_cast<cMobHeadEntity &>(a_BlockEntity);
69 
70  int Rotation = 0;
71  if (BlockMeta == 1)
72  {
73  Rotation = FloorC(a_Player.GetYaw() * 16.0f / 360.0f + 0.5f) & 0x0f;
74  }
75 
76  MobHeadEntity.SetType(HeadType);
77  MobHeadEntity.SetRotation(static_cast<eMobHeadRotation>(Rotation));
78  return false;
79  });
80  }
81 
82 
83 
84 
85 
88  bool TrySpawnWitherAround(cPlayer & a_Player, const Vector3i a_BlockPos) const
89  {
90  // No wither can be created at Y < 2 - not enough space for the formula:
91  if (a_BlockPos.y < 2)
92  {
93  return false;
94  }
95 
96  // Check for all relevant wither locations:
97  static const Vector3i RelCoords[] =
98  {
99  { 0, 0, 0},
100  { 1, 0, 0},
101  {-1, 0, 0},
102  { 0, 0, 1},
103  { 0, 0, -1},
104  };
105 
106  for (auto & RelCoord : RelCoords)
107  {
108  if (TrySpawnWitherAt(
109  *a_Player.GetWorld(), a_Player,
110  a_BlockPos,
111  RelCoord.x, RelCoord.z
112  ))
113  {
114  return true;
115  }
116  } // for i - RelCoords[]
117 
118  return false;
119  }
120 
121 
128  cWorld & a_World, cPlayer & a_Player,
129  Vector3i a_PlacedHeadPos,
130  int a_OffsetX, int a_OffsetZ
131  ) const
132  {
133  // Image for the wither at the X axis:
134  static const sSetBlock ImageWitherX[] =
135  {
136  {-1, 0, 0, E_BLOCK_HEAD, 0},
137  { 0, 0, 0, E_BLOCK_HEAD, 0},
138  { 1, 0, 0, E_BLOCK_HEAD, 0},
139  {-1, -1, 0, E_BLOCK_SOULSAND, 0},
140  { 0, -1, 0, E_BLOCK_SOULSAND, 0},
141  { 1, -1, 0, E_BLOCK_SOULSAND, 0},
142  {-1, -2, 0, E_BLOCK_AIR, 0},
143  { 0, -2, 0, E_BLOCK_SOULSAND, 0},
144  { 1, -2, 0, E_BLOCK_AIR, 0},
145  };
146 
147  // Image for the wither at the Z axis:
148  static const sSetBlock ImageWitherZ[] =
149  {
150  { 0, 0, -1, E_BLOCK_HEAD, 0},
151  { 0, 0, 0, E_BLOCK_HEAD, 0},
152  { 0, 0, 1, E_BLOCK_HEAD, 0},
153  { 0, -1, -1, E_BLOCK_SOULSAND, 0},
154  { 0, -1, 0, E_BLOCK_SOULSAND, 0},
155  { 0, -1, 1, E_BLOCK_SOULSAND, 0},
156  { 0, -2, -1, E_BLOCK_AIR, 0},
157  { 0, -2, 0, E_BLOCK_SOULSAND, 0},
158  { 0, -2, 1, E_BLOCK_AIR, 0},
159  };
160 
161  // Try to spawn the wither from each image:
162  return (
164  a_World, a_Player, ImageWitherX,
165  a_PlacedHeadPos,
166  a_OffsetX, a_OffsetZ
167  ) ||
169  a_World, a_Player, ImageWitherZ,
170  a_PlacedHeadPos,
171  a_OffsetX, a_OffsetZ
172  )
173  );
174  }
175 
176 
177 
178 
179 
186  cWorld & a_World, cPlayer & a_Player, const sSetBlock (& a_Image)[9],
187  Vector3i a_PlacedHeadPos,
188  int a_OffsetX, int a_OffsetZ
189  ) const
190  {
191  std::array<Vector3i, 9> PositionsToClear;
192 
193  // Check each block individually:
194  for (size_t i = 0; i != std::size(a_Image); i++)
195  {
196  // The absolute coords of the block in the image to check.
197  const Vector3i Block(
198  a_PlacedHeadPos.x + a_OffsetX + a_Image[i].GetX(),
199  a_PlacedHeadPos.y + a_Image[i].GetY(),
200  a_PlacedHeadPos.z + a_OffsetZ + a_Image[i].GetZ()
201  );
202 
203  // If the query is for the head the player is about to place (remember, it hasn't been set into the world yet), short-circuit-evaluate it:
204  if (Block == a_PlacedHeadPos)
205  {
206  if (a_Image[i].m_BlockType != E_BLOCK_HEAD)
207  {
208  return false; // Didn't match.
209  }
210 
211  PositionsToClear[i] = Block;
212  continue; // Matched, continue checking the rest of the image.
213  }
214 
215  // Query the world block:
217  NIBBLETYPE BlockMeta;
218  if (!a_World.GetBlockTypeMeta(Block, BlockType, BlockMeta))
219  {
220  // Cannot query block, assume unloaded chunk, fail to spawn the wither
221  return false;
222  }
223 
224  // Compare the world block:
225  if (BlockType != a_Image[i].m_BlockType)
226  {
227  return false;
228  }
229 
230  // If it is a mob head, check it's a wither skull using the block entity:
231  if (
232  (BlockType == E_BLOCK_HEAD) &&
233  !a_World.DoWithBlockEntityAt(Block, [&](cBlockEntity & a_BlockEntity)
234  {
235  ASSERT(a_BlockEntity.GetBlockType() == E_BLOCK_HEAD);
236 
237  return static_cast<cMobHeadEntity &>(a_BlockEntity).GetType() == SKULL_TYPE_WITHER;
238  })
239  )
240  {
241  return false;
242  }
243 
244  // Matched, continue checking:
245  PositionsToClear[i] = Block;
246  } // for i - a_Image
247 
248  // All image blocks matched, try replace the image with air blocks:
249  if (
250  !a_Player.PlaceBlocks(
251  {
252  { PositionsToClear[0], E_BLOCK_AIR, 0 },
253  { PositionsToClear[1], E_BLOCK_AIR, 0 },
254  { PositionsToClear[2], E_BLOCK_AIR, 0 },
255  { PositionsToClear[3], E_BLOCK_AIR, 0 },
256  { PositionsToClear[4], E_BLOCK_AIR, 0 },
257  { PositionsToClear[5], E_BLOCK_AIR, 0 },
258  { PositionsToClear[6], E_BLOCK_AIR, 0 },
259  { PositionsToClear[7], E_BLOCK_AIR, 0 },
260  { PositionsToClear[8], E_BLOCK_AIR, 0 },
261  })
262  )
263  {
264  return false;
265  }
266 
267  // Spawn the wither:
268  int BlockX = a_PlacedHeadPos.x + a_OffsetX;
269  int BlockZ = a_PlacedHeadPos.z + a_OffsetZ;
270  a_World.SpawnMob(static_cast<double>(BlockX) + 0.5, a_PlacedHeadPos.y - 2, static_cast<double>(BlockZ) + 0.5, mtWither, false);
271  AwardSpawnWitherAchievement(a_World, {BlockX, a_PlacedHeadPos.y - 2, BlockZ});
272  return true;
273  }
274 
275 
276 
277 
278 
280  void AwardSpawnWitherAchievement(cWorld & a_World, Vector3i a_BlockPos) const
281  {
282  Vector3f Pos(a_BlockPos);
283  a_World.ForEachPlayer([=](cPlayer & a_Player)
284  {
285  // If player is close, award achievement:
286  double Dist = (a_Player.GetPosition() - Pos).Length();
287  if (Dist < 50.0)
288  {
290  }
291  return false;
292  }
293  );
294  }
295 
296 
297 
298 
299 
302  static NIBBLETYPE BlockFaceToBlockMeta(int a_BlockFace)
303  {
304  switch (a_BlockFace)
305  {
306  case BLOCK_FACE_TOP: return 0x01; // On ground (rotation provided in block entity)
307  case BLOCK_FACE_XM: return 0x04; // west wall, facing east
308  case BLOCK_FACE_XP: return 0x05; // east wall, facing west
309  case BLOCK_FACE_ZM: return 0x02; // north wall, facing south
310  case BLOCK_FACE_ZP: return 0x03; // south wall, facing north
311  default:
312  {
313  ASSERT(!"Unhandled block face");
314  return 0;
315  }
316  }
317  }
318 
319 
320 
321 
322 
323  virtual bool IsPlaceable(void) const override
324  {
325  return true;
326  }
327 } ;
328 
329 
330 
331 
@ E_BLOCK_HEAD
Definition: BlockType.h:159
@ E_BLOCK_AIR
Definition: BlockType.h:10
@ E_BLOCK_SOULSAND
Definition: BlockType.h:103
@ E_META_HEAD_WITHER
Definition: BlockType.h:1068
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
eMobHeadRotation
Definition: Defines.h:191
eBlockFace
Block face constants, used in PlayerDigging and PlayerBlockPlacement packets and bbox collision calc.
Definition: Defines.h:38
@ BLOCK_FACE_XP
Definition: Defines.h:41
@ BLOCK_FACE_TOP
Definition: Defines.h:49
@ BLOCK_FACE_ZM
Definition: Defines.h:44
@ BLOCK_FACE_BOTTOM
Definition: Defines.h:48
@ BLOCK_FACE_ZP
Definition: Defines.h:45
@ BLOCK_FACE_XM
Definition: Defines.h:40
@ BLOCK_FACE_NONE
Definition: Defines.h:39
eMobHeadType
Definition: Defines.h:177
#define ASSERT(x)
Definition: Globals.h:276
std::enable_if< std::is_arithmetic< T >::value, C >::type FloorC(T a_Value)
Floors a value, then casts it to C (an int by default).
Definition: Globals.h:347
@ mtWither
Definition: MonsterTypes.h:75
BlockType
Definition: BlockTypes.h:4
unsigned char Rotation(const BlockState Block)
BLOCKTYPE GetBlockType() const
Definition: BlockEntity.h:97
double GetYaw(void) const
Definition: Entity.h:198
const Vector3d & GetPosition(void) const
Exported in ManualBindings.
Definition: Entity.h:297
cWorld * GetWorld(void) const
Definition: Entity.h:190
Definition: Player.h:29
void AwardAchievement(CustomStatistic a_Ach)
Awards the player an achievement.
Definition: Player.cpp:1372
bool PlaceBlocks(std::initializer_list< sSetBlock > a_Blocks)
Calls the block placement hooks and places the blocks in the world.
Definition: Player.cpp:2449
bool PlaceBlock(Vector3i a_Position, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
Attempts to place the block in the world with a call to PlaceBlocks.
Definition: Player.cpp:2440
Definition: Item.h:37
short m_ItemDamage
Definition: Item.h:165
constexpr cItemHandler(int a_ItemType)
Definition: ItemHandler.h:37
bool TrySpawnWitherAround(cPlayer &a_Player, const Vector3i a_BlockPos) const
Spawns a wither if the wither skull placed at the specified coords completes wither's spawning formul...
Definition: ItemMobHead.h:88
bool TrySpawnWitherFromImage(cWorld &a_World, cPlayer &a_Player, const sSetBlock(&a_Image)[9], Vector3i a_PlacedHeadPos, int a_OffsetX, int a_OffsetZ) const
Tries to spawn a wither from the specified image at the specified offset from the placed head block.
Definition: ItemMobHead.h:185
void RegularHeadPlaced(const cPlayer &a_Player, const cItem &a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace) const
Called after placing a regular head block with no mob spawning.
Definition: ItemMobHead.h:58
bool TrySpawnWitherAt(cWorld &a_World, cPlayer &a_Player, Vector3i a_PlacedHeadPos, int a_OffsetX, int a_OffsetZ) const
Tries to spawn a wither at the specified offset from the placed head block.
Definition: ItemMobHead.h:127
virtual bool IsPlaceable(void) const override
Blocks simply get placed.
Definition: ItemMobHead.h:323
virtual bool CommitPlacement(cPlayer &a_Player, const cItem &a_HeldItem, const Vector3i a_PlacePosition, const eBlockFace a_ClickedBlockFace, const Vector3i a_CursorPosition) const override
Performs the actual placement of this placeable item.
Definition: ItemMobHead.h:25
static NIBBLETYPE BlockFaceToBlockMeta(int a_BlockFace)
Converts the block face of the placement (which face of the block was clicked to place the head) into...
Definition: ItemMobHead.h:302
void AwardSpawnWitherAchievement(cWorld &a_World, Vector3i a_BlockPos) const
Awards the achievement to all players close to the specified point.
Definition: ItemMobHead.h:280
T x
Definition: Vector3.h:17
T y
Definition: Vector3.h:17
T z
Definition: Vector3.h:17
Definition: World.h:53
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
bool GetBlockTypeMeta(Vector3i a_BlockPos, BLOCKTYPE &a_BlockType, NIBBLETYPE &a_BlockMeta) const
Retrieves the block type and meta at the specified coords.
Definition: World.cpp:1779
virtual bool DoWithBlockEntityAt(Vector3i a_Position, cBlockEntityCallback a_Callback) override
Calls the callback for the block entity at the specified coords; returns false if there's no block en...
Definition: World.cpp:1420
virtual bool ForEachPlayer(cPlayerListCallback a_Callback) override
Calls the callback for each player in the list; returns true if all players processed,...
Definition: World.cpp:2266