]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blob - qcsrc/common/mapinfo.qh
Merge branch 'master' into Mario/monsters
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / mapinfo.qh
1 #pragma once
2
3 #include "util.qh"
4
5 // info about a map that MapInfo loads
6 string MapInfo_Map_bspname;
7 string MapInfo_Map_title;
8 string MapInfo_Map_titlestring; // either bspname: title or just title, depending on whether bspname is redundant
9 string MapInfo_Map_description;
10 string MapInfo_Map_author;
11 string MapInfo_Map_clientstuff; // not in cache, only for map load
12 string MapInfo_Map_fog; // not in cache, only for map load
13 int MapInfo_Map_supportedGametypes;
14 int MapInfo_Map_supportedFeatures;
15 int MapInfo_Map_flags;
16 vector MapInfo_Map_mins; // these are '0 0 0' if not supported!
17 vector MapInfo_Map_maxs; // these are '0 0 0' if not specified!
18
19 int MAPINFO_TYPE_ALL;
20 .int m_flags;
21
22 CLASS(Gametype, Object)
23     ATTRIB(Gametype, m_id, int, 0);
24     /** game type ID */
25     ATTRIB(Gametype, items, int, 0);
26     /** game type name as in cvar (with g_ prefix) */
27     ATTRIB(Gametype, netname, string);
28     /** game type short name */
29     ATTRIB(Gametype, mdl, string);
30     /** human readable name */
31     ATTRIB(Gametype, message, string);
32     /** does this gametype support teamplay? */
33     ATTRIB(Gametype, team, bool, false);
34     /** does this gametype use a point limit? */
35     ATTRIB(Gametype, frags, bool, true);
36     /** game type defaults */
37     ATTRIB(Gametype, model2, string);
38     /** game type description */
39     ATTRIB(Gametype, gametype_description, string);
40 #ifdef CSQC
41     ATTRIB(Gametype, m_modicons, void(vector pos, vector mySize));
42     ATTRIB(Gametype, m_modicons_reset, void());
43 #endif
44
45     /** DO NOT USE, this is compatibility for legacy maps! */
46     ATTRIB(Gametype, m_legacydefaults, string, "");
47
48     ATTRIB(Gametype, m_mutators, string);
49     METHOD(Gametype, m_parse_mapinfo, bool(string k, string v))
50     {
51         return false;
52     }
53     METHOD(Gametype, m_generate_mapinfo, void(Gametype this, string v))
54     {
55         TC(Gametype, this);
56     }
57     METHOD(Gametype, m_isTwoBaseMode, bool())
58     {
59         return false;
60     }
61     METHOD(Gametype, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter))
62     {
63         return false;
64     }
65     METHOD(Gametype, m_isForcedSupported, bool(Gametype this))
66     {
67         return false;
68     }
69     METHOD(Gametype, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns))
70     {
71         TC(Gametype, this);
72         returns(menu, _("Frag limit:"),      5,  100,  5, "fraglimit_override",        string_null,                    _("The amount of frags needed before the match will end"));
73     }
74
75     METHOD(Gametype, describe, string(Gametype this))
76     {
77         TC(Gametype, this);
78         return this.gametype_description;
79     }
80
81     METHOD(Gametype, display, void(Gametype this, void(string name, string icon) returns))
82     {
83         TC(Gametype, this);
84         returns(this.message, strcat("gametype_", this.mdl));
85     }
86
87     METHOD(Gametype, gametype_init, void(Gametype this, string hname, string sname, string g_name, bool gteamplay, bool gusepoints, string mutators, string defaults, string gdescription))
88     {
89         this.netname = g_name;
90         this.mdl = sname;
91         this.message = hname;
92         this.team = gteamplay;
93         this.m_mutators = cons(sname, mutators);
94         this.model2 = defaults;
95         this.gametype_description = gdescription;
96         this.frags = gusepoints;
97
98         // same as `1 << m_id`
99         MAPINFO_TYPE_ALL |= this.items = this.m_flags = (MAPINFO_TYPE_ALL + 1);
100     }
101 ENDCLASS(Gametype)
102
103 REGISTRY(Gametypes, 24)
104 REGISTER_REGISTRY(Gametypes)
105 REGISTRY_CHECK(Gametypes)
106
107 REGISTRY_DEFINE_GET(Gametypes, NULL)
108 #define REGISTER_GAMETYPE(NAME, inst) REGISTER(Gametypes, MAPINFO_TYPE, NAME, m_id, inst)
109
110 #define IS_GAMETYPE(NAME) (MapInfo_LoadedGametype == MAPINFO_TYPE_##NAME)
111
112 CLASS(Deathmatch, Gametype)
113     INIT(Deathmatch)
114     {
115         this.gametype_init(this, _("Deathmatch"),"dm","g_dm",false,true,"","timelimit=15 pointlimit=30 leadlimit=0",_("Score as many frags as you can"));
116     }
117     METHOD(Deathmatch, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter))
118     {
119         return true;
120     }
121     ATTRIB(Deathmatch, m_legacydefaults, string, "30 20 0");
122 ENDCLASS(Deathmatch)
123 REGISTER_GAMETYPE(DEATHMATCH, NEW(Deathmatch));
124
125 CLASS(LastManStanding, Gametype)
126     INIT(LastManStanding)
127     {
128         this.gametype_init(this, _("Last Man Standing"),"lms","g_lms",false,true,"","timelimit=20 lives=5 leadlimit=0",_("Survive and kill until the enemies have no lives left"));
129     }
130     METHOD(LastManStanding, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter))
131     {
132         return true;
133     }
134     METHOD(LastManStanding, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns))
135     {
136         TC(Gametype, this);
137         returns(menu, _("Lives:"),           3,   50,  1, "g_lms_lives_override",      string_null,                    string_null);
138     }
139     ATTRIB(LastManStanding, m_legacydefaults, string, "9 20 0");
140 ENDCLASS(LastManStanding)
141 REGISTER_GAMETYPE(LMS, NEW(LastManStanding));
142
143 #ifdef CSQC
144 void HUD_Mod_Race(vector pos, vector mySize);
145 #endif
146 CLASS(Race, Gametype)
147     INIT(Race)
148     {
149         this.gametype_init(this, _("Race"),"rc","g_race",false,true,"","timelimit=20 qualifying_timelimit=5 laplimit=7 teamlaplimit=15 leadlimit=0",_("Race against other players to the finish line"));
150     }
151     METHOD(Race, m_parse_mapinfo, bool(string k, string v))
152     {
153         if (!k) {
154             cvar_set("g_race_qualifying_timelimit", cvar_defstring("g_race_qualifying_timelimit"));
155             return true;
156         }
157         switch (k) {
158             case "qualifying_timelimit":
159                 cvar_set("g_race_qualifying_timelimit", v);
160                 return true;
161         }
162         return false;
163     }
164     METHOD(Race, m_generate_mapinfo, void(Gametype this, string v))
165     {
166         if(v == "trigger_race_checkpoint")
167             MapInfo_Map_supportedGametypes |= this.m_flags;
168     }
169     METHOD(Race, m_isTwoBaseMode, bool())
170     {
171         return true;
172     }
173     METHOD(Race, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns))
174     {
175         TC(Gametype, this);
176         returns(menu, _("Laps:"),            1,   25,  1, "g_race_laps_limit",         string_null,                    string_null);
177     }
178 #ifdef CSQC
179     ATTRIB(Race, m_modicons, void(vector pos, vector mySize), HUD_Mod_Race);
180 #endif
181     ATTRIB(Race, m_legacydefaults, string, "20 5 7 15 0");
182 ENDCLASS(Race)
183 REGISTER_GAMETYPE(RACE, NEW(Race));
184 #define g_race IS_GAMETYPE(RACE)
185
186 CLASS(RaceCTS, Gametype)
187     INIT(RaceCTS)
188     {
189         this.gametype_init(this, _("Race CTS"),"cts","g_cts",false,false,"cloaked","timelimit=20",_("Race for fastest time."));
190     }
191     METHOD(RaceCTS, m_generate_mapinfo, void(Gametype this, string v))
192     {
193         if(v == "target_startTimer")
194             MapInfo_Map_supportedGametypes |= this.m_flags;
195     }
196     METHOD(RaceCTS, m_setTeams, void(string sa))
197     {
198         // this is the skill of the map
199         // not parsed by anything yet
200         // for map databases
201         //  cvar_set("fraglimit", sa);
202     }
203     METHOD(RaceCTS, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns))
204     {
205         TC(Gametype, this);
206         returns(menu, _("Point limit:"),    50,  500, 10, string_null,                 string_null,                    string_null);
207     }
208 #ifdef CSQC
209     ATTRIB(RaceCTS, m_modicons, void(vector pos, vector mySize), HUD_Mod_Race);
210 #endif
211     ATTRIB(RaceCTS, m_legacydefaults, string, "20 0 0");
212 ENDCLASS(RaceCTS)
213 REGISTER_GAMETYPE(CTS, NEW(RaceCTS));
214 #define g_cts IS_GAMETYPE(CTS)
215
216 CLASS(TeamDeathmatch, Gametype)
217     INIT(TeamDeathmatch)
218     {
219         this.gametype_init(this, _("Team Deathmatch"),"tdm","g_tdm",true,true,"","timelimit=15 pointlimit=50 teams=2 leadlimit=0",_("Help your team score the most frags against the enemy team"));
220     }
221     METHOD(TeamDeathmatch, m_parse_mapinfo, bool(string k, string v))
222     {
223         if (!k) {
224             cvar_set("g_tdm_teams", cvar_defstring("g_tdm_teams"));
225             return true;
226         }
227         switch (k) {
228             case "teams":
229                 cvar_set("g_tdm_teams", v);
230                 return true;
231         }
232         return false;
233     }
234     METHOD(TeamDeathmatch, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter))
235     {
236         if(spawnpoints >= 8 && diameter > 4096)
237             return true;
238         return false;
239     }
240     METHOD(TeamDeathmatch, m_isForcedSupported, bool(Gametype this))
241     {
242         if(cvar("g_tdm_on_dm_maps"))
243         {
244             // if this is set, all DM maps support TDM too
245             if(!(MapInfo_Map_supportedGametypes & this.m_flags) && (MapInfo_Map_supportedGametypes & MAPINFO_TYPE_DEATHMATCH.m_flags))
246                 return true; // TODO: references another gametype (alternatively, we could check which gamemodes are always enabled and append this if any are supported)
247         }
248         return false;
249     }
250     METHOD(TeamDeathmatch, m_setTeams, void(string sa))
251     {
252         cvar_set("g_tdm_teams", sa);
253     }
254     METHOD(TeamDeathmatch, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns))
255     {
256         TC(Gametype, this);
257         returns(menu, _("Point limit:"),     5,  100,  5, "g_tdm_point_limit",         "g_tdm_teams_override",         _("The amount of points needed before the match will end"));
258     }
259     ATTRIB(TeamDeathmatch, m_legacydefaults, string, "50 20 2 0");
260 ENDCLASS(TeamDeathmatch)
261 REGISTER_GAMETYPE(TEAM_DEATHMATCH, NEW(TeamDeathmatch));
262 #define g_tdm IS_GAMETYPE(TEAM_DEATHMATCH)
263
264 #ifdef CSQC
265 void HUD_Mod_CTF(vector pos, vector mySize);
266 void HUD_Mod_CTF_Reset();
267 #endif
268 CLASS(CaptureTheFlag, Gametype)
269     INIT(CaptureTheFlag)
270     {
271         this.gametype_init(this, _("Capture the Flag"),"ctf","g_ctf",true,true,"","timelimit=20 caplimit=10 leadlimit=6",_("Find and bring the enemy flag to your base to capture it, defend your base from the other team"));
272     }
273     METHOD(CaptureTheFlag, m_generate_mapinfo, void(Gametype this, string v))
274     {
275         if(v == "item_flag_team2" || v == "team_CTF_blueflag")
276             MapInfo_Map_supportedGametypes |= this.m_flags;
277     }
278     METHOD(CaptureTheFlag, m_isTwoBaseMode, bool())
279     {
280         return true;
281     }
282     METHOD(CaptureTheFlag, m_setTeams, void(string sa))
283     {
284         cvar_set("fraglimit", sa);
285     }
286     METHOD(CaptureTheFlag, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns))
287     {
288         TC(Gametype, this);
289         returns(menu, _("Capture limit:"),   1,   20,  1, "capturelimit_override",     string_null,                    _("The amount of captures needed before the match will end"));
290     }
291 #ifdef CSQC
292     ATTRIB(CaptureTheFlag, m_modicons, void(vector pos, vector mySize), HUD_Mod_CTF);
293     ATTRIB(CaptureTheFlag, m_modicons_reset, void(), HUD_Mod_CTF_Reset);
294 #endif
295     ATTRIB(CaptureTheFlag, m_legacydefaults, string, "300 20 10 0");
296 ENDCLASS(CaptureTheFlag)
297 REGISTER_GAMETYPE(CTF, NEW(CaptureTheFlag));
298 #define g_ctf IS_GAMETYPE(CTF)
299
300 #ifdef CSQC
301 void HUD_Mod_CA(vector pos, vector mySize);
302 #endif
303 CLASS(ClanArena, Gametype)
304     INIT(ClanArena)
305     {
306         this.gametype_init(this, _("Clan Arena"),"ca","g_ca",true,true,"","timelimit=20 pointlimit=10 teams=2 leadlimit=6",_("Kill all enemy teammates to win the round"));
307     }
308     METHOD(ClanArena, m_parse_mapinfo, bool(string k, string v))
309     {
310         if (!k) {
311             cvar_set("g_ca_teams", cvar_defstring("g_ca_teams"));
312             return true;
313         }
314         switch (k) {
315             case "teams":
316                 cvar_set("g_ca_teams", v);
317                 return true;
318         }
319         return false;
320     }
321     METHOD(ClanArena, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter))
322     {
323         if(spawnpoints >= 8 && diameter > 4096)
324             return true;
325         return false;
326     }
327     METHOD(ClanArena, m_setTeams, void(string sa))
328     {
329         cvar_set("g_ca_teams", sa);
330     }
331     METHOD(ClanArena, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns))
332     {
333         TC(Gametype, this);
334         returns(menu, _("Frag limit:"),      5,  100,  5, "fraglimit_override",        "g_ca_teams_override",          _("The amount of frags needed before the match will end"));
335     }
336 #ifdef CSQC
337     ATTRIB(ClanArena, m_modicons, void(vector pos, vector mySize), HUD_Mod_CA);
338 #endif
339     ATTRIB(ClanArena, m_legacydefaults, string, "10 20 0");
340 ENDCLASS(ClanArena)
341 REGISTER_GAMETYPE(CA, NEW(ClanArena));
342 #define g_ca IS_GAMETYPE(CA)
343
344 #ifdef CSQC
345 void HUD_Mod_Dom(vector pos, vector mySize);
346 #endif
347 CLASS(Domination, Gametype)
348     INIT(Domination)
349     {
350         this.gametype_init(this, _("Domination"),"dom","g_domination",true,true,"","timelimit=20 pointlimit=200 teams=2 leadlimit=0",_("Capture and defend all the control points to win"));
351     }
352     METHOD(Domination, m_parse_mapinfo, bool(string k, string v))
353     {
354         if (!k) {
355             cvar_set("g_domination_default_teams", cvar_defstring("g_domination_default_teams"));
356             return true;
357         }
358         switch (k) {
359             case "teams":
360                 cvar_set("g_domination_default_teams", v);
361                 return true;
362         }
363         return false;
364     }
365     METHOD(Domination, m_generate_mapinfo, void(Gametype this, string v))
366     {
367         if(v == "dom_controlpoint")
368             MapInfo_Map_supportedGametypes |= this.m_flags;
369     }
370     METHOD(Domination, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns))
371     {
372         TC(Gametype, this);
373         returns(menu, _("Point limit:"),    50,  500, 10, "g_domination_point_limit",  "g_domination_teams_override",  _("The amount of points needed before the match will end"));
374     }
375 #ifdef CSQC
376     ATTRIB(Domination, m_modicons, void(vector pos, vector mySize), HUD_Mod_Dom);
377 #endif
378     ATTRIB(Domination, m_legacydefaults, string, "200 20 0");
379 ENDCLASS(Domination)
380 REGISTER_GAMETYPE(DOMINATION, NEW(Domination));
381
382 #ifdef CSQC
383 void HUD_Mod_KH(vector pos, vector mySize);
384 #endif
385 CLASS(KeyHunt, Gametype)
386     INIT(KeyHunt)
387     {
388         this.gametype_init(this, _("Key Hunt"),"kh","g_keyhunt",true,true,"","timelimit=20 pointlimit=1000 teams=3 leadlimit=0",_("Gather all the keys to win the round"));
389     }
390     METHOD(KeyHunt, m_parse_mapinfo, bool(string k, string v))
391     {
392         if (!k) {
393             cvar_set("g_keyhunt_teams", cvar_defstring("g_keyhunt_teams"));
394             return true;
395         }
396         switch (k) {
397             case "teams":
398                 cvar_set("g_keyhunt_teams", v);
399                 return true;
400         }
401         return false;
402     }
403     METHOD(KeyHunt, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter))
404     {
405         if(spawnpoints >= 12 && diameter > 5120)
406             return true;
407         return false;
408     }
409     METHOD(KeyHunt, m_setTeams, void(string sa))
410     {
411         cvar_set("g_keyhunt_teams", sa);
412     }
413     METHOD(KeyHunt, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns))
414     {
415         TC(Gametype, this);
416         returns(menu, _("Point limit:"),   200, 1500, 50, "g_keyhunt_point_limit",     "g_keyhunt_teams_override",     _("The amount of points needed before the match will end"));
417     }
418 #ifdef CSQC
419     ATTRIB(KeyHunt, m_modicons, void(vector pos, vector mySize), HUD_Mod_KH);
420 #endif
421     ATTRIB(KeyHunt, m_legacydefaults, string, "1000 20 3 0");
422 ENDCLASS(KeyHunt)
423 REGISTER_GAMETYPE(KEYHUNT, NEW(KeyHunt));
424
425 CLASS(Assault, Gametype)
426     INIT(Assault)
427     {
428         this.gametype_init(this, _("Assault"),"as","g_assault",true,false,"","timelimit=20",_("Destroy obstacles to find and destroy the enemy power core before time runs out"));
429     }
430     METHOD(Assault, m_generate_mapinfo, void(Gametype this, string v))
431     {
432         if(v == "target_assault_roundend")
433             MapInfo_Map_supportedGametypes |= this.m_flags;
434     }
435     METHOD(Assault, m_isTwoBaseMode, bool())
436     {
437         return true;
438     }
439     METHOD(Assault, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns))
440     {
441         TC(Gametype, this);
442         returns(menu, _("Point limit:"),    50,  500, 10, string_null,                 string_null,                    string_null);
443     }
444     ATTRIB(Assault, m_legacydefaults, string, "20 0");
445 ENDCLASS(Assault)
446 REGISTER_GAMETYPE(ASSAULT, NEW(Assault));
447 #define g_assault IS_GAMETYPE(ASSAULT)
448
449 CLASS(Onslaught, Gametype)
450     INIT(Onslaught)
451     {
452         this.gametype_init(this, _("Onslaught"),"ons","g_onslaught",true,false,"","pointlimit=1 timelimit=20",_("Capture control points to reach and destroy the enemy generator"));
453     }
454     METHOD(Onslaught, m_generate_mapinfo, void(Gametype this, string v))
455     {
456         if(v == "onslaught_generator")
457             MapInfo_Map_supportedGametypes |= this.m_flags;
458     }
459     METHOD(Onslaught, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns))
460     {
461         TC(Gametype, this);
462         returns(menu, _("Point limit:"),    50,  500, 10, string_null,                 string_null,                    string_null);
463     }
464     ATTRIB(Onslaught, m_legacydefaults, string, "20 0");
465 ENDCLASS(Onslaught)
466 REGISTER_GAMETYPE(ONSLAUGHT, NEW(Onslaught));
467
468 #ifdef CSQC
469 void HUD_Mod_NexBall(vector pos, vector mySize);
470 #endif
471 CLASS(NexBall, Gametype)
472     INIT(NexBall)
473     {
474         this.gametype_init(this, _("Nexball"),"nb","g_nexball",true,true,"","timelimit=20 pointlimit=5 leadlimit=0",_("Shoot and kick the ball into the enemies goal, keep your goal clean"));
475     }
476     METHOD(NexBall, m_generate_mapinfo, void(Gametype this, string v))
477     {
478         if(substring(v, 0, 8) == "nexball_" || substring(v, 0, 4) == "ball")
479             MapInfo_Map_supportedGametypes |= this.m_flags;
480     }
481     METHOD(NexBall, m_isTwoBaseMode, bool())
482     {
483         return true;
484     }
485     METHOD(NexBall, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns))
486     {
487         TC(Gametype, this);
488         returns(menu, _("Goals:"),           1,   50,  1, "g_nexball_goallimit",       string_null,                    _("The amount of goals needed before the match will end"));
489     }
490 #ifdef CSQC
491     ATTRIB(NexBall, m_modicons, void(vector pos, vector mySize), HUD_Mod_NexBall);
492 #endif
493     ATTRIB(NexBall, m_legacydefaults, string, "5 20 0");
494 ENDCLASS(NexBall)
495 REGISTER_GAMETYPE(NEXBALL, NEW(NexBall));
496 #define g_nexball IS_GAMETYPE(NEXBALL)
497
498 CLASS(FreezeTag, Gametype)
499     INIT(FreezeTag)
500     {
501         this.gametype_init(this, _("Freeze Tag"),"ft","g_freezetag",true,true,"","timelimit=20 pointlimit=10 teams=2 leadlimit=6",_("Kill enemies to freeze them, stand next to frozen teammates to revive them; freeze all enemies to win"));
502     }
503     METHOD(FreezeTag, m_parse_mapinfo, bool(string k, string v))
504     {
505         if (!k) {
506             cvar_set("g_freezetag_teams", cvar_defstring("g_freezetag_teams"));
507             return true;
508         }
509         switch (k) {
510             case "teams":
511                 cvar_set("g_freezetag_teams", v);
512                 return true;
513         }
514         return false;
515     }
516     METHOD(FreezeTag, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter))
517     {
518         if(spawnpoints >= 8 && diameter > 4096)
519             return true;
520         return false;
521     }
522     METHOD(FreezeTag, m_setTeams, void(string sa))
523     {
524         cvar_set("g_freezetag_teams", sa);
525     }
526     METHOD(FreezeTag, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns))
527     {
528         TC(Gametype, this);
529         returns(menu, _("Frag limit:"),      5,  100,  5, "fraglimit_override",        "g_freezetag_teams_override",   _("The amount of frags needed before the match will end"));
530     }
531 #ifdef CSQC
532     ATTRIB(FreezeTag, m_modicons, void(vector pos, vector mySize), HUD_Mod_CA);
533 #endif
534     ATTRIB(FreezeTag, m_legacydefaults, string, "10 20 0");
535 ENDCLASS(FreezeTag)
536 REGISTER_GAMETYPE(FREEZETAG, NEW(FreezeTag));
537 #define g_freezetag IS_GAMETYPE(FREEZETAG)
538
539 #ifdef CSQC
540 void HUD_Mod_Keepaway(vector pos, vector mySize);
541 #endif
542 CLASS(Keepaway, Gametype)
543     INIT(Keepaway)
544     {
545         this.gametype_init(this, _("Keepaway"),"ka","g_keepaway",false,true,"","timelimit=20 pointlimit=30",_("Hold the ball to get points for kills"));
546     }
547     METHOD(Keepaway, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter))
548     {
549         return true;
550     }
551 #ifdef CSQC
552     ATTRIB(Keepaway, m_modicons, void(vector pos, vector mySize), HUD_Mod_Keepaway);
553 #endif
554 ENDCLASS(Keepaway)
555 REGISTER_GAMETYPE(KEEPAWAY, NEW(Keepaway));
556
557 CLASS(Invasion, Gametype)
558     INIT(Invasion)
559     {
560         this.gametype_init(this, _("Invasion"),"inv","g_invasion",false,true,"","pointlimit=50 type=0",_("Survive against waves of monsters"));
561     }
562     METHOD(Invasion, m_parse_mapinfo, bool(string k, string v))
563     {
564         switch (k) {
565             case "type":
566                 cvar_set("g_invasion_type", v);
567                 return true;
568         }
569         return false;
570     }
571     METHOD(Invasion, m_generate_mapinfo, void(Gametype this, string v))
572     {
573         if(v == "invasion_spawnpoint")
574             MapInfo_Map_supportedGametypes |= this.m_flags;
575     }
576     METHOD(Invasion, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns))
577     {
578         TC(Gametype, this);
579         returns(menu, _("Point limit:"),    50,  500, 10, string_null,                 string_null,                    string_null);
580     }
581 ENDCLASS(Invasion)
582 REGISTER_GAMETYPE(INVASION, NEW(Invasion));
583
584 CLASS(Duel, Gametype)
585     INIT(Duel)
586     {
587         this.gametype_init(this, _("Duel"),"duel","g_duel",false,true,"","timelimit=10 pointlimit=0 leadlimit=0",_("Fight in a one versus one arena battle to decide the winner"));
588     }
589     METHOD(Duel, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter))
590     {
591         return (diameter < 16384);
592     }
593     METHOD(Duel, m_isForcedSupported, bool(Gametype this))
594     {
595         if(!cvar("g_duel_not_dm_maps"))
596         {
597             // if this is set, all DM maps support duel too
598             // TODO: we should really check the size of maps, some DM maps do not work for duel!
599             if(!(MapInfo_Map_supportedGametypes & this.m_flags) && (MapInfo_Map_supportedGametypes & MAPINFO_TYPE_DEATHMATCH.m_flags))
600                 return true; // TODO: references another gametype (alternatively, we could check which gamemodes are always enabled and append this if any are supported)
601         }
602         return false;
603     }
604 ENDCLASS(Duel)
605 REGISTER_GAMETYPE(DUEL, NEW(Duel));
606 #define g_duel IS_GAMETYPE(DUEL)
607
608 const int MAPINFO_FEATURE_WEAPONS       = 1; // not defined for instagib-only maps
609 const int MAPINFO_FEATURE_VEHICLES      = 2;
610 const int MAPINFO_FEATURE_TURRETS       = 4;
611 const int MAPINFO_FEATURE_MONSTERS      = 8;
612
613 const int MAPINFO_FLAG_HIDDEN           = 1; // not in lsmaps/menu/vcall/etc., can just be changed to manually
614 const int MAPINFO_FLAG_FORBIDDEN        = 2; // don't even allow the map by a cvar setting that allows hidden maps
615 const int MAPINFO_FLAG_FRUSTRATING      = 4; // this map is near impossible to play, enable at your own risk
616 const int MAPINFO_FLAG_NOAUTOMAPLIST    = 8; // do not include when automatically building maplist (counts as hidden for maplist building purposes)
617
618 float MapInfo_count;
619
620 // load MapInfo_count; generate mapinfo for maps that miss them, and clear the
621 // cache; you need to call MapInfo_FilterGametype afterwards!
622 void MapInfo_Enumerate();
623
624 // filter the info by game type mask (updates MapInfo_count)
625 float MapInfo_progress;
626 float MapInfo_FilterGametype(Gametype gametypeFlags, float features, float pFlagsRequired, float pFlagsForbidden, float pAbortOnGenerate); // 1 on success, 0 on temporary failure (call it again next frame then; use MapInfo_progress as progress indicator)
627 float _MapInfo_FilterGametype(int gametypeFlags, float features, float pFlagsRequired, float pFlagsForbidden, float pAbortOnGenerate); // 1 on success, 0 on temporary failure (call it again next frame then; use MapInfo_progress as progress indicator)
628 void MapInfo_FilterString(string sf); // filter _MapInfo_filtered (created by MapInfo_FilterGametype) with keyword
629 int MapInfo_CurrentFeatures(); // retrieves currently required features from cvars
630 Gametype MapInfo_CurrentGametype(); // retrieves current gametype from cvars
631 int MapInfo_ForbiddenFlags(); // retrieves current flags from cvars
632 int MapInfo_RequiredFlags(); // retrieves current flags from cvars
633
634 // load info about the i-th map into the MapInfo_Map_* globals
635 float MapInfo_Get_ByID(float i); // 1 on success, 0 on failure
636 string MapInfo_BSPName_ByID(float i);
637
638 // load info about a map by name into the MapInfo_Map_* globals
639 int MapInfo_Get_ByName(string s, float allowGenerate, Gametype gametypeToSet); // 1 on success, 0 on failure, 2 if it autogenerated a mapinfo file
640
641 // look for a map by a prefix, returns the actual map name on success, string_null on failure or ambigous match
642 string MapInfo_FindName_match; // the name of the map that was found
643 float MapInfo_FindName_firstResult; // -1 if none were found, index of first one if not unique but found (FindName then returns -1)
644 float MapInfo_FindName(string s);
645 string MapInfo_FixName(string s);
646
647 // play a map
648 float MapInfo_CheckMap(string s); // returns 0 if the map can't be played with the current settings
649 void MapInfo_LoadMap(string s, float reinit);
650
651 // list all maps for the current game type
652 string MapInfo_ListAllowedMaps(Gametype type, float pFlagsRequired, float pFlagsForbidden);
653 // list all allowed maps (for any game type)
654 string MapInfo_ListAllAllowedMaps(float pFlagsRequired, float pFlagsForbidden);
655
656 // gets a gametype from a string
657 string _MapInfo_GetDefaultEx(Gametype t);
658 float _MapInfo_GetTeamPlayBool(Gametype t);
659 Gametype MapInfo_Type_FromString(string t);
660 string MapInfo_Type_Description(Gametype t);
661 string MapInfo_Type_ToString(Gametype t);
662 string MapInfo_Type_ToText(Gametype t);
663 void MapInfo_SwitchGameType(Gametype t);
664
665 // to be called from worldspawn to set up cvars
666 void MapInfo_LoadMapSettings(string s);
667 Gametype MapInfo_LoadedGametype; // game type that was active during map load
668
669 void MapInfo_Cache_Destroy(); // disable caching
670 void MapInfo_Cache_Create(); // enable caching
671 void MapInfo_Cache_Invalidate(); // delete cache if any, but keep enabled
672
673 void _MapInfo_Parse_Settemp(string pFilename, string acl, float type, string s, float recurse);
674
675 void MapInfo_ClearTemps(); // call this when done with mapinfo for this frame
676
677 void MapInfo_Shutdown(); // call this in the shutdown handler
678
679 #define MAPINFO_SETTEMP_ACL_USER cvar_string("g_mapinfo_settemp_acl")
680 #define MAPINFO_SETTEMP_ACL_SYSTEM "-g_mapinfo_* -rcon_* -_* -g_ban* -r_water +*"