]> de.git.xonotic.org Git - voretournament/voretournament.git/blob - data/qcsrc/common/mapinfo.qc
Initial checkout of Vore Tournament 0.1.alpha.
[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) // 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 == "all")     return MAPINFO_TYPE_ALL;\r
474         else                    return 0;\r
475 }\r
476 \r
477 void _MapInfo_Parse_Settemp(string pFilename, string acl, float type, string s, float recurse)\r
478 {\r
479         string t;\r
480         float fh, o;\r
481         t = car(s); s = cdr(s);\r
482 \r
483         // limited support of "" and comments\r
484         //   remove trailing and leading " of t\r
485         if(substring(t, 0, 1) == "\"")\r
486         {\r
487                 if(substring(t, -1, 1) == "\"")\r
488                         t = substring(t, 1, -2);\r
489         }\r
490 \r
491         //   remove leading " of s\r
492         if(substring(s, 0, 1) == "\"")\r
493         {\r
494                 s = substring(s, 1, -1);\r
495         }\r
496         //   remove trailing " of s, and all that follows (cvar description)\r
497         o = strstrofs(s, "\"", 0);\r
498         if(o >= 0)\r
499                 s = substring(s, 0, o);\r
500         \r
501         //   remove // comments\r
502         o = strstrofs(s, "//", 0);\r
503         if(o >= 0)\r
504                 s = substring(s, 0, o);\r
505         \r
506         //   remove trailing spaces\r
507         while(substring(s, -1, 1) == " ")\r
508                 s = substring(s, 0, -2);\r
509 \r
510         if(t == "#include")\r
511         {\r
512                 if(recurse > 0)\r
513                 {\r
514                         fh = fopen(s, FILE_READ);\r
515                         if(fh < 0)\r
516                                 print("Map ", pFilename, " references not existing config file ", s, "\n");\r
517                         else\r
518                         {\r
519                                 for(;;)\r
520                                 {\r
521                                         if not((s = fgets(fh)))\r
522                                                 break;\r
523 \r
524                                         // catch different sorts of comments\r
525                                         if(s == "")                    // empty lines\r
526                                                 continue;\r
527                                         if(substring(s, 0, 1) == "#")  // UNIX style\r
528                                                 continue;\r
529                                         if(substring(s, 0, 2) == "//") // C++ style\r
530                                                 continue;\r
531                                         if(substring(s, 0, 1) == "_")  // q3map style\r
532                                                 continue;\r
533 \r
534                                         if(substring(s, 0, 4) == "set ")\r
535                                                 s = substring(s, 4, -1);\r
536                                         if(substring(s, 0, 5) == "seta ")\r
537                                                 s = substring(s, 5, -1);\r
538 \r
539                                         _MapInfo_Parse_Settemp(pFilename, acl, type, s, recurse - 1);\r
540                                 }\r
541                                 fclose(fh);\r
542                         }\r
543                 }\r
544                 else\r
545                         print("Map ", pFilename, " uses too many levels of inclusion\n");\r
546         }\r
547         else if(t == "")\r
548                 print("Map ", pFilename, " contains a potentially harmful setting, ignored\n");\r
549         else if not(cvar_value_issafe(t))\r
550                 print("Map ", pFilename, " contains a potentially harmful setting, ignored\n");\r
551         else if not (cvar_value_issafe(s))\r
552                 print("Map ", pFilename, " contains a potentially harmful setting, ignored\n");\r
553         else if(matchacl(MAPINFO_SETTEMP_ACL_SYSTEM, t) <= 0)\r
554                 print("Map ", pFilename, " contains a potentially harmful setting, ignored\n");\r
555         else if(matchacl(acl, t) <= 0)\r
556                 print("Map ", pFilename, " contains a denied setting, ignored\n");\r
557         else\r
558         {\r
559                 if(type == 0) // server set\r
560                 {\r
561                         dprint("Applying temporary setting ", t, " := ", s, "\n");\r
562                         if(cvar("g_campaign"))\r
563                                 cvar_set(t, s); // this is a wrapper and is always temporary anyway; no need to backup old values then\r
564                         else\r
565                                 cvar_settemp(t, s);\r
566                 }\r
567                 else\r
568                 {\r
569                         dprint("Applying temporary client setting ", t, " := ", s, "\n");\r
570                         MapInfo_Map_clientstuff = strcat(\r
571                                         MapInfo_Map_clientstuff, "cl_cmd settemp \"", t, "\" \"", s, "\"\n"\r
572                                         );\r
573                 }\r
574         }\r
575 }\r
576 \r
577 // load info about a map by name into the MapInfo_Map_* globals\r
578 float MapInfo_Get_ByName(string pFilename, float pAllowGenerate, float pGametypeToSet)\r
579 {\r
580         string fn;\r
581         string s, t;\r
582         float fh, fh2;\r
583         float r, f, n, i;\r
584         string acl;\r
585 \r
586         acl = MAPINFO_SETTEMP_ACL_USER;\r
587 \r
588         if(strstrofs(pFilename, "/", 0) >= 0)\r
589         {\r
590                 print("Invalid character in map name, ignored\n");\r
591                 return 0;\r
592         }\r
593 \r
594         if(pGametypeToSet == 0)\r
595                 if(MapInfo_Cache_Retrieve(pFilename))\r
596                         return 1;\r
597 \r
598         r = 1;\r
599 \r
600         MapInfo_Map_bspname = pFilename;\r
601 \r
602         // default all generic fields so they have "good" values in case something fails\r
603         fn = strcat("maps/", pFilename, ".mapinfo");\r
604         fh = fopen(fn, FILE_READ);\r
605         if(fh < 0)\r
606         {\r
607                 if(!pAllowGenerate)\r
608                         return 0;\r
609                 _MapInfo_Map_Reset();\r
610                 r = _MapInfo_Generate(pFilename);\r
611                 if(!r)\r
612                         return 0;\r
613                 fh = fopen(fn, FILE_WRITE);\r
614                 fputs(fh, strcat("title ", MapInfo_Map_title, "\n"));\r
615                 fputs(fh, strcat("description ", MapInfo_Map_description, "\n"));\r
616                 fputs(fh, strcat("author ", MapInfo_Map_author, "\n"));\r
617                 if(_MapInfo_Map_worldspawn_music != "")\r
618                 {\r
619                         if(\r
620                                 substring(_MapInfo_Map_worldspawn_music, strlen(_MapInfo_Map_worldspawn_music) - 4, 4) == ".wav"\r
621                                 ||\r
622                                 substring(_MapInfo_Map_worldspawn_music, strlen(_MapInfo_Map_worldspawn_music) - 4, 4) == ".ogg"\r
623                         )\r
624                                 fputs(fh, strcat("cdtrack ", substring(_MapInfo_Map_worldspawn_music, 0, strlen(_MapInfo_Map_worldspawn_music) - 4), "\n"));\r
625                         else\r
626                                 fputs(fh, strcat("cdtrack ", _MapInfo_Map_worldspawn_music, "\n"));\r
627                 }\r
628                 else if(_MapInfo_Map_worldspawn_music)\r
629                 {\r
630                         n = tokenize_console(cvar_string("g_cdtracks_remaplist"));\r
631                         s = strcat(" ", cvar_string("g_cdtracks_dontusebydefault"), " ");\r
632                         for(;;)\r
633                         {\r
634                                 i = floor(random() * n);\r
635                                 if(strstrofs(s, strcat(" ", argv(i), " "), 0) < 0)\r
636                                         break;\r
637                         }\r
638                         fputs(fh, strcat("cdtrack ", ftos(i + 1), "\n"));\r
639                 }\r
640                 if(MapInfo_Map_supportedFeatures & MAPINFO_FEATURE_WEAPONS)\r
641                         fputs(fh, "has weapons\n");\r
642                 else\r
643                         fputs(fh, "// uncomment this if you added weapon pickups: has weapons\n");\r
644                 if(MapInfo_Map_flags & MAPINFO_FLAG_FRUSTRATING)\r
645                         fputs(fh, "frustrating\n");\r
646                 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_DEATHMATCH)      fputs(fh, "type dm 30 20\n");\r
647                 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_TEAM_DEATHMATCH) fputs(fh, "type tdm 50 20 2\n");\r
648                 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_DOMINATION)      fputs(fh, "type dom 200 20\n");\r
649                 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_CTF)             fputs(fh, "type ctf 300 20 10\n");\r
650                 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_LMS)             fputs(fh, "type lms 9 20\n");\r
651                 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_ARENA)           fputs(fh, "type arena 10 20\n");\r
652                 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_CA)              fputs(fh, "type ca 10 20\n");\r
653                 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_KEYHUNT)         fputs(fh, "type kh 1000 20 3\n");\r
654                 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_ASSAULT)         fputs(fh, "type as 20\n");\r
655                 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_RACE)            fputs(fh, "type rc 20 5 7 15\n");\r
656                 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_ONSLAUGHT)       fputs(fh, "type ons 20\n");\r
657                 if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_CTS)             fputs(fh, "type cts 20 -1\n");\r
658 \r
659                 fh2 = fopen(strcat("scripts/", pFilename, ".arena"), FILE_READ);\r
660                 if(fh2 >= 0)\r
661                 {\r
662                         fclose(fh2);\r
663                         fputs(fh, "settemp_for_type all sv_q3acompat_machineshotgunswap 1\n");\r
664                 }\r
665 \r
666                 fputs(fh, "// optional: fog density red green blue alpha mindist maxdist\n");\r
667                 fputs(fh, "// optional: settemp_for_type (all|gametypename) cvarname value\n");\r
668                 fputs(fh, "// optional: clientsettemp_for_type (all|gametypename) cvarname value\n");\r
669                 fputs(fh, "// optional: size mins_x mins_y mins_z maxs_x maxs_y maxs_z\n");\r
670                 fputs(fh, "// optional: hidden\n");\r
671 \r
672                 fclose(fh);\r
673                 r = 2;\r
674                 // return r;\r
675                 fh = fopen(fn, FILE_READ);\r
676                 if(fh < 0)\r
677                         error("... but I just wrote it!");\r
678         }\r
679 \r
680         _MapInfo_Map_Reset();\r
681         for(;;)\r
682         {\r
683                 if not((s = fgets(fh)))\r
684                         break;\r
685 \r
686                 // catch different sorts of comments\r
687                 if(s == "")                    // empty lines\r
688                         continue;\r
689                 if(substring(s, 0, 1) == "#")  // UNIX style\r
690                         continue;\r
691                 if(substring(s, 0, 2) == "//") // C++ style\r
692                         continue;\r
693                 if(substring(s, 0, 1) == "_")  // q3map style\r
694                         continue;\r
695 \r
696                 t = car(s); s = cdr(s);\r
697                 if(t == "title")\r
698                         MapInfo_Map_title = s;\r
699                 else if(t == "description")\r
700                         MapInfo_Map_description = s;\r
701                 else if(t == "author")\r
702                         MapInfo_Map_author = s;\r
703                 else if(t == "has")\r
704                 {\r
705                         t = car(s); s = cdr(s);\r
706                         if     (t == "weapons") MapInfo_Map_supportedFeatures |= MAPINFO_FEATURE_WEAPONS;\r
707                         else\r
708                                 dprint("Map ", pFilename, " supports unknown feature ", t, ", ignored\n");\r
709                 }\r
710                 else if(t == "hidden")\r
711                 {\r
712                         MapInfo_Map_flags |= MAPINFO_FLAG_HIDDEN;\r
713                 }\r
714                 else if(t == "forbidden")\r
715                 {\r
716                         MapInfo_Map_flags |= MAPINFO_FLAG_FORBIDDEN;\r
717                 }\r
718                 else if(t == "frustrating")\r
719                 {\r
720                         MapInfo_Map_flags |= MAPINFO_FLAG_FRUSTRATING;\r
721                 }\r
722                 else if(t == "type")\r
723                 {\r
724                         t = car(s); s = cdr(s);\r
725                         f = MapInfo_Type_FromString(t);\r
726                         if(f)\r
727                                 _MapInfo_Map_ApplyGametype (s, pGametypeToSet, f);\r
728                         else\r
729                                 dprint("Map ", pFilename, " supports unknown game type ", t, ", ignored\n");\r
730                 }\r
731                 else if(t == "size")\r
732                 {\r
733                         float a, b, c, d, e;\r
734                         t = car(s); s = cdr(s); a = stof(t);\r
735                         t = car(s); s = cdr(s); b = stof(t);\r
736                         t = car(s); s = cdr(s); c = stof(t);\r
737                         t = car(s); s = cdr(s); d = stof(t);\r
738                         t = car(s); s = cdr(s); e = stof(t);\r
739                         if(s == "")\r
740                                 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
741                         else\r
742                         {\r
743                                 t = car(s); s = cdr(s); f = stof(t);\r
744                                 if(s != "")\r
745                                         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
746                                 else\r
747                                 {\r
748                                         if(a >= d || b >= e || c >= f)\r
749                                                 print("Map ", pFilename, " contains an incorrect size line, mins have to be < maxs\n");\r
750                                         else\r
751                                         {\r
752                                                 MapInfo_Map_mins_x = a;\r
753                                                 MapInfo_Map_mins_y = b;\r
754                                                 MapInfo_Map_mins_z = c;\r
755                                                 MapInfo_Map_maxs_x = d;\r
756                                                 MapInfo_Map_maxs_y = e;\r
757                                                 MapInfo_Map_maxs_z = f;\r
758                                         }\r
759                                 }\r
760                         }\r
761                 }\r
762                 else if(t == "settemp_for_type")\r
763                 {\r
764                         t = car(s); s = cdr(s);\r
765                         if((f = MapInfo_Type_FromString(t)))\r
766                         {\r
767                                 if(f & pGametypeToSet)\r
768                                 {\r
769                                         _MapInfo_Parse_Settemp(pFilename, acl, 0, s, 1);\r
770                                 }\r
771                         }\r
772                         else\r
773                         {\r
774                                 dprint("Map ", pFilename, " has a setting for unknown game type ", t, ", ignored\n");\r
775                         }\r
776                 }\r
777                 else if(t == "clientsettemp_for_type")\r
778                 {\r
779                         t = car(s); s = cdr(s);\r
780                         if((f = MapInfo_Type_FromString(t)))\r
781                         {\r
782                                 if(f & pGametypeToSet)\r
783                                 {\r
784                                         _MapInfo_Parse_Settemp(pFilename, acl, 1, s, 1);\r
785                                 }\r
786                         }\r
787                         else\r
788                         {\r
789                                 dprint("Map ", pFilename, " has a client setting for unknown game type ", t, ", ignored\n");\r
790                         }\r
791                 }\r
792                 else if(t == "fog")\r
793                 {\r
794                         if not(cvar_value_issafe(t))\r
795                                 print("Map ", pFilename, " contains a potentially harmful fog setting, ignored\n");\r
796                         else\r
797                                 MapInfo_Map_fog = s;\r
798                 }\r
799                 else if(t == "cdtrack")\r
800                 {\r
801                         if(pGametypeToSet)\r
802                         {\r
803                                 if not(cvar_value_issafe(t))\r
804                                         print("Map ", pFilename, " contains a potentially harmful cdtrack, ignored\n");\r
805                                 else\r
806                                         MapInfo_Map_clientstuff = strcat(\r
807                                                 MapInfo_Map_clientstuff, "cd loop \"", s, "\"\n"\r
808                                         );\r
809                         }\r
810                 }\r
811                 else\r
812                         dprint("Map ", pFilename, " provides unknown info item ", t, ", ignored\n");\r
813         }\r
814         fclose(fh);\r
815 \r
816         if(pGametypeToSet)\r
817         {\r
818                 if(!(MapInfo_Map_supportedGametypes & pGametypeToSet))\r
819                 {\r
820                         print("Can't select the requested game type. Trying anyway with stupid settings.\n");\r
821                         _MapInfo_Map_ApplyGametype("0 0 0", pGametypeToSet, MAPINFO_TYPE_DEATHMATCH);\r
822                 }\r
823         }\r
824 \r
825         MapInfo_Cache_Store();\r
826         if(MapInfo_Map_supportedGametypes != 0)\r
827                 return r;\r
828         dprint("Map ", pFilename, " supports no game types, ignored\n");\r
829         return 0;\r
830 }\r
831 \r
832 float MapInfo_FindName(string s)\r
833 {\r
834         // if there is exactly one map of prefix s, return it\r
835         // if not, return the null string\r
836         // note that DP sorts glob results... so I can use a binary search\r
837         float l, r, m, cmp;\r
838         l = 0;\r
839         r = MapInfo_count;\r
840         // invariants: r is behind s, l-1 is equal or before\r
841         while(l != r)\r
842         {\r
843                 m = floor((l + r) / 2);\r
844                 MapInfo_FindName_match = _MapInfo_GlobItem(MapInfo_FilterList_Lookup(m));\r
845                 cmp = strcasecmp(MapInfo_FindName_match, s);\r
846                 if(cmp == 0)\r
847                         return m; // found and good\r
848                 if(cmp < 0)\r
849                         l = m + 1; // l-1 is before s\r
850                 else\r
851                         r = m; // behind s\r
852         }\r
853         MapInfo_FindName_match = _MapInfo_GlobItem(MapInfo_FilterList_Lookup(l));\r
854         MapInfo_FindName_firstResult = l;\r
855         // r == l, so: l is behind s, l-1 is before\r
856         // SO: if there is any, l is the one with the right prefix\r
857         //     and l+1 may be one too\r
858         if(l == MapInfo_count)\r
859         {\r
860                 MapInfo_FindName_match = string_null;\r
861                 MapInfo_FindName_firstResult = -1;\r
862                 return -1; // no MapInfo_FindName_match, behind last item\r
863         }\r
864         if(!startsWithNocase(MapInfo_FindName_match, s))\r
865         {\r
866                 MapInfo_FindName_match = string_null;\r
867                 MapInfo_FindName_firstResult = -1;\r
868                 return -1; // wrong prefix\r
869         }\r
870         if(l == MapInfo_count - 1)\r
871                 return l; // last one, nothing can follow => unique\r
872         if(startsWithNocase(_MapInfo_GlobItem(MapInfo_FilterList_Lookup(l + 1)), s))\r
873         {\r
874                 MapInfo_FindName_match = string_null;\r
875                 return -1; // ambigous MapInfo_FindName_match\r
876         }\r
877         return l;\r
878 }\r
879 \r
880 string MapInfo_FixName(string s)\r
881 {\r
882         MapInfo_FindName(s);\r
883         return MapInfo_FindName_match;\r
884 }\r
885 \r
886 float MapInfo_CurrentFeatures()\r
887 {\r
888         float req;\r
889         req = 0;\r
890         if(!(cvar("g_lms")) || !cvar("g_pickup_items") || cvar("g_race") || cvar("g_cts"))\r
891                 req |= MAPINFO_FEATURE_WEAPONS;\r
892         return req;\r
893 }\r
894 \r
895 float MapInfo_CurrentGametype()\r
896 {\r
897         if(cvar("g_domination"))\r
898                 return MAPINFO_TYPE_DOMINATION;\r
899         else if(cvar("g_ctf"))\r
900                 return MAPINFO_TYPE_CTF;\r
901         else if(cvar("g_tdm"))\r
902                 return MAPINFO_TYPE_TEAM_DEATHMATCH;\r
903         else if(cvar("g_assault"))\r
904                 return MAPINFO_TYPE_ASSAULT;\r
905         else if(cvar("g_lms"))\r
906                 return MAPINFO_TYPE_LMS;\r
907         else if(cvar("g_arena"))\r
908                 return MAPINFO_TYPE_ARENA;\r
909         else if(cvar("g_ca"))\r
910                 return MAPINFO_TYPE_CA; \r
911         else if(cvar("g_keyhunt"))\r
912                 return MAPINFO_TYPE_KEYHUNT;\r
913         else if(cvar("g_onslaught"))\r
914                 return MAPINFO_TYPE_ONSLAUGHT;\r
915         else if(cvar("g_race"))\r
916                 return MAPINFO_TYPE_RACE;\r
917         else if(cvar("g_cts"))\r
918                 return MAPINFO_TYPE_CTS;\r
919         else\r
920                 return MAPINFO_TYPE_DEATHMATCH;\r
921 }\r
922 \r
923 float _MapInfo_CheckMap(string s) // returns 0 if the map can't be played with the current settings, 1 otherwise\r
924 {\r
925         if(!MapInfo_Get_ByName(s, 1, 0))\r
926                 return 0;\r
927         if((MapInfo_Map_supportedGametypes & MapInfo_CurrentGametype()) == 0)\r
928                 return 0;\r
929         if((MapInfo_Map_supportedFeatures & MapInfo_CurrentFeatures()) != MapInfo_CurrentFeatures())\r
930                 return 0;\r
931         return 1;\r
932 }\r
933 \r
934 float MapInfo_CheckMap(string s) // returns 0 if the map can't be played with the current settings, 1 otherwise\r
935 {\r
936         float r;\r
937         r = _MapInfo_CheckMap(s);\r
938         MapInfo_ClearTemps();\r
939         return r;\r
940 }\r
941 \r
942 string MapInfo_GetGameTypeCvar(float t)\r
943 {\r
944         switch(t)\r
945         {\r
946                 case MAPINFO_TYPE_DEATHMATCH: return "g_dm";\r
947                 case MAPINFO_TYPE_TEAM_DEATHMATCH: return "g_tdm";\r
948                 case MAPINFO_TYPE_DOMINATION: return "g_domination";\r
949                 case MAPINFO_TYPE_CTF: return "g_ctf";\r
950                 case MAPINFO_TYPE_LMS: return "g_lms";\r
951                 case MAPINFO_TYPE_ARENA: return "g_arena";\r
952                 case MAPINFO_TYPE_CA: return "g_ca";\r
953                 case MAPINFO_TYPE_KEYHUNT: return "g_kh";\r
954                 case MAPINFO_TYPE_ASSAULT: return "g_assault";\r
955                 case MAPINFO_TYPE_ONSLAUGHT: return "g_onslaught";\r
956                 case MAPINFO_TYPE_RACE: return "g_race";\r
957                 case MAPINFO_TYPE_CTS: return "g_cts";\r
958                 default: return "";\r
959         }\r
960 }\r
961 \r
962 void MapInfo_SwitchGameType(float t)\r
963 {\r
964         cvar_set("gamecfg",      "0");\r
965         cvar_set("g_dm",         (t == MAPINFO_TYPE_DEATHMATCH)      ? "1" : "0");\r
966         cvar_set("g_tdm",        (t == MAPINFO_TYPE_TEAM_DEATHMATCH) ? "1" : "0");\r
967         cvar_set("g_domination", (t == MAPINFO_TYPE_DOMINATION)      ? "1" : "0");\r
968         cvar_set("g_ctf",        (t == MAPINFO_TYPE_CTF)             ? "1" : "0");\r
969         cvar_set("g_lms",        (t == MAPINFO_TYPE_LMS)             ? "1" : "0");\r
970         cvar_set("g_arena",      (t == MAPINFO_TYPE_ARENA)           ? "1" : "0");\r
971         cvar_set("g_ca",         (t == MAPINFO_TYPE_CA)              ? "1" : "0");\r
972         cvar_set("g_keyhunt",    (t == MAPINFO_TYPE_KEYHUNT)         ? "1" : "0");\r
973         cvar_set("g_assault",    (t == MAPINFO_TYPE_ASSAULT)         ? "1" : "0");\r
974         cvar_set("g_onslaught",  (t == MAPINFO_TYPE_ONSLAUGHT)       ? "1" : "0");\r
975         cvar_set("g_race",       (t == MAPINFO_TYPE_RACE)            ? "1" : "0");\r
976         cvar_set("g_cts",        (t == MAPINFO_TYPE_CTS)             ? "1" : "0");\r
977 }\r
978 \r
979 void MapInfo_LoadMap(string s)\r
980 {\r
981         MapInfo_Map_supportedGametypes = 0;\r
982         // we shouldn't need this, as LoadMapSettings already fixes the gametype\r
983         //if(!MapInfo_CheckMap(s))\r
984         //{\r
985         //      print("EMERGENCY: can't play the selected map in the given game mode. Falling back to DM.\n");\r
986         //      MapInfo_SwitchGameType(MAPINFO_TYPE_DEATHMATCH);\r
987         //}\r
988         localcmd(strcat("\nsettemp_restore\nchangelevel ", s, "\n"));\r
989 }\r
990 \r
991 string MapInfo_ListAllowedMaps(float pRequiredFlags, float pForbiddenFlags)\r
992 {\r
993         string out;\r
994         float i;\r
995 \r
996         // to make absolutely sure:\r
997         MapInfo_Enumerate();\r
998         MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), pRequiredFlags, pForbiddenFlags, 0);\r
999 \r
1000         out = "";\r
1001         for(i = 0; i < MapInfo_count; ++i)\r
1002                 out = strcat(out, " ", _MapInfo_GlobItem(MapInfo_FilterList_Lookup(i)));\r
1003         return substring(out, 1, strlen(out) - 1);\r
1004 }\r
1005 \r
1006 void MapInfo_LoadMapSettings(string s) // to be called from worldspawn\r
1007 {\r
1008         float t, t0;\r
1009         if(!_MapInfo_CheckMap(s)) // with underscore, it keeps temps\r
1010         {\r
1011                 if(MapInfo_Map_supportedGametypes == 0)\r
1012                 {\r
1013                         print("Mapinfo system is not functional at all. Assuming deathmatch.\n");\r
1014                         MapInfo_Map_supportedGametypes = MAPINFO_TYPE_DEATHMATCH;\r
1015                 }\r
1016 \r
1017                 t = 1;\r
1018                 while(!(MapInfo_Map_supportedGametypes & 1))\r
1019                 {\r
1020                         t *= 2;\r
1021                         MapInfo_Map_supportedGametypes = floor(MapInfo_Map_supportedGametypes / 2);\r
1022                 }\r
1023                 // t is now a supported mode!\r
1024                 t0 = MapInfo_CurrentGametype();\r
1025                 if(cvar("g_mapinfo_allow_unsupported_modes_and_let_stuff_break"))\r
1026                 {\r
1027                         print("EMERGENCY: can't play the selected map in the given game mode. Working with only the override settings.\n");\r
1028                         cvar_set("timelimit", "0");\r
1029                         cvar_set("fraglimit", "0");\r
1030                         cvar_set("g_tdm_teams", "2");\r
1031                         cvar_set("g_keyhunt_teams", "3");\r
1032                         cvar_set("g_race_qualifying_timelimit", "0");\r
1033                         cvar_set("leadlimit", "0");\r
1034                 }\r
1035                 else\r
1036                 {\r
1037                         print("EMERGENCY: can't play the selected map in the given game mode. Falling back to a supported mode.\n");\r
1038                         MapInfo_SwitchGameType(t);\r
1039                 }\r
1040         }\r
1041         cvar_settemp_restore();\r
1042         MapInfo_Get_ByName(s, 1, MapInfo_CurrentGametype());\r
1043 }\r
1044 \r
1045 void MapInfo_ClearTemps()\r
1046 {\r
1047         MapInfo_Map_bspname = string_null;\r
1048         MapInfo_Map_title = string_null;\r
1049         MapInfo_Map_description = string_null;\r
1050         MapInfo_Map_author = string_null;\r
1051         MapInfo_Map_clientstuff = string_null;\r
1052         MapInfo_Map_supportedGametypes = 0;\r
1053         MapInfo_Map_supportedFeatures = 0;\r
1054 }\r
1055 \r
1056 void MapInfo_Shutdown()\r
1057 {\r
1058         MapInfo_ClearTemps();\r
1059         MapInfo_Filter_Free();\r
1060         MapInfo_Cache_Destroy();\r
1061         if(_MapInfo_globopen)\r
1062         {\r
1063                 search_end(_MapInfo_globhandle);\r
1064                 _MapInfo_globhandle = -1;\r
1065                 _MapInfo_globopen = FALSE;\r
1066         }\r
1067 }\r
1068 \r
1069 float MapInfo_ForbiddenFlags()\r
1070 {\r
1071         float f;\r
1072         f = MAPINFO_FLAG_FORBIDDEN;\r
1073 \r
1074 #ifndef MENUQC\r
1075         if not(cvar("g_maplist_allow_hidden"))\r
1076 #endif\r
1077                 f |= MAPINFO_FLAG_HIDDEN;\r
1078 \r
1079         if not(cvar("g_maplist_allow_frustrating"))\r
1080                 f |= MAPINFO_FLAG_FRUSTRATING;\r
1081 \r
1082         return f;\r
1083 }\r
1084 \r
1085 float MapInfo_RequiredFlags()\r
1086 {\r
1087         float f;\r
1088         f = 0;\r
1089 \r
1090         if(cvar("g_maplist_allow_frustrating") > 1)\r
1091                 f |= MAPINFO_FLAG_FRUSTRATING;\r
1092 \r
1093         return f;\r
1094 }\r