Cuberite
A lightweight, fast and extensible game server for Minecraft
EnderDragonFightStructuresGen.cpp
Go to the documentation of this file.
1 
2 #include "Globals.h"
3 
5 #include "../Chunk.h"
6 #include "../Entities/EnderCrystal.h"
7 #include "../WorldStorage/SchematicFileSerializer.h"
8 
9 const std::array<Vector3i, 48> cEnderDragonFightStructuresGen::m_CagePos =
10 {
11  {
12  // First layer
13  {-2, -1, -2}, {-2, -1, -1}, {-2, -1, 0}, {-2, -1, 1}, {-2, -1, 2},
14  {2, -1, -2}, {2, -1, -1}, {2, -1, 0}, {2, -1, 1}, {2, -1, 2},
15  {-1, -1, -2}, {0, -1, -2}, {1, -1, -2},
16  {-1, -1, 2}, {0, -1, 2}, {1, -1, 2},
17 
18  // Second layer
19  {-2, 0, -2}, {-2, 0, -1}, {-2, 0, 0}, {-2, 0, 1}, {-2, 0, 2},
20  {2, 0, -2}, {2, 0, -1}, {2, 0, 0}, {2, 0, 1}, {2, 0, 2},
21  {-1, 0, -2}, {0, 0, -2}, {1, 0, -2},
22  {-1, 0, 2}, {0, 0, 2}, {1, 0, 2},
23 
24  // Third layer
25  {-2, 1, -2}, {-2, 1, -1}, {-2, 1, 0}, {-2, 1, 1}, {-2, 1, 2},
26  {2, 1, -2}, {2, 1, -1}, {2, 1, 0}, {2, 1, 1}, {2, 1, 2},
27  {-1, 1, -2}, {0, 1, -2}, {1, 1, -2},
28  {-1, 1, 2}, {0, 1, 2}, {1, 1, 2},
29  }
30 };
31 
32 
33 
34 
35 
36 const std::array<Vector3i, 26> cEnderDragonFightStructuresGen::m_CageAir =
37 {
38  {
39  // First layer
40  {-1, -1, -1}, {0, -1, -1}, {1, -1, -1},
41  {-1, -1, 1}, {0, -1, 1}, {1, -1, 1},
42  {-1, -1, 0}, {1, -1, 0},
43 
44  // Second layer
45  {-1, 0, -1}, {0, 0, -1}, {1, 0, -1},
46  {-1, 0, 1}, {0, 0, 1}, {1, 0, 1},
47  {-1, 0, 0}, {1, 0, 0}, {0, 0, 0},
48 
49  // Third layer
50  {-1, 1, -1}, {0, 1, -1}, {1, 1, -1},
51  {-1, 1, 1}, {0, 1, 1}, {1, 1, 1},
52  {-1, 1, 0}, {1, 1, 0}, {0, 1, 0},
53  }
54 };
55 
56 
57 
58 
59 
61  m_Noise(a_Seed)
62 {
63 }
64 
65 
66 
67 
68 
69 void cEnderDragonFightStructuresGen::Init(const AString & a_TowerProperties, int a_Radius)
70 {
71  const auto ChunkWidth = cChunkDef::Width;
72  // Loads the fountain schematic
73  if (!cFile::IsFile(AString("Prefabs") + cFile::GetPathSeparator() + "SinglePieceStructures" + cFile::GetPathSeparator() + "EndFountain.schematic"))
74  {
75  LOGWARNING("EnderDragonFightStructuresGen is missing its end fountain prefab, please update your Cuberite server files! There will be no end fountain!");
76  }
77  else
78  {
79  cSchematicFileSerializer::LoadFromSchematicFile(m_Fountain, AString("Prefabs") + cFile::GetPathSeparator() + "SinglePieceStructures" + cFile::GetPathSeparator() + "EndFountain.schematic");
80  }
81 
82  // Reads the given tower properties
83  const auto TowerPropertiesVector = StringSplitAndTrim(a_TowerProperties, ";");
84  std::vector<sTowerProperties> TowerProperties;
85  for (const auto & TowerProperty : TowerPropertiesVector)
86  {
87  const auto TowerPropertyVector = StringSplitAndTrim(TowerProperty, "|");
88  if (TowerPropertyVector.size() != 3)
89  {
90  LOGWARNING("Got unknown parameters on generating obsidian pillars: %s, Please use \"Height|Radius|HasCage\"; ...", TowerProperty);
91  continue;
92  }
93  int Height = std::min(std::stoi(TowerPropertyVector[0]), cChunkDef::Height - 2); // The highest block placed is two blocks above the given height (the cage above some towers)
94  int Radius = std::stoi(TowerPropertyVector[1]);
95  bool HasCage;
96  if (NoCaseCompare(TowerPropertyVector[2], "true") == 0)
97  {
98  HasCage = true;
99  }
100  else if (NoCaseCompare(TowerPropertyVector[2], "false") == 0)
101  {
102  HasCage = false;
103  }
104  else
105  {
106  LOGWARNING("Got unknown value for boolean of the tower: %s should have a cage! %s. Tower will not be generated!", TowerProperty, TowerPropertyVector[2]);
107  continue;
108  }
109  TowerProperties.push_back({Vector3d(), Height, Radius, HasCage});
110  }
111  // A random angle in radian
112  double Angle = m_Noise.IntNoise1D(m_Noise.GetSeed()) * M_PI + M_PI;
113  // Shuffles the order of the towers
114  std::shuffle(TowerProperties.begin(), TowerProperties.end(), std::default_random_engine(static_cast<std::default_random_engine::result_type>(m_Noise.GetSeed())));
115  // Generate Positions in a circle
116  for (size_t I = 0; I < TowerProperties.size(); I++)
117  {
118  auto TowerPos = Vector3i(FloorC(a_Radius * cos(Angle)), 0, FloorC(a_Radius * sin(Angle)));
119  TowerProperties[I].m_Pos = TowerPos;
120 
121  // Check all crossed chunks
122  for (int X = -TowerProperties[I].m_Radius - ChunkWidth; X <= TowerProperties[I].m_Radius + ChunkWidth; X+=std::min(TowerProperties[I].m_Radius, ChunkWidth))
123  {
124  for (int Z = -TowerProperties[I].m_Radius - ChunkWidth; Z <= TowerProperties[I].m_Radius + ChunkWidth; Z+=std::min(TowerProperties[I].m_Radius, ChunkWidth))
125  {
126  auto Chunk = cChunkDef::BlockToChunk({TowerPos.x + X, 0, TowerPos.z + Z});
127  // Update limits
128  m_MinX = std::min(Chunk.m_ChunkX, m_MinX);
129  m_MinZ = std::min(Chunk.m_ChunkZ, m_MinZ);
130 
131  m_MaxX = std::max(Chunk.m_ChunkX, m_MaxX);
132  m_MaxZ = std::max(Chunk.m_ChunkZ, m_MaxZ);
133  // If the tower is not in chunk put it in
134  bool ShouldPlace = true;
135  for (const auto & Property : m_TowerPos[Chunk])
136  {
137  ShouldPlace &= !(TowerPos == Property.m_Pos);
138  }
139  if (ShouldPlace)
140  {
141  m_TowerPos[Chunk].emplace_back(TowerProperties[I]);
142  }
143  }
144  }
145  // Generate angle of next tower
146  Angle = fmod(Angle + (2.0 * M_PI / static_cast<double>(TowerProperties.size())), 2.0 * M_PI);
147  }
148 }
149 
150 
151 
152 
153 
155 {
156  auto Coords = a_ChunkDesc.GetChunkCoords();
157  // If not in the chunks to write
158  if ((Coords.m_ChunkX > m_MaxX) ||
159  (Coords.m_ChunkX < m_MinX) ||
160  (Coords.m_ChunkZ > m_MaxZ) ||
161  (Coords.m_ChunkZ < m_MinZ))
162  {
163  return;
164  }
165  // Places the exit portal
166  if (Coords == cChunkCoords({0, 0}))
167  {
168  /*
169  auto EnderDragon = std::make_unique<cEnderDragon>();
170  EnderDragon->SetPosition({0.0, static_cast<double>(a_ChunkDesc.GetHeight(0, 0) + 20), 0.0}); // Spawns the dragon 20 blocks above the terrain at (0, 0)
171  a_ChunkDesc.GetEntities().emplace_back(std::move(EnderDragon));
172  */ // Todo: 25.10.20 - Add the ender dragon spawning when the dragon behaves properly - 12xx12
173  a_ChunkDesc.WriteBlockArea(m_Fountain,
174  static_cast<int>(FloorC(-m_Fountain.GetSizeX() / 2)),
175  62,
176  static_cast<int>(FloorC(-m_Fountain.GetSizeX() / 2)),
178  );
179  }
180  else if (Coords == cChunkCoords({-1, 0}))
181  {
182  a_ChunkDesc.WriteBlockArea(m_Fountain,
183  cChunkDef::Width - static_cast<int>(FloorC(m_Fountain.GetSizeX() / 2)),
184  62,
185  static_cast<int>(FloorC(-m_Fountain.GetSizeZ() / 2)),
187  );
188  }
189  else if (Coords == cChunkCoords({0, -1}))
190  {
191  a_ChunkDesc.WriteBlockArea(m_Fountain,
192  static_cast<int>(FloorC(-m_Fountain.GetSizeX() / 2)),
193  62,
194  cChunkDef::Width - static_cast<int>(FloorC(m_Fountain.GetSizeZ() / 2)),
196  }
197  else if (Coords == cChunkCoords({-1, -1}))
198  {
199  a_ChunkDesc.WriteBlockArea(m_Fountain,
200  cChunkDef::Width - static_cast<int>(FloorC(m_Fountain.GetSizeX() / 2)),
201  62,
202  cChunkDef::Width - static_cast<int>(FloorC(m_Fountain.GetSizeZ() / 2)),
204  }
205  auto It = m_TowerPos.find(Coords);
206  if (It == m_TowerPos.end())
207  {
208  return;
209  }
210  for (const auto & Property : It->second)
211  {
212  PlaceTower(a_ChunkDesc, Property);
213  }
214 }
215 
216 
217 
218 
219 
221 {
222  auto Pos = cChunkDef::AbsoluteToRelative(a_Properties.m_Pos, a_ChunkDesc.GetChunkCoords());
223  // Place obsidian pillar
224  for (int X = -a_Properties.m_Radius; X < a_Properties.m_Radius; X++)
225  {
226  for (int Z = -a_Properties.m_Radius; Z < a_Properties.m_Radius; Z++)
227  {
228  Vector3i NewPos = {Pos.x + X, 1, Pos.z + Z};
229  if (cChunkDef::IsValidRelPos(NewPos))
230  {
231  // The 3 was achieved by trial and error till the shape matched the notchian implementation
232  if ((NewPos - Vector3i(Pos.x, 1, Pos.z)).SqrLength() < a_Properties.m_Radius * a_Properties.m_Radius - 3)
233  {
234  for (int Y = 0; Y <= a_Properties.m_Height - 2; Y++)
235  {
236  a_ChunkDesc.SetBlockType(NewPos.x, Y, NewPos.z, E_BLOCK_OBSIDIAN);
237  }
238  }
239  }
240  }
241  }
242 
243  // Spawn the iron bars if there are some
244  if (a_Properties.m_HasCage)
245  {
246  // Place walls
247  for (const auto & Offset : m_CagePos)
248  {
249  if (cChunkDef::IsValidRelPos(Vector3d(Pos.x, a_Properties.m_Height, Pos.z) + Offset))
250  {
251  a_ChunkDesc.SetBlockType(Pos.x + Offset.x, a_Properties.m_Height + Offset.y, Pos.z + Offset.z, E_BLOCK_IRON_BARS);
252  }
253  }
254  // Remove any block that may generate inside the cage
255  for (const auto & Offset : m_CageAir)
256  {
257  if (cChunkDef::IsValidRelPos(Pos + Offset))
258  {
259  a_ChunkDesc.SetBlockType(Pos.x + Offset.x, a_Properties.m_Height + Offset.y, Pos.z + Offset.z, E_BLOCK_AIR);
260  }
261  }
262  // Place roof
263  for (int X = -2; X <= 2; ++X)
264  {
265  for (int Z = -2; Z <= 2; ++Z)
266  {
267  if (cChunkDef::IsValidRelPos({Pos.x + X, 1, Pos.z + Z}))
268  {
269  a_ChunkDesc.SetBlockType(Pos.x + X, a_Properties.m_Height + 2, Pos.z + Z, E_BLOCK_IRON_BARS);
270  }
271  }
272  }
273  }
274  // Place the top decoration if the origin is in this chunk
275  if (cChunkDef::IsValidRelPos(Pos))
276  {
277  // Spawn the bedrock
278  a_ChunkDesc.SetBlockType(Pos.x, a_Properties.m_Height - 1, Pos.z, E_BLOCK_BEDROCK);
279  // Spawn the fire
280  a_ChunkDesc.SetBlockType(Pos.x, a_Properties.m_Height, Pos.z, E_BLOCK_FIRE);
281  // Spawn the ender crystal of the origin position is in this chunk
282  auto EnderCrystal = std::make_unique<cEnderCrystal>(Vector3d(0.5, a_Properties.m_Height, 0.5) + a_Properties.m_Pos, true);
283  a_ChunkDesc.GetEntities().emplace_back(std::move(EnderCrystal));
284  }
285 }
@ E_BLOCK_AIR
Definition: BlockType.h:10
@ E_BLOCK_FIRE
Definition: BlockType.h:61
@ E_BLOCK_BEDROCK
Definition: BlockType.h:17
@ E_BLOCK_OBSIDIAN
Definition: BlockType.h:59
@ E_BLOCK_IRON_BARS
Definition: BlockType.h:116
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
void LOGWARNING(std::string_view a_Format, const Args &... args)
Definition: LoggerSimple.h:67
AStringVector StringSplitAndTrim(const AString &str, const AString &delim)
Split the string at any of the listed delimiters and trim each value.
int NoCaseCompare(const AString &s1, const AString &s2)
Case-insensitive string comparison.
std::string AString
Definition: StringUtils.h:11
Vector3< double > Vector3d
Definition: Vector3.h:485
Vector3< int > Vector3i
Definition: Vector3.h:487
int GetSizeZ(void) const
Definition: BlockArea.h:349
@ msSpongePrint
Definition: BlockArea.h:64
int GetSizeX(void) const
Definition: BlockArea.h:347
Wraps the chunk coords into a single structure.
Definition: ChunkDef.h:57
static void BlockToChunk(int a_X, int a_Z, int &a_ChunkX, int &a_ChunkZ)
Converts absolute block coords to chunk coords:
Definition: ChunkDef.h:210
static void AbsoluteToRelative(int &a_X, int &a_Y, int &a_Z, int &a_ChunkX, int &a_ChunkZ)
Converts absolute block coords into relative (chunk + block) coords:
Definition: ChunkDef.h:147
static bool IsValidRelPos(Vector3i a_RelPos)
Validates a chunk relative coordinate.
Definition: ChunkDef.h:199
static const int Width
Definition: ChunkDef.h:124
static const int Height
Definition: ChunkDef.h:125
void SetBlockType(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType)
Definition: ChunkDesc.cpp:81
cEntityList & GetEntities(void)
Definition: ChunkDesc.h:240
void WriteBlockArea(const cBlockArea &a_BlockArea, int a_RelX, int a_RelY, int a_RelZ, cBlockArea::eMergeStrategy a_MergeStrategy=cBlockArea::msOverwrite)
Writes the block area into the chunk, with its origin set at the specified relative coords.
Definition: ChunkDesc.cpp:271
cChunkCoords GetChunkCoords() const
Definition: ChunkDesc.h:54
static const std::array< Vector3i, 26 > m_CageAir
void Init(const AString &a_TowerProperties, int a_Radius)
void GenFinish(cChunkDesc &a_ChunkDesc) override
void PlaceTower(cChunkDesc &a_ChunkDesc, const sTowerProperties &a_TowerProperties)
std::map< cChunkCoords, std::vector< sTowerProperties > > m_TowerPos
static const std::array< Vector3i, 48 > m_CagePos
int GetSeed(void) const
Definition: Noise.h:53
NOISE_DATATYPE IntNoise1D(int a_X) const
Definition: Noise.h:187
static AString GetPathSeparator()
Returns the path separator used by the current platform.
Definition: File.cpp:669
static bool IsFile(const AString &a_Path)
Returns true if the specified path is a regular file.
Definition: File.cpp:425
T x
Definition: Vector3.h:17
T z
Definition: Vector3.h:17
static void LoadFromSchematicFile(cBlockArea &a_BlockArea, const std::string &a_FileName)
Loads an area from a .schematic file.