LCOV - code coverage report
Current view: top level - engine/world - world.cpp (source / functions) Hit Total Coverage
Test: Libprimis Test Coverage Lines: 5 409 1.2 %
Date: 2024-11-22 05:07:59 Functions: 2 33 6.1 %

          Line data    Source code
       1             : // world.cpp: core map management stuff
       2             : 
       3             : #include "../libprimis-headers/cube.h"
       4             : #include "../../shared/geomexts.h"
       5             : #include "../../shared/glexts.h"
       6             : 
       7             : #include <memory>
       8             : #include <optional>
       9             : 
      10             : #include "bih.h"
      11             : #include "entities.h"
      12             : #include "light.h"
      13             : #include "octaedit.h"
      14             : #include "octaworld.h"
      15             : #include "raycube.h"
      16             : #include "world.h"
      17             : 
      18             : #include "interface/console.h"
      19             : #include "interface/cs.h"
      20             : #include "interface/menus.h"
      21             : 
      22             : #include "model/model.h"
      23             : 
      24             : #include "render/octarender.h"
      25             : #include "render/renderlights.h"
      26             : #include "render/rendermodel.h"
      27             : #include "render/renderparticles.h"
      28             : #include "render/shaderparam.h"
      29             : #include "render/stain.h"
      30             : #include "render/texture.h"
      31             : 
      32             : SVARR(maptitle, "Untitled Map by Unknown");
      33             : 
      34             : std::vector<int> outsideents;
      35             : std::vector<int> entgroup;
      36             : 
      37             : namespace entities
      38             : {
      39             :     std::vector<extentity *> ents;
      40             : 
      41           0 :     std::vector<extentity *> &getents()
      42             :     {
      43           0 :         return ents;
      44             :     }
      45             : 
      46           0 :     const char *entmodel(const entity &e)
      47             :     {
      48           0 :         return nullptr;
      49             :     }
      50             : 
      51           0 :     extentity *newentity()
      52             :     {
      53           0 :         return new extentity();
      54             :     }
      55             : 
      56           0 :     void deleteentity(extentity *e)
      57             :     {
      58           0 :         delete e;
      59           0 :     }
      60             : 
      61           0 :     void clearents()
      62             :     {
      63           0 :         while(ents.size())
      64             :         {
      65           0 :             deleteentity(ents.back());
      66           0 :             ents.pop_back();
      67             :         }
      68           0 :     }
      69             : 
      70             : }
      71             : //octaents are the ones that modify the level directly: other entities like
      72             : //sounds, lights, spawns etc. don't get directly rendered
      73             : VAR(entselradius, 0, 2, 10);
      74             : VAR(octaentsize, 0, 64, 1024);
      75             : 
      76           2 : void entcancel()
      77             : {
      78           2 :     entgroup.clear();
      79           2 : }
      80             : 
      81             : //need a getter fxn because decalslot obj not exposed to the game
      82           0 : float getdecalslotdepth(const DecalSlot &s)
      83             : {
      84           0 :     return s.depth;
      85             : }
      86             : 
      87           0 : void detachentity(extentity &e)
      88             : {
      89           0 :     if(!e.attached)
      90             :     {
      91           0 :         return;
      92             :     }
      93           0 :     e.attached->attached = nullptr;
      94           0 :     e.attached = nullptr;
      95             : }
      96             : 
      97             : VAR(attachradius, 1, 100, 1000);
      98             : 
      99           0 : void attachentity(extentity &e)
     100             : {
     101             :     //don't attempt to attach invalid to attach ents
     102           0 :     switch(e.type)
     103             :     {
     104           0 :         case EngineEnt_Spotlight:
     105             :         {
     106           0 :             break;
     107             :         }
     108           0 :         default:
     109             :         {
     110           0 :             return;
     111             :         }
     112             :     }
     113           0 :     detachentity(e);
     114             : 
     115           0 :     std::vector<extentity *> &ents = entities::getents();
     116           0 :     int closest = -1;
     117           0 :     float closedist = 1e10f; //some arbitrary high value
     118           0 :     for(uint i = 0; i < ents.size(); i++)
     119             :     {
     120           0 :         extentity *a = ents[i];
     121           0 :         if(a->attached)
     122             :         {
     123           0 :             continue;
     124             :         }
     125           0 :         switch(e.type)
     126             :         {
     127           0 :             case EngineEnt_Spotlight:
     128             :             {
     129             :                 //only attempt to attach to lights
     130           0 :                 if(a->type!=EngineEnt_Light)
     131             :                 {
     132           0 :                     continue;
     133             :                 }
     134           0 :                 break;
     135             :             }
     136           0 :             default:
     137             :             {
     138           0 :                 continue;
     139             :             }
     140             :         }
     141           0 :         float dist = e.o.dist(a->o);
     142           0 :         if(dist < closedist)
     143             :         {
     144           0 :             closest = i;
     145           0 :             closedist = dist;
     146             :         }
     147             :     }
     148           0 :     if(closedist>attachradius)
     149             :     {
     150           0 :         return;
     151             :     }
     152           0 :     e.attached = ents[closest];
     153           0 :     ents[closest]->attached = &e;
     154             : }
     155             : 
     156             : //used in iengine
     157           0 : void attachentities()
     158             : {
     159           0 :     for(extentity *& i : entities::getents())
     160             :     {
     161           0 :         attachentity(*i);
     162             :     }
     163           0 : }
     164             : 
     165             : enum ModOctaEnt
     166             : {
     167             :     ModOctaEnt_Add      = 1<<0,
     168             :     ModOctaEnt_UpdateBB = 1<<1,
     169             :     ModOctaEnt_Changed  = 1<<2
     170             : };
     171             : 
     172           0 : static void rotatebb(vec &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 :         uint idx = std::distance(outsideents.begin(), std::find(outsideents.begin(), outsideents.end(), id));
     498           0 :         if(flags&ModOctaEnt_Add)
     499             :         {
     500           0 :             if(idx < outsideents.size())
     501             :             {
     502           0 :                 outsideents.push_back(id);
     503             :             }
     504             :         }
     505             :         else if(idx >= 0)
     506             :         {
     507           0 :             outsideents.erase(outsideents.begin() + idx);
     508             :         }
     509             :     }
     510             :     else
     511             :     {
     512           0 :         int leafsize = octaentsize,
     513           0 :             limit    = std::max(r.x - o.x, std::max(r.y - o.y, r.z - o.z));
     514           0 :         while(leafsize < limit)
     515             :         {
     516           0 :             leafsize *= 2;
     517             :         }
     518           0 :         int diff = ~(leafsize-1) & ((o.x^r.x)|(o.y^r.y)|(o.z^r.z));
     519           0 :         if(diff && (limit > octaentsize/2 || diff < leafsize*2))
     520             :         {
     521           0 :             leafsize *= 2;
     522             :         }
     523           0 :         modifyoctaentity(flags, id, e, *worldroot, ivec(0, 0, 0), mapsize()>>1, o, r, leafsize);
     524             :     }
     525           0 :     e.flags ^= EntFlag_Octa;
     526           0 :     switch(e.type)
     527             :     {
     528           0 :         case EngineEnt_Light:
     529             :         {
     530           0 :             clearlightcache(id);
     531           0 :             if(e.attr5&LightEnt_Volumetric)
     532             :             {
     533           0 :                 if(flags&ModOctaEnt_Add)
     534             :                 {
     535           0 :                     volumetriclights++;
     536             :                 }
     537             :                 else
     538             :                 {
     539           0 :                     --volumetriclights;
     540             :                 }
     541             :             }
     542           0 :             if(e.attr5&LightEnt_NoSpecular)
     543             :             {
     544           0 :                 if(!(flags&ModOctaEnt_Add ? nospeclights++ : --nospeclights))
     545             :                 {
     546           0 :                     cleardeferredlightshaders();
     547             :                 }
     548             :             }
     549           0 :             break;
     550             :         }
     551           0 :         case EngineEnt_Spotlight:
     552             :         {
     553           0 :             if(!(flags&ModOctaEnt_Add ? spotlights++ : --spotlights))
     554             :             {
     555           0 :                 cleardeferredlightshaders();
     556           0 :                 cleanupvolumetric();
     557             :             }
     558           0 :             break;
     559             :         }
     560           0 :         case EngineEnt_Particles:
     561             :         {
     562           0 :             clearparticleemitters();
     563           0 :             break;
     564             :         }
     565           0 :         case EngineEnt_Decal:
     566             :         {
     567           0 :             if(flags&ModOctaEnt_Changed)
     568             :             {
     569           0 :                 changed(o, r, false);
     570             :             }
     571           0 :             break;
     572             :         }
     573             :     }
     574           0 :     return true;
     575             : }
     576             : 
     577           0 : void addentityedit(int id)
     578             : {
     579           0 :     modifyoctaent(ModOctaEnt_Add|ModOctaEnt_UpdateBB|ModOctaEnt_Changed, id);
     580           0 : }
     581             : 
     582           0 : void removeentityedit(int id)
     583             : {
     584           0 :     modifyoctaent(ModOctaEnt_UpdateBB|ModOctaEnt_Changed, id);
     585           0 : }
     586             : 
     587           0 : void cubeworld::entitiesinoctanodes()
     588             : {
     589           0 :     std::vector<extentity *> &ents = entities::getents();
     590           0 :     for(uint i = 0; i < ents.size(); i++)
     591             :     {
     592           0 :         modifyoctaent(ModOctaEnt_Add, i, *ents[i]);
     593             :     }
     594           0 : }
     595             : 
     596           0 : void entselectionbox(const entity &e, vec &eo, vec &es)
     597             : {
     598           0 :     model *m = nullptr;
     599           0 :     const char *mname = entities::entmodel(e);
     600           0 :     if(mname && (m = loadmodel(mname)))
     601             :     {
     602           0 :         m->collisionbox(eo, es);
     603           0 :         if(es.x > es.y)
     604             :         {
     605           0 :             es.y = es.x;
     606             :         }
     607             :         else
     608             :         {
     609           0 :             es.x = es.y; // square
     610             :         }
     611           0 :         es.z = (es.z + eo.z + 1 + entselradius)/2; // enclose ent radius box and model box
     612           0 :         eo.x += e.o.x;
     613           0 :         eo.y += e.o.y;
     614           0 :         eo.z = e.o.z - entselradius + es.z;
     615             :     }
     616           0 :     else if(e.type == EngineEnt_Mapmodel && (m = loadmapmodel(e.attr1)))
     617             :     {
     618           0 :         mmcollisionbox(e, m, eo, es);
     619           0 :         es.max(entselradius);
     620           0 :         eo.add(e.o);
     621             :     }
     622           0 :     else if(e.type == EngineEnt_Decal)
     623             :     {
     624           0 :         DecalSlot &s = lookupdecalslot(e.attr1, false);
     625           0 :         decalboundbox(e, s, eo, es);
     626           0 :         es.max(entselradius);
     627           0 :         eo.add(e.o);
     628             :     }
     629             :     else
     630             :     {
     631           0 :         es = vec(entselradius);
     632           0 :         eo = e.o;
     633             :     }
     634           0 :     eo.sub(es);
     635           0 :     es.mul(2);
     636           0 : }
     637             : 
     638             : ////////////////////////////// world size/octa /////////////////////////////////
     639             : 
     640           0 : static void splitocta(std::array<cube, 8> &c, int size)
     641             : {
     642           0 :     if(size <= 0x1000)
     643             :     {
     644           0 :         return;
     645             :     }
     646           0 :     for(int i = 0; i < 8; ++i)
     647             :     {
     648           0 :         if(!c[i].children)
     649             :         {
     650           0 :             c[i].children = newcubes(c[i].isempty() ? faceempty : facesolid);
     651             :         }
     652           0 :         splitocta(*(c[i].children), size>>1);
     653             :     }
     654             : }
     655             : 
     656           0 : void cubeworld::resetmap()
     657             : {
     658           0 :     clearoverrides();
     659           0 :     clearlights();
     660           0 :     clearslots();
     661           0 :     clearparticles();
     662           0 :     clearstains();
     663           0 :     clearsleep();
     664           0 :     cancelsel();
     665           0 :     pruneundos();
     666           0 :     clearmapcrc();
     667             : 
     668           0 :     entities::clearents();
     669           0 :     outsideents.clear();
     670           0 :     spotlights = 0;
     671           0 :     volumetriclights = 0;
     672           0 :     nospeclights = 0;
     673           0 : }
     674             : 
     675           0 : bool cubeworld::emptymap(int scale, bool force, bool usecfg)    // main empty world creation routine
     676             : {
     677           0 :     if(!force && !editmode)
     678             :     {
     679           0 :         conoutf(Console_Error, "newmap only allowed in edit mode");
     680           0 :         return false;
     681             :     }
     682           0 :     resetmap();
     683           0 :     worldscale = std::clamp(scale, 10, 16);
     684           0 :     setvar("emptymap", 1, true, false);
     685           0 :     texmru.clear();
     686           0 :     freeocta(worldroot);
     687           0 :     worldroot = newcubes(faceempty);
     688           0 :     for(int i = 0; i < 4; ++i)
     689             :     {
     690           0 :         setcubefaces((*worldroot)[i], facesolid);
     691             :     }
     692           0 :     if(mapsize() > 0x1000)
     693             :     {
     694           0 :         splitocta(*worldroot, mapsize()>>1);
     695             :     }
     696           0 :     clearmainmenu();
     697           0 :     if(usecfg)
     698             :     {
     699           0 :         identflags |= Idf_Overridden;
     700           0 :         execfile("config/default_map_settings.cfg", false);
     701           0 :         identflags &= ~Idf_Overridden;
     702             :     }
     703           0 :     allchanged(true);
     704           0 :     return true;
     705             : }
     706             : 
     707             : /* enlargemap: grows the map to an additional gridsize
     708             :  * bool force forces the growth even if not in edit mode
     709             :  *
     710             :  * expands the map by placing the old map in the corner nearest the origin and
     711             :  * adding 7 old-map sized cubes to create a new largest cube
     712             :  *
     713             :  * this moves the worldroot cube to the new parent cube of the old map
     714             :  */
     715           0 : bool cubeworld::enlargemap(bool force)
     716             : {
     717           0 :     worldscale++;
     718           0 :     std::array<cube, 8> *c = newcubes(faceempty);
     719           0 :     (*c)[0].children = worldroot;
     720           0 :     for(int i = 0; i < 3; ++i)
     721             :     {
     722           0 :         setcubefaces((*c)[i+1], facesolid);
     723             :     }
     724           0 :     worldroot = c;
     725             : 
     726           0 :     if(mapsize() > 0x1000)
     727             :     {
     728           0 :         splitocta(*worldroot, mapsize()>>1);
     729             :     }
     730           0 :     allchanged();
     731           0 :     return true;
     732             : }
     733             : 
     734             : /* isallempty: checks whether the cube, and all of its children, are empty
     735             :  *
     736             :  * returns true if cube is empty and has no children, OR
     737             :  * returns true if cube has children cubes and none of their children (recursively)
     738             :  * have children that are not empty
     739             :  *
     740             :  * returns false if any cube or child cube is not empty (has geometry)
     741             :  *
     742             :  */
     743           0 : static bool isallempty(const cube &c)
     744             : {
     745           0 :     if(!c.children)
     746             :     {
     747           0 :         return c.isempty();
     748             :     }
     749           0 :     for(int i = 0; i < 8; ++i)
     750             :     {
     751           0 :         if(!isallempty((*c.children)[i]))
     752             :         {
     753           0 :             return false;
     754             :         }
     755             :     }
     756           0 :     return true;
     757             : }
     758             : 
     759             : /* shrinkmap: attempts to reduce the mapsize by 1 (halves all linear dimensions)
     760             :  *
     761             :  * fails if the 7 octants not at the origin are not empty
     762             :  * on success, the new map will have its maximum gridsize reduced by 1
     763             :  */
     764           0 : void cubeworld::shrinkmap()
     765             : {
     766           0 :     if(noedit(true) || (nompedit && multiplayer))
     767             :     {
     768           0 :         multiplayerwarn();
     769           0 :         return;
     770             :     }
     771           0 :     if(mapsize() <= 1<<10) //do not allow maps smaller than 2^10 cubits
     772             :     {
     773           0 :         return;
     774             :     }
     775           0 :     int octant = -1;
     776           0 :     for(int i = 0; i < 8; ++i)
     777             :     {
     778           0 :         if(!isallempty((*worldroot)[i]))
     779             :         {
     780           0 :             if(octant >= 0)
     781             :             {
     782           0 :                 return;
     783             :             }
     784           0 :             octant = i;
     785             :         }
     786             :     }
     787           0 :     if(octant < 0)
     788             :     {
     789           0 :         return;
     790             :     }
     791           0 :     if(!(*worldroot)[octant].children)
     792             :     {
     793           0 :         subdividecube((*worldroot)[octant], false, false);
     794             :     }
     795           0 :     std::array<cube, 8> *&root = (*worldroot)[octant].children; //change worldroot to cube 0
     796           0 :     (*worldroot)[octant].children = nullptr; //free the old largest cube
     797           0 :     freeocta(worldroot);
     798           0 :     worldroot = root;
     799           0 :     worldscale--;
     800           0 :     ivec offset(octant, ivec(0, 0, 0), mapsize());
     801           0 :     std::vector<extentity *> &ents = entities::getents();
     802           0 :     for(extentity * const i : ents)
     803             :     {
     804           0 :         i->o.sub(vec(offset));
     805             :     }
     806           0 :     allchanged();
     807           0 :     conoutf("shrunk map to size %d", worldscale);
     808             : }
     809             : 
     810          96 : int cubeworld::mapsize() const
     811             : {
     812          96 :     return 1<<worldscale;
     813             : }
     814             : 
     815           0 : int cubeworld::mapscale() const
     816             : {
     817           0 :     return worldscale;
     818             : }

Generated by: LCOV version 1.14