LCOV - code coverage report
Current view: top level - engine/world - world.cpp (source / functions) Coverage Total Hit
Test: Libprimis Test Coverage Lines: 1.2 % 411 5
Test Date: 2026-03-24 05:50:27 Functions: 6.1 % 33 2

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

Generated by: LCOV version 2.0-1