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); }