From e318ecd68e5af9b66d6b45feb79dda38a9f1cd59 Mon Sep 17 00:00:00 2001 From: ac-stef Date: Mon, 27 Apr 2015 15:18:24 +0200 Subject: [PATCH] integrated automatic embedded map config files This patch enables an option to keep the map config embedded in the map file - with autogenerated content. Any changes to map config data will be written with the map. This includes data by loadnotexture, loadsky, mapmodel, texture, fog, fogcolour, mapsound and shadowyaw. Once enabled, all changes with those commands become persistent to the map. New variables: mapsoundchanged Set to "1" with every command that changes mapsound slots. Should be used to trigger a rebuild of mapsound menus. New/changed commands: mapsound Now returns the number of the created slot. mapmodel Now returns the number of the created slot. texture Now returns the number of the created slot. loadsky Is now a builtin string variable (instead of a command), which means, that the current value can easily be retrieved or edited. The skymap name in the argument is now required to start with "textures/skymaps/" - but that part of the path can be omitted. examples: loadsky steini/steini2 (loads textures/skymaps/steini/steini2_*) loadsky textures/skymaps/steini/steini2 (same) loadsky ../foo/bar (loads textures/foo/bar_*) echo $loadsky getnotexture Returns the current "notexture" path. (Set by loadnotexture) dumpmapconfig (available for debug binaries only) Writes the current automatically generated map config to dumpmapconfig.txt. DOCREF mapsoundchanged mapsound mapmodel texture loadsky getnotexture --- .gitignore | 2 +- config/menus_edit.cfg | 2 +- source/src/audiomanager.cpp | 10 ++++ source/src/cube.h | 5 ++ source/src/protos.h | 6 ++- source/src/rendergl.cpp | 5 +- source/src/rendermodel.cpp | 20 +++++--- source/src/texture.cpp | 65 +++++++++++++++++++------ source/src/vertmodel.h | 2 +- source/src/world.cpp | 2 + source/src/world.h | 18 ++++++- source/src/worldio.cpp | 94 +++++++++++++++++++++++++++++++++---- 12 files changed, 192 insertions(+), 39 deletions(-) diff --git a/.gitignore b/.gitignore index a8f82d93..fecf9b28 100644 --- a/.gitignore +++ b/.gitignore @@ -26,4 +26,4 @@ source/src/ac_server source/src/cube.h.gch source/src/Makefile_local clientlog*.txt - +dumpmapconfig.txt diff --git a/config/menus_edit.cfg b/config/menus_edit.cfg index cad1c035..0201ea7d 100644 --- a/config/menus_edit.cfg +++ b/config/menus_edit.cfg @@ -271,7 +271,7 @@ tempalias __rebuildmapmodelmenus [ looplist $__mapmodelcategories [key menu] [delmenu $menu] looplist (getmapmodelattributes explodekeywords sortby: keywords sortby: desc usage distribution) [path keyword desc usage dist] [ path = (concatword "mapmodels/" $path) - idxs = (mapmodelbyname $path) + idxs = (mapmodelslotbyname $path) if (|| (! (strlen $keyword)) (< (findlist $__mapmodelcategories $keyword) 0)) [ keyword = unsorted if (strcmp $dist "official") [ keyword = unsoo ] // unless someone writes the missing config files ;) diff --git a/source/src/audiomanager.cpp b/source/src/audiomanager.cpp index e3860f4f..6ff5a6ee 100644 --- a/source/src/audiomanager.cpp +++ b/source/src/audiomanager.cpp @@ -790,9 +790,14 @@ COMMANDF(soundmuted, "i", (int *n) intret(audiomgr.soundmuted(*n)); }); +VAR(mapsoundchanged, 0, 0, 1); + COMMANDF(mapsoundreset, "", () { audiomgr.mapsoundreset(); + mapconfigdata.mapsoundlines.shrink(0); + mapsoundchanged = 1; + flagmapconfigchange(); }); VARF(soundchannels, 4, 32, 1024, audiomgr.setchannels(soundchannels); ); @@ -807,6 +812,11 @@ COMMANDF(registersound, "siii", (char *name, int *vol, int *loop, int *audiblera COMMANDF(mapsound, "si", (char *name, int *maxuses) { audiomgr.addsound(name, 255, *maxuses, true, mapsounds, false, 0); + intret(mapconfigdata.mapsoundlines.length()); + copystring(mapconfigdata.mapsoundlines.add().name, name); + mapconfigdata.mapsoundlines.last().maxuses = *maxuses; + mapsoundchanged = 1; + flagmapconfigchange(); }); COMMANDF(registermusic, "s", (char *name) diff --git a/source/src/cube.h b/source/src/cube.h index cd20b747..80d86d24 100644 --- a/source/src/cube.h +++ b/source/src/cube.h @@ -32,6 +32,7 @@ extern sqr *world, *wmip[]; // map data, the mips are sequential 2D arrays in memory extern header hdr; // current map header +extern _mapconfigdata mapconfigdata; // current mapconfig extern int sfactor, ssize; // ssize = 2^sfactor extern int cubicsize, mipsize; // cubicsize = ssize^2 extern physent *camera1; // camera representing perspective of player, usually player1 @@ -68,6 +69,10 @@ extern int verbose; #define MAXCL 16 #define CONFIGROTATEMAX 5 // keep 5 old versions of saved.cfg and init.cfg around +#define DEFAULT_FOG 180 +#define DEFAULT_FOGCOLOUR 0x8099B3 +#define DEFAULT_SHADOWYAW 45 + #include "protos.h" // external function decls #endif diff --git a/source/src/protos.h b/source/src/protos.h index b1919d12..d61a3490 100644 --- a/source/src/protos.h +++ b/source/src/protos.h @@ -301,12 +301,13 @@ extern SDL_Surface *forcergbsurface(SDL_Surface *os); extern SDL_Surface *forcergbasurface(SDL_Surface *os); extern Texture *textureload(const char *name, int clamp = 0, bool mipmap = true, bool canreduce = false, float scale = 1.0f, bool trydl = false); extern Texture *lookuptexture(int tex, Texture *failtex = notexture, bool trydl = false); +extern const char *gettextureslot(int i); extern bool reloadtexture(Texture &t); extern bool reloadtexture(const char *name); extern void reloadtextures(); Texture *createtexturefromsurface(const char *name, SDL_Surface *s); extern void blitsurface(SDL_Surface *dst, SDL_Surface *src, int x, int y); -void loadsky(char *basename, bool reload); +void loadskymap(bool reload); static inline Texture *lookupworldtexture(int tex, bool trydl = true) { return lookuptexture(tex, noworldtexture, trydl); } @@ -687,6 +688,8 @@ extern uchar *readmcfggz(char *name, int *size, int *sizegz); extern void rlencodecubes(vector &f, sqr *s, int len, bool preservesolids); extern void rldecodecubes(ucharbuf &f, sqr *s, int len, int version, bool silent); extern void clearheaderextras(); +extern void flagmapconfigchange(); +extern void getcurrentmapconfig(vector &f, bool onlysounds); extern void xmapbackup(const char *nickprefix, const char *nick); extern void writeallxmaps(); extern int loadallxmaps(); @@ -745,6 +748,7 @@ 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 writemapmodelattributes(); +extern const char *mmshortname(const char *name); // weapon extern void shoot(playerent *d, vec &to); diff --git a/source/src/rendergl.cpp b/source/src/rendergl.cpp index dd8b1c32..9fe1ed8a 100644 --- a/source/src/rendergl.cpp +++ b/source/src/rendergl.cpp @@ -468,8 +468,9 @@ float dynfov() else return (float)fov; } -VAR(fog, 64, 180, 1024); -VAR(fogcolour, 0, 0x8099B3, 0xFFFFFF); +VARF(fog, 64, DEFAULT_FOG, 1024, flagmapconfigchange()); +VARF(fogcolour, 0, DEFAULT_FOGCOLOUR, 0xFFFFFF, flagmapconfigchange()); + float fovy, aspect; int farplane; diff --git a/source/src/rendermodel.cpp b/source/src/rendermodel.cpp index bfe79b7e..25e4f6f0 100644 --- a/source/src/rendermodel.cpp +++ b/source/src/rendermodel.cpp @@ -107,9 +107,11 @@ VAR(mapmodelchanged, 0, 0, 1); 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 *snap, char *name) { + intret(mapmodels.length()); mapmodelinfo &mmi = mapmodels.add(); mmi.rad = *rad; mmi.h = *h; @@ -117,21 +119,25 @@ void mapmodel(int *rad, int *h, int *zoff, char *snap, char *name) mmi.m = NULL; formatstring(mmi.name)("%s%s", mmpath, name); mapmodelchanged = 1; + flagmapconfigchange(); } +COMMAND(mapmodel, "iiiss"); void mapmodelreset() { - if(execcontext==IEXC_MAPCFG) mapmodels.shrink(0); - mapmodelchanged = 1; + if(execcontext==IEXC_MAPCFG) + { + mapmodels.shrink(0); + mapmodelchanged = 1; + flagmapconfigchange(); + } } +COMMAND(mapmodelreset, ""); mapmodelinfo &getmminfo(int i) { return mapmodels.inrange(i) ? mapmodels[i] : *(mapmodelinfo *)0; } -COMMAND(mapmodel, "iiiss"); -COMMAND(mapmodelreset, ""); - -COMMANDF(mapmodelname, "i", (int *idx) { result(mapmodels.inrange(*idx) ? mapmodels[*idx].name : ""); }); -COMMANDF(mapmodelbyname, "s", (char *name) +COMMANDF(mapmodelslotname, "i", (int *idx) { result(mapmodels.inrange(*idx) ? mmshortname(mapmodels[*idx].name) : ""); }); // returns the model name that is configured in a certain slot +COMMANDF(mapmodelslotbyname, "s", (char *name) // returns the slot(s) that a certain mapmodel is configured in { string res = ""; loopv(mapmodels) if(!strcmp(name, mapmodels[i].name)) concatformatstring(res, "%s%d", *res ? " " : "", i); diff --git a/source/src/texture.cpp b/source/src/texture.cpp index 69379304..f342b8c6 100644 --- a/source/src/texture.cpp +++ b/source/src/texture.cpp @@ -481,28 +481,49 @@ Texture *createtexturefromsurface(const char *name, SDL_Surface *s) struct Slot { string name; - float scale; + float scale, orgscale; Texture *tex; bool loaded; }; vector slots; -void texturereset() { if(execcontext==IEXC_MAPCFG) slots.setsize(0); } +COMMANDF(texturereset, "", () +{ + if(execcontext==IEXC_MAPCFG) + { + slots.setsize(0); + flagmapconfigchange(); + } +}); void texture(float *scale, char *name) { + intret(slots.length()); Slot &s = slots.add(); copystring(s.name, name); - path(s.name); s.tex = NULL; s.loaded = false; + s.orgscale = *scale; s.scale = (*scale > 0 && *scale <= 2.0f) ? *scale : 1.0f; + flagmapconfigchange(); } -COMMAND(texturereset, ""); COMMAND(texture, "fs"); +const char *gettextureslot(int i) +{ + static string res; + if(slots.inrange(i)) + { + Slot &s = slots[i]; + defformatstring(d)("%d", int(s.orgscale)); + formatstring(res)("texture %s \"%s\"", s.orgscale == int(s.orgscale) ? d : floatstr(s.orgscale), s.name); + return res; + } + else return NULL; +} + Texture *lookuptexture(int tex, Texture *failtex, bool trydl) { Texture *t = failtex; @@ -511,13 +532,13 @@ Texture *lookuptexture(int tex, Texture *failtex, bool trydl) Slot &s = slots[tex]; if(!s.loaded) { - defformatstring(pname)("packages/textures/%s", s.name); + defformatstring(pname)("packages/textures/%s", path(s.name, true)); s.tex = textureload(pname, 0, true, true, s.scale, trydl); if(!trydl) { - if(s.tex==notexture) s.tex = failtex; - s.loaded = true; - } + if(s.tex==notexture) s.tex = failtex; + s.loaded = true; + } } if(s.tex) t = s.tex; } @@ -555,38 +576,52 @@ void reloadtextures() } Texture *sky[6] = {NULL, NULL, NULL, NULL, NULL, NULL}; -static string skybox; -void loadsky(char *basename, bool reload) +SVARF(loadsky, "", { loadskymap(false); }); + +void loadskymap(bool reload) { const char *side[] = { "lf", "rt", "ft", "bk", "dn", "up" }; - if(reload) basename = skybox; - else copystring(skybox, basename); + const char *legacyprefix = "textures/skymaps/"; + if(!reload) + { + int n = strlen(legacyprefix); + if(!strncmp(loadsky, legacyprefix, n)) + { // remove the prefix + char *t = loadsky; + loadsky = newstring(loadsky + n); + delstring(t); + } + flagmapconfigchange(); + } loopi(6) { - defformatstring(name)("packages/%s_%s.jpg", basename, side[i]); + defformatstring(name)("packages/%s%s_%s.jpg", legacyprefix, loadsky, side[i]); sky[i] = textureload(name, 3); if(sky[i] == notexture && !reload && autodownload) { - defformatstring(dl)("packages/%s", basename); + defformatstring(dl)("packages/%s%s", legacyprefix, loadsky); requirepackage(PCK_SKYBOX, dl); break; } } } -COMMANDF(loadsky, "s", (char *name) { loadsky(name, false); intret(0); }); void loadnotexture(char *c) { noworldtexture = notexture; // reset to default + *mapconfigdata.notexturename = '\0'; if(c[0]) { defformatstring(p)("packages/textures/%s", c); noworldtexture = textureload(p); if(noworldtexture==notexture) conoutf("could not load alternative texture '%s'.", p); } + flagmapconfigchange(); } + COMMAND(loadnotexture, "s"); +COMMANDF(getnotexture, "", () { result(mapconfigdata.notexturename); }); void draw_envbox_face(float s0, float t0, float x0, float y0, float z0, float s1, float t1, float x1, float y1, float z1, diff --git a/source/src/vertmodel.h b/source/src/vertmodel.h index 6465da1a..b705c0ec 100644 --- a/source/src/vertmodel.h +++ b/source/src/vertmodel.h @@ -4,7 +4,7 @@ VARP(saveshadows, 0, 1, 1); VARP(dynshadowquad, 0, 0, 1); -VAR(shadowyaw, 0, 45, 360); +VARF(shadowyaw, 0, DEFAULT_SHADOWYAW, 360, flagmapconfigchange()); vec shadowdir(0, 0, -1), shadowpos(0, 0, 0); const int dbgstenc = 0; diff --git a/source/src/world.cpp b/source/src/world.cpp index 9e334663..d4a22a28 100644 --- a/source/src/world.cpp +++ b/source/src/world.cpp @@ -8,6 +8,7 @@ sqr *world = NULL; int sfactor, ssize, cubicsize, mipsize; header hdr; +_mapconfigdata mapconfigdata; // main geometric mipmapping routine, recursively rebuild mipmaps within block b. // tries to produce cube out of 4 lower level mips as well as possible, @@ -529,6 +530,7 @@ bool empty_world(int factor, bool force) // main empty world creation routine { // all-new map int oldunsavededits = unsavededits; memset(&hdr, 0, sizeof(header)); + mapconfigdata.clear(); formatstring(hdr.maptitle)("Untitled Map by %s", player1->name); setvar("waterlevel", (hdr.waterlevel = -100000)); setwatercolor(); diff --git a/source/src/world.h b/source/src/world.h index bce742e7..cbdf3628 100644 --- a/source/src/world.h +++ b/source/src/world.h @@ -40,6 +40,8 @@ enum // stuff encoded in sqr.tag }; #define TAGANYCLIP (TAGCLIP|TAGPLCLIP) +enum { MHF_AUTOMAPCONFIG = 1<<0 }; + #define MAXMAPVERSION 10 // defines the highest readable format #define MAPVERSION 9 // default map format version to be written (bump if map format changes, see worldio.cpp) @@ -56,10 +58,24 @@ struct header // map file format header uchar watercolor[4]; int maprevision; int ambient; - int reserved[12]; + int flags; // MHF_* + int reserved[11]; //char mediareq[128]; // version 7 and 8 only. }; +struct mapsoundline { string name; int maxuses; }; + +struct _mapconfigdata +{ + string notexturename; + vector mapsoundlines; + void clear() + { + *notexturename = '\0'; + mapsoundlines.shrink(0); + } +}; + struct mapstats { struct header hdr; diff --git a/source/src/worldio.cpp b/source/src/worldio.cpp index 1eedbebf..6b005422 100644 --- a/source/src/worldio.cpp +++ b/source/src/worldio.cpp @@ -388,12 +388,66 @@ void extractconfigfile() delete f; conoutf("extracted embedded map config to file %s", mcfname); deleteheaderextra(n); + hdr.flags &= ~MHF_AUTOMAPCONFIG; // external config file, manually edited } else conoutf("\f3failed to write config to %s", mcfname); } } COMMAND(extractconfigfile, ""); +void flagmapconfigchange() +{ + if(hdr.flags & MHF_AUTOMAPCONFIG) unsavededits++; +} + +void getcurrentmapconfig(vector &f, bool onlysounds) +{ + if(!onlysounds) + { + extern int fog, fogcolour, shadowyaw; + extern char *loadsky; + if(fog != DEFAULT_FOG) cvecprintf(f, "fog %d\n", fog); + if(fogcolour != DEFAULT_FOGCOLOUR) cvecprintf(f, "fogcolour 0x%06x\n", fogcolour); + if(shadowyaw != DEFAULT_SHADOWYAW) cvecprintf(f, "shadowyaw %d\n", shadowyaw); + if(*mapconfigdata.notexturename) cvecprintf(f, "loadnotexture \"%s\"\n", mapconfigdata.notexturename); + if(*loadsky) cvecprintf(f, "loadsky \"%s\"\n", loadsky); + cvecprintf(f, "%smapmodelreset\n", f.length() ? "\n" : ""); + loopi(256) + { + mapmodelinfo &mmi = getmminfo(i); + if(!&mmi) break; + cvecprintf(f, "mapmodel %d %d %d 0 \"%s\"\n", mmi.rad, mmi.h, mmi.zoff, mmshortname(mmi.name)); + } + cvecprintf(f, "\ntexturereset\n"); + loopi(256) + { + const char *t = gettextureslot(i); + if(!t) break; + cvecprintf(f, "%s\n", t); + } + cvecprintf(f, "\n"); + } + cvecprintf(f, "mapsoundreset\n"); + loopv(mapconfigdata.mapsoundlines) + { + mapsoundline &s = mapconfigdata.mapsoundlines[i]; + cvecprintf(f, "mapsound \"%s\" %d\n", s.name, s.maxuses); + } + cvecprintf(f, "\n"); +} + +#ifdef _DEBUG +COMMANDF(dumpmapconfig, "", () +{ + vector buf; + getcurrentmapconfig(buf, false); + stream *f = openfile("dumpmapconfig.txt", "w"); + if(f) f->write(buf.getbuf(), buf.length()); + DELETEP(f); + hdr.flags |= MHF_AUTOMAPCONFIG; +}); +#endif + bool clearvantagepoint() { bool yep = false; @@ -463,6 +517,17 @@ void save_world(char *mname, bool skipoptimise, bool addcomfort) if(mapbackupsonsave) backup(cgzname, bakname); stream *f = opengzfile(cgzname, "wb"); if(!f) { conoutf("could not write map to %s", cgzname); return; } + bool autoembedconfig = (hdr.flags & MHF_AUTOMAPCONFIG) != 0; + if(autoembedconfig) + { // write embedded map config + backup(mcfname, cbakname); // rename possibly existing external map config file + for(int n; (n = findheaderextra(HX_CONFIG)) >= 0; ) deleteheaderextra(n); // delete existing embedded config + vector ec; + getcurrentmapconfig(ec, false); + uchar *ecu = new uchar[ec.length()]; + memcpy(ecu, ec.getbuf(), ec.length()); + headerextras.add(new headerextra(ec.length(), HX_CONFIG|HX_FLAG_PERSIST, NULL))->data = ecu; + } strncpy(hdr.head, "ACMP", 4); // ensure map now declares itself as an AssaultCube map, even if imported as CUBE hdr.version = MAPVERSION; hdr.headersize = sizeof(header); @@ -481,9 +546,9 @@ void save_world(char *mname, bool skipoptimise, bool addcomfort) bool oldmapmodelscaling = tmp.version < 10; tmp.maprevision += advancemaprevision; DEBUG("version " << tmp.version << " headersize " << tmp.headersize << " entities " << tmp.numents << " factor " << tmp.sfactor << " revision " << tmp.maprevision); - lilswap(&tmp.version, 4); + lilswap(&tmp.version, 4); // version, headersize, sfactor, numents lilswap(&tmp.waterlevel, 1); - lilswap(&tmp.maprevision, 2); + lilswap(&tmp.maprevision, 3); // maprevision, ambient, flags f->write(&tmp, sizeof(header)); if(writeextra) f->write(hx.buf, writeextra); delete[] hx.buf; @@ -505,7 +570,7 @@ void save_world(char *mname, bool skipoptimise, bool addcomfort) f->write(rawcubes.getbuf(), rawcubes.length()); delete f; unsavededits = 0; - conoutf("wrote map file %s %s%s", cgzname, skipoptimise ? "without optimisation" : "(optimised)", addcomfort ? " (including editing history)" : ""); + conoutf("wrote map file %s %s%s%s", cgzname, skipoptimise ? "without optimisation" : "(optimised)", addcomfort ? " (including editing history)" : "", autoembedconfig ? " (including map config)" : ""); } VARP(preserveundosonsave, 0, 0, 1); COMMANDF(savemap, "s", (char *name) { save_world(name, preserveundosonsave && editmode, preserveundosonsave && editmode); } ); @@ -600,6 +665,7 @@ bool load_world(char *mname) // still supports all map formats that have f->seek(tmp.headersize, SEEK_SET); } hdr = tmp; + mapconfigdata.clear(); rebuildtexlists(); loadingscreen("%s", hdr.maptitle); resetmap(); @@ -607,7 +673,7 @@ bool load_world(char *mname) // still supports all map formats that have { lilswap(&hdr.waterlevel, 1); if(!hdr.watercolor[3]) setwatercolor(); - lilswap(&hdr.maprevision, 2); + lilswap(&hdr.maprevision, 3); // maprevision, ambient, flags curmaprevision = hdr.maprevision; } else @@ -703,10 +769,18 @@ bool load_world(char *mname) // still supports all map formats that have pushscontext(IEXC_MAPCFG); // untrusted altogether per_idents = false; neverpersist = true; - execfile("config/default_map_settings.cfg"); - execfile(pcfname); - execfile(mcfname); - copystring(lastloadedconfigfile, mcfname); + if(hdr.flags & MHF_AUTOMAPCONFIG) + { // full featured embedded config: no need to read any other map config files + copystring(lastloadedconfigfile, ""); + unsavededits = 0; + } + else + { // map config from files + execfile("config/default_map_settings.cfg"); + execfile(pcfname); + execfile(mcfname); + copystring(lastloadedconfigfile, mcfname); + } parseheaderextra(); neverpersist = false; per_idents = true; @@ -738,7 +812,7 @@ bool load_world(char *mname) // still supports all map formats that have c2skeepalive(); - loadsky(NULL, true); + loadskymap(true); watch.start(); loopi(256) if(texuse[i]) lookupworldtexture(i, false); @@ -870,7 +944,7 @@ struct xmap neverpersist = false; per_idents = true; popscontext(); - loadsky(NULL, true); + loadskymap(true); startmap(name, false, true); // "start" but don't respawn: basically just set clientmap restoreposition(position); }