seta hud_panel_modicons_bg_alpha "" "if set to something else than \"\" = override default panel background alpha"
seta hud_panel_modicons_bg_border "" "if set to something else than \"\" = override default size of border around the background"
seta hud_panel_modicons_bg_padding "" "if set to something else than \"\" = override default padding of contents from border"
+seta hud_panel_modicons_ca_layout "" "2 possible layouts: 0) number of alive players; 1) icons and number of alive players"
seta hud_panel_modicons_dom_layout "" "3 possible layouts: 0) only icons; 1) icons and percentage of average pps (points per second); 2) icons and average pps"
+seta hud_panel_modicons_freezetag_layout "" "2 possible layouts: 0) number of alive players; 1) icons and number of alive players"
seta hud_panel_pressedkeys "" "enable/disable this panel, 1 = show only when spectating other players, 2 = show always"
seta hud_panel_pressedkeys_pos "" "position of this base of the panel"
set g_arena 0 "Arena: many one-on-one rounds are played to find the winner"
set g_arena_maxspawned 2 "maximum number of players to spawn at once (the rest is spectating, waiting for their turn)"
set g_arena_roundbased 1 "if disabled, the next player will spawn as soon as someone dies"
+set g_arena_round_timelimit 180
set g_arena_warmup 5 "time, newly spawned players have to prepare themselves in round based matches"
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
+seta g_ca_teams_override 0
+set g_ca_teams 0
+
// ==================
// freezetag
// ===========
set g_freezetag 0 "Freeze Tag: Freeze the opposing team(s) to win, unfreeze teammates by standing next to them"
-seta g_freezetag_warmup 5 "Time players get to run around before the round starts"
+set g_freezetag_warmup 5 "Time players get to run around before the round starts"
seta g_freezetag_point_limit -1 "Freeze Tag point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
seta g_freezetag_point_leadlimit -1 "Freeze Tag point lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
-seta g_freezetag_revive_speed 0.4 "Speed for reviving a frozen teammate"
-seta g_freezetag_revive_clearspeed 1.6 "Speed at which reviving progress gets lost when out of range"
-seta g_freezetag_revive_extra_size 100 "Distance in qu that you can stand from a frozen teammate to keep reviving him"
-seta g_freezetag_frozen_force 0.6 "How much to multiply the force on a frozen player with"
+set g_freezetag_revive_speed 0.4 "Speed for reviving a frozen teammate"
+set g_freezetag_revive_clearspeed 1.6 "Speed at which reviving progress gets lost when out of range"
+set g_freezetag_revive_extra_size 100 "Distance in qu that you can stand from a frozen teammate to keep reviving him"
+set g_freezetag_round_timelimit 180
+set g_freezetag_frozen_force 0.6 "How much to multiply the force on a frozen player with"
+set g_freezetag_frozen_maxtime 60 "frozen players will be automatically unfrozen after this time in seconds"
+seta g_freezetag_teams_override 0
+set g_freezetag_teams 0
// ==========
seta hud_panel_modicons_bg_alpha ""
seta hud_panel_modicons_bg_border ""
seta hud_panel_modicons_bg_padding "0"
+seta hud_panel_modicons_ca_layout "1"
seta hud_panel_modicons_dom_layout "1"
+seta hud_panel_modicons_freezetag_layout "1"
seta hud_panel_pressedkeys 1
seta hud_panel_pressedkeys_pos "0.450000 0.720000"
seta hud_panel_modicons_bg_alpha ""
seta hud_panel_modicons_bg_border ""
seta hud_panel_modicons_bg_padding ""
+seta hud_panel_modicons_ca_layout "1"
seta hud_panel_modicons_dom_layout "1"
+seta hud_panel_modicons_freezetag_layout "1"
seta hud_panel_pressedkeys 1
seta hud_panel_pressedkeys_pos "0.450000 0.650000"
seta hud_panel_modicons_bg_alpha ""
seta hud_panel_modicons_bg_border ""
seta hud_panel_modicons_bg_padding ""
+seta hud_panel_modicons_ca_layout "1"
seta hud_panel_modicons_dom_layout "1"
+seta hud_panel_modicons_freezetag_layout "1"
seta hud_panel_pressedkeys 1
seta hud_panel_pressedkeys_pos "0.450000 0.690000"
seta hud_panel_modicons_bg_alpha ""
seta hud_panel_modicons_bg_border ""
seta hud_panel_modicons_bg_padding ""
+seta hud_panel_modicons_ca_layout "1"
seta hud_panel_modicons_dom_layout "1"
+seta hud_panel_modicons_freezetag_layout "1"
seta hud_panel_pressedkeys 1
seta hud_panel_pressedkeys_pos "0.410000 0.710000"
seta hud_panel_modicons_bg_alpha ""
seta hud_panel_modicons_bg_border ""
seta hud_panel_modicons_bg_padding ""
+seta hud_panel_modicons_ca_layout "1"
seta hud_panel_modicons_dom_layout "1"
+seta hud_panel_modicons_freezetag_layout "1"
seta hud_panel_pressedkeys 1
seta hud_panel_pressedkeys_pos "0.440000 0.760000"
}
}
}
-
- if(autocvar_hud_damage)
+
+ if(autocvar_hud_damage && !getstati(STAT_FROZEN))
{
splash_size_x = max(vid_conwidth, vid_conheight);
splash_size_y = max(vid_conwidth, vid_conheight);
if(getstatf(STAT_REVIVE_PROGRESS))
{
DrawCircleClippedPic(eX * 0.5 * vid_conwidth + eY * 0.6 * vid_conheight, 0.1 * vid_conheight, "gfx/crosshair_ring.tga", getstatf(STAT_REVIVE_PROGRESS), '0.25 0.90 1', autocvar_hud_colorflash_alpha, DRAWFLAG_ADDITIVE);
- drawstring_aspect(eY * 0.64 * vid_conheight, "Revival progress", eX * vid_conwidth + eY * 0.025 * vid_conheight, '1 1 1', 1, DRAWFLAG_NORMAL);
+ drawstring_aspect(eY * 0.64 * vid_conheight, _("Revival progress"), eX * vid_conwidth + eY * 0.025 * vid_conheight, '1 1 1', 1, DRAWFLAG_NORMAL);
}
}
void Announcer_Countdown()
{
float starttime = getstatf(STAT_GAMESTARTTIME);
+ float roundstarttime = getstatf(STAT_ROUNDSTARTTIME);
+ if(roundstarttime == -1)
+ {
+ Local_Notification(MSG_CENTER, CENTER_COUNTDOWN_ROUNDSTOP);
+ remove(self);
+ return;
+ }
+ if(roundstarttime >= starttime)
+ starttime = roundstarttime;
+ if(starttime <= time && roundstarttime != starttime) // game start time has passed
+ announcer_5min = announcer_1min = FALSE; // reset maptime announcers now as well
+
float countdown = (starttime - time);
float countdown_rounded = floor(0.5 + countdown);
-
+
if(countdown <= 0) // countdown has finished, starttime is now
{
+ Local_Notification(MSG_CENTER, CENTER_COUNTDOWN_BEGIN);
Local_Notification(MSG_MULTI, MULTI_COUNTDOWN_BEGIN);
- announcer_5min = announcer_1min = FALSE; // reset maptime announcers now as well
remove(self);
return;
}
else // countdown is still going
{
- Local_Notification(MSG_CENTER, CENTER_COUNTDOWN_GAMESTART, countdown_rounded);
+ if(roundstarttime == starttime)
+ Local_Notification(MSG_CENTER, CENTER_COUNTDOWN_ROUNDSTART, countdown_rounded);
+ else
+ Local_Notification(MSG_CENTER, CENTER_COUNTDOWN_GAMESTART, countdown_rounded);
switch(countdown_rounded)
{
void Announcer_Gamestart()
{
float startTime = getstatf(STAT_GAMESTARTTIME);
-
+ float roundstarttime = getstatf(STAT_ROUNDSTARTTIME);
+ if(roundstarttime > startTime)
+ startTime = roundstarttime;
+
if(previous_game_starttime != startTime)
{
if((time + 5.0) < startTime) // if connecting to server while restart was active don't always play prepareforbattle
Local_Notification(MSG_ANNCE, ANNCE_PREPARE);
-
+
if(time < startTime)
{
- entity e;
- e = spawn();
- e.think = Announcer_Countdown;
+ entity e = find(world, classname, "announcer_countdown");
+ if not(e)
+ {
+ e = spawn();
+ e.classname = "announcer_countdown";
+ e.think = Announcer_Countdown;
+ }
e.nextthink = startTime - floor(startTime - time); //synchronize nextthink to startTime
}
}
float autocvar_hud_panel_infomessages;
float autocvar_hud_panel_infomessages_flip;
float autocvar_hud_panel_modicons;
+float autocvar_hud_panel_modicons_ca_layout;
float autocvar_hud_panel_modicons_dom_layout;
+float autocvar_hud_panel_modicons_freezetag_layout;
float autocvar_hud_panel_notify;
float autocvar_hud_panel_notify_fadetime;
float autocvar_hud_panel_notify_flip;
float mod_active; // is there any active mod icon?
-// Clan Arena HUD modicons
-void HUD_Mod_CA(vector pos, vector mySize)
+void DrawCAItem(vector myPos, vector mySize, float aspect_ratio, float layout, float i)
{
- mod_active = 1; // CA should never hide the mod icons panel
- float redalive, bluealive;
- redalive = getstati(STAT_REDALIVE);
- bluealive = getstati(STAT_BLUEALIVE);
+ float stat;
+ string pic;
+ vector color;
+#ifdef GMQCC
+ stat = -1;
+ pic = "";
+ color = '0 0 0';
+#endif
+ switch(i)
+ {
+ case 0:
+ stat = getstati(STAT_REDALIVE);
+ pic = "player_red.tga";
+ color = '1 0 0';
+ break;
+ case 1:
+ stat = getstati(STAT_BLUEALIVE);
+ pic = "player_blue.tga";
+ color = '0 0 1';
+ break;
+ case 2:
+ stat = getstati(STAT_YELLOWALIVE);
+ pic = "player_yellow.tga";
+ color = '1 1 0';
+ break;
+ default:
+ case 3:
+ stat = getstati(STAT_PINKALIVE);
+ pic = "player_pink.tga";
+ color = '1 0 1';
+ break;
+ }
- vector redpos, bluepos;
- if(mySize_x > mySize_y)
+ if(mySize_x/mySize_y > aspect_ratio)
{
- redpos = pos;
- bluepos = pos + eY * 0.5 * mySize_y;
- drawpic_aspect_skin(redpos, "player_red.tga", 0.5 * mySize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- drawstring_aspect(redpos + eX * 0.5 * mySize_x, ftos(redalive), 0.5 * mySize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- drawpic_aspect_skin(bluepos, "player_blue.tga", 0.5 * mySize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- drawstring_aspect(bluepos + eX * 0.5 * mySize_x, ftos(bluealive), 0.5 * mySize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ i = aspect_ratio * mySize_y;
+ myPos_x = myPos_x + (mySize_x - i) / 2;
+ mySize_x = i;
}
else
{
- redpos = pos;
- bluepos = pos + eY * 0.5 * mySize_y;
- drawpic_aspect_skin(redpos, "player_red.tga", eX * mySize_x + eY * 0.3 * mySize_y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- drawstring_aspect(redpos + eY * 0.3 * mySize_y, ftos(redalive), eX * mySize_x + eY * 0.2 * mySize_y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- drawpic_aspect_skin(bluepos, "player_blue.tga", eX * mySize_x + eY * 0.3 * mySize_y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
- drawstring_aspect(bluepos + eY * 0.3 * mySize_y, ftos(bluealive), eX * mySize_x + eY * 0.2 * mySize_y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ i = 1/aspect_ratio * mySize_x;
+ myPos_y = myPos_y + (mySize_y - i) / 2;
+ mySize_y = i;
+ }
+
+ if(layout)
+ {
+ drawpic_aspect_skin(myPos, pic, eX * 0.7 * mySize_x + eY * mySize_y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL);
+ drawstring_aspect(myPos + eX * 0.7 * mySize_x, ftos(stat), eX * 0.3 * mySize_x + eY * mySize_y, color, panel_fg_alpha, DRAWFLAG_NORMAL);
+ }
+ else
+ drawstring_aspect(myPos, ftos(stat), mySize, color, panel_fg_alpha, DRAWFLAG_NORMAL);
+}
+
+// Clan Arena and Freeze Tag HUD modicons
+void HUD_Mod_CA(vector myPos, vector mySize)
+{
+ mod_active = 1; // required in each mod function that always shows something
+ entity tm;
+ float teams_count = 0;
+ for(tm = teams.sort_next; tm; tm = tm.sort_next)
+ if(tm.team != NUM_SPECTATOR)
+ ++teams_count;
+
+ float layout;
+ if(gametype == MAPINFO_TYPE_CA)
+ layout = autocvar_hud_panel_modicons_ca_layout;
+ else //if(gametype == MAPINFO_TYPE_FREEZETAG)
+ layout = autocvar_hud_panel_modicons_freezetag_layout;
+ float rows, columns, aspect_ratio;
+ rows = mySize_y/mySize_x;
+ aspect_ratio = (layout) ? 2 : 1;
+ rows = bound(1, floor((sqrt((4 * aspect_ratio * teams_count + rows) * rows) + rows + 0.5) / 2), teams_count);
+ columns = ceil(teams_count/rows);
+
+ int i;
+ float row = 0, column = 0;
+ vector pos, itemSize;
+ itemSize = eX * mySize_x*(1/columns) + eY * mySize_y*(1/rows);
+ for(i=0; i<teams_count; ++i)
+ {
+ pos = myPos + eX * column * itemSize_x + eY * row * itemSize_y;
+
+ DrawCAItem(pos, itemSize, aspect_ratio, layout, i);
+
+ ++row;
+ if(row >= rows)
+ {
+ row = 0;
+ ++column;
+ }
}
}
int i;
float row = 0, column = 0;
+ vector pos, itemSize;
+ itemSize = eX * mySize_x*(1/columns) + eY * mySize_y*(1/rows);
for(i=0; i<teams_count; ++i)
{
- vector pos, itemSize;
- pos = myPos + eX * column * mySize_x*(1/columns) + eY * row * mySize_y*(1/rows);
- itemSize = eX * mySize_x*(1/columns) + eY * mySize_y*(1/rows);
+ pos = myPos + eX * column * itemSize_x + eY * row * itemSize_y;
DrawDomItem(pos, itemSize, aspect_ratio, layout, i);
HUD_Write_PanelCvar_q("_alreadyvoted_alpha");
break;
case HUD_PANEL_MODICONS:
+ HUD_Write_PanelCvar_q("_ca_layout");
HUD_Write_PanelCvar_q("_dom_layout");
+ HUD_Write_PanelCvar_q("_freezetag_layout");
break;
case HUD_PANEL_PRESSEDKEYS:
HUD_Write_PanelCvar_q("_aspect");
const float STAT_SECRETS_FOUND = 71;
const float STAT_RESPAWN_TIME = 72;
+const float STAT_ROUNDSTARTTIME = 73;
// mod stats (1xx)
const float STAT_REDALIVE = 100;
s = cdr(s);
}
+ if(pWantedType == MAPINFO_TYPE_CA)
+ {
+ sa = car(s);
+ if(sa != "")
+ cvar_set("g_ca_teams", sa);
+ s = cdr(s);
+ }
+
+ if(pWantedType == MAPINFO_TYPE_FREEZETAG)
+ {
+ sa = car(s);
+ if(sa != "")
+ cvar_set("g_freezetag_teams", sa);
+ s = cdr(s);
+ }
+
if(pWantedType == MAPINFO_TYPE_CTF)
{
sa = car(s);
cvar_set("leadlimit", cvar_defstring("leadlimit"));
cvar_set("fraglimit", cvar_defstring("fraglimit"));
cvar_set("g_tdm_teams", cvar_defstring("g_tdm_teams"));
+ cvar_set("g_ca_teams", cvar_defstring("g_ca_teams"));
+ cvar_set("g_freezetag_teams", cvar_defstring("g_freezetag_teams"));
cvar_set("g_keyhunt_teams", cvar_defstring("g_keyhunt_teams"));
cvar_set("g_domination_default_teams", cvar_defstring("g_domination_default_teams"));
cvar_set("g_race_qualifying_timelimit", cvar_defstring("g_race_qualifying_timelimit"));
else if(k == "teams")
{
cvar_set("g_tdm_teams", v);
+ cvar_set("g_ca_teams", v);
+ cvar_set("g_freezetag_teams", v);
cvar_set("g_keyhunt_teams", v);
cvar_set("g_domination_default_teams", v);
}
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\n"), "") \
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\n"), "") \
MSG_INFO_NOTIF(1, INFO_FREEZETAG_FREEZE, 2, 0, "s1 s2", "", "", _("^BG%s^K1 was frozen by ^BG%s\n"), "") \
- MSG_INFO_NOTIF(1, INFO_FREEZETAG_REVIVE, 2, 0, "s1 s2", "", "", _("^BG%s^K3 was revived by ^BG%s\n"), "") \
- MULTITEAM_INFO(1, INFO_FREEZETAG_ROUND_WIN_, 4, 0, 0, "", "", "", _("^TC^TT^BG team wins the round, all other teams were frozen\n"), "") \
+ MSG_INFO_NOTIF(1, INFO_FREEZETAG_REVIVED, 2, 0, "s1 s2", "", "", _("^BG%s^K3 was revived by ^BG%s\n"), "") \
+ MSG_INFO_NOTIF(1, INFO_FREEZETAG_AUTO_REVIVED, 1, 1, "s1 f1", "", "", _("^BG%s^K3 was automatically revived after %s second(s)\n"), "") \
+ MULTITEAM_INFO(1, INFO_ROUND_TEAM_WIN_, 4, 0, 0, "", "", "", _("^TC^TT^BG team wins the round\n"), "") \
+ MSG_INFO_NOTIF(1, INFO_ROUND_PLAYER_WIN, 1, 0, "s1", "", "", _("^BG%s^BG wins the round\n"), "") \
+ MSG_INFO_NOTIF(1, INFO_ROUND_TIED, 0, 0, "", "", "", _("^BGRound tied\n"), "") \
+ MSG_INFO_NOTIF(1, INFO_ROUND_OVER, 0, 0, "", "", "", _("^BGRound over, there's no winner\n"), "") \
MSG_INFO_NOTIF(1, INFO_FREEZETAG_SELF, 1, 0, "s1", "", "", _("^BG%s^K1 froze themself\n"), "") \
MSG_INFO_NOTIF(1, INFO_GODMODE_OFF, 0, 1, "f1", "", "", _("^BGGodmode saved you %s units of damage, cheater!\n"), "") \
MSG_INFO_NOTIF(0, INFO_ITEM_WEAPON_DONTHAVE, 0, 1, "item_wepname", "", "", _("^BGYou do not have the ^F1%s\n"), "") \
MSG_CENTER_NOTIF(default, prefix##PINK, strnum, flnum, args, cpid, durcnt, TCR(normal, COL_TEAM_4, strtoupper(STATIC_NAME_TEAM_4)), TCR(gentle, COL_TEAM_4, strtoupper(STATIC_NAME_TEAM_4))) \
#endif
#define MSG_CENTER_NOTIFICATIONS \
- MSG_CENTER_NOTIF(1, CENTER_ARENA_BEGIN, 0, 0, "", CPID_ARENA, "2 0", _("^F4Begin!"), "") \
- MSG_CENTER_NOTIF(1, CENTER_ARENA_NEEDPLAYER, 0, 0, "", CPID_ARENA, "2 0", _("^BGNeed at least 1 player in each team to play Clan Arena!"), "") \
- MSG_CENTER_NOTIF(1, CENTER_ARENA_ROUNDSTART, 0, 1, "", CPID_ARENA, "1 f1", _("^F4Round will start in ^COUNT"), "") \
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_GAMESTART, "2 0", _("^F4Begin!"), "") \
- MSG_CENTER_NOTIF(1, CENTER_COUNTDOWN_GAMESTART, 0, 1, "", CPID_GAMESTART, "1 f1", _("^F4Game starts in ^COUNT"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_COUNTDOWN_BEGIN, 0, 0, "", CPID_ROUND, "2 0", _("^F4Begin!"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_COUNTDOWN_GAMESTART, 0, 1, "", CPID_ROUND, "1 f1", _("^F4Game starts in ^COUNT"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_COUNTDOWN_ROUNDSTART, 0, 1, "", CPID_ROUND, "1 f1", _("^F4Round starts in ^COUNT"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_COUNTDOWN_ROUNDSTOP, 0, 0, "", CPID_ROUND, "2 0", _("^F4Round cannot start"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_ROUND_TIED, 0, 0, "", CPID_ROUND, "0 0", _("^BGRound tied"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_ROUND_OVER, 0, 0, "", CPID_ROUND, "0 0", _("^BGRound over, there's no winner"), "") \
MSG_CENTER_NOTIF(1, CENTER_CTF_CAPTURESHIELD_FREE, 0, 0, "", CPID_CTF_CAPSHIELD, "0 0", _("^BGYou are now free.\n^BGFeel free to ^F2try to capture^BG the flag again\n^BGif you think you will succeed."), "") \
MSG_CENTER_NOTIF(1, CENTER_CTF_CAPTURESHIELD_SHIELDED, 0, 0, "", CPID_CTF_CAPSHIELD, "0 0", _("^BGYou are now ^F1shielded^BG from the flag\n^BGfor ^F2too many unsuccessful attempts^BG to capture.\n^BGMake some defensive scores before trying again."), "") \
MULTITEAM_CENTER(1, CENTER_CTF_CAPTURE_, 2, 0, 0, "", CPID_CTF_LOWPRIO, "0 0", _("^BGYou captured the ^TC^TT^BG flag!"), "") \
MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_FROZEN, 1, 0, "s1", NO_CPID, "0 0", _("^K1You were frozen by ^BG%s"), "") \
MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_REVIVE, 1, 0, "s1", NO_CPID, "0 0", _("^K3You revived ^BG%s"), "") \
MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_REVIVED, 1, 0, "s1", NO_CPID, "0 0", _("^K3You were revived by ^BG%s"), "") \
- MULTITEAM_CENTER(1, CENTER_FREEZETAG_ROUND_WIN_, 4, 0, 0, "", NO_CPID, "0 0", _("^TC^TT^BG team wins the round, all other teams were frozen"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_AUTO_REVIVED, 0, 1, "f1", NO_CPID, "0 0", _("^K3You were automatically revived after %s second(s)"), "") \
+ MULTITEAM_CENTER(1, CENTER_ROUND_TEAM_WIN_, 4, 0, 0, "", CPID_ROUND, "0 0", _("^TC^TT^BG team wins the round"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_ROUND_PLAYER_WIN, 1, 0, "s1", CPID_ROUND, "0 0", _("^BG%s^BG wins the round"), "") \
MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_SELF, 0, 0, "", NO_CPID, "0 0", _("^K1You froze yourself"), "") \
- MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_SPAWN_LATE, 0, 0, "", NO_CPID, "0 0", _("^K1You spawned after the round started, you'll spawn as frozen"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_SPAWN_LATE, 0, 0, "", NO_CPID, "0 0", _("^K1Round already started, you spawn as frozen"), "") \
MSG_CENTER_NOTIF(1, CENTER_ITEM_WEAPON_DONTHAVE, 0, 1, "item_wepname", CPID_ITEM, "item_centime 0", _("^BGYou do not have the ^F1%s"), "") \
MSG_CENTER_NOTIF(1, CENTER_ITEM_WEAPON_DROP, 1, 1, "item_wepname item_wepammo", CPID_ITEM, "item_centime 0", _("^BGYou dropped the ^F1%s^BG%s"), "") \
MSG_CENTER_NOTIF(1, CENTER_ITEM_WEAPON_GOT, 0, 1, "item_wepname", CPID_ITEM, "item_centime 0", _("^BGYou got the ^F1%s"), "") \
MSG_CENTER_NOTIF(1, CENTER_KEYHUNT_HELP, 0, 0, "", CPID_KEYHUNT, "0 0", _("^BGAll keys are in your team's hands!\nHelp the key carriers to meet!"), "") \
MULTITEAM_CENTER(1, CENTER_KEYHUNT_INTERFERE_, 4, 0, 0, "", CPID_KEYHUNT, "0 0", _("^BGAll keys are in ^TC^TT team^BG's hands!\nInterfere ^F4NOW^BG!"), "") \
MSG_CENTER_NOTIF(1, CENTER_KEYHUNT_MEET, 0, 0, "", CPID_KEYHUNT, "0 0", _("^BGAll keys are in your team's hands!\nMeet the other key carriers ^F4NOW^BG!"), "") \
+ 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, "kh_teams", CPID_KEYHUNT_OTHER, "0 0", _("^BGWaiting for players to join...\nNeed active players for: %s"), "") \
+ 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_MISSING_PLAYERS, 0, 1, "f1", CPID_MISSING_PLAYERS, "-1 0", _("^BGWaiting for %s player(s) to join..."), "") \
MSG_CENTER_NOTIF(1, CENTER_LMS_CAMPCHECK, 0, 0, "", CPID_LMS_CAMP, "0 0", _("^F2Don't camp!"), "") \
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!")) \
MSG_MULTI_NOTIF(1, ITEM_WEAPON_NOAMMO, NO_MSG, INFO_ITEM_WEAPON_NOAMMO, CENTER_ITEM_WEAPON_NOAMMO) \
MSG_MULTI_NOTIF(1, ITEM_WEAPON_PRIMORSEC, NO_MSG, INFO_ITEM_WEAPON_PRIMORSEC, CENTER_ITEM_WEAPON_PRIMORSEC) \
MSG_MULTI_NOTIF(1, ITEM_WEAPON_UNAVAILABLE, NO_MSG, INFO_ITEM_WEAPON_UNAVAILABLE, CENTER_ITEM_WEAPON_UNAVAILABLE) \
- MSG_MULTI_NOTIF(1, MULTI_ARENA_BEGIN, ANNCE_BEGIN, NO_MSG, CENTER_ARENA_BEGIN) \
MSG_MULTI_NOTIF(1, MULTI_COUNTDOWN_BEGIN, ANNCE_BEGIN, NO_MSG, CENTER_COUNTDOWN_BEGIN) \
MSG_MULTI_NOTIF(1, MULTI_MINSTA_FINDAMMO, ANNCE_NUM_10, NO_MSG, CENTER_MINSTA_FINDAMMO_FIRST) \
MSG_MULTI_NOTIF(1, WEAPON_ACCORDEON_MURDER, NO_MSG, INFO_WEAPON_ACCORDEON_MURDER, NO_MSG) \
f2race_time: mmssss of f2
race_col: color of race time/position (i.e. good or bad)
race_diff: show time difference between f2 and f3
- kh_teams: show which teams still need players in keyhunt centerprint
+ missing_teams: show which teams still need players
pass_key: find the keybind for "passing" or "dropping" in CTF game mode
frag_ping: show the ping of a player
frag_stats: show health/armor/ping of a player
ARG_CASE(ARG_CS_SV, "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, "kh_teams", notif_arg_kh_teams(f1, f2, f3, f4)) \
+ ARG_CASE(ARG_CS, "missing_teams", notif_arg_missing_teams(f1, f2, f3, f4)) \
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_kh_teams(float f1, float f2, float f3, float f4)
+string notif_arg_missing_teams(float f1, float f2, float f3, float f4)
{
return sprintf("%s%s%s%s",
(f1 ?
me.TR(me);
me.TDempty(me, 0.2);
me.TD(me, 1, 1.2, makeXonoticTextLabel(0, _("Teams:")));
- me.TD(me, 1, 1.6, e = makeXonoticTextSlider("g_tdm_teams_override g_domination_teams_override g_keyhunt_teams_override"));
+ me.TD(me, 1, 1.6, e = makeXonoticTextSlider("g_tdm_teams_override g_domination_teams_override g_ca_teams_override g_freezetag_teams_override g_keyhunt_teams_override"));
e.addValue(e, "Default", "0");
e.addValue(e, "2 teams", "2");
e.addValue(e, "3 teams", "3");
+++ /dev/null
-float maxspawned;
-float numspawned;
-float arena_roundbased;
-.float spawned;
-.entity spawnqueue_next;
-.entity spawnqueue_prev;
-.float spawnqueue_in;
-entity spawnqueue_first;
-entity spawnqueue_last;
-entity champion;
-float warmup;
-.float caplayer;
-
-void PutObserverInServer();
-void PutClientInServer();
-
-float next_round;
-float redalive, bluealive, yellowalive, pinkalive;
-float totalalive;
-.float redalive_stat, bluealive_stat, yellowalive_stat, pinkalive_stat;
-float red_players, blue_players, yellow_players, pink_players;
-float total_players;
-
-/**
- * Resets the state of all clients, items, flags, keys, weapons, waypoints, ... of the map.
- * Sets the 'warmup' global variable.
- */
-void reset_map(float dorespawn)
-{
- entity oldself;
- oldself = self;
-
- if(g_arena && autocvar_g_arena_warmup)
- warmup = time + autocvar_g_arena_warmup;
- else if(g_ca) {
- warmup = time + autocvar_g_ca_warmup;
- allowed_to_spawn = 1;
- }
- else if(g_freezetag)
- {
- warmup = time + autocvar_g_freezetag_warmup;
- }
-
- lms_lowest_lives = 999;
- lms_next_place = player_count;
-
- race_ReadyRestart();
-
- for(self = world; (self = nextent(self)); )
- if(clienttype(self) == CLIENTTYPE_NOTACLIENT)
- {
- if(self.reset)
- {
- self.reset();
- continue;
- }
-
- if(self.team_saved)
- self.team = self.team_saved;
-
- if(self.flags & FL_PROJECTILE) // remove any projectiles left
- remove(self);
- }
-
- // Waypoints and assault start come LAST
- for(self = world; (self = nextent(self)); )
- if(clienttype(self) == CLIENTTYPE_NOTACLIENT)
- {
- if(self.reset2)
- {
- self.reset2();
- continue;
- }
- }
-
- // Moving the player reset code here since the player-reset depends
- // on spawnpoint entities which have to be reset first --blub
- if(dorespawn)
- FOR_EACH_CLIENT(self) {
- if(self.flags & FL_CLIENT) // reset all players
- {
- if(g_arena)
- {
- if(self.spawned)
- PutClientInServer();
- else
- PutObserverInServer();
- }
- else if(g_ca && self.caplayer) {
- self.classname = "player";
- PutClientInServer();
- }
- else if(g_freezetag)
- {
- if(self.classname == "player")
- PutClientInServer();
- }
- else
- {
- /*
- only reset players if a restart countdown is active
- this can either be due to cvar sv_ready_restart_after_countdown having set
- restart_mapalreadyrestarted to 1 after the countdown ended or when
- sv_ready_restart_after_countdown is not used and countdown is still running
- */
- if (restart_mapalreadyrestarted || (time < game_starttime))
- {
- //NEW: changed behaviour so that it prevents that previous spectators/observers suddenly spawn as players
- if (self.classname == "player") {
- //PlayerScore_Clear(self);
- if(g_lms)
- PlayerScore_Add(self, SP_LMS_LIVES, LMS_NewPlayerLives());
- self.killcount = 0;
- //stop the player from moving so that he stands still once he gets respawned
- self.velocity = '0 0 0';
- self.avelocity = '0 0 0';
- self.movement = '0 0 0';
- PutClientInServer();
- }
- }
- }
- }
- }
-
- if(g_keyhunt)
- kh_Controller_SetThink(autocvar_g_balance_keyhunt_delay_round+(game_starttime - time), kh_StartRound);
-
- if(g_arena)
- if(champion && champion.classname == "player" && player_count > 1)
- UpdateFrags(champion, +1);
-
- self = oldself;
-}
-
-void Spawnqueue_Insert(entity e)
-{
- if(e.spawnqueue_in)
- return;
- dprint(strcat("Into queue: ", e.netname, "\n"));
- e.spawnqueue_in = TRUE;
- e.spawnqueue_prev = spawnqueue_last;
- e.spawnqueue_next = world;
- if(spawnqueue_last)
- spawnqueue_last.spawnqueue_next = e;
- spawnqueue_last = e;
- if(!spawnqueue_first)
- spawnqueue_first = e;
-}
-
-void Spawnqueue_Remove(entity e)
-{
- if(!e.spawnqueue_in)
- return;
- dprint(strcat("Out of queue: ", e.netname, "\n"));
- e.spawnqueue_in = FALSE;
- if(e == spawnqueue_first)
- spawnqueue_first = e.spawnqueue_next;
- if(e == spawnqueue_last)
- spawnqueue_last = e.spawnqueue_prev;
- if(e.spawnqueue_prev)
- e.spawnqueue_prev.spawnqueue_next = e.spawnqueue_next;
- if(e.spawnqueue_next)
- e.spawnqueue_next.spawnqueue_prev = e.spawnqueue_prev;
- e.spawnqueue_next = world;
- e.spawnqueue_prev = world;
-}
-
-void Spawnqueue_Unmark(entity e)
-{
- if(!e.spawned)
- return;
- e.spawned = FALSE;
- numspawned = numspawned - 1;
-}
-
-void Spawnqueue_Mark(entity e)
-{
- if(e.spawned)
- return;
- e.spawned = TRUE;
- numspawned = numspawned + 1;
-}
-
-/**
- * If roundbased arena game mode is active, it centerprints the texts for the
- * player when player is waiting for the countdown to finish.
- * Blocks the players movement while countdown is active.
- * Unblocks the player once the countdown is over.
- *
- * Called in StartFrame()
- */
-float roundStartTime_prev; // prevent networkspam
-void Arena_Warmup()
-{
- float f;
- entity e;
-
- if(gameover)
- {
- if(warmup && time < warmup)
- {
- Kill_Notification(NOTIF_ALL, world, MSG_CENTER_CPID, CPID_ARENA);
- warmup = 0;
- }
- if(champion && g_arena)
- {
- FOR_EACH_REALCLIENT(e)
- centerprint(e, strcat("The Champion is ", champion.netname));
- champion = world;
- }
- return;
- }
- if((!g_arena && !g_ca && !g_freezetag) || (g_arena && !arena_roundbased) || (time < game_starttime))
- return;
-
- f = ceil(warmup - time);
-
- if(inWarmupStage)
- allowed_to_spawn = 1;
- else if(!g_ca)
- allowed_to_spawn = 0;
-
- if(time < warmup && !inWarmupStage)
- {
- if (g_ca)
- allowed_to_spawn = 1;
- if(champion && g_arena)
- {
- FOR_EACH_REALCLIENT(e)
- centerprint(e, strcat("The Champion is ", champion.netname));
- }
-
- if(f != roundStartTime_prev) {
- roundStartTime_prev = f;
- if(g_ca && !(red_players && blue_players)) {
- Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ARENA_NEEDPLAYER);
- warmup = time + autocvar_g_ca_warmup;
- } else {
- if(f == 5)
- Send_Notification(NOTIF_ALL, world, MSG_ANNCE, ANNCE_PREPARE);
- else if(f == 3)
- Send_Notification(NOTIF_ALL, world, MSG_ANNCE, ANNCE_NUM_3);
- else if(f == 2)
- Send_Notification(NOTIF_ALL, world, MSG_ANNCE, ANNCE_NUM_2);
- else if(f == 1)
- Send_Notification(NOTIF_ALL, world, MSG_ANNCE, ANNCE_NUM_1);
-
- Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ARENA_ROUNDSTART, f);
- }
- }
-
- if (g_arena) {
- FOR_EACH_CLIENT(e)
- {
- if(e.spawned && e.classname == "player")
- e.player_blocked = 1;
- }
- }
- }
- else if(f > -1 && f != roundStartTime_prev)
- {
- roundStartTime_prev = f;
- if(g_ca) {
- if(red_players && blue_players)
- allowed_to_spawn = 0;
- else
- reset_map(TRUE);
- } else {
- Send_Notification(NOTIF_ALL, world, MSG_MULTI, MULTI_ARENA_BEGIN);
- }
-
- if(g_arena) {
- FOR_EACH_CLIENT(e)
- {
- if(e.player_blocked)
- e.player_blocked = 0;
- }
- }
- }
-
- // clear champion to avoid centerprinting again the champion msg
- if (champion)
- champion = world;
-}
-
-void count_players()
-{
- // count amount of players in each team
- total_players = red_players = blue_players = yellow_players = pink_players = 0;
- FOR_EACH_PLAYER(self) {
- if (self.team == NUM_TEAM_1)
- {
- red_players += 1;
- total_players += 1;
- }
- else if (self.team == NUM_TEAM_2)
- {
- blue_players += 1;
- total_players += 1;
- }
- else if (self.team == NUM_TEAM_3)
- {
- yellow_players += 1;
- total_players += 1;
- }
- else if (self.team == NUM_TEAM_4)
- {
- pink_players += 1;
- total_players += 1;
- }
- }
-}
-
-void count_alive_players()
-{
- totalalive = redalive = bluealive = yellowalive = pinkalive = 0;
- if(g_ca)
- {
- FOR_EACH_PLAYER(self) {
- if (self.team == NUM_TEAM_1 && self.health >= 1)
- {
- redalive += 1;
- totalalive += 1;
- }
- else if (self.team == NUM_TEAM_2 && self.health >= 1)
- {
- bluealive += 1;
- totalalive += 1;
- }
- }
- FOR_EACH_REALCLIENT(self) {
- self.redalive_stat = redalive;
- self.bluealive_stat = bluealive;
- }
- }
- else if(g_freezetag)
- {
- // count amount of alive players in each team
- FOR_EACH_PLAYER(self) {
- if (self.team == NUM_TEAM_1 && self.freezetag_frozen == 0 && self.health >= 1)
- {
- redalive += 1;
- totalalive += 1;
- }
- else if (self.team == NUM_TEAM_2 && self.freezetag_frozen == 0 && self.health >= 1)
- {
- bluealive += 1;
- totalalive += 1;
- }
- else if (self.team == NUM_TEAM_3 && self.freezetag_frozen == 0 && self.health >= 1)
- {
- yellowalive += 1;
- totalalive += 1;
- }
- else if (self.team == NUM_TEAM_4 && self.freezetag_frozen == 0 && self.health >= 1)
- {
- pinkalive += 1;
- totalalive += 1;
- }
- }
- FOR_EACH_REALCLIENT(self) {
- self.redalive_stat = redalive;
- self.bluealive_stat = bluealive;
- self.yellowalive_stat = yellowalive;
- self.pinkalive_stat = pinkalive;
- }
- }
-
-}
-
-/**
- * This function finds out whether an arena round is over 1 player is left.
- * It determines the last player who's still alive and saves it's entity reference
- * in the global variable 'champion'. Then the new enemy/enemies are put into the server.
- *
- * Gets called in StartFrame()
- */
-void Spawnqueue_Check()
-{
- if(warmup == 0 && g_ca && !inWarmupStage)
- {
- if(red_players || blue_players)
- reset_map(TRUE);
- return;
- }
- if(time < warmup + 1 || inWarmupStage || intermission_running)
- return;
-
- if(g_ca) {
- if(allowed_to_spawn) // round is not started yet
- return;
- if(!next_round) {
- if(!(redalive && bluealive)) {
- // every player of (at least) one team is dead, round ends here
- if(redalive) {
- play2all("ctf/red_capture.wav");
- FOR_EACH_CLIENT(self) centerprint(self, "^1RED ^7team wins the round");
- TeamScore_AddToTeam(NUM_TEAM_1, ST_SCORE, +1);
- }
- else if(bluealive) {
- play2all("ctf/blue_capture.wav");
- FOR_EACH_CLIENT(self) centerprint(self, "^4BLUE ^7team wins the round");
- TeamScore_AddToTeam(NUM_TEAM_2, ST_SCORE, +1);
- }
- else
- FOR_EACH_CLIENT(self) centerprint(self, "^7Round tied");
- next_round = -1;
- }
- else if(time - warmup > autocvar_g_ca_round_timelimit) {
- FOR_EACH_CLIENT(self) centerprint(self, "^7Round tied");
- next_round = time + 5;
- }
- }
- else if(next_round == -1) {
- // wait for killed players to be put as spectators
- if(!(red_players && blue_players))
- next_round = time + 5;
- }
- else if((next_round > 0 && next_round < time))
- {
- next_round = 0;
- reset_map(TRUE);
- }
- } else if(g_freezetag) {
- if((next_round && next_round < time))
- {
- next_round = 0;
- reset_map(TRUE);
- }
- } else { // arena
- //extend next_round if it isn't set yet and only 1 player is spawned
- if(!next_round)
- if(numspawned < 2)
- next_round = time + 3;
-
- if(!arena_roundbased || (next_round && next_round < time && player_count > 1))
- {
- next_round = 0;
-
- if(arena_roundbased)
- {
- champion = find(world, classname, "player");
- while(champion && champion.deadflag)
- champion = find(champion, classname, "player");
- reset_map(TRUE);
- }
-
- while(numspawned < maxspawned && spawnqueue_first)
- {
- self = spawnqueue_first;
-
- bprint ("^4", self.netname, "^4 is the next challenger\n");
-
- Spawnqueue_Remove(self);
- Spawnqueue_Mark(self);
-
- self.classname = "player";
- PutClientInServer();
- }
- }
- }
-}
float autocvar_g_arena_point_leadlimit;
float autocvar_g_arena_point_limit;
float autocvar_g_arena_roundbased;
+float autocvar_g_arena_round_timelimit;
float autocvar_g_arena_warmup;
float autocvar_g_assault;
float autocvar_g_balance_armor_blockpercent;
float autocvar_g_ca_point_limit;
float autocvar_g_ca_round_timelimit;
float autocvar_g_ca_spectate_enemies;
+float autocvar_g_ca_teams;
+float autocvar_g_ca_teams_override;
float autocvar_g_ca_warmup;
float autocvar_g_campaign;
#define autocvar_g_campaign_forceteam cvar("g_campaign_forceteam")
string autocvar_g_forced_team_red;
string autocvar_g_forced_team_yellow;
float autocvar_g_freezetag_frozen_force;
+float autocvar_g_freezetag_frozen_maxtime;
float autocvar_g_freezetag_point_leadlimit;
float autocvar_g_freezetag_point_limit;
float autocvar_g_freezetag_revive_extra_size;
float autocvar_g_freezetag_revive_speed;
float autocvar_g_freezetag_revive_clearspeed;
+float autocvar_g_freezetag_round_timelimit;
+float autocvar_g_freezetag_teams;
+float autocvar_g_freezetag_teams_override;
float autocvar_g_freezetag_warmup;
#define autocvar_g_friendlyfire cvar("g_friendlyfire")
#define autocvar_g_friendlyfire_virtual cvar("g_friendlyfire_virtual")
float autocvar_g_spawnpoints_auto_move_out_of_solid;
#define autocvar_g_spawnshieldtime cvar("g_spawnshieldtime")
float autocvar_g_spawnsound;
-float autocvar_g_start_delay;
#define autocvar_g_start_weapon_laser cvar("g_start_weapon_laser")
float autocvar_g_tdm_team_spawns;
float autocvar_g_tdm_teams;
return FALSE;
}
- if(g_freezetag)
- if(e.freezetag_frozen)
- return FALSE;
+ if(e.freezetag_frozen)
+ return FALSE;
// If neither player has ball then don't attack unless the ball is on the
// ground.
FOR_EACH_REALCLIENT(head)
{
- if(head.classname == "player" || g_lms || g_arena || g_ca)
+ if(head.classname == "player" || g_lms || g_arena || head.caplayer == 1)
++activerealplayers;
++realplayers;
}
else
{
float mindist;
- if (arena_roundbased && !g_ca)
+ if (g_arena && arena_roundbased)
mindist = 800;
else
mindist = 100;
WriteEntity(MSG_ONE, self);
}
+ if(g_lms)
+ {
+ // Only if the player cannot play at all
+ if(PlayerScore_Add(self, SP_LMS_RANK, 0) == 666)
+ self.frags = FRAGS_SPECTATOR;
+ else
+ self.frags = FRAGS_LMS_LOSER;
+ }
+ else if((g_race && g_race_qualifying) || g_cts)
+ {
+ if(PlayerScore_Add(self, SP_RACE_FASTEST, 0))
+ self.frags = FRAGS_LMS_LOSER;
+ else
+ self.frags = FRAGS_SPECTATOR;
+ }
+ else
+ self.frags = FRAGS_SPECTATOR;
+
MUTATOR_CALLHOOK(MakePlayerObserver);
minstagib_stop_countdown(self);
self.pauseregen_finished = 0;
self.damageforcescale = 0;
self.death_time = 0;
+ self.respawn_flags = 0;
self.respawn_time = 0;
+ self.stat_respawn_time = 0;
self.alpha = 0;
self.scale = 0;
self.fade_time = 0;
self.punchvector = '0 0 0';
self.oldvelocity = self.velocity;
self.fire_endtime = -1;
-
- if(g_arena)
- {
- if(self.version_mismatch)
- {
- self.frags = FRAGS_SPECTATOR;
- Spawnqueue_Unmark(self);
- Spawnqueue_Remove(self);
- }
- else
- {
- self.frags = FRAGS_LMS_LOSER;
- Spawnqueue_Insert(self);
- }
- }
- else if(g_lms)
- {
- // Only if the player cannot play at all
- if(PlayerScore_Add(self, SP_LMS_RANK, 0) == 666)
- self.frags = FRAGS_SPECTATOR;
- else
- self.frags = FRAGS_LMS_LOSER;
- }
- else if(g_ca)
- {
- if(self.caplayer)
- self.frags = FRAGS_LMS_LOSER;
- else
- self.frags = FRAGS_SPECTATOR;
- }
- else if((g_race && g_race_qualifying) || g_cts)
- {
- if(PlayerScore_Add(self, SP_RACE_FASTEST, 0))
- self.frags = FRAGS_LMS_LOSER;
- else
- self.frags = FRAGS_SPECTATOR;
- }
- else
- self.frags = FRAGS_SPECTATOR;
}
.float model_randomizer;
void PutClientInServer (void)
{
if(clienttype(self) == CLIENTTYPE_BOT)
- {
self.classname = "player";
- if(g_ca)
- self.caplayer = 1;
- }
else if(clienttype(self) == CLIENTTYPE_REAL)
{
msg_entity = self;
self.classname = "observer";
}
- if((g_arena && !self.spawned) || (g_ca && !allowed_to_spawn))
- self.classname = "observer";
+ MUTATOR_CALLHOOK(PutClientInServer);
if(gameover)
self.classname = "observer";
- if(self.classname == "player" && (!g_ca || (g_ca && allowed_to_spawn))) {
+ if(self.classname == "player") {
entity spot, oldself;
float j;
}
self.damageforcescale = 2;
self.death_time = 0;
+ self.respawn_flags = 0;
self.respawn_time = 0;
+ self.stat_respawn_time = 0;
self.scale = 0;
self.fade_time = 0;
self.pain_frame = 0;
self.lastteleporttime = time; // prevent insane speeds due to changing origin
self.hud = HUD_NORMAL;
- if(g_arena)
- {
- Spawnqueue_Remove(self);
- Spawnqueue_Mark(self);
- }
- else if(g_ca)
- self.caplayer = 1;
-
self.event_damage = PlayerDamage;
self.bot_attack = TRUE;
}
else if(self.killindicator_teamchange == -2)
{
- if(g_ca)
- self.caplayer = 0;
if(blockSpectators)
Send_Notification(NOTIF_ONE_ONLY, self, MSG_INFO, INFO_SPECTATE_WARNING, autocvar_g_maxplayers_spectator_blocktime);
PutObserverInServer();
}
else
SV_ChangeTeam(self.killindicator_teamchange - 1);
+ self.killindicator_teamchange = 0;
}
void ClientKill_Now()
void ClientKill (void)
{
- if (gameover)
- return;
+ if(gameover) return;
+ if(self.player_blocked) return;
+ if(self.freezetag_frozen) return;
- if((g_arena || g_ca) && ((champion && champion.classname == "player" && player_count > 1) || player_count == 1)) // don't allow a kill in this case either
- {
- // do nothing
- }
- else if(self.freezetag_frozen)
- {
- // do nothing
- }
- else
- ClientKill_TeamChange(0);
+ ClientKill_TeamChange(0);
}
void CTS_ClientKill (entity e) // silent version of ClientKill, used when player finishes a CTS run. Useful to prevent cheating by running back to the start line and starting out with more speed
else
stuffcmd(self, "set _teams_available 0\n");
- if(g_arena || g_ca)
- {
- self.classname = "observer";
- if(g_arena)
- Spawnqueue_Insert(self);
- }
-
attach_entcs();
bot_relinkplayerlist();
CSQCMODEL_AUTOINIT();
self.model_randomizer = random();
-
- if(clienttype(self) != CLIENTTYPE_REAL)
- return;
-
- sv_notice_join();
-
- MUTATOR_CALLHOOK(ClientConnect);
+
+ if(clienttype(self) == CLIENTTYPE_REAL)
+ sv_notice_join();
+
+ MUTATOR_CALLHOOK(ClientConnect);
}
/*
=============
bot_relinkplayerlist();
- if(g_arena)
- {
- Spawnqueue_Unmark(self);
- Spawnqueue_Remove(self);
- }
-
accuracy_free(self);
ClientData_Detach();
PlayerScore_Detach(self);
limita = limita * limit_mod;
//limitf = limitf * limit_mod;
- if(g_lms && g_ca)
+ if(g_ca)
rot_mod = 0;
if (!g_minstagib && !g_ca && (!g_lms || autocvar_g_lms_regenerate))
self.dmg_inflictor = spectatee.dmg_inflictor;
self.v_angle = spectatee.v_angle;
self.angles = spectatee.v_angle;
- self.stat_respawn_time = spectatee.stat_respawn_time;
if(!self.BUTTON_USE)
self.fixangle = TRUE;
setorigin(self, spectatee.origin);
void LeaveSpectatorMode()
{
+ if(self.caplayer)
+ return;
if(nJoinAllowed(self))
{
if(!teamplay || autocvar_g_campaign || autocvar_g_balance_teams || (self.wasplayer && autocvar_g_changeteam_banned) || self.team_forced > 0)
if(IS_PLAYER(self)) { Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_JOIN_PLAY, self.netname); }
}
- else if not(g_ca && self.caplayer) { stuffcmd(self, "menu_showteamselect\n"); }
+ else
+ stuffcmd(self, "menu_showteamselect\n");
}
else
{
return maxclients - totalClients;
float currentlyPlaying = 0;
- FOR_EACH_REALPLAYER(e)
- currentlyPlaying += 1;
+ FOR_EACH_REALCLIENT(e)
+ if(e.classname == "player" || e.caplayer == 1)
+ currentlyPlaying += 1;
if(currentlyPlaying < autocvar_g_maxplayers)
return min(maxclients - totalClients, autocvar_g_maxplayers - currentlyPlaying);
WarpZone_PlayerPhysics_FixVAngle();
self.stat_game_starttime = game_starttime;
+ self.stat_round_starttime = round_starttime;
self.stat_allow_oldnexbeam = autocvar_g_allow_oldnexbeam;
self.stat_leadlimit = autocvar_leadlimit;
- if(g_arena || (g_ca && !allowed_to_spawn))
- self.stat_respawn_time = 0;
- else
- self.stat_respawn_time = self.respawn_time;
-
if(frametime)
{
// physics frames: update anticheat stuff
if (self.deadflag != DEAD_NO)
{
- float button_pressed, force_respawn;
if(self.personal && g_race_qualifying)
{
if(time > self.respawn_time)
{
self.respawn_time = time + 1; // only retry once a second
+ self.stat_respawn_time = self.respawn_time;
respawn();
self.impulse = 141;
}
}
else
{
+ float button_pressed;
if(frametime)
player_anim();
button_pressed = (self.BUTTON_ATCK || self.BUTTON_JUMP || self.BUTTON_ATCK2 || self.BUTTON_HOOK || self.BUTTON_USE);
- force_respawn = (g_lms || g_ca || g_cts || autocvar_g_forced_respawn);
+
if (self.deadflag == DEAD_DYING)
{
- if(force_respawn)
+ if(self.respawn_flags & RESPAWN_FORCE)
self.deadflag = DEAD_RESPAWNING;
else if(!button_pressed)
self.deadflag = DEAD_DEAD;
respawn();
}
}
+
ShowRespawnCountdown();
+
+ if(self.respawn_flags & RESPAWN_SILENT)
+ self.stat_respawn_time = 0;
+ else
+ self.stat_respawn_time = self.respawn_time;
}
// if respawning, invert stat_respawn_time to indicate this, the client translates it
return;
self.impulse = 0;
+ // forbid impulses when not in round time
+ if(round_handler_IsActive() && !round_handler_IsRoundStarted())
+ return;
+
if (timeout_status == TIMEOUT_ACTIVE) //don't allow any impulses while the game is paused
return;
}
void ClientKill_Now_TeamChange();
-void freezetag_CheckWinner();
void PlayerDamage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
{
float valid_damage_for_weaponstats;
float excess;
- if((g_arena && numspawned < 2) || (g_ca && allowed_to_spawn) && !inWarmupStage)
- return;
-
dh = max(self.health, 0);
da = max(self.armorvalue, 0);
}
}
- if(!g_freezetag)
- {
- // become fully visible
- self.alpha = default_player_alpha;
- // throw a weapon
- SpawnThrownWeapon (self.origin + (self.mins + self.maxs) * 0.5, self.switchweapon);
- }
-
// print an obituary message
Obituary (attacker, inflictor, self, deathtype);
race_PreDie();
if(accuracy_isgooddamage(attacker, self))
attacker.accuracy.(accuracy_frags[w-1]) += 1;
- if(deathtype == DEATH_HURTTRIGGER && g_freezetag)
- {
- PutClientInServer();
- count_alive_players(); // re-count players
- freezetag_CheckWinner();
- return;
- }
-
frag_attacker = attacker;
frag_inflictor = inflictor;
frag_target = self;
+ frag_deathtype = deathtype;
MUTATOR_CALLHOOK(PlayerDies);
+
weapon_action(self.weapon, WR_PLAYERDEATH);
RemoveGrapplingHook(self);
//WriteAngle (MSG_ONE, 80);
}
- if(defer_ClientKill_Now_TeamChange) // TODO does this work with FreezeTag?
- ClientKill_Now_TeamChange();
-
- if(g_arena)
- Spawnqueue_Unmark(self);
+ if(defer_ClientKill_Now_TeamChange)
+ ClientKill_Now_TeamChange(); // can turn player into spectator
- if(g_freezetag)
+ // player could have been miraculously resuscitated ;)
+ // e.g. players in freezetag get frozen, they don't really die
+ if(self.health >= 1 || self.classname != "player")
return;
// when we get here, player actually dies
- // clear waypoints (do this AFTER FreezeTag)
+
+ // clear waypoints
WaypointSprite_PlayerDead();
+ // throw a weapon
+ SpawnThrownWeapon (self.origin + (self.mins + self.maxs) * 0.5, self.switchweapon);
+ // become fully visible
+ self.alpha = default_player_alpha;
// make the corpse upright (not tilted)
self.angles_x = 0;
self.angles_z = 0;
self.respawn_countdown = 10; // first number to count down from is 10
else
self.respawn_countdown = -1; // do not count down
+
+ if(g_lms || g_cts || autocvar_g_forced_respawn)
+ self.respawn_flags = self.respawn_flags | RESPAWN_FORCE;
+
self.death_time = time;
if (random() < 0.5)
animdecide_setstate(self, self.anim_state | ANIMSTATE_DEAD1, TRUE);
return 0;
if (g_lms)
return 0;
- if (g_ca)
- return 0;
if (g_cts)
return 0;
if (g_nexball && w == WEP_GRENADE_LAUNCHER)
Send_Notification(NOTIF_ONE, self, MSG_MULTI, ITEM_WEAPON_DROP, a, w);
}
-// Bringed back weapon frame
+float forbidWeaponUse()
+{
+ if(time < game_starttime && !autocvar_sv_ready_restart_after_countdown)
+ return 1;
+ if(round_handler_IsActive() && !round_handler_IsRoundStarted())
+ return 1;
+ if(self.player_blocked)
+ return 1;
+ if(self.freezetag_frozen)
+ return 1;
+ return 0;
+}
+
void W_WeaponFrame()
{
vector fo, ri, up;
if (frametime)
self.weapon_frametime = frametime;
- if(((arena_roundbased || g_ca || g_freezetag) && time < warmup) || ((time < game_starttime) && !autocvar_sv_ready_restart_after_countdown))
- return;
-
- if(self.freezetag_frozen == 1)
- return;
-
if (!self.weaponentity || self.health < 1)
return; // Dead player can't use weapons and injure impulse commands
+ if(forbidWeaponUse())
+ if(self.weaponentity.state != WS_CLEAR)
+ {
+ w_ready();
+ return;
+ }
+
if(!self.switchweapon)
{
self.weapon = 0;
{
if(nJoinAllowed(self))
{
- if(g_ca) { self.caplayer = 1; }
if(autocvar_g_campaign) { campaign_bots_may_start = 1; }
-
+
self.classname = "player";
PlayerScore_Clear(self);
Kill_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER_CPID, CPID_PREVENT_JOIN);
{
if(!readyrestart_happened || autocvar_sv_ready_restart_repeatable)
{
+ if(time < game_starttime) // game is already restarting
+ return;
if (self.ready) // toggle
{
self.ready = FALSE;
if(self.classname == "player" && 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(g_ca && self.caplayer && (self.classname == "spectator" || self.classname == "observer"))
+ if(self.caplayer && (self.classname == "spectator" || self.classname == "observer"))
{
sprint(self, "WARNING: you will spectate in the next round.\n");
self.caplayer = 0;
// Game logic for warmup
// =======================
+// Resets the state of all clients, items, weapons, waypoints, ... of the map.
+void reset_map(float dorespawn)
+{
+ entity oldself;
+ oldself = self;
+
+ if(time <= game_starttime && round_handler_IsActive())
+ round_handler_Reset(game_starttime);
+
+ if(g_race || g_cts)
+ race_ReadyRestart();
+ else MUTATOR_CALLHOOK(reset_map_global);
+
+ lms_lowest_lives = 999;
+ lms_next_place = player_count;
+
+ for(self = world; (self = nextent(self)); )
+ if(clienttype(self) == CLIENTTYPE_NOTACLIENT)
+ {
+ if(self.reset)
+ {
+ self.reset();
+ continue;
+ }
+
+ if(self.team_saved)
+ self.team = self.team_saved;
+
+ if(self.flags & FL_PROJECTILE) // remove any projectiles left
+ remove(self);
+ }
+
+ // Waypoints and assault start come LAST
+ for(self = world; (self = nextent(self)); )
+ if(clienttype(self) == CLIENTTYPE_NOTACLIENT)
+ {
+ if(self.reset2)
+ {
+ self.reset2();
+ continue;
+ }
+ }
+
+ // Moving the player reset code here since the player-reset depends
+ // on spawnpoint entities which have to be reset first --blub
+ if(dorespawn)
+ if(!MUTATOR_CALLHOOK(reset_map_players))
+ FOR_EACH_CLIENT(self) // reset all players
+ {
+ /*
+ only reset players if a restart countdown is active
+ this can either be due to cvar sv_ready_restart_after_countdown having set
+ restart_mapalreadyrestarted to 1 after the countdown ended or when
+ sv_ready_restart_after_countdown is not used and countdown is still running
+ */
+ if (restart_mapalreadyrestarted || (time < game_starttime))
+ {
+ //NEW: changed behaviour so that it prevents that previous spectators/observers suddenly spawn as players
+ if (IS_PLAYER(self)) {
+ //PlayerScore_Clear(self);
+ if(g_lms)
+ PlayerScore_Add(self, SP_LMS_LIVES, LMS_NewPlayerLives());
+ self.killcount = 0;
+ //stop the player from moving so that he stands still once he gets respawned
+ self.velocity = '0 0 0';
+ self.avelocity = '0 0 0';
+ self.movement = '0 0 0';
+ PutClientInServer();
+ }
+ }
+ }
+
+ if(g_keyhunt)
+ kh_Controller_SetThink(autocvar_g_balance_keyhunt_delay_round + (game_starttime - time), kh_StartRound);
+
+ self = oldself;
+}
+
// Restarts the map after the countdown is over (and cvar sv_ready_restart_after_countdown is set)
void ReadyRestart_think()
{
checkrules_suddendeathend = checkrules_overtimesadded = checkrules_suddendeathwarning = 0;
readyrestart_happened = 1;
- game_starttime = time;
- if(!g_ca && !g_arena) { game_starttime += RESTART_COUNTDOWN; }
+ game_starttime = time + RESTART_COUNTDOWN;
// clear player attributes
FOR_EACH_CLIENT(tmp_player)
inWarmupStage = 0; // once the game is restarted the game is in match stage
// reset the .ready status of all players (also spectators)
- FOR_EACH_CLIENTSLOT(tmp_player) { tmp_player.ready = 0; }
+ FOR_EACH_REALCLIENT(tmp_player) { tmp_player.ready = 0; }
readycount = 0;
Nagger_ReadyCounted(); // NOTE: this causes a resend of that entity, and will also turn off warmup state on the client
// lock teams with lockonrestart
- if(autocvar_teamplay_lockonrestart && teamplay)
+ if(autocvar_teamplay_lockonrestart && teamplay)
{
lockteams = 1;
bprint("^1The teams are now locked.\n");
}
//initiate the restart-countdown-announcer entity
- if(autocvar_sv_ready_restart_after_countdown && !g_ca && !g_arena)
+ if(autocvar_sv_ready_restart_after_countdown)
{
restart_timer = spawn();
restart_timer.think = ReadyRestart_think;
float ready_needed_factor, ready_needed_count;
float t_ready = 0, t_players = 0;
- FOR_EACH_REALPLAYER(tmp_player)
+ FOR_EACH_REALCLIENT(tmp_player)
{
- ++t_players;
- if(tmp_player.ready) { ++t_ready; }
+ if(IS_PLAYER(tmp_player) || tmp_player.caplayer == 1)
+ {
+ ++t_players;
+ if(tmp_player.ready) { ++t_ready; }
+ }
}
readycount = t_ready;
float readyrestart_happened; // keeps track of whether a restart has already happened
float restart_mapalreadyrestarted; // bool, indicates whether reset_map() was already executed
.float ready; // flag for if a player is ready
+void reset_map(float dorespawn);
void ReadyCount();
\ No newline at end of file
float DEAD_RESPAWNABLE = 3;
float DEAD_RESPAWNING = 4;
+float RESPAWN_FORCE = 1;
+float RESPAWN_SILENT = 2;
+
float DAMAGE_NO = 0;
float DAMAGE_YES = 1;
float DAMAGE_AIM = 2;
//.float cnt2;
.float play_time;
+.float respawn_flags;
.float respawn_time;
.float death_time;
.float fade_time;
string cvar_purechanges;
float cvar_purechanges_count;
-float game_starttime; //point in time when the countdown is over
+float game_starttime; //point in time when the countdown to game start is over
+float round_starttime; //point in time when the countdown to round start is over
.float stat_game_starttime;
+.float stat_round_starttime;
.float stat_sv_airaccel_qw;
.float stat_sv_airstrafeaccel_qw;
.void (float act_state) setactive;
.entity realowner;
-float allowed_to_spawn; // boolean variable used by the clan arena code to determine if a player can spawn (after the round has ended)
-
float serverflags;
.float team_forced; // can be a team number to force a team, or 0 for default action, or -1 for forced spectator
.float player_blocked;
.float freezetag_frozen;
-.float freezetag_revive_progress;
.entity muzzle_flash;
.float misc_bulletcounter; // replaces uzi & hlac bullet counter.
PlayerScore_Add(targ, SP_DEATHS, 1);
- if(g_arena || g_ca)
- if(autocvar_g_arena_roundbased)
- return;
-
if(targ != attacker) // not for suicides
if(g_weaponarena_random)
{
error("Owner lost the hook!\n");
return;
}
- if(LostMovetypeFollow(self) || intermission_running)
+ if(LostMovetypeFollow(self) || intermission_running || (round_handler_IsActive() && !round_handler_IsRoundStarted()))
{
RemoveGrapplingHook(self.realowner);
return;
float s;
vector vs;
- if((arena_roundbased && time < warmup) || (time < game_starttime))
- return;
-
- if(self.freezetag_frozen)
- return;
-
- if(self.vehicle)
- return;
+ if(forbidWeaponUse()) return;
+ if(self.vehicle) return;
makevectors(self.v_angle);
BADCVAR("g_arena");
BADCVAR("g_assault");
BADCVAR("g_ca");
+ BADCVAR("g_ca_teams");
BADCVAR("g_ctf");
BADCVAR("g_cts");
BADCVAR("g_dm");
BADCVAR("g_domination");
BADCVAR("g_domination_default_teams");
BADCVAR("g_freezetag");
+ BADCVAR("g_freezetag_teams");
BADCVAR("g_keepaway");
BADCVAR("g_keyhunt");
BADCVAR("g_keyhunt_teams");
- BADCVAR("g_keyhunt_teams");
BADCVAR("g_lms");
BADCVAR("g_nexball");
BADCVAR("g_onslaught");
BADCVAR("g_balance_teams_scorefactor");
BADCVAR("g_ban_sync_trusted_servers");
BADCVAR("g_ban_sync_uri");
+ BADCVAR("g_ca_teams_override");
BADCVAR("g_ctf_ignore_frags");
BADCVAR("g_domination_point_limit");
+ BADCVAR("g_freezetag_teams_override");
BADCVAR("g_friendlyfire");
BADCVAR("g_fullbrightitems");
BADCVAR("g_fullbrightplayers");
compressShortVector_init();
- allowed_to_spawn = TRUE;
-
entity head;
head = nextent(world);
maxclients = 0;
addstat(STAT_SWITCHWEAPON, AS_INT, switchweapon);
addstat(STAT_SWITCHINGWEAPON, AS_INT, switchingweapon);
addstat(STAT_GAMESTARTTIME, AS_FLOAT, stat_game_starttime);
+ addstat(STAT_ROUNDSTARTTIME, AS_FLOAT, stat_round_starttime);
addstat(STAT_ALLOW_OLDNEXBEAM, AS_INT, stat_allow_oldnexbeam);
Nagger_Init();
addstat(STAT_HAGAR_LOAD, AS_INT, hagar_load);
- if(g_ca || g_freezetag)
- {
- addstat(STAT_REDALIVE, AS_INT, redalive_stat);
- addstat(STAT_BLUEALIVE, AS_INT, bluealive_stat);
- addstat(STAT_YELLOWALIVE, AS_INT, yellowalive_stat);
- addstat(STAT_PINKALIVE, AS_INT, pinkalive_stat);
- }
- if(g_freezetag)
- {
- addstat(STAT_FROZEN, AS_INT, freezetag_frozen);
- addstat(STAT_REVIVE_PROGRESS, AS_FLOAT, freezetag_revive_progress);
- }
-
// g_movementspeed hack
addstat(STAT_MOVEVARS_AIRSPEEDLIMIT_NONQW, AS_FLOAT, stat_sv_airspeedlimit_nonqw);
addstat(STAT_MOVEVARS_MAXSPEED, AS_FLOAT, stat_sv_maxspeed);
{
s = strcat(":player:see-labels:", GetPlayerScoreString(other, 0), ":");
s = strcat(s, ftos(rint(time - other.jointime)), ":");
- if(other.classname == "player" || g_arena || g_ca || g_lms)
+ if(other.classname == "player" || g_arena || other.caplayer == 1 || g_lms)
s = strcat(s, ftos(other.team), ":");
else
s = strcat(s, "spectator:");
if(!g_weapon_stay)
g_weapon_stay = cvar("g_weapon_stay");
- if not(inWarmupStage && !g_ca)
- game_starttime = cvar("g_start_delay");
+ if not(inWarmupStage)
+ game_starttime = time + cvar("g_start_delay");
readplayerstartcvars();
}
MUTATOR_HOOKABLE(MakePlayerObserver);
// called when a player becomes observer, after shared setup
+MUTATOR_HOOKABLE(PutClientInServer);
+ entity self; // client wanting to spawn
+
MUTATOR_HOOKABLE(PlayerSpawn);
entity spawn_spot; // spot that was used, or world
// called when a player spawns as player, after shared setup, before his weapon is chosen (so items may be changed in here)
+MUTATOR_HOOKABLE(reset_map_global);
+ // called in reset_map
+
+MUTATOR_HOOKABLE(reset_map_players);
+ // called in reset_map
+
+MUTATOR_HOOKABLE(ForbidPlayerScore_Clear);
+ // returns 1 if clearing player score shall not be allowed
+
MUTATOR_HOOKABLE(ClientDisconnect);
// called when a player disconnects
entity frag_inflictor;
entity frag_attacker;
entity frag_target; // same as self
+ float frag_deathtype;
MUTATOR_HOOKABLE(GiveFragsForKill);
// called when someone was fragged by "self", and is expected to change frag_score to adjust scoring for the kill
--- /dev/null
+.float spawned;
+float maxspawned;
+float numspawned;
+.entity spawnqueue_next;
+.entity spawnqueue_prev;
+.float spawnqueue_in;
+entity spawnqueue_first;
+entity spawnqueue_last;
+
+void Spawnqueue_Insert(entity e)
+{
+ if(e.spawnqueue_in)
+ return;
+ dprint(strcat("Into queue: ", e.netname, "\n"));
+ e.spawnqueue_in = TRUE;
+ e.spawnqueue_prev = spawnqueue_last;
+ e.spawnqueue_next = world;
+ if(spawnqueue_last)
+ spawnqueue_last.spawnqueue_next = e;
+ spawnqueue_last = e;
+ if(!spawnqueue_first)
+ spawnqueue_first = e;
+}
+
+void Spawnqueue_Remove(entity e)
+{
+ if(!e.spawnqueue_in)
+ return;
+ dprint(strcat("Out of queue: ", e.netname, "\n"));
+ e.spawnqueue_in = FALSE;
+ if(e == spawnqueue_first)
+ spawnqueue_first = e.spawnqueue_next;
+ if(e == spawnqueue_last)
+ spawnqueue_last = e.spawnqueue_prev;
+ if(e.spawnqueue_prev)
+ e.spawnqueue_prev.spawnqueue_next = e.spawnqueue_next;
+ if(e.spawnqueue_next)
+ e.spawnqueue_next.spawnqueue_prev = e.spawnqueue_prev;
+ e.spawnqueue_next = world;
+ e.spawnqueue_prev = world;
+}
+
+void Spawnqueue_Unmark(entity e)
+{
+ if(!e.spawned)
+ return;
+ e.spawned = FALSE;
+ numspawned = numspawned - 1;
+}
+
+void Spawnqueue_Mark(entity e)
+{
+ if(e.spawned)
+ return;
+ e.spawned = TRUE;
+ numspawned = numspawned + 1;
+}
+
+float Arena_CheckWinner()
+{
+ entity e;
+
+ if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0)
+ {
+ Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_OVER);
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_OVER);
+ round_handler_Init(5, autocvar_g_arena_warmup, autocvar_g_arena_round_timelimit);
+ return 1;
+ }
+
+ if(numspawned > 1)
+ return 0;
+
+ entity champion;
+ champion = world;
+ FOR_EACH_CLIENT(e)
+ {
+ if(e.spawned && IS_PLAYER(e))
+ champion = e;
+ }
+
+ if(champion)
+ {
+ Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_PLAYER_WIN, champion.netname);
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_PLAYER_WIN, champion.netname);
+ UpdateFrags(champion, +1);
+ }
+ else
+ {
+ Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_TIED);
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_TIED);
+ }
+ round_handler_Init(5, autocvar_g_arena_warmup, autocvar_g_arena_round_timelimit);
+ return 1;
+}
+
+void Arena_AddChallengers()
+{
+ entity e;
+ if(time < 2) // don't force players to spawn so early
+ return;
+ e = self;
+ while(numspawned < maxspawned && spawnqueue_first)
+ {
+ self = spawnqueue_first;
+
+ bprint ("^4", self.netname, "^4 is the next challenger\n");
+
+ Spawnqueue_Remove(self);
+ Spawnqueue_Mark(self);
+
+ self.classname = "player";
+ PutClientInServer();
+ }
+ self = e;
+}
+
+float prev_numspawned;
+float Arena_CheckPlayers()
+{
+ Arena_AddChallengers();
+
+ if(numspawned >= 2)
+ {
+ if(prev_numspawned > 0)
+ Kill_Notification(NOTIF_ALL, world, MSG_CENTER_CPID, CPID_MISSING_PLAYERS);
+ prev_numspawned = -1;
+ return 1;
+ }
+
+ if(prev_numspawned != numspawned && numspawned == 1)
+ {
+ if(maxspawned - numspawned > 0)
+ Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_MISSING_PLAYERS, maxspawned - numspawned);
+ prev_numspawned = numspawned;
+ }
+
+ return 0;
+}
+
+void Arena_RoundStart()
+{
+ entity e;
+ FOR_EACH_PLAYER(e)
+ e.player_blocked = 0;
+}
+
+MUTATOR_HOOKFUNCTION(arena_ClientDisconnect)
+{
+ Spawnqueue_Unmark(self);
+ Spawnqueue_Remove(self);
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(arena_reset_map_players)
+{
+ FOR_EACH_CLIENT(self)
+ {
+ if(self.spawned)
+ {
+ PutClientInServer();
+ self.player_blocked = 1;
+ }
+ else
+ PutObserverInServer();
+ }
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(arena_MakePlayerObserver)
+{
+ if(self.version_mismatch)
+ {
+ self.frags = FRAGS_SPECTATOR;
+ Spawnqueue_Unmark(self);
+ Spawnqueue_Remove(self);
+ }
+ else
+ {
+ self.frags = FRAGS_LMS_LOSER;
+ Spawnqueue_Insert(self);
+ }
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(arena_PutClientInServer)
+{
+ if(!self.spawned)
+ self.classname = "observer";
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(arena_ClientConnect)
+{
+ self.classname = "observer";
+ Spawnqueue_Insert(self);
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(arena_PlayerSpawn)
+{
+ Spawnqueue_Remove(self);
+ Spawnqueue_Mark(self);
+ if(arena_roundbased)
+ self.player_blocked = 1;
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(arena_ForbidPlayerScore_Clear)
+{
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(arena_GiveFragsForKill)
+{
+ if(arena_roundbased)
+ frag_score = 0; // score will be given to the champion when the round ends
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(arena_PlayerDies)
+{
+ // put dead players in the spawn queue
+ if(arena_roundbased)
+ self.respawn_flags = (RESPAWN_FORCE | RESPAWN_SILENT);
+ else
+ self.respawn_flags = RESPAWN_SILENT;
+ Spawnqueue_Unmark(self);
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(arena_SV_StartFrame)
+{
+ if(gameover) return 1;
+ if(time <= game_starttime || !arena_roundbased)
+ Arena_AddChallengers();
+ return 1;
+}
+
+void arena_Initialize()
+{
+ maxspawned = max(2, autocvar_g_arena_maxspawned);
+ arena_roundbased = autocvar_g_arena_roundbased;
+ if(arena_roundbased)
+ {
+ round_handler_Spawn(Arena_CheckPlayers, Arena_CheckWinner, Arena_RoundStart);
+ round_handler_Init(5, autocvar_g_arena_warmup, autocvar_g_arena_round_timelimit);
+ }
+}
+
+MUTATOR_DEFINITION(gamemode_arena)
+{
+ MUTATOR_HOOK(ClientDisconnect, arena_ClientDisconnect, CBC_ORDER_ANY);
+ MUTATOR_HOOK(reset_map_players, arena_reset_map_players, CBC_ORDER_ANY);
+ MUTATOR_HOOK(MakePlayerObserver, arena_MakePlayerObserver, CBC_ORDER_ANY);
+ MUTATOR_HOOK(PutClientInServer, arena_PutClientInServer, CBC_ORDER_ANY);
+ MUTATOR_HOOK(ClientConnect, arena_ClientConnect, CBC_ORDER_ANY);
+ MUTATOR_HOOK(PlayerSpawn, arena_PlayerSpawn, CBC_ORDER_ANY);
+ MUTATOR_HOOK(ForbidPlayerScore_Clear, arena_ForbidPlayerScore_Clear, CBC_ORDER_ANY);
+ MUTATOR_HOOK(GiveFragsForKill, arena_GiveFragsForKill, CBC_ORDER_ANY);
+ MUTATOR_HOOK(PlayerDies, arena_PlayerDies, CBC_ORDER_ANY);
+ MUTATOR_HOOK(SV_StartFrame, arena_SV_StartFrame, CBC_ORDER_ANY);
+
+ MUTATOR_ONADD
+ {
+ if(time > 1) // game loads at time 1
+ error("This is a game type and it cannot be added at runtime.");
+ arena_Initialize();
+ }
+
+ MUTATOR_ONREMOVE
+ {
+ print("This is a game type and it cannot be removed at runtime.");
+ return -1;
+ }
+
+ return 0;
+}
--- /dev/null
+// should be removed in the future, as other code should not have to care
+float arena_roundbased;
--- /dev/null
+float total_players;
+float redalive, bluealive, yellowalive, pinkalive;
+.float redalive_stat, bluealive_stat, yellowalive_stat, pinkalive_stat;
+float ca_teams;
+float allowed_to_spawn;
+
+void CA_count_alive_players()
+{
+ entity e;
+ total_players = redalive = bluealive = yellowalive = pinkalive = 0;
+ FOR_EACH_PLAYER(e) {
+ if(e.team == NUM_TEAM_1)
+ {
+ ++total_players;
+ if (e.health >= 1) ++redalive;
+ }
+ else if(e.team == NUM_TEAM_2)
+ {
+ ++total_players;
+ if (e.health >= 1) ++bluealive;
+ }
+ else if(e.team == NUM_TEAM_3)
+ {
+ ++total_players;
+ if (e.health >= 1) ++yellowalive;
+ }
+ else if(e.team == NUM_TEAM_4)
+ {
+ ++total_players;
+ if (e.health >= 1) ++pinkalive;
+ }
+ }
+ FOR_EACH_REALCLIENT(e) {
+ e.redalive_stat = redalive;
+ e.bluealive_stat = bluealive;
+ e.yellowalive_stat = yellowalive;
+ e.pinkalive_stat = pinkalive;
+ }
+}
+
+float CA_GetWinnerTeam()
+{
+ float winner_team = 0;
+ if(redalive >= 1)
+ winner_team = NUM_TEAM_1;
+ if(bluealive >= 1)
+ {
+ if(winner_team) return 0;
+ winner_team = NUM_TEAM_2;
+ }
+ if(yellowalive >= 1)
+ {
+ if(winner_team) return 0;
+ winner_team = NUM_TEAM_3;
+ }
+ if(pinkalive >= 1)
+ {
+ if(winner_team) return 0;
+ winner_team = NUM_TEAM_4;
+ }
+ if(winner_team)
+ return winner_team;
+ return -1; // no player left
+}
+
+#define CA_ALIVE_TEAMS() ((redalive > 0) + (bluealive > 0) + (yellowalive > 0) + (pinkalive > 0))
+#define CA_ALIVE_TEAMS_OK() (CA_ALIVE_TEAMS() == ca_teams)
+float CA_CheckWinner()
+{
+ if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0)
+ {
+ Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_OVER);
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_OVER);
+ allowed_to_spawn = FALSE;
+ round_handler_Init(5, autocvar_g_ca_warmup, autocvar_g_ca_round_timelimit);
+ return 1;
+ }
+
+ CA_count_alive_players();
+ if(CA_ALIVE_TEAMS() > 1)
+ return 0;
+
+ float winner_team = CA_GetWinnerTeam();
+ if(winner_team > 0)
+ {
+ Send_Notification(NOTIF_ALL, world, MSG_CENTER, APP_TEAM_NUM_4(winner_team, CENTER_ROUND_TEAM_WIN_));
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(winner_team, INFO_ROUND_TEAM_WIN_));
+ TeamScore_AddToTeam(winner_team, ST_SCORE, +1);
+ }
+ else if(winner_team == -1)
+ {
+ Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_TIED);
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_TIED);
+ }
+
+ allowed_to_spawn = FALSE;
+ round_handler_Init(5, autocvar_g_ca_warmup, autocvar_g_ca_round_timelimit);
+ return 1;
+}
+
+void CA_RoundStart()
+{
+ if(inWarmupStage)
+ allowed_to_spawn = TRUE;
+ else
+ allowed_to_spawn = FALSE;
+}
+
+float prev_total_players;
+float CA_CheckTeams()
+{
+ allowed_to_spawn = TRUE;
+ CA_count_alive_players();
+ if(CA_ALIVE_TEAMS_OK())
+ {
+ if(prev_total_players > 0)
+ Kill_Notification(NOTIF_ALL, world, MSG_CENTER_CPID, CPID_MISSING_TEAMS);
+ prev_total_players = -1;
+ return 1;
+ }
+ if(prev_total_players != total_players)
+ {
+ 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;
+ }
+ return 0;
+}
+
+MUTATOR_HOOKFUNCTION(ca_PlayerSpawn)
+{
+ self.caplayer = 1;
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(ca_PutClientInServer)
+{
+ if(!allowed_to_spawn)
+ {
+ self.classname = "observer";
+ if(!self.caplayer)
+ {
+ self.caplayer = 0.5;
+ if(clienttype(self) == CLIENTTYPE_REAL)
+ sprint(self, "You will join the game in the next round.\n");
+ }
+ }
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(ca_reset_map_players)
+{
+ FOR_EACH_CLIENT(self)
+ {
+ if(self.caplayer)
+ {
+ self.classname = "player";
+ self.caplayer = 1;
+ PutClientInServer();
+ }
+ }
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(ca_ClientConnect)
+{
+ self.classname = "observer";
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(ca_reset_map_global)
+{
+ allowed_to_spawn = TRUE;
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(ca_GetTeamCount)
+{
+ ca_teams = autocvar_g_ca_teams_override;
+ if(ca_teams < 2)
+ ca_teams = autocvar_g_ca_teams;
+ ca_teams = bound(2, ca_teams, 4);
+ ret_float = ca_teams;
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(ca_PlayerDies)
+{
+ if(!allowed_to_spawn)
+ self.respawn_flags = RESPAWN_SILENT;
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(ca_ForbidPlayerScore_Clear)
+{
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(ca_MakePlayerObserver)
+{
+ if(self.killindicator_teamchange == -2)
+ self.caplayer = 0;
+ if(self.caplayer)
+ self.frags = FRAGS_LMS_LOSER;
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(ca_ForbidThrowCurrentWeapon)
+{
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(ca_GiveFragsForKill)
+{
+ frag_score = 0; // score will be given to the winner team when the round ends
+ return 1;
+}
+
+void ca_Initialize()
+{
+ allowed_to_spawn = TRUE;
+
+ round_handler_Spawn(CA_CheckTeams, CA_CheckWinner, CA_RoundStart);
+ round_handler_Init(5, autocvar_g_ca_warmup, autocvar_g_ca_round_timelimit);
+
+ addstat(STAT_REDALIVE, AS_INT, redalive_stat);
+ addstat(STAT_BLUEALIVE, AS_INT, bluealive_stat);
+ addstat(STAT_YELLOWALIVE, AS_INT, yellowalive_stat);
+ addstat(STAT_PINKALIVE, AS_INT, pinkalive_stat);
+}
+
+MUTATOR_DEFINITION(gamemode_ca)
+{
+ MUTATOR_HOOK(PlayerSpawn, ca_PlayerSpawn, CBC_ORDER_ANY);
+ MUTATOR_HOOK(PutClientInServer, ca_PutClientInServer, CBC_ORDER_ANY);
+ MUTATOR_HOOK(MakePlayerObserver, ca_MakePlayerObserver, CBC_ORDER_ANY);
+ MUTATOR_HOOK(ClientConnect, ca_ClientConnect, CBC_ORDER_ANY);
+ MUTATOR_HOOK(reset_map_global, ca_reset_map_global, CBC_ORDER_ANY);
+ 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(ForbidPlayerScore_Clear, ca_ForbidPlayerScore_Clear, CBC_ORDER_ANY);
+ MUTATOR_HOOK(ForbidThrowCurrentWeapon, ca_ForbidThrowCurrentWeapon, CBC_ORDER_ANY);
+ MUTATOR_HOOK(GiveFragsForKill, ca_GiveFragsForKill, CBC_ORDER_FIRST);
+
+ MUTATOR_ONADD
+ {
+ if(time > 1) // game loads at time 1
+ error("This is a game type and it cannot be added at runtime.");
+ ca_Initialize();
+ }
+
+ MUTATOR_ONREMOVE
+ {
+ print("This is a game type and it cannot be removed at runtime.");
+ return -1;
+ }
+
+ return 0;
+}
--- /dev/null
+// should be removed in the future, as other code should not have to care
+.float caplayer; // 0.5 if scheduled to join the next round
+
-void freezetag_Initialize()
+.float freezetag_frozen_time;
+.float freezetag_frozen_timeout;
+.float freezetag_revive_progress;
+.entity freezetag_ice;
+#define ICE_MAX_ALPHA 1
+#define ICE_MIN_ALPHA 0.1
+float freezetag_teams;
+
+void freezetag_count_alive_players()
{
- precache_model("models/ice/ice.md3");
- warmup = time + autocvar_g_start_delay + autocvar_g_freezetag_warmup;
- ScoreRules_freezetag();
+ entity e;
+ total_players = redalive = bluealive = yellowalive = pinkalive = 0;
+ FOR_EACH_PLAYER(e) {
+ if(e.team == NUM_TEAM_1 && e.health >= 1)
+ {
+ ++total_players;
+ if (!e.freezetag_frozen) ++redalive;
+ }
+ else if(e.team == NUM_TEAM_2 && e.health >= 1)
+ {
+ ++total_players;
+ if (!e.freezetag_frozen) ++bluealive;
+ }
+ else if(e.team == NUM_TEAM_3 && e.health >= 1)
+ {
+ ++total_players;
+ if (!e.freezetag_frozen) ++yellowalive;
+ }
+ else if(e.team == NUM_TEAM_4 && e.health >= 1)
+ {
+ ++total_players;
+ if (!e.freezetag_frozen) ++pinkalive;
+ }
+ }
+ FOR_EACH_REALCLIENT(e) {
+ e.redalive_stat = redalive;
+ e.bluealive_stat = bluealive;
+ e.yellowalive_stat = yellowalive;
+ e.pinkalive_stat = pinkalive;
+ }
}
+#define FREEZETAG_ALIVE_TEAMS() ((redalive > 0) + (bluealive > 0) + (yellowalive > 0) + (pinkalive > 0))
+#define FREEZETAG_ALIVE_TEAMS_OK() (FREEZETAG_ALIVE_TEAMS() == freezetag_teams)
-void freezetag_CheckWinner()
+float prev_total_players;
+float freezetag_CheckTeams()
{
- if(time <= game_starttime) // game didn't even start yet! nobody can win in that case.
- return;
+ if(FREEZETAG_ALIVE_TEAMS_OK())
+ {
+ if(prev_total_players > 0)
+ Kill_Notification(NOTIF_ALL, world, MSG_CENTER_CPID, CPID_MISSING_TEAMS);
+ prev_total_players = -1;
+ return 1;
+ }
+ if(prev_total_players != total_players)
+ {
+ 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;
+ }
+ return 0;
+}
- if(next_round || (time > warmup - autocvar_g_freezetag_warmup && time < warmup))
- return; // already waiting for next round to start
+float freezetag_getWinnerTeam()
+{
+ float winner_team = 0;
+ if(redalive >= 1)
+ winner_team = NUM_TEAM_1;
+ if(bluealive >= 1)
+ {
+ if(winner_team) return 0;
+ winner_team = NUM_TEAM_2;
+ }
+ if(yellowalive >= 1)
+ {
+ if(winner_team) return 0;
+ winner_team = NUM_TEAM_3;
+ }
+ if(pinkalive >= 1)
+ {
+ if(winner_team) return 0;
+ winner_team = NUM_TEAM_4;
+ }
+ if(winner_team)
+ return winner_team;
+ return -1; // no player left
+}
- if((redalive >= 1 && bluealive >= 1)
- || (redalive >= 1 && yellowalive >= 1)
- || (redalive >= 1 && pinkalive >= 1)
- || (bluealive >= 1 && yellowalive >= 1)
- || (bluealive >= 1 && pinkalive >= 1)
- || (yellowalive >= 1 && pinkalive >= 1))
- return; // we still have active players on two or more teams, nobody won yet
+float freezetag_CheckWinner()
+{
+ entity e;
+ if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0)
+ {
+ Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_OVER);
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_OVER);
+ FOR_EACH_PLAYER(e)
+ e.freezetag_frozen_timeout = 0;
+ round_handler_Init(5, autocvar_g_freezetag_warmup, autocvar_g_freezetag_round_timelimit);
+ return 1;
+ }
- entity e, winner;
- winner = world;
+ if(FREEZETAG_ALIVE_TEAMS() > 1)
+ return 0;
- FOR_EACH_PLAYER(e)
+ float winner_team;
+ winner_team = freezetag_getWinnerTeam();
+ if(winner_team > 0)
{
- if(e.freezetag_frozen == 0 && e.health >= 1) // here's one player from the winning team... good
- {
- winner = e;
- break; // break, we found the winner
- }
+ Send_Notification(NOTIF_ALL, world, MSG_CENTER, APP_TEAM_NUM_4(winner_team, CENTER_ROUND_TEAM_WIN_));
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(winner_team, INFO_ROUND_TEAM_WIN_));
+ TeamScore_AddToTeam(winner_team, ST_SCORE, +1);
}
-
- if(winner != world) // just in case a winner wasn't found
+ else if(winner_team == -1)
{
- Send_Notification(NOTIF_ALL, world, MSG_CENTER, APP_TEAM_NUM_4(winner.team, CENTER_FREEZETAG_ROUND_WIN_));
- Send_Notification(NOTIF_ALL, world, MSG_INFO, APP_TEAM_NUM_4(winner.team, INFO_FREEZETAG_ROUND_WIN_));
- TeamScore_AddToTeam(winner.team, ST_SCORE, +1);
+ Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_TIED);
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_TIED);
}
- next_round = time + 5;
+ FOR_EACH_PLAYER(e)
+ e.freezetag_frozen_timeout = 0;
+ round_handler_Init(5, autocvar_g_freezetag_warmup, autocvar_g_freezetag_round_timelimit);
+ return 1;
}
// this is needed to allow the player to turn his view around (fixangle can't
self.nextthink = time;
}
+void freezetag_Add_Score(entity attacker)
+{
+ if(attacker == self)
+ {
+ // you froze your own dumb self
+ // counted as "suicide" already
+ PlayerScore_Add(self, SP_SCORE, -1);
+ }
+ else if(IS_PLAYER(attacker))
+ {
+ // got frozen by an enemy
+ // counted as "kill" and "death" already
+ PlayerScore_Add(self, SP_SCORE, -1);
+ PlayerScore_Add(attacker, SP_SCORE, +1);
+ }
+ // else nothing - got frozen by the game type rules themselves
+}
+
void freezetag_Freeze(entity attacker)
{
if(self.freezetag_frozen)
return;
self.freezetag_frozen = 1;
+ self.freezetag_frozen_time = time;
self.freezetag_revive_progress = 0;
self.health = 1;
+ if(autocvar_g_freezetag_frozen_maxtime > 0)
+ self.freezetag_frozen_timeout = time + autocvar_g_freezetag_frozen_maxtime;
+
+ freezetag_count_alive_players();
entity ice;
ice = spawn();
ice.think = freezetag_Ice_Think;
ice.nextthink = time;
ice.frame = floor(random() * 21); // ice model has 20 different looking frames
+ ice.alpha = ICE_MAX_ALPHA;
+ ice.colormod = Team_ColorRGB(self.team);
+ ice.glowmod = ice.colormod;
setmodel(ice, "models/ice/ice.md3");
- entity oldself;
- oldself = self;
- self = ice;
- freezetag_Ice_Think();
- self = oldself;
+ self.freezetag_ice = ice;
RemoveGrapplingHook(self);
// add waypoint
WaypointSprite_Spawn("freezetag_frozen", 0, 0, self, '0 0 64', world, self.team, self, waypointsprite_attached, TRUE, RADARICON_WAYPOINT, '0.25 0.90 1');
- if(attacker == self)
- {
- // you froze your own dumb self
- // counted as "suicide" already
- PlayerScore_Add(self, SP_SCORE, -1);
- }
- else if(attacker.classname == "player")
- {
- // got frozen by an enemy
- // counted as "kill" and "death" already
- PlayerScore_Add(self, SP_SCORE, -1);
- PlayerScore_Add(attacker, SP_SCORE, +1);
- }
- else
- {
- // nothing - got frozen by the game type rules themselves
- }
+ freezetag_Add_Score(attacker);
}
void freezetag_Unfreeze(entity attacker)
{
self.freezetag_frozen = 0;
+ self.freezetag_frozen_time = 0;
+ self.freezetag_frozen_timeout = 0;
self.freezetag_revive_progress = 0;
- self.health = autocvar_g_balance_health_start;
- // remove the ice block
- entity ice;
- for(ice = world; (ice = find(ice, classname, "freezetag_ice")); ) if(ice.owner == self)
- {
- remove(ice);
- break;
- }
+ remove(self.freezetag_ice);
+ self.freezetag_ice = world;
- // remove waypoint
if(self.waypointsprite_attached)
WaypointSprite_Kill(self.waypointsprite_attached);
}
MUTATOR_HOOKFUNCTION(freezetag_RemovePlayer)
{
- if(self.freezetag_frozen == 0 && self.health >= 1)
- {
- if(self.team == NUM_TEAM_1)
- --redalive;
- else if(self.team == NUM_TEAM_2)
- --bluealive;
- else if(self.team == NUM_TEAM_3)
- --yellowalive;
- else if(self.team == NUM_TEAM_4)
- --pinkalive;
- --totalalive;
- }
-
- if(total_players > 2) // only check for winners if we had more than two players (one of them left, don't let the other player win just because of that)
- freezetag_CheckWinner();
-
+ self.health = 0; // neccessary to update correctly alive stats
freezetag_Unfreeze(world);
-
+ freezetag_count_alive_players();
return 1;
}
MUTATOR_HOOKFUNCTION(freezetag_PlayerDies)
{
- if(self.freezetag_frozen == 0)
+ if(round_handler_IsActive())
+ if(round_handler_CountdownRunning())
{
- if(self.team == NUM_TEAM_1)
- --redalive;
- else if(self.team == NUM_TEAM_2)
- --bluealive;
- else if(self.team == NUM_TEAM_3)
- --yellowalive;
- else if(self.team == NUM_TEAM_4)
- --pinkalive;
- --totalalive;
+ if(self.freezetag_frozen)
+ freezetag_Unfreeze(world);
+ freezetag_count_alive_players();
+ return 1; // let the player die so that he can respawn whenever he wants
+ }
- freezetag_Freeze(frag_attacker);
+ // Cases DEATH_TEAMCHANGE and DEATH_AUTOTEAMCHANGE are needed to fix a bug whe
+ // you succeed changing team through the menu: you both really die (gibbing) and get frozen
+ if(ITEM_DAMAGE_NEEDKILL(frag_deathtype)
+ || frag_deathtype == DEATH_TEAMCHANGE || frag_deathtype == DEATH_AUTOTEAMCHANGE)
+ {
+ // let the player die, he will be automatically frozen when he respawns
+ if(!self.freezetag_frozen)
+ {
+ freezetag_Add_Score(frag_attacker);
+ freezetag_count_alive_players();
+ }
+ else
+ freezetag_Unfreeze(world); // remove ice
+ self.freezetag_frozen_timeout = -2; // freeze on respawn
+ return 1;
}
+ if(self.freezetag_frozen)
+ return 1;
+
+ freezetag_Freeze(frag_attacker);
+
if(frag_attacker == frag_target || frag_attacker == world)
{
if(frag_target.classname == STR_PLAYER)
frag_target.health = 1; // "respawn" the player :P
- freezetag_CheckWinner();
-
return 1;
}
MUTATOR_HOOKFUNCTION(freezetag_PlayerSpawn)
{
- freezetag_Unfreeze(world); // start by making sure that all ice blocks are removed
+ if(self.freezetag_frozen_timeout == -1) // if PlayerSpawn is called by reset_map_players
+ return 1; // do nothing, round is starting right now
- if(total_players == 1 && time > game_starttime) // only one player active on server, start a new match immediately
- if(!next_round && warmup && (time < warmup - autocvar_g_freezetag_warmup || time > warmup)) // not awaiting next round
+ if(self.freezetag_frozen_timeout == -2) // player was dead
{
- next_round = time;
+ freezetag_Freeze(world);
return 1;
}
- if(warmup && time > warmup) // spawn too late, freeze player
+
+ freezetag_count_alive_players();
+
+ if(round_handler_IsActive())
+ if(round_handler_IsRoundStarted())
{
Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_FREEZETAG_SPAWN_LATE);
freezetag_Freeze(world);
return 1;
}
+MUTATOR_HOOKFUNCTION(freezetag_reset_map_players)
+{
+ FOR_EACH_PLAYER(self)
+ {
+ if (self.freezetag_frozen)
+ freezetag_Unfreeze(world);
+ self.freezetag_frozen_timeout = -1;
+ PutClientInServer();
+ self.freezetag_frozen_timeout = 0;
+ }
+ freezetag_count_alive_players();
+ return 1;
+}
+
MUTATOR_HOOKFUNCTION(freezetag_GiveFragsForKill)
{
frag_score = 0; // no frags counted in Freeze Tag
return 1;
}
+.float reviving; // temp var
MUTATOR_HOOKFUNCTION(freezetag_PlayerPreThink)
{
float n;
- vector revive_extra_size;
- revive_extra_size = '1 1 1' * autocvar_g_freezetag_revive_extra_size;
+ if(gameover)
+ return 1;
+
+ if(self.freezetag_frozen)
+ {
+ // keep health = 1
+ self.pauseregen_finished = time + autocvar_g_balance_pause_health_regen;
+ }
+
+ if(round_handler_IsActive())
+ if(!round_handler_IsRoundStarted())
+ return 1;
entity o;
o = world;
- n = 0;
- FOR_EACH_PLAYER(other) if(self != other)
+ if(self.freezetag_frozen_timeout > 0 && time < self.freezetag_frozen_timeout)
+ self.freezetag_ice.alpha = ICE_MIN_ALPHA + (ICE_MAX_ALPHA - ICE_MIN_ALPHA) * (self.freezetag_frozen_timeout - time) / (self.freezetag_frozen_timeout - self.freezetag_frozen_time);
+
+ if(self.freezetag_frozen_timeout > 0 && time >= self.freezetag_frozen_timeout)
+ n = -1;
+ else
{
- if(other.freezetag_frozen == 0)
+ vector revive_extra_size = '1 1 1' * autocvar_g_freezetag_revive_extra_size;
+ n = 0;
+ FOR_EACH_PLAYER(other) if(self != other)
{
- if(other.team == self.team)
+ if(other.freezetag_frozen == 0)
{
- if(boxesoverlap(self.absmin - revive_extra_size, self.absmax + revive_extra_size, other.absmin, other.absmax))
+ if(other.team == self.team)
{
- if(!o)
- o = other;
- ++n;
+ if(boxesoverlap(self.absmin - revive_extra_size, self.absmax + revive_extra_size, other.absmin, other.absmax))
+ {
+ if(!o)
+ o = other;
+ if(self.freezetag_frozen)
+ other.reviving = TRUE;
+ ++n;
+ }
}
}
}
if(n && self.freezetag_frozen) // OK, there is at least one teammate reviving us
{
- self.freezetag_revive_progress = bound(0, self.freezetag_revive_progress + frametime * autocvar_g_freezetag_revive_speed, 1);
+ self.freezetag_revive_progress = bound(0, self.freezetag_revive_progress + frametime * max(1/60, autocvar_g_freezetag_revive_speed), 1);
self.health = max(1, self.freezetag_revive_progress * autocvar_g_balance_health_start);
if(self.freezetag_revive_progress >= 1)
{
freezetag_Unfreeze(self);
+ freezetag_count_alive_players();
+
+ if(n == -1)
+ {
+ Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_FREEZETAG_AUTO_REVIVED, autocvar_g_freezetag_frozen_maxtime);
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_FREEZETAG_AUTO_REVIVED, self.netname, autocvar_g_freezetag_frozen_maxtime);
+ return 1;
+ }
// EVERY team mate nearby gets a point (even if multiple!)
- FOR_EACH_PLAYER(other) if(self != other)
+ FOR_EACH_PLAYER(other)
{
- if(other.freezetag_frozen == 0)
+ if(other.reviving)
{
- if(other.team == self.team)
- {
- if(boxesoverlap(self.absmin - revive_extra_size, self.absmax + revive_extra_size, other.absmin, other.absmax))
- {
- PlayerScore_Add(other, SP_FREEZETAG_REVIVALS, +1);
- PlayerScore_Add(other, SP_SCORE, +1);
- }
- }
+ PlayerScore_Add(other, SP_FREEZETAG_REVIVALS, +1);
+ PlayerScore_Add(other, SP_SCORE, +1);
}
}
Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_FREEZETAG_REVIVED, o.netname);
Send_Notification(NOTIF_ONE, o, MSG_CENTER, CENTER_FREEZETAG_REVIVE, self.netname);
- Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_FREEZETAG_REVIVE, self.netname, o.netname);
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_FREEZETAG_REVIVED, self.netname, o.netname);
}
- // now find EVERY teammate within reviving radius, set their revive_progress values correct
- FOR_EACH_PLAYER(other) if(self != other)
+ FOR_EACH_PLAYER(other)
{
- if(other.freezetag_frozen == 0)
+ if(other.reviving)
{
- if(other.team == self.team)
- {
- if(boxesoverlap(self.absmin - revive_extra_size, self.absmax + revive_extra_size, other.absmin, other.absmax))
- other.freezetag_revive_progress = self.freezetag_revive_progress;
- }
+ other.freezetag_revive_progress = self.freezetag_revive_progress;
+ other.reviving = FALSE;
}
}
}
MUTATOR_HOOKFUNCTION(freezetag_PlayerDamage_Calculate)
{
- if(g_freezetag)
- {
- if(frag_target.freezetag_frozen == 1 && frag_deathtype != DEATH_HURTTRIGGER)
- {
- frag_damage = 0;
- frag_force = frag_force * autocvar_g_freezetag_frozen_force;
- }
- }
- return 1;
+ if(frag_target.freezetag_frozen && frag_deathtype != DEATH_HURTTRIGGER)
+ {
+ frag_damage = 0;
+ frag_force = frag_force * autocvar_g_freezetag_frozen_force;
+ }
+ return 1;
}
MUTATOR_HOOKFUNCTION(freezetag_ForbidThrowCurrentWeapon)
return 0;
}
+MUTATOR_HOOKFUNCTION(freezetag_ItemTouch)
+{
+ if (other.freezetag_frozen)
+ return 1;
+ return 0;
+}
+
MUTATOR_HOOKFUNCTION(freezetag_BotRoles)
{
if not(self.deadflag)
else
self.havocbot_role = havocbot_role_ft_offense;
}
-
+
return TRUE;
}
+MUTATOR_HOOKFUNCTION(freezetag_SpectateCopy)
+{
+ self.freezetag_frozen = other.freezetag_frozen;
+ self.freezetag_revive_progress = other.freezetag_revive_progress;
+ return 0;
+}
+
+MUTATOR_HOOKFUNCTION(freezetag_GetTeamCount)
+{
+ freezetag_teams = autocvar_g_freezetag_teams_override;
+ if(freezetag_teams < 2)
+ freezetag_teams = autocvar_g_freezetag_teams;
+ freezetag_teams = bound(2, freezetag_teams, 4);
+ ret_float = freezetag_teams;
+ return 0;
+}
+
+void freezetag_Initialize()
+{
+ precache_model("models/ice/ice.md3");
+ ScoreRules_freezetag();
+
+ round_handler_Spawn(freezetag_CheckTeams, freezetag_CheckWinner, func_null);
+ round_handler_Init(5, autocvar_g_freezetag_warmup, autocvar_g_freezetag_round_timelimit);
+
+ addstat(STAT_REDALIVE, AS_INT, redalive_stat);
+ addstat(STAT_BLUEALIVE, AS_INT, bluealive_stat);
+ addstat(STAT_YELLOWALIVE, AS_INT, yellowalive_stat);
+ addstat(STAT_PINKALIVE, AS_INT, pinkalive_stat);
+
+ addstat(STAT_FROZEN, AS_INT, freezetag_frozen);
+ addstat(STAT_REVIVE_PROGRESS, AS_FLOAT, freezetag_revive_progress);
+}
+
MUTATOR_DEFINITION(gamemode_freezetag)
{
MUTATOR_HOOK(MakePlayerObserver, freezetag_RemovePlayer, CBC_ORDER_ANY);
MUTATOR_HOOK(ClientDisconnect, freezetag_RemovePlayer, CBC_ORDER_ANY);
MUTATOR_HOOK(PlayerDies, freezetag_PlayerDies, CBC_ORDER_ANY);
MUTATOR_HOOK(PlayerSpawn, freezetag_PlayerSpawn, CBC_ORDER_ANY);
+ MUTATOR_HOOK(reset_map_players, freezetag_reset_map_players, CBC_ORDER_ANY);
MUTATOR_HOOK(GiveFragsForKill, freezetag_GiveFragsForKill, CBC_ORDER_FIRST);
MUTATOR_HOOK(PlayerPreThink, freezetag_PlayerPreThink, CBC_ORDER_FIRST);
MUTATOR_HOOK(PlayerPhysics, freezetag_PlayerPhysics, CBC_ORDER_FIRST);
MUTATOR_HOOK(PlayerDamage_Calculate, freezetag_PlayerDamage_Calculate, CBC_ORDER_ANY);
- MUTATOR_HOOK(ForbidThrowCurrentWeapon, freezetag_ForbidThrowCurrentWeapon, CBC_ORDER_FIRST); //first, last or any? dunno.
+ MUTATOR_HOOK(ForbidThrowCurrentWeapon, freezetag_ForbidThrowCurrentWeapon, CBC_ORDER_ANY);
+ MUTATOR_HOOK(ItemTouch, freezetag_ItemTouch, CBC_ORDER_ANY);
MUTATOR_HOOK(HavocBot_ChooseRule, freezetag_BotRoles, CBC_ORDER_ANY);
+ MUTATOR_HOOK(SpectateCopy, freezetag_SpectateCopy, CBC_ORDER_ANY);
+ MUTATOR_HOOK(GetTeamCount, freezetag_GetTeamCount, CBC_ORDER_EXCLUSIVE);
MUTATOR_ONADD
{
kh_Key_Remove(key);
kh_no_radar_circles = FALSE;
- Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ARENA_ROUNDSTART, autocvar_g_balance_keyhunt_delay_round);
+ Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_KEYHUNT_ROUNDSTART, autocvar_g_balance_keyhunt_delay_round);
kh_Controller_SetThink(autocvar_g_balance_keyhunt_delay_round, kh_StartRound);
}
float p1 = kh_CheckPlayers(0), p2 = kh_CheckPlayers(1), p3 = kh_CheckPlayers(2), p4 = kh_CheckPlayers(3);
if not(p1 || p2 || p3 || p4)
{
- Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ARENA_ROUNDSTART, autocvar_g_balance_keyhunt_delay_round);
+ Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_KEYHUNT_ROUNDSTART, autocvar_g_balance_keyhunt_delay_round);
kh_Controller_SetThink(autocvar_g_balance_keyhunt_delay_round, kh_StartRound);
}
else
.entity kh_next;
float kh_Key_AllOwnedByWhichTeam();
-// used by arena.qc ready-restart:
typedef void(void) kh_Think_t;
void kh_StartRound();
void kh_Controller_SetThink(float t, kh_Think_t func);
MUTATOR_HOOKFUNCTION(superspec_ClientConnect)
{
+ if(clienttype(self) != CLIENTTYPE_REAL)
+ return FALSE;
+
string fn = "superspec-local.options";
float fh;
+MUTATOR_DECLARATION(gamemode_arena);
+MUTATOR_DECLARATION(gamemode_ca);
MUTATOR_DECLARATION(gamemode_keyhunt);
MUTATOR_DECLARATION(gamemode_freezetag);
MUTATOR_DECLARATION(gamemode_keepaway);
mutators/base.qh
mutators/mutators.qh
+mutators/gamemode_arena.qh
+mutators/gamemode_ca.qh
mutators/gamemode_ctf.qh
mutators/gamemode_domination.qh
mutators/gamemode_keyhunt.qh // TODO fix this
playerdemo.qh
+round_handler.qh
+
// singleplayer stuff
item_key.qh
secret.qh
g_tetris.qc
//runematch.qc
-arena.qc
g_violence.qc
g_damage.qc
cheats.qc
playerstats.qc
+round_handler.qc
+
../common/explosion_equation.qc
mutators/base.qc
+mutators/gamemode_arena.qc
+mutators/gamemode_ca.qc
mutators/gamemode_ctf.qc
mutators/gamemode_domination.qc
mutators/gamemode_freezetag.qc
--- /dev/null
+void round_handler_Think()
+{
+ float f;
+
+ if(time < game_starttime)
+ {
+ round_handler_Reset(game_starttime);
+ return;
+ }
+
+ if(gameover)
+ {
+ round_handler_Reset(0);
+ round_handler_Remove();
+ return;
+ }
+
+ if(self.wait)
+ {
+ self.wait = FALSE;
+ self.cnt = self.count + 1; // init countdown
+ round_starttime = time + self.count;
+ reset_map(TRUE);
+ }
+
+ if(self.cnt > 0) // countdown running
+ {
+ if(self.canRoundStart())
+ {
+ if(self.cnt == self.count + 1)
+ round_starttime = time + self.count;
+ f = self.cnt - 1;
+ if(f == 0)
+ {
+ self.cnt = 0;
+ self.round_endtime = (self.round_timelimit) ? time + self.round_timelimit : 0;
+ self.nextthink = time;
+ if(self.roundStart)
+ self.roundStart();
+ return;
+ }
+ self.cnt = self.cnt - 1;
+ }
+ else
+ {
+ round_handler_Reset(0);
+ }
+ self.nextthink = time + 1; // canRoundStart every second
+ }
+ else
+ {
+ if(self.canRoundEnd())
+ {
+ // schedule a new round
+ self.wait = TRUE;
+ self.nextthink = time + self.delay;
+ }
+ else
+ {
+ self.nextthink = time; // canRoundEnd every frame
+ }
+ }
+}
+
+void round_handler_Init(float the_delay, float the_count, float the_round_timelimit)
+{
+ round_handler.delay = (the_delay > 0) ? the_delay : 0;
+ round_handler.count = fabs(floor(the_count));
+ round_handler.cnt = round_handler.count + 1;
+ round_handler.round_timelimit = (the_round_timelimit > 0) ? the_round_timelimit : 0;
+}
+
+// NOTE: this is only needed because if round_handler spawns at time 1
+// gamestarttime isn't initialized yet
+void round_handler_FirstThink()
+{
+ round_starttime = max(time, game_starttime) + round_handler.count;
+ round_handler.think = round_handler_Think;
+ round_handler.nextthink = max(time, game_starttime);
+}
+
+void round_handler_Spawn(float() canRoundStart_func, float() canRoundEnd_func, void() roundStart_func)
+{
+ if(round_handler)
+ {
+ backtrace("Can't spawn round_handler again!");
+ return;
+ }
+ round_handler = spawn();
+ round_handler.classname = "round_handler";
+
+ round_handler.think = round_handler_FirstThink;
+ round_handler.canRoundStart = canRoundStart_func;
+ round_handler.canRoundEnd = canRoundEnd_func;
+ round_handler.roundStart = roundStart_func;
+ round_handler.wait = FALSE;
+ round_handler_Init(5, 5, 180);
+ round_handler.nextthink = time;
+}
+
+void round_handler_Reset(float next_think)
+{
+ round_handler.wait = FALSE;
+ if(round_handler.count)
+ if(round_handler.cnt < round_handler.count + 1)
+ round_handler.cnt = round_handler.count + 1;
+ round_handler.nextthink = next_think;
+ round_starttime = (next_think) ? (next_think + round_handler.count) : -1;
+}
+
+void round_handler_Remove()
+{
+ remove(round_handler);
+ round_handler = world;
+}
+
--- /dev/null
+entity round_handler;
+.float delay; // stores delay from round end to countdown start
+.float count; // stores initial number of the countdown
+.float wait; // it's set to TRUE when round ends, to FALSE when countdown starts
+.float cnt; // its initial value is .count + 1, then decreased while counting down
+ // reaches 0 when the round starts
+.float round_timelimit;
+.float round_endtime;
+.float() canRoundStart;
+.float() canRoundEnd;
+.void() roundStart;
+
+void round_handler_Init(float the_delay, float the_count, float the_round_timelimit);
+void round_handler_Spawn(float() canRoundStart_func, float() canRoundEnd_func, void() roundStart_func);
+void round_handler_Reset(float next_think);
+void round_handler_Remove();
+
+#define round_handler_IsActive() (round_handler != world)
+#define round_handler_AwaitingNextRound() (round_handler.wait)
+#define round_handler_CountdownRunning() (!round_handler.wait && round_handler.cnt)
+#define round_handler_IsRoundStarted() (!round_handler.wait && !round_handler.cnt)
+#define round_handler_GetEndTime() (round_handler.round_endtime)
+
if(teamscores_entities_count)
return 0;
+ if(MUTATOR_CALLHOOK(ForbidPlayerScore_Clear)) return 0;
if(g_lms) return 0;
- if(g_arena || g_ca) return 0;
if(g_cts) return 0; // in CTS, you don't lose score by observing
if(g_race && g_race_qualifying) return 0; // in qualifying, you don't lose score by observing
s = strcat(s, ":human");
else
s = strcat(s, ":bot");
- if(p.classname != "player" && !g_arena && !g_ca && !g_lms)
+ if(p.classname != "player" && !g_arena && p.caplayer != 1 && !g_lms)
s = strcat(s, ":spectator");
}
else
{
- if(p.classname == "player" || g_arena || g_ca || g_lms)
+ if(p.classname == "player" || g_arena || p.caplayer == 1 || g_lms)
s = GetPlayerScoreString(p, 2);
else
s = "-666";
skill = autocvar_skill;
- count_players();
- if(g_ca || g_freezetag)
- count_alive_players();
- Arena_Warmup();
- Spawnqueue_Check();
-
// detect when the pre-game countdown (if any) has ended and the game has started
game_delay = (time < game_starttime) ? TRUE : FALSE;
if(game_delay_last == TRUE)
if(game_delay == FALSE)
if(autocvar_sv_eventlog)
- GameLogEcho(":startdelay_ended");
+ GameLogEcho(":startdelay_ended");
game_delay_last = game_delay;
{
fraglimit_override = autocvar_g_arena_point_limit;
leadlimit_override = autocvar_g_arena_point_leadlimit;
- maxspawned = autocvar_g_arena_maxspawned;
- if(maxspawned < 2)
- maxspawned = 2;
- arena_roundbased = autocvar_g_arena_roundbased;
+ MUTATOR_ADD(gamemode_arena);
}
if(g_ca)
ActivateTeamplay();
fraglimit_override = autocvar_g_ca_point_limit;
leadlimit_override = autocvar_g_ca_point_leadlimit;
- precache_sound("ctf/red_capture.wav");
- precache_sound("ctf/blue_capture.wav");
+ MUTATOR_ADD(gamemode_ca);
}
+
if(g_keyhunt)
{
ActivateTeamplay();
minstagib_stop_countdown(self);
else if (self.ammo_cells > 0 || (self.items & IT_UNLIMITED_WEAPON_AMMO))
{
+ if (self.minstagib_needammo)
+ self.health = 100;
minstagib_stop_countdown(self);
- self.health = 100;
}
else
{