Cuberite
A lightweight, fast and extensible game server for Minecraft
BlockButton.h
Go to the documentation of this file.
1 #pragma once
2 
3 #include "BlockHandler.h"
4 #include "BlockSlab.h"
5 #include "BlockStairs.h"
6 #include "../BlockInfo.h"
7 #include "../Chunk.h"
8 #include "Defines.h"
9 #include "Entities/Player.h"
10 #include "Mixins.h"
11 #include "ChunkInterface.h"
12 #include "World.h"
13 
14 
15 
16 
17 class cBlockButtonHandler final :
18  public cClearMetaOnDrop<cMetaRotator<cBlockHandler, 0x07, 0x04, 0x01, 0x03, 0x02, true>>
19 {
21 
22 public:
23 
24  using Super::Super;
25 
27  static bool IsButtonOn(NIBBLETYPE a_Meta)
28  {
29  return (a_Meta & 0x08) == 0x08;
30  }
31 
34  static void OnArrowHit(cWorld & a_World, const Vector3i a_Position, const eBlockFace a_HitFace)
35  {
37  NIBBLETYPE Meta;
38  const auto Pos = AddFaceDirection(a_Position, a_HitFace);
39 
40  if (
41  !a_World.GetBlockTypeMeta(Pos, Type, Meta) ||
42  IsButtonOn(Meta) ||
43  !IsButtonPressedByArrow(a_World, Pos, Type, Meta)
44  )
45  {
46  // Bail if we're not specifically a wooden button, or it's already on
47  // or if the arrow didn't intersect. It is very important that nothing is
48  // done if the button is depressed, since the release task will already be queued
49  return;
50  }
51 
52  a_World.SetBlockMeta(Pos, Meta | 0x08);
53  a_World.WakeUpSimulators(Pos);
54 
55  // sound name is ok to be wood, because only wood gets triggered by arrow
56  a_World.GetBroadcastManager().BroadcastSoundEffect("block.wood_button.click_on", Pos, 0.5f, 0.6f);
57 
58  // Queue a button reset
59  QueueButtonRelease(a_World, Pos, Type);
60  }
61 
62 private:
63 
64  virtual bool OnUse(
65  cChunkInterface & a_ChunkInterface,
66  cWorldInterface & a_WorldInterface,
67  cPlayer & a_Player,
68  const Vector3i a_BlockPos,
69  eBlockFace a_BlockFace,
70  const Vector3i a_CursorPos
71  ) const override
72  {
73  NIBBLETYPE Meta = a_ChunkInterface.GetBlockMeta(a_BlockPos);
74 
75  // If button is already on, do nothing:
76  if (IsButtonOn(Meta))
77  {
78  return false;
79  }
80 
81  // Set the ON bit to on
82  Meta |= 0x08;
83 
84  const auto SoundToPlay = (m_BlockType == E_BLOCK_STONE_BUTTON) ? "block.stone_button.click_on" : "block.wood_button.click_on";
85 
86  a_ChunkInterface.SetBlockMeta(a_BlockPos, Meta);
87  a_WorldInterface.WakeUpSimulators(a_BlockPos);
88  a_WorldInterface.GetBroadcastManager().BroadcastSoundEffect(SoundToPlay, a_BlockPos, 0.5f, 0.6f, a_Player.GetClientHandle());
89 
90  // Queue a button reset (unpress)
91  QueueButtonRelease(*a_Player.GetWorld(), a_BlockPos, m_BlockType);
92 
93  return true;
94  }
95 
96 
97 
98 
99 
100  virtual bool IsUseable(void) const override
101  {
102  return true;
103  }
104 
105 
106 
107 
108 
111  {
112  switch (a_Meta & 0x7)
113  {
114  case 0x0: return BLOCK_FACE_YM;
115  case 0x1: return BLOCK_FACE_XP;
116  case 0x2: return BLOCK_FACE_XM;
117  case 0x3: return BLOCK_FACE_ZP;
118  case 0x4: return BLOCK_FACE_ZM;
119  case 0x5: return BLOCK_FACE_YP;
120  default:
121  {
122  ASSERT(!"Unhandled block meta!");
123  return BLOCK_FACE_NONE;
124  }
125  }
126  }
127 
128 
129 
130 
131 
132  virtual bool CanBeAt(const cChunk & a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
133  {
134  auto SupportRelPos = AddFaceDirection(a_Position, BlockMetaDataToBlockFace(a_Meta), true);
135  if (!cChunkDef::IsValidHeight(SupportRelPos))
136  {
137  return false;
138  }
139  BLOCKTYPE SupportBlockType;
140  NIBBLETYPE SupportBlockMeta;
141  a_Chunk.UnboundedRelGetBlock(SupportRelPos, SupportBlockType, SupportBlockMeta);
143 
144  // upside down slabs
145  if (cBlockSlabHandler::IsAnySlabType(SupportBlockType))
146  {
147  return (Face == BLOCK_FACE_YP) && (SupportBlockMeta & E_META_WOODEN_SLAB_UPSIDE_DOWN);
148  }
149 
150  // stairs (top and sides)
151  if (cBlockStairsHandler::IsAnyStairType(SupportBlockType))
152  {
153  switch (Face)
154  {
156  return (SupportBlockMeta & E_BLOCK_STAIRS_UPSIDE_DOWN);
158  return ((SupportBlockMeta & 0b11) == E_BLOCK_STAIRS_XP);
160  return ((SupportBlockMeta & 0b11) == E_BLOCK_STAIRS_XM);
162  return ((SupportBlockMeta & 0b11) == E_BLOCK_STAIRS_ZP);
164  return ((SupportBlockMeta & 0b11) == E_BLOCK_STAIRS_ZM);
165  default:
166  {
167  return false;
168  }
169  }
170  }
171 
172  return cBlockInfo::FullyOccupiesVoxel(SupportBlockType);
173  }
174 
175 
176 
177 
178 
179  virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) const override
180  {
181  UNUSED(a_Meta);
182  return 0;
183  }
184 
187  static void QueueButtonRelease(cWorld & a_ButtonWorld, const Vector3i a_Position, const BLOCKTYPE a_BlockType)
188  {
189  const auto TickDelay = (a_BlockType == E_BLOCK_STONE_BUTTON) ? 20_tick : 30_tick;
190  a_ButtonWorld.ScheduleTask(
191  TickDelay,
192  [a_Position, a_BlockType](cWorld & a_World)
193  {
194  BLOCKTYPE Type;
195  NIBBLETYPE Meta;
196 
197  if (
198  !a_World.GetBlockTypeMeta(a_Position, Type, Meta) ||
199  (Type != a_BlockType) || !IsButtonOn(Meta)
200  )
201  {
202  // Total failure or block changed, bail
203  return;
204  }
205 
206  if (IsButtonPressedByArrow(a_World, a_Position, Type, Meta))
207  {
208  // Try again in a little while
209  QueueButtonRelease(a_World, a_Position, a_BlockType);
210  return;
211  }
212 
213  // Block hasn't change in the meantime; release it
214  const auto SoundToPlayOnRelease = (Type == E_BLOCK_STONE_BUTTON) ? "block.stone_button.click_off" : "block.wood_button.click_off";
215 
216  a_World.SetBlockMeta(a_Position, Meta & 0x07);
217  a_World.WakeUpSimulators(a_Position);
218  a_World.BroadcastSoundEffect(SoundToPlayOnRelease, a_Position, 0.5f, 0.5f);
219  }
220  );
221  }
222 
224  static bool IsButtonPressedByArrow(cWorld & a_World, const Vector3i a_ButtonPosition, const BLOCKTYPE a_BlockType, const NIBBLETYPE a_Meta)
225  {
226  if (a_BlockType != E_BLOCK_WOODEN_BUTTON)
227  {
228  return false;
229  }
230 
231  const auto FaceOffset = GetButtonOffsetOnBlock(a_Meta);
232  const bool FoundArrow = !a_World.ForEachEntityInBox(
233  cBoundingBox(FaceOffset + a_ButtonPosition, 0.2, 0.2),
234  [](cEntity & a_Entity)
235  {
236  return a_Entity.IsArrow();
237  }
238  );
239 
240  return FoundArrow;
241  }
242 
249  {
250  switch (BlockMetaDataToBlockFace(a_Meta))
251  {
252  case BLOCK_FACE_YM: return { 0.5, 1, 0.5 };
253  case BLOCK_FACE_XP: return { 0, 0.5, 0.5 };
254  case BLOCK_FACE_XM: return { 1, 0.5, 0.5 };
255  case BLOCK_FACE_ZP: return { 0.5, 0.5, 0 };
256  case BLOCK_FACE_ZM: return { 0.5, 0.5, 1 };
257  case BLOCK_FACE_YP: return { 0.5, 0, 0.5 };
258  case BLOCK_FACE_NONE:
259  {
260  break;
261  }
262  }
263  UNREACHABLE("Unhandled block face!");
264  }
265 } ;
@ E_META_WOODEN_SLAB_UPSIDE_DOWN
Definition: BlockType.h:989
@ E_BLOCK_STAIRS_UPSIDE_DOWN
Definition: BlockType.h:902
@ E_BLOCK_STAIRS_ZP
Definition: BlockType.h:900
@ E_BLOCK_STAIRS_ZM
Definition: BlockType.h:901
@ E_BLOCK_STAIRS_XM
Definition: BlockType.h:899
@ E_BLOCK_STAIRS_XP
Definition: BlockType.h:898
@ E_BLOCK_WOODEN_BUTTON
Definition: BlockType.h:158
@ E_BLOCK_STONE_BUTTON
Definition: BlockType.h:91
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
void AddFaceDirection(int &a_BlockX, int &a_BlockY, int &a_BlockZ, eBlockFace a_BlockFace, bool a_bInverse)
Modifies the specified coords so that they point to the block adjacent to the one specified through i...
Definition: Defines.cpp:378
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_YP
Definition: Defines.h:43
@ BLOCK_FACE_YM
Definition: Defines.h:42
@ BLOCK_FACE_ZM
Definition: Defines.h:44
@ BLOCK_FACE_ZP
Definition: Defines.h:45
@ BLOCK_FACE_XM
Definition: Defines.h:40
@ BLOCK_FACE_NONE
Definition: Defines.h:39
#define UNREACHABLE(x)
Definition: Globals.h:288
Byte ColourID
Definition: Globals.h:162
#define ASSERT(x)
Definition: Globals.h:276
#define UNUSED
Definition: Globals.h:72
static bool FullyOccupiesVoxel(BLOCKTYPE Block)
Does this block fully occupy its voxel - is it a 'full' block?
Definition: BlockInfo.cpp:606
static bool IsButtonOn(NIBBLETYPE a_Meta)
Extracts the ON bit from metadata and returns if true if it is set.
Definition: BlockButton.h:27
static void QueueButtonRelease(cWorld &a_ButtonWorld, const Vector3i a_Position, const BLOCKTYPE a_BlockType)
Schedules a recurring event at appropriate intervals to release a button at a given position.
Definition: BlockButton.h:187
static Vector3d GetButtonOffsetOnBlock(NIBBLETYPE a_Meta)
Returns an offset to the integer world coordinates of a button.
Definition: BlockButton.h:248
virtual bool CanBeAt(const cChunk &a_Chunk, const Vector3i a_Position, const NIBBLETYPE a_Meta) const override
Definition: BlockButton.h:132
virtual bool IsUseable(void) const override
Definition: BlockButton.h:100
static eBlockFace BlockMetaDataToBlockFace(NIBBLETYPE a_Meta)
Converts the block meta of this button into a block face of the neighbor to which the button is attac...
Definition: BlockButton.h:110
virtual bool OnUse(cChunkInterface &a_ChunkInterface, cWorldInterface &a_WorldInterface, cPlayer &a_Player, const Vector3i a_BlockPos, eBlockFace a_BlockFace, const Vector3i a_CursorPos) const override
Definition: BlockButton.h:64
static void OnArrowHit(cWorld &a_World, const Vector3i a_Position, const eBlockFace a_HitFace)
Event handler for an arrow striking a block.
Definition: BlockButton.h:34
static bool IsButtonPressedByArrow(cWorld &a_World, const Vector3i a_ButtonPosition, const BLOCKTYPE a_BlockType, const NIBBLETYPE a_Meta)
Returns true if an arrow was found in the wooden button.
Definition: BlockButton.h:224
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) const override
Definition: BlockButton.h:179
static bool IsAnySlabType(BLOCKTYPE a_BlockType)
Returns true if the specified blocktype is one of the slabs handled by this handler.
Definition: BlockSlab.h:30
static bool IsAnyStairType(BLOCKTYPE a_Block)
Definition: BlockStairs.h:19
virtual void BroadcastSoundEffect(const AString &a_SoundName, Vector3d a_Position, float a_Volume, float a_Pitch, const cClientHandle *a_Exclude=nullptr)=0
void SetBlockMeta(Vector3i a_BlockPos, NIBBLETYPE a_MetaData)
Sets the meta for the specified block, while keeping the blocktype.
NIBBLETYPE GetBlockMeta(Vector3i a_Pos)
Mixin to clear the block's meta value when converting to a pickup.
Definition: Mixins.h:31
virtual cBroadcastInterface & GetBroadcastManager()=0
virtual void WakeUpSimulators(Vector3i a_Block)=0
Wakes up the simulators for the specified block.
Represents two sets of coords, minimum and maximum for each direction.
Definition: BoundingBox.h:24
Definition: Chunk.h:36
bool UnboundedRelGetBlock(Vector3i a_RelCoords, BLOCKTYPE &a_BlockType, NIBBLETYPE &a_BlockMeta) const
Same as GetBlock(), but relative coords needn't be in this chunk (uses m_Neighbor-s or m_ChunkMap in ...
Definition: Chunk.cpp:1008
static bool IsValidHeight(Vector3i a_BlockPosition)
Validates a height-coordinate.
Definition: ChunkDef.h:185
Definition: Entity.h:76
bool IsArrow(void) const
Definition: Entity.h:158
cWorld * GetWorld(void) const
Definition: Entity.h:190
Definition: Player.h:29
cClientHandle * GetClientHandle(void) const
Definition: Player.h:276
Definition: World.h:53
void ScheduleTask(cTickTime a_DelayTicks, std::function< void(cWorld &)> a_Task)
Queues a lambda task onto the tick thread, with the specified delay.
Definition: World.cpp:2744
virtual cBroadcastInterface & GetBroadcastManager(void) override
Definition: World.h:203
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 ForEachEntityInBox(const cBoundingBox &a_Box, cEntityCallback a_Callback) override
Calls the callback for each entity that has a nonempty intersection with the specified boundingbox.
Definition: World.cpp:2445
virtual void WakeUpSimulators(Vector3i a_Block) override
Wakes up the simulators for the specified block.
Definition: World.cpp:1357
virtual void BroadcastSoundEffect(const AString &a_SoundName, Vector3d a_Position, float a_Volume, float a_Pitch, const cClientHandle *a_Exclude=nullptr) override
void SetBlockMeta(Vector3i a_BlockPos, NIBBLETYPE a_MetaData)
Sets the meta for the specified block, while keeping the blocktype.
Definition: World.cpp:1752