From 71a12bfe82e57ead31e47b55e043cd65111e585e Mon Sep 17 00:00:00 2001 From: ac-stef Date: Sun, 16 Oct 2016 21:54:22 +0200 Subject: [PATCH] allow custom mapmodels to depend on other custom mapmodels By adding a "requires"-attribute to the mapmodel config file, a mapmodel can trigger the map editor to include another model and have it loaded during map load. This can be used to create model modifications which use ressources of another model. For example, a new model can provide a different skin for an existing model - and that attribute will make sure, that the required original model is also loaded. This mechanism does not work, if the attribute is changed after the map is finished, because the map editor has to copy the dependency into the map config file. After adding the attribute, the map has to be written at least once (the dependencies are checked during savemap). The point of dependency-attributes is to make sure the custom original model is also loaded from a package server. Mods of official models do not need to use the attribute, because the official model does not need to be downloaded. The attribute is necessary, if one model on akimbo depends on another model on akimbo. The mapmodels that are required by other models have an additional parameter "1" added to their mapmodel command in the config file - which will preload the model during map load, even if no entity actually uses it. The additional parameter is handled automatically. The new command updatemapmodeldependencies triggers the dependency check manually - which will make required models appear in the model list. This is done automatically during savemap. Changed commands: mapmodelslotusage now returns a single space, if the model slot is not used by any map entity, but is required by another model (with entities) mapmodel allows one more parameter - if "1", the model is preloaded, even if no entities use it mdlattribute requires if used, specifies that the current model depends on parts of another custom model (which therefore also has to be downloaded) can only be used once per model config example: mdlattribute requires makke/signs/exit could be used in signs/loading-dock/md2.cfg to reuse makke/signs/exit/tris.md2 DOCREF mdlattribute mapmodel mapmodelslotusage --- config/menus_edit.cfg | 5 +++- source/src/model.h | 5 ++-- source/src/protos.h | 1 + source/src/rendermodel.cpp | 50 ++++++++++++++++++++++++++++++++++---- source/src/worldio.cpp | 6 ++++- 5 files changed, 58 insertions(+), 9 deletions(-) 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