// ============
// clan arena
// ============
-set g_ca 0 "Clan Arena: Played in rounds, once you're dead you're out! The team with survivors wins the round."
-set g_ca_point_limit 10 "point limit 10 is standard for clan arena"
-set g_ca_point_leadlimit 0
-set g_ca_spectate_enemies 0 "Allow spectating enemy player by dead player during clan arena games."
+set g_ca 0 "Clan Arena: Played in rounds, once you're dead you're out! The team with survivors wins the round"
+seta g_ca_point_limit -1 "Clan Arena point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+seta g_ca_point_leadlimit -1 "Clan Arena point lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+set g_ca_spectate_enemies 0 "Allow spectating enemy player by dead player during clan arena games"
set g_ca_warmup 10 "how long the players will have time to run around the map before the round starts"
set g_ca_damage2score_multiplier 0.01
set g_ca_round_timelimit 180 "round time limit in seconds"
warmup_stage = (nags & 16);
}
+void Ent_EliminatedPlayers()
+{
+ float sf, i, j, b, f;
+
+ sf = ReadByte();
+ if(sf & 1)
+ {
+ for(j = 0; j < maxclients; ++j)
+ if(playerslots[j])
+ playerslots[j].eliminated = 1;
+ for(i = 1; i <= maxclients; i += 8)
+ {
+ f = ReadByte();
+ for(j = i-1, b = 1; b < 256; b *= 2, ++j)
+ if (!(f & b))
+ if(playerslots[j])
+ playerslots[j].eliminated = 0;
+ }
+ }
+}
+
void Ent_RandomSeed()
{
float s;
case ENT_CLIENT_RAINSNOW: Ent_RainOrSnow(); break;
case ENT_CLIENT_LASER: Ent_Laser(); break;
case ENT_CLIENT_NAGGER: Ent_Nagger(); break;
+ case ENT_CLIENT_ELIMINATEDPLAYERS: Ent_EliminatedPlayers(); break;
case ENT_CLIENT_WAYPOINT: Ent_WaypointSprite(); break;
case ENT_CLIENT_RADARLINK: Ent_RadarLink(); break;
case ENT_CLIENT_PROJECTILE: Ent_Projectile(); break;
.float gotscores;
.entity owner;
.float ready;
+.float eliminated;
.void(void) draw;
.void(void) draw2d;
return str;
}
-void HUD_PrintScoreboardItem(vector pos, entity pl, float is_self, float pl_number)
+void HUD_PrintScoreboardItem(vector pos, vector item_size, entity pl, float is_self, float pl_number)
{
vector tmp, rgb;
rgb = Team_ColorRGB(pl.team);
rgb_y = autocvar_scoreboard_color_bg_g + 0.5;
rgb_z = autocvar_scoreboard_color_bg_b + 0.5; }
- // Layout:
- tmp_x = sbwidth;
- tmp_y = hud_fontsize_y * 1.25;
- tmp_z = 0;
-
+ vector h_pos = pos - '1 1 0';
+ vector h_size = item_size + '2 0 0';
// alternated rows highlighting
if(is_self)
- drawfill(pos - '1 1 0', tmp + '2 0 0', rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
+ drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL);
else if((scoreboard_highlight) && (!mod(pl_number,2)))
- drawfill(pos - '1 1 0', tmp + '2 0 0', rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
+ drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL);
+ tmp_x = item_size_x;
tmp_y = 0;
+ tmp_z = 0;
for(i = 0; i < hud_num_fields; ++i)
{
pos_x -= hud_size[i] + hud_fontsize_x;
}
}
+
+ if(pl.eliminated)
+ drawfill(h_pos, h_size, '0 0 0', 0.5, DRAWFLAG_NORMAL);
}
/*
pos_y += 1.25 * hud_fontsize_y; // skip the header
pos_y += autocvar_scoreboard_border_thickness;
+ // item size
+ tmp_x = sbwidth;
+ tmp_y = hud_fontsize_y * 1.25;
+
// fill the table and draw the rows
i = 0;
if (teamplay)
{
if(pl.team != tm.team)
continue;
- HUD_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localnum), i);
+ HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
pos_y += 1.25 * hud_fontsize_y;
++i;
}
{
if(pl.team == NUM_SPECTATOR)
continue;
- HUD_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localnum), i);
+ HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i);
pos_y += 1.25 * hud_fontsize_y;
++i;
}
float specs;
specs = 0;
tmp = pos;
+ vector item_size;
+ item_size_x = sbwidth;
+ item_size_y = hud_fontsize_y * 1.25;
+ item_size_z = 0;
for(pl = players.sort_next; pl; pl = pl.sort_next)
{
if(pl.team != NUM_SPECTATOR)
continue;
pos_y += 1.25 * hud_fontsize_y;
- HUD_PrintScoreboardItem(pos, pl, (pl.sv_entnum == player_localnum), specs);
+ HUD_PrintScoreboardItem(pos, item_size, pl, (pl.sv_entnum == player_localnum), specs);
++specs;
}
const float ENT_CLIENT_SPAWNPOINT = 36;
const float ENT_CLIENT_SPAWNEVENT = 37;
const float ENT_CLIENT_NOTIFICATION = 38;
-
+const float ENT_CLIENT_ELIMINATEDPLAYERS = 39;
const float ENT_CLIENT_TURRET = 40;
const float ENT_CLIENT_AUXILIARYXHAIR = 50;
const float ENT_CLIENT_VEHICLE = 60;
REGISTER_GAMETYPE(_("Capture the Flag"),ctf,g_ctf,CTF,"timelimit=20 caplimit=10 leadlimit=0",_("Find and bring the enemy flag to your base to capture it"));
#define g_ctf IS_GAMETYPE(CTF)
-REGISTER_GAMETYPE(_("Clan Arena"),ca,g_ca,CA,"timelimit=20 pointlimit=10 leadlimit=0",_("Kill all enemy teammates to win the round"));
+REGISTER_GAMETYPE(_("Clan Arena"),ca,g_ca,CA,"timelimit=20 pointlimit=10 teams=2 leadlimit=0",_("Kill all enemy teammates to win the round"));
#define g_ca IS_GAMETYPE(CA)
REGISTER_GAMETYPE(_("Domination"),dom,g_domination,DOMINATION,"timelimit=20 pointlimit=200 teams=2 leadlimit=0",_("Capture all the control points to win"));
MSG_INFO_NOTIF(1, INFO_DEATH_SELF_VH_WAKI_ROCKET, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 couldn't find shelter from a Racer rocket%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_SELF_VOID, 2, 1, "s1 s2loc spree_lost", "s1", "notify_void", _("^BG%s^K1 was in the wrong place%s%s"), "") \
MULTITEAM_INFO(1, INFO_DEATH_TEAMKILL_, 4, 3, 1, "s1 s2 s3loc spree_end", "s2 s1", "notify_teamkill_%s", _("^BG%s^K1 was betrayed by ^BG%s^K1%s%s"), "") \
+ MSG_INFO_NOTIF(1, INFO_CA_JOIN_LATE, 0, 0, "", "", "", _("^F1Round already started, you will join the game in the next round"), "") \
+ MSG_INFO_NOTIF(1, INFO_CA_LEAVE, 0, 0, "", "", "", _("^F2You will spectate in the next round"), "") \
MSG_INFO_NOTIF(1, INFO_DOMINATION_CAPTURE_TIME, 2, 2, "s1 s2 f1 f2", "", "", _("^BG%s^BG%s^BG (%s points every %s seconds)"), "") \
MSG_INFO_NOTIF(1, INFO_FREEZETAG_FREEZE, 2, 0, "s1 s2", "", "", _("^BG%s^K1 was frozen by ^BG%s"), "") \
MSG_INFO_NOTIF(1, INFO_FREEZETAG_REVIVED, 2, 0, "s1 s2", "", "", _("^BG%s^K3 was revived by ^BG%s"), "") \
MULTITEAM_CENTER##teams(default,prefix,strnum,flnum,args,cpid,durcnt,normal,gentle)
#define MSG_CENTER_NOTIFICATIONS \
+ MSG_CENTER_NOTIF(1, CENTER_ALONE, 0, 0, "", NO_CPID, "0 0", _("^F4You are now alone!"), "") \
MSG_CENTER_NOTIF(1, CENTER_ASSAULT_ATTACKING, 0, 0, "", CPID_ASSAULT_ROLE, "0 0", _("^BGYou are attacking!"), "") \
MSG_CENTER_NOTIF(1, CENTER_ASSAULT_DEFENDING, 0, 0, "", CPID_ASSAULT_ROLE, "0 0", _("^BGYou are defending!"), "") \
MSG_CENTER_NOTIF(1, CENTER_COUNTDOWN_BEGIN, 0, 0, "", CPID_ROUND, "2 0", _("^F4Begin!"), "") \
MSG_CENTER_NOTIF(1, CENTER_KEYHUNT_ROUNDSTART, 0, 1, "", CPID_KEYHUNT_OTHER, "1 f1", _("^F4Round will start in ^COUNT"), "") \
MSG_CENTER_NOTIF(1, CENTER_KEYHUNT_SCAN, 0, 1, "", CPID_KEYHUNT_OTHER, "f1 0", _("^BGScanning frequency range..."), "") \
MULTITEAM_CENTER(1, CENTER_KEYHUNT_START_, 4, 0, 0, "", CPID_KEYHUNT, "0 0", _("^BGYou are starting with the ^TC^TT Key"), "") \
- MSG_CENTER_NOTIF(1, CENTER_KEYHUNT_WAIT, 0, 4, "missing_teams", CPID_KEYHUNT_OTHER, "0 0", _("^BGWaiting for players to join...\nNeed active players for: %s"), "") \
- MSG_CENTER_NOTIF(1, CENTER_MISSING_TEAMS, 0, 4, "missing_teams", CPID_MISSING_TEAMS, "-1 0", _("^BGWaiting for players to join...\nNeed active players for: %s"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_KEYHUNT_WAIT, 0, 1, "missing_teams", CPID_KEYHUNT_OTHER, "0 0", _("^BGWaiting for players to join...\nNeed active players for: %s"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_MISSING_TEAMS, 0, 1, "missing_teams", CPID_MISSING_TEAMS, "-1 0", _("^BGWaiting for players to join...\nNeed active players for: %s"), "") \
MSG_CENTER_NOTIF(1, CENTER_MISSING_PLAYERS, 0, 1, "f1", CPID_MISSING_PLAYERS, "-1 0", _("^BGWaiting for %s player(s) to join..."), "") \
MSG_CENTER_NOTIF(1, CENTER_MINSTA_FINDAMMO, 0, 0, "", CPID_MINSTA_FINDAMMO, "1 9", _("^F4^COUNT^BG left to find some ammo!"), "") \
MSG_CENTER_NOTIF(1, CENTER_MINSTA_FINDAMMO_FIRST, 0, 0, "", CPID_MINSTA_FINDAMMO, "1 10", _("^BGGet some ammo or you'll be dead in ^F4^COUNT^BG!"), _("^BGGet some ammo! ^F4^COUNT^BG left!")) \
ARG_CASE(ARG_CS_SV_HA, "f3race_time", mmssss(f3)) \
ARG_CASE(ARG_CS_SV, "race_col", CCR(((f1 == 1) ? "^F1" : "^F2"))) \
ARG_CASE(ARG_CS_SV, "race_diff", ((f2 > f3) ? sprintf(CCR("^1[+%s]"), mmssss(f2 - f3)) : sprintf(CCR("^2[-%s]"), mmssss(f3 - f2)))) \
- ARG_CASE(ARG_CS, "missing_teams", notif_arg_missing_teams(f1, f2, f3, f4)) \
+ ARG_CASE(ARG_CS, "missing_teams", notif_arg_missing_teams(f1)) \
ARG_CASE(ARG_CS, "pass_key", ((((tmp_s = getcommandkey("pass", "+use")) != "pass") && !(strstrofs(tmp_s, "not bound", 0) >= 0)) ? sprintf(CCR(_(" ^F1(Press %s)")), tmp_s) : "")) \
ARG_CASE(ARG_CS, "frag_ping", notif_arg_frag_ping(TRUE, f2)) \
ARG_CASE(ARG_CS, "frag_stats", notif_arg_frag_stats(f2, f3, f4)) \
return sprintf(CCR(_("\n(^F4Dead^BG)%s")), notif_arg_frag_ping(FALSE, fping));
}
-string notif_arg_missing_teams(float f1, float f2, float f3, float f4)
+string notif_arg_missing_teams(float f1)
{
return sprintf("%s%s%s%s",
- (f1 ?
- sprintf("%s%s", Team_ColoredFullName(f1 - 1), ((f2 + f3 + f4) ? ", " : ""))
+ ((f1 & 1) ?
+ sprintf("%s%s", Team_ColoredFullName(NUM_TEAM_1), ((f1 & (2 + 4 + 8)) ? ", " : ""))
:
""
),
- (f2 ?
- sprintf("%s%s", Team_ColoredFullName(f2 - 1), ((f3 + f4) ? ", " : ""))
+ ((f1 & 2) ?
+ sprintf("%s%s", Team_ColoredFullName(NUM_TEAM_2), ((f1 & (4 + 8)) ? ", " : ""))
:
""
),
- (f3 ?
- sprintf("%s%s", Team_ColoredFullName(f3 - 1), (f4 ? ", " : ""))
+ ((f1 & 4) ?
+ sprintf("%s%s", Team_ColoredFullName(NUM_TEAM_3), ((f1 & 8) ? ", " : ""))
:
""
),
- (f4 ?
- Team_ColoredFullName(f4 - 1)
+ ((f1 & 8) ?
+ Team_ColoredFullName(NUM_TEAM_4)
:
""
)
//self.bot_painintensity = self.bot_painintensity + self.bot_oldhealth - self.health;
//self.bot_painintensity = bound(0, self.bot_painintensity, 100);
- if (autocvar_g_campaign && !campaign_bots_may_start)
+ if (!IS_PLAYER(self) || (autocvar_g_campaign && !campaign_bots_may_start))
{
self.bot_nextthink = time + 0.5;
return;
self.punchvector = '0 0 0';
self.oldvelocity = self.velocity;
self.fire_endtime = -1;
+ self.event_damage = func_null;
}
.float model_randomizer;
if(self.killindicator_teamchange)
ClientKill_Now_TeamChange();
- // in any case:
- Damage(self, self, self, 100000, DEATH_KILL, self.origin, '0 0 0');
+ if(IS_PLAYER(self))
+ Damage(self, self, self, 100000, DEATH_KILL, self.origin, '0 0 0');
// now I am sure the player IS dead
}
float currentlyPlaying = 0;
FOR_EACH_REALCLIENT(e)
- if(IS_PLAYER(e) || e.caplayer == 1)
+ if(IS_PLAYER(e) || e.caplayer)
currentlyPlaying += 1;
if(currentlyPlaying < autocvar_g_maxplayers)
* g_maxplayers_spectator_blocktime seconds
*/
void checkSpectatorBlock() {
- if(IS_SPEC(self) || IS_OBSERVER(self)) {
+ if(IS_SPEC(self) || IS_OBSERVER(self))
+ if(!self.caplayer)
+ if(IS_REAL_CLIENT(self))
+ {
if( time > (self.spectatortime + autocvar_g_maxplayers_spectator_blocktime) ) {
Send_Notification(NOTIF_ONE_ONLY, self, MSG_INFO, INFO_QUIT_KICK_SPECTATING);
dropclient(self);
void calculate_player_respawn_time()
{
+ if(g_ca)
+ return;
+
float gametype_setting_tmp;
float sdelay_max = GAMETYPE_DEFAULTED_SETTING(respawn_delay_max);
float sdelay_small = GAMETYPE_DEFAULTED_SETTING(respawn_delay_small);
self.flags &= ~FL_ONGROUND;
// dying animation
self.deadflag = DEAD_DYING;
+
// when to allow respawn
calculate_player_respawn_time();
{
if(!IS_PLAYER(self) && !lockteams)
{
+ if(self.caplayer)
+ return;
if(nJoinAllowed(self))
{
if(autocvar_g_campaign) { campaign_bots_may_start = 1; }
}
}
- if(IS_PLAYER(self) && autocvar_sv_spectate == 1)
- ClientKill_TeamChange(-2); // observe
-
- // in CA, allow a dead player to move to spectators (without that, caplayer!=0 will be moved back to the player list)
- // note: if arena game mode is ever done properly, this needs to be removed.
- if(self.caplayer && (IS_SPEC(self) || IS_OBSERVER(self)))
+ if((IS_PLAYER(self) || self.caplayer) && autocvar_sv_spectate == 1)
{
- sprint(self, "WARNING: you will spectate in the next round.\n");
- self.caplayer = 0;
+ if(self.caplayer && (IS_SPEC(self) || IS_OBSERVER(self)))
+ Send_Notification(NOTIF_ONE_ONLY, self, MSG_INFO, INFO_CA_LEAVE);
+ ClientKill_TeamChange(-2); // observe
}
}
return; // never fall through to usage
FOR_EACH_REALPLAYER(client)
{
self = client;
+ if(self.caplayer)
+ self.caplayer = 0;
PutObserverInServer();
++i;
}
if(!IS_SPEC(client) && !IS_OBSERVER(client))
{
self = client;
+ if(self.caplayer)
+ self.caplayer = 0;
PutObserverInServer();
successful = strcat(successful, (successful ? ", " : ""), client.netname);
{
blockSpectators = 1;
entity plr;
- FOR_EACH_CLIENT(plr) //give every spectator <g_maxplayers_spectator_blocktime> seconds time to become a player
+ FOR_EACH_REALCLIENT(plr) //give every spectator <g_maxplayers_spectator_blocktime> seconds time to become a player
{
if(IS_SPEC(plr) || IS_OBSERVER(plr))
+ if(!plr.caplayer)
{
plr.spectatortime = time;
Send_Notification(NOTIF_ONE_ONLY, plr, MSG_INFO, INFO_SPECTATE_WARNING, autocvar_g_maxplayers_spectator_blocktime);
}
}
+
+entity eliminatedPlayers;
+.float(entity) isEliminated;
+float EliminatedPlayers_SendEntity(entity to, float sendflags)
+{
+ float i, f, b;
+ entity e;
+ WriteByte(MSG_ENTITY, ENT_CLIENT_ELIMINATEDPLAYERS);
+ WriteByte(MSG_ENTITY, sendflags);
+
+ if(sendflags & 1)
+ {
+ for(i = 1; i <= maxclients; i += 8)
+ {
+ for(f = 0, e = edict_num(i), b = 1; b < 256; b *= 2, e = nextent(e))
+ {
+ if(eliminatedPlayers.isEliminated(e))
+ f |= b;
+ }
+ WriteByte(MSG_ENTITY, f);
+ }
+ }
+
+ return TRUE;
+}
+
+void EliminatedPlayers_Init(float(entity) isEliminated_func)
+{
+ if(eliminatedPlayers)
+ {
+ backtrace("Can't spawn eliminatedPlayers again!");
+ return;
+ }
+ Net_LinkEntity(eliminatedPlayers = spawn(), FALSE, 0, EliminatedPlayers_SendEntity);
+ eliminatedPlayers.isEliminated = isEliminated_func;
+}
+
+
void adaptor_think2touch()
{
entity o;
allowed_to_spawn = FALSE;
}
-float prev_total_players;
+float prev_missing_teams_mask;
float CA_CheckTeams()
{
allowed_to_spawn = TRUE;
CA_count_alive_players();
if(CA_ALIVE_TEAMS_OK())
{
- if(prev_total_players > 0)
+ if(prev_missing_teams_mask > 0)
Kill_Notification(NOTIF_ALL, world, MSG_CENTER_CPID, CPID_MISSING_TEAMS);
- prev_total_players = -1;
+ prev_missing_teams_mask = -1;
return 1;
}
- if(prev_total_players != total_players)
+ if(total_players == 0)
{
- float p1 = 0, p2 = 0, p3 = 0, p4 = 0;
- if(!redalive) p1 = NUM_TEAM_1;
- if(!bluealive) p2 = NUM_TEAM_2;
- if(ca_teams >= 3)
- if(!yellowalive) p3 = NUM_TEAM_3;
- if(ca_teams >= 4)
- if(!pinkalive) p4 = NUM_TEAM_4;
- Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_MISSING_TEAMS, p1, p2, p3, p4);
- prev_total_players = total_players;
+ if(prev_missing_teams_mask > 0)
+ Kill_Notification(NOTIF_ALL, world, MSG_CENTER_CPID, CPID_MISSING_TEAMS);
+ prev_missing_teams_mask = -1;
+ return 0;
+ }
+ float missing_teams_mask = (!redalive) + (!bluealive) * 2;
+ if(ca_teams >= 3) missing_teams_mask += (!yellowalive) * 4;
+ if(ca_teams >= 4) missing_teams_mask += (!pinkalive) * 8;
+ if(prev_missing_teams_mask != missing_teams_mask)
+ {
+ Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_MISSING_TEAMS, missing_teams_mask);
+ prev_missing_teams_mask = missing_teams_mask;
}
return 0;
}
+float ca_isEliminated(entity e)
+{
+ if(e.caplayer == 1 && (e.deadflag != DEAD_NO || e.frags == FRAGS_LMS_LOSER))
+ return TRUE;
+ if(e.caplayer == 0.5)
+ return TRUE;
+ return FALSE;
+}
+
MUTATOR_HOOKFUNCTION(ca_PlayerSpawn)
{
self.caplayer = 1;
+ if(!warmup_stage)
+ eliminatedPlayers.SendFlags |= 1;
return 1;
}
MUTATOR_HOOKFUNCTION(ca_PutClientInServer)
{
if(!allowed_to_spawn)
+ if(IS_PLAYER(self)) // this is true even when player is trying to join
{
self.classname = "observer";
if(self.jointime != time) //not when connecting
{
self.caplayer = 0.5;
if(IS_REAL_CLIENT(self))
- sprint(self, "You will join the game in the next round.\n");
+ Send_Notification(NOTIF_ONE_ONLY, self, MSG_INFO, INFO_CA_JOIN_LATE);
}
}
return 1;
FOR_EACH_CLIENT(self)
{
self.killcount = 0;
+ if(!self.caplayer && IS_BOT_CLIENT(self))
+ {
+ self.team = -1;
+ self.caplayer = 1;
+ }
if(self.caplayer)
{
self.classname = "player";
return 0;
}
+entity ca_LastPlayerForTeam()
+{
+ entity pl, last_pl = world;
+ FOR_EACH_PLAYER(pl)
+ {
+ if(pl.health >= 1)
+ if(pl != self)
+ if(pl.team == self.team)
+ if(!last_pl)
+ last_pl = pl;
+ else
+ return world;
+ }
+ return last_pl;
+}
+
+void ca_LastPlayerForTeam_Notify()
+{
+ if(round_handler_IsActive())
+ if(round_handler_IsRoundStarted())
+ {
+ entity pl = ca_LastPlayerForTeam();
+ if(pl)
+ Send_Notification(NOTIF_ONE, pl, MSG_CENTER, CENTER_ALONE);
+ }
+}
+
MUTATOR_HOOKFUNCTION(ca_PlayerDies)
{
+ ca_LastPlayerForTeam_Notify();
if(!allowed_to_spawn)
self.respawn_flags = RESPAWN_SILENT;
+ if(!warmup_stage)
+ eliminatedPlayers.SendFlags |= 1;
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(ca_ClientDisconnect)
+{
+ if(self.caplayer == 1)
+ ca_LastPlayerForTeam_Notify();
return 1;
}
MUTATOR_HOOKFUNCTION(ca_MakePlayerObserver)
{
+ if(self.caplayer == 1)
+ ca_LastPlayerForTeam_Notify();
if(self.killindicator_teamchange == -2)
self.caplayer = 0;
if(self.caplayer)
self.frags = FRAGS_LMS_LOSER;
+ if(!warmup_stage)
+ eliminatedPlayers.SendFlags |= 1;
return 1;
}
addstat(STAT_BLUEALIVE, AS_INT, bluealive_stat);
addstat(STAT_YELLOWALIVE, AS_INT, yellowalive_stat);
addstat(STAT_PINKALIVE, AS_INT, pinkalive_stat);
+
+ EliminatedPlayers_Init(ca_isEliminated);
}
MUTATOR_DEFINITION(gamemode_ca)
MUTATOR_HOOK(reset_map_players, ca_reset_map_players, CBC_ORDER_ANY);
MUTATOR_HOOK(GetTeamCount, ca_GetTeamCount, CBC_ORDER_EXCLUSIVE);
MUTATOR_HOOK(PlayerDies, ca_PlayerDies, CBC_ORDER_ANY);
+ MUTATOR_HOOK(ClientDisconnect, ca_ClientDisconnect, CBC_ORDER_ANY);
MUTATOR_HOOK(ForbidPlayerScore_Clear, ca_ForbidPlayerScore_Clear, CBC_ORDER_ANY);
MUTATOR_HOOK(ForbidThrowCurrentWeapon, ca_ForbidThrowCurrentWeapon, CBC_ORDER_ANY);
MUTATOR_HOOK(GiveFragsForKill, ca_GiveFragsForKill, CBC_ORDER_FIRST);
e.yellowalive_stat = yellowalive;
e.pinkalive_stat = pinkalive;
}
+
+ eliminatedPlayers.SendFlags |= 1;
}
#define FREEZETAG_ALIVE_TEAMS() ((redalive > 0) + (bluealive > 0) + (yellowalive > 0) + (pinkalive > 0))
#define FREEZETAG_ALIVE_TEAMS_OK() (FREEZETAG_ALIVE_TEAMS() == freezetag_teams)
-float prev_total_players;
+float prev_missing_teams_mask;
float freezetag_CheckTeams()
{
if(FREEZETAG_ALIVE_TEAMS_OK())
{
- if(prev_total_players > 0)
+ if(prev_missing_teams_mask > 0)
Kill_Notification(NOTIF_ALL, world, MSG_CENTER_CPID, CPID_MISSING_TEAMS);
- prev_total_players = -1;
+ prev_missing_teams_mask = -1;
return 1;
}
- if(prev_total_players != total_players)
+ if(total_players == 0)
+ {
+ if(prev_missing_teams_mask > 0)
+ Kill_Notification(NOTIF_ALL, world, MSG_CENTER_CPID, CPID_MISSING_TEAMS);
+ prev_missing_teams_mask = -1;
+ return 0;
+ }
+ float missing_teams_mask = (!redalive) + (!bluealive) * 2;
+ if(freezetag_teams >= 3) missing_teams_mask += (!yellowalive) * 4;
+ if(freezetag_teams >= 4) missing_teams_mask += (!pinkalive) * 8;
+ if(prev_missing_teams_mask != missing_teams_mask)
{
- float p1 = 0, p2 = 0, p3 = 0, p4 = 0;
- if(!redalive) p1 = NUM_TEAM_1;
- if(!bluealive) p2 = NUM_TEAM_2;
- if(freezetag_teams >= 3)
- if(!yellowalive) p3 = NUM_TEAM_3;
- if(freezetag_teams >= 4)
- if(!pinkalive) p4 = NUM_TEAM_4;
- Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_MISSING_TEAMS, p1, p2, p3, p4);
- prev_total_players = total_players;
+ Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_MISSING_TEAMS, missing_teams_mask);
+ prev_missing_teams_mask = missing_teams_mask;
}
return 0;
}
return 1;
}
+entity freezetag_LastPlayerForTeam()
+{
+ entity pl, last_pl = world;
+ FOR_EACH_PLAYER(pl)
+ {
+ if(pl.health >= 1)
+ if(!pl.frozen)
+ if(pl != self)
+ if(pl.team == self.team)
+ if(!last_pl)
+ last_pl = pl;
+ else
+ return world;
+ }
+ return last_pl;
+}
+
+void freezetag_LastPlayerForTeam_Notify()
+{
+ if(round_handler_IsActive())
+ if(round_handler_IsRoundStarted())
+ {
+ entity pl = freezetag_LastPlayerForTeam();
+ if(pl)
+ Send_Notification(NOTIF_ONE, pl, MSG_CENTER, CENTER_ALONE);
+ }
+}
+
void freezetag_Add_Score(entity attacker)
{
if(attacker == self)
Unfreeze(self);
}
+float freezetag_isEliminated(entity e)
+{
+ if(e.frozen == 1 || e.deadflag != DEAD_NO)
+ return TRUE;
+ return FALSE;
+}
+
// ================
// Bot player logic
MUTATOR_HOOKFUNCTION(freezetag_RemovePlayer)
{
self.health = 0; // neccessary to update correctly alive stats
+ if(!self.frozen)
+ freezetag_LastPlayerForTeam_Notify();
freezetag_Unfreeze(world);
freezetag_count_alive_players();
return 1;
{
freezetag_Add_Score(frag_attacker);
freezetag_count_alive_players();
+ freezetag_LastPlayerForTeam_Notify();
}
else
freezetag_Unfreeze(world); // remove ice
return 1;
freezetag_Freeze(frag_attacker);
+ freezetag_LastPlayerForTeam_Notify();
if(frag_attacker == frag_target || frag_attacker == world)
{
addstat(STAT_BLUEALIVE, AS_INT, bluealive_stat);
addstat(STAT_YELLOWALIVE, AS_INT, yellowalive_stat);
addstat(STAT_PINKALIVE, AS_INT, pinkalive_stat);
+
+ EliminatedPlayers_Init(freezetag_isEliminated);
}
MUTATOR_DEFINITION(gamemode_freezetag)
round_handler_Spawn(Invasion_CheckPlayers, Invasion_CheckWinner, Invasion_RoundStart);
round_handler_Init(5, autocvar_g_invasion_warmup, autocvar_g_invasion_round_timelimit);
- allowed_to_spawn = TRUE;
-
inv_roundcnt = 0;
inv_maxrounds = 15; // 15?
}
FOR_EACH_CLIENT(head)
{
float t;
- if(IS_PLAYER(head))
+ if(IS_PLAYER(head) || head.caplayer)
t = head.team;
else if(head.team_forced > 0)
t = head.team_forced; // reserve the spot