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: 2025-08-24 05:32:49 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<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 &center, 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 &center, 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 &center, 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 &center, 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 &center, 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              : }
        

Generated by: LCOV version 2.0-1