Line data Source code
1 : // world.cpp: core map management stuff
2 :
3 : #include "../libprimis-headers/cube.h"
4 : #include "../../shared/geomexts.h"
5 : #include "../../shared/glexts.h"
6 :
7 : #include <memory>
8 : #include <optional>
9 :
10 : #include "bih.h"
11 : #include "entities.h"
12 : #include "light.h"
13 : #include "octaedit.h"
14 : #include "octaworld.h"
15 : #include "raycube.h"
16 : #include "world.h"
17 :
18 : #include "interface/console.h"
19 : #include "interface/cs.h"
20 : #include "interface/menus.h"
21 :
22 : #include "model/model.h"
23 :
24 : #include "render/octarender.h"
25 : #include "render/renderlights.h"
26 : #include "render/rendermodel.h"
27 : #include "render/renderparticles.h"
28 : #include "render/shaderparam.h"
29 : #include "render/stain.h"
30 : #include "render/texture.h"
31 :
32 : SVARR(maptitle, "Untitled Map by Unknown");
33 :
34 : std::vector<int> outsideents;
35 : std::vector<int> entgroup;
36 :
37 : namespace entities
38 : {
39 : std::vector<extentity *> ents;
40 :
41 0 : std::vector<extentity *> &getents()
42 : {
43 0 : return ents;
44 : }
45 :
46 0 : const char *entmodel(const entity &e)
47 : {
48 0 : return nullptr;
49 : }
50 :
51 0 : extentity *newentity()
52 : {
53 0 : return new extentity();
54 : }
55 :
56 0 : void deleteentity(extentity *e)
57 : {
58 0 : delete e;
59 0 : }
60 :
61 0 : void clearents()
62 : {
63 0 : while(ents.size())
64 : {
65 0 : deleteentity(ents.back());
66 0 : ents.pop_back();
67 : }
68 0 : }
69 :
70 : }
71 : //octaents are the ones that modify the level directly: other entities like
72 : //sounds, lights, spawns etc. don't get directly rendered
73 : VAR(entselradius, 0, 2, 10);
74 : VAR(octaentsize, 0, 64, 1024);
75 :
76 2 : void entcancel()
77 : {
78 2 : entgroup.clear();
79 2 : }
80 :
81 : //need a getter fxn because decalslot obj not exposed to the game
82 0 : float getdecalslotdepth(const DecalSlot &s)
83 : {
84 0 : return s.depth;
85 : }
86 :
87 0 : void detachentity(extentity &e)
88 : {
89 0 : if(!e.attached)
90 : {
91 0 : return;
92 : }
93 0 : e.attached->attached = nullptr;
94 0 : e.attached = nullptr;
95 : }
96 :
97 : VAR(attachradius, 1, 100, 1000);
98 :
99 0 : void attachentity(extentity &e)
100 : {
101 : //don't attempt to attach invalid to attach ents
102 0 : switch(e.type)
103 : {
104 0 : case EngineEnt_Spotlight:
105 : {
106 0 : break;
107 : }
108 0 : default:
109 : {
110 0 : return;
111 : }
112 : }
113 0 : detachentity(e);
114 :
115 0 : std::vector<extentity *> &ents = entities::getents();
116 0 : int closest = -1;
117 0 : float closedist = 1e10f; //some arbitrary high value
118 0 : for(uint i = 0; i < ents.size(); i++)
119 : {
120 0 : extentity *a = ents[i];
121 0 : if(a->attached)
122 : {
123 0 : continue;
124 : }
125 0 : switch(e.type)
126 : {
127 0 : case EngineEnt_Spotlight:
128 : {
129 : //only attempt to attach to lights
130 0 : if(a->type!=EngineEnt_Light)
131 : {
132 0 : continue;
133 : }
134 0 : break;
135 : }
136 0 : default:
137 : {
138 0 : continue;
139 : }
140 : }
141 0 : float dist = e.o.dist(a->o);
142 0 : if(dist < closedist)
143 : {
144 0 : closest = i;
145 0 : closedist = dist;
146 : }
147 : }
148 0 : if(closedist>attachradius)
149 : {
150 0 : return;
151 : }
152 0 : e.attached = ents[closest];
153 0 : ents[closest]->attached = &e;
154 : }
155 :
156 : //used in iengine
157 0 : void attachentities()
158 : {
159 0 : for(extentity *& i : entities::getents())
160 : {
161 0 : attachentity(*i);
162 : }
163 0 : }
164 :
165 : enum ModOctaEnt
166 : {
167 : ModOctaEnt_Add = 1<<0,
168 : ModOctaEnt_UpdateBB = 1<<1,
169 : ModOctaEnt_Changed = 1<<2
170 : };
171 :
172 0 : static void rotatebb(vec ¢er, vec &radius, int yaw, int pitch, int roll)
173 : {
174 0 : matrix3 orient;
175 0 : orient.identity();
176 0 : if(yaw)
177 : {
178 0 : orient.rotate_around_z(sincosmod360(yaw));
179 : }
180 0 : if(pitch)
181 : {
182 0 : orient.rotate_around_x(sincosmod360(pitch));
183 : }
184 0 : if(roll)
185 : {
186 0 : orient.rotate_around_y(sincosmod360(-roll));
187 : }
188 0 : center = orient.transform(center);
189 0 : radius = orient.abstransform(radius);
190 0 : }
191 :
192 0 : static void transformbb(const entity &e, vec ¢er, vec &radius)
193 : {
194 0 : if(e.attr5 > 0)
195 : {
196 0 : float scale = e.attr5/100.0f;
197 0 : center.mul(scale);
198 0 : radius.mul(scale);
199 : }
200 0 : rotatebb(center, radius, e.attr2, e.attr3, e.attr4);
201 0 : }
202 :
203 : //used in iengine
204 0 : void mmboundbox(const entity &e, model *m, vec ¢er, vec &radius)
205 : {
206 0 : m->boundbox(center, radius);
207 0 : transformbb(e, center, radius);
208 0 : }
209 :
210 0 : static void mmcollisionbox(const entity &e, model *m, vec ¢er, vec &radius)
211 : {
212 0 : m->collisionbox(center, radius);
213 0 : transformbb(e, center, radius);
214 0 : }
215 :
216 0 : static void decalboundbox(const entity &e, const DecalSlot &s, vec ¢er, vec &radius)
217 : {
218 0 : float size = std::max(static_cast<float>(e.attr5), 1.0f);
219 0 : center = vec(0, s.depth * size/2, 0);
220 0 : radius = vec(size/2, s.depth * size/2, size/2);
221 0 : rotatebb(center, radius, e.attr2, e.attr3, e.attr4);
222 0 : }
223 :
224 0 : static bool modifyoctaent(int flags, int id)
225 : {
226 0 : std::vector<extentity *> &ents = entities::getents();
227 0 : return (static_cast<int>(ents.size()) > id) && ::rootworld.modifyoctaent(flags, id, *ents[id]);
228 : }
229 :
230 0 : static void removeentity(int id)
231 : {
232 0 : modifyoctaent(ModOctaEnt_UpdateBB, id);
233 0 : }
234 :
235 0 : void freeoctaentities(cube &c)
236 : {
237 0 : if(!c.ext)
238 : {
239 0 : return;
240 : }
241 0 : if(entities::getents().size())
242 : {
243 0 : while(c.ext->ents && !c.ext->ents->mapmodels.empty())
244 : {
245 0 : int id = c.ext->ents->mapmodels.back();
246 0 : c.ext->ents->mapmodels.pop_back();
247 0 : removeentity(id);
248 : }
249 0 : while(c.ext->ents && !c.ext->ents->decals.empty())
250 : {
251 0 : int id = c.ext->ents->decals.back();
252 0 : c.ext->ents->decals.pop_back();
253 0 : removeentity(id);
254 : }
255 0 : while(c.ext->ents && !c.ext->ents->other.empty())
256 : {
257 0 : int id = c.ext->ents->other.back();
258 0 : c.ext->ents->other.pop_back();
259 0 : removeentity(id);
260 : }
261 : }
262 0 : if(c.ext->ents)
263 : {
264 0 : delete c.ext->ents;
265 0 : c.ext->ents = nullptr;
266 : }
267 : }
268 :
269 0 : static bool getentboundingbox(const extentity &e, ivec &o, ivec &r)
270 : {
271 0 : switch(e.type)
272 : {
273 0 : case EngineEnt_Empty:
274 : {
275 0 : return false;
276 : }
277 0 : case EngineEnt_Decal:
278 : {
279 0 : const DecalSlot &s = lookupdecalslot(e.attr1, false);
280 0 : vec center, radius;
281 0 : decalboundbox(e, s, center, radius);
282 0 : center.add(e.o);
283 0 : radius.max(entselradius);
284 0 : o = ivec(vec(center).sub(radius));
285 0 : r = ivec(vec(center).add(radius).add(1));
286 0 : break;
287 : }
288 0 : case EngineEnt_Mapmodel:
289 : {
290 0 : if(model *m = loadmapmodel(e.attr1))
291 : {
292 0 : vec center, radius;
293 0 : mmboundbox(e, m, center, radius);
294 0 : center.add(e.o);
295 0 : radius.max(entselradius);
296 0 : o = ivec(vec(center).sub(radius));
297 0 : r = ivec(vec(center).add(radius).add(1));
298 : }
299 0 : break;
300 : }
301 : // invisible mapmodels use entselradius: lights sounds spawns etc.
302 0 : default:
303 : {
304 0 : o = ivec(vec(e.o).sub(entselradius));
305 0 : r = ivec(vec(e.o).add(entselradius+1));
306 0 : break;
307 : }
308 : }
309 0 : return true;
310 : }
311 :
312 0 : static void modifyoctaentity(int flags, int id, const extentity &e, std::array<cube, 8> &c, const ivec &cor, int size, const ivec &bo, const ivec &br, int leafsize, vtxarray *lastva = nullptr)
313 : {
314 0 : LOOP_OCTA_BOX(cor, size, bo, br)
315 : {
316 0 : ivec o(i, cor, size);
317 0 : vtxarray *va = c[i].ext && c[i].ext->va ? c[i].ext->va : lastva;
318 0 : if(c[i].children != nullptr && size > leafsize)
319 : {
320 0 : modifyoctaentity(flags, id, e, *(c[i].children), o, size>>1, bo, br, leafsize, va);
321 : }
322 0 : else if(flags&ModOctaEnt_Add)
323 : {
324 0 : if(!c[i].ext || !c[i].ext->ents)
325 : {
326 0 : ext(c[i]).ents = new octaentities(o, size);
327 : }
328 0 : octaentities &oe = *c[i].ext->ents;
329 0 : switch(e.type)
330 : {
331 0 : case EngineEnt_Decal:
332 : {
333 0 : if(va)
334 : {
335 0 : va->bbmin.x = -1;
336 0 : if(oe.decals.empty())
337 : {
338 0 : va->decals.push_back(&oe);
339 : }
340 : }
341 0 : oe.decals.push_back(id);
342 0 : oe.bbmin.min(bo).max(oe.o);
343 0 : oe.bbmax.max(br).min(ivec(oe.o).add(oe.size));
344 0 : break;
345 : }
346 0 : case EngineEnt_Mapmodel:
347 : {
348 0 : if(loadmapmodel(e.attr1))
349 : {
350 0 : if(va)
351 : {
352 0 : va->bbmin.x = -1;
353 0 : if(oe.mapmodels.empty())
354 : {
355 0 : va->mapmodels.push_back(&oe);
356 : }
357 : }
358 0 : oe.mapmodels.push_back(id);
359 0 : oe.bbmin.min(bo).max(oe.o);
360 0 : oe.bbmax.max(br).min(ivec(oe.o).add(oe.size));
361 : }
362 0 : break;
363 : }
364 : // invisible mapmodels: lights sounds spawns etc.
365 0 : default:
366 : {
367 0 : oe.other.push_back(id);
368 0 : break;
369 : }
370 : }
371 : }
372 0 : else if(c[i].ext && c[i].ext->ents)
373 : {
374 0 : octaentities &oe = *c[i].ext->ents;
375 0 : switch(e.type)
376 : {
377 0 : case EngineEnt_Decal:
378 : {
379 0 : if(std::find(oe.decals.begin(), oe.decals.end(), id) != oe.decals.end())
380 : {
381 0 : oe.decals.erase(std::find(oe.decals.begin(), oe.decals.end(), id));
382 : }
383 0 : if(va)
384 : {
385 0 : va->bbmin.x = -1;
386 0 : if(oe.decals.empty())
387 : {
388 0 : auto itr = std::find(va->decals.begin(), va->decals.end(), &oe);
389 0 : if(itr != va->decals.end())
390 : {
391 0 : va->decals.erase(itr);
392 : }
393 : }
394 : }
395 0 : oe.bbmin = oe.bbmax = oe.o;
396 0 : oe.bbmin.add(oe.size);
397 0 : for(uint j = 0; j < oe.decals.size(); j++)
398 : {
399 0 : extentity &e = *entities::getents()[oe.decals[j]];
400 0 : ivec eo, er;
401 0 : if(getentboundingbox(e, eo, er))
402 : {
403 0 : oe.bbmin.min(eo);
404 0 : oe.bbmax.max(er);
405 : }
406 : }
407 0 : oe.bbmin.max(oe.o);
408 0 : oe.bbmax.min(ivec(oe.o).add(oe.size));
409 0 : break;
410 : }
411 0 : case EngineEnt_Mapmodel:
412 : {
413 0 : if(loadmapmodel(e.attr1))
414 : {
415 0 : auto itr = std::find(oe.mapmodels.begin(), oe.mapmodels.end(), id);
416 0 : if(itr != oe.mapmodels.end())
417 : {
418 0 : oe.mapmodels.erase(itr);
419 : }
420 0 : if(va)
421 : {
422 0 : va->bbmin.x = -1;
423 0 : if(oe.mapmodels.empty())
424 : {
425 0 : if(std::find(va->mapmodels.begin(), va->mapmodels.end(), &oe) != va->mapmodels.end())
426 : {
427 0 : va->mapmodels.erase(std::find(va->mapmodels.begin(), va->mapmodels.end(), &oe));
428 : }
429 : }
430 : }
431 0 : oe.bbmin = oe.bbmax = oe.o;
432 0 : oe.bbmin.add(oe.size);
433 0 : for(const int &j : oe.mapmodels)
434 : {
435 0 : extentity &e = *entities::getents()[j];
436 0 : ivec eo, er;
437 0 : if(getentboundingbox(e, eo, er))
438 : {
439 0 : oe.bbmin.min(eo);
440 0 : oe.bbmax.max(er);
441 : }
442 : }
443 0 : oe.bbmin.max(oe.o);
444 0 : oe.bbmax.min(ivec(oe.o).add(oe.size));
445 : }
446 0 : break;
447 : }
448 : // invisible mapmodels: light sounds spawns etc.
449 0 : default:
450 : {
451 0 : if(std::find(oe.other.begin(), oe.other.end(), id) != oe.other.end())
452 : {
453 0 : oe.other.erase(std::find(oe.other.begin(), oe.other.end(), id));
454 : }
455 0 : break;
456 : }
457 : }
458 0 : if(oe.mapmodels.empty() && oe.decals.empty() && oe.other.empty())
459 : {
460 0 : freeoctaentities(c[i]);
461 : }
462 : }
463 0 : if(c[i].ext && c[i].ext->ents)
464 : {
465 0 : c[i].ext->ents->query = nullptr;
466 : }
467 0 : if(va && va!=lastva)
468 : {
469 0 : if(lastva)
470 : {
471 0 : if(va->bbmin.x < 0)
472 : {
473 0 : lastva->bbmin.x = -1;
474 : }
475 : }
476 0 : else if(flags&ModOctaEnt_UpdateBB)
477 : {
478 0 : updatevabb(va);
479 : }
480 : }
481 : }
482 0 : }
483 :
484 0 : bool cubeworld::modifyoctaent(int flags, int id, extentity &e)
485 : {
486 0 : if(flags&ModOctaEnt_Add ? e.flags&EntFlag_Octa : !(e.flags&EntFlag_Octa))
487 : {
488 0 : return false;
489 : }
490 0 : ivec o, r;
491 0 : if(!getentboundingbox(e, o, r))
492 : {
493 0 : return false;
494 : }
495 0 : if(!insideworld(e.o))
496 : {
497 0 : uint idx = std::distance(outsideents.begin(), std::find(outsideents.begin(), outsideents.end(), id));
498 0 : if(flags&ModOctaEnt_Add)
499 : {
500 0 : if(idx < outsideents.size())
501 : {
502 0 : outsideents.push_back(id);
503 : }
504 : }
505 : else if(idx >= 0)
506 : {
507 0 : outsideents.erase(outsideents.begin() + idx);
508 : }
509 : }
510 : else
511 : {
512 0 : int leafsize = octaentsize,
513 0 : limit = std::max(r.x - o.x, std::max(r.y - o.y, r.z - o.z));
514 0 : while(leafsize < limit)
515 : {
516 0 : leafsize *= 2;
517 : }
518 0 : int diff = ~(leafsize-1) & ((o.x^r.x)|(o.y^r.y)|(o.z^r.z));
519 0 : if(diff && (limit > octaentsize/2 || diff < leafsize*2))
520 : {
521 0 : leafsize *= 2;
522 : }
523 0 : modifyoctaentity(flags, id, e, *worldroot, ivec(0, 0, 0), mapsize()>>1, o, r, leafsize);
524 : }
525 0 : e.flags ^= EntFlag_Octa;
526 0 : switch(e.type)
527 : {
528 0 : case EngineEnt_Light:
529 : {
530 0 : clearlightcache(id);
531 0 : if(e.attr5&LightEnt_Volumetric)
532 : {
533 0 : if(flags&ModOctaEnt_Add)
534 : {
535 0 : volumetriclights++;
536 : }
537 : else
538 : {
539 0 : --volumetriclights;
540 : }
541 : }
542 0 : if(e.attr5&LightEnt_NoSpecular)
543 : {
544 0 : if(!(flags&ModOctaEnt_Add ? nospeclights++ : --nospeclights))
545 : {
546 0 : cleardeferredlightshaders();
547 : }
548 : }
549 0 : break;
550 : }
551 0 : case EngineEnt_Spotlight:
552 : {
553 0 : if(!(flags&ModOctaEnt_Add ? spotlights++ : --spotlights))
554 : {
555 0 : cleardeferredlightshaders();
556 0 : cleanupvolumetric();
557 : }
558 0 : break;
559 : }
560 0 : case EngineEnt_Particles:
561 : {
562 0 : clearparticleemitters();
563 0 : break;
564 : }
565 0 : case EngineEnt_Decal:
566 : {
567 0 : if(flags&ModOctaEnt_Changed)
568 : {
569 0 : changed(o, r, false);
570 : }
571 0 : break;
572 : }
573 : }
574 0 : return true;
575 : }
576 :
577 0 : void addentityedit(int id)
578 : {
579 0 : modifyoctaent(ModOctaEnt_Add|ModOctaEnt_UpdateBB|ModOctaEnt_Changed, id);
580 0 : }
581 :
582 0 : void removeentityedit(int id)
583 : {
584 0 : modifyoctaent(ModOctaEnt_UpdateBB|ModOctaEnt_Changed, id);
585 0 : }
586 :
587 0 : void cubeworld::entitiesinoctanodes()
588 : {
589 0 : std::vector<extentity *> &ents = entities::getents();
590 0 : for(uint i = 0; i < ents.size(); i++)
591 : {
592 0 : modifyoctaent(ModOctaEnt_Add, i, *ents[i]);
593 : }
594 0 : }
595 :
596 0 : void entselectionbox(const entity &e, vec &eo, vec &es)
597 : {
598 0 : model *m = nullptr;
599 0 : const char *mname = entities::entmodel(e);
600 0 : if(mname && (m = loadmodel(mname)))
601 : {
602 0 : m->collisionbox(eo, es);
603 0 : if(es.x > es.y)
604 : {
605 0 : es.y = es.x;
606 : }
607 : else
608 : {
609 0 : es.x = es.y; // square
610 : }
611 0 : es.z = (es.z + eo.z + 1 + entselradius)/2; // enclose ent radius box and model box
612 0 : eo.x += e.o.x;
613 0 : eo.y += e.o.y;
614 0 : eo.z = e.o.z - entselradius + es.z;
615 : }
616 0 : else if(e.type == EngineEnt_Mapmodel && (m = loadmapmodel(e.attr1)))
617 : {
618 0 : mmcollisionbox(e, m, eo, es);
619 0 : es.max(entselradius);
620 0 : eo.add(e.o);
621 : }
622 0 : else if(e.type == EngineEnt_Decal)
623 : {
624 0 : DecalSlot &s = lookupdecalslot(e.attr1, false);
625 0 : decalboundbox(e, s, eo, es);
626 0 : es.max(entselradius);
627 0 : eo.add(e.o);
628 : }
629 : else
630 : {
631 0 : es = vec(entselradius);
632 0 : eo = e.o;
633 : }
634 0 : eo.sub(es);
635 0 : es.mul(2);
636 0 : }
637 :
638 : ////////////////////////////// world size/octa /////////////////////////////////
639 :
640 0 : static void splitocta(std::array<cube, 8> &c, int size)
641 : {
642 0 : if(size <= 0x1000)
643 : {
644 0 : return;
645 : }
646 0 : for(int i = 0; i < 8; ++i)
647 : {
648 0 : if(!c[i].children)
649 : {
650 0 : c[i].children = newcubes(c[i].isempty() ? faceempty : facesolid);
651 : }
652 0 : splitocta(*(c[i].children), size>>1);
653 : }
654 : }
655 :
656 0 : void cubeworld::resetmap()
657 : {
658 0 : clearoverrides();
659 0 : clearlights();
660 0 : clearslots();
661 0 : clearparticles();
662 0 : clearstains();
663 0 : clearsleep();
664 0 : cancelsel();
665 0 : pruneundos();
666 0 : clearmapcrc();
667 :
668 0 : entities::clearents();
669 0 : outsideents.clear();
670 0 : spotlights = 0;
671 0 : volumetriclights = 0;
672 0 : nospeclights = 0;
673 0 : }
674 :
675 0 : bool cubeworld::emptymap(int scale, bool force, bool usecfg) // main empty world creation routine
676 : {
677 0 : if(!force && !editmode)
678 : {
679 0 : conoutf(Console_Error, "newmap only allowed in edit mode");
680 0 : return false;
681 : }
682 0 : resetmap();
683 0 : worldscale = std::clamp(scale, 10, 16);
684 0 : setvar("emptymap", 1, true, false);
685 0 : texmru.clear();
686 0 : freeocta(worldroot);
687 0 : worldroot = newcubes(faceempty);
688 0 : for(int i = 0; i < 4; ++i)
689 : {
690 0 : setcubefaces((*worldroot)[i], facesolid);
691 : }
692 0 : if(mapsize() > 0x1000)
693 : {
694 0 : splitocta(*worldroot, mapsize()>>1);
695 : }
696 0 : clearmainmenu();
697 0 : if(usecfg)
698 : {
699 0 : identflags |= Idf_Overridden;
700 0 : execfile("config/default_map_settings.cfg", false);
701 0 : identflags &= ~Idf_Overridden;
702 : }
703 0 : allchanged(true);
704 0 : return true;
705 : }
706 :
707 : /* enlargemap: grows the map to an additional gridsize
708 : * bool force forces the growth even if not in edit mode
709 : *
710 : * expands the map by placing the old map in the corner nearest the origin and
711 : * adding 7 old-map sized cubes to create a new largest cube
712 : *
713 : * this moves the worldroot cube to the new parent cube of the old map
714 : */
715 0 : bool cubeworld::enlargemap(bool force)
716 : {
717 0 : worldscale++;
718 0 : std::array<cube, 8> *c = newcubes(faceempty);
719 0 : (*c)[0].children = worldroot;
720 0 : for(int i = 0; i < 3; ++i)
721 : {
722 0 : setcubefaces((*c)[i+1], facesolid);
723 : }
724 0 : worldroot = c;
725 :
726 0 : if(mapsize() > 0x1000)
727 : {
728 0 : splitocta(*worldroot, mapsize()>>1);
729 : }
730 0 : allchanged();
731 0 : return true;
732 : }
733 :
734 : /* isallempty: checks whether the cube, and all of its children, are empty
735 : *
736 : * returns true if cube is empty and has no children, OR
737 : * returns true if cube has children cubes and none of their children (recursively)
738 : * have children that are not empty
739 : *
740 : * returns false if any cube or child cube is not empty (has geometry)
741 : *
742 : */
743 0 : static bool isallempty(const cube &c)
744 : {
745 0 : if(!c.children)
746 : {
747 0 : return c.isempty();
748 : }
749 0 : for(int i = 0; i < 8; ++i)
750 : {
751 0 : if(!isallempty((*c.children)[i]))
752 : {
753 0 : return false;
754 : }
755 : }
756 0 : return true;
757 : }
758 :
759 : /* shrinkmap: attempts to reduce the mapsize by 1 (halves all linear dimensions)
760 : *
761 : * fails if the 7 octants not at the origin are not empty
762 : * on success, the new map will have its maximum gridsize reduced by 1
763 : */
764 0 : void cubeworld::shrinkmap()
765 : {
766 0 : if(noedit(true) || (nompedit && multiplayer))
767 : {
768 0 : multiplayerwarn();
769 0 : return;
770 : }
771 0 : if(mapsize() <= 1<<10) //do not allow maps smaller than 2^10 cubits
772 : {
773 0 : return;
774 : }
775 0 : int octant = -1;
776 0 : for(int i = 0; i < 8; ++i)
777 : {
778 0 : if(!isallempty((*worldroot)[i]))
779 : {
780 0 : if(octant >= 0)
781 : {
782 0 : return;
783 : }
784 0 : octant = i;
785 : }
786 : }
787 0 : if(octant < 0)
788 : {
789 0 : return;
790 : }
791 0 : if(!(*worldroot)[octant].children)
792 : {
793 0 : subdividecube((*worldroot)[octant], false, false);
794 : }
795 0 : std::array<cube, 8> *&root = (*worldroot)[octant].children; //change worldroot to cube 0
796 0 : (*worldroot)[octant].children = nullptr; //free the old largest cube
797 0 : freeocta(worldroot);
798 0 : worldroot = root;
799 0 : worldscale--;
800 0 : ivec offset(octant, ivec(0, 0, 0), mapsize());
801 0 : std::vector<extentity *> &ents = entities::getents();
802 0 : for(extentity * const i : ents)
803 : {
804 0 : i->o.sub(vec(offset));
805 : }
806 0 : allchanged();
807 0 : conoutf("shrunk map to size %d", worldscale);
808 : }
809 :
810 96 : int cubeworld::mapsize() const
811 : {
812 96 : return 1<<worldscale;
813 : }
814 :
815 0 : int cubeworld::mapscale() const
816 : {
817 0 : return worldscale;
818 : }
|