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