Xonotic
sv_clanarena.qc
Go to the documentation of this file.
1 #include "sv_clanarena.qh"
2 
5 
14 
16 
18 {
19  total_players = 0;
20  for (int i = 1; i <= NUM_TEAMS; ++i)
21  {
23  }
25  {
26  ++total_players;
27  if (IS_DEAD(it))
28  {
29  continue;
30  }
31  entity team_ = Entity_GetTeam(it);
32  int num_alive = Team_GetNumberOfAlivePlayers(team_);
33  ++num_alive;
34  Team_SetNumberOfAlivePlayers(team_, num_alive);
35  });
37  {
38  STAT(REDALIVE, it) = Team_GetNumberOfAlivePlayers(Team_GetTeamFromIndex(1));
39  STAT(BLUEALIVE, it) = Team_GetNumberOfAlivePlayers(Team_GetTeamFromIndex(2));
40  STAT(YELLOWALIVE, it) = Team_GetNumberOfAlivePlayers(Team_GetTeamFromIndex(3));
41  STAT(PINKALIVE, it) = Team_GetNumberOfAlivePlayers(Team_GetTeamFromIndex(4));
42  });
43 }
44 
45 void nades_Clear(entity player);
46 
48 {
50  {
51  Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_ROUND_OVER);
52  Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ROUND_OVER);
53  FOREACH_CLIENT(IS_PLAYER(it), { nades_Clear(it); });
54 
55  allowed_to_spawn = false;
56  game_stopped = true;
58  return 1;
59  }
60 
62  int winner_team = Team_GetWinnerAliveTeam();
63  if (!winner_team)
64  return 0;
65 
66  if(winner_team > 0)
67  {
68  Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, APP_TEAM_NUM(winner_team, CENTER_ROUND_TEAM_WIN));
69  Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(winner_team, INFO_ROUND_TEAM_WIN));
70  TeamScore_AddToTeam(winner_team, ST_CA_ROUNDS, +1);
71  }
72  else if(winner_team == -1)
73  {
74  Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_ROUND_TIED);
75  Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ROUND_TIED);
76  }
77 
78  allowed_to_spawn = false;
79  game_stopped = true;
81 
82  FOREACH_CLIENT(IS_PLAYER(it), { nades_Clear(it); });
83 
84  return 1;
85 }
86 
88 {
90 }
91 
93 {
94  static int prev_missing_teams_mask;
95  allowed_to_spawn = true;
98  {
99  if(prev_missing_teams_mask > 0)
100  Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_MISSING_TEAMS);
101  prev_missing_teams_mask = -1;
102  return true;
103  }
104  if(total_players == 0)
105  {
106  if(prev_missing_teams_mask > 0)
107  Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_MISSING_TEAMS);
108  prev_missing_teams_mask = -1;
109  return false;
110  }
111  int missing_teams_mask = 0;
112  for (int i = 1; i <= NUM_TEAMS; ++i)
113  {
114  if ((ca_teams & Team_IndexToBit(i)) &&
116  {
117  missing_teams_mask |= Team_IndexToBit(i);
118  }
119  }
120  if(prev_missing_teams_mask != missing_teams_mask)
121  {
122  Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_MISSING_TEAMS, missing_teams_mask);
123  prev_missing_teams_mask = missing_teams_mask;
124  }
125  return false;
126 }
127 
129 {
130  if(INGAME_JOINED(e) && (IS_DEAD(e) || e.frags == FRAGS_PLAYER_OUT_OF_GAME))
131  return true;
132  if(INGAME_JOINING(e))
133  return true;
134  return false;
135 }
136 
139 {
140  if (SAME_TEAM(start, player)) return start;
141  // continue from current player
142  for (entity e = start; (e = find(e, classname, STR_PLAYER)); )
143  {
144  if (SAME_TEAM(player, e)) return e;
145  }
146  // restart from the beginning
147  for (entity e = NULL; (e = find(e, classname, STR_PLAYER)); )
148  {
149  if (SAME_TEAM(player, e)) return e;
150  }
151  return start;
152 }
153 
154 
155 MUTATOR_HOOKFUNCTION(ca, PlayerSpawn)
156 {
157  entity player = M_ARGV(0, entity);
158 
160  if (time <= game_starttime) // reset on game restart, not on round start
161  player.ca_damage_counter = 0;
162  if (!warmup_stage)
163  eliminatedPlayers.SendFlags |= 1;
164 }
165 
166 MUTATOR_HOOKFUNCTION(ca, ForbidSpawn)
167 {
168  entity player = M_ARGV(0, entity);
169 
170  // spectators / observers that weren't playing can join; they are
171  // immediately forced to observe in the PutClientInServer hook
172  // this way they are put in a team and can play in the next round
173  if (!allowed_to_spawn && INGAME(player))
174  return true;
175  return false;
176 }
177 
179 {
180  entity player = M_ARGV(0, entity);
181 
182  if (!allowed_to_spawn && IS_PLAYER(player)) // this is true even when player is trying to join
183  {
184  TRANSMUTE(Observer, player);
185  if (CS(player).jointime != time && !INGAME(player)) // not when connecting
186  {
188  Send_Notification(NOTIF_ONE_ONLY, player, MSG_INFO, INFO_CA_JOIN_LATE);
189  }
190  }
191 }
192 
193 MUTATOR_HOOKFUNCTION(ca, reset_map_players)
194 {
195  FOREACH_CLIENT(true, {
196  CS(it).killcount = 0;
197  if (!INGAME(it) && IS_BOT_CLIENT(it))
198  {
199  it.team = -1;
201  }
202  if (INGAME(it))
203  {
204  TRANSMUTE(Player, it);
206  PutClientInServer(it);
207  }
208  });
209  return true;
210 }
211 
212 MUTATOR_HOOKFUNCTION(ca, reset_map_global)
213 {
214  allowed_to_spawn = true;
215  return true;
216 }
217 
219 {
220  M_ARGV(0, float) = ca_teams;
221  return true;
222 }
223 
225 {
226  entity last_pl = NULL;
227  FOREACH_CLIENT(IS_PLAYER(it) && it != this, {
228  if (!IS_DEAD(it) && SAME_TEAM(this, it))
229  {
230  if (!last_pl)
231  last_pl = it;
232  else
233  return NULL;
234  }
235  });
236  return last_pl;
237 }
238 
240 {
242  {
243  entity pl = ca_LastPlayerForTeam(this);
244  if (pl)
245  Send_Notification(NOTIF_ONE, pl, MSG_CENTER, CENTER_ALONE);
246  }
247 }
248 
249 MUTATOR_HOOKFUNCTION(ca, PlayerDies)
250 {
251  entity frag_target = M_ARGV(2, entity);
252 
253  ca_LastPlayerForTeam_Notify(frag_target);
254  if (!allowed_to_spawn)
255  {
256  frag_target.respawn_flags = RESPAWN_SILENT;
257  // prevent unwanted sudden rejoin as spectator and movement of spectator camera
258  frag_target.respawn_time = time + 2;
259  }
260  frag_target.respawn_flags |= RESPAWN_FORCE;
261  if (!warmup_stage)
262  eliminatedPlayers.SendFlags |= 1;
263  return true;
264 }
265 
266 
268 {
269  entity player = M_ARGV(0, entity);
270 
271  if (IS_PLAYER(player) && !IS_DEAD(player))
273  return true;
274 }
275 
276 MUTATOR_HOOKFUNCTION(ca, MakePlayerObserver)
277 {
278  entity player = M_ARGV(0, entity);
279 
280  bool is_forced = M_ARGV(1, bool);
281  if (is_forced && INGAME(player))
282  INGAME_STATUS_CLEAR(player);
283 
284  if (IS_PLAYER(player) && !IS_DEAD(player))
286  if (player.killindicator_teamchange == -2) // player wants to spectate
287  {
288  entcs_update_players(player);
289  INGAME_STATUS_CLEAR(player);
290  }
291  if (INGAME(player))
292  player.frags = FRAGS_PLAYER_OUT_OF_GAME;
293  if (!warmup_stage)
294  eliminatedPlayers.SendFlags |= 1;
295  if (!INGAME(player))
296  return false; // allow team reset
297  return true; // prevent team reset
298 }
299 
300 MUTATOR_HOOKFUNCTION(ca, ForbidThrowCurrentWeapon)
301 {
302  return true;
303 }
304 
305 MUTATOR_HOOKFUNCTION(ca, GiveFragsForKill, CBC_ORDER_FIRST)
306 {
307  M_ARGV(2, float) = 0; // score will be given to the winner team when the round ends
308  return true;
309 }
310 
311 MUTATOR_HOOKFUNCTION(ca, SetStartItems)
312 {
313  start_items &= ~(IT_UNLIMITED_AMMO | IT_UNLIMITED_SUPERWEAPONS);
314  if(!cvar("g_use_ammunition"))
315  start_items |= IT_UNLIMITED_AMMO;
316 
325 }
326 
327 MUTATOR_HOOKFUNCTION(ca, Damage_Calculate)
328 {
329  entity frag_attacker = M_ARGV(1, entity);
330  entity frag_target = M_ARGV(2, entity);
331  float frag_deathtype = M_ARGV(3, float);
332  float frag_damage = M_ARGV(4, float);
333  float frag_mirrordamage = M_ARGV(5, float);
334 
335  if (IS_PLAYER(frag_target))
336  if (!IS_DEAD(frag_target))
337  if (frag_target == frag_attacker || SAME_TEAM(frag_target, frag_attacker) || frag_deathtype == DEATH_FALL.m_id)
338  frag_damage = 0;
339 
340  frag_mirrordamage = 0;
341 
342  M_ARGV(4, float) = frag_damage;
343  M_ARGV(5, float) = frag_mirrordamage;
344 }
345 
346 MUTATOR_HOOKFUNCTION(ca, FilterItem)
347 {
348  entity item = M_ARGV(0, entity);
349 
350  if (autocvar_g_powerups <= 0)
351  if (item.itemdef.instanceOfPowerup)
352  return true;
353 
354  if (autocvar_g_pickup_items <= 0)
355  return true;
356 }
357 
358 MUTATOR_HOOKFUNCTION(ca, PlayerDamage_SplitHealthArmor)
359 {
360  if (time < game_starttime || (round_handler_IsActive() && !round_handler_IsRoundStarted()))
361  return;
362 
363  entity frag_attacker = M_ARGV(1, entity);
364  entity frag_target = M_ARGV(2, entity);
365  float frag_deathtype = M_ARGV(6, float);
366  float frag_damage = M_ARGV(7, float);
367  float damage_take = bound(0, M_ARGV(4, float), GetResource(frag_target, RES_HEALTH));
368  float damage_save = bound(0, M_ARGV(5, float), GetResource(frag_target, RES_ARMOR));
369 
370  float excess = max(0, frag_damage - damage_take - damage_save);
371 
372  if (autocvar_g_ca_damage2score <= 0 || frag_damage - excess == 0) return;
373 
374  entity scorer = NULL;
375  float scorer_damage = 0;
376 
377  if (IS_PLAYER(frag_attacker))
378  {
379  if (DIFF_TEAM(frag_target, frag_attacker))
380  scorer_damage = frag_damage - excess;
381  else // friendly fire
382  scorer_damage = -(frag_damage - excess);
383 
384  scorer = frag_attacker;
385  }
386  else
387  {
388  //handle (environmental hazard) suiciding, check first if player has a registered attacker who most likely pushed them there to avoid punishing pushed players as pushers are already rewarded
389  //deathtypes:
390  //kill = suicide, drown = drown in water/liquid, hurttrigger = out of the map void or hurt triggers inside maps like electric sparks
391  //camp = campcheck, lava = lava, slime = slime
392  //team change / rebalance suicides are currently not included
393  if (frag_deathtype == DEATH_KILL.m_id ||
394  frag_deathtype == DEATH_DROWN.m_id ||
395  frag_deathtype == DEATH_HURTTRIGGER.m_id ||
396  frag_deathtype == DEATH_CAMP.m_id ||
397  frag_deathtype == DEATH_LAVA.m_id ||
398  frag_deathtype == DEATH_SLIME.m_id ||
399  frag_deathtype == DEATH_SWAMP.m_id)
400  {
401  scorer_damage = -(frag_damage - excess);
402  scorer = frag_target;
403  }
404  }
405 
406  if (scorer)
408 }
409 
410 MUTATOR_HOOKFUNCTION(ca, CalculateRespawnTime)
411 {
412  // no respawn calculations needed, player is forced to spectate anyway
413  return true;
414 }
415 
416 MUTATOR_HOOKFUNCTION(ca, PlayerRegen)
417 {
418  // no regeneration in CA
419  return true;
420 }
421 
422 MUTATOR_HOOKFUNCTION(ca, Scores_CountFragsRemaining)
423 {
424  // announce remaining frags
425  return true;
426 }
427 
429 {
430  entity client = M_ARGV(0, entity);
431  entity targ = M_ARGV(1, entity);
432 
433  if (!autocvar_g_ca_spectate_enemies && INGAME(client))
434  if (DIFF_TEAM(targ, client))
435  return true;
436 }
437 
439 {
440  entity client = M_ARGV(0, entity);
441 
442  if (!autocvar_g_ca_spectate_enemies && INGAME(client)
444  {
445  entity targ = M_ARGV(1, entity);
446  M_ARGV(1, entity) = CA_SpectateNext(client, targ);
447  return true;
448  }
449 }
450 
452 {
453  entity client = M_ARGV(0, entity);
454  entity targ = M_ARGV(1, entity);
455  entity first = M_ARGV(2, entity);
456 
457  if (!autocvar_g_ca_spectate_enemies && INGAME(client)
459  {
460  do { targ = targ.chain; }
461  while(targ && DIFF_TEAM(targ, client));
462 
463  if (!targ)
464  {
465  for (targ = first; targ && DIFF_TEAM(targ, client); targ = targ.chain);
466 
467  if (targ == client.enemy)
468  return MUT_SPECPREV_RETURN;
469  }
470  }
471  else
472  return MUT_SPECPREV_CONTINUE;
473 
474  M_ARGV(1, entity) = targ;
475 
476  return MUT_SPECPREV_FOUND;
477 }
478 
480 {
482  if (IS_PLAYER(it) || INGAME_JOINED(it))
483  ++M_ARGV(0, int);
484  ++M_ARGV(1, int);
485  });
486  return true;
487 }
488 
489 MUTATOR_HOOKFUNCTION(ca, ClientCommand_Spectate)
490 {
491  entity player = M_ARGV(0, entity);
492 
493  if (INGAME(player))
494  {
495  // they're going to spec, we can do other checks
496  if (autocvar_sv_spectate && (IS_SPEC(player) || IS_OBSERVER(player)))
497  Send_Notification(NOTIF_ONE_ONLY, player, MSG_INFO, INFO_CA_LEAVE);
498  return MUT_SPECCMD_FORCE;
499  }
500 
501  return MUT_SPECCMD_CONTINUE;
502 }
503 
504 MUTATOR_HOOKFUNCTION(ca, HideTeamNagger)
505 {
506  return true; // doesn't work well with the whole spectator as player thing
507 }
508 
509 MUTATOR_HOOKFUNCTION(ca, SetWeaponArena)
510 {
511  if (M_ARGV(0, string) == "0" || M_ARGV(0, string) == "")
512  M_ARGV(0, string) = autocvar_g_ca_weaponarena;
513 }
514 
515 MUTATOR_HOOKFUNCTION(ca, SV_ParseServerCommand)
516 {
517  string cmd_name = M_ARGV(0, string);
518  if (cmd_name == "shuffleteams")
520  return false;
521 }
#define INGAME(it)
Definition: sv_rules.qh:20
const int NUM_TEAMS
Number of teams in the game.
Definition: teams.qh:3
int Team_IndexToBit(int index)
Converts team index into bit value that is used in team bitmasks.
Definition: teams.qh:211
const int RESPAWN_FORCE
Definition: client.qh:330
#define APP_TEAM_NUM(num, prefix)
Definition: all.qh:85
#define round_handler_IsActive()
float start_ammo_rockets
Definition: world.qh:87
#define INGAME_STATUS_SET(it, s)
Definition: sv_rules.qh:17
const int CBC_ORDER_EXCLUSIVE
Definition: base.qh:9
float autocvar_g_ca_start_ammo_fuel
Definition: sv_clanarena.qc:13
TRANSMUTE(Player, this)
void ca_LastPlayerForTeam_Notify(entity this)
int NumTeams(int teams)
Definition: scores_rules.qc:17
float start_ammo_fuel
Definition: world.qh:90
float warmup_start_ammo_shells
Definition: world.qh:104
#define INGAME_STATUS_JOINED
Definition: sv_rules.qh:13
MUTATOR_HOOKFUNCTION(ca, PlayerSpawn)
float TeamScore_AddToTeam(int t, float scorefield, float score)
Adds a score to the given team.
Definition: scores.qc:108
float autocvar_g_ca_start_health
Definition: sv_clanarena.qc:6
entity() spawn
int Team_GetNumberOfAlivePlayers(entity team_ent)
Returns the number of alive players in a team.
Definition: teamplay.qc:85
ClientState CS(Client this)
Definition: state.qh:47
void nades_Clear(entity player)
#define FOREACH_CLIENT(cond, body)
Definition: utils.qh:49
#define IS_OBSERVER(v)
Definition: utils.qh:11
void CA_RoundStart()
Definition: sv_clanarena.qc:87
bool warmup_stage
Definition: main.qh:103
int start_items
Definition: world.qh:84
float autocvar_g_ca_start_ammo_rockets
Definition: sv_clanarena.qc:10
bool allowed_to_spawn
Definition: sv_clanarena.qh:19
float start_ammo_shells
Definition: world.qh:85
int autocvar_g_powerups
Definition: sv_powerups.qh:7
string classname
Definition: csprogsdefs.qc:107
#define round_handler_IsRoundStarted()
#define DIFF_TEAM(a, b)
Definition: teams.qh:240
float autocvar_g_ca_start_armor
Definition: sv_clanarena.qc:7
entity Entity_GetTeam(entity this)
Returns the team entity of the given entity.
Definition: teamplay.qc:181
float warmup_start_ammo_plasma
Definition: world.qh:108
ClientDisconnect(this)
float autocvar_g_ca_start_ammo_nails
Definition: sv_clanarena.qc:9
float autocvar_g_ca_start_ammo_plasma
Definition: sv_clanarena.qc:12
#define IS_REAL_CLIENT(v)
Definition: utils.qh:17
float CA_CheckWinner()
Definition: sv_clanarena.qc:47
RES_HEALTH
Definition: ent_cs.qc:126
bool Entity_HasValidTeam(entity this)
Returns whether the given entity belongs to a valid team.
Definition: teamplay.qc:171
#define IS_SPEC(v)
Definition: utils.qh:10
bool autocvar_g_ca_spectate_enemies
Definition: sv_clanarena.qc:4
bool SpectateNext(entity this)
Definition: client.qc:1883
float warmup_start_ammo_cells
Definition: world.qh:107
void CA_count_alive_players()
Definition: sv_clanarena.qc:17
bool CA_CheckTeams()
Definition: sv_clanarena.qc:92
float warmup_start_ammo_fuel
Definition: world.qh:109
float autocvar_g_ca_start_ammo_shells
Definition: sv_clanarena.qc:8
float start_ammo_cells
Definition: world.qh:88
const int CBC_ORDER_FIRST
Definition: base.qh:7
#define NULL
Definition: post.qh:17
entity ca_LastPlayerForTeam(entity this)
int autocvar_sv_spectate
Definition: client.qh:54
void Team_SetNumberOfAlivePlayers(entity team_ent, int number)
Sets the number of alive players in a team.
Definition: teamplay.qc:90
float autocvar_g_ca_start_ammo_cells
Definition: sv_clanarena.qc:11
float jointime
Definition: client.qh:64
#define round_handler_GetEndTime()
#define SAME_TEAM(a, b)
Definition: teams.qh:239
#define GameRules_scoring_add_float2int(client, fld, value, float_field, score_factor)
Definition: sv_rules.qh:75
#define M_ARGV(x, type)
Definition: events.qh:17
#define IS_DEAD(s)
Definition: utils.qh:26
#define INGAME_STATUS_CLEAR(it)
Definition: sv_rules.qh:18
int Team_GetNumberOfAliveTeams()
Returns the number of alive teams.
Definition: teamplay.qc:110
entity Team_GetTeamFromIndex(int index)
Returns the global team entity at the given index.
Definition: teamplay.qc:57
int total_players
Definition: sv_rules.qh:8
float warmup_start_armorvalue
Definition: world.qh:111
entity CA_SpectateNext(entity player, entity start)
Returns next available player to spectate if g_ca_spectate_enemies == 0.
float autocvar_g_ca_round_timelimit
Definition: sv_clanarena.qh:10
bool shuffleteams_on_reset_map
Definition: sv_cmd.qh:7
const int ST_CA_ROUNDS
Definition: sv_clanarena.qh:21
float GetResource(entity e, Resource res_type)
Returns the current amount of resource the given entity has.
Definition: cl_resources.qc:10
float start_health
Definition: world.qh:98
const int FRAGS_PLAYER_OUT_OF_GAME
Definition: constants.qh:5
const string STR_PLAYER
Definition: utils.qh:5
float autocvar_g_ca_warmup
Definition: sv_clanarena.qh:14
entity eliminatedPlayers
Definition: elimination.qh:3
float warmup_start_ammo_rockets
Definition: world.qh:106
int ca_teams
Definition: sv_clanarena.qh:18
string autocvar_g_ca_weaponarena
Definition: sv_clanarena.qh:15
#define INGAME_JOINED(it)
Definition: sv_rules.qh:21
float start_ammo_nails
Definition: world.qh:86
float ca_damage_counter
Definition: sv_clanarena.qc:15
#define IS_BOT_CLIENT(v)
want: (IS_CLIENT(v) && !IS_REAL_CLIENT(v))
Definition: utils.qh:15
float warmup_start_health
Definition: world.qh:110
bool ca_isEliminated(entity e)
int Team_GetWinnerAliveTeam()
Returns the winner team.
Definition: teamplay.qc:95
int autocvar_g_pickup_items
Definition: items.qh:11
PutClientInServer(this)
entity TeamBalance_CheckAllowedTeams(entity for_whom)
Checks whether the player can join teams according to global configuration and mutator settings...
Definition: teamplay.qc:487
bool SpectateSet(entity this)
Definition: client.qc:1800
float time
Definition: csprogsdefs.qc:16
float start_armorvalue
Definition: world.qh:99
#define INGAME_JOINING(it)
Definition: sv_rules.qh:22
void round_handler_Init(float the_delay, float the_count, float the_round_timelimit)
float start_ammo_plasma
Definition: world.qh:89
#define boolean(value)
Definition: bool.qh:9
float autocvar_g_ca_damage2score
Definition: sv_clanarena.qc:3
#define IS_PLAYER(v)
Definition: utils.qh:9
const int RESPAWN_SILENT
Definition: client.qh:331
#define INGAME_STATUS_JOINING
Definition: sv_rules.qh:12
bool SpectatePrev(entity this)
Definition: client.qc:1897
float warmup_start_ammo_nails
Definition: world.qh:105