3 Copyright (C) 2014-2018 kwolekr, Ryan Kwolek <kwolekr@minetest.net>
4 Copyright (C) 2015-2018 paramat
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc.,
18 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "mg_decoration.h"
22 #include "mg_schematic.h"
27 #include "util/numeric.h"
32 FlagDesc flagdesc_deco
[] = {
33 {"place_center_x", DECO_PLACE_CENTER_X
},
34 {"place_center_y", DECO_PLACE_CENTER_Y
},
35 {"place_center_z", DECO_PLACE_CENTER_Z
},
36 {"force_placement", DECO_FORCE_PLACEMENT
},
37 {"liquid_surface", DECO_LIQUID_SURFACE
},
38 {"all_floors", DECO_ALL_FLOORS
},
39 {"all_ceilings", DECO_ALL_CEILINGS
},
44 ///////////////////////////////////////////////////////////////////////////////
47 DecorationManager::DecorationManager(IGameDef
*gamedef
) :
48 ObjDefManager(gamedef
, OBJDEF_DECORATION
)
53 size_t DecorationManager::placeAllDecos(Mapgen
*mg
, u32 blockseed
,
54 v3s16 nmin
, v3s16 nmax
)
58 for (size_t i
= 0; i
!= m_objects
.size(); i
++) {
59 Decoration
*deco
= (Decoration
*)m_objects
[i
];
63 nplaced
+= deco
->placeDeco(mg
, blockseed
, nmin
, nmax
);
70 DecorationManager
*DecorationManager::clone() const
72 auto mgr
= new DecorationManager();
73 ObjDefManager::cloneTo(mgr
);
78 ///////////////////////////////////////////////////////////////////////////////
81 void Decoration::resolveNodeNames()
83 getIdsFromNrBacklog(&c_place_on
);
84 getIdsFromNrBacklog(&c_spawnby
);
88 bool Decoration::canPlaceDecoration(MMVManip
*vm
, v3s16 p
)
90 // Check if the decoration can be placed on this node
91 u32 vi
= vm
->m_area
.index(p
);
92 if (!CONTAINS(c_place_on
, vm
->m_data
[vi
].getContent()))
95 // Don't continue if there are no spawnby constraints
100 static const v3s16 dirs
[16] = {
120 // Check these 16 neighbouring nodes for enough spawnby nodes
121 for (size_t i
= 0; i
!= ARRLEN(dirs
); i
++) {
122 u32 index
= vm
->m_area
.index(p
+ dirs
[i
]);
123 if (!vm
->m_area
.contains(index
))
126 if (CONTAINS(c_spawnby
, vm
->m_data
[index
].getContent()))
130 if (nneighs
< nspawnby
)
137 size_t Decoration::placeDeco(Mapgen
*mg
, u32 blockseed
, v3s16 nmin
, v3s16 nmax
)
139 PcgRandom
ps(blockseed
+ 53);
140 int carea_size
= nmax
.X
- nmin
.X
+ 1;
142 // Divide area into parts
143 // If chunksize is changed it may no longer be divisable by sidelen
144 if (carea_size
% sidelen
)
145 sidelen
= carea_size
;
147 s16 divlen
= carea_size
/ sidelen
;
148 int area
= sidelen
* sidelen
;
150 for (s16 z0
= 0; z0
< divlen
; z0
++)
151 for (s16 x0
= 0; x0
< divlen
; x0
++) {
152 v2s16
p2d_center( // Center position of part of division
153 nmin
.X
+ sidelen
/ 2 + sidelen
* x0
,
154 nmin
.Z
+ sidelen
/ 2 + sidelen
* z0
156 v2s16
p2d_min( // Minimum edge of part of division
157 nmin
.X
+ sidelen
* x0
,
158 nmin
.Z
+ sidelen
* z0
160 v2s16
p2d_max( // Maximum edge of part of division
161 nmin
.X
+ sidelen
+ sidelen
* x0
- 1,
162 nmin
.Z
+ sidelen
+ sidelen
* z0
- 1
166 // Amount of decorations
167 float nval
= (flags
& DECO_USE_NOISE
) ?
168 NoisePerlin2D(&np
, p2d_center
.X
, p2d_center
.Y
, mapseed
) :
173 // Complete coverage. Disable random placement to avoid
174 // redundant multiple placements at one position.
178 float deco_count_f
= (float)area
* nval
;
179 if (deco_count_f
>= 1.0f
) {
180 deco_count
= deco_count_f
;
181 } else if (deco_count_f
> 0.0f
) {
182 // For very low density calculate a chance for 1 decoration
183 if (ps
.range(1000) <= deco_count_f
* 1000.0f
)
188 s16 x
= p2d_min
.X
- 1;
191 for (u32 i
= 0; i
< deco_count
; i
++) {
193 x
= ps
.range(p2d_min
.X
, p2d_max
.X
);
194 z
= ps
.range(p2d_min
.Y
, p2d_max
.Y
);
197 if (x
== p2d_max
.X
+ 1) {
202 int mapindex
= carea_size
* (z
- nmin
.Z
) + (x
- nmin
.X
);
204 if ((flags
& DECO_ALL_FLOORS
) ||
205 (flags
& DECO_ALL_CEILINGS
)) {
206 // All-surfaces decorations
207 // Check biome of column
208 if (mg
->biomemap
&& !biomes
.empty()) {
209 auto iter
= biomes
.find(mg
->biomemap
[mapindex
]);
210 if (iter
== biomes
.end())
214 // Get all floors and ceilings in node column
215 u16 size
= (nmax
.Y
- nmin
.Y
+ 1) / 2;
216 std::vector
<s16
> floors
;
217 std::vector
<s16
> ceilings
;
218 floors
.reserve(size
);
219 ceilings
.reserve(size
);
221 mg
->getSurfaces(v2s16(x
, z
), nmin
.Y
, nmax
.Y
, floors
, ceilings
);
223 if (flags
& DECO_ALL_FLOORS
) {
225 for (const s16 y
: floors
) {
226 if (y
< y_min
|| y
> y_max
)
230 if (generate(mg
->vm
, &ps
, pos
, false))
231 mg
->gennotify
.addEvent(
232 GENNOTIFY_DECORATION
, pos
, index
);
236 if (flags
& DECO_ALL_CEILINGS
) {
237 // Ceiling decorations
238 for (const s16 y
: ceilings
) {
239 if (y
< y_min
|| y
> y_max
)
243 if (generate(mg
->vm
, &ps
, pos
, true))
244 mg
->gennotify
.addEvent(
245 GENNOTIFY_DECORATION
, pos
, index
);
248 } else { // Heightmap decorations
249 s16 y
= -MAX_MAP_GENERATION_LIMIT
;
250 if (flags
& DECO_LIQUID_SURFACE
)
251 y
= mg
->findLiquidSurface(v2s16(x
, z
), nmin
.Y
, nmax
.Y
);
252 else if (mg
->heightmap
)
253 y
= mg
->heightmap
[mapindex
];
255 y
= mg
->findGroundLevel(v2s16(x
, z
), nmin
.Y
, nmax
.Y
);
257 if (y
< y_min
|| y
> y_max
|| y
< nmin
.Y
|| y
> nmax
.Y
)
260 if (mg
->biomemap
&& !biomes
.empty()) {
261 auto iter
= biomes
.find(mg
->biomemap
[mapindex
]);
262 if (iter
== biomes
.end())
267 if (generate(mg
->vm
, &ps
, pos
, false))
268 mg
->gennotify
.addEvent(GENNOTIFY_DECORATION
, pos
, index
);
277 void Decoration::cloneTo(Decoration
*def
) const
279 ObjDef::cloneTo(def
);
281 def
->mapseed
= mapseed
;
282 def
->c_place_on
= c_place_on
;
283 def
->sidelen
= sidelen
;
286 def
->fill_ratio
= fill_ratio
;
288 def
->c_spawnby
= c_spawnby
;
289 def
->nspawnby
= nspawnby
;
290 def
->place_offset_y
= place_offset_y
;
291 def
->biomes
= biomes
;
295 ///////////////////////////////////////////////////////////////////////////////
298 ObjDef
*DecoSimple::clone() const
300 auto def
= new DecoSimple();
301 Decoration::cloneTo(def
);
303 def
->c_decos
= c_decos
;
304 def
->deco_height
= deco_height
;
305 def
->deco_height_max
= deco_height_max
;
306 def
->deco_param2
= deco_param2
;
307 def
->deco_param2_max
= deco_param2_max
;
313 void DecoSimple::resolveNodeNames()
315 Decoration::resolveNodeNames();
316 getIdsFromNrBacklog(&c_decos
);
320 size_t DecoSimple::generate(MMVManip
*vm
, PcgRandom
*pr
, v3s16 p
, bool ceiling
)
322 // Don't bother if there aren't any decorations to place
326 if (!canPlaceDecoration(vm
, p
))
329 // Check for placement outside the voxelmanip volume
331 // Ceiling decorations
332 // 'place offset y' is inverted
333 if (p
.Y
- place_offset_y
- std::max(deco_height
, deco_height_max
) <
334 vm
->m_area
.MinEdge
.Y
)
337 if (p
.Y
- 1 - place_offset_y
> vm
->m_area
.MaxEdge
.Y
)
340 } else { // Heightmap and floor decorations
341 if (p
.Y
+ place_offset_y
+ std::max(deco_height
, deco_height_max
) >
342 vm
->m_area
.MaxEdge
.Y
)
345 if (p
.Y
+ 1 + place_offset_y
< vm
->m_area
.MinEdge
.Y
)
349 content_t c_place
= c_decos
[pr
->range(0, c_decos
.size() - 1)];
350 s16 height
= (deco_height_max
> 0) ?
351 pr
->range(deco_height
, deco_height_max
) : deco_height
;
352 u8 param2
= (deco_param2_max
> 0) ?
353 pr
->range(deco_param2
, deco_param2_max
) : deco_param2
;
354 bool force_placement
= (flags
& DECO_FORCE_PLACEMENT
);
356 const v3s16
&em
= vm
->m_area
.getExtent();
357 u32 vi
= vm
->m_area
.index(p
);
360 // Ceiling decorations
361 // 'place offset y' is inverted
362 VoxelArea::add_y(em
, vi
, -place_offset_y
);
364 for (int i
= 0; i
< height
; i
++) {
365 VoxelArea::add_y(em
, vi
, -1);
366 content_t c
= vm
->m_data
[vi
].getContent();
367 if (c
!= CONTENT_AIR
&& c
!= CONTENT_IGNORE
&& !force_placement
)
370 vm
->m_data
[vi
] = MapNode(c_place
, 0, param2
);
372 } else { // Heightmap and floor decorations
373 VoxelArea::add_y(em
, vi
, place_offset_y
);
375 for (int i
= 0; i
< height
; i
++) {
376 VoxelArea::add_y(em
, vi
, 1);
377 content_t c
= vm
->m_data
[vi
].getContent();
378 if (c
!= CONTENT_AIR
&& c
!= CONTENT_IGNORE
&& !force_placement
)
381 vm
->m_data
[vi
] = MapNode(c_place
, 0, param2
);
389 ///////////////////////////////////////////////////////////////////////////////
392 DecoSchematic::~DecoSchematic()
399 ObjDef
*DecoSchematic::clone() const
401 auto def
= new DecoSchematic();
402 Decoration::cloneTo(def
);
403 NodeResolver::cloneTo(def
);
405 def
->rotation
= rotation
;
406 /* FIXME: We do not own this schematic, yet we only have a pointer to it
407 * and not a handle. We are left with no option but to clone it ourselves.
408 * This is a waste of memory and should be replaced with an alternative
409 * approach sometime. */
410 def
->schematic
= dynamic_cast<Schematic
*>(schematic
->clone());
411 def
->was_cloned
= true;
417 size_t DecoSchematic::generate(MMVManip
*vm
, PcgRandom
*pr
, v3s16 p
, bool ceiling
)
419 // Schematic could have been unloaded but not the decoration
420 // In this case generate() does nothing (but doesn't *fail*)
421 if (schematic
== NULL
)
424 if (!canPlaceDecoration(vm
, p
))
427 if (flags
& DECO_PLACE_CENTER_Y
) {
428 p
.Y
-= (schematic
->size
.Y
- 1) / 2;
430 // Only apply 'place offset y' if not 'deco place center y'
432 // Shift down so that schematic top layer is level with ceiling
433 // 'place offset y' is inverted
434 p
.Y
-= (place_offset_y
+ schematic
->size
.Y
- 1);
436 p
.Y
+= place_offset_y
;
439 // Check schematic top and base are in voxelmanip
440 if (p
.Y
+ schematic
->size
.Y
- 1 > vm
->m_area
.MaxEdge
.Y
)
443 if (p
.Y
< vm
->m_area
.MinEdge
.Y
)
446 Rotation rot
= (rotation
== ROTATE_RAND
) ?
447 (Rotation
)pr
->range(ROTATE_0
, ROTATE_270
) : rotation
;
449 if (flags
& DECO_PLACE_CENTER_X
) {
450 if (rot
== ROTATE_0
|| rot
== ROTATE_180
)
451 p
.X
-= (schematic
->size
.X
- 1) / 2;
453 p
.Z
-= (schematic
->size
.X
- 1) / 2;
455 if (flags
& DECO_PLACE_CENTER_Z
) {
456 if (rot
== ROTATE_0
|| rot
== ROTATE_180
)
457 p
.Z
-= (schematic
->size
.Z
- 1) / 2;
459 p
.X
-= (schematic
->size
.Z
- 1) / 2;
462 bool force_placement
= (flags
& DECO_FORCE_PLACEMENT
);
464 schematic
->blitToVManip(vm
, p
, rot
, force_placement
);