1 #include "clanarena.qh"
3 // TODO: split into sv_clanarena
5 float autocvar_g_ca_damage2score_multiplier;
6 bool autocvar_g_ca_spectate_enemies;
8 void CA_count_alive_players()
10 total_players = redalive = bluealive = yellowalive = pinkalive = 0;
11 FOREACH_CLIENT(IS_PLAYER(it), {
14 case NUM_TEAM_1: ++total_players; if(!IS_DEAD(it)) ++redalive; break;
15 case NUM_TEAM_2: ++total_players; if(!IS_DEAD(it)) ++bluealive; break;
16 case NUM_TEAM_3: ++total_players; if(!IS_DEAD(it)) ++yellowalive; break;
17 case NUM_TEAM_4: ++total_players; if(!IS_DEAD(it)) ++pinkalive; break;
20 FOREACH_CLIENT(IS_REAL_CLIENT(it), {
21 STAT(REDALIVE, it) = redalive;
22 STAT(BLUEALIVE, it) = bluealive;
23 STAT(YELLOWALIVE, it) = yellowalive;
24 STAT(PINKALIVE, it) = pinkalive;
28 float CA_GetWinnerTeam()
30 float winner_team = 0;
32 winner_team = NUM_TEAM_1;
35 if(winner_team) return 0;
36 winner_team = NUM_TEAM_2;
40 if(winner_team) return 0;
41 winner_team = NUM_TEAM_3;
45 if(winner_team) return 0;
46 winner_team = NUM_TEAM_4;
50 return -1; // no player left
53 void nades_Clear(entity player);
55 #define CA_ALIVE_TEAMS() ((redalive > 0) + (bluealive > 0) + (yellowalive > 0) + (pinkalive > 0))
56 #define CA_ALIVE_TEAMS_OK() (CA_ALIVE_TEAMS() == NumTeams(ca_teams))
57 float CA_CheckWinner()
59 if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0)
61 Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_ROUND_OVER);
62 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ROUND_OVER);
63 FOREACH_CLIENT(IS_PLAYER(it), { nades_Clear(it); });
65 allowed_to_spawn = false;
67 round_handler_Init(5, autocvar_g_ca_warmup, autocvar_g_ca_round_timelimit);
71 CA_count_alive_players();
72 if(CA_ALIVE_TEAMS() > 1)
75 int winner_team = CA_GetWinnerTeam();
78 Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, APP_TEAM_NUM(winner_team, CENTER_ROUND_TEAM_WIN));
79 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(winner_team, INFO_ROUND_TEAM_WIN));
80 TeamScore_AddToTeam(winner_team, ST_CA_ROUNDS, +1);
82 else if(winner_team == -1)
84 Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_ROUND_TIED);
85 Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ROUND_TIED);
88 allowed_to_spawn = false;
90 round_handler_Init(5, autocvar_g_ca_warmup, autocvar_g_ca_round_timelimit);
92 FOREACH_CLIENT(IS_PLAYER(it), { nades_Clear(it); });
99 allowed_to_spawn = boolean(warmup_stage);
104 static int prev_missing_teams_mask;
105 allowed_to_spawn = true;
106 CA_count_alive_players();
107 if(CA_ALIVE_TEAMS_OK())
109 if(prev_missing_teams_mask > 0)
110 Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_MISSING_TEAMS);
111 prev_missing_teams_mask = -1;
114 if(total_players == 0)
116 if(prev_missing_teams_mask > 0)
117 Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_MISSING_TEAMS);
118 prev_missing_teams_mask = -1;
121 int missing_teams_mask = 0;
122 if(ca_teams & BIT(0))
123 missing_teams_mask += (!redalive) * 1;
124 if(ca_teams & BIT(1))
125 missing_teams_mask += (!bluealive) * 2;
126 if(ca_teams & BIT(2))
127 missing_teams_mask += (!yellowalive) * 4;
128 if(ca_teams & BIT(3))
129 missing_teams_mask += (!pinkalive) * 8;
130 if(prev_missing_teams_mask != missing_teams_mask)
132 Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_MISSING_TEAMS, missing_teams_mask);
133 prev_missing_teams_mask = missing_teams_mask;
138 bool ca_isEliminated(entity e)
140 if(e.caplayer == 1 && (IS_DEAD(e) || e.frags == FRAGS_LMS_LOSER))
142 if(e.caplayer == 0.5)
147 /** Returns next available player to spectate if g_ca_spectate_enemies == 0 */
148 entity CA_SpectateNext(entity player, entity start)
150 if (SAME_TEAM(start, player)) return start;
151 // continue from current player
152 for (entity e = start; (e = find(e, classname, STR_PLAYER)); )
154 if (SAME_TEAM(player, e)) return e;
156 // restart from begining
157 for (entity e = NULL; (e = find(e, classname, STR_PLAYER)); )
159 if (SAME_TEAM(player, e)) return e;
165 MUTATOR_HOOKFUNCTION(ca, PlayerSpawn)
167 entity player = M_ARGV(0, entity);
171 eliminatedPlayers.SendFlags |= 1;
174 MUTATOR_HOOKFUNCTION(ca, ForbidSpawn)
176 entity player = M_ARGV(0, entity);
178 // spectators / observers that weren't playing can join; they are
179 // immediately forced to observe in the PutClientInServer hook
180 // this way they are put in a team and can play in the next round
181 if (!allowed_to_spawn && player.caplayer)
186 MUTATOR_HOOKFUNCTION(ca, PutClientInServer)
188 entity player = M_ARGV(0, entity);
190 if (!allowed_to_spawn && IS_PLAYER(player)) // this is true even when player is trying to join
192 TRANSMUTE(Observer, player);
193 if (CS(player).jointime != time && !player.caplayer) // not when connecting
195 player.caplayer = 0.5;
196 Send_Notification(NOTIF_ONE_ONLY, player, MSG_INFO, INFO_CA_JOIN_LATE);
201 MUTATOR_HOOKFUNCTION(ca, reset_map_players)
203 FOREACH_CLIENT(true, {
204 CS(it).killcount = 0;
205 if (!it.caplayer && IS_BOT_CLIENT(it))
212 TRANSMUTE(Player, it);
214 PutClientInServer(it);
217 bot_relinkplayerlist();
221 MUTATOR_HOOKFUNCTION(ca, ClientConnect)
223 entity player = M_ARGV(0, entity);
225 TRANSMUTE(Observer, player);
229 MUTATOR_HOOKFUNCTION(ca, reset_map_global)
231 allowed_to_spawn = true;
235 MUTATOR_HOOKFUNCTION(ca, CheckAllowedTeams, CBC_ORDER_EXCLUSIVE)
237 M_ARGV(0, float) = ca_teams;
240 entity ca_LastPlayerForTeam(entity this)
242 entity last_pl = NULL;
243 FOREACH_CLIENT(IS_PLAYER(it) && it != this, {
245 if (SAME_TEAM(this, it))
254 void ca_LastPlayerForTeam_Notify(entity this)
256 if (round_handler_IsActive())
257 if (round_handler_IsRoundStarted())
259 entity pl = ca_LastPlayerForTeam(this);
261 Send_Notification(NOTIF_ONE, pl, MSG_CENTER, CENTER_ALONE);
265 MUTATOR_HOOKFUNCTION(ca, PlayerDies)
267 entity frag_target = M_ARGV(2, entity);
269 ca_LastPlayerForTeam_Notify(frag_target);
270 if (!allowed_to_spawn)
272 frag_target.respawn_flags = RESPAWN_SILENT;
273 // prevent unwanted sudden rejoin as spectator and movement of spectator camera
274 frag_target.respawn_time = time + 2;
276 frag_target.respawn_flags |= RESPAWN_FORCE;
278 eliminatedPlayers.SendFlags |= 1;
279 if(IS_BOT_CLIENT(frag_target))
280 bot_clear(frag_target);
284 MUTATOR_HOOKFUNCTION(ca, ClientDisconnect)
286 entity player = M_ARGV(0, entity);
288 if (player.caplayer == 1)
289 ca_LastPlayerForTeam_Notify(player);
293 MUTATOR_HOOKFUNCTION(ca, MakePlayerObserver)
295 entity player = M_ARGV(0, entity);
297 if (!IS_DEAD(player))
298 ca_LastPlayerForTeam_Notify(player);
299 if (player.killindicator_teamchange == -2) // player wants to spectate
302 player.frags = FRAGS_LMS_LOSER;
304 eliminatedPlayers.SendFlags |= 1;
305 if (!player.caplayer)
306 return false; // allow team reset
307 return true; // prevent team reset
310 MUTATOR_HOOKFUNCTION(ca, ForbidThrowCurrentWeapon)
315 MUTATOR_HOOKFUNCTION(ca, GiveFragsForKill, CBC_ORDER_FIRST)
317 M_ARGV(2, float) = 0; // score will be given to the winner team when the round ends
321 MUTATOR_HOOKFUNCTION(ca, SetStartItems)
323 start_items &= ~IT_UNLIMITED_AMMO;
324 start_health = warmup_start_health = cvar("g_lms_start_health");
325 start_armorvalue = warmup_start_armorvalue = cvar("g_lms_start_armor");
326 start_ammo_shells = warmup_start_ammo_shells = cvar("g_lms_start_ammo_shells");
327 start_ammo_nails = warmup_start_ammo_nails = cvar("g_lms_start_ammo_nails");
328 start_ammo_rockets = warmup_start_ammo_rockets = cvar("g_lms_start_ammo_rockets");
329 start_ammo_cells = warmup_start_ammo_cells = cvar("g_lms_start_ammo_cells");
330 start_ammo_plasma = warmup_start_ammo_plasma = cvar("g_lms_start_ammo_plasma");
331 start_ammo_fuel = warmup_start_ammo_fuel = cvar("g_lms_start_ammo_fuel");
334 MUTATOR_HOOKFUNCTION(ca, Damage_Calculate)
336 entity frag_attacker = M_ARGV(1, entity);
337 entity frag_target = M_ARGV(2, entity);
338 float frag_deathtype = M_ARGV(3, float);
339 float frag_damage = M_ARGV(4, float);
340 float frag_mirrordamage = M_ARGV(5, float);
342 if (IS_PLAYER(frag_target))
343 if (!IS_DEAD(frag_target))
344 if (frag_target == frag_attacker || SAME_TEAM(frag_target, frag_attacker) || frag_deathtype == DEATH_FALL.m_id)
347 frag_mirrordamage = 0;
349 M_ARGV(4, float) = frag_damage;
350 M_ARGV(5, float) = frag_mirrordamage;
353 MUTATOR_HOOKFUNCTION(ca, FilterItem)
355 entity item = M_ARGV(0, entity);
357 if (autocvar_g_powerups <= 0)
358 if (item.flags & FL_POWERUP)
361 if (autocvar_g_pickup_items <= 0)
365 MUTATOR_HOOKFUNCTION(ca, PlayerDamage_SplitHealthArmor)
367 entity frag_attacker = M_ARGV(1, entity);
368 entity frag_target = M_ARGV(2, entity);
369 float frag_damage = M_ARGV(7, float);
370 float damage_take = M_ARGV(4, float);
371 float damage_save = M_ARGV(5, float);
373 float excess = max(0, frag_damage - damage_take - damage_save);
375 if (frag_target != frag_attacker && IS_PLAYER(frag_attacker))
376 GameRules_scoring_add_team(frag_attacker, SCORE, (frag_damage - excess) * autocvar_g_ca_damage2score_multiplier);
379 MUTATOR_HOOKFUNCTION(ca, CalculateRespawnTime)
381 // no respawn calculations needed, player is forced to spectate anyway
385 MUTATOR_HOOKFUNCTION(ca, PlayerRegen)
387 // no regeneration in CA
391 MUTATOR_HOOKFUNCTION(ca, Scores_CountFragsRemaining)
393 // announce remaining frags
397 MUTATOR_HOOKFUNCTION(ca, SpectateSet)
399 entity client = M_ARGV(0, entity);
400 entity targ = M_ARGV(1, entity);
402 if (!autocvar_g_ca_spectate_enemies && client.caplayer)
403 if (DIFF_TEAM(targ, client))
407 MUTATOR_HOOKFUNCTION(ca, SpectateNext)
409 entity client = M_ARGV(0, entity);
411 if (!autocvar_g_ca_spectate_enemies && client.caplayer)
413 entity targ = M_ARGV(1, entity);
414 M_ARGV(1, entity) = CA_SpectateNext(client, targ);
419 MUTATOR_HOOKFUNCTION(ca, SpectatePrev)
421 entity client = M_ARGV(0, entity);
422 entity targ = M_ARGV(1, entity);
423 entity first = M_ARGV(2, entity);
425 if (!autocvar_g_ca_spectate_enemies && client.caplayer)
427 do { targ = targ.chain; }
428 while(targ && DIFF_TEAM(targ, client));
432 for (targ = first; targ && DIFF_TEAM(targ, client); targ = targ.chain);
434 if (targ == client.enemy)
435 return MUT_SPECPREV_RETURN;
439 M_ARGV(1, entity) = targ;
441 return MUT_SPECPREV_FOUND;
444 MUTATOR_HOOKFUNCTION(ca, Bot_FixCount, CBC_ORDER_EXCLUSIVE)
446 FOREACH_CLIENT(IS_REAL_CLIENT(it), {
447 if (IS_PLAYER(it) || it.caplayer == 1)
454 MUTATOR_HOOKFUNCTION(ca, ClientCommand_Spectate)
456 entity player = M_ARGV(0, entity);
460 // they're going to spec, we can do other checks
461 if (autocvar_sv_spectate && (IS_SPEC(player) || IS_OBSERVER(player)))
462 Send_Notification(NOTIF_ONE_ONLY, player, MSG_INFO, INFO_CA_LEAVE);
463 return MUT_SPECCMD_FORCE;
466 return MUT_SPECCMD_CONTINUE;
469 MUTATOR_HOOKFUNCTION(ca, WantWeapon)
471 M_ARGV(2, bool) = true; // all weapons
474 MUTATOR_HOOKFUNCTION(ca, HideTeamNagger)
476 return true; // doesn't work well with the whole spectator as player thing
479 MUTATOR_HOOKFUNCTION(ca, GetPlayerStatus)
481 entity player = M_ARGV(0, entity);
483 return player.caplayer == 1;
486 MUTATOR_HOOKFUNCTION(ca, SetWeaponArena)
488 // most weapons arena
489 if (M_ARGV(0, string) == "0" || M_ARGV(0, string) == "") M_ARGV(0, string) = "most";