X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=qcsrc%2Fcommon%2Fmapinfo.qc;h=e66ebb7671d4b85e9dc832d690d9e11e67392b44;hb=90ac5f4f1f4aa420546283c58953f387b82e33ba;hp=4f23862d2e8979a48596f257f8fbc27f4c8e935b;hpb=6bf2ddbf7560ca09c4529a4b243e9b34cf83232c;p=xonotic%2Fxonotic-data.pk3dir.git diff --git a/qcsrc/common/mapinfo.qc b/qcsrc/common/mapinfo.qc index 4f23862d2..e66ebb767 100644 --- a/qcsrc/common/mapinfo.qc +++ b/qcsrc/common/mapinfo.qc @@ -8,8 +8,7 @@ #include #endif -bool autocvar_g_mapinfo_arena_compat = true; -bool autocvar_g_mapinfo_arena_generate = false; +int autocvar_g_mapinfo_q3compat = 1; #ifdef MENUQC #define WARN_COND false @@ -274,6 +273,7 @@ float _MapInfo_Generate(string pFilename) // 0: failure, 1: ok ent, 2: ok bsp float r; float diameter, spawnpoints; float spawnplaces; + bool is_q3df_map = false; vector mapMins, mapMaxs; @@ -299,17 +299,20 @@ float _MapInfo_Generate(string pFilename) // 0: failure, 1: ok ent, 2: ok bsp mapMins = '0 0 0'; mapMaxs = '0 0 0'; - if(autocvar_g_mapinfo_arena_generate) + if(autocvar_g_mapinfo_q3compat == 2) // generate mapinfo using arena data { // try for .arena or .defi files, as they may have more accurate information bool isdefi = false; + float arena_fh = -1; string arena_fn = _MapInfo_FindArenaFile(pFilename, ".arena"); - int arena_fh = fopen(arena_fn, FILE_READ); + if(arena_fn != "") + arena_fh = fopen(arena_fn, FILE_READ); if(arena_fh < 0) { isdefi = true; arena_fn = _MapInfo_FindArenaFile(pFilename, ".defi"); - arena_fh = fopen(arena_fn, FILE_READ); + if(arena_fn != "") + arena_fh = fopen(arena_fn, FILE_READ); } if(arena_fh >= 0) { @@ -339,7 +342,7 @@ float _MapInfo_Generate(string pFilename) // 0: failure, 1: ok ent, 2: ok bsp _MapInfo_Map_worldspawn_music = v; else if(k == "noise") _MapInfo_Map_worldspawn_music = v; - else if(k == "message") + else if(k == "message" && (!MapInfo_Map_title || MapInfo_Map_title == "")) { i = strstrofs(v, " by ", 0); if(MapInfo_Map_author == "<AUTHOR>" && i >= 0) @@ -392,6 +395,8 @@ float _MapInfo_Generate(string pFilename) // 0: failure, 1: ok ent, 2: ok bsp MapInfo_Map_supportedFeatures |= MAPINFO_FEATURE_MONSTERS; else if(v == "target_music" || v == "trigger_music") _MapInfo_Map_worldspawn_music = string_null; // don't use regular BGM + else if(v == "target_stopTimer") + is_q3df_map = true; // don't support standard gamemodes else FOREACH(Gametypes, true, it.m_generate_mapinfo(it, v)); } @@ -410,7 +415,7 @@ float _MapInfo_Generate(string pFilename) // 0: failure, 1: ok ent, 2: ok bsp { // we have a symmetrical map, don't add the modes without bases } - else + else if(!is_q3df_map) { FOREACH(Gametypes, it.m_isAlwaysSupported(it, spawnpoints, diameter), MapInfo_Map_supportedGametypes |= it.m_flags); } @@ -624,6 +629,7 @@ Gametype MapInfo_Type_FromString(string gtype, bool dowarn, bool is_q3compat) case "ffa": replacement = "dm"; do_warn = false; break; case "cctf": case "oneflag": replacement = "ctf"; do_warn = false; break; + case "tournament": case "tourney": replacement = "duel"; do_warn = false; break; case "arena": if(is_q3compat) { replacement = "ca"; do_warn = false; } break; } @@ -656,7 +662,10 @@ string MapInfo_Type_ToText(Gametype t) void _MapInfo_Parse_Settemp(string pFilename, string acl, float type, string s, float recurse) { string t; - float fh, o; + float o; + // tabs are invalid, treat them as "empty" + s = strreplace("\t", "", s); + t = car(s); s = cdr(s); // limited support of "" and comments @@ -690,7 +699,7 @@ void _MapInfo_Parse_Settemp(string pFilename, string acl, float type, string s, { if(recurse > 0) { - fh = fopen(s, FILE_READ); + float fh = fopen(s, FILE_READ); if(fh < 0) { if(WARN_COND) @@ -700,6 +709,7 @@ void _MapInfo_Parse_Settemp(string pFilename, string acl, float type, string s, { while((s = fgets(fh))) { + s = strreplace("\t", "", s); // treat tabs as "empty", perform here first to ensure coments are detected // catch different sorts of comments if(s == "") // empty lines continue; @@ -785,7 +795,7 @@ bool _MapInfo_ParseArena(string arena_filename, int fh, string pFilename, Gamety // NOTE: .arena files can hold more than 1 map's information! // to handle this, we're going to store gathered information in local variables and save it if we encounter the correct map name bool in_brackets = false; // testing a potential mapinfo section (within brackets) - bool dosave = (arena_filename == strcat("scripts/", pFilename, ((isdefi) ? ".defi" : ".arena"))); // if the map is using the fallback, just accept the first found mapinfo (it's probably correct!) + bool dosave = false; string stored_Map_description = ""; string stored_Map_title = ""; string stored_Map_author = ""; @@ -801,12 +811,8 @@ bool _MapInfo_ParseArena(string arena_filename, int fh, string pFilename, Gamety // catch different sorts of comments if(s == "") // empty lines continue; - if(substring(s, 0, 1) == "#") // UNIX style - continue; if(substring(s, 0, 2) == "//") // C++ style continue; - if(substring(s, 0, 1) == "_") // q3map style - continue; if(strstrofs(s, "{", 0) >= 0) { if(in_brackets) @@ -896,7 +902,12 @@ bool _MapInfo_ParseArena(string arena_filename, int fh, string pFilename, Gamety // if there is a valid gametype in this .arena file, include it in the menu stored_supportedFeatures |= MAPINFO_FEATURE_WEAPONS; // type in quake 3 holds all the supported gametypes, so we must loop through all of them - string types = strreplace("team", "tdm ft", s); // TODO: handle support here better to include more Xonotic teamplay modes + // TODO: handle support here better to include more Xonotic teamplay modes + string types = s; + types = strreplace("team", "tdm ft", types); + types = strreplace("ffa", "dm lms ka", types); + if(strstrofs(types, "tournament", 0) < 0 && strstrofs(types, "tdm", 0) >= 0) // larger team map, support additional gamemodes! + types = cons(types, "ca kh"); FOREACH_WORD(types, true, { Gametype f = MapInfo_Type_FromString(it, false, true); @@ -924,47 +935,61 @@ bool _MapInfo_ParseArena(string arena_filename, int fh, string pFilename, Gamety return false; } -#if defined(CSQC) || defined(MENUQC) -string(string filename) whichpack = #503; -#endif +string _MapInfo_CheckArenaFile(string pFilename, string pMapname) +{ + // returns the file name if valid, otherwise returns "" + // a string is returned to optimise the use cases where a filename is also returned + int fh = fopen(pFilename, FILE_READ); + if(fh < 0) + return ""; + for(string s; (s = fgets(fh)); ) + { + s = strreplace("\t", "", s); + while(substring(s, 0, 1) == " ") + s = substring(s, 1, -1); + if(substring(s, 0, 2) == "//") + continue; + if(s == "") + continue; + int offset = strstrofs(s, "map", 0); + if(offset >= 0) + { + if(strstrofs(strtolower(s), strcat("\"", strtolower(pMapname), "\""), offset) >= 0) // quake 3 is case insensitive + { + fclose(fh); + return pFilename; // FOUND IT! + } + } + } + fclose(fh); + return ""; // file did not contain a "map" field matching our map name +} + string _MapInfo_FindArenaFile(string pFilename, string extension) { string fallback = strcat("scripts/", pFilename, extension); if(!checkextension("DP_QC_FS_SEARCH_PACKFILE")) - return fallback; + return _MapInfo_CheckArenaFile(fallback, pFilename); string base_pack = whichpack(strcat("maps/", pFilename, ".bsp")); if(base_pack == "") // this map isn't packaged! - return fallback; + return _MapInfo_CheckArenaFile(fallback, pFilename); int glob = search_packfile_begin(strcat("scripts/*", extension), true, true, base_pack); if(glob < 0) - return fallback; + return _MapInfo_CheckArenaFile(fallback, pFilename); int n = search_getsize(glob); for(int j = 0; j < n; ++j) { string file = search_getfilename(glob, j); - - int fh = fopen(file, FILE_READ); - if(fh < 0) - continue; // how? - for(string s; (s = fgets(fh)); ) + if(_MapInfo_CheckArenaFile(file, pFilename) != "") { - int offset = strstrofs(s, "map", 0); - if(offset >= 0) - { - if(strstrofs(strtolower(s), strcat("\"", strtolower(pFilename), "\""), offset) >= 0) // quake 3 is case insensitive - { - fclose(fh); - search_end(glob); - return file; // FOUND IT! - } - } + search_end(glob); + return file; } - fclose(fh); } search_end(glob); - return fallback; // if we get here, a valid .arena file could not be found + return ""; // if we get here, a valid .arena file could not be found } // load info about a map by name into the MapInfo_Map_* globals @@ -998,17 +1023,21 @@ float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, Gamet fh = fopen(fn, FILE_READ); if(fh < 0) { - if(autocvar_g_mapinfo_arena_compat) + if(autocvar_g_mapinfo_q3compat) // use arena data instead of generating a mapinfo file { - // try for .arena or .defi files if no .mapinfo exists bool isdefi = false; - fn = _MapInfo_FindArenaFile(pFilename, ".arena"); - fh = fopen(fn, FILE_READ); - if(fh < 0) + if(autocvar_g_mapinfo_q3compat == 1) // only parse .arena files in mode 1 + { + fn = _MapInfo_FindArenaFile(pFilename, ".arena"); + if(fn != "") + fh = fopen(fn, FILE_READ); + } + if(fh < 0 || autocvar_g_mapinfo_q3compat == 2) { isdefi = true; fn = _MapInfo_FindArenaFile(pFilename, ".defi"); - fh = fopen(fn, FILE_READ); + if(fn != "") + fh = fopen(fn, FILE_READ); } if(fh >= 0) { @@ -1034,12 +1063,8 @@ float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, Gamet fputs(fh, strcat("author ", MapInfo_Map_author, "\n")); if(_MapInfo_Map_worldspawn_music != "") { - if( - substring(_MapInfo_Map_worldspawn_music, strlen(_MapInfo_Map_worldspawn_music) - 4, 4) == ".wav" - || - substring(_MapInfo_Map_worldspawn_music, strlen(_MapInfo_Map_worldspawn_music) - 4, 4) == ".ogg" - ) - fputs(fh, strcat("cdtrack ", substring(_MapInfo_Map_worldspawn_music, 0, strlen(_MapInfo_Map_worldspawn_music) - 4), "\n")); + if(strcasecmp(substring(_MapInfo_Map_worldspawn_music, -4, 4), ".wav") == 0 || strcasecmp(substring(_MapInfo_Map_worldspawn_music, -4, 4), ".ogg") == 0) + fputs(fh, strcat("cdtrack ", substring(_MapInfo_Map_worldspawn_music, 0, -4), "\n")); else fputs(fh, strcat("cdtrack ", _MapInfo_Map_worldspawn_music, "\n")); } @@ -1142,14 +1167,14 @@ float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, Gamet { MapInfo_Map_flags |= MAPINFO_FLAG_FRUSTRATING; } - else if(t == "noautomaplist") + else if(t == "donotwant" || t == "noautomaplist") { - MapInfo_Map_flags |= MAPINFO_FLAG_NOAUTOMAPLIST; + MapInfo_Map_flags |= MAPINFO_FLAG_DONOTWANT; } else if(t == "gameversion_min") { if (cvar("gameversion") < stof(s)) - MapInfo_Map_flags |= MAPINFO_FLAG_NOAUTOMAPLIST; + MapInfo_Map_flags |= MAPINFO_FLAG_DONOTWANT; } else if(t == "type") { @@ -1316,6 +1341,23 @@ int MapInfo_Get_ByName(string pFilename, float pAllowGenerate, Gametype pGametyp return r; } +bool MapReadSizes(string map) +{ + // TODO: implement xonotic#28 / xonvote 172 (sizes in mapinfo) + string readsize_msg = strcat("MapReadSizes ", map); + float fh = fopen(strcat("maps/", map, ".sizes"), FILE_READ); + if(fh >= 0) + { + map_minplayers = stoi(fgets(fh)); + map_maxplayers = stoi(fgets(fh)); + fclose(fh); + LOG_TRACEF(readsize_msg, ": ok, min %d max %d", map_minplayers, map_maxplayers); + return true; + } + LOG_TRACE(readsize_msg, ": not found"); + return false; +} + float MapInfo_FindName(string s) { // if there is exactly one map of prefix s, return it @@ -1374,7 +1416,7 @@ int MapInfo_CurrentFeatures() { int req = 0; // TODO: find a better way to check if weapons are required on the map - if(!(cvar("g_instagib") || cvar("g_overkill") || cvar("g_nix") || cvar("g_weaponarena") || !cvar("g_pickup_items") + if(!(cvar("g_instagib") || cvar("g_overkill") || cvar("g_nix") || cvar("g_weaponarena") || !cvar("g_pickup_items") || !cvar("g_melee_only") || cvar("g_race") || cvar("g_cts") || cvar("g_nexball") || cvar("g_ca") || cvar("g_freezetag") || cvar("g_lms"))) req |= MAPINFO_FEATURE_WEAPONS; return req;