// ring around crosshair, used for various purposes (such as indicating bullets left in clip, nex charge)
seta crosshair_ring_size 2 "bullet counter ring size for Rifle, velocity ring for Nex"
+seta crosshair_ring_alpha 0.2 "ring alpha"
seta crosshair_ring_campingrifle_alpha 0.15
seta g_ctf_win_mode 0 "0: captures only, 1: captures, then points, 2: points only"
seta g_ctf_ignore_frags 0 "1: regular frags give no points"
+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"
+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_time 2.5 "Time it takes to revive a frozen teammate"
+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"
+
// 50% of the spawns shall be far away from any players
set g_spawn_furthest 0.5
// respawn delay
set g_cts_respawn_delay 0.25
set g_cts_selfdamage 1 "0 = disable all selfdamage and falldamage in cts"
set g_cts_finish_kill_delay 10 "prevent cheating by running back to the start line, and starting out with more speed than otherwise possible"
+set g_freezetag_respawn_waves 0
+set g_freezetag_respawn_delay 0.25
// overtime
seta timelimit_overtime 2 "duration in minutes of one added overtime, added to the timelimit"
seta hud_showbinds 1 "the way to show the keys to press in HUD messages: 0 displays commands, 1 bound keys, 2 both"
seta hud_showbinds_limit 2 "maximum number of bound keys to show for a command. 0 for unlimited"
+seta hud_colorflash_alpha 0.5 "starting alpha of the color flash"
+
seta hud_damage 1 "an improved version of gl_polyblend, draw an image instead when hurt"
seta hud_damage_gentle_alpha_multiplier 0.25 "how much to multiply alpha of flash when using the cl_gentle version, it's much more opaque than the non-gentle version"
seta hud_damage_gentle_color "1 0.7 1" "color of flash for cl_gentle version"
CSQC_RAPTOR_HUD();
else
{
+ if(gametype == GAME_FREEZETAG)
+ {
+ if(getstati(STAT_FROZEN))
+ drawfill('0 0 0', eX * vid_conwidth + eY * vid_conheight, '0.25 0.90 1', cvar_or("hud_colorflash_alpha", 0.5), DRAWFLAG_ADDITIVE);
+ 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', cvar("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);
+ }
+ }
+
if(cvar("r_letterbox") == 0)
if(cvar("viewsize") < 120)
CSQC_common_hud();
if(!autocvar_hud_panel_modicons && !autocvar__hud_configure)
return;
- if (gametype != GAME_KEYHUNT && gametype != GAME_CTF && gametype != GAME_NEXBALL && gametype != GAME_CTS && gametype != GAME_RACE && gametype != GAME_CA && !autocvar__hud_configure)
+ if (gametype != GAME_KEYHUNT && gametype != GAME_CTF && gametype != GAME_NEXBALL && gametype != GAME_CTS && gametype != GAME_RACE && gametype != GAME_CA && gametype != GAME_FREEZETAG && !autocvar__hud_configure)
return;
active_panel = HUD_PANEL_MODICONS;
HUD_Mod_NexBall(pos, mySize);
else if(gametype == GAME_CTS || gametype == GAME_RACE)
HUD_Mod_Race(pos, mySize);
- else if(gametype == GAME_CA)
+ else if(gametype == GAME_CA || gametype == GAME_FREEZETAG)
HUD_Mod_CA(pos, mySize);
}
const float GAME_NEXBALL = 12;
const float GAME_CTS = 13;
const float GAME_CA = 14;
+const float GAME_FREEZETAG = 15;
const float AS_STRING = 1;
const float AS_INT = 2;
// mod stats (1xx)
const float STAT_REDALIVE = 100;
const float STAT_BLUEALIVE = 101;
+const float STAT_YELLOWALIVE = 102;
+const float STAT_PINKALIVE = 103;
+
+// freeze tag
+const float STAT_FROZEN = 104;
+const float STAT_REVIVE_PROGRESS = 105;
//const float STAT_SPIDERBOT_AIM 53 // compressShotOrigin
//const float STAT_SPIDERBOT_TARGET 54 // compressShotOrigin
if(spawnpoints >= 8 && diameter > 4096) {
MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_TEAM_DEATHMATCH;
+ MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_FREEZETAG;
MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_CA;
}
if( diameter < 4096)
case MAPINFO_TYPE_ONSLAUGHT: return "20 0";
case MAPINFO_TYPE_NEXBALL: return "5 20 0";
case MAPINFO_TYPE_CTS: return "20 0 0";
+ case MAPINFO_TYPE_FREEZETAG: return "10 20 0";
default: return "";
}
}
case MAPINFO_TYPE_ONSLAUGHT: return "timelimit=20";
case MAPINFO_TYPE_NEXBALL: return "timelimit=20 pointlimit=5 leadlimit=0";
case MAPINFO_TYPE_CTS: return "timelimit=20 skill=-1";
+ case MAPINFO_TYPE_FREEZETAG: return "timelimit=20 pointlimit=10 teams=2 leadlimit=0";
default: return "";
}
}
else if(t == "rc") return MAPINFO_TYPE_RACE;
else if(t == "nexball") return MAPINFO_TYPE_NEXBALL;
else if(t == "cts") return MAPINFO_TYPE_CTS;
+ else if(t == "freezetag") return MAPINFO_TYPE_FREEZETAG;
else if(t == "all") return MAPINFO_TYPE_ALL;
else return 0;
}
else if(t == MAPINFO_TYPE_RACE) return "rc";
else if(t == MAPINFO_TYPE_NEXBALL) return "nexball";
else if(t == MAPINFO_TYPE_CTS) return "cts";
+ else if(t == MAPINFO_TYPE_FREEZETAG) return "freezetag";
else if(t == MAPINFO_TYPE_ALL) return "all";
else return "";
}
return MAPINFO_TYPE_NEXBALL;
else if(cvar("g_cts"))
return MAPINFO_TYPE_CTS;
+ else if(cvar("g_freezetag"))
+ return MAPINFO_TYPE_FREEZETAG;
else
return MAPINFO_TYPE_DEATHMATCH;
}
case MAPINFO_TYPE_ONSLAUGHT: return "g_onslaught";
case MAPINFO_TYPE_RACE: return "g_race";
case MAPINFO_TYPE_NEXBALL: return "g_nexball";
+ case MAPINFO_TYPE_FREEZETAG: return "g_freezetag";
case MAPINFO_TYPE_CTS: return "g_cts";
default: return "";
}
cvar_set("g_race", (t == MAPINFO_TYPE_RACE) ? "1" : "0");
cvar_set("g_nexball", (t == MAPINFO_TYPE_NEXBALL) ? "1" : "0");
cvar_set("g_cts", (t == MAPINFO_TYPE_CTS) ? "1" : "0");
+ cvar_set("g_freezetag", (t == MAPINFO_TYPE_FREEZETAG) ? "1" : "0");
}
void MapInfo_LoadMap(string s)
float MAPINFO_TYPE_ASSAULT = 2048;
float MAPINFO_TYPE_ONSLAUGHT = 4096;
float MAPINFO_TYPE_NEXBALL = 8192;
-float MAPINFO_TYPE_ALL = 16383; // this has to include all above bits
+float MAPINFO_TYPE_FREEZETAG = 16384;
+float MAPINFO_TYPE_ALL = 32767; // this has to include all above bits
float MAPINFO_FEATURE_WEAPONS = 1; // not defined for minstagib-only maps
else if (g == GAME_RACE) return "rc";
else if (g == GAME_NEXBALL) return "nexball";
else if (g == GAME_CTS) return "cts";
+ else if (g == GAME_FREEZETAG) return "freezetag";
return "dm";
}
me.TD(me, 1, me.columns / n, e = makeXonoticGametypeButton(1, "g_cts", "Race CTS"));
if(e.checked) e0 = NULL;
me.TR(me);
- n = 8;
+ n = 9;
me.TD(me, 1, me.columns / n, e = makeXonoticGametypeButton(1, "g_tdm", "TDM"));
if(e.checked) e0 = NULL;
me.TD(me, 1, me.columns / n, e = makeXonoticGametypeButton(1, "g_ctf", "CTF"));
if(e.checked) e0 = NULL;
me.TD(me, 1, me.columns / n, e = makeXonoticGametypeButton(1, "g_ca", "CA"));
if(e.checked) e0 = NULL;
+ me.TD(me, 1, me.columns / n, e = makeXonoticGametypeButton(1, "g_freezetag", "Freeze Tag"));
+ if(e.checked) e0 = NULL;
me.TD(me, 1, me.columns / n, e = makeXonoticGametypeButton(1, "g_domination", "Domination"));
if(e.checked) e0 = NULL;
me.TD(me, 1, me.columns / n, e = makeXonoticGametypeButton(1, "g_keyhunt", "Key Hunt"));
void assault_objective_reset();
void target_assault_roundend_reset();
+float next_round;
+float stopalivecheck;
+float redalive, bluealive, yellowalive, pinkalive;
+float totalalive;
+.float redalive_stat, bluealive_stat, yellowalive_stat, pinkalive_stat;
+float redspawned, bluespawned, yellowspawned, pinkspawned;
+float totalspawned;
+
/**
* Resets the state of all clients, items, flags, runes, keys, weapons, waypoints, ... of the map.
* Sets the 'warmup' global variable.
warmup = time + cvar("g_ca_warmup");
allowed_to_spawn = 1;
}
+ else if(g_freezetag)
+ {
+ warmup = time + cvar("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(clienttype(self) == CLIENTTYPE_NOTACLIENT && self.items != IT_STRENGTH && self.items != IT_INVINCIBLE) // don't respawn strength or shield, that will only lead to them spawning very early each match
{
if(self.reset)
{
self.classname = "player";
PutClientInServer();
}
+ else if(g_freezetag)
+ {
+ if(self.classname == "player")
+ PutClientInServer();
+ }
else
{
/*
float f;
string msg;
- if((!g_arena && !g_ca) || (g_arena && !arena_roundbased) || (time < game_starttime))
+ if((!g_arena && !g_ca && !g_freezetag) || (g_arena && !arena_roundbased) || (time < game_starttime))
return;
f = ceil(warmup - time);
}
}
- if(self.classname == "player" && self.health > 0)
+ if(self.classname == "player" && self.health > 0 && self.movetype == MOVETYPE_NONE)
self.movetype = MOVETYPE_WALK;
}
-float next_round;
-float stopalivecheck;
-float redalive, bluealive;
-.float redalive_stat, bluealive_stat;
+void count_spawned_players()
+{
+ // TODO fix "*spawned" name, it should rather be "*players" or so
+ // not doing this now to prevent merge hell with Tag
+ // fix after merging with Tag
+
+ // count amount of players in each team
+ totalspawned = redspawned = bluespawned = yellowspawned = pinkspawned = 0;
+ FOR_EACH_PLAYER(self) {
+ if (self.team == COLOR_TEAM1)
+ {
+ redspawned += 1;
+ totalspawned += 1;
+ }
+ else if (self.team == COLOR_TEAM2)
+ {
+ bluespawned += 1;
+ totalspawned += 1;
+ }
+ else if (self.team == COLOR_TEAM3)
+ {
+ yellowspawned += 1;
+ totalspawned += 1;
+ }
+ else if (self.team == COLOR_TEAM4)
+ {
+ pinkspawned += 1;
+ totalspawned += 1;
+ }
+ }
+}
+
+void count_alive_players()
+{
+ totalalive = redalive = bluealive = yellowalive = pinkalive = 0;
+ if(g_ca)
+ {
+ FOR_EACH_PLAYER(self) {
+ if (self.team == COLOR_TEAM1 && self.health >= 1)
+ {
+ redalive += 1;
+ totalalive += 1;
+ }
+ else if (self.team == COLOR_TEAM2 && self.health >= 1)
+ {
+ bluealive += 1;
+ totalalive += 1;
+ }
+ }
+ FOR_EACH_PLAYER(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 == COLOR_TEAM1 && self.freezetag_frozen == 0 && self.health >= 1)
+ {
+ redalive += 1;
+ totalalive += 1;
+ }
+ else if (self.team == COLOR_TEAM2 && self.freezetag_frozen == 0 && self.health >= 1)
+ {
+ bluealive += 1;
+ totalalive += 1;
+ }
+ else if (self.team == COLOR_TEAM3 && self.freezetag_frozen == 0 && self.health >= 1)
+ {
+ yellowalive += 1;
+ totalalive += 1;
+ }
+ else if (self.team == COLOR_TEAM4 && self.freezetag_frozen == 0 && self.health >= 1)
+ {
+ pinkalive += 1;
+ totalalive += 1;
+ }
+ }
+ FOR_EACH_PLAYER(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
*/
void Spawnqueue_Check()
{
- if(g_ca) // we want to perform this before the return block below...
+ count_spawned_players();
+ if(g_ca || g_freezetag) // we want to perform this before the return block below (CA)...
{
- // this is STUPID to perform again, but has to be done so that we can give instant feedback when a round ends
- // and so the code won't start searching for a champion using find() before all players are actually REMOVED
- redalive = 0; bluealive = 0;
- FOR_EACH_PLAYER(self) {
- if (self.team == COLOR_TEAM1 && self.health >= 1) redalive += 1;
- else if (self.team == COLOR_TEAM2 && self.health >= 1) bluealive += 1;
- }
- // as if the above stuff wasn't stupid enough, let's run it a third time! :D
- // (so that we can send redalive/bluealive as a stat)
- FOR_EACH_PLAYER(self) {
- self.redalive_stat = redalive;
- self.bluealive_stat = bluealive;
- }
+ count_alive_players();
}
- if(time < warmup + 1 || inWarmupStage)
+ if(time < warmup + 1 || inWarmupStage || intermission_running)
return;
if(g_ca) {
- // check the amount of spawned players in each team
- float redspawned, bluespawned;
- FOR_EACH_PLAYER(self) {
- if (self.team == COLOR_TEAM1) redspawned += 1;
- else if (self.team == COLOR_TEAM2) bluespawned += 1;
- }
-
required_ca_players = max(2, fabs(cvar("bot_vs_human") + 1));
if(ca_players < required_ca_players && (redspawned && bluespawned)) {
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)
return FALSE;
}
+ if(g_freezetag)
+ if(e.freezetag_frozen)
+ return FALSE;
+
if(teams_matter)
{
if(e.team==0)
*/
void PlayerJump (void)
{
+ if(g_freezetag && self.freezetag_frozen)
+ return; // no jumping in freezetag when frozen
+
float mjumpheight;
float doublejump;
}
void ClientKill_Now_TeamChange();
+void freezetag_CheckWinner();
void PlayerDamage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
{
}
}
- // become fully visible
- self.alpha = 1;
- // clear selected player display
- ClearSelectedPlayer();
- // throw a weapon
- SpawnThrownWeapon (self.origin + (self.mins + self.maxs) * 0.5, self.switchweapon);
+ if(!g_freezetag)
+ {
+ // become fully visible
+ self.alpha = 1;
+ // clear selected player display
+ ClearSelectedPlayer();
+ // 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();
DropAllRunes(self);
+ if(deathtype == DEATH_HURTTRIGGER)
+ {
+ PutClientInServer();
+ count_alive_players(); // re-count players
+ freezetag_CheckWinner();
+ return;
+ }
+
frag_attacker = attacker;
frag_inflictor = inflictor;
frag_target = self;
MUTATOR_CALLHOOK(PlayerDies);
+ if(g_freezetag)
+ return;
+
if(self.flagcarried)
{
if(attacker.classname != "player" && attacker.classname != "gib")
if (frametime)
self.weapon_frametime = frametime;
- if(((arena_roundbased || g_ca) && time < warmup) || ((time < game_starttime) && !cvar("sv_ready_restart_after_countdown")))
+ if(((arena_roundbased || g_ca || g_freezetag) && time < warmup) || ((time < game_starttime) && !cvar("sv_ready_restart_after_countdown")))
+ return;
+
+ if(g_freezetag && self.freezetag_frozen == 1)
return;
if (!self.weaponentity || self.health < 1)
float ctf_score_value(string parameter);
-float g_dm, g_domination, g_ctf, g_tdm, g_keyhunt, g_onslaught, g_assault, g_arena, g_ca, g_lms, g_runematch, g_race, g_nexball, g_cts;
+float g_dm, g_domination, g_ctf, g_tdm, g_keyhunt, g_onslaught, g_assault, g_arena, g_ca, g_lms, g_runematch, g_race, g_nexball, g_cts, g_freezetag;
float g_cloaked, g_footsteps, g_jump_grunt, g_grappling_hook, g_midair, g_minstagib, g_pinata, g_norecoil, g_minstagib_invis_alpha, g_bloodloss;
float g_warmup_limit;
float g_warmup_allguns;
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 freezetag_frozen;
+.float freezetag_beginrevive_time;
+.float freezetag_revive_progress;
if (attacker.isbot)
damage = damage * bound(0.1, (skill + 5) * 0.1, 1);
+ if(g_freezetag)
+ {
+ if(targ.freezetag_frozen == 1)
+ {
+ damage = 0;
+ force = force * cvar("g_freezetag_frozen_force");
+ }
+ }
+
// nullify damage if teamplay is on
if(deathtype != DEATH_TELEFRAG)
if(attacker.classname == "player")
addstat(STAT_NEX_CHARGE, AS_FLOAT, nex_charge);
addstat(STAT_NEX_CHARGEPOOL, AS_FLOAT, nex_charge_pool_ammo);
- if(g_ca)
+ 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);
--- /dev/null
+void freezetag_Initialize()
+{
+ precache_model("models/ice/ice.md3");
+ warmup = time + cvar("g_start_delay") + cvar("g_freezetag_warmup");
+}
+
+void freezetag_CheckWinner()
+{
+ if(time <= game_starttime) // game didn't even start yet! nobody can win in that case.
+ return;
+
+ if(next_round || (time > warmup - cvar("g_freezetag_warmup") && time < warmup))
+ return; // already waiting for next round to start
+
+ if((redalive >= 1 && bluealive >= 1) // counted in arena.qc
+ || (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
+
+ entity e, winner;
+ string teamname;
+
+ FOR_EACH_PLAYER(e)
+ {
+ if(e.freezetag_frozen == 0 && e.classname == "player" && e.health >= 1) // here's one player from the winning team... good
+ {
+ winner = e;
+ break; // break, we found the winner
+ }
+ }
+
+ if(winner != world) // just in case a winner wasn't found
+ {
+ TeamScore_AddToTeam(winner.team, ST_SCORE, +1);
+ if(winner.team == COLOR_TEAM1)
+ teamname = "^1Red Team";
+ else if(winner.team == COLOR_TEAM2)
+ teamname = "^4Blue Team";
+ else if(winner.team == COLOR_TEAM3)
+ teamname = "^3Yellow Team";
+ else
+ teamname = "^6Pink Team";
+ FOR_EACH_PLAYER(e) {
+ centerprint(e, strcat(teamname, "^5 wins the round, all other teams were frozen.\n"));
+ }
+ bprint(teamname, "^5 wins the round since all the other teams were frozen.\n");
+ }
+
+ next_round = time + 5;
+}
+
+void freezetag_Ice_Think()
+{
+ setorigin(self, self.owner.origin - '0 0 16');
+ self.nextthink = time;
+}
+
+void freezetag_Freeze()
+{
+ self.freezetag_frozen = 1;
+
+ entity ice;
+ ice = spawn();
+ ice.owner = self;
+ ice.classname = "freezetag_ice";
+ ice.think = freezetag_Ice_Think;
+ ice.nextthink = time;
+ ice.frame = floor(random() * 21); // ice model has 20 different looking frames
+ setmodel(ice, "models/ice/ice.md3");
+
+ self.movement = '0 0 0';
+
+ // add waypoint
+ WaypointSprite_Spawn("freezetag_frozen", 0, 0, self, '0 0 64', world, self.team, self, waypointsprite_attached, TRUE);
+ if(self.waypointsprite_attached)
+ {
+ WaypointSprite_UpdateTeamRadar(self.waypointsprite_attached, RADARICON_WAYPOINT, '0.25 0.90 1');
+ }
+}
+
+void freezetag_Unfreeze()
+{
+ self.freezetag_frozen = 0;
+
+ // remove the ice block
+ entity ice;
+ for(ice = world; (ice = find(ice, classname, "freezetag_ice")); ) if(ice.owner == self)
+ {
+ remove(ice);
+ break;
+ }
+
+ // remove waypoint
+ if(self.waypointsprite_attached)
+ WaypointSprite_Kill(self.waypointsprite_attached);
+}
+
+MUTATOR_HOOKFUNCTION(freezetag_RemovePlayer)
+{
+ if(self.freezetag_frozen == 0)
+ {
+ if(self.team == COLOR_TEAM1)
+ --redalive;
+ else if(self.team == COLOR_TEAM2)
+ --bluealive;
+ else if(self.team == COLOR_TEAM3)
+ --yellowalive;
+ else if(self.team == COLOR_TEAM4)
+ --pinkalive;
+ --totalalive;
+ }
+
+ if(totalspawned > 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();
+
+ freezetag_Unfreeze();
+
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(freezetag_PlayerDies)
+{
+ if(self.freezetag_frozen == 0)
+ {
+ if(self.team == COLOR_TEAM1)
+ --redalive;
+ else if(self.team == COLOR_TEAM2)
+ --bluealive;
+ else if(self.team == COLOR_TEAM3)
+ --yellowalive;
+ else if(self.team == COLOR_TEAM4)
+ --pinkalive;
+ --totalalive;
+ }
+
+ freezetag_Freeze();
+
+ centerprint(frag_attacker, strcat("^2You froze ^7", frag_target.netname, ".\n"));
+ if(frag_attacker == frag_target || frag_attacker == world)
+ {
+ centerprint(frag_target, "^1You froze yourself.\n");
+ bprint("^7", frag_target.netname, "^1 froze himself.\n");
+ }
+ else
+ {
+ centerprint(frag_target, strcat("^1You were frozen by ^7", frag_attacker.netname, ".\n"));
+ bprint("^7", frag_target.netname, "^1 was frozen by ^7", frag_attacker.netname, ".\n");
+ }
+
+ frag_target.health = cvar("g_balance_health_start"); // "respawn" the player :P
+
+ freezetag_CheckWinner();
+
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(freezetag_PlayerSpawn)
+{
+ if(totalspawned == 1 && time > game_starttime) // only one player active on server, start a new match immediately
+ if(!next_round && warmup && (time < warmup - cvar("g_freezetag_warmup") || time > warmup)) // not awaiting next round
+ {
+ next_round = time;
+ return 1;
+ }
+ if(warmup && time > warmup) // spawn too late, freeze player
+ {
+ centerprint(self, "^1You spawned after the round started, you'll spawn as frozen.\n");
+ freezetag_Freeze();
+ }
+ else // we are still in the delay period before the round starts
+ {
+ freezetag_Unfreeze();
+ }
+
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(freezetag_GiveFragsForKill)
+{
+ frag_score = 0; // no frags counted in Freeze Tag
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(freezetag_PlayerPreThink)
+{
+ vector revive_extra_size;
+ revive_extra_size = '1 1 1' * cvar("g_freezetag_revive_extra_size");
+
+ float teammate_nearby;
+ FOR_EACH_PLAYER(other) if(self != other)
+ {
+ if(other.freezetag_frozen == 0)
+ {
+ if(other.team == self.team)
+ {
+ teammate_nearby = boxesoverlap(self.absmin - revive_extra_size, self.absmax + revive_extra_size, other.absmin, other.absmax);
+ if(teammate_nearby)
+ break;
+ }
+ }
+ }
+
+ if(teammate_nearby && self.freezetag_frozen == 1)
+ {
+ if(self.freezetag_beginrevive_time == -9999)
+ {
+ self.freezetag_beginrevive_time = time;
+ self.freezetag_revive_progress = 0;
+ other.freezetag_revive_progress = 0;
+ }
+ else
+ {
+ self.freezetag_revive_progress = (time - self.freezetag_beginrevive_time) / cvar("g_freezetag_revive_time");
+ other.freezetag_revive_progress = (time - self.freezetag_beginrevive_time) / cvar("g_freezetag_revive_time");
+ if(time - self.freezetag_beginrevive_time >= cvar("g_freezetag_revive_time"))
+ {
+ freezetag_Unfreeze();
+
+ centerprint(self, strcat("^5You were revived by ^7", other.netname, ".\n"));
+ centerprint(other, strcat("^5You revived ^7", self.netname, ".\n"));
+ bprint("^7", other.netname, "^5 revived ^7", self.netname, ".\n");
+
+ self.freezetag_beginrevive_time = -9999;
+ self.freezetag_revive_progress = 0;
+ other.freezetag_revive_progress = 0;
+ }
+ }
+ }
+ else if(!teammate_nearby) // only if no teammate is nearby will we reset
+ {
+ self.freezetag_beginrevive_time = -9999;
+ self.freezetag_revive_progress = 0;
+ }
+
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(freezetag_PlayerPhysics)
+{
+ if(self.freezetag_frozen)
+ self.movement = '0 0 0';
+ return 1;
+}
+
+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(GiveFragsForKill, freezetag_GiveFragsForKill, CBC_ORDER_FIRST);
+ MUTATOR_HOOK(PlayerPreThink, freezetag_PlayerPreThink, CBC_ORDER_FIRST);
+ MUTATOR_HOOK(PlayerPhysics, freezetag_PlayerPhysics, 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.");
+ g_freezetag = 1;
+ freezetag_Initialize();
+ }
+
+ MUTATOR_ONREMOVE
+ {
+ g_freezetag = 0;
+ error("This is a game type and it cannot be removed at runtime.");
+ }
+
+ return 0;
+}
MUTATOR_DECLARATION(gamemode_keyhunt);
+MUTATOR_DECLARATION(gamemode_freezetag);
MUTATOR_DECLARATION(mutator_nix);
MUTATOR_DECLARATION(mutator_dodging);
mutators/base.qc
mutators/gamemode_keyhunt.qc
+mutators/gamemode_freezetag.qc
mutators/mutator_nix.qc
mutators/mutator_dodging.qc
mutators/mutator_rocketflying.qc
cvar_set("g_race", ftos(g_race));
cvar_set("g_nexball", ftos(g_nexball));
cvar_set("g_cts", ftos(g_cts));
+ cvar_set("g_freezetag", ftos(g_freezetag));
}
void ReadGameCvars()
found += (g_race = (!found && (prev != GAME_RACE) && cvar("g_race")));
found += (g_nexball = (!found && (prev != GAME_NEXBALL) && cvar("g_nexball")));
found += (g_cts = (!found && (prev != GAME_CTS) && cvar("g_cts")));
+ found += (g_freezetag = (!found && (prev != GAME_FREEZETAG) && cvar("g_freezetag")));
if(found)
break;
MUTATOR_ADD(gamemode_keyhunt);
}
+ if(g_freezetag)
+ {
+ game = GAME_FREEZETAG;
+ gamemode_name = "Freeze Tag";
+ ActivateTeamplay();
+ fraglimit_override = cvar("g_freezetag_point_limit");
+ leadlimit_override = cvar("g_freezetag_point_leadlimit");
+ MUTATOR_ADD(gamemode_freezetag);
+ }
+
if(g_assault)
{
game = GAME_ASSAULT;
error("Too few teams available for ctf\n");
else if(g_keyhunt)
error("Too few teams available for key hunt\n");
+ else if(g_freezetag)
+ error("Too few teams available for freeze tag\n");
else
error("Too few teams available for team deathmatch\n");
}