diff --git a/config/menus_edit.cfg b/config/menus_edit.cfg index 992d6fc5..09494a40 100644 --- a/config/menus_edit.cfg +++ b/config/menus_edit.cfg @@ -344,7 +344,7 @@ menuinit [ push __distribution (getmapmodelattributes (mapmodelslotname $__mmslot) distribution) if (! (strlen $__distribution)) [ __distribution = "custom/unsorted" ] menuitem (concat "Distribution:" (pop __distribution)) -1 - looplist [defaults "Defaults:" usage "Usage:" license "License:" version "Version:"] [attr hd] [ // also possible: desc "Desc:" author "Author:" + looplist [defaults "Defaults:" usage "Usage:" license "License:" version "Version:" requires "Requires:"] [attr hd] [ // also possible: desc "Desc:" author "Author:" attr = (getmapmodelattributes (mapmodelslotname $__mmslot) $attr) if (strlen $attr) [ menuitem (concat $hd $attr) -1 @@ -365,6 +365,9 @@ menuinit [ looplist (mapmodelslotusage $__mmslot) ii [ menuitem (concatword "slot used by map entity #" $ii) (concat gotoentity $ii) (concat gotoentity $ii) ] + if (strcmp " " (mapmodelslotusage $__mmslot)) [ + menuitem "(this model is required by another model)" -1 + ] menuitem "" -1 (concat gotoposition (gotoposition)) ][ menuitem "" -1 diff --git a/source/src/model.h b/source/src/model.h index a5e4c13d..9e3c8a77 100644 --- a/source/src/model.h +++ b/source/src/model.h @@ -67,9 +67,10 @@ struct model virtual void endrender() {} }; -struct mapmodelinfo { int rad, h, zoff; float scale; string name; model *m; }; +struct mapmodelinfo { int rad, h, zoff; float scale; string name; model *m; int flags; }; -enum { MMA_KEYWORDS = 0, MMA_DESC, MMA_DEFAULTPARAMS, MMA_USAGE, MMA_AUTHOR, MMA_LICENSE, MMA_DISTRIBUTION, MMA_VERSION, MMA_NUM }; +enum { MMA_KEYWORDS = 0, MMA_DESC, MMA_DEFAULTPARAMS, MMA_USAGE, MMA_AUTHOR, MMA_LICENSE, MMA_DISTRIBUTION, MMA_VERSION, MMA_REQUIRES, MMA_NUM }; +enum { MMF_REQUIRED = 1<<0, MMF_TEMP_USED = 1<<30, MMF_CONFIGMASK = 0xff }; struct mapmodelattributes { diff --git a/source/src/protos.h b/source/src/protos.h index df003804..15f133c4 100644 --- a/source/src/protos.h +++ b/source/src/protos.h @@ -898,6 +898,7 @@ extern void renderclients(); extern void renderclient(playerent *d); extern void renderclient(playerent *d, const char *mdlname, const char *vwepname, int tex = 0); extern void updateclientname(playerent *d); +extern void updatemapmodeldependencies(); extern void writemapmodelattributes(); extern const char *mmshortname(const char *name); diff --git a/source/src/rendermodel.cpp b/source/src/rendermodel.cpp index a441fd7c..771b0c1b 100644 --- a/source/src/rendermodel.cpp +++ b/source/src/rendermodel.cpp @@ -87,7 +87,7 @@ void mdlcachelimit(int *limit) COMMAND(mdlcachelimit, "i"); -const char *mdlattrnames[] = { "keywords", "desc", "defaults", "usage", "author", "license", "distribution", "version", "" }; +const char *mdlattrnames[] = { "keywords", "desc", "defaults", "usage", "author", "license", "distribution", "version", "requires", "" }; void mdlattribute(char *attrname, char *val) { @@ -109,7 +109,7 @@ vector mapmodels; const char *mmpath = "mapmodels/"; const char *mmshortname(const char *name) { return !strncmp(name, mmpath, strlen(mmpath)) ? name + strlen(mmpath) : name; } -void mapmodel(int *rad, int *h, int *zoff, char *scale, char *name) +void mapmodel(int *rad, int *h, int *zoff, char *scale, char *name, char *flags) { if(*scale && *name) // ignore "mapmodel" commands with insufficient parameters { @@ -120,6 +120,8 @@ void mapmodel(int *rad, int *h, int *zoff, char *scale, char *name) mmi.h = *h; mmi.zoff = *zoff; mmi.scale = atof(scale); + mmi.flags = 0; + if(*flags) mmi.flags = ATOI(flags); if(mmi.scale < 0.25f || mmi.scale > 4.0f) { mmi.scale = 1.0f; @@ -134,7 +136,7 @@ void mapmodel(int *rad, int *h, int *zoff, char *scale, char *name) } else flagmapconfigerror(LWW_CONFIGERR * 2); } -COMMAND(mapmodel, "iiiss"); +COMMAND(mapmodel, "iiisss"); void mapmodelreset() { @@ -160,7 +162,7 @@ void mapmodelslotusage(int *n) // returns all entity indices that contain a mapm { string res = ""; loopv(ents) if(ents[i].type == MAPMODEL && ents[i].attr2 == *n) concatformatstring(res, "%s%d", i ? " " : "", i); - result(res); + result(*res ? res : (mapmodels.inrange(*n) && (mapmodels[*n].flags & MMF_REQUIRED) ? " " : res)); } COMMAND(mapmodelslotusage, "i"); @@ -433,6 +435,42 @@ void getmapmodelattributes(char *name, char *attr) } COMMAND(getmapmodelattributes, "ss"); +void updatemapmodeldependencies() +{ + loopv(mapmodels) mapmodels[i].flags = 0; + loopv(ents) if(ents[i].type == MAPMODEL && mapmodels.inrange(ents[i].attr2)) mapmodels[ents[i].attr2].flags |= MMF_TEMP_USED; + bool foundone = true; + while(foundone) + { + foundone = false; + loopvk(mapmodels) if(mapmodels[k].flags & MMF_TEMP_USED) + { + const char *name = mmshortname(mapmodels[k].name); + mapmodelattributes **ap = mdlregistry.access(name), *a = ap ? *ap : NULL; + const char *dep = a ? a->n[MMA_REQUIRES] : NULL; + if(dep && *dep) + { + bool found = false; + loopv(mapmodels) if(!strcmp(mmshortname(mapmodels[i].name), dep)) + { + if(!(mapmodels[i].flags & MMF_TEMP_USED)) foundone = true; + mapmodels[i].flags |= MMF_REQUIRED | MMF_TEMP_USED; + found = true; + } + if(!found) + { + defformatstring(cmd)("mapmodel 0 0 0 0 \"%s\" %d", dep, MMF_REQUIRED); + execute(cmd); + foundone = true; + clientlogf(" mapmodel %s added to config, because %s requires it", dep, name); + } + } + } + } + loopv(mapmodels) mapmodels[i].flags &= MMF_CONFIGMASK; +} +COMMAND(updatemapmodeldependencies, ""); + static int mmasortorder = 0; int mmasort(mapmodelattributes **a, mapmodelattributes **b) { return strcmp((*a)->name, (*b)->name); } int mmasort2(mapmodelattributes **a, mapmodelattributes **b) @@ -915,13 +953,15 @@ void preload_entmodels() bool preload_mapmodels(bool trydl) { int missing = 0; + loopv(mapmodels) mapmodels[i].flags &= ~MMF_TEMP_USED; loopv(ents) { entity &e = ents[i]; if(e.type!=MAPMODEL || !mapmodels.inrange(e.attr2)) continue; - if(!loadmodel(NULL, e.attr2, trydl)) missing++; + mapmodels[ents[i].attr2].flags |= MMF_TEMP_USED; if(e.attr4 && lookuptexture(e.attr4, notexture, trydl) == notexture) missing++; } + loopv(mapmodels) if((mapmodels[i].flags & (MMF_REQUIRED | MMF_TEMP_USED)) && !loadmodel(NULL, i, trydl)) missing++; return !missing; } diff --git a/source/src/worldio.cpp b/source/src/worldio.cpp index 30b50b71..b40f56d5 100644 --- a/source/src/worldio.cpp +++ b/source/src/worldio.cpp @@ -438,7 +438,9 @@ void getcurrentmapconfig(vector &f, bool onlysounds) { mapmodelinfo *mmi = getmminfo(i); if(!mmi) break; - cvecprintf(f, "mapmodel %d %d %d %s \"%s\"\n", mmi->rad, mmi->h, mmi->zoff, mmi->scale == 1.0f ? "0" : floatstr(mmi->scale, true), mmshortname(mmi->name)); + cvecprintf(f, "mapmodel %d %d %d %s \"%s\"", mmi->rad, mmi->h, mmi->zoff, mmi->scale == 1.0f ? "0" : floatstr(mmi->scale, true), mmshortname(mmi->name)); + if(mmi->flags & MMF_CONFIGMASK) cvecprintf(f, " %d", mmi->flags & MMF_CONFIGMASK); + f.add('\n'); } cvecprintf(f, "\ntexturereset\n"); loopi(256) @@ -463,6 +465,7 @@ void getcurrentmapconfig(vector &f, bool onlysounds) COMMANDF(dumpmapconfig, "", () { vector buf; + updatemapmodeldependencies(); getcurrentmapconfig(buf, false); stream *f = openfile("dumpmapconfig.txt", "w"); if(f) f->write(buf.getbuf(), buf.length() - 1); @@ -596,6 +599,7 @@ void save_world(char *mname, bool skipoptimise, bool addcomfort) if(!f) { conoutf("could not write map to %s", cgzname); return; } // update embedded config file (if used) + updatemapmodeldependencies(); bool autoembedconfig = (hdr.flags & MHF_AUTOMAPCONFIG) != 0; if(autoembedconfig) { // write embedded map config