From 07311a0279e7637ce3566fefeb69e9375435ea61 Mon Sep 17 00:00:00 2001 From: div0 Date: Thu, 15 Nov 2007 21:17:59 +0000 Subject: [PATCH] mapinfo system should work now; user friendly additions come later git-svn-id: svn://svn.icculus.org/nexuiz/trunk@2961 f962a42d-fe04-0410-a3ab-8c8b0445ebaa --- data/qcsrc/common/campaign_setup.qc | 13 +++ data/qcsrc/common/mapinfo.qc | 142 ++++++++++++++++++++------- data/qcsrc/common/mapinfo.qh | 5 +- data/qcsrc/menu-div0test/mbuiltin.qh | 1 + data/qcsrc/menu-div0test/menu.qh | 4 + data/qcsrc/server/campaign.qc | 4 + data/qcsrc/server/g_world.qc | 40 +++++++- 7 files changed, 170 insertions(+), 39 deletions(-) diff --git a/data/qcsrc/common/campaign_setup.qc b/data/qcsrc/common/campaign_setup.qc index e6ec38ac8..8a52a93be 100644 --- a/data/qcsrc/common/campaign_setup.qc +++ b/data/qcsrc/common/campaign_setup.qc @@ -11,9 +11,22 @@ void CampaignSetup(float n) localcmd("\n"); localcmd(campaign_mutators[n]); localcmd("\n"); +#ifdef MAPINFO + cvar_set("g_tdm", (campaign_gametype[n] == "tdm") ? "0" : "1"); + cvar_set("g_domination", (campaign_gametype[n] == "dom") ? "0" : "1"); + cvar_set("g_ctf", (campaign_gametype[n] == "ctf") ? "0" : "1"); + cvar_set("g_runematch", (campaign_gametype[n] == "rune") ? "0" : "1"); + cvar_set("g_lms", (campaign_gametype[n] == "lms") ? "0" : "1"); + cvar_set("g_arena", (campaign_gametype[n] == "arena") ? "0" : "1"); + cvar_set("g_keyhunt", (campaign_gametype[n] == "kh") ? "0" : "1"); + cvar_set("g_assault", (campaign_gametype[n] == "as") ? "0" : "1"); + cvar_set("g_onslaught", (campaign_gametype[n] == "ons") ? "0" : "1"); + MapInfo_LoadMap(campaign_mapname[n]); +#else localcmd("exec maps/"); // can't use strcat here in current fteqcc localcmd(campaign_gametype[n]); localcmd("_"); localcmd(campaign_mapname[n]); localcmd(".mapcfg\n"); +#endif } diff --git a/data/qcsrc/common/mapinfo.qc b/data/qcsrc/common/mapinfo.qc index 1dbab5d87..7f186a843 100644 --- a/data/qcsrc/common/mapinfo.qc +++ b/data/qcsrc/common/mapinfo.qc @@ -34,6 +34,22 @@ string extractRestOfLine(string haystack, string needle) return substring(haystack, strlen(needle), strlen(haystack) - strlen(needle)); return string_null; } +string car(string s) +{ + float o; + o = strstrofs(s, " ", 0); + if(o < 0) + return s; + return substring(s, 0, o); +} +string cdr(string s) +{ + float o; + o = strstrofs(s, " ", 0); + if(o < 0) + return string_null; + return substring(s, o + 1, strlen(s) - (o + 1)); +} // GLOB HANDLING (for all BSP files) float _MapInfo_globopen; @@ -59,7 +75,7 @@ void MapInfo_Enumerate() string _MapInfo_filtered; string MapInfo_FilterGametype_Recursive(float pGametype, float pFeatures, float pBegin, float pEnd) { - float m; + float m, valid; string l, r; if(pBegin == pEnd) @@ -70,13 +86,14 @@ string MapInfo_FilterGametype_Recursive(float pGametype, float pFeatures, float l = MapInfo_FilterGametype_Recursive(pGametype, pFeatures, pBegin, m); if not(l) return string_null; // BAIL OUT - if(MapInfo_Get_ByName(_MapInfo_GlobItem(m), 1) == 2) // if we generated one... BAIL OUT and let the caller continue in the next frame. + if(MapInfo_Get_ByName(_MapInfo_GlobItem(m), 1, 0) == 2) // if we generated one... BAIL OUT and let the caller continue in the next frame. return string_null; // BAIL OUT + valid = ((MapInfo_Map_supportedGametypes & pGametype) != 0) && ((MapInfo_Map_supportedFeatures & pFeatures) == pFeatures); r = MapInfo_FilterGametype_Recursive(pGametype, pFeatures, m + 1, pEnd); if not(r) return string_null; // BAIL OUT - if(((MapInfo_Map_supportedGametypes & pGametype) != 0) && ((MapInfo_Map_supportedFeatures & pFeatures) == pFeatures)) + if(valid) return HugeSetOfIntegers_insert(l, m, r); else return HugeSetOfIntegers_concat(l, r); @@ -93,7 +110,7 @@ float MapInfo_FilterGametype(float pGametype, float pFeatures) } _MapInfo_filtered = strzone(_MapInfo_filtered); MapInfo_count = HugeSetOfIntegers_length(_MapInfo_filtered); - dprint("Filter ", ftos(pGametype), " results in ", _MapInfo_filtered, "\n"); + dprint("Filter ", ftos(pGametype), "/", ftos(pFeatures), " results in ", _MapInfo_filtered, "\n"); // TODO clear cache return 1; } @@ -102,7 +119,7 @@ float MapInfo_FilterGametype(float pGametype, float pFeatures) float MapInfo_Get_ByID(float i) { // TODO check cache - if(MapInfo_Get_ByName(_MapInfo_GlobItem(HugeSetOfIntegers_get(_MapInfo_filtered, i)), 0)) + if(MapInfo_Get_ByName(_MapInfo_GlobItem(HugeSetOfIntegers_get(_MapInfo_filtered, i)), 0, 0)) { // TODO save in cache return 1; @@ -136,6 +153,7 @@ float _MapInfo_Generate(string pFilename) // 0: failure, 1: ok ent, 2: ok bsp print("Analyzing ", fn, " to generate initial mapinfo; please edit that file later\n"); inWorldspawn = 2; + MapInfo_Map_supportedGametypes = 0; for(;;) { @@ -228,28 +246,59 @@ float _MapInfo_Generate(string pFilename) // 0: failure, 1: ok ent, 2: ok bsp return r; } -// load info about a map by name into the MapInfo_Map_* globals -float MapInfo_Get_ByName(string pFilename, float pAllowGenerate) +void _MapInfo_Map_Reset() { - string fn; - string s, t; - float fh; - float r; - - // default all generic fields so they have "good" values in case something fails MapInfo_Map_title = "Untitled1"; MapInfo_Map_description = "Bleh."; MapInfo_Map_supportedGametypes = 0; MapInfo_Map_supportedFeatures = 0; MapInfo_Map_diameter = 0; MapInfo_Map_spawnpoints = 0; +} + +void _MapInfo_Map_ApplyGametype(string s, float pWantedType, float pThisType) +{ + MapInfo_Map_supportedGametypes |= pThisType; + if(!(pThisType & pWantedType)) + return; + + cvar_set("fraglimit", car(s)); + s = cdr(s); + cvar_set("timelimit", car(s)); + s = cdr(s); + + if(pWantedType == MAPINFO_TYPE_TEAM_DEATHMATCH) + { + cvar_set("g_tdm_teams", car(s)); + s = cdr(s); + } + + if(pWantedType == MAPINFO_TYPE_KEYHUNT) + { + cvar_set("g_keyhunt_teams", car(s)); + s = cdr(s); + } +} + +// load info about a map by name into the MapInfo_Map_* globals +float MapInfo_Get_ByName(string pFilename, float pAllowGenerate, float pGametypeToSet) +{ + string fn; + string s, t; + float fh; + float r; + + r = 1; + + // default all generic fields so they have "good" values in case something fails fn = strcat("maps/", pFilename, ".mapinfo"); fh = fopen(fn, FILE_READ); if(fh < 0) { if(!pAllowGenerate) return 0; + _MapInfo_Map_Reset(); r = _MapInfo_Generate(pFilename); if(!r) return 0; @@ -260,8 +309,8 @@ float MapInfo_Get_ByName(string pFilename, float pAllowGenerate) fputs(fh, strcat("_spawnpoints ", ftos(MapInfo_Map_spawnpoints), "\n")); if(MapInfo_Map_supportedFeatures & MAPINFO_FEATURE_WEAPONS) fputs(fh, "has weapons\n"); if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_DEATHMATCH) fputs(fh, "type dm 30 20\n"); - if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_TEAM_DEATHMATCH) fputs(fh, "type tdm 50 20 2\n"); // TODO count tdm_team entities - if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_DOMINATION) fputs(fh, "type dom 200 20 2\n"); // TODO count tdm_team entities + if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_TEAM_DEATHMATCH) fputs(fh, "type tdm 50 20 2\n"); + if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_DOMINATION) fputs(fh, "type dom 200 20\n"); if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_CTF) fputs(fh, "type ctf 300 20\n"); if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_RUNEMATCH) fputs(fh, "type rune 200 20\n"); if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_LMS) fputs(fh, "type lms 9 20\n"); @@ -270,50 +319,58 @@ float MapInfo_Get_ByName(string pFilename, float pAllowGenerate) if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_ASSAULT) fputs(fh, "type as 20\n"); if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_ONSLAUGHT) fputs(fh, "type ons 20\n"); fclose(fh); + r = 2; // return r; - return 2; + fh = fopen(fn, FILE_READ); + if(fh < 0) + error("... but I just wrote it!"); } + + _MapInfo_Map_Reset(); for(;;) { if not((s = fgets(fh))) break; - tokenize(s); - t = argv(0); - if(t == "title") - MapInfo_Map_title = substring(s, 6, strlen(s) - 6); // without "title" + t = car(s); s = cdr(s); + if (t == "title") + MapInfo_Map_title = t; else if(t == "description") MapInfo_Map_description = substring(s, 12, strlen(s) - 12); else if(t == "_diameter") - MapInfo_Map_diameter = stof(argv(1)); + MapInfo_Map_diameter = stof(s); else if(t == "_spawnpoints") - MapInfo_Map_spawnpoints = stof(argv(1)); + MapInfo_Map_spawnpoints = stof(s); else if(t == "has") { - t = argv(1); + t = car(s); s = cdr(s); if (t == "weapons") MapInfo_Map_supportedFeatures |= MAPINFO_FEATURE_WEAPONS; else dprint("Map ", pFilename, " supports unknown feature ", t, ", ignored\n"); } else if(t == "type") { - t = argv(1); - if (t == "dm") MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_DEATHMATCH; - else if(t == "tdm") MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_TEAM_DEATHMATCH; - else if(t == "dom") MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_DOMINATION; - else if(t == "ctf") MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_CTF; - else if(t == "rune") MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_RUNEMATCH; - else if(t == "lms") MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_LMS; - else if(t == "arena") MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_ARENA; - else if(t == "kh") MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_KEYHUNT; - else if(t == "as") MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_ASSAULT; - else if(t == "ons") MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_ONSLAUGHT; + t = car(s); s = cdr(s); + if (t == "dm") _MapInfo_Map_ApplyGametype (s, pGametypeToSet, MAPINFO_TYPE_DEATHMATCH); + else if(t == "tdm") _MapInfo_Map_ApplyGametype (s, pGametypeToSet, MAPINFO_TYPE_TEAM_DEATHMATCH); + else if(t == "dom") _MapInfo_Map_ApplyGametype (s, pGametypeToSet, MAPINFO_TYPE_DOMINATION); + else if(t == "ctf") _MapInfo_Map_ApplyGametype (s, pGametypeToSet, MAPINFO_TYPE_CTF); + else if(t == "rune") _MapInfo_Map_ApplyGametype (s, pGametypeToSet, MAPINFO_TYPE_RUNEMATCH); + else if(t == "lms") _MapInfo_Map_ApplyGametype (s, pGametypeToSet, MAPINFO_TYPE_LMS); + else if(t == "arena") _MapInfo_Map_ApplyGametype (s, pGametypeToSet, MAPINFO_TYPE_ARENA); + else if(t == "kh") _MapInfo_Map_ApplyGametype (s, pGametypeToSet, MAPINFO_TYPE_KEYHUNT); + else if(t == "as") _MapInfo_Map_ApplyGametype (s, pGametypeToSet, MAPINFO_TYPE_ASSAULT); + else if(t == "ons") _MapInfo_Map_ApplyGametype (s, pGametypeToSet, MAPINFO_TYPE_ONSLAUGHT); else dprint("Map ", pFilename, " supports unknown game type ", t, ", ignored\n"); } else - dprint("Map ", pFilename, " supports unknown game type ", t, ", ignored\n"); + dprint("Map ", pFilename, " provides unknown info item ", t, ", ignored\n"); } fclose(fh); + print(pFilename, " -> ", ftos(MapInfo_Map_supportedGametypes), ", ", ftos(MapInfo_Map_supportedFeatures), "\n"); + if(pGametypeToSet) + if(!(MapInfo_Map_supportedGametypes & pGametypeToSet)) + error("Can't select the requested game type. Bailing out."); if(MapInfo_Map_supportedGametypes != 0) return 1; dprint("Map ", pFilename, " supports no game types, ignored\n"); @@ -391,7 +448,7 @@ float MapInfo_CurrentGametype() float MapInfo_CheckMap(string s) // returns 0 if the map can't be played with the current settings, 1 otherwise { - if(!MapInfo_Get_ByName(s, 1)) + if(!MapInfo_Get_ByName(s, 1, 0)) return 0; if((MapInfo_Map_supportedGametypes & MapInfo_CurrentGametype()) == 0) return 0; @@ -415,5 +472,18 @@ void MapInfo_LoadMap(string s) cvar_set("g_keyhunt", "0"); cvar_set("g_onslaught", "0"); } + MapInfo_Get_ByName(s, 1, MapInfo_CurrentGametype()); localcmd(strcat("\nchangelevel ", s, "\n")); } + +string MapInfo_ListAllowedMaps() +{ + string out; + float i; + MapInfo_Enumerate(); + MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures()); + out = ""; + for(i = 0; i < MapInfo_count; ++i) + out = strcat(out, " ", _MapInfo_GlobItem(HugeSetOfIntegers_get(_MapInfo_filtered, i))); + return substring(out, 1, strlen(out) - 1); +} diff --git a/data/qcsrc/common/mapinfo.qh b/data/qcsrc/common/mapinfo.qh index 2152e496d..81fe25345 100644 --- a/data/qcsrc/common/mapinfo.qh +++ b/data/qcsrc/common/mapinfo.qh @@ -35,7 +35,7 @@ float MapInfo_CurrentGametype(); // retrieves current gametype from cvars float MapInfo_Get_ByID(float i); // 1 on success, 0 on failure // load info about a map by name into the MapInfo_Map_* globals -float MapInfo_Get_ByName(string s, float allowGenerate); // 1 on success, 0 on failure, 2 if it autogenerated a mapinfo file +float MapInfo_Get_ByName(string s, float allowGenerate, float gametypeToSet); // 1 on success, 0 on failure, 2 if it autogenerated a mapinfo file // look for a map by a prefix, returns the actual map name on success, string_null on failure or ambigous match string MapInfo_FixName(string s); @@ -43,3 +43,6 @@ string MapInfo_FixName(string s); // play a map float MapInfo_CheckMap(string s); // returns 0 if the map can't be played with the current settings void MapInfo_LoadMap(string s); + +// list all maps for the current game type +string MapInfo_ListAllowedMaps(); diff --git a/data/qcsrc/menu-div0test/mbuiltin.qh b/data/qcsrc/menu-div0test/mbuiltin.qh index 58529e558..6b5c5e8ab 100644 --- a/data/qcsrc/menu-div0test/mbuiltin.qh +++ b/data/qcsrc/menu-div0test/mbuiltin.qh @@ -97,6 +97,7 @@ float strlen(string s) = #52; string strcat(string s1, ...) = #53; string substring(string s, float start, float length) = #54; +float(string str, string sub, float startpos) strstrofs = #221; float(string s1, string s2, float len) strncmp = #228; float(string s1, string s2) strcasecmp = #229; float(string s1, string s2, float len) strncasecmp = #230; diff --git a/data/qcsrc/menu-div0test/menu.qh b/data/qcsrc/menu-div0test/menu.qh index ded20f105..923996ef1 100644 --- a/data/qcsrc/menu-div0test/menu.qh +++ b/data/qcsrc/menu-div0test/menu.qh @@ -2,6 +2,10 @@ #define NULL (null_entity) +#ifndef MAPINFO +#error Sorry, need to compile with -DMAPINFO enabled (server QC too, by the way) +#endif + // constants const string string_null; diff --git a/data/qcsrc/server/campaign.qc b/data/qcsrc/server/campaign.qc index 8787eef92..81a0aa4ca 100644 --- a/data/qcsrc/server/campaign.qc +++ b/data/qcsrc/server/campaign.qc @@ -49,8 +49,12 @@ void() CampaignPostInit = // now some sanity checks string thismapname, wantedmapname; thismapname = GetMapname(); +#ifdef MAPINFO + wantedmapname = campaign_mapname[0]; +#else wantedmapname = campaign_gametype[0]; wantedmapname = strcat(wantedmapname, "_", campaign_mapname[0]); +#endif if(wantedmapname != thismapname) return CampaignBailout(strcat("wrong map: ", wantedmapname, " != ", thismapname)); cvar_set("fraglimit", ftos(campaign_fraglimit[0])); diff --git a/data/qcsrc/server/g_world.qc b/data/qcsrc/server/g_world.qc index b52d6ea04..1ce97c638 100644 --- a/data/qcsrc/server/g_world.qc +++ b/data/qcsrc/server/g_world.qc @@ -395,10 +395,11 @@ string GetGametype() float IsSameGametype(string mapcfgname) { - string gt; - gt = GetGametype(); #ifdef MAPINFO + return TRUE; // can't change game type by map name here #else + string gt; + gt = GetGametype(); if(substring(mapcfgname, 0, strlen(gt) + 1) == strcat(gt, "_")) return TRUE; return FALSE; @@ -409,6 +410,7 @@ string getmapname_stored; string GetMapname() { #ifdef MAPINFO + return mapname; #else if(getmapname_stored == "") getmapname_stored = strzone(strcat(GetGametype(), "_", mapname)); @@ -519,6 +521,7 @@ float(float position, float pass) Map_Check = return 0; } #ifdef MAPINFO + if(MapInfo_CheckMap(argv(position))) #else filename = Map_Filename(position); if(TryFile(filename)) @@ -554,6 +557,12 @@ void(float position) Map_Goto_SetFloat = void() GameResetCfg = { +#ifdef MAPINFO + // settings persist, except... + if(cvar("g_campaign")) + localcmd("\nexec mutator_reset.cfg\n"); + localcmd("\nsettemp_restore\n"); +#else // if an exit cfg is defined by exiting map, exec it. string exit_cfg; exit_cfg = cvar_string("exit_cfg"); @@ -561,6 +570,7 @@ void() GameResetCfg = localcmd(strcat("exec \"", exit_cfg, "\"\n")); localcmd("exec game_reset.cfg\n"); +#endif Ban_SaveBans(); }; @@ -570,6 +580,7 @@ void() Map_Goto = Map_MarkAsRecent(getmapname_stored); GameResetCfg(); #ifdef MAPINFO + MapInfo_LoadMap(getmapname_stored); #else localcmd(strcat("exec \"maps/", getmapname_stored ,".mapcfg\"\n")); #endif @@ -641,11 +652,20 @@ float(float exponent) MaplistMethod_Shuffle = // more clever shuffling // insert the current map there newlist = ""; +#ifdef MAPINFO + for(j = 1; j < insertpos; ++j) // i == 1: no loop, will be inserted as first; however, i == 1 has been excluded above + newlist = strcat(newlist, " ", argv(j)); + newlist = strcat(newlist, " ", argv(0)); // now insert the just selected map + for(j = insertpos; j < Map_Count; ++j) // i == Map_Count: no loop, has just been inserted as last + newlist = strcat(newlist, " ", argv(j)); + newlist = substring(newlist, 1, strlen(newlist) - 1); +#else for(j = 1; j < insertpos; ++j) // i == 1: no loop, will be inserted as first; however, i == 1 has been excluded above newlist = strcat(newlist, "'", argv(j), "'"); newlist = strcat(newlist, "'", argv(0), "'"); // now insert the just selected map for(j = insertpos; j < Map_Count; ++j) // i == Map_Count: no loop, has just been inserted as last newlist = strcat(newlist, "'", argv(j), "'"); +#endif cvar_set("g_maplist", newlist); Map_Count = tokenize(newlist); @@ -666,6 +686,7 @@ void() Maplist_Init = { bprint( "Maplist is empty! Resetting it to default map list.\n" ); #ifdef MAPINFO + cvar_set("g_maplist", temp = MapInfo_ListAllowedMaps()); #else cvar_set("g_maplist", temp = cvar_string("g_maplist_defaultlist")); #endif @@ -747,6 +768,7 @@ float() DoNextMapOverride = } if(cvar_string("nextmap") != "") #ifdef MAPINFO + if(MapInfo_CheckMap(cvar_string("nextmap"))) #else if(TryFile(strcat("maps/", cvar_string("nextmap"), ".mapcfg"))) #endif @@ -789,6 +811,7 @@ void() GotoNextMap = { bprint( "Maplist contains no single playable map! Resetting it to default map list.\n" ); #ifdef MAPINFO + cvar_set("g_maplist", MapInfo_ListAllowedMaps()); #else cvar_set("g_maplist", cvar_string("g_maplist_defaultlist")); #endif @@ -1565,12 +1588,22 @@ void ShuffleMaplist() selected = ceil(random() * (litems - start) + start) - 1; // shift this item to the place start +#ifdef MAPINFO + for(i = 0; i < start; ++i) + result = strcat(result, " ", argv(i)); + result = strcat(result, " ", argv(selected)); + for(i = start; i < litems; ++i) + if(i != selected) + result = strcat(result, " ", argv(i)); + result = substring(result, 1, strlen(result) - 1); +#else for(i = 0; i < start; ++i) result = strcat(result, "'", argv(i), "'"); result = strcat(result, "'", argv(selected), "'"); for(i = start; i < litems; ++i) if(i != selected) result = strcat(result, "'", argv(i), "'"); +#endif litems = tokenize(result); @@ -1763,6 +1796,7 @@ string MapVote_Suggest(string m) return "This server does not allow for recent maps to be played again. Please be patient for some rounds."; #ifdef MAPINFO + if(!MapInfo_CheckMap(m)) #else if(!TryFile(strcat("maps/", m, ".mapcfg"))) #endif @@ -1822,6 +1856,7 @@ void MapVote_Init() { bprint( "Maplist contains no single playable map! Resetting it to default map list.\n" ); #ifdef MAPINFO + cvar_set("g_maplist", MapInfo_ListAllowedMaps()); #else cvar_set("g_maplist", cvar_string("g_maplist_defaultlist")); #endif @@ -2063,6 +2098,7 @@ void MapVote_Think() string GotoMap(string m) { #ifdef MAPINFO + if(!MapInfo_CheckMap(m)) #else if(!TryFile(strcat("maps/", m, ".mapcfg"))) #endif -- 2.39.2