]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/common/mapinfo.qc
use strcasecmp for map name comparisons
[divverent/nexuiz.git] / data / qcsrc / common / mapinfo.qc
1 // internal toy
2 void cvar_settemp(string pKey, string pValue)
3 {
4         //localcmd(strcat("\nsettemp ", t, " \"", s, "\"\n"));
5         
6         // duplicate what this alias does:
7         // alias settemp "settemp_list \"1 $1 $settemp_var $settemp_list\"; set $settemp_var \"${$1}\"; settemp_var ${settemp_var}x; $1 \"$2\""
8         
9         cvar_set("settemp_list", strcat("1 ", pKey, " ", cvar_string("settemp_var"), " ", cvar_string("settemp_list")));
10 #ifdef MENUQC
11         registercvar(cvar_string("settemp_var"), "", 0);
12 #else
13         registercvar(cvar_string("settemp_var"), "");
14 #endif
15         cvar_set(cvar_string("settemp_var"), cvar_string(pKey));
16         cvar_set("settemp_var", strcat(cvar_string("settemp_var"), "x"));
17         cvar_set(pKey, pValue);
18 }
19
20 void cvar_settemp_restore()
21 {
22         // undo what cvar_settemp did
23         float n, i;
24         n = tokenize(cvar_string("settemp_list"));
25         for(i = 0; i < n - 3; i += 3)
26                 cvar_set(argv(i + 1), cvar_string(argv(i + 2)));
27         cvar_set("settemp_list", "0");
28 }
29
30 // HUGE SET - stored in a string
31 string HugeSetOfIntegers_empty()
32 {
33         return "";
34 }
35 float HugeSetOfIntegers_get(string pArr, float i)
36 {
37         return stof(substring(pArr, i * 4, 4));
38 }
39 float HugeSetOfIntegers_length(string pArr)
40 {
41         return strlen(pArr) / 4;
42 }
43 string HugeSetOfIntegers_concat(string a1, string a2)
44 {
45         return strcat(a1, a2);
46 }
47 string HugeSetOfIntegers_insert(string a1, float n, string a2)
48         // special concat function to build up large lists in less time by binary concatenation
49 {
50         string s;
51         s = strcat("    ", ftos(n));
52         return strcat(a1, substring(s, strlen(s) - 4, 4), a2);
53 }
54
55 // generic string stuff
56 float startsWith(string haystack, string needle)
57 {
58         return substring(haystack, 0, strlen(needle)) == needle;
59 }
60 float startsWithNocase(string haystack, string needle)
61 {
62         return strcasecmp(substring(haystack, 0, strlen(needle)), needle) == 0;
63 }
64 string extractRestOfLine(string haystack, string needle)
65 {
66         if(startsWith(haystack, needle))
67                 return substring(haystack, strlen(needle), strlen(haystack) - strlen(needle));
68         return string_null;
69 }
70 string car(string s)
71 {
72         float o;
73         o = strstrofs(s, " ", 0);
74         if(o < 0)
75                 return s;
76         return substring(s, 0, o);
77 }
78 string cdr(string s)
79 {
80         float o;
81         o = strstrofs(s, " ", 0);
82         if(o < 0)
83                 return string_null;
84         return substring(s, o + 1, strlen(s) - (o + 1));
85 }
86
87 // GLOB HANDLING (for all BSP files)
88 float _MapInfo_globopen;
89 float _MapInfo_globcount; 
90 float _MapInfo_globhandle;
91 string _MapInfo_GlobItem(float i)
92 {
93         string s;
94         s = search_getfilename(_MapInfo_globhandle, i);
95         return substring(s, 5, strlen(s) - 9); // without maps/ and .bsp
96 }
97
98 void MapInfo_Enumerate()
99 {
100         if(_MapInfo_globopen)
101                 search_end(_MapInfo_globhandle);
102         _MapInfo_globhandle = search_begin("maps/*.bsp", TRUE, TRUE);
103         _MapInfo_globcount = search_getsize(_MapInfo_globhandle);
104         _MapInfo_globopen = 1;
105 }
106
107 // filter the info by game type mask (updates MapInfo_count)
108 string _MapInfo_filtered;
109 string MapInfo_FilterGametype_Recursive(float pGametype, float pFeatures, float pBegin, float pEnd, float pAbortOnGenerate)
110 {
111         float m, valid;
112         string l, r;
113
114         if(pBegin == pEnd)
115                 return HugeSetOfIntegers_empty();
116
117         m = floor((pBegin + pEnd) / 2);
118
119         l = MapInfo_FilterGametype_Recursive(pGametype, pFeatures, pBegin, m, pAbortOnGenerate);
120         if not(l)
121                 return string_null; // BAIL OUT
122         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.
123                 if(pAbortOnGenerate)
124                         return string_null; // BAIL OUT
125         valid = (((MapInfo_Map_supportedGametypes & pGametype) != 0) && ((MapInfo_Map_supportedFeatures & pFeatures) == pFeatures));
126         r = MapInfo_FilterGametype_Recursive(pGametype, pFeatures, m + 1, pEnd, pAbortOnGenerate);
127         if not(r)
128                 return string_null; // BAIL OUT
129
130         if(valid)
131                 return HugeSetOfIntegers_insert(l, m, r);
132         else
133                 return HugeSetOfIntegers_concat(l, r);
134 }
135 float MapInfo_FilterGametype(float pGametype, float pFeatures, float pAbortOnGenerate)
136 {
137         if(_MapInfo_filtered)
138                 strunzone(_MapInfo_filtered);
139         _MapInfo_filtered = MapInfo_FilterGametype_Recursive(pGametype, pFeatures, 0, _MapInfo_globcount, pAbortOnGenerate);
140         if not(_MapInfo_filtered)
141         {
142                 dprint("Autogenerated a .mapinfo, doing the rest later.\n");
143                 return 0;
144         }
145         _MapInfo_filtered = strzone(_MapInfo_filtered);
146         MapInfo_count = HugeSetOfIntegers_length(_MapInfo_filtered);
147         //print("Filter ", ftos(pGametype), "/", ftos(pFeatures), " has ", ftos(MapInfo_count), "\n");
148         // TODO clear cache
149         return 1;
150 }
151
152 // load info about the i-th map into the MapInfo_Map_* globals
153 string MapInfo_BSPName_ByID(float i)
154 {
155         return _MapInfo_GlobItem(HugeSetOfIntegers_get(_MapInfo_filtered, i));
156 }
157
158 string unquote(string s)
159 {
160         float i, j, l;
161         l = strlen(s);
162         j = -1;
163         for(i = 0; i < l; ++i)
164         {
165                 string ch;
166                 ch = substring(s, i, 1);
167                 if(ch != " ") if(ch != "\"")
168                 {
169                         for(j = strlen(s) - i - 1; j > 0; --j)
170                         {
171                                 ch = substring(s, i+j, 1);
172                                 if(ch != " ") if(ch != "\"")
173                                         return substring(s, i, j+1);
174                         }
175                         return substring(s, i, 1);
176                 }
177         }
178         return "";
179 }
180
181 float MapInfo_Get_ByID(float i)
182 {
183         // TODO check cache
184         if(MapInfo_Get_ByName(MapInfo_BSPName_ByID(i), 0, 0))
185         {
186                 // TODO save in cache
187                 return 1;
188         }
189         return 0;
190 }
191
192 float _MapInfo_Generate(string pFilename) // 0: failure, 1: ok ent, 2: ok bsp
193 {
194         string fn;
195         float fh;
196         string s, k, v;
197         vector o;
198         float i;
199         float inWorldspawn;
200         float r;
201         float twoBaseModes;
202
203         vector mapMins, mapMaxs;
204
205         r = 1;
206         fn = strcat("maps/", pFilename, ".ent");
207         fh = fopen(fn, FILE_READ);
208         if(fh < 0)
209         {
210                 r = 2;
211                 fn = strcat("maps/", pFilename, ".bsp");
212                 fh = fopen(fn, FILE_READ);
213         }
214         if(fh < 0)
215                 return 0;
216         print("Analyzing ", fn, " to generate initial mapinfo; please edit that file later\n");
217
218         inWorldspawn = 2;
219         MapInfo_Map_supportedGametypes = 0;
220
221         for(;;)
222         {
223                 if not((s = fgets(fh)))
224                         break;
225                 if(inWorldspawn == 1)
226                         if(startsWith(s, "}"))
227                                 inWorldspawn = 0;
228                 k = unquote(car(s));
229                 v = unquote(cdr(s));
230                 if(inWorldspawn)
231                 {
232                         if(k == "classname" && v == "worldspawn")
233                                 inWorldspawn = 1;
234                         else if(k == "author")
235                                 MapInfo_Map_author = v;
236                         else if(k == "message")
237                         {
238                                 i = strstrofs(v, " by ", 0);
239                                 if(MapInfo_Map_author == "He-Who-Must-Not-Be-Named" && i >= 0)
240                                 {
241                                         MapInfo_Map_title = substring(v, 0, i);
242                                         MapInfo_Map_author = substring(v, i + 4, strlen(v) - (i + 4));
243                                 }
244                                 else
245                                         MapInfo_Map_title = v;
246                         }
247                 }
248                 else
249                 {
250                         if(k == "origin")
251                         {
252                                 o = stov(strcat("'", v, "'"));
253                                 mapMins_x = min(mapMins_x, o_x);
254                                 mapMins_y = min(mapMins_y, o_y);
255                                 mapMins_z = min(mapMins_z, o_z);
256                                 mapMaxs_x = max(mapMaxs_x, o_x);
257                                 mapMaxs_y = max(mapMaxs_y, o_y);
258                                 mapMaxs_z = max(mapMaxs_z, o_z);
259                         }
260                         else if(k == "classname")
261                         {
262                                 if(v == "dom_controlpoint")
263                                         MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_DOMINATION;
264                                 else if(v == "item_flag_team2")
265                                         MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_CTF;
266                                 else if(v == "team_CTF_blueflag")
267                                         MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_CTF;
268                                 else if(v == "runematch_spawn_point")
269                                         MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_RUNEMATCH;
270                                 else if(v == "target_assault_roundend")
271                                         MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_ASSAULT;
272                                 else if(v == "onslaught_generator")
273                                         MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_ONSLAUGHT;
274                                 else if(v == "info_player_team1")
275                                         ++MapInfo_Map_spawnpoints;
276                                 else if(v == "info_player_team2")
277                                         ++MapInfo_Map_spawnpoints;
278                                 else if(v == "info_player_start")
279                                         ++MapInfo_Map_spawnpoints;
280                                 else if(v == "info_player_deathmatch")
281                                         ++MapInfo_Map_spawnpoints;
282                                 else if(v == "weapon_nex")
283                                         { }
284                                 else if(v == "weapon_railgun")
285                                         { }
286                                 else if(startsWith(v, "weapon_"))
287                                         MapInfo_Map_supportedFeatures |= MAPINFO_FEATURE_WEAPONS;
288                         }
289                 }
290         }
291         if(inWorldspawn)
292         {
293                 print(fn, " ended still in worldspawn, BUG\n");
294                 return 0;
295         }
296         MapInfo_Map_diameter = vlen(mapMaxs - mapMins);
297
298         twoBaseModes = MapInfo_Map_supportedGametypes & (MAPINFO_TYPE_CTF | MAPINFO_TYPE_ASSAULT);
299         if(twoBaseModes && (MapInfo_Map_supportedGametypes == twoBaseModes))
300         {
301                 // we have a CTF-only or Assault-only map. Don't add other modes then,
302                 // as the map is too symmetric for them.
303         }
304         else
305         {
306                 MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_DEATHMATCH;      // DM always works
307                 MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_LMS;             // LMS always works
308
309                 if(MapInfo_Map_spawnpoints >= 8  && MapInfo_Map_diameter > 4096)
310                         MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_TEAM_DEATHMATCH;
311                 if(                MapInfo_Map_diameter < 4096)
312                         MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_ARENA;
313                 if(MapInfo_Map_spawnpoints >= 12 && MapInfo_Map_diameter > 5120)
314                         MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_KEYHUNT;
315         }
316
317         fclose(fh);
318
319         return r;
320 }
321
322 void _MapInfo_Map_Reset()
323 {
324         MapInfo_Map_title = "Untitled1";
325         MapInfo_Map_description = "Bleh.";
326         MapInfo_Map_author = "He-Who-Must-Not-Be-Named";
327         MapInfo_Map_supportedGametypes = 0;
328         MapInfo_Map_supportedFeatures = 0;
329         MapInfo_Map_diameter = 0;
330         MapInfo_Map_spawnpoints = 0;
331 }
332
333 void _MapInfo_Map_ApplyGametype(string s, float pWantedType, float pThisType)
334 {
335         MapInfo_Map_supportedGametypes |= pThisType;
336         if(!(pThisType & pWantedType))
337                 return;
338         
339         cvar_set("fraglimit", car(s));
340         s = cdr(s);
341
342         cvar_set("timelimit", car(s));
343         s = cdr(s);
344
345         if(pWantedType == MAPINFO_TYPE_TEAM_DEATHMATCH)
346         {
347                 cvar_set("g_tdm_teams", car(s));
348                 s = cdr(s);
349         }
350
351         if(pWantedType == MAPINFO_TYPE_KEYHUNT)
352         {
353                 cvar_set("g_keyhunt_teams", car(s));
354                 s = cdr(s);
355         }
356 }
357
358 float MapInfo_Type_FromString(string t)
359 {
360         if     (t == "dm")    return MAPINFO_TYPE_DEATHMATCH;
361         else if(t == "tdm")   return MAPINFO_TYPE_TEAM_DEATHMATCH;
362         else if(t == "dom")   return MAPINFO_TYPE_DOMINATION;
363         else if(t == "ctf")   return MAPINFO_TYPE_CTF;
364         else if(t == "rune")  return MAPINFO_TYPE_RUNEMATCH;
365         else if(t == "lms")   return MAPINFO_TYPE_LMS;
366         else if(t == "arena") return MAPINFO_TYPE_ARENA;
367         else if(t == "kh")    return MAPINFO_TYPE_KEYHUNT;
368         else if(t == "as")    return MAPINFO_TYPE_ASSAULT;
369         else if(t == "ons")   return MAPINFO_TYPE_ONSLAUGHT;
370         else if(t == "all")   return MAPINFO_TYPE_ALL;
371         else                  return 0;
372 }
373
374 // load info about a map by name into the MapInfo_Map_* globals
375 float MapInfo_Get_ByName(string pFilename, float pAllowGenerate, float pGametypeToSet)
376 {
377         string fn;
378         string s, t;
379         float fh, fh2;
380         float r, f;
381
382         r = 1;
383
384         MapInfo_Map_bspname = pFilename;
385
386         // default all generic fields so they have "good" values in case something fails
387         fn = strcat("maps/", pFilename, ".mapinfo");
388         fh = fopen(fn, FILE_READ);
389         if(fh < 0)
390         {
391                 if(!pAllowGenerate)
392                         return 0;
393                 _MapInfo_Map_Reset();
394                 r = _MapInfo_Generate(pFilename);
395                 if(!r)
396                         return 0;
397                 fh = fopen(fn, FILE_WRITE);
398                 fputs(fh, strcat("title ", MapInfo_Map_title, "\n"));
399                 fputs(fh, strcat("description ", MapInfo_Map_description, "\n"));
400                 fputs(fh, strcat("author ", MapInfo_Map_author, "\n"));
401                 fputs(fh, strcat("_diameter ", ftos(MapInfo_Map_diameter), "\n"));
402                 fputs(fh, strcat("_spawnpoints ", ftos(MapInfo_Map_spawnpoints), "\n"));
403                 if(MapInfo_Map_supportedFeatures & MAPINFO_FEATURE_WEAPONS)       fputs(fh, "has weapons\n");
404                 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_DEATHMATCH)      fputs(fh, "type dm 30 20\n");
405                 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_TEAM_DEATHMATCH) fputs(fh, "type tdm 50 20 2\n");
406                 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_DOMINATION)      fputs(fh, "type dom 200 20\n");
407                 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_CTF)             fputs(fh, "type ctf 300 20\n");
408                 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_RUNEMATCH)       fputs(fh, "type rune 200 20\n");
409                 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_LMS)             fputs(fh, "type lms 9 20\n");
410                 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_ARENA)           fputs(fh, "type arena 10 20\n");
411                 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_KEYHUNT)         fputs(fh, "type kh 1000 20 3\n");
412                 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_ASSAULT)         fputs(fh, "type as 20\n");
413                 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_ONSLAUGHT)       fputs(fh, "type ons 20\n");
414
415                 fh2 = fopen(strcat("scripts/", pFilename, ".arena"), FILE_READ);
416                 if(fh2 >= 0)
417                 {
418                         fclose(fh2);
419                         fputs(fh, "settemp_for_type all sv_q3acompat_machineshotgunswap 1\n");
420                 }
421
422                 fclose(fh);
423                 r = 2;
424                 // return r;
425                 fh = fopen(fn, FILE_READ);
426                 if(fh < 0)
427                         error("... but I just wrote it!");
428         }
429
430         _MapInfo_Map_Reset();
431         for(;;)
432         {
433                 if not((s = fgets(fh)))
434                         break;
435                 t = car(s); s = cdr(s);
436                 if     (t == "title")
437                         MapInfo_Map_title = s;
438                 else if(t == "description")
439                         MapInfo_Map_description = s;
440                 else if(t == "author")
441                         MapInfo_Map_author = s;
442                 else if(t == "_diameter")
443                         MapInfo_Map_diameter = stof(s);
444                 else if(t == "_spawnpoints")
445                         MapInfo_Map_spawnpoints = stof(s);
446                 else if(t == "has")
447                 {
448                         t = car(s); s = cdr(s);
449                         if     (t == "weapons") MapInfo_Map_supportedFeatures |= MAPINFO_FEATURE_WEAPONS;
450                         else
451                                 dprint("Map ", pFilename, " supports unknown feature ", t, ", ignored\n");
452                 }
453                 else if(t == "type")
454                 {
455                         t = car(s); s = cdr(s);
456                         f = MapInfo_Type_FromString(t);
457                         if(f)
458                                 _MapInfo_Map_ApplyGametype (s, pGametypeToSet, f);
459                         else
460                                 dprint("Map ", pFilename, " supports unknown game type ", t, ", ignored\n");
461                 }
462                 else if(t == "settemp_for_type")
463                 {
464                         t = car(s); s = cdr(s);
465                         if((f = MapInfo_Type_FromString(t)))
466                         {
467                                 if(f & pGametypeToSet)
468                                 {
469                                         t = car(s); s = cdr(s);
470                                         if(strstrofs(t, "\"", 0) >= 0)
471                                                 print("Map ", pFilename, " contains a potentially harmful setting, ignored\n");
472                                         else if(strstrofs(t, "\\", 0) >= 0)
473                                                 print("Map ", pFilename, " contains a potentially harmful setting, ignored\n");
474                                         else if(strstrofs(t, ";", 0) >= 0)
475                                                 print("Map ", pFilename, " contains a potentially harmful setting, ignored\n");
476                                         else if(strstrofs(s, "\"", 0) >= 0)
477                                                 print("Map ", pFilename, " contains a potentially harmful setting, ignored\n");
478                                         else if(strstrofs(s, "\\", 0) >= 0)
479                                                 print("Map ", pFilename, " contains a potentially harmful setting, ignored\n");
480                                         else if(strstrofs(s, ";", 0) >= 0)
481                                                 print("Map ", pFilename, " contains a potentially harmful setting, ignored\n");
482                                         else
483                                         {
484                                                 dprint("Applying temporary setting ", t, " := ", s, "\n");
485                                                 cvar_settemp(t, s);
486                                         }
487                                 }
488                         }
489                         else
490                         {
491                                 dprint("Map ", pFilename, " has a setting for unknown game type ", t, ", ignored\n");
492                         }
493                 }
494                 else
495                         dprint("Map ", pFilename, " provides unknown info item ", t, ", ignored\n");
496         }
497         fclose(fh);
498         if(pGametypeToSet)
499                 if(!(MapInfo_Map_supportedGametypes & pGametypeToSet))
500                         error("Can't select the requested game type. Bailing out.");
501         if(MapInfo_Map_supportedGametypes != 0)
502                 return r;
503         dprint("Map ", pFilename, " supports no game types, ignored\n");
504         return 0;
505 }
506
507 float MapInfo_FindName(string s)
508 {
509         // if there is exactly one map of prefix s, return it
510         // if not, return the null string
511         // note that DP sorts glob results... so I can use a binary search
512         float l, r, m, cmp;
513         l = 0;
514         r = MapInfo_count;
515         // invariants: r is behind s, l-1 is equal or before
516         while(l != r)
517         {
518                 m = floor((l + r) / 2);
519                 MapInfo_FindName_match = _MapInfo_GlobItem(HugeSetOfIntegers_get(_MapInfo_filtered, m));
520                 cmp = strcasecmp(MapInfo_FindName_match, s);
521                 if(cmp == 0)
522                         return m; // found and good
523                 if(cmp < 0)
524                         l = m + 1; // l-1 is before s
525                 else
526                         r = m; // behind s
527         }
528         MapInfo_FindName_match = _MapInfo_GlobItem(HugeSetOfIntegers_get(_MapInfo_filtered, l));
529         MapInfo_FindName_firstResult = l;
530         // r == l, so: l is behind s, l-1 is before
531         // SO: if there is any, l is the one with the right prefix
532         //     and l+1 may be one too
533         if(l == MapInfo_count)
534         {
535                 MapInfo_FindName_match = string_null;
536                 MapInfo_FindName_firstResult = -1;
537                 return -1; // no MapInfo_FindName_match, behind last item
538         }
539         if(!startsWithNocase(MapInfo_FindName_match, s))
540         {
541                 MapInfo_FindName_match = string_null;
542                 MapInfo_FindName_firstResult = -1;
543                 return -1; // wrong prefix
544         }
545         if(l == MapInfo_count - 1)
546                 return l; // last one, nothing can follow => unique
547         if(startsWithNocase(_MapInfo_GlobItem(HugeSetOfIntegers_get(_MapInfo_filtered, l + 1)), s))
548         {
549                 MapInfo_FindName_match = string_null;
550                 return -1; // ambigous MapInfo_FindName_match
551         }
552         return l;
553 }
554
555 string MapInfo_FixName(string s)
556 {
557         MapInfo_FindName(s);
558         return MapInfo_FindName_match;
559 }
560
561 float MapInfo_CurrentFeatures()
562 {
563         float req;
564         req = 0;
565         if(!(cvar("g_instagib") || cvar("g_minstagib") || cvar("g_nixnex") || cvar("g_rocketarena")))
566                 req |= MAPINFO_FEATURE_WEAPONS;
567         return req;
568 }
569
570 float MapInfo_CurrentGametype()
571 {
572         if(cvar("g_domination"))
573                 return MAPINFO_TYPE_DOMINATION;
574         else if(cvar("g_ctf"))
575                 return MAPINFO_TYPE_CTF;
576         else if(cvar("g_runematch"))
577                 return MAPINFO_TYPE_RUNEMATCH;
578         else if(cvar("g_tdm"))
579                 return MAPINFO_TYPE_TEAM_DEATHMATCH;
580         else if(cvar("g_assault"))
581                 return MAPINFO_TYPE_ASSAULT;
582         else if(cvar("g_lms"))
583                 return MAPINFO_TYPE_LMS;
584         else if(cvar("g_arena"))
585                 return MAPINFO_TYPE_ARENA;
586         else if(cvar("g_keyhunt"))
587                 return MAPINFO_TYPE_KEYHUNT;
588         else if(cvar("g_onslaught"))
589                 return MAPINFO_TYPE_ONSLAUGHT;
590         else
591                 return MAPINFO_TYPE_DEATHMATCH;
592 }
593
594 float MapInfo_CheckMap(string s) // returns 0 if the map can't be played with the current settings, 1 otherwise
595 {
596         if(!MapInfo_Get_ByName(s, 1, 0))
597                 return 0;
598         if((MapInfo_Map_supportedGametypes & MapInfo_CurrentGametype()) == 0)
599                 return 0;
600         if((MapInfo_Map_supportedFeatures & MapInfo_CurrentFeatures()) != MapInfo_CurrentFeatures())
601                 return 0;
602         return 1;
603 }
604
605 void MapInfo_SwitchGameType(float t)
606 {
607         cvar_set("gamecfg",      "0");
608         cvar_set("g_dm",         (t == MAPINFO_TYPE_DEATHMATCH)      ? "1" : "0");
609         cvar_set("g_tdm",        (t == MAPINFO_TYPE_TEAM_DEATHMATCH) ? "1" : "0");
610         cvar_set("g_domination", (t == MAPINFO_TYPE_DOMINATION)      ? "1" : "0");
611         cvar_set("g_ctf",        (t == MAPINFO_TYPE_CTF)             ? "1" : "0");
612         cvar_set("g_runematch",  (t == MAPINFO_TYPE_RUNEMATCH)       ? "1" : "0");
613         cvar_set("g_lms",        (t == MAPINFO_TYPE_LMS)             ? "1" : "0");
614         cvar_set("g_arena",      (t == MAPINFO_TYPE_ARENA)           ? "1" : "0");
615         cvar_set("g_keyhunt",    (t == MAPINFO_TYPE_KEYHUNT)         ? "1" : "0");
616         cvar_set("g_assault",    (t == MAPINFO_TYPE_ASSAULT)         ? "1" : "0");
617         cvar_set("g_onslaught",  (t == MAPINFO_TYPE_ONSLAUGHT)       ? "1" : "0");
618 }
619
620 void MapInfo_LoadMap(string s)
621 {
622         MapInfo_Map_supportedGametypes = 0;
623         if(!MapInfo_CheckMap(s))
624         {
625                 print("EMERGENCY: can't play the selected map in the given game mode. Falling back to DM.\n");
626                 MapInfo_SwitchGameType(MAPINFO_TYPE_DEATHMATCH);
627         }
628         localcmd(strcat("\nsettemp_restore\nchangelevel ", s, "\n"));
629 }
630
631 string MapInfo_ListAllowedMaps()
632 {
633         string out;
634         float i;
635
636         // to make absolutely sure:
637         MapInfo_Enumerate();
638         MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), 0);
639
640         out = "";
641         for(i = 0; i < MapInfo_count; ++i)
642                 out = strcat(out, " ", _MapInfo_GlobItem(HugeSetOfIntegers_get(_MapInfo_filtered, i)));
643         return substring(out, 1, strlen(out) - 1);
644 }
645
646 void MapInfo_LoadMapSettings(string s) // to be called from worldspawn
647 {
648         float t;
649         if(!MapInfo_CheckMap(s))
650         {
651                 if(MapInfo_Map_supportedGametypes <= 0)
652                         error("Mapinfo system is not functional at all. BAILED OUT.\n");
653
654                 t = 1;
655                 while(!(MapInfo_Map_supportedGametypes & 1))
656                 {
657                         t *= 2;
658                         MapInfo_Map_supportedGametypes = floor(MapInfo_Map_supportedGametypes / 2);
659                 }
660                 // t is now a supported mode!
661                 print("EMERGENCY: can't play the selected map in the given game mode. Falling back to a supported mode.\n");
662                 MapInfo_SwitchGameType(t);
663         }
664         cvar_settemp_restore();
665         MapInfo_Get_ByName(s, 1, MapInfo_CurrentGametype());
666 }