Cuberite
A lightweight, fast and extensible game server for Minecraft
BlockLeaves.h
Go to the documentation of this file.
1 #pragma once
2 #include "BlockHandler.h"
3 #include "../FastRandom.h"
4 #include "../BlockArea.h"
5 
6 
7 
8 
9 
10 // Leaves can be this many blocks that away (inclusive) from the log not to decay
11 #define LEAVES_CHECK_DISTANCE 6
12 
13 
14 
15 
16 
17 class cBlockLeavesHandler final :
18  public cBlockHandler
19 {
21 
22 public:
23 
24  using Super::Super;
25 
26 private:
27 
28  static double FortuneDropProbability(unsigned char a_DefaultDenominator, unsigned char a_FirstDenominatorReduction, unsigned char a_FortuneLevel)
29  {
30  // Fortune 3 behaves like fortune 4 for some reason
31  if (a_FortuneLevel == 3)
32  {
33  a_FortuneLevel++;
34  }
35 
36  // Denominator, capped at minimum of 10.
37  const auto Denominator = std::max<unsigned char>(10, a_DefaultDenominator - a_FortuneLevel * a_FirstDenominatorReduction);
38  return 1.0 / Denominator;
39  }
40 
41 
42 
43 
44 
46  static bool HasNearLog(cBlockArea & a_Area, const Vector3i a_BlockPos)
47  {
48  // Filter the blocks into a {leaves, log, other (air)} set:
49  auto * Types = a_Area.GetBlockTypes();
50  for (size_t i = a_Area.GetBlockCount() - 1; i > 0; i--)
51  {
52  switch (Types[i])
53  {
54  case E_BLOCK_LEAVES:
55  case E_BLOCK_LOG:
56  case E_BLOCK_NEW_LEAVES:
57  case E_BLOCK_NEW_LOG:
58  {
59  break;
60  }
61  default:
62  {
63  Types[i] = E_BLOCK_AIR;
64  break;
65  }
66  }
67  } // for i - Types[]
68 
69  // Perform a breadth-first search to see if there's a log connected within 4 blocks of the leaves block:
70  // Simply replace all reachable leaves blocks with a sponge block plus iteration (in the Area) and see if we can reach a log
71  a_Area.SetBlockType(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, E_BLOCK_SPONGE);
72  for (int i = 0; i < LEAVES_CHECK_DISTANCE; i++)
73  {
74  auto ProcessNeighbor = [&a_Area, i](int cbx, int cby, int cbz) -> bool
75  {
76  switch (a_Area.GetBlockType(cbx, cby, cbz))
77  {
78  case E_BLOCK_LEAVES: a_Area.SetBlockType(cbx, cby, cbz, static_cast<BLOCKTYPE>(E_BLOCK_SPONGE + i + 1)); break;
79  case E_BLOCK_LOG: return true;
80  case E_BLOCK_NEW_LEAVES: a_Area.SetBlockType(cbx, cby, cbz, static_cast<BLOCKTYPE>(E_BLOCK_SPONGE + i + 1)); break;
81  case E_BLOCK_NEW_LOG: return true;
82  }
83  return false;
84  };
85  for (int y = std::max(a_BlockPos.y - i, 0); y <= std::min(a_BlockPos.y + i, cChunkDef::Height - 1); y++)
86  {
87  for (int z = a_BlockPos.z - i; z <= a_BlockPos.z + i; z++)
88  {
89  for (int x = a_BlockPos.x - i; x <= a_BlockPos.x + i; x++)
90  {
91  if (a_Area.GetBlockType(x, y, z) != E_BLOCK_SPONGE + i)
92  {
93  continue;
94  }
95  if (
96  ProcessNeighbor(x - 1, y, z) ||
97  ProcessNeighbor(x + 1, y, z) ||
98  ProcessNeighbor(x, y, z - 1) ||
99  ProcessNeighbor(x, y, z + 1) ||
100  ProcessNeighbor(x, y + 1, z) ||
101  ProcessNeighbor(x, y - 1, z)
102  )
103  {
104  return true;
105  }
106  } // for x
107  } // for z
108  } // for y
109  } // for i - BFS iterations
110  return false;
111  }
112 
113  virtual cItems ConvertToPickups(const NIBBLETYPE a_BlockMeta, const cItem * const a_Tool) const override
114  {
115  // If breaking with shears, drop self:
116  if ((a_Tool != nullptr) && (a_Tool->m_ItemType == E_ITEM_SHEARS))
117  {
118  return cItem(m_BlockType, 1, a_BlockMeta & 0x03);
119  }
120 
121 
122  // There is a chance to drop a sapling that varies depending on the type of leaf broken.
123  // Note: It is possible (though very rare) for a single leaves block to drop both a sapling and an apple
124  double DropProbability;
125  const auto FortuneLevel = ToolFortuneLevel(a_Tool);
126  auto & Random = GetRandomProvider();
127  cItems Res;
128 
129  if ((m_BlockType == E_BLOCK_LEAVES) && ((a_BlockMeta & 0x03) == E_META_LEAVES_JUNGLE))
130  {
131  // Jungle leaves have a 2.5% default chance of dropping a sapling.
132  DropProbability = FortuneDropProbability(40, 4, FortuneLevel);
133  }
134  else
135  {
136  // Other leaves have a 5% default chance of dropping a sapling.
137  DropProbability = FortuneDropProbability(20, 4, FortuneLevel);
138  }
139 
140  if (Random.RandBool(DropProbability))
141  {
142  Res.Add(
144  1,
145  (m_BlockType == E_BLOCK_LEAVES) ? (a_BlockMeta & 0x03) : static_cast<short>(4 + (a_BlockMeta & 0x01))
146  );
147  }
148 
149  // 0.5 % chance of dropping an apple, increased by fortune, if the leaves' type is Apple Leaves
150  if ((m_BlockType == E_BLOCK_LEAVES) && ((a_BlockMeta & 0x03) == E_META_LEAVES_APPLE))
151  {
152  DropProbability = FortuneDropProbability(200, 20, FortuneLevel);
153  if (Random.RandBool(DropProbability))
154  {
155  Res.Add(E_ITEM_RED_APPLE);
156  }
157  }
158 
159  // 2% chance of dropping sticks (yuck) in 1.14
160  DropProbability = FortuneDropProbability(50, 5, FortuneLevel);
161  if (Random.RandBool(DropProbability))
162  {
163  // 1 or 2 sticks are dropped on success:
164  Res.Add(E_ITEM_STICK, Random.RandInt<char>(1, 2));
165  }
166 
167  return Res;
168  }
169 
170 
171 
172 
173 
174  virtual void OnNeighborChanged(cChunkInterface & a_ChunkInterface, Vector3i a_BlockPos, eBlockFace a_WhichNeighbor) const override
175  {
176  auto meta = a_ChunkInterface.GetBlockMeta(a_BlockPos);
177 
178  // Set bit 0x08, so this block gets checked for decay:
179  if ((meta & 0x08) == 0)
180  {
181  a_ChunkInterface.SetBlockMeta(a_BlockPos.x, a_BlockPos.y, a_BlockPos.z, meta | 0x8);
182  }
183  }
184 
185 
186 
187 
188 
189  virtual void OnUpdate(
190  cChunkInterface & a_ChunkInterface,
191  cWorldInterface & a_WorldInterface,
192  cBlockPluginInterface & a_PluginInterface,
193  cChunk & a_Chunk,
194  const Vector3i a_RelPos
195  ) const override
196  {
197  auto Meta = a_Chunk.GetMeta(a_RelPos);
198  if ((Meta & 0x04) != 0)
199  {
200  // Player-placed leaves, don't decay
201  return;
202  }
203 
204  if ((Meta & 0x08) == 0)
205  {
206  // These leaves have been checked for decay lately and nothing around them changed
207  return;
208  }
209 
210  // Get the data around the leaves:
211  auto worldPos = a_Chunk.RelativeToAbsolute(a_RelPos);
212  cBlockArea Area;
213  if (!Area.Read(
214  *a_Chunk.GetWorld(),
218  )
219  {
220  // Cannot check leaves, a chunk is missing too close
221  return;
222  }
223 
224  if (HasNearLog(Area, worldPos))
225  {
226  // Wood found, the leaves stay; unset the check bit
227  a_Chunk.SetMeta(a_RelPos, Meta ^ 0x08);
228  return;
229  }
230 
231  // Decay the leaves:
232  a_ChunkInterface.DropBlockAsPickups(worldPos);
233  }
234 
235 
236 
237 
238 
239  virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) const override
240  {
241  UNUSED(a_Meta);
242  return 7;
243  }
244 } ;
#define LEAVES_CHECK_DISTANCE
Definition: BlockLeaves.h:11
@ E_META_LEAVES_APPLE
Definition: BlockType.h:702
@ E_META_LEAVES_JUNGLE
Definition: BlockType.h:705
@ E_BLOCK_NEW_LEAVES
Definition: BlockType.h:180
@ E_BLOCK_SPONGE
Definition: BlockType.h:29
@ E_BLOCK_AIR
Definition: BlockType.h:10
@ E_BLOCK_LEAVES
Definition: BlockType.h:28
@ E_BLOCK_NEW_LOG
Definition: BlockType.h:181
@ E_BLOCK_LOG
Definition: BlockType.h:27
@ E_BLOCK_SAPLING
Definition: BlockType.h:16
@ E_ITEM_STICK
Definition: BlockType.h:324
@ E_ITEM_SHEARS
Definition: BlockType.h:404
@ E_ITEM_RED_APPLE
Definition: BlockType.h:304
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
eBlockFace
Block face constants, used in PlayerDigging and PlayerBlockPlacement packets and bbox collision calc.
Definition: Defines.h:38
MTRand & GetRandomProvider()
Returns the current thread's random number source.
Definition: FastRandom.cpp:12
Byte ColourID
Definition: Globals.h:162
#define UNUSED
Definition: Globals.h:72
Vector3< int > Vector3i
Definition: Vector3.h:487
void SetBlockType(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType)
Definition: BlockArea.cpp:1647
size_t GetBlockCount(void) const
Definition: BlockArea.h:393
BLOCKTYPE * GetBlockTypes(void) const
Returns the internal pointer to the block types.
Definition: BlockArea.h:389
bool Read(cForEachChunkProvider &a_ForEachChunkProvider, int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ, int a_DataTypes=baTypes|baMetas|baBlockEntities)
Reads an area of blocks specified.
Definition: BlockArea.cpp:445
BLOCKTYPE GetBlockType(int a_BlockX, int a_BlockY, int a_BlockZ) const
Definition: BlockArea.cpp:1724
static unsigned char ToolFortuneLevel(const cItem *a_Tool)
Returns the fortune level of a tool, if it is a valid tool.
constexpr cBlockHandler(BLOCKTYPE a_BlockType)
Definition: BlockHandler.h:29
const BLOCKTYPE m_BlockType
Definition: BlockHandler.h:205
virtual ColourID GetMapBaseColourID(NIBBLETYPE a_Meta) const override
Returns the base colour ID of the block, as will be represented on a map, as per documentation: https...
Definition: BlockLeaves.h:239
static double FortuneDropProbability(unsigned char a_DefaultDenominator, unsigned char a_FirstDenominatorReduction, unsigned char a_FortuneLevel)
Definition: BlockLeaves.h:28
static bool HasNearLog(cBlockArea &a_Area, const Vector3i a_BlockPos)
Returns true if the area contains a continous path from the specified block to a log block entirely m...
Definition: BlockLeaves.h:46
virtual void OnUpdate(cChunkInterface &a_ChunkInterface, cWorldInterface &a_WorldInterface, cBlockPluginInterface &a_PluginInterface, cChunk &a_Chunk, const Vector3i a_RelPos) const override
Called when the block gets ticked either by a random tick or by a queued tick.
Definition: BlockLeaves.h:189
virtual cItems ConvertToPickups(const NIBBLETYPE a_BlockMeta, const cItem *const a_Tool) const override
Returns the pickups that would result if the block was mined by a_Digger using a_Tool.
Definition: BlockLeaves.h:113
virtual void OnNeighborChanged(cChunkInterface &a_ChunkInterface, Vector3i a_BlockPos, eBlockFace a_WhichNeighbor) const override
Called when a direct neighbor of this block has been changed.
Definition: BlockLeaves.h:174
This interface is used to decouple block handlers from the cPluginManager dependency through cWorld.
void SetBlockMeta(Vector3i a_BlockPos, NIBBLETYPE a_MetaData)
Sets the meta for the specified block, while keeping the blocktype.
NIBBLETYPE GetBlockMeta(Vector3i a_Pos)
void DropBlockAsPickups(Vector3i a_BlockPos, const cEntity *a_Digger=nullptr, const cItem *a_Tool=nullptr)
Digs the block and spawns the relevant pickups, as if a_Digger used a_Tool to dig the block.
Definition: Chunk.h:36
NIBBLETYPE GetMeta(int a_RelX, int a_RelY, int a_RelZ) const
Definition: Chunk.h:279
Vector3i RelativeToAbsolute(Vector3i a_RelBlockPosition) const
Converts the coord relative to this chunk into an absolute coord.
Definition: Chunk.h:450
void SetMeta(int a_RelX, int a_RelY, int a_RelZ, NIBBLETYPE a_Meta)
Definition: Chunk.h:286
cWorld * GetWorld(void) const
Definition: Chunk.h:135
static const int Height
Definition: ChunkDef.h:125
Definition: Item.h:37
short m_ItemType
Definition: Item.h:163
This class bridges a vector of cItem for safe access via Lua.
Definition: Item.h:215
void Add(const cItem &a_Item)
Definition: Item.h:233
T x
Definition: Vector3.h:17
T y
Definition: Vector3.h:17
T z
Definition: Vector3.h:17