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