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