Implement RPG / Free Roam gametype. Next step is to remove scoring under this type...
[voretournament/voretournament.git] / data / qcsrc / common / mapinfo.qc
1 // generic string stuff\r
2 \r
3 float _MapInfo_Cache_Active;\r
4 float _MapInfo_Cache_DB_NameToIndex;\r
5 float _MapInfo_Cache_Buf_IndexToMapData;\r
6 \r
7 void MapInfo_Cache_Destroy()\r
8 {\r
9         if(!_MapInfo_Cache_Active)\r
10                 return;\r
11 \r
12         db_close(_MapInfo_Cache_DB_NameToIndex);\r
13         buf_del(_MapInfo_Cache_Buf_IndexToMapData);\r
14         _MapInfo_Cache_Active = 0;\r
15 }\r
16 \r
17 void MapInfo_Cache_Create()\r
18 {\r
19         MapInfo_Cache_Destroy();\r
20         _MapInfo_Cache_DB_NameToIndex = db_create();\r
21         _MapInfo_Cache_Buf_IndexToMapData = buf_create();\r
22         _MapInfo_Cache_Active = 1;\r
23 }\r
24 \r
25 void MapInfo_Cache_Invalidate()\r
26 {\r
27         if(!_MapInfo_Cache_Active)\r
28                 return;\r
29 \r
30         MapInfo_Cache_Create();\r
31 }\r
32 \r
33 void MapInfo_Cache_Store()\r
34 {\r
35         float i;\r
36         string s;\r
37         if(!_MapInfo_Cache_Active)\r
38                 return;\r
39 \r
40         s = db_get(_MapInfo_Cache_DB_NameToIndex, MapInfo_Map_bspname);\r
41         if(!s) // empty string is NOT valid here!\r
42         {\r
43                 i = buf_getsize(_MapInfo_Cache_Buf_IndexToMapData);\r
44                 db_put(_MapInfo_Cache_DB_NameToIndex, MapInfo_Map_bspname, ftos(i));\r
45         }\r
46         else\r
47                 i = stof(s);\r
48 \r
49         // now store all the stuff\r
50         bufstr_set(_MapInfo_Cache_Buf_IndexToMapData,   i, MapInfo_Map_bspname);\r
51         bufstr_set(_MapInfo_Cache_Buf_IndexToMapData, ++i, MapInfo_Map_title);\r
52         bufstr_set(_MapInfo_Cache_Buf_IndexToMapData, ++i, MapInfo_Map_description);\r
53         bufstr_set(_MapInfo_Cache_Buf_IndexToMapData, ++i, MapInfo_Map_author);\r
54         bufstr_set(_MapInfo_Cache_Buf_IndexToMapData, ++i, ftos(MapInfo_Map_supportedGametypes));\r
55         bufstr_set(_MapInfo_Cache_Buf_IndexToMapData, ++i, ftos(MapInfo_Map_supportedFeatures));\r
56         bufstr_set(_MapInfo_Cache_Buf_IndexToMapData, ++i, ftos(MapInfo_Map_flags));\r
57 }\r
58 \r
59 float MapInfo_Cache_Retrieve(string map)\r
60 {\r
61         float i;\r
62         string s;\r
63         if(!_MapInfo_Cache_Active)\r
64                 return 0;\r
65 \r
66         s = db_get(_MapInfo_Cache_DB_NameToIndex, map);\r
67         if(!s)\r
68                 return 0;\r
69         i = stof(s);\r
70 \r
71         // now retrieve all the stuff\r
72         MapInfo_Map_bspname = bufstr_get(_MapInfo_Cache_Buf_IndexToMapData, i);\r
73         MapInfo_Map_title = bufstr_get(_MapInfo_Cache_Buf_IndexToMapData, ++i);\r
74         MapInfo_Map_description = bufstr_get(_MapInfo_Cache_Buf_IndexToMapData, ++i);\r
75         MapInfo_Map_author = bufstr_get(_MapInfo_Cache_Buf_IndexToMapData, ++i);\r
76         MapInfo_Map_supportedGametypes = stof(bufstr_get(_MapInfo_Cache_Buf_IndexToMapData, ++i));\r
77         MapInfo_Map_supportedFeatures = stof(bufstr_get(_MapInfo_Cache_Buf_IndexToMapData, ++i));\r
78         MapInfo_Map_flags = stof(bufstr_get(_MapInfo_Cache_Buf_IndexToMapData, ++i));\r
79         return 1;\r
80 }\r
81 \r
82 // GLOB HANDLING (for all BSP files)\r
83 float _MapInfo_globopen;\r
84 float _MapInfo_globcount; \r
85 float _MapInfo_globhandle;\r
86 string _MapInfo_GlobItem(float i)\r
87 {\r
88         string s;\r
89         s = search_getfilename(_MapInfo_globhandle, i);\r
90         return substring(s, 5, strlen(s) - 9); // without maps/ and .bsp\r
91 }\r
92 \r
93 void MapInfo_Enumerate()\r
94 {\r
95         if(_MapInfo_globopen)\r
96                 search_end(_MapInfo_globhandle);\r
97         MapInfo_Cache_Invalidate();\r
98         _MapInfo_globhandle = search_begin("maps/*.bsp", TRUE, TRUE);\r
99         _MapInfo_globcount = search_getsize(_MapInfo_globhandle);\r
100         _MapInfo_globopen = 1;\r
101 }\r
102 \r
103 // filter the info by game type mask (updates MapInfo_count)\r
104 //\r
105 float _MapInfo_filtered;\r
106 float _MapInfo_filtered_allocated;\r
107 float MapInfo_FilterList_Lookup(float i)\r
108 {\r
109         return stof(bufstr_get(_MapInfo_filtered, i));\r
110 }\r
111 \r
112 void _MapInfo_FilterList_swap(float i, float j, entity pass)\r
113 {\r
114         string h;\r
115         h = bufstr_get(_MapInfo_filtered, i);\r
116         bufstr_set(_MapInfo_filtered, i, bufstr_get(_MapInfo_filtered, j));\r
117         bufstr_set(_MapInfo_filtered, j, h);\r
118 }\r
119 \r
120 float _MapInfo_FilterList_cmp(float i, float j, entity pass)\r
121 {\r
122         string a, b;\r
123         a = _MapInfo_GlobItem(stof(bufstr_get(_MapInfo_filtered, i)));\r
124         b = _MapInfo_GlobItem(stof(bufstr_get(_MapInfo_filtered, j)));\r
125         return strcasecmp(a, b);\r
126 }\r
127 \r
128 float MapInfo_FilterGametype(float pGametype, float pFeatures, float pFlagsRequired, float pFlagsForbidden, float pAbortOnGenerate)\r
129 {\r
130         float i, j;\r
131         if not(_MapInfo_filtered_allocated)\r
132         {\r
133                 _MapInfo_filtered_allocated = 1;\r
134                 _MapInfo_filtered = buf_create();\r
135         }\r
136         MapInfo_count = 0;\r
137         for(i = 0, j = -1; i < _MapInfo_globcount; ++i)\r
138         {\r
139                 if(MapInfo_Get_ByName(_MapInfo_GlobItem(i), 1, 0) == 2) // if we generated one... BAIL OUT and let the caller continue in the next frame.\r
140                         if(pAbortOnGenerate)\r
141                         {\r
142                                 dprint("Autogenerated a .mapinfo, doing the rest later.\n");\r
143                                 MapInfo_progress = i / _MapInfo_globcount;\r
144                                 return 0;\r
145                         }\r
146                 if((MapInfo_Map_supportedGametypes & pGametype) != 0)\r
147                 if((MapInfo_Map_supportedFeatures & pFeatures) == pFeatures)\r
148                 if((MapInfo_Map_flags & pFlagsForbidden) == 0)\r
149                 if((MapInfo_Map_flags & pFlagsRequired) == pFlagsRequired)\r
150                         bufstr_set(_MapInfo_filtered, ++j, ftos(i));\r
151         }\r
152         MapInfo_count = j + 1;\r
153         MapInfo_ClearTemps();\r
154         \r
155         // sometimes the glob isn't sorted nicely, so fix it here...\r
156         heapsort(MapInfo_count, _MapInfo_FilterList_swap, _MapInfo_FilterList_cmp, world);\r
157 \r
158         return 1;\r
159 }\r
160 \r
161 void MapInfo_Filter_Free()\r
162 {\r
163         if(_MapInfo_filtered_allocated)\r
164         {\r
165                 buf_del(_MapInfo_filtered);\r
166                 _MapInfo_filtered_allocated = 0;\r
167         }\r
168 }\r
169 \r
170 // load info about the i-th map into the MapInfo_Map_* globals\r
171 string MapInfo_BSPName_ByID(float i)\r
172 {\r
173         return _MapInfo_GlobItem(MapInfo_FilterList_Lookup(i));\r
174 }\r
175 \r
176 string unquote(string s)\r
177 {\r
178         float i, j, l;\r
179         l = strlen(s);\r
180         j = -1;\r
181         for(i = 0; i < l; ++i)\r
182         {\r
183                 string ch;\r
184                 ch = substring(s, i, 1);\r
185                 if(ch != " ") if(ch != "\"")\r
186                 {\r
187                         for(j = strlen(s) - i - 1; j > 0; --j)\r
188                         {\r
189                                 ch = substring(s, i+j, 1);\r
190                                 if(ch != " ") if(ch != "\"")\r
191                                         return substring(s, i, j+1);\r
192                         }\r
193                         return substring(s, i, 1);\r
194                 }\r
195         }\r
196         return "";\r
197 }\r
198 \r
199 float MapInfo_Get_ByID(float i)\r
200 {\r
201         if(MapInfo_Get_ByName(MapInfo_BSPName_ByID(i), 0, 0))\r
202                 return 1;\r
203         return 0;\r
204 }\r
205 \r
206 string _MapInfo_Map_worldspawn_music;\r
207 \r
208 float _MapInfo_Generate(string pFilename) // 0: failure, 1: ok ent, 2: ok bsp\r
209 {\r
210         string fn;\r
211         float fh;\r
212         string s, k, v;\r
213         vector o;\r
214         float i;\r
215         float inWorldspawn;\r
216         float r;\r
217         float twoBaseModes;\r
218         float diameter, spawnpoints;\r
219         float spawnplaces;\r
220 \r
221         vector mapMins, mapMaxs;\r
222 \r
223         r = 1;\r
224         fn = strcat("maps/", pFilename, ".ent");\r
225         fh = fopen(fn, FILE_READ);\r
226         if(fh < 0)\r
227         {\r
228                 r = 2;\r
229                 fn = strcat("maps/", pFilename, ".bsp");\r
230                 fh = fopen(fn, FILE_READ);\r
231         }\r
232         if(fh < 0)\r
233                 return 0;\r
234         print("Analyzing ", fn, " to generate initial mapinfo; please edit that file later\n");\r
235 \r
236         inWorldspawn = 2;\r
237         MapInfo_Map_flags = 0;\r
238         MapInfo_Map_supportedGametypes = 0;\r
239         spawnpoints = 0;\r
240         spawnplaces = 0;\r
241         _MapInfo_Map_worldspawn_music = "";\r
242 \r
243         for(;;)\r
244         {\r
245                 if not((s = fgets(fh)))\r
246                         break;\r
247                 if(inWorldspawn == 1)\r
248                         if(startsWith(s, "}"))\r
249                                 inWorldspawn = 0;\r
250                 k = unquote(car(s));\r
251                 v = unquote(cdr(s));\r
252                 if(inWorldspawn)\r
253                 {\r
254                         if(k == "classname" && v == "worldspawn")\r
255                                 inWorldspawn = 1;\r
256                         else if(k == "author")\r
257                                 MapInfo_Map_author = v;\r
258                         else if(k == "_description")\r
259                                 MapInfo_Map_description = v;\r
260                         else if(k == "music")\r
261                                 _MapInfo_Map_worldspawn_music = v;\r
262                         else if(k == "noise")\r
263                                 _MapInfo_Map_worldspawn_music = v;\r
264                         else if(k == "message")\r
265                         {\r
266                                 i = strstrofs(v, " by ", 0);\r
267                                 if(MapInfo_Map_author == "<AUTHOR>" && i >= 0)\r
268                                 {\r
269                                         MapInfo_Map_title = substring(v, 0, i);\r
270                                         MapInfo_Map_author = substring(v, i + 4, strlen(v) - (i + 4));\r
271                                 }\r
272                                 else\r
273                                         MapInfo_Map_title = v;\r
274                         }\r
275                 }\r
276                 else\r
277                 {\r
278                         if(k == "origin")\r
279                         {\r
280                                 o = stov(strcat("'", v, "'"));\r
281                                 mapMins_x = min(mapMins_x, o_x);\r
282                                 mapMins_y = min(mapMins_y, o_y);\r
283                                 mapMins_z = min(mapMins_z, o_z);\r
284                                 mapMaxs_x = max(mapMaxs_x, o_x);\r
285                                 mapMaxs_y = max(mapMaxs_y, o_y);\r
286                                 mapMaxs_z = max(mapMaxs_z, o_z);\r
287                         }\r
288                         else if(k == "race_place")\r
289                         {\r
290                                 if(stof(v) > 0)\r
291                                         spawnplaces = 1;\r
292                         }\r
293                         else if(k == "classname")\r
294                         {\r
295                                 if(v == "dom_controlpoint")\r
296                                         MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_DOMINATION;\r
297                                 else if(v == "item_flag_team2")\r
298                                         MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_CTF;\r
299                                 else if(v == "team_CTF_blueflag")\r
300                                         MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_CTF;\r
301                                 else if(v == "target_assault_roundend")\r
302                                         MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_ASSAULT;\r
303                                 else if(v == "onslaught_generator")\r
304                                         MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_ONSLAUGHT;\r
305                                 else if(v == "info_player_team1")\r
306                                         ++spawnpoints;\r
307                                 else if(v == "info_player_team2")\r
308                                         ++spawnpoints;\r
309                                 else if(v == "info_player_start")\r
310                                         ++spawnpoints;\r
311                                 else if(v == "info_player_deathmatch")\r
312                                         ++spawnpoints;\r
313                                 else if(v == "trigger_race_checkpoint")\r
314                                         MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_RACE;\r
315                                 else if(v == "target_startTimer")\r
316                                         MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_CTS;\r
317                                 else if(v == "weapon_nex")\r
318                                         { }\r
319                                 else if(v == "weapon_railgun")\r
320                                         { }\r
321                                 else if(startsWith(v, "weapon_"))\r
322                                         MapInfo_Map_supportedFeatures |= MAPINFO_FEATURE_WEAPONS;\r
323                                 else if(v == "target_music" || v == "trigger_music")\r
324                                         _MapInfo_Map_worldspawn_music = string_null; // don't use regular BGM\r
325                         }\r
326                 }\r
327         }\r
328         if(inWorldspawn)\r
329         {\r
330                 print(fn, " ended still in worldspawn, BUG\n");\r
331                 return 0;\r
332         }\r
333         diameter = vlen(mapMaxs - mapMins);\r
334 \r
335         twoBaseModes = MapInfo_Map_supportedGametypes & (MAPINFO_TYPE_CTF | MAPINFO_TYPE_ASSAULT | MAPINFO_TYPE_RACE);\r
336         if(twoBaseModes && (MapInfo_Map_supportedGametypes == twoBaseModes))\r
337         {\r
338                 // we have a CTF-only or Assault-only map. Don't add other modes then,\r
339                 // as the map is too symmetric for them.\r
340         }\r
341         else\r
342         {\r
343                 MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_DEATHMATCH;      // DM always works\r
344                 MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_LMS;             // LMS always works\r
345 \r
346                 if(spawnpoints >= 8  && diameter > 4096) {\r
347                         MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_TEAM_DEATHMATCH;\r
348                         MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_CA;\r
349                 }\r
350                 if(                     diameter < 4096)\r
351                         MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_ARENA;\r
352                 if(spawnpoints >= 12 && diameter > 5120)\r
353                         MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_KEYHUNT;\r
354         }\r
355 \r
356         if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_RACE)\r
357         if(!spawnplaces)\r
358         {\r
359                 MapInfo_Map_supportedGametypes &~= MAPINFO_TYPE_RACE;\r
360                 MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_CTS;\r
361         }\r
362 \r
363         dprint("-> diameter ",    ftos(diameter));\r
364         dprint(";  spawnpoints ", ftos(spawnpoints));\r
365         dprint(";  modes ",       ftos(MapInfo_Map_supportedGametypes), "\n");\r
366 \r
367         fclose(fh);\r
368 \r
369         return r;\r
370 }\r
371 \r
372 void _MapInfo_Map_Reset()\r
373 {\r
374         MapInfo_Map_title = "<TITLE>";\r
375         MapInfo_Map_description = "<DESCRIPTION>";\r
376         MapInfo_Map_author = "<AUTHOR>";\r
377         MapInfo_Map_supportedGametypes = 0;\r
378         MapInfo_Map_supportedFeatures = 0;\r
379         MapInfo_Map_flags = 0;\r
380         MapInfo_Map_clientstuff = "";\r
381         MapInfo_Map_fog = "";\r
382         MapInfo_Map_mins = '0 0 0';\r
383         MapInfo_Map_maxs = '0 0 0';\r
384 }\r
385 \r
386 void _MapInfo_Map_ApplyGametype(string s, float pWantedType, float pThisType)\r
387 {\r
388         string sa;\r
389         MapInfo_Map_supportedGametypes |= pThisType;\r
390         if(!(pThisType & pWantedType))\r
391                 return;\r
392         \r
393         if(pWantedType == MAPINFO_TYPE_ASSAULT || pWantedType == MAPINFO_TYPE_ONSLAUGHT || pWantedType == MAPINFO_TYPE_RACE || pWantedType == MAPINFO_TYPE_CTS || pWantedType == MAPINFO_TYPE_RPG) // these modes don't use fraglimit\r
394         {\r
395                 cvar_set("fraglimit", "0");\r
396         }\r
397         else\r
398         {\r
399                 cvar_set("fraglimit", car(s));\r
400                 s = cdr(s);\r
401         }\r
402 \r
403         cvar_set("timelimit", car(s));\r
404         s = cdr(s);\r
405 \r
406         if(pWantedType == MAPINFO_TYPE_TEAM_DEATHMATCH)\r
407         {\r
408                 sa = car(s); if(sa == "") sa = "2";\r
409                 cvar_set("g_tdm_teams", sa);\r
410                 s = cdr(s);\r
411         }\r
412 \r
413         if(pWantedType == MAPINFO_TYPE_KEYHUNT)\r
414         {\r
415                 sa = car(s); if(sa == "") sa = "3";\r
416                 cvar_set("g_keyhunt_teams", sa);\r
417                 s = cdr(s);\r
418         }\r
419 \r
420         if(pWantedType == MAPINFO_TYPE_CTF)\r
421         {\r
422                 sa = car(s); if(sa == "") sa = "10";\r
423                 if(cvar("g_ctf_win_mode") < 2)\r
424                         cvar_set("fraglimit", sa);\r
425                 s = cdr(s);\r
426         }\r
427 \r
428         // rc = timelimit timelimit_qualification laps laps_teamplay\r
429         if(pWantedType == MAPINFO_TYPE_RACE)\r
430         {\r
431                 sa = car(s); if(sa == "") sa = cvar_string("timelimit");\r
432                 cvar_set("g_race_qualifying_timelimit", sa);\r
433                 s = cdr(s);\r
434 \r
435                 sa = car(s); if(sa == "") sa = "10";\r
436                 if(cvar("g_race_teams") < 2)\r
437                         cvar_set("fraglimit", sa);\r
438                 s = cdr(s);\r
439 \r
440                 sa = car(s); if(sa == "") sa = "20";\r
441                 if(cvar("g_race_teams") >= 2)\r
442                         cvar_set("fraglimit", sa);\r
443                 s = cdr(s);\r
444         }\r
445 \r
446         if(pWantedType == MAPINFO_TYPE_CTS)\r
447         {\r
448                 sa = car(s); if(sa == "") sa = cvar_string("fraglimit");\r
449                 if(cvar("g_race_teams"))\r
450                         cvar_set("fraglimit", sa);\r
451                 s = cdr(s);\r
452         }\r
453 \r
454         sa = car(s); if(sa == "") sa = "0";\r
455         cvar_set("leadlimit", sa);\r
456         s = cdr(s);\r
457 }\r
458 \r
459 float MapInfo_Type_FromString(string t)\r
460 {\r
461         if     (t == "dm")      return MAPINFO_TYPE_DEATHMATCH;\r
462         else if(t == "tdm")     return MAPINFO_TYPE_TEAM_DEATHMATCH;\r
463         else if(t == "dom")     return MAPINFO_TYPE_DOMINATION;\r
464         else if(t == "ctf")     return MAPINFO_TYPE_CTF;\r
465         else if(t == "lms")     return MAPINFO_TYPE_LMS;\r
466         else if(t == "arena")   return MAPINFO_TYPE_ARENA;\r
467         else if(t == "ca")      return MAPINFO_TYPE_CA;\r
468         else if(t == "kh")      return MAPINFO_TYPE_KEYHUNT;\r
469         else if(t == "as")      return MAPINFO_TYPE_ASSAULT;\r
470         else if(t == "ons")     return MAPINFO_TYPE_ONSLAUGHT;\r
471         else if(t == "rc")      return MAPINFO_TYPE_RACE;\r
472         else if(t == "cts")     return MAPINFO_TYPE_CTS;\r
473         else if(t == "rpg")     return MAPINFO_TYPE_RPG;\r
474         else if(t == "all")     return MAPINFO_TYPE_ALL;\r
475         else                    return 0;\r
476 }\r
477 \r
478 void _MapInfo_Parse_Settemp(string pFilename, string acl, float type, string s, float recurse)\r
479 {\r
480         string t;\r
481         float fh, o;\r
482         t = car(s); s = cdr(s);\r
483 \r
484         // limited support of "" and comments\r
485         //   remove trailing and leading " of t\r
486         if(substring(t, 0, 1) == "\"")\r
487         {\r
488                 if(substring(t, -1, 1) == "\"")\r
489                         t = substring(t, 1, -2);\r
490         }\r
491 \r
492         //   remove leading " of s\r
493         if(substring(s, 0, 1) == "\"")\r
494         {\r
495                 s = substring(s, 1, -1);\r
496         }\r
497         //   remove trailing " of s, and all that follows (cvar description)\r
498         o = strstrofs(s, "\"", 0);\r
499         if(o >= 0)\r
500                 s = substring(s, 0, o);\r
501         \r
502         //   remove // comments\r
503         o = strstrofs(s, "//", 0);\r
504         if(o >= 0)\r
505                 s = substring(s, 0, o);\r
506         \r
507         //   remove trailing spaces\r
508         while(substring(s, -1, 1) == " ")\r
509                 s = substring(s, 0, -2);\r
510 \r
511         if(t == "#include")\r
512         {\r
513                 if(recurse > 0)\r
514                 {\r
515                         fh = fopen(s, FILE_READ);\r
516                         if(fh < 0)\r
517                                 print("Map ", pFilename, " references not existing config file ", s, "\n");\r
518                         else\r
519                         {\r
520                                 for(;;)\r
521                                 {\r
522                                         if not((s = fgets(fh)))\r
523                                                 break;\r
524 \r
525                                         // catch different sorts of comments\r
526                                         if(s == "")                    // empty lines\r
527                                                 continue;\r
528                                         if(substring(s, 0, 1) == "#")  // UNIX style\r
529                                                 continue;\r
530                                         if(substring(s, 0, 2) == "//") // C++ style\r
531                                                 continue;\r
532                                         if(substring(s, 0, 1) == "_")  // q3map style\r
533                                                 continue;\r
534 \r
535                                         if(substring(s, 0, 4) == "set ")\r
536                                                 s = substring(s, 4, -1);\r
537                                         if(substring(s, 0, 5) == "seta ")\r
538                                                 s = substring(s, 5, -1);\r
539 \r
540                                         _MapInfo_Parse_Settemp(pFilename, acl, type, s, recurse - 1);\r
541                                 }\r
542                                 fclose(fh);\r
543                         }\r
544                 }\r
545                 else\r
546                         print("Map ", pFilename, " uses too many levels of inclusion\n");\r
547         }\r
548         else if(t == "")\r
549                 print("Map ", pFilename, " contains a potentially harmful setting, ignored\n");\r
550         else if not(cvar_value_issafe(t))\r
551                 print("Map ", pFilename, " contains a potentially harmful setting, ignored\n");\r
552         else if not (cvar_value_issafe(s))\r
553                 print("Map ", pFilename, " contains a potentially harmful setting, ignored\n");\r
554         else if(matchacl(MAPINFO_SETTEMP_ACL_SYSTEM, t) <= 0)\r
555                 print("Map ", pFilename, " contains a potentially harmful setting, ignored\n");\r
556         else if(matchacl(acl, t) <= 0)\r
557                 print("Map ", pFilename, " contains a denied setting, ignored\n");\r
558         else\r
559         {\r
560                 if(type == 0) // server set\r
561                 {\r
562                         dprint("Applying temporary setting ", t, " := ", s, "\n");\r
563                         if(cvar("g_campaign"))\r
564                                 cvar_set(t, s); // this is a wrapper and is always temporary anyway; no need to backup old values then\r
565                         else\r
566                                 cvar_settemp(t, s);\r
567                 }\r
568                 else\r
569                 {\r
570                         dprint("Applying temporary client setting ", t, " := ", s, "\n");\r
571                         MapInfo_Map_clientstuff = strcat(\r
572                                         MapInfo_Map_clientstuff, "cl_cmd settemp \"", t, "\" \"", s, "\"\n"\r
573                                         );\r
574                 }\r
575         }\r
576 }\r
577 \r
578 // load info about a map by name into the MapInfo_Map_* globals\r
579 float MapInfo_Get_ByName(string pFilename, float pAllowGenerate, float pGametypeToSet)\r
580 {\r
581         string fn;\r
582         string s, t;\r
583         float fh, fh2;\r
584         float r, f, n, i;\r
585         string acl;\r
586 \r
587         acl = MAPINFO_SETTEMP_ACL_USER;\r
588 \r
589         if(strstrofs(pFilename, "/", 0) >= 0)\r
590         {\r
591                 print("Invalid character in map name, ignored\n");\r
592                 return 0;\r
593         }\r
594 \r
595         if(pGametypeToSet == 0)\r
596                 if(MapInfo_Cache_Retrieve(pFilename))\r
597                         return 1;\r
598 \r
599         r = 1;\r
600 \r
601         MapInfo_Map_bspname = pFilename;\r
602 \r
603         // default all generic fields so they have "good" values in case something fails\r
604         fn = strcat("maps/", pFilename, ".mapinfo");\r
605         fh = fopen(fn, FILE_READ);\r
606         if(fh < 0)\r
607         {\r
608                 if(!pAllowGenerate)\r
609                         return 0;\r
610                 _MapInfo_Map_Reset();\r
611                 r = _MapInfo_Generate(pFilename);\r
612                 if(!r)\r
613                         return 0;\r
614                 fh = fopen(fn, FILE_WRITE);\r
615                 fputs(fh, strcat("title ", MapInfo_Map_title, "\n"));\r
616                 fputs(fh, strcat("description ", MapInfo_Map_description, "\n"));\r
617                 fputs(fh, strcat("author ", MapInfo_Map_author, "\n"));\r
618                 if(_MapInfo_Map_worldspawn_music != "")\r
619                 {\r
620                         if(\r
621                                 substring(_MapInfo_Map_worldspawn_music, strlen(_MapInfo_Map_worldspawn_music) - 4, 4) == ".wav"\r
622                                 ||\r
623                                 substring(_MapInfo_Map_worldspawn_music, strlen(_MapInfo_Map_worldspawn_music) - 4, 4) == ".ogg"\r
624                         )\r
625                                 fputs(fh, strcat("cdtrack ", substring(_MapInfo_Map_worldspawn_music, 0, strlen(_MapInfo_Map_worldspawn_music) - 4), "\n"));\r
626                         else\r
627                                 fputs(fh, strcat("cdtrack ", _MapInfo_Map_worldspawn_music, "\n"));\r
628                 }\r
629                 else if(_MapInfo_Map_worldspawn_music)\r
630                 {\r
631                         n = tokenize_console(cvar_string("g_cdtracks_remaplist"));\r
632                         s = strcat(" ", cvar_string("g_cdtracks_dontusebydefault"), " ");\r
633                         for(;;)\r
634                         {\r
635                                 i = floor(random() * n);\r
636                                 if(strstrofs(s, strcat(" ", argv(i), " "), 0) < 0)\r
637                                         break;\r
638                         }\r
639                         fputs(fh, strcat("cdtrack ", ftos(i + 1), "\n"));\r
640                 }\r
641                 if(MapInfo_Map_supportedFeatures & MAPINFO_FEATURE_WEAPONS)\r
642                         fputs(fh, "has weapons\n");\r
643                 else\r
644                         fputs(fh, "// uncomment this if you added weapon pickups: has weapons\n");\r
645                 if(MapInfo_Map_flags & MAPINFO_FLAG_FRUSTRATING)\r
646                         fputs(fh, "frustrating\n");\r
647                 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_DEATHMATCH)      fputs(fh, "type dm 30 20\n");\r
648                 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_TEAM_DEATHMATCH) fputs(fh, "type tdm 50 20 2\n");\r
649                 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_DOMINATION)      fputs(fh, "type dom 200 20\n");\r
650                 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_CTF)             fputs(fh, "type ctf 300 20 10\n");\r
651                 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_LMS)             fputs(fh, "type lms 9 20\n");\r
652                 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_ARENA)           fputs(fh, "type arena 10 20\n");\r
653                 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_CA)              fputs(fh, "type ca 10 20\n");\r
654                 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_KEYHUNT)         fputs(fh, "type kh 1000 20 3\n");\r
655                 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_ASSAULT)         fputs(fh, "type as 20\n");\r
656                 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_RACE)            fputs(fh, "type rc 20 5 7 15\n");\r
657                 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_ONSLAUGHT)       fputs(fh, "type ons 20\n");\r
658                 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_CTS)             fputs(fh, "type cts 20 -1\n");\r
659                 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_RPG)             fputs(fh, "type rpg -1\n");\r
660 \r
661                 fh2 = fopen(strcat("scripts/", pFilename, ".arena"), FILE_READ);\r
662                 if(fh2 >= 0)\r
663                 {\r
664                         fclose(fh2);\r
665                         fputs(fh, "settemp_for_type all sv_q3acompat_machineshotgunswap 1\n");\r
666                 }\r
667 \r
668                 fputs(fh, "// optional: fog density red green blue alpha mindist maxdist\n");\r
669                 fputs(fh, "// optional: settemp_for_type (all|gametypename) cvarname value\n");\r
670                 fputs(fh, "// optional: clientsettemp_for_type (all|gametypename) cvarname value\n");\r
671                 fputs(fh, "// optional: size mins_x mins_y mins_z maxs_x maxs_y maxs_z\n");\r
672                 fputs(fh, "// optional: hidden\n");\r
673 \r
674                 fclose(fh);\r
675                 r = 2;\r
676                 // return r;\r
677                 fh = fopen(fn, FILE_READ);\r
678                 if(fh < 0)\r
679                         error("... but I just wrote it!");\r
680         }\r
681 \r
682         _MapInfo_Map_Reset();\r
683         for(;;)\r
684         {\r
685                 if not((s = fgets(fh)))\r
686                         break;\r
687 \r
688                 // catch different sorts of comments\r
689                 if(s == "")                    // empty lines\r
690                         continue;\r
691                 if(substring(s, 0, 1) == "#")  // UNIX style\r
692                         continue;\r
693                 if(substring(s, 0, 2) == "//") // C++ style\r
694                         continue;\r
695                 if(substring(s, 0, 1) == "_")  // q3map style\r
696                         continue;\r
697 \r
698                 t = car(s); s = cdr(s);\r
699                 if(t == "title")\r
700                         MapInfo_Map_title = s;\r
701                 else if(t == "description")\r
702                         MapInfo_Map_description = s;\r
703                 else if(t == "author")\r
704                         MapInfo_Map_author = s;\r
705                 else if(t == "has")\r
706                 {\r
707                         t = car(s); s = cdr(s);\r
708                         if     (t == "weapons") MapInfo_Map_supportedFeatures |= MAPINFO_FEATURE_WEAPONS;\r
709                         else\r
710                                 dprint("Map ", pFilename, " supports unknown feature ", t, ", ignored\n");\r
711                 }\r
712                 else if(t == "hidden")\r
713                 {\r
714                         MapInfo_Map_flags |= MAPINFO_FLAG_HIDDEN;\r
715                 }\r
716                 else if(t == "forbidden")\r
717                 {\r
718                         MapInfo_Map_flags |= MAPINFO_FLAG_FORBIDDEN;\r
719                 }\r
720                 else if(t == "frustrating")\r
721                 {\r
722                         MapInfo_Map_flags |= MAPINFO_FLAG_FRUSTRATING;\r
723                 }\r
724                 else if(t == "type")\r
725                 {\r
726                         t = car(s); s = cdr(s);\r
727                         f = MapInfo_Type_FromString(t);\r
728                         if(f)\r
729                                 _MapInfo_Map_ApplyGametype (s, pGametypeToSet, f);\r
730                         else\r
731                                 dprint("Map ", pFilename, " supports unknown game type ", t, ", ignored\n");\r
732                 }\r
733                 else if(t == "size")\r
734                 {\r
735                         float a, b, c, d, e;\r
736                         t = car(s); s = cdr(s); a = stof(t);\r
737                         t = car(s); s = cdr(s); b = stof(t);\r
738                         t = car(s); s = cdr(s); c = stof(t);\r
739                         t = car(s); s = cdr(s); d = stof(t);\r
740                         t = car(s); s = cdr(s); e = stof(t);\r
741                         if(s == "")\r
742                                 print("Map ", pFilename, " contains an incorrect size line (not enough params), syntax: size mins_x mins_y mins_z maxs_x maxs_y maxs_z\n");\r
743                         else\r
744                         {\r
745                                 t = car(s); s = cdr(s); f = stof(t);\r
746                                 if(s != "")\r
747                                         print("Map ", pFilename, " contains an incorrect size line (too many params), syntax: size mins_x mins_y mins_z maxs_x maxs_y maxs_z\n");\r
748                                 else\r
749                                 {\r
750                                         if(a >= d || b >= e || c >= f)\r
751                                                 print("Map ", pFilename, " contains an incorrect size line, mins have to be < maxs\n");\r
752                                         else\r
753                                         {\r
754                                                 MapInfo_Map_mins_x = a;\r
755                                                 MapInfo_Map_mins_y = b;\r
756                                                 MapInfo_Map_mins_z = c;\r
757                                                 MapInfo_Map_maxs_x = d;\r
758                                                 MapInfo_Map_maxs_y = e;\r
759                                                 MapInfo_Map_maxs_z = f;\r
760                                         }\r
761                                 }\r
762                         }\r
763                 }\r
764                 else if(t == "settemp_for_type")\r
765                 {\r
766                         t = car(s); s = cdr(s);\r
767                         if((f = MapInfo_Type_FromString(t)))\r
768                         {\r
769                                 if(f & pGametypeToSet)\r
770                                 {\r
771                                         _MapInfo_Parse_Settemp(pFilename, acl, 0, s, 1);\r
772                                 }\r
773                         }\r
774                         else\r
775                         {\r
776                                 dprint("Map ", pFilename, " has a setting for unknown game type ", t, ", ignored\n");\r
777                         }\r
778                 }\r
779                 else if(t == "clientsettemp_for_type")\r
780                 {\r
781                         t = car(s); s = cdr(s);\r
782                         if((f = MapInfo_Type_FromString(t)))\r
783                         {\r
784                                 if(f & pGametypeToSet)\r
785                                 {\r
786                                         _MapInfo_Parse_Settemp(pFilename, acl, 1, s, 1);\r
787                                 }\r
788                         }\r
789                         else\r
790                         {\r
791                                 dprint("Map ", pFilename, " has a client setting for unknown game type ", t, ", ignored\n");\r
792                         }\r
793                 }\r
794                 else if(t == "fog")\r
795                 {\r
796                         if not(cvar_value_issafe(t))\r
797                                 print("Map ", pFilename, " contains a potentially harmful fog setting, ignored\n");\r
798                         else\r
799                                 MapInfo_Map_fog = s;\r
800                 }\r
801                 else if(t == "cdtrack")\r
802                 {\r
803                         if(pGametypeToSet)\r
804                         {\r
805                                 if not(cvar_value_issafe(t))\r
806                                         print("Map ", pFilename, " contains a potentially harmful cdtrack, ignored\n");\r
807                                 else\r
808                                         MapInfo_Map_clientstuff = strcat(\r
809                                                 MapInfo_Map_clientstuff, "cd loop \"", s, "\"\n"\r
810                                         );\r
811                         }\r
812                 }\r
813                 else\r
814                         dprint("Map ", pFilename, " provides unknown info item ", t, ", ignored\n");\r
815         }\r
816         fclose(fh);\r
817 \r
818         if(pGametypeToSet)\r
819         {\r
820                 if(!(MapInfo_Map_supportedGametypes & pGametypeToSet))\r
821                 {\r
822                         print("Can't select the requested game type. Trying anyway with stupid settings.\n");\r
823                         _MapInfo_Map_ApplyGametype("0 0 0", pGametypeToSet, MAPINFO_TYPE_DEATHMATCH);\r
824                 }\r
825         }\r
826 \r
827         MapInfo_Cache_Store();\r
828         if(MapInfo_Map_supportedGametypes != 0)\r
829                 return r;\r
830         dprint("Map ", pFilename, " supports no game types, ignored\n");\r
831         return 0;\r
832 }\r
833 \r
834 float MapInfo_FindName(string s)\r
835 {\r
836         // if there is exactly one map of prefix s, return it\r
837         // if not, return the null string\r
838         // note that DP sorts glob results... so I can use a binary search\r
839         float l, r, m, cmp;\r
840         l = 0;\r
841         r = MapInfo_count;\r
842         // invariants: r is behind s, l-1 is equal or before\r
843         while(l != r)\r
844         {\r
845                 m = floor((l + r) / 2);\r
846                 MapInfo_FindName_match = _MapInfo_GlobItem(MapInfo_FilterList_Lookup(m));\r
847                 cmp = strcasecmp(MapInfo_FindName_match, s);\r
848                 if(cmp == 0)\r
849                         return m; // found and good\r
850                 if(cmp < 0)\r
851                         l = m + 1; // l-1 is before s\r
852                 else\r
853                         r = m; // behind s\r
854         }\r
855         MapInfo_FindName_match = _MapInfo_GlobItem(MapInfo_FilterList_Lookup(l));\r
856         MapInfo_FindName_firstResult = l;\r
857         // r == l, so: l is behind s, l-1 is before\r
858         // SO: if there is any, l is the one with the right prefix\r
859         //     and l+1 may be one too\r
860         if(l == MapInfo_count)\r
861         {\r
862                 MapInfo_FindName_match = string_null;\r
863                 MapInfo_FindName_firstResult = -1;\r
864                 return -1; // no MapInfo_FindName_match, behind last item\r
865         }\r
866         if(!startsWithNocase(MapInfo_FindName_match, s))\r
867         {\r
868                 MapInfo_FindName_match = string_null;\r
869                 MapInfo_FindName_firstResult = -1;\r
870                 return -1; // wrong prefix\r
871         }\r
872         if(l == MapInfo_count - 1)\r
873                 return l; // last one, nothing can follow => unique\r
874         if(startsWithNocase(_MapInfo_GlobItem(MapInfo_FilterList_Lookup(l + 1)), s))\r
875         {\r
876                 MapInfo_FindName_match = string_null;\r
877                 return -1; // ambigous MapInfo_FindName_match\r
878         }\r
879         return l;\r
880 }\r
881 \r
882 string MapInfo_FixName(string s)\r
883 {\r
884         MapInfo_FindName(s);\r
885         return MapInfo_FindName_match;\r
886 }\r
887 \r
888 float MapInfo_CurrentFeatures()\r
889 {\r
890         float req;\r
891         req = 0;\r
892         if(!(cvar("g_lms")) || !cvar("g_pickup_items") || cvar("g_race") || cvar("g_cts"))\r
893                 req |= MAPINFO_FEATURE_WEAPONS;\r
894         return req;\r
895 }\r
896 \r
897 float MapInfo_CurrentGametype()\r
898 {\r
899         if(cvar("g_domination"))\r
900                 return MAPINFO_TYPE_DOMINATION;\r
901         else if(cvar("g_ctf"))\r
902                 return MAPINFO_TYPE_CTF;\r
903         else if(cvar("g_tdm"))\r
904                 return MAPINFO_TYPE_TEAM_DEATHMATCH;\r
905         else if(cvar("g_assault"))\r
906                 return MAPINFO_TYPE_ASSAULT;\r
907         else if(cvar("g_lms"))\r
908                 return MAPINFO_TYPE_LMS;\r
909         else if(cvar("g_arena"))\r
910                 return MAPINFO_TYPE_ARENA;\r
911         else if(cvar("g_ca"))\r
912                 return MAPINFO_TYPE_CA; \r
913         else if(cvar("g_keyhunt"))\r
914                 return MAPINFO_TYPE_KEYHUNT;\r
915         else if(cvar("g_onslaught"))\r
916                 return MAPINFO_TYPE_ONSLAUGHT;\r
917         else if(cvar("g_race"))\r
918                 return MAPINFO_TYPE_RACE;\r
919         else if(cvar("g_cts"))\r
920                 return MAPINFO_TYPE_CTS;\r
921         else if(cvar("g_rpg"))\r
922                 return MAPINFO_TYPE_RPG;\r
923         else\r
924                 return MAPINFO_TYPE_DEATHMATCH;\r
925 }\r
926 \r
927 float _MapInfo_CheckMap(string s) // returns 0 if the map can't be played with the current settings, 1 otherwise\r
928 {\r
929         if(!MapInfo_Get_ByName(s, 1, 0))\r
930                 return 0;\r
931         if((MapInfo_Map_supportedGametypes & MapInfo_CurrentGametype()) == 0)\r
932                 return 0;\r
933         if((MapInfo_Map_supportedFeatures & MapInfo_CurrentFeatures()) != MapInfo_CurrentFeatures())\r
934                 return 0;\r
935         return 1;\r
936 }\r
937 \r
938 float MapInfo_CheckMap(string s) // returns 0 if the map can't be played with the current settings, 1 otherwise\r
939 {\r
940         float r;\r
941         r = _MapInfo_CheckMap(s);\r
942         MapInfo_ClearTemps();\r
943         return r;\r
944 }\r
945 \r
946 string MapInfo_GetGameTypeCvar(float t)\r
947 {\r
948         switch(t)\r
949         {\r
950                 case MAPINFO_TYPE_DEATHMATCH: return "g_dm";\r
951                 case MAPINFO_TYPE_TEAM_DEATHMATCH: return "g_tdm";\r
952                 case MAPINFO_TYPE_DOMINATION: return "g_domination";\r
953                 case MAPINFO_TYPE_CTF: return "g_ctf";\r
954                 case MAPINFO_TYPE_LMS: return "g_lms";\r
955                 case MAPINFO_TYPE_ARENA: return "g_arena";\r
956                 case MAPINFO_TYPE_CA: return "g_ca";\r
957                 case MAPINFO_TYPE_KEYHUNT: return "g_kh";\r
958                 case MAPINFO_TYPE_ASSAULT: return "g_assault";\r
959                 case MAPINFO_TYPE_ONSLAUGHT: return "g_onslaught";\r
960                 case MAPINFO_TYPE_RACE: return "g_race";\r
961                 case MAPINFO_TYPE_CTS: return "g_cts";\r
962                 case MAPINFO_TYPE_RPG: return "g_rpg";\r
963                 default: return "";\r
964         }\r
965 }\r
966 \r
967 void MapInfo_SwitchGameType(float t)\r
968 {\r
969         cvar_set("gamecfg",      "0");\r
970         cvar_set("g_dm",         (t == MAPINFO_TYPE_DEATHMATCH)      ? "1" : "0");\r
971         cvar_set("g_tdm",        (t == MAPINFO_TYPE_TEAM_DEATHMATCH) ? "1" : "0");\r
972         cvar_set("g_domination", (t == MAPINFO_TYPE_DOMINATION)      ? "1" : "0");\r
973         cvar_set("g_ctf",        (t == MAPINFO_TYPE_CTF)             ? "1" : "0");\r
974         cvar_set("g_lms",        (t == MAPINFO_TYPE_LMS)             ? "1" : "0");\r
975         cvar_set("g_arena",      (t == MAPINFO_TYPE_ARENA)           ? "1" : "0");\r
976         cvar_set("g_ca",         (t == MAPINFO_TYPE_CA)              ? "1" : "0");\r
977         cvar_set("g_keyhunt",    (t == MAPINFO_TYPE_KEYHUNT)         ? "1" : "0");\r
978         cvar_set("g_assault",    (t == MAPINFO_TYPE_ASSAULT)         ? "1" : "0");\r
979         cvar_set("g_onslaught",  (t == MAPINFO_TYPE_ONSLAUGHT)       ? "1" : "0");\r
980         cvar_set("g_race",       (t == MAPINFO_TYPE_RACE)            ? "1" : "0");\r
981         cvar_set("g_cts",        (t == MAPINFO_TYPE_CTS)             ? "1" : "0");\r
982         cvar_set("g_rpg",        (t == MAPINFO_TYPE_RPG)             ? "1" : "0");\r
983 }\r
984 \r
985 void MapInfo_LoadMap(string s)\r
986 {\r
987         MapInfo_Map_supportedGametypes = 0;\r
988         // we shouldn't need this, as LoadMapSettings already fixes the gametype\r
989         //if(!MapInfo_CheckMap(s))\r
990         //{\r
991         //      print("EMERGENCY: can't play the selected map in the given game mode. Falling back to DM.\n");\r
992         //      MapInfo_SwitchGameType(MAPINFO_TYPE_DEATHMATCH);\r
993         //}\r
994         localcmd(strcat("\nsettemp_restore\nchangelevel ", s, "\n"));\r
995 }\r
996 \r
997 string MapInfo_ListAllowedMaps(float pRequiredFlags, float pForbiddenFlags)\r
998 {\r
999         string out;\r
1000         float i;\r
1001 \r
1002         // to make absolutely sure:\r
1003         MapInfo_Enumerate();\r
1004         MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), pRequiredFlags, pForbiddenFlags, 0);\r
1005 \r
1006         out = "";\r
1007         for(i = 0; i < MapInfo_count; ++i)\r
1008                 out = strcat(out, " ", _MapInfo_GlobItem(MapInfo_FilterList_Lookup(i)));\r
1009         return substring(out, 1, strlen(out) - 1);\r
1010 }\r
1011 \r
1012 void MapInfo_LoadMapSettings(string s) // to be called from worldspawn\r
1013 {\r
1014         float t, t0;\r
1015         if(!_MapInfo_CheckMap(s)) // with underscore, it keeps temps\r
1016         {\r
1017                 if(MapInfo_Map_supportedGametypes == 0)\r
1018                 {\r
1019                         print("Mapinfo system is not functional at all. Assuming deathmatch.\n");\r
1020                         MapInfo_Map_supportedGametypes = MAPINFO_TYPE_DEATHMATCH;\r
1021                 }\r
1022 \r
1023                 t = 1;\r
1024                 while(!(MapInfo_Map_supportedGametypes & 1))\r
1025                 {\r
1026                         t *= 2;\r
1027                         MapInfo_Map_supportedGametypes = floor(MapInfo_Map_supportedGametypes / 2);\r
1028                 }\r
1029                 // t is now a supported mode!\r
1030                 t0 = MapInfo_CurrentGametype();\r
1031                 if(cvar("g_mapinfo_allow_unsupported_modes_and_let_stuff_break"))\r
1032                 {\r
1033                         print("EMERGENCY: can't play the selected map in the given game mode. Working with only the override settings.\n");\r
1034                         cvar_set("timelimit", "0");\r
1035                         cvar_set("fraglimit", "0");\r
1036                         cvar_set("g_tdm_teams", "2");\r
1037                         cvar_set("g_keyhunt_teams", "3");\r
1038                         cvar_set("g_race_qualifying_timelimit", "0");\r
1039                         cvar_set("leadlimit", "0");\r
1040                 }\r
1041                 else\r
1042                 {\r
1043                         print("EMERGENCY: can't play the selected map in the given game mode. Falling back to a supported mode.\n");\r
1044                         MapInfo_SwitchGameType(t);\r
1045                 }\r
1046         }\r
1047         cvar_settemp_restore();\r
1048         MapInfo_Get_ByName(s, 1, MapInfo_CurrentGametype());\r
1049 }\r
1050 \r
1051 void MapInfo_ClearTemps()\r
1052 {\r
1053         MapInfo_Map_bspname = string_null;\r
1054         MapInfo_Map_title = string_null;\r
1055         MapInfo_Map_description = string_null;\r
1056         MapInfo_Map_author = string_null;\r
1057         MapInfo_Map_clientstuff = string_null;\r
1058         MapInfo_Map_supportedGametypes = 0;\r
1059         MapInfo_Map_supportedFeatures = 0;\r
1060 }\r
1061 \r
1062 void MapInfo_Shutdown()\r
1063 {\r
1064         MapInfo_ClearTemps();\r
1065         MapInfo_Filter_Free();\r
1066         MapInfo_Cache_Destroy();\r
1067         if(_MapInfo_globopen)\r
1068         {\r
1069                 search_end(_MapInfo_globhandle);\r
1070                 _MapInfo_globhandle = -1;\r
1071                 _MapInfo_globopen = FALSE;\r
1072         }\r
1073 }\r
1074 \r
1075 float MapInfo_ForbiddenFlags()\r
1076 {\r
1077         float f;\r
1078         f = MAPINFO_FLAG_FORBIDDEN;\r
1079 \r
1080 #ifndef MENUQC\r
1081         if not(cvar("g_maplist_allow_hidden"))\r
1082 #endif\r
1083                 f |= MAPINFO_FLAG_HIDDEN;\r
1084 \r
1085         if not(cvar("g_maplist_allow_frustrating"))\r
1086                 f |= MAPINFO_FLAG_FRUSTRATING;\r
1087 \r
1088         return f;\r
1089 }\r
1090 \r
1091 float MapInfo_RequiredFlags()\r
1092 {\r
1093         float f;\r
1094         f = 0;\r
1095 \r
1096         if(cvar("g_maplist_allow_frustrating") > 1)\r
1097                 f |= MAPINFO_FLAG_FRUSTRATING;\r
1098 \r
1099         return f;\r
1100 }\r