Xonotic QuakeC
The free, fast arena FPS with crisp movement and a wide array of weapons
intermission.qc
Go to the documentation of this file.
1#include "mapvoting.qh"
2
3#include <common/mapinfo.qh>
4#include <common/util.qh>
5#include <server/bot/api.qh>
7#include <server/campaign.qh>
8#include <server/client.qh>
9#include <server/mapvoting.qh>
11#include <server/world.qh>
12
14{
15 stuffcmd(pl, sprintf("\nsettemp _nextmap %s\n", get_nextmap()));
16}
17
18void Set_NextMap(string mapname)
19{
20 if (mapname != "")
22 else
24
26}
27
32
33string GetMapname()
34{
35 return mapname;
36}
37
39
40// NOTE: this now expects the map list to be already tokenized and the count in Map_Count
42{
43 string map = GetMapname();
45
46 if(idx >= 0)
47 {
48 if(idx < Map_Count)
49 {
50 if(map == bufstr_get(maplist_buffer, idx))
51 {
52 return idx;
53 }
54 }
55 }
56
57 for(int pos = 0; pos < Map_Count; ++pos)
58 {
59 if(map == bufstr_get(maplist_buffer, pos))
60 return pos;
61 }
62
63 // resume normal maplist rotation if current map is not in g_maplist
64 return idx;
65}
66
67bool MapHasRightSize(string map)
68{
69 int minplayers = max(0, floor(autocvar_minplayers));
70 if (teamplay)
73 && (currentbots || autocvar_bot_number || player_count < minplayers))
74 {
75 string checkwp_msg = strcat("checkwp ", map);
76 if(!fexists(strcat("maps/", map, ".waypoints")))
77 {
78 LOG_TRACE(checkwp_msg, ": no waypoints");
79 return false;
80 }
81 LOG_TRACE(checkwp_msg, ": has waypoints");
82 }
83
85 return true;
86
87 // open map size restriction file
88 if(!MapReadSizes(map))
89 return true; // map has no size restrictions
90
91 string checksize_msg = strcat("MapHasRightSize ", map);
92 int player_limit = ((autocvar_g_maplist_sizes_count_maxplayers) ? GetPlayerLimit() : 0);
93 int pcount = ((player_limit > 0) ? min(player_count, player_limit) : player_count); // bind it to the player limit so that forced spectators don't influence the limits
94
96 pcount -= currentbots;
97 pcount -= rint(cvar("g_maplist_sizes_specparty") * pcount);
98
99 // ensure small maps can be selected when pcount is low
101 map_minplayers = 0;
102
103 if(pcount < map_minplayers)
104 {
105 LOG_TRACE(checksize_msg, ": not enough");
106 return false;
107 }
108 if(map_maxplayers && pcount > map_maxplayers)
109 {
110 LOG_TRACE(checksize_msg, ": too many");
111 return false;
112 }
113 LOG_TRACE(checksize_msg, ": right size");
114 return true;
115}
116
117string Map_Filename(string m)
118{
119 return strcat("maps/", m, ".bsp");
120}
121
122void Map_MarkAsRecent(string m)
123{
125}
126
127bool Map_IsRecent(string m)
128{
130 return false;
132}
133
134bool Map_Check(string m, int pass)
135{
136 if(pass <= 1)
137 {
138 if(Map_IsRecent(m))
139 return false;
140 }
141 if(MapInfo_CheckMap(m))
142 {
143 if(pass == 2)
144 return true;
145 // MapInfo_Map_flags was set by MapInfo_CheckMap()
147 return false;
148 if(MapHasRightSize(m))
149 return true;
150 return false;
151 }
152 else
153 {
154 string filename = Map_Filename(m);
155 LOG_DEBUG( "Couldn't select '", filename, "'..." );
156 }
157
158 return false;
159}
160
161void Map_Goto_SetStr(string nextmapname)
162{
163 if(getmapname_stored != "")
165 if(nextmapname == "")
167 else
168 getmapname_stored = strzone(nextmapname);
169}
170
171void Map_Goto_SetIndex(int position)
172{
173 Map_Current = position;
174 cvar_set("g_maplist_index", ftos(position));
175 Map_Goto_SetStr(bufstr_get(maplist_buffer, position));
176}
177
178void Map_Goto(float reinit)
179{
181}
182
183// return codes of map selectors:
184// -1 = temporary failure (that is, try some method that is guaranteed to succeed)
185// -2 = permanent failure
186int MaplistMethod_Iterate(void) // usual method
187{
188 int attempts = 42; // skip advanced checks if this many maps fail
189
190 LOG_TRACE("Trying MaplistMethod_Iterate");
191
192 for(int i = 1; i < Map_Count; ++i)
193 {
194 int mapindex = (i + Map_Current) % Map_Count;
195 if(Map_Check(bufstr_get(maplist_buffer, mapindex), 1))
196 return mapindex;
197
198 --attempts;
199 if(attempts <= 0)
200 {
201 LOG_DEBUG("MaplistMethod_Iterate: Couldn't find a suitable map, falling back to next valid");
202 break;
203 }
204 }
205
206 // failing that, just accept the next map in the list
207 int mapindex = (1 + Map_Current) % Map_Count;
208 return mapindex;
209}
210
211int MaplistMethod_Repeat(void) // fallback method
212{
213 LOG_TRACE("Trying MaplistMethod_Repeat");
214
215 if(Map_Check(bufstr_get(maplist_buffer, Map_Current), 2))
216 return Map_Current;
217 return -2;
218}
219
220int MaplistMethod_Random(void) // random map selection
221{
222 int i, imax;
223
224 LOG_TRACE("Trying MaplistMethod_Random");
225
226 imax = 42;
227
228 for(i = 0; i <= imax; ++i)
229 {
230 int mapindex = (Map_Current + floor(random() * (Map_Count - 1) + 1)) % Map_Count; // any OTHER map
231 if(Map_Check(bufstr_get(maplist_buffer, mapindex), 1))
232 return mapindex;
233 }
234 return -1;
235}
236
237// NOTE: call Maplist_Close when you're done!
239{
240 bool have_maps = false;
241 if(autocvar_g_maplist != "")
242 {
243 // make sure there is at least one playable map in the list
244 bool needtrim = false;
246 {
247 if(!fexists(Map_Filename(it)))
248 {
249 needtrim = true;
250 if(have_maps)
251 break;
252 continue;
253 }
254 if(have_maps || !Map_Check(it, 2))
255 continue;
256 have_maps = true;
257 if(needtrim)
258 break;
259 });
260
261 // additionally trim any non-existent maps
262 if(needtrim)
263 {
264 int trimmedmaps = 0;
265 string newmaplist = "";
267 {
268 if(!fexists(Map_Filename(it)))
269 {
270 ++trimmedmaps;
271 continue;
272 }
273 newmaplist = cons(newmaplist, it);
274 });
275 cvar_set("g_maplist", newmaplist);
276 LOG_DEBUGF("Maplist_Init: trimmed %d missing maps from the list", trimmedmaps);
277 }
278 }
279
280 if (!have_maps)
281 {
282 bprint( "Maplist contains no usable maps! Resetting it to default map list.\n" );
285 localcmd("\nmenu_cmd sync\n");
286 }
287
289
290 int _cnt = 0;
292 {
293 // NOTE: inlined maplist_shuffle function to avoid a second buffer, keep both in sync
295 {
296 int _j = floor(random() * (_cnt + 1));
297 if(_j != _cnt)
298 bufstr_set(maplist_buffer, _cnt, bufstr_get(maplist_buffer, _j));
299 bufstr_set(maplist_buffer, _j, it);
300 ++_cnt;
301 }
302 else
303 bufstr_set(maplist_buffer, i, it);
304 });
305
306 Map_Count = buf_getsize(maplist_buffer);
307
308 if(Map_Count == 0)
309 error("empty maplist, cannot select a new map");
310
312
313 return Map_Count;
314}
315
317{
318 buf_del(maplist_buffer);
319}
320
321// NOTE: call Maplist_Init() before making GetNextMap() call(s)
322string GetNextMap(void)
323{
324 int nextMap = -1;
325
326 if(nextMap == -1 && autocvar_g_maplist_selectrandom)
327 nextMap = MaplistMethod_Random();
328
329 if(nextMap == -1)
330 nextMap = MaplistMethod_Iterate();
331
332 if(nextMap == -1)
333 nextMap = MaplistMethod_Repeat();
334
335 if(nextMap >= 0)
336 {
337 Map_Goto_SetIndex(nextMap);
338 return getmapname_stored;
339 }
340
341 return "";
342}
343
344float DoNextMapOverride(float reinit)
345{
347 {
349 alreadychangedlevel = true;
350 return true;
351 }
353 {
355 {
356 localcmd("quit\n");
357 alreadychangedlevel = true;
358 return true;
359 }
360 }
362 {
364 alreadychangedlevel = true;
365 return true;
366 }
367 if (!reinit && autocvar_samelevel) // if samelevel is set, stay on same level
368 {
369 localcmd("restart\n");
370 alreadychangedlevel = true;
371 return true;
372 }
373 if(get_nextmap() != "")
374 {
376 if (m != get_nextmap())
377 Set_NextMap(m);
378
379 if(!m || gametypevote)
380 return false;
382 {
384 return false;
385 }
386
387 if(MapInfo_CheckMap(m))
388 {
390 Map_Goto(reinit);
391 alreadychangedlevel = true;
392 return true;
393 }
394 }
395 if(!reinit && autocvar_lastlevel)
396 {
398 localcmd("set lastlevel 0\ntogglemenu 1\n");
399 alreadychangedlevel = true;
400 return true;
401 }
402 return false;
403}
404
405void GotoNextMap(float reinit)
406{
407 //string nextmap;
408 //float n, nummaps;
409 //string s;
411 return;
412 alreadychangedlevel = true;
413
414 Maplist_Init();
415 string nextMap = GetNextMap();
417 if(nextMap == "")
418 error("Everything is broken - cannot find a next map. Please report this to the developers.");
419 Map_Goto(reinit);
420}
421
422string GotoMap(string m)
423{
425 if (!m)
426 return "The map you suggested is not available on this server.";
428 if (!MapInfo_CheckMap(m))
429 return "The map you suggested does not support the current gametype.";
430 if (m != get_nextmap())
431 Set_NextMap(m);
433 cvar_set("_endmatch", "1");
435 {
436 if(DoNextMapOverride(0))
437 return "Map switch initiated.";
438 else
439 return "Hm... no. For some reason I like THIS map more.";
440 }
441 else
442 return "Map switch will happen after scoreboard.";
443}
444
445
446/*
447============
448IntermissionThink
449
450When the player presses attack or jump, change to the next level
451============
452*/
455{
457
458 float server_screenshot = (autocvar_sv_autoscreenshot && CS_CVAR(this).cvar_cl_autoscreenshot);
459 float client_screenshot = (CS_CVAR(this).cvar_cl_autoscreenshot == 2);
460
461 if( (server_screenshot || client_screenshot)
462 && ((this.autoscreenshot > 0) && (time > this.autoscreenshot)) )
463 {
464 this.autoscreenshot = -1;
465 if(IS_REAL_CLIENT(this))
466 {
467 // in old clients "cl_autoscreenshot_screenshot_s %s %s;" takes the screenshot
468 // "cl_autoscreenshot_screenshot %s %s;" does nothing because the cl_autoscreenshot_screenshot alias
469 // doesn't exist; the cl_autoscreenshot_screenshot dummy cvar is created
470 // so that "cl_autoscreenshot_screenshot %s %s" doesn't print any warning in console
471
472 // in new clients "cl_autoscreenshot_screenshot %s %s;" takes the screenshot
473 // even if the cl_autoscreenshot_screenshot cvar is created, only the cl_autoscreenshot_screenshot alias
474 // is executed since only the alias is executed if a cvar with the same name exists
475 // cl_autoscreenshot_screenshot_s is not run as cl_autoscreenshot_screenshot alias clears it
476 // (it doesn't delete it so that "unalias cl_autoscreenshot_screenshot_s;" doesn't print any warning)
477
478 // this stuffcmd is needed only for Xonotic 0.8.6 or lower
479 // the s in cl_autoscreenshot_screenshot_s stands for server alias (alias name can't be longer than 32)
480 stuffcmd(this, sprintf("\n"
481 "alias cl_autoscreenshot_screenshot_s \"screenshot screenshots/autoscreenshot/%s-%s.jpg\";"
482 "set cl_autoscreenshot_screenshot 0;", GetMapname(), matchid));
483
484 // keep only this stuffcmd after the next release
485 stuffcmd(this, sprintf("\ncl_autoscreenshot_screenshot %s %s;"
486 "echo \"^5A screenshot has been taken at request of the server.\"\n", GetMapname(), matchid));
487
488 // this stuffcmd is needed only for Xonotic 0.8.6 or lower
489 stuffcmd(this, "\n"
490 "cl_autoscreenshot_screenshot_s %s %s;"
491 "unset cl_autoscreenshot_screenshot;"
492 "unalias cl_autoscreenshot_screenshot_s;");
493 }
494 return;
495 }
496
498 return;
499
502 return;
503
505}
506
508{
509 if(!e.autoscreenshot) // initial call
510 {
511 e.autoscreenshot = time + 0.1;
512 SetResourceExplicit(e, RES_HEALTH, -2342); // health in the first intermission phase
513 for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
514 {
515 .entity weaponentity = weaponentities[slot];
516 if(e.(weaponentity))
517 {
518 e.(weaponentity).effects = EF_NODRAW;
519 if (e.(weaponentity).weaponchild)
520 e.(weaponentity).weaponchild.effects = EF_NODRAW;
521 }
522 }
523 if(IS_REAL_CLIENT(e))
524 {
525 stuffcmd(e, "\nscr_printspeed 1000000\n");
529 });
531 {
532 stuffcmd(e, sprintf("\ncd loop %s\n", RandomSelection_chosen_string));
533 }
534 msg_entity = e;
536 }
537 }
538}
int currentbots
Definition api.qh:104
int player_count
Definition api.qh:103
var entity(vector mins, vector maxs,.entity tofield) findbox_tofield_OrFallback
bool SetResourceExplicit(entity e, Resource res_type, float amount)
Sets the resource amount of an entity without calling any hooks.
bool gametypevote
Definition mapvoting.qc:48
#define PHYS_INPUT_BUTTON_JUMP(s)
Definition player.qh:153
#define PHYS_INPUT_BUTTON_HOOK(s)
Definition player.qh:157
#define PHYS_INPUT_BUTTON_USE(s)
Definition player.qh:160
#define PHYS_INPUT_BUTTON_ATCK(s)
Definition player.qh:152
#define PHYS_INPUT_BUTTON_ATCK2(s)
Definition player.qh:154
int cvar_settemp_restore()
Definition util.qc:845
string mapname
float effects
float time
const float EF_NODRAW
int autocvar_bot_number
Definition cvars.qh:64
int autocvar_minplayers
Definition cvars.qh:68
int autocvar_minplayers_per_team
Definition cvars.qh:69
#define buf_create
#define pass(name, colormin, colormax)
ERASEABLE bool fexists(string f)
Definition file.qh:4
string Map_Filename(string m)
void Maplist_Close()
void Set_NextMap(string mapname)
string GetMapname()
void Map_Goto_SetIndex(int position)
int Map_Current
void FixIntermissionClient(entity e)
void IntermissionThink(entity this)
int Map_Count
bool MapHasRightSize(string map)
void Map_Goto_SetStr(string nextmapname)
float autoscreenshot
int MaplistMethod_Iterate(void)
bool Map_IsRecent(string m)
int GetMaplistPosition()
string GotoMap(string m)
int MaplistMethod_Random(void)
int Maplist_Init(void)
bool Map_Check(string m, int pass)
string GetGametype()
int MaplistMethod_Repeat(void)
void Map_Goto(float reinit)
string GetNextMap(void)
float DoNextMapOverride(float reinit)
void Send_NextMap_To_Player(entity pl)
void Map_MarkAsRecent(string m)
void GotoNextMap(float reinit)
string autocvar_sv_intermission_cdtrack
bool intermission_running
float intermission_exittime
string _nextmap
bool autocvar_samelevel
bool alreadychangedlevel
bool autocvar_lastlevel
#define get_nextmap()
int maplist_buffer
bool autocvar_sv_autoscreenshot
#define FOREACH_WORD(words, cond, body)
Definition iter.qh:33
#define LOG_TRACE(...)
Definition log.qh:76
#define LOG_DEBUG(...)
Definition log.qh:80
#define LOG_DEBUGF(...)
Definition log.qh:81
bool MapReadSizes(string map)
Definition mapinfo.qc:1401
float MapInfo_CheckMap(string s)
Definition mapinfo.qc:1502
int MapInfo_RequiredFlags()
Definition mapinfo.qc:1670
string MapInfo_Type_ToString(Gametype t)
Definition mapinfo.qc:655
void MapInfo_LoadMap(string s, float reinit)
Definition mapinfo.qc:1514
Gametype MapInfo_CurrentGametype()
Definition mapinfo.qc:1482
int MapInfo_ForbiddenFlags()
Definition mapinfo.qc:1655
string MapInfo_ListAllowedMaps(Gametype type, float pRequiredFlags, float pForbiddenFlags)
Definition mapinfo.qc:1533
float _MapInfo_GetTeamPlayBool(Gametype t)
Definition mapinfo.qc:538
int map_minplayers
Definition mapinfo.qh:190
Gametype MapInfo_LoadedGametype
Definition mapinfo.qh:220
int map_maxplayers
Definition mapinfo.qh:191
int MapInfo_Map_flags
Definition mapinfo.qh:15
const int MAPINFO_FLAG_DONOTWANT
Definition mapinfo.qh:164
bool autocvar_g_campaign
Definition menu.qc:752
void localcmd(string command,...)
void cvar_set(string name, string value)
float MSG_ONE
Definition menudefs.qc:56
float bound(float min, float value, float max)
float cvar(string name)
float random(void)
void bprint(string text,...)
float min(float f,...)
void strunzone(string s)
float rint(float f)
string ftos(float f)
void WriteByte(float data, float dest, float desto)
float floor(float f)
string strzone(string s)
float max(float f,...)
strcat(_("^F4Countdown stopped!"), "\n^BG", _("Teams are too unbalanced."))
#define error
Definition pre.qh:6
entity msg_entity
Definition progsdefs.qc:63
float SVC_INTERMISSION
Definition progsdefs.qc:340
#define stuffcmd(cl,...)
Definition progsdefs.qh:23
ERASEABLE void RandomSelection_Init()
Definition random.qc:4
string RandomSelection_chosen_string
Definition random.qh:7
#define RandomSelection_AddString(s, weight, priority)
Definition random.qh:16
#define AVAILABLE_TEAMS
void CampaignPostIntermission()
Definition campaign.qc:240
int GetPlayerLimit()
Definition client.qc:2128
string GameTypeVote_MapInfo_FixName(string m)
Definition mapvoting.qc:103
void MapVote_Start()
Definition mapvoting.qc:743
float autocvar_g_maplist_shuffle
Definition mapvoting.qh:9
bool autocvar_g_maplist_check_waypoints
Definition mapvoting.qh:4
bool autocvar_sv_vote_gametype
Definition mapvoting.qh:24
int autocvar_g_maplist_index
Definition mapvoting.qh:5
int autocvar_g_maplist_mostrecent_count
Definition mapvoting.qh:7
bool autocvar_g_maplist_sizes_count_maxplayers
Definition mapvoting.qh:21
bool autocvar_g_maplist_ignore_sizes
Definition mapvoting.qh:20
bool autocvar_g_maplist_sizes_count_bots
Definition mapvoting.qh:22
#define autocvar_g_maplist
Definition mapvoting.qh:3
float mapvote_initialized
Definition mapvoting.qh:46
string getmapname_stored
Definition mapvoting.qh:45
string autocvar_g_maplist_mostrecent
Definition mapvoting.qh:6
bool autocvar_g_maplist_selectrandom
Definition mapvoting.qh:8
#define CS_CVAR(this)
Definition state.qh:51
ERASEABLE string strwords(string s, int w)
Definition string.qh:361
#define strfree(this)
Definition string.qh:59
#define strhasword(s, w)
Definition string.qh:370
#define strcpy(this, s)
Definition string.qh:52
ERASEABLE string cons(string a, string b)
Definition string.qh:276
bool teamplay
Definition teams.qh:59
#define IS_REAL_CLIENT(v)
Definition utils.qh:17
#define FOREACH_CLIENT(cond, body)
Definition utils.qh:52
entity weaponchild
Definition all.qh:400
const int MAX_WEAPONSLOTS
Definition weapon.qh:16
entity weaponentities[MAX_WEAPONSLOTS]
Definition weapon.qh:17
string matchid
Definition world.qh:63
string autocvar_quit_and_redirect
Definition world.qh:13
bool autocvar_quit_when_empty
Definition world.qh:15
bool autocvar_sv_dedicated
Definition world.qh:41
string redirection_target
Definition world.qh:67