Xonotic
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>
10 #include <server/scores_rules.qh>
11 #include <server/world.qh>
12 
13 string GetGametype()
14 {
16 }
17 
18 string GetMapname()
19 {
20  return mapname;
21 }
22 
25 
26 // NOTE: this now expects the map list to be already tokenized and the count in Map_Count
28 {
29  string map = GetMapname();
30  int idx = autocvar_g_maplist_index;
31 
32  if(idx >= 0)
33  {
34  if(idx < Map_Count)
35  {
36  if(map == argv(idx))
37  {
38  return idx;
39  }
40  }
41  }
42 
43  for(int pos = 0; pos < Map_Count; ++pos)
44  {
45  if(map == argv(pos))
46  return pos;
47  }
48 
49  // resume normal maplist rotation if current map is not in g_maplist
50  return idx;
51 }
52 
53 bool MapHasRightSize(string map)
54 {
55  int minplayers = max(0, floor(autocvar_minplayers));
56  if (teamplay)
59  && (currentbots || autocvar_bot_number || player_count < minplayers))
60  {
61  string checkwp_msg = strcat("checkwp ", map);
62  if(!fexists(strcat("maps/", map, ".waypoints")))
63  {
64  LOG_TRACE(checkwp_msg, ": no waypoints");
65  return false;
66  }
67  LOG_TRACE(checkwp_msg, ": has waypoints");
68  }
69 
71  return true;
72 
73  // open map size restriction file
74  string opensize_msg = strcat("opensize ", map);
75  float fh = fopen(strcat("maps/", map, ".sizes"), FILE_READ);
76  int player_limit = ((autocvar_g_maplist_sizes_count_maxplayers) ? GetPlayerLimit() : 0);
77  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
79  pcount -= currentbots;
80  if(fh >= 0)
81  {
82  opensize_msg = strcat(opensize_msg, ": ok, ");
83  int mapmin = stoi(fgets(fh));
84  int mapmax = stoi(fgets(fh));
85  fclose(fh);
86  if(pcount < mapmin)
87  {
88  LOG_TRACE(opensize_msg, "not enough");
89  return false;
90  }
91  if(mapmax && pcount > mapmax)
92  {
93  LOG_TRACE(opensize_msg, "too many");
94  return false;
95  }
96  LOG_TRACE(opensize_msg, "right size");
97  return true;
98  }
99  LOG_TRACE(opensize_msg, ": not found");
100  return true;
101 }
102 
103 string Map_Filename(int position)
104 {
105  return strcat("maps/", argv(position), ".bsp");
106 }
107 
108 void Map_MarkAsRecent(string m)
109 {
111 }
112 
113 bool Map_IsRecent(string m)
114 {
116 }
117 
118 bool Map_Check(int position, float pass)
119 {
120  string filename;
121  string map_next;
122  map_next = argv(position);
123  if(pass <= 1)
124  {
125  if(Map_IsRecent(map_next))
126  return false;
127  }
128  filename = Map_Filename(position);
129  if(MapInfo_CheckMap(map_next))
130  {
131  if(pass == 2)
132  return true;
133  if(MapHasRightSize(map_next))
134  return true;
135  return false;
136  }
137  else
138  LOG_DEBUG( "Couldn't select '", filename, "'..." );
139 
140  return false;
141 }
142 
143 void Map_Goto_SetStr(string nextmapname)
144 {
145  if(getmapname_stored != "")
147  if(nextmapname == "")
148  getmapname_stored = "";
149  else
150  getmapname_stored = strzone(nextmapname);
151 }
152 
153 void Map_Goto_SetFloat(float position)
154 {
155  cvar_set("g_maplist_index", ftos(position));
156  Map_Goto_SetStr(argv(position));
157 }
158 
159 void Map_Goto(float reinit)
160 {
162 }
163 
164 // return codes of map selectors:
165 // -1 = temporary failure (that is, try some method that is guaranteed to succeed)
166 // -2 = permanent failure
167 float MaplistMethod_Iterate() // usual method
168 {
169  float pass, i;
170 
171  LOG_TRACE("Trying MaplistMethod_Iterate");
172 
173  for(pass = 1; pass <= 2; ++pass)
174  {
175  for(i = 1; i < Map_Count; ++i)
176  {
177  float mapindex;
178  mapindex = (i + Map_Current) % Map_Count;
179  if(Map_Check(mapindex, pass))
180  return mapindex;
181  }
182  }
183  return -1;
184 }
185 
186 float MaplistMethod_Repeat() // fallback method
187 {
188  LOG_TRACE("Trying MaplistMethod_Repeat");
189 
190  if(Map_Check(Map_Current, 2))
191  return Map_Current;
192  return -2;
193 }
194 
195 float MaplistMethod_Random() // random map selection
196 {
197  float i, imax;
198 
199  LOG_TRACE("Trying MaplistMethod_Random");
200 
201  imax = 42;
202 
203  for(i = 0; i <= imax; ++i)
204  {
205  float mapindex;
206  mapindex = (Map_Current + floor(random() * (Map_Count - 1) + 1)) % Map_Count; // any OTHER map
207  if(Map_Check(mapindex, 1))
208  return mapindex;
209  }
210  return -1;
211 }
212 
213 float MaplistMethod_Shuffle(float exponent) // more clever shuffling
214 // the exponent sets a bias on the map selection:
215 // the higher the exponent, the less likely "shortly repeated" same maps are
216 {
217  float i, j, imax, insertpos;
218 
219  LOG_TRACE("Trying MaplistMethod_Shuffle");
220 
221  imax = 42;
222 
223  for(i = 0; i <= imax; ++i)
224  {
225  string newlist;
226 
227  // now reinsert this at another position
228  insertpos = (random() ** (1 / exponent)); // ]0, 1]
229  insertpos = insertpos * (Map_Count - 1); // ]0, Map_Count - 1]
230  insertpos = ceil(insertpos) + 1; // {2, 3, 4, ..., Map_Count}
231  LOG_TRACE("SHUFFLE: insert pos = ", ftos(insertpos));
232 
233  // insert the current map there
234  newlist = "";
235  for(j = 1; j < insertpos; ++j) // i == 1: no loop, will be inserted as first; however, i == 1 has been excluded above
236  newlist = strcat(newlist, " ", argv(j));
237  newlist = strcat(newlist, " ", argv(0)); // now insert the just selected map
238  for(j = insertpos; j < Map_Count; ++j) // i == Map_Count: no loop, has just been inserted as last
239  newlist = strcat(newlist, " ", argv(j));
240  newlist = substring(newlist, 1, strlen(newlist) - 1);
241  cvar_set("g_maplist", newlist);
242  Map_Count = tokenizebyseparator(autocvar_g_maplist, " ");
243 
244  // NOTE: the selected map has just been inserted at (insertpos-1)th position
245  Map_Current = insertpos - 1; // this is not really valid, but this way the fallback has a chance of working
246  if(Map_Check(Map_Current, 1))
247  return Map_Current;
248  }
249  return -1;
250 }
251 
253 {
254  float i = Map_Count = 0;
255  if(autocvar_g_maplist != "")
256  {
258  for (i = 0; i < Map_Count; ++i)
259  {
260  if (Map_Check(i, 2))
261  break;
262  }
263  }
264 
265  if (i == Map_Count)
266  {
267  bprint( "Maplist contains no usable maps! Resetting it to default map list.\n" );
270  ShuffleMaplist();
272  localcmd("\nmenu_cmd sync\n");
274  }
275  if(Map_Count == 0)
276  error("empty maplist, cannot select a new map");
278 
279  strcpy(Map_Current_Name, argv(Map_Current)); // will be automatically freed on exit thanks to DP
280  // this may or may not be correct, but who cares, in the worst case a map
281  // isn't chosen in the first pass that should have been
282 }
283 
284 string GetNextMap()
285 {
286  Maplist_Init();
287  float nextMap = -1;
288 
289  if(nextMap == -1)
292 
293  if(nextMap == -1)
295  nextMap = MaplistMethod_Random();
296 
297  if(nextMap == -1)
298  nextMap = MaplistMethod_Iterate();
299 
300  if(nextMap == -1)
301  nextMap = MaplistMethod_Repeat();
302 
303  if(nextMap >= 0)
304  {
305  Map_Goto_SetFloat(nextMap);
306  return getmapname_stored;
307  }
308 
309  return "";
310 }
311 
312 float DoNextMapOverride(float reinit)
313 {
315  {
317  alreadychangedlevel = true;
318  return true;
319  }
321  {
323  {
324  localcmd("quit\n");
325  alreadychangedlevel = true;
326  return true;
327  }
328  }
330  {
332  alreadychangedlevel = true;
333  return true;
334  }
335  if (!reinit && autocvar_samelevel) // if samelevel is set, stay on same level
336  {
337  localcmd("restart\n");
338  alreadychangedlevel = true;
339  return true;
340  }
341  if(autocvar_nextmap != "")
342  {
343  string m;
345  cvar_set("nextmap",m);
346 
347  if(!m || gametypevote)
348  return false;
350  {
351  Map_Goto_SetStr(m);
352  return false;
353  }
354 
355  if(MapInfo_CheckMap(m))
356  {
357  Map_Goto_SetStr(m);
358  Map_Goto(reinit);
359  alreadychangedlevel = true;
360  return true;
361  }
362  }
363  if(!reinit && autocvar_lastlevel)
364  {
366  localcmd("set lastlevel 0\ntogglemenu 1\n");
367  alreadychangedlevel = true;
368  return true;
369  }
370  return false;
371 }
372 
373 void GotoNextMap(float reinit)
374 {
375  //string nextmap;
376  //float n, nummaps;
377  //string s;
379  return;
380  alreadychangedlevel = true;
381 
382  string nextMap = GetNextMap();
383  if(nextMap == "")
384  error("Everything is broken - cannot find a next map. Please report this to the developers.");
385  Map_Goto(reinit);
386 }
387 
389 {
391 }
392 
393 string GotoMap(string m)
394 {
396  if (!m)
397  return "The map you suggested is not available on this server.";
399  if(!MapInfo_CheckMap(m))
400  return "The map you suggested does not support the current game mode.";
401  cvar_set("nextmap", m);
402  cvar_set("_endmatch", "1");
404  {
405  if(DoNextMapOverride(0))
406  return "Map switch initiated.";
407  else
408  return "Hm... no. For some reason I like THIS map more.";
409  }
410  else
411  return "Map switch will happen after scoreboard.";
412 }
413 
414 
415 /*
416 ============
417 IntermissionThink
418 
419 When the player presses attack or jump, change to the next level
420 ============
421 */
424 {
425  FixIntermissionClient(this);
426 
427  float server_screenshot = (autocvar_sv_autoscreenshot && CS_CVAR(this).cvar_cl_autoscreenshot);
428  float client_screenshot = (CS_CVAR(this).cvar_cl_autoscreenshot == 2);
429 
430  if( (server_screenshot || client_screenshot)
431  && ((this.autoscreenshot > 0) && (time > this.autoscreenshot)) )
432  {
433  this.autoscreenshot = -1;
434  if(IS_REAL_CLIENT(this))
435  {
436  string num = strftime_s(); // strftime(false, "%s") isn't reliable, see strftime_s description
437  stuffcmd(this, sprintf("\nscreenshot screenshots/autoscreenshot/%s-%s.jpg; "
438  "echo \"^5A screenshot has been taken at request of the server.\"\n", GetMapname(), num));
439  }
440  return;
441  }
442 
444  return;
445 
448  return;
449 
450  MapVote_Start();
451 }
452 
454 {
455  if(!e.autoscreenshot) // initial call
456  {
457  e.autoscreenshot = time + 0.8; // used for autoscreenshot
458  SetResourceExplicit(e, RES_HEALTH, -2342); // health in the first intermission phase
459  for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
460  {
461  .entity weaponentity = weaponentities[slot];
462  if(e.(weaponentity))
463  {
464  e.(weaponentity).effects = EF_NODRAW;
465  if (e.(weaponentity).weaponchild)
466  e.(weaponentity).weaponchild.effects = EF_NODRAW;
467  }
468  }
469  if(IS_REAL_CLIENT(e))
470  {
471  stuffcmd(e, "\nscr_printspeed 1000000\n");
474  RandomSelection_AddString(it, 1, 1);
475  });
477  {
478  stuffcmd(e, sprintf("\ncd loop %s\n", RandomSelection_chosen_string));
479  }
480  msg_entity = e;
482  }
483  }
484 }
#define PHYS_INPUT_BUTTON_ATCK2(s)
Definition: player.qh:148
string MapInfo_Type_ToString(Gametype t)
Definition: mapinfo.qc:616
#define PHYS_INPUT_BUTTON_JUMP(s)
Definition: player.qh:147
int MapInfo_RequiredFlags()
Definition: mapinfo.qc:1339
string RandomSelection_chosen_string
Definition: random.qh:7
float intermission_exittime
Definition: intermission.qh:10
bool SetResourceExplicit(entity e, Resource res_type, float amount)
Sets the resource amount of an entity without calling any hooks.
Definition: cl_resources.qc:15
#define PHYS_INPUT_BUTTON_HOOK(s)
Definition: player.qh:151
string Map_Filename(int position)
void GotoNextMap(float reinit)
float MaplistMethod_Repeat()
float MaplistMethod_Random()
void Maplist_Init()
string GetMapname()
Definition: intermission.qc:18
string GetNextMap()
ERASEABLE void RandomSelection_Init()
Definition: random.qc:4
bool autocvar_lastlevel
Definition: intermission.qh:3
entity() spawn
int player_count
Definition: api.qh:103
string autocvar_g_maplist_mostrecent
Definition: mapvoting.qh:6
Gametype MapInfo_LoadedGametype
Definition: mapinfo.qh:193
string MapInfo_ListAllAllowedMaps(float pRequiredFlags, float pForbiddenFlags)
Definition: mapinfo.qc:1216
int GetPlayerLimit()
Definition: client.qc:1990
#define CS_CVAR(this)
Definition: state.qh:51
const float FILE_READ
Definition: csprogsdefs.qc:231
#define RandomSelection_AddString(s, weight, priority)
Definition: random.qh:16
bool autocvar_quit_when_empty
Definition: world.qh:14
int MapInfo_ForbiddenFlags()
Definition: mapinfo.qc:1324
float Map_Count
Definition: intermission.qc:23
ERASEABLE string shufflewords(string str)
Definition: string.qh:307
#define strhasword(s, w)
Definition: string.qh:352
void Map_Goto_SetStr(string nextmapname)
int autocvar_minplayers
Definition: cvars.qh:71
bool MapHasRightSize(string map)
Definition: intermission.qc:53
float effects
Definition: csprogsdefs.qc:111
string GameTypeVote_MapInfo_FixName(string m)
Definition: mapvoting.qc:95
string autocvar_nextmap
Definition: intermission.qh:4
#define FOREACH_WORD(words, cond, body)
Definition: iter.qh:33
int autocvar_g_maplist_index
Definition: mapvoting.qh:5
#define IS_REAL_CLIENT(v)
Definition: utils.qh:17
#define strcpy(this, s)
Definition: string.qh:49
int currentbots
Definition: api.qh:104
#define stoi(s)
Definition: int.qh:4
string mapname
Definition: csprogsdefs.qc:26
RES_HEALTH
Definition: ent_cs.qc:126
float Map_Current
Definition: intermission.qc:23
const float EF_NODRAW
Definition: csprogsdefs.qc:305
void IntermissionThink(entity this)
string redirection_target
Definition: world.qh:61
void Map_Goto(float reinit)
float MaplistMethod_Shuffle(float exponent)
void Map_MarkAsRecent(string m)
bool Map_Check(int position, float pass)
bool autocvar_g_maplist_selectrandom
Definition: mapvoting.qh:8
#define autocvar_g_maplist
Definition: mapvoting.qh:3
entity msg_entity
Definition: progsdefs.qc:63
spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 spree_cen s1 f1 s1 strcat(_("Level %s: "), "^BG%s\3\, _("^BGPress ^F2%s^BG to enter the game"))
const int MAX_WEAPONSLOTS
Definition: weapon.qh:13
void CampaignPostIntermission()
Definition: campaign.qc:244
#define PHYS_INPUT_BUTTON_USE(s)
Definition: player.qh:154
bool autocvar_sv_autoscreenshot
Definition: intermission.qh:6
void ShuffleMaplist()
float DoNextMapOverride(float reinit)
int autocvar_bot_number
Definition: cvars.qh:67
#define PHYS_INPUT_BUTTON_ATCK(s)
Definition: player.qh:146
int AvailableTeams()
Definition: scores_rules.qc:22
float teamplay
Definition: progsdefs.qc:31
float MapInfo_CheckMap(string s)
Definition: mapinfo.qc:1170
bool autocvar_g_maplist_check_waypoints
Definition: mapvoting.qh:4
bool Map_IsRecent(string m)
#define stuffcmd(cl,...)
Definition: progsdefs.qh:23
float gametypevote
Definition: mapvoting.qc:40
#define LOG_TRACE(...)
Definition: log.qh:81
void FixIntermissionClient(entity e)
void MapVote_Start()
Definition: mapvoting.qc:632
string GetGametype()
Definition: intermission.qc:13
bool alreadychangedlevel
Definition: intermission.qh:11
#define tokenizebyseparator
Definition: dpextensions.qh:21
bool autocvar_samelevel
Definition: intermission.qh:5
entity weaponentities[MAX_WEAPONSLOTS]
Definition: weapon.qh:14
float MaplistMethod_Iterate()
float autocvar_g_maplist_shuffle
Definition: mapvoting.qh:9
float mapvote_initialized
Definition: mapvoting.qh:39
ERASEABLE string strwords(string s, int w)
Definition: string.qh:343
float SVC_INTERMISSION
Definition: progsdefs.qc:340
bool autocvar_g_campaign
Definition: campaign.qh:6
ERASEABLE string cons(string a, string b)
Definition: string.qh:257
int autocvar_minplayers_per_team
Definition: cvars.qh:72
int cvar_settemp_restore()
Definition: util.qc:736
bool autocvar_g_maplist_ignore_sizes
Definition: mapvoting.qh:18
int GetMaplistPosition()
Definition: intermission.qc:27
string GotoMap(string m)
bool autocvar_g_maplist_sizes_count_bots
Definition: mapvoting.qh:20
void Map_Goto_SetFloat(float position)
string autocvar_quit_and_redirect
Definition: world.qh:12
void MapInfo_LoadMap(string s, float reinit)
Definition: mapinfo.qc:1183
float time
Definition: csprogsdefs.qc:16
#define pass(name, colormin, colormax)
ERASEABLE bool fexists(string f)
Definition: file.qh:4
string autocvar_sv_intermission_cdtrack
Definition: intermission.qh:7
string getmapname_stored
Definition: mapvoting.qh:38
ERASEABLE string strftime_s()
Definition: string.qh:91
bool autocvar_g_maplist_sizes_count_maxplayers
Definition: mapvoting.qh:19
string Map_Current_Name
Definition: intermission.qc:24
const int MAPINFO_FLAG_NOAUTOMAPLIST
Definition: mapinfo.qh:142
bool server_is_dedicated
Definition: world.qh:37
int autocvar_g_maplist_mostrecent_count
Definition: mapvoting.qh:7
bool autocvar_sv_vote_gametype
Definition: mapvoting.qh:22
float autoscreenshot
#define LOG_DEBUG(...)
Definition: log.qh:85