set g_freezetag_revive_nade 1 "Enable reviving from own nade explosion"
set g_freezetag_revive_nade_health 40 "Amount of health player has if they revived from their own nade explosion"
set g_freezetag_round_timelimit 360 "round time limit in seconds"
+set g_freezetag_revive_auto 1 "automatically revive frozen players after some time (g_freezetag_frozen_maxtime)"
+set g_freezetag_revive_auto_progress 1 "start the automatic reviving progress as soon as the player gets frozen"
+set g_freezetag_revive_auto_reducible 1 "reduce auto-revival time when frozen players are hit by enemies; set to -1 to reduce it even when they are hit by teammates"
+set g_freezetag_revive_auto_reducible_forcefactor 0.025 "hit force to time reduction conversion factor"
set g_freezetag_frozen_maxtime 60 "frozen players will be automatically unfrozen after this time in seconds"
set g_freezetag_teams_override 0
set g_freezetag_team_spawns 0 "when 1, players spawn from the team spawnpoints of the map, if any"
else if(STAT(FROZEN))
{
vector col = '0.25 0.90 1';
- if(STAT(REVIVE_PROGRESS))
- col += vec3(STAT(REVIVE_PROGRESS), -STAT(REVIVE_PROGRESS), -STAT(REVIVE_PROGRESS));
- drawfill('0 0 0', vec2(vid_conwidth, vid_conheight), col, autocvar_hud_colorflash_alpha, DRAWFLAG_ADDITIVE);
+ float col_fade = max(0, STAT(REVIVE_PROGRESS) * 2 - 1);
+ float alpha_fade = 0.3 + 0.7 * (1 - max(0, STAT(REVIVE_PROGRESS) * 4 - 3));
+ if(col_fade)
+ col += vec3(col_fade, -col_fade, -col_fade);
+ drawfill('0 0 0', vec2(vid_conwidth, vid_conheight), col, autocvar_hud_colorflash_alpha * alpha_fade, DRAWFLAG_ADDITIVE);
}
HUD_Scale_Enable();
if(STAT(FROZEN, targ))
return;
- if(autocvar_g_freezetag_frozen_maxtime > 0)
+ targ.freezetag_frozen_time = time;
+ if (autocvar_g_freezetag_revive_auto && autocvar_g_freezetag_frozen_maxtime > 0)
targ.freezetag_frozen_timeout = time + autocvar_g_freezetag_frozen_maxtime;
Freeze(targ, 0, FROZEN_NORMAL, true);
targ.freezetag_frozen_timeout = 0;
}
+MUTATOR_HOOKFUNCTION(ft, Damage_Calculate)
+{
+ entity frag_attacker = M_ARGV(1, entity);
+ entity frag_target = M_ARGV(2, entity);
+ //float frag_deathtype = M_ARGV(3, float);
+ //float frag_damage = M_ARGV(4, float);
+ vector frag_force = M_ARGV(6, vector);
+
+ if (STAT(FROZEN, frag_target) == FROZEN_NORMAL && autocvar_g_freezetag_revive_auto_reducible
+ && autocvar_g_freezetag_frozen_maxtime > 0 && autocvar_g_freezetag_revive_auto)
+ {
+ float t = 0;
+ if ((autocvar_g_freezetag_revive_auto_reducible < 0 || DIFF_TEAM(frag_attacker, frag_target))
+ && frag_target.freezetag_frozen_timeout > time)
+ {
+ if (fabs(autocvar_g_freezetag_revive_auto_reducible) == 1)
+ t = vlen(frag_force) * autocvar_g_freezetag_revive_auto_reducible_forcefactor;
+ frag_target.freezetag_frozen_timeout -= t;
+ if (frag_target.freezetag_frozen_timeout < time)
+ frag_target.freezetag_frozen_timeout = time;
+ }
+ }
+}
+
#ifdef IS_REVIVING
#undef IS_REVIVING
#endif
if (!n && player.freezetag_frozen_timeout > 0 && time >= player.freezetag_frozen_timeout)
n = -1;
+ float base_progress = 0;
+ if (STAT(FROZEN, player) == FROZEN_NORMAL && autocvar_g_freezetag_revive_auto
+ && autocvar_g_freezetag_frozen_maxtime > 0 && autocvar_g_freezetag_revive_auto_progress)
+ {
+ // NOTE if auto-revival is in progress, manual revive speed is reduced so that it always takes the same amount of time
+ base_progress = bound(0, (1 - (player.freezetag_frozen_timeout - time) / autocvar_g_freezetag_frozen_maxtime), 1);
+ }
+
if (!n) // no teammate nearby
{
if (STAT(FROZEN, player) == FROZEN_NORMAL)
- {
- STAT(REVIVE_PROGRESS, player) = bound(0, STAT(REVIVE_PROGRESS, player) - frametime * autocvar_g_freezetag_revive_clearspeed, 1);
- SetResourceExplicit(player, RES_HEALTH, max(1, STAT(REVIVE_PROGRESS, player) * ((warmup_stage) ? warmup_start_health : start_health)));
- }
+ STAT(REVIVE_PROGRESS, player) = bound(base_progress, STAT(REVIVE_PROGRESS, player) - frametime * autocvar_g_freezetag_revive_clearspeed * (1 - base_progress), 1);
else if (!STAT(FROZEN, player))
- STAT(REVIVE_PROGRESS, player) = 0; // thawing nobody
+ STAT(REVIVE_PROGRESS, player) = base_progress; // thawing nobody
}
else if (STAT(FROZEN, player) == FROZEN_NORMAL) // OK, there is at least one teammate reviving us
{
- STAT(REVIVE_PROGRESS, player) = bound(0, STAT(REVIVE_PROGRESS, player) + frametime * max(1/60, autocvar_g_freezetag_revive_speed), 1);
- SetResourceExplicit(player, RES_HEALTH, max(1, STAT(REVIVE_PROGRESS, player) * ((warmup_stage) ? warmup_start_health : start_health)));
+ STAT(REVIVE_PROGRESS, player) = bound(base_progress, STAT(REVIVE_PROGRESS, player) + frametime * max(1/60, autocvar_g_freezetag_revive_speed * (1 - base_progress)), 1);
if(STAT(REVIVE_PROGRESS, player) >= 1)
{
+ float frozen_time = time - player.freezetag_frozen_time;
Unfreeze(player, false);
+ SetResourceExplicit(player, RES_HEALTH, ((warmup_stage) ? warmup_start_health : start_health));
freezetag_count_alive_players();
if(n == -1)
{
- Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_FREEZETAG_AUTO_REVIVED, autocvar_g_freezetag_frozen_maxtime);
- Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_FREEZETAG_AUTO_REVIVED, player.netname, autocvar_g_freezetag_frozen_maxtime);
+ Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_FREEZETAG_AUTO_REVIVED, frozen_time);
+ Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_FREEZETAG_AUTO_REVIVED, player.netname, frozen_time);
return true;
}
STAT(REVIVE_PROGRESS, it) = STAT(REVIVE_PROGRESS, player);
}
+ if (STAT(FROZEN, player) == FROZEN_NORMAL)
+ {
+ entity player_wp = player.waypointsprite_attached;
+ if (n > 0 || (n == 0 && STAT(REVIVE_PROGRESS, player) > 0.95))
+ {
+ WaypointSprite_UpdateSprites(player_wp, WP_Reviving, WP_Null, WP_Null);
+ WaypointSprite_UpdateTeamRadar(player_wp, RADARICON_WAYPOINT, WP_REVIVING_COLOR);
+ }
+ else
+ {
+ WaypointSprite_UpdateSprites(player_wp, WP_Frozen, WP_Null, WP_Null);
+ WaypointSprite_UpdateTeamRadar(player_wp, RADARICON_WAYPOINT, WP_FROZEN_COLOR);
+ }
+
+ WaypointSprite_UpdateMaxHealth(player_wp, 1);
+ WaypointSprite_UpdateHealth(player_wp, STAT(REVIVE_PROGRESS, player));
+ }
+
return true;
}
const float ICE_MIN_ALPHA = 0.1;
float freezetag_teams;
+bool autocvar_g_freezetag_revive_auto = 1;
+int autocvar_g_freezetag_revive_auto_progress = 1;
+int autocvar_g_freezetag_revive_auto_reducible;
+float autocvar_g_freezetag_revive_auto_reducible_forcefactor;
float autocvar_g_freezetag_revive_extra_size;
float autocvar_g_freezetag_revive_speed;
bool autocvar_g_freezetag_revive_nade;
REGISTER_WAYPOINT(Here, _("Here"), "", '0 1 0', 1);
REGISTER_WAYPOINT(Danger, _("DANGER"), "", '1 0.5 0', 1);
-REGISTER_WAYPOINT(Frozen, _("Frozen!"), "", '0.25 0.90 1', 1);
+const vector WP_FROZEN_COLOR = '0.25 0.9 1';
+const vector WP_REVIVING_COLOR = '1 0.5 0';
+REGISTER_WAYPOINT(Frozen, _("Frozen!"), "", WP_FROZEN_COLOR, 1);
+REGISTER_WAYPOINT(Reviving, _("Reviving"), "", WP_REVIVING_COLOR, 1);
REGISTER_WAYPOINT(Item, _("Item"), "", '1 0 1', 1);
{
// no check, as this is never called without doing an actual change (usually only once)
int i = icon.m_id;
- e.cnt = (e.cnt & BIT(7)) | (i & BITS(7));
- e.colormod = col;
- e.SendFlags |= 32;
+ int new_cnt = (e.cnt & BIT(7)) | (i & BITS(7));
+ if (new_cnt != e.cnt || col != e.colormod)
+ {
+ e.cnt = new_cnt;
+ e.colormod = col;
+ e.SendFlags |= 32;
+ }
}
void WaypointSprite_Ping(entity e)
damage = 0;
force = '0 0 0';
}
- else if(SAME_TEAM(attacker, targ))
+ else if(!STAT(FROZEN, targ) && SAME_TEAM(attacker, targ))
{
if(autocvar_teamplay_mode == 1)
damage = 0;
if(IS_PLAYER(victim) || (IS_TURRET(victim) && victim.active == ACTIVE_ACTIVE) || IS_MONSTER(victim) || MUTATOR_CALLHOOK(PlayHitsound, victim, attacker))
{
- if(DIFF_TEAM(victim, attacker) && !STAT(FROZEN, victim))
+ if (DIFF_TEAM(victim, attacker))
{
if(damage > 0)
{
}
}
}
- else if(IS_PLAYER(attacker))
+ else if (IS_PLAYER(attacker) && !STAT(FROZEN, victim)) // same team
{
- // if enemy gets frozen in this frame and receives other damage don't
- // play the typehitsound e.g. when hit by multiple bullets of the shotgun
- if (deathtype != DEATH_FIRE.m_id && (!STAT(FROZEN, victim) || time > victim.freeze_time))
+ if (deathtype != DEATH_FIRE.m_id)
{
attacker.typehitsound += 1;
}
}
}
- if (time < this.spawnshieldtime && autocvar_g_spawnshield_blockdamage < 1)
+ if (STAT(FROZEN, this))
+ {
+ if (!ITEM_DAMAGE_NEEDKILL(deathtype))
+ damage = 0;
+ }
+ else if (time < this.spawnshieldtime && autocvar_g_spawnshield_blockdamage < 1)
damage *= 1 - max(0, autocvar_g_spawnshield_blockdamage);
if(deathtype & HITTYPE_SOUND) // sound based attacks cause bleeding from the ears
if(take)
this.pauseregen_finished = max(this.pauseregen_finished, time + autocvar_g_balance_pause_health_regen);
- if (time > this.pain_finished) //Don't switch pain sequences like crazy
+ if (time > this.pain_finished && !STAT(FROZEN, this)) // Don't switch pain sequences like crazy
{
this.pain_finished = time + 0.5; //Supajoe
}
float realdmg = damage - excess;
- if (this != attacker && realdmg)
- if (!(round_handler_IsActive() && !round_handler_IsRoundStarted()) && time >= game_starttime)
+ if (this != attacker && realdmg && !STAT(FROZEN, this)
+ && (!(round_handler_IsActive() && !round_handler_IsRoundStarted()) && time >= game_starttime))
{
if (IS_PLAYER(attacker) && DIFF_TEAM(attacker, this)) {
GameRules_scoring_add(attacker, DMG, realdmg);
if(vbot || IS_REAL_CLIENT(this))
if(abot || IS_REAL_CLIENT(attacker))
if(attacker && this != attacker)
- if(DIFF_TEAM(this, attacker))
+ if (DIFF_TEAM(this, attacker) && (!STAT(FROZEN, this) || this.freeze_time > time))
{
if(DEATH_ISSPECIAL(deathtype))
awep = attacker.(weaponentity).m_weapon;
valid_damage_for_weaponstats = true;
}
- dh = dh - max(GetResource(this, RES_HEALTH), 0);
- da = da - max(GetResource(this, RES_ARMOR), 0);
+ dh -= max(GetResource(this, RES_HEALTH), 0); // health difference
+ da -= max(GetResource(this, RES_ARMOR), 0); // armor difference
if(valid_damage_for_weaponstats)
{
WeaponStats_LogDamage(awep.m_id, abot, this.(weaponentity).m_weapon.m_id, vbot, dh + da);