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