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

Generated by: LCOV version 2.0-1