]> git.rm.cloudns.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Add a Frozen status effect for ice nades separate from the frozen state in Freeze Tag
authorMario <mario.mario@y7mail.com>
Fri, 18 Apr 2025 20:56:05 +0000 (20:56 +0000)
committerterencehill <piuntn@gmail.com>
Fri, 18 Apr 2025 20:56:05 +0000 (22:56 +0200)
Entities with the Frozen status effect can be damaged and will die if their health reaches 0. They are not healed when the effect is removed

Flags and other items can be interacted with (besides vehicles) and are not lost when becoming frozen

53 files changed:
mutators.cfg
notifications.cfg
qcsrc/client/mutators/events.qh
qcsrc/client/view.qc
qcsrc/common/deathtypes/all.inc
qcsrc/common/gamemodes/gamemode/ctf/sv_ctf.qc
qcsrc/common/gamemodes/gamemode/freezetag/cl_freezetag.qc
qcsrc/common/gamemodes/gamemode/freezetag/sv_freezetag.qc
qcsrc/common/gamemodes/gamemode/freezetag/sv_freezetag.qh
qcsrc/common/gamemodes/gamemode/keepaway/sv_keepaway.qc
qcsrc/common/gamemodes/gamemode/nexball/sv_nexball.qc
qcsrc/common/gamemodes/gamemode/nexball/sv_weapon.qc
qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qc
qcsrc/common/gamemodes/gamemode/tka/sv_tka.qc
qcsrc/common/monsters/monster/mage.qc
qcsrc/common/monsters/sv_monsters.qc
qcsrc/common/mutators/mutator/buffs/sv_buffs.qc
qcsrc/common/mutators/mutator/nades/nade/ice.qc
qcsrc/common/mutators/mutator/nades/nade/ice.qh
qcsrc/common/mutators/mutator/nades/sv_nades.qc
qcsrc/common/mutators/mutator/overkill/sv_overkill.qc
qcsrc/common/mutators/mutator/status_effects/cl_status_effects.qc
qcsrc/common/mutators/mutator/status_effects/status_effect/_mod.inc
qcsrc/common/mutators/mutator/status_effects/status_effect/_mod.qh
qcsrc/common/mutators/mutator/status_effects/status_effect/frozen.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/status_effects/status_effect/frozen.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/status_effects/status_effects.qh
qcsrc/common/mutators/mutator/status_effects/sv_status_effects.qc
qcsrc/common/mutators/mutator/walljump/walljump.qc
qcsrc/common/notifications/all.inc
qcsrc/common/physics/player.qc
qcsrc/common/physics/player.qh
qcsrc/common/stats.qh
qcsrc/common/turrets/sv_turrets.qc
qcsrc/common/vehicles/sv_vehicles.qc
qcsrc/common/weapons/weapon/fireball.qc
qcsrc/server/bot/default/aim.qc
qcsrc/server/bot/default/havocbot/havocbot.qc
qcsrc/server/bot/default/navigation.qc
qcsrc/server/client.qc
qcsrc/server/clientkill.qc
qcsrc/server/command/common.qc
qcsrc/server/command/vote.qc
qcsrc/server/damage.qc
qcsrc/server/damage.qh
qcsrc/server/hook.qc
qcsrc/server/items/items.qc
qcsrc/server/main.qc
qcsrc/server/mutators/events.qh
qcsrc/server/player.qc
qcsrc/server/utils.qh
qcsrc/server/weapons/accuracy.qc
qcsrc/server/weapons/weaponsystem.qc

index cd6ac0c870d5b77123c7ef845281cc3e96b4e84c..634744e73887d04e9296ee17acac455e4aedd2f6 100644 (file)
@@ -263,7 +263,6 @@ set g_nades_napalm_fountain_radius 130 "distance from the fountain"
 // Ice (3)
 set g_nades_ice 1 "Ice nade: freezes and reduces health; \"1\" = allow client selection of this nade type" // script-ignore
 set g_nades_ice_freeze_time 3 "how long the ice field will last"
-set g_nades_ice_health      0 "how much health the player will have after being unfrozen"
 set g_nades_ice_explode     0 "whether the ice nade should explode again once the ice field dissipated"
 set g_nades_ice_teamcheck   2 "\"0\" = freezes everyone including the player who threw the nade, \"1\" = freezes enemies and teammates, \"2\" = freezes only enemies"
 
index 1f891f7cecd77f768e4975bc3f1ff3e0d82e613a..98db7e9eba4d29fc7e8893ff274c02bf9c516b9e 100644 (file)
@@ -139,7 +139,6 @@ seta notification_INFO_DEATH_MURDER_MONSTER "1" "\"0\" = off, \"1\" = print to c
 seta notification_INFO_DEATH_MURDER_NADE "1" "\"0\" = off, \"1\" = print to console, \"2\" = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
 seta notification_INFO_DEATH_MURDER_NADE_HEAL "1" "\"0\" = off, \"1\" = print to console, \"2\" = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
 seta notification_INFO_DEATH_MURDER_NADE_ICE "1" "\"0\" = off, \"1\" = print to console, \"2\" = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
-seta notification_INFO_DEATH_MURDER_NADE_ICE_FREEZE "1" "\"0\" = off, \"1\" = print to console, \"2\" = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
 seta notification_INFO_DEATH_MURDER_NADE_NAPALM "1" "\"0\" = off, \"1\" = print to console, \"2\" = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
 seta notification_INFO_DEATH_MURDER_SHOOTING_STAR "1" "\"0\" = off, \"1\" = print to console, \"2\" = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
 seta notification_INFO_DEATH_MURDER_SLIME "1" "\"0\" = off, \"1\" = print to console, \"2\" = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
@@ -181,7 +180,6 @@ seta notification_INFO_DEATH_SELF_MON_ZOMBIE_MELEE "1" "\"0\" = off, \"1\" = pri
 seta notification_INFO_DEATH_SELF_NADE "1" "\"0\" = off, \"1\" = print to console, \"2\" = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
 seta notification_INFO_DEATH_SELF_NADE_HEAL "1" "\"0\" = off, \"1\" = print to console, \"2\" = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
 seta notification_INFO_DEATH_SELF_NADE_ICE "1" "\"0\" = off, \"1\" = print to console, \"2\" = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
-seta notification_INFO_DEATH_SELF_NADE_ICE_FREEZE "1" "\"0\" = off, \"1\" = print to console, \"2\" = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
 seta notification_INFO_DEATH_SELF_NADE_NAPALM "1" "\"0\" = off, \"1\" = print to console, \"2\" = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
 seta notification_INFO_DEATH_SELF_NOAMMO "1" "\"0\" = off, \"1\" = print to console, \"2\" = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
 seta notification_INFO_DEATH_SELF_ROT "1" "\"0\" = off, \"1\" = print to console, \"2\" = print to console and chatbox (if notification_allow_chatboxprint is enabled)"
@@ -438,7 +436,6 @@ seta notification_CENTER_DEATH_SELF_LAVA "1" "\"0\" = off, \"1\" = centerprint"
 seta notification_CENTER_DEATH_SELF_MONSTER "1" "\"0\" = off, \"1\" = centerprint"
 seta notification_CENTER_DEATH_SELF_NADE "1" "\"0\" = off, \"1\" = centerprint"
 seta notification_CENTER_DEATH_SELF_NADE_HEAL "1" "\"0\" = off, \"1\" = centerprint"
-seta notification_CENTER_DEATH_SELF_NADE_ICE_FREEZE "1" "\"0\" = off, \"1\" = centerprint"
 seta notification_CENTER_DEATH_SELF_NADE_NAPALM "1" "\"0\" = off, \"1\" = centerprint"
 seta notification_CENTER_DEATH_SELF_NOAMMO "1" "\"0\" = off, \"1\" = centerprint"
 seta notification_CENTER_DEATH_SELF_ROT "1" "\"0\" = off, \"1\" = centerprint"
@@ -596,7 +593,6 @@ seta notification_DEATH_MURDER_MONSTER "1" "enable this multiple notification"
 seta notification_DEATH_MURDER_NADE "1" "enable this multiple notification"
 seta notification_DEATH_MURDER_NADE_HEAL "1" "enable this multiple notification"
 seta notification_DEATH_MURDER_NADE_ICE "1" "enable this multiple notification"
-seta notification_DEATH_MURDER_NADE_ICE_FREEZE "1" "enable this multiple notification"
 seta notification_DEATH_MURDER_NADE_NAPALM "1" "enable this multiple notification"
 seta notification_DEATH_MURDER_SHOOTING_STAR "1" "enable this multiple notification"
 seta notification_DEATH_MURDER_SLIME "1" "enable this multiple notification"
@@ -638,7 +634,6 @@ seta notification_DEATH_SELF_MON_ZOMBIE_MELEE "1" "enable this multiple notifica
 seta notification_DEATH_SELF_NADE "1" "enable this multiple notification"
 seta notification_DEATH_SELF_NADE_HEAL "1" "enable this multiple notification"
 seta notification_DEATH_SELF_NADE_ICE "1" "enable this multiple notification"
-seta notification_DEATH_SELF_NADE_ICE_FREEZE "1" "enable this multiple notification"
 seta notification_DEATH_SELF_NADE_NAPALM "1" "enable this multiple notification"
 seta notification_DEATH_SELF_NOAMMO "1" "enable this multiple notification"
 seta notification_DEATH_SELF_ROT "1" "enable this multiple notification"
index 6f6f34399f18e19a476771b9b7d41bc0bf237daa..0b5a9a6aaaed9d16e348f458428d3dbd1191b7e6 100644 (file)
@@ -116,6 +116,9 @@ MUTATOR_HOOKABLE(Ent_Init, EV_NO_ARGS);
        /**/
 MUTATOR_HOOKABLE(HUD_Draw_overlay, EV_HUD_Draw_overlay);
 
+/** return true to hide the damage HUD overlay */
+MUTATOR_HOOKABLE(HUD_Damage_show, EV_NO_ARGS);
+
 MUTATOR_HOOKABLE(HUD_Powerups_add, EV_NO_ARGS);
 
 /** return true to show the physics HUD panel when optional mode is enabled */
index 6fc7d063a5d05aaaeeb86410cdc527df7562e2fb..238fd4c87c1a342c1546577dbb8cc60f3d556c8c 100644 (file)
@@ -741,7 +741,7 @@ int WantEventchase(entity this, bool want_vehiclechase)
                        return 1;
                if(MUTATOR_CALLHOOK(WantEventchase, this))
                        return 1;
-               if(autocvar_cl_eventchase_frozen && STAT(FROZEN))
+               if(autocvar_cl_eventchase_frozen && StatusEffects_active(STATUSEFFECT_Frozen, this))
                        return 1;
                if(autocvar_cl_eventchase_death && (STAT(HEALTH) <= 0))
                {
@@ -997,20 +997,12 @@ void HUD_Draw(entity this)
                        if (alpha_multipl > 0)
                                drawfill('0 0 0', vec2(vid_conwidth, vid_conheight), col, autocvar_hud_colorflash_alpha * alpha_multipl, DRAWFLAG_ADDITIVE);
                }
-               else if(STAT(FROZEN))
-               {
-                       vector col = '0.25 0.90 1';
-                       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(!intermission)
        {
+               // TODO: mutator hook for these? maybe something more generic!
                if(STAT(NADE_TIMER) && autocvar_cl_nade_timer) // give nade top priority, as it's a matter of life and death
                {
                        vector col = '0.25 0.90 1' + vec3(STAT(NADE_TIMER), -STAT(NADE_TIMER), -STAT(NADE_TIMER));
@@ -1244,7 +1236,7 @@ void HUD_Contents()
 // provides some effects to the postprocessing function
 void HUD_Damage()
 {
-       if(!autocvar_hud_damage || STAT(FROZEN))
+       if(!autocvar_hud_damage || MUTATOR_CALLHOOK(HUD_Damage_show))
                return;
 
        vector splash_pos = '0 0 0', splash_size = '0 0 0';
index 19de656e0d8824f0247c498eb8404f521bf05d00..f5e93ed9ffa84de103622900f4ba3f6f2cdc2faa 100644 (file)
@@ -22,7 +22,6 @@ REGISTER_DEATHTYPE(MONSTER_ZOMBIE_MELEE,    DEATH_SELF_MON_ZOMBIE_MELEE,    DEAT
 REGISTER_DEATHTYPE(NADE,                    DEATH_SELF_NADE,                DEATH_MURDER_NADE,              NULL, NULL, "")
 REGISTER_DEATHTYPE(NADE_NAPALM,             DEATH_SELF_NADE_NAPALM,         DEATH_MURDER_NADE_NAPALM,       NULL, NULL, "")
 REGISTER_DEATHTYPE(NADE_ICE,                DEATH_SELF_NADE_ICE,            DEATH_MURDER_NADE_ICE,          NULL, NULL, "")
-REGISTER_DEATHTYPE(NADE_ICE_FREEZE,         DEATH_SELF_NADE_ICE_FREEZE,     DEATH_MURDER_NADE_ICE_FREEZE,   NULL, NULL, "")
 REGISTER_DEATHTYPE(NADE_HEAL,               DEATH_SELF_NADE_HEAL,           DEATH_MURDER_NADE_HEAL,         NULL, NULL, "")
 REGISTER_DEATHTYPE(NOAMMO,                  DEATH_SELF_NOAMMO,              NULL,                           NULL, NULL, "")
 REGISTER_DEATHTYPE(ROT,                     DEATH_SELF_ROT,                 NULL,                           NULL, NULL, "")
index 20359569442d154c65edca2a7821608fe1fbbf61..43e03d65fa86a2e29cc4b6f537999df840efa01f 100644 (file)
@@ -1142,8 +1142,8 @@ METHOD(Flag, giveTo, bool(Flag this, entity flag, entity toucher))
        }
 
        // special touch behaviors
+       // TODO: mutator hook to prevent picking up objectives
        if(IS_INDEPENDENT_PLAYER(toucher)) { return; }
-       else if(STAT(FROZEN, toucher)) { return; }
        else if(IS_VEHICLE(toucher))
        {
                if(autocvar_g_ctf_allow_vehicle_touch && toucher.owner)
index df4931a37133793b69a021ed28d66d106b70beee..8a3eeeb0149aca474f662403b1e2d659a8e76263 100644 (file)
@@ -13,3 +13,36 @@ void HUD_Mod_FreezeTag(vector myPos, vector mySize)
 
        HUD_Mod_CA_Draw(myPos, mySize, autocvar_hud_panel_modicons_freezetag_layout);
 }
+
+REGISTER_MUTATOR(cl_ft, true);
+
+MUTATOR_HOOKFUNCTION(cl_ft, WantEventchase)
+{
+       if(autocvar_cl_eventchase_frozen && ISGAMETYPE(FREEZETAG) && STAT(FROZEN))
+               return true;
+       return false;
+}
+
+MUTATOR_HOOKFUNCTION(cl_ft, HUD_Draw_overlay)
+{
+       if(STAT(FROZEN) && ISGAMETYPE(FREEZETAG))
+       {
+               vector col = '0.25 0.90 1';
+               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);
+
+               M_ARGV(0, vector) = col;
+               M_ARGV(1, float) = alpha_fade;
+               return true;
+       }
+       return false;
+}
+
+MUTATOR_HOOKFUNCTION(cl_ft, HUD_Damage_show)
+{
+       if(ISGAMETYPE(FREEZETAG) && STAT(FROZEN))
+               return true;
+       return false;
+}
index a9f0143a87ab742f4fb36e20edbd0ed253df1cf4..e67936089a5cf125e8a548f200e217a22109d30d 100644 (file)
@@ -28,7 +28,7 @@ void freezetag_count_alive_players()
        FOREACH_CLIENT(IS_PLAYER(it) && Entity_HasValidTeam(it),
        {
                ++total_players;
-               if (GetResource(it, RES_HEALTH) < 1 || STAT(FROZEN, it) == FROZEN_NORMAL)
+               if (GetResource(it, RES_HEALTH) < 1 || STAT(FROZEN, it))
                {
                        continue;
                }
@@ -153,7 +153,7 @@ entity freezetag_LastPlayerForTeam(entity this)
 {
        entity last_pl = NULL;
        FOREACH_CLIENT(IS_PLAYER(it) && it != this && SAME_TEAM(it, this), {
-               if (STAT(FROZEN, it) != FROZEN_NORMAL && GetResource(it, RES_HEALTH) >= 1)
+               if (!STAT(FROZEN, it) && GetResource(it, RES_HEALTH) >= 1)
                {
                        if (!last_pl)
                                last_pl = it;
@@ -196,26 +196,114 @@ void freezetag_Add_Score(entity targ, entity attacker)
        // else nothing - got frozen by the game type rules themselves
 }
 
+void freezetag_Ice_Think(entity this)
+{
+       if(!STAT(FROZEN, this.owner) || this.owner.iceblock != this)
+       {
+               delete(this);
+               return;
+       }
+       vector ice_org = this.owner.origin - '0 0 16';
+       if (this.origin != ice_org)
+               setorigin(this, ice_org);
+       this.nextthink = time;
+}
+
 // to be called when the player is frozen by freezetag (on death, spectator join etc), gives the score
 void freezetag_Freeze(entity targ, entity attacker)
 {
-       if(STAT(FROZEN, targ))
+       if(!IS_PLAYER(targ) || STAT(FROZEN, targ))
                return;
 
        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);
+       STAT(FROZEN, targ) = true;
+       STAT(REVIVE_PROGRESS, targ) = 0;
+       SetResource(targ, RES_HEALTH, 1);
+       targ.revive_speed = 0;
+       if(targ.bot_attack)
+               IL_REMOVE(g_bot_targets, targ);
+       targ.bot_attack = false;
+       targ.freeze_time = time;
+
+       entity ice = new(ice);
+       ice.owner = targ;
+       ice.scale = targ.scale;
+       // set_movetype(ice, MOVETYPE_FOLLOW) would rotate the ice model with the player
+       setthink(ice, freezetag_Ice_Think);
+       ice.nextthink = time;
+       ice.frame = floor(random() * 21); // ice model has 20 different looking frames
+       setmodel(ice, MDL_ICE);
+       ice.alpha = 1;
+       ice.colormod = Team_ColorRGB(targ.team);
+       ice.glowmod = ice.colormod;
+       targ.iceblock = ice;
+       targ.revival_time = 0;
+
+       freezetag_Ice_Think(ice);
+
+       RemoveGrapplingHooks(targ);
+
+       FOREACH_CLIENT(IS_PLAYER(it),
+       {
+               for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+               {
+                       .entity weaponentity = weaponentities[slot];
+                       if(it.(weaponentity).hook.aiment == targ)
+                               RemoveHook(it.(weaponentity).hook);
+               }
+       });
+
+       WaypointSprite_Spawn(WP_Frozen, 0, 0, targ, '0 0 64', NULL, targ.team, targ, waypointsprite_attached, true, RADARICON_WAYPOINT);
 
        freezetag_count_alive_players();
 
        freezetag_Add_Score(targ, attacker);
 }
 
+void freezetag_Unfreeze(entity targ, bool reset_health)
+{
+       if(!STAT(FROZEN, targ))
+               return;
+
+       if (reset_health)
+               SetResource(targ, RES_HEALTH, ((IS_PLAYER(targ)) ? start_health : targ.max_health));
+
+       targ.pauseregen_finished = time + autocvar_g_balance_pause_health_regen;
+
+       STAT(FROZEN, targ) = false;
+       STAT(REVIVE_PROGRESS, targ) = 0;
+       targ.revival_time = time;
+       if(!targ.bot_attack)
+               IL_PUSH(g_bot_targets, targ);
+       targ.bot_attack = true;
+
+       WaypointSprite_Kill(targ.waypointsprite_attached);
+
+       FOREACH_CLIENT(IS_PLAYER(it),
+       {
+               for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+               {
+                       .entity weaponentity = weaponentities[slot];
+                       if(it.(weaponentity).hook.aiment == targ)
+                               RemoveHook(it.(weaponentity).hook);
+               }
+       });
+
+       // remove the ice block
+       if(targ.iceblock)
+               delete(targ.iceblock);
+       targ.iceblock = NULL;
+
+       targ.freezetag_frozen_time = 0;
+       targ.freezetag_frozen_timeout = 0;
+}
+
 bool freezetag_isEliminated(entity e)
 {
-       if(IS_PLAYER(e) && (STAT(FROZEN, e) == FROZEN_NORMAL || IS_DEAD(e)))
+       if(IS_PLAYER(e) && (STAT(FROZEN, e) || IS_DEAD(e)))
                return true;
        return false;
 }
@@ -233,7 +321,7 @@ void havocbot_goalrating_ft_freeplayers(entity this, float ratingscale, vector o
        entity best_pl = NULL;
        float best_dist2 = FLOAT_MAX;
        FOREACH_CLIENT(IS_PLAYER(it) && it != this && SAME_TEAM(it, this), {
-               if (STAT(FROZEN, it) == FROZEN_NORMAL)
+               if (STAT(FROZEN, it))
                {
                        if(vdist(it.origin - org, >, sradius))
                                continue;
@@ -269,12 +357,12 @@ void havocbot_role_ft_offense(entity this)
 
        // Count how many players on team are unfrozen.
        int unfrozen = 0;
-       FOREACH_CLIENT(IS_PLAYER(it) && SAME_TEAM(it, this) && STAT(FROZEN, it) != FROZEN_NORMAL, {
+       FOREACH_CLIENT(IS_PLAYER(it) && SAME_TEAM(it, this) && !STAT(FROZEN, it), {
                unfrozen++;
        });
 
        // If only one left on team or if role has timed out then start trying to free players.
-       if ((!unfrozen && STAT(FROZEN, this) != FROZEN_NORMAL) || time > this.havocbot_role_timeout)
+       if ((!unfrozen && !STAT(FROZEN, this)) || time > this.havocbot_role_timeout)
        {
                LOG_TRACE("changing role to freeing");
                this.havocbot_role = havocbot_role_ft_freeing;
@@ -331,9 +419,9 @@ void havocbot_role_ft_freeing(entity this)
 
 void ft_RemovePlayer(entity this)
 {
-       if (STAT(FROZEN, this) != FROZEN_NORMAL)
+       if (!STAT(FROZEN, this))
                freezetag_LastPlayerForTeam_Notify(this);
-       Unfreeze(this, false);
+       freezetag_Unfreeze(this, false);
 
        SetResourceExplicit(this, RES_HEALTH, 0); // neccessary to correctly count alive players
        freezetag_count_alive_players();
@@ -354,6 +442,29 @@ MUTATOR_HOOKFUNCTION(ft, MakePlayerObserver)
        ft_RemovePlayer(player);
 }
 
+MUTATOR_HOOKFUNCTION(ft, SpectateCopy)
+{
+       entity spectatee = M_ARGV(0, entity);
+       entity client = M_ARGV(1, entity);
+
+       STAT(FROZEN, client) = STAT(FROZEN, spectatee);
+       STAT(REVIVE_PROGRESS, client) = STAT(REVIVE_PROGRESS, spectatee);
+}
+
+MUTATOR_HOOKFUNCTION(ft, ClientKill)
+{
+       entity player = M_ARGV(0, entity);
+
+       return STAT(FROZEN, player);
+}
+
+MUTATOR_HOOKFUNCTION(ft, PlayerDied)
+{
+       entity player = M_ARGV(0, entity);
+
+       freezetag_Unfreeze(player, false);
+}
+
 MUTATOR_HOOKFUNCTION(ft, PlayerDies)
 {
        entity frag_attacker = M_ARGV(1, entity);
@@ -363,8 +474,8 @@ MUTATOR_HOOKFUNCTION(ft, PlayerDies)
        if(round_handler_IsActive())
        if(round_handler_CountdownRunning())
        {
-               if (STAT(FROZEN, frag_target) == FROZEN_NORMAL)
-                       Unfreeze(frag_target, true);
+               if (STAT(FROZEN, frag_target))
+                       freezetag_Unfreeze(frag_target, true);
                freezetag_count_alive_players();
                frag_target.respawn_time = time;
                frag_target.respawn_flags |= RESPAWN_FORCE;
@@ -390,7 +501,7 @@ MUTATOR_HOOKFUNCTION(ft, PlayerDies)
        {
                // can't use freezetag_Add_Score here since it doesn't assign any points
                // if the attacker is not a player (e.g. triggerhurt) by design
-               if ((STAT(FROZEN, frag_target) != FROZEN_NORMAL) && !IS_PLAYER(frag_attacker))
+               if (!STAT(FROZEN, frag_target) && !IS_PLAYER(frag_attacker))
                        GameRules_scoring_add(frag_target, SCORE, -1);
 
                // by restoring some health right after player death (soft-kill)
@@ -412,7 +523,7 @@ MUTATOR_HOOKFUNCTION(ft, PlayerDies)
                frag_target.punchvector = '0 0 0';
        }
 
-       if (STAT(FROZEN, frag_target) == FROZEN_NORMAL)
+       if (STAT(FROZEN, frag_target))
                return true;
 
        freezetag_Freeze(frag_target, frag_attacker);
@@ -436,6 +547,8 @@ MUTATOR_HOOKFUNCTION(ft, PlayerSpawn)
 {
        entity player = M_ARGV(0, entity);
 
+       freezetag_Unfreeze(player, false);
+
        if(player.freezetag_frozen_timeout == -1) // if PlayerSpawn is called by reset_map_players
                return true; // do nothing, round is starting right now
 
@@ -462,6 +575,21 @@ MUTATOR_HOOKFUNCTION(ft, PutClientInServer)
        eliminatedPlayers.SendFlags |= 1;
 }
 
+MUTATOR_HOOKFUNCTION(ft, PlayerAnim)
+{
+       entity player = M_ARGV(0, entity);
+
+       if(STAT(FROZEN, player))
+               M_ARGV(1, int) |= ANIMSTATE_FROZEN;
+}
+
+MUTATOR_HOOKFUNCTION(ft, reset_map_global)
+{
+       FOREACH_CLIENT(IS_PLAYER(it) && STAT(FROZEN, it), {
+               freezetag_Unfreeze(it, false);
+       });
+}
+
 MUTATOR_HOOKFUNCTION(ft, reset_map_players)
 {
        FOREACH_CLIENT(IS_PLAYER(it), {
@@ -481,24 +609,39 @@ MUTATOR_HOOKFUNCTION(ft, GiveFragsForKill, CBC_ORDER_FIRST)
        return true;
 }
 
-MUTATOR_HOOKFUNCTION(ft, Unfreeze)
+MUTATOR_HOOKFUNCTION(ft, LockWeapon)
 {
-       entity targ = M_ARGV(0, entity);
-       targ.freezetag_frozen_time = 0;
-       targ.freezetag_frozen_timeout = 0;
+       entity player = M_ARGV(0, entity);
+       return STAT(FROZEN, player);
+}
+
+MUTATOR_HOOKFUNCTION(ft, PlayerDamaged)
+{
+       entity frag_target = M_ARGV(1, entity);
+       return STAT(FROZEN, frag_target);
+}
+
+MUTATOR_HOOKFUNCTION(ft, AccuracyTargetValid)
+{
+       entity frag_target = M_ARGV(1, entity);
+
+       // damage to frozen players is good only if it happens in the frame they get frozen
+       if (STAT(FROZEN, frag_target) && time > frag_target.freeze_time)
+               return MUT_ACCADD_INDIFFERENT;
+       return MUT_ACCADD_VALID;
 }
 
 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);
+       float frag_deathtype = M_ARGV(3, float);
+       float frag_damage = M_ARGV(4, float);
        vector frag_force = M_ARGV(6, vector);
 
        frag_target.freezetag_frozen_armor = GetResource(frag_target, RES_ARMOR);
 
-       if (STAT(FROZEN, frag_target) == FROZEN_NORMAL && autocvar_g_freezetag_revive_auto_reducible
+       if (STAT(FROZEN, frag_target) && autocvar_g_freezetag_revive_auto_reducible
                && autocvar_g_freezetag_frozen_maxtime > 0 && autocvar_g_freezetag_revive_auto)
        {
                float t = 0;
@@ -525,6 +668,58 @@ MUTATOR_HOOKFUNCTION(ft, Damage_Calculate)
                                frag_target.freezetag_frozen_timeout = time;
                }
        }
+
+       if(STAT(FROZEN, frag_target) && !ITEM_DAMAGE_NEEDKILL(frag_deathtype)
+                       && frag_deathtype != DEATH_TEAMCHANGE.m_id && frag_deathtype != DEATH_AUTOTEAMCHANGE.m_id)
+       {
+               if(autocvar_g_frozen_revive_falldamage > 0 && frag_deathtype == DEATH_FALL.m_id && frag_damage >= autocvar_g_frozen_revive_falldamage)
+               {
+                       freezetag_Unfreeze(frag_target, false);
+                       SetResource(frag_target, RES_HEALTH, autocvar_g_frozen_revive_falldamage_health);
+                       Send_Effect(EFFECT_ICEORGLASS, frag_target.origin, '0 0 0', 3);
+                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_FREEZETAG_REVIVED_FALL, frag_target.netname);
+                       Send_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CENTER_FREEZETAG_REVIVE_SELF);
+               }
+
+               frag_damage = 0;
+               frag_force *= autocvar_g_frozen_force;
+       }
+
+       if(IS_PLAYER(frag_target) && STAT(FROZEN, frag_target)
+                       && ITEM_DAMAGE_NEEDKILL(frag_deathtype) && !autocvar_g_frozen_damage_trigger)
+       {
+               Send_Effect(EFFECT_TELEPORT, frag_target.origin, '0 0 0', 1);
+
+               entity spot = SelectSpawnPoint(frag_target, false);
+               if(spot)
+               {
+                       frag_damage = 0;
+                       frag_target.deadflag = DEAD_NO;
+
+                       frag_target.angles = spot.angles;
+
+                       frag_target.effects = 0;
+                       frag_target.effects |= EF_TELEPORT_BIT;
+
+                       frag_target.angles_z = 0; // never spawn tilted even if the spot says to
+                       frag_target.fixangle = true; // turn this way immediately
+                       frag_target.velocity = '0 0 0';
+                       frag_target.avelocity = '0 0 0';
+                       frag_target.punchangle = '0 0 0';
+                       frag_target.punchvector = '0 0 0';
+                       frag_target.oldvelocity = frag_target.velocity;
+
+                       frag_target.spawnorigin = spot.origin;
+                       setorigin(frag_target, spot.origin + '0 0 1' * (1 - frag_target.mins.z - 24));
+                       // don't reset back to last position, even if new position is stuck in solid
+                       frag_target.oldorigin = frag_target.origin;
+
+                       Send_Effect(EFFECT_TELEPORT, frag_target.origin, '0 0 0', 1);
+               }
+       }
+
+       M_ARGV(4, float) = frag_damage;
+       M_ARGV(6, vector) = frag_force;
 }
 
 #ifdef IN_REVIVING_RANGE
@@ -545,7 +740,7 @@ MUTATOR_HOOKFUNCTION(ft, PlayerPreThink, CBC_ORDER_FIRST)
                return true;
 
        entity player = M_ARGV(0, entity);
-       //if (STAT(FROZEN, player) == FROZEN_NORMAL)
+       //if (STAT(FROZEN, player))
        //if(player.freezetag_frozen_timeout > 0 && time < player.freezetag_frozen_timeout)
                //player.iceblock.alpha = ICE_MIN_ALPHA + (ICE_MAX_ALPHA - ICE_MIN_ALPHA) * (player.freezetag_frozen_timeout - time) / (player.freezetag_frozen_timeout - player.freezetag_frozen_time);
 
@@ -562,9 +757,9 @@ MUTATOR_HOOKFUNCTION(ft, PlayerPreThink, CBC_ORDER_FIRST)
        vector revive_extra_size = '1 1 1' * autocvar_g_freezetag_revive_extra_size;
        FOREACH_CLIENT(IS_PLAYER(it), {
                // check if player is reviving anyone
-               if (STAT(FROZEN, it) == FROZEN_NORMAL)
+               if (STAT(FROZEN, it))
                {
-                       if ((STAT(FROZEN, player) == FROZEN_NORMAL))
+                       if ((STAT(FROZEN, player)))
                                continue;
                        if (!IN_REVIVING_RANGE(player, it, revive_extra_size))
                                continue;
@@ -572,13 +767,13 @@ MUTATOR_HOOKFUNCTION(ft, PlayerPreThink, CBC_ORDER_FIRST)
                        break;
                }
 
-               if (!(STAT(FROZEN, player) == FROZEN_NORMAL))
+               if (!(STAT(FROZEN, player)))
                        continue; // both player and it are NOT frozen
                if (!IN_REVIVING_RANGE(player, it, revive_extra_size))
                        continue;
 
                // found a teammate that is reviving player
-               if (autocvar_g_freezetag_revive_time_to_score > 0 && STAT(FROZEN, player) == FROZEN_NORMAL)
+               if (autocvar_g_freezetag_revive_time_to_score > 0 && STAT(FROZEN, player))
                {
                        it.freezetag_revive_time += frametime / autocvar_g_freezetag_revive_time_to_score;
                        while (it.freezetag_revive_time > 1)
@@ -599,12 +794,12 @@ MUTATOR_HOOKFUNCTION(ft, PlayerPreThink, CBC_ORDER_FIRST)
 
        // allow normal revival during automatic revival
        // (if we wouldn't allow it then freezetag_frozen_timeout should be checked too in the previous loop)
-       //if (STAT(FROZEN, player) == FROZEN_NORMAL) // redundant check
+       //if (STAT(FROZEN, player)) // redundant check
        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
+       if  (STAT(FROZEN, player) && 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
@@ -614,7 +809,7 @@ MUTATOR_HOOKFUNCTION(ft, PlayerPreThink, CBC_ORDER_FIRST)
        if (!n) // no teammate nearby
        {
                float clearspeed = autocvar_g_freezetag_revive_clearspeed;
-               if (STAT(FROZEN, player) == FROZEN_NORMAL)
+               if (STAT(FROZEN, player))
                {
                        if (autocvar_g_freezetag_revive_time_to_score > 0)
                        {
@@ -634,7 +829,7 @@ MUTATOR_HOOKFUNCTION(ft, PlayerPreThink, CBC_ORDER_FIRST)
                else if (!STAT(FROZEN, player) && !player_is_reviving)
                        STAT(REVIVE_PROGRESS, player) = base_progress; // thawing nobody
        }
-       else if (STAT(FROZEN, player) == FROZEN_NORMAL) // OK, there is at least one teammate reviving us
+       else if (STAT(FROZEN, player)) // OK, there is at least one teammate reviving us
        {
                float spd = autocvar_g_freezetag_revive_speed_t2s;
                if (autocvar_g_freezetag_revive_time_to_score <= 0)
@@ -644,7 +839,7 @@ MUTATOR_HOOKFUNCTION(ft, PlayerPreThink, CBC_ORDER_FIRST)
                if(STAT(REVIVE_PROGRESS, player) >= 1)
                {
                        float frozen_time = time - player.freezetag_frozen_time;
-                       Unfreeze(player, false);
+                       freezetag_Unfreeze(player, false);
                        SetResourceExplicit(player, RES_HEALTH, ((warmup_stage) ? warmup_start_health : start_health));
                        player.spawnshieldtime = time + autocvar_g_freezetag_revive_spawnshield;
                        freezetag_count_alive_players();
@@ -684,7 +879,7 @@ MUTATOR_HOOKFUNCTION(ft, PlayerPreThink, CBC_ORDER_FIRST)
                        STAT(REVIVE_PROGRESS, it) = STAT(REVIVE_PROGRESS, player);
        }
 
-       if (STAT(FROZEN, player) == FROZEN_NORMAL)
+       if (STAT(FROZEN, player))
        {
                entity player_wp = player.waypointsprite_attached;
                if (n > 0 || (n == 0 && STAT(REVIVE_PROGRESS, player) > 0.95))
@@ -705,6 +900,31 @@ MUTATOR_HOOKFUNCTION(ft, PlayerPreThink, CBC_ORDER_FIRST)
        return true;
 }
 
+MUTATOR_HOOKFUNCTION(ft, PlayerRegen)
+{
+       entity player = M_ARGV(0, entity);
+
+       return STAT(FROZEN, player);
+}
+
+MUTATOR_HOOKFUNCTION(ft, ItemTouch)
+{
+       if(MUTATOR_RETURNVALUE) return false;
+
+       entity toucher = M_ARGV(1, entity);
+
+       if(STAT(FROZEN, toucher))
+               return MUT_ITEMTOUCH_RETURN;
+       return MUT_ITEMTOUCH_CONTINUE;
+}
+
+MUTATOR_HOOKFUNCTION(ft, BuffTouch)
+{
+       entity toucher = M_ARGV(1, entity);
+
+       return STAT(FROZEN, toucher);
+}
+
 MUTATOR_HOOKFUNCTION(ft, SetStartItems)
 {
        start_items &= ~(IT_UNLIMITED_AMMO | IT_UNLIMITED_SUPERWEAPONS);
@@ -720,6 +940,38 @@ MUTATOR_HOOKFUNCTION(ft, SetStartItems)
        start_ammo_fuel    = warmup_start_ammo_fuel    = autocvar_g_ft_start_ammo_fuel;
 }
 
+MUTATOR_HOOKFUNCTION(ft, AllowMobSpawning)
+{
+       M_ARGV(1, string) = "You can't spawn monsters while frozen";
+       return true;
+}
+
+MUTATOR_HOOKFUNCTION(ft, MonsterValidTarget)
+{
+       entity targ = M_ARGV(1, entity);
+
+       return !STAT(FROZEN, targ);
+}
+
+MUTATOR_HOOKFUNCTION(ft, TurretValidateTarget)
+{
+       entity targ = M_ARGV(1, entity);
+
+       if(STAT(FROZEN, targ))
+       {
+               M_ARGV(3, float) = -6;
+               return true;
+       }
+       return false;
+}
+
+MUTATOR_HOOKFUNCTION(ft, BotShouldAttack)
+{
+       entity targ = M_ARGV(1, entity);
+
+       return !STAT(FROZEN, targ);
+}
+
 MUTATOR_HOOKFUNCTION(ft, HavocBot_ChooseRole)
 {
        entity bot = M_ARGV(0, entity);
@@ -759,7 +1011,7 @@ MUTATOR_HOOKFUNCTION(ft, FragCenterMessage)
        int kill_count_to_attacker = M_ARGV(3, int);
        int kill_count_to_target = M_ARGV(4, int);
 
-       if(STAT(FROZEN, frag_target) == FROZEN_NORMAL)
+       if(STAT(FROZEN, frag_target))
                return; // target was already frozen, so this is just pushing them off the cliff
 
        Send_Notification(NOTIF_ONE, frag_attacker, MSG_CHOICE, CHOICE_FRAG_FREEZE, frag_target.netname, kill_count_to_attacker, (IS_BOT_CLIENT(frag_target) ? -1 : CS(frag_target).ping));
index 049f037147e19b22fa68d1c0c02d6004f9b30bb7..f760331dfb94ed6eb75a5a2057e5d67c715b1647 100644 (file)
@@ -48,3 +48,16 @@ float autocvar_g_freezetag_revive_time_to_score = 1.5;
 bool autocvar_g_freezetag_revive_nade;
 float autocvar_g_freezetag_revive_nade_health;
 float autocvar_g_freezetag_revive_spawnshield = 1;
+float autocvar_g_frozen_revive_falldamage;
+int autocvar_g_frozen_revive_falldamage_health;
+bool autocvar_g_frozen_damage_trigger;
+float autocvar_g_frozen_force;
+
+// frozen (eliminated) state
+.float revival_time; // time at which player was last revived
+.float revive_speed; // NOTE: multiplier (anything above 1 is instaheal)
+.float freeze_time;
+.entity iceblock;
+.entity frozen_by; // for ice fields
+
+void freezetag_Unfreeze(entity targ, bool reset_health);
index 3bff5c560af80b0e9a3db41af69a4030fa619d41..9451bf7deb590b9a56568c5fa3ac7fb932bc91d4 100644 (file)
@@ -121,9 +121,9 @@ void ka_TouchEvent(entity this, entity toucher) // runs any time that the ball c
                ka_RespawnBall(this);
                return;
        }
+       // TODO: mutator hook to prevent picking up objectives
        if(IS_INDEPENDENT_PLAYER(toucher)) { return; }
        if(IS_DEAD(toucher)) { return; }
-       if(STAT(FROZEN, toucher)) { return; }
        if (!IS_PLAYER(toucher))
        {  // The ball just touched an object, most likely the world
                Send_Effect(EFFECT_BALL_SPARKS, this.origin, '0 0 0', 1);
index c8a82b0c1b4a7e53d121b4d8254481966220330e..5c05d4b30f254a0199bc8dce894d65c62a4d3dbf 100644 (file)
@@ -341,7 +341,8 @@ void basketball_touch(entity this, entity toucher)
                football_touch(this, toucher);
                return;
        }
-       if(!this.cnt && IS_PLAYER(toucher) && !STAT(FROZEN, toucher) && !IS_DEAD(toucher) && (toucher != this.nb_dropper || time > this.nb_droptime + autocvar_g_nexball_delay_collect))
+       // TODO: mutator hook to prevent picking up objectives
+       if(!this.cnt && IS_PLAYER(toucher) && !IS_DEAD(toucher) && (toucher != this.nb_dropper || time > this.nb_droptime + autocvar_g_nexball_delay_collect))
        {
                if(GetResource(toucher, RES_HEALTH) < 1)
                        return;
index 7bb6c98c1dd89a1bd3eaddc64ad008f2df38f781..76e04d72f7da480ae2ae228229758669419eb361 100644 (file)
@@ -82,8 +82,9 @@ void W_Nexball_Touch(entity this, entity toucher)
        //this.enemy = NULL;
 
        PROJECTILE_TOUCH(this, toucher);
+       // TODO: mutator hook to prevent picking up objectives
        if(attacker.team != toucher.team || autocvar_g_nexball_basketball_teamsteal)
-               if((ball = toucher.ballcarried) && !STAT(FROZEN, toucher) && !IS_DEAD(toucher) && (IS_PLAYER(attacker)))
+               if((ball = toucher.ballcarried) && !IS_DEAD(toucher) && (IS_PLAYER(attacker)))
                {
                        toucher.velocity = toucher.velocity + normalize(this.velocity) * toucher.damageforcescale * autocvar_g_balance_nexball_secondary_force;
                        UNSET_ONGROUND(toucher);
index 5838c6242dcccfbb60eee047e6112f9fe6ef6eec..5d842578929ebdab6c632d9aa5b4ab8840c50be4 100644 (file)
@@ -693,7 +693,6 @@ void ons_ControlPoint_Touch(entity this, entity toucher)
        }
 
        if(!IS_PLAYER(toucher)) { return; }
-       if(STAT(FROZEN, toucher)) { return; }
        if(IS_DEAD(toucher)) { return; }
 
        if ( SAME_TEAM(this,toucher) )
@@ -1934,46 +1933,40 @@ MUTATOR_HOOKFUNCTION(ons, SV_ParseClientCommand)
 
                if ( IS_PLAYER(player) )
                {
-                       if ( !STAT(FROZEN, player) )
-                       {
-                               entity source_point = ons_Nearest_ControlPoint(player, player.origin, autocvar_g_onslaught_teleport_radius);
+                       entity source_point = ons_Nearest_ControlPoint(player, player.origin, autocvar_g_onslaught_teleport_radius);
 
-                               if ( !source_point && GetResource(player, RES_HEALTH) > 0 )
-                               {
-                                       sprint(player, "\nYou need to be next to a control point\n");
-                                       return true;
-                               }
+                       if ( !source_point && GetResource(player, RES_HEALTH) > 0 )
+                       {
+                               sprint(player, "\nYou need to be next to a control point\n");
+                               return true;
+                       }
 
+                       entity closest_target = ons_Nearest_ControlPoint_2D(player, pos, autocvar_g_onslaught_click_radius);
 
-                               entity closest_target = ons_Nearest_ControlPoint_2D(player, pos, autocvar_g_onslaught_click_radius);
+                       if ( closest_target == NULL )
+                       {
+                               sprint(player, "\nNo control point found\n");
+                               return true;
+                       }
 
-                               if ( closest_target == NULL )
+                       if ( GetResource(player, RES_HEALTH) <= 0 )
+                       {
+                               player.ons_spawn_by = closest_target;
+                               player.respawn_flags = player.respawn_flags | RESPAWN_FORCE;
+                       }
+                       else
+                       {
+                               if ( source_point == closest_target )
                                {
-                                       sprint(player, "\nNo control point found\n");
+                                       sprint(player, "\nTeleporting to the same point\n");
                                        return true;
                                }
 
-                               if ( GetResource(player, RES_HEALTH) <= 0 )
-                               {
-                                       player.ons_spawn_by = closest_target;
-                                       player.respawn_flags = player.respawn_flags | RESPAWN_FORCE;
-                               }
-                               else
-                               {
-                                       if ( source_point == closest_target )
-                                       {
-                                               sprint(player, "\nTeleporting to the same point\n");
-                                               return true;
-                                       }
-
-                                       if ( !ons_Teleport(player,closest_target,autocvar_g_onslaught_teleport_radius,true) )
-                                               sprint(player, "\nUnable to teleport there\n");
-                               }
-
-                               return true;
+                               if ( !ons_Teleport(player,closest_target,autocvar_g_onslaught_teleport_radius,true) )
+                                       sprint(player, "\nUnable to teleport there\n");
                        }
 
-                       sprint(player, "\nNo teleportation for you\n");
+                       return true;
                }
 
                return true;
index 7f68e72672d5d2eb9dc78318e338f6fab94c8ee3..969146a561b9d44905ac05f1ba23fa1d76131a22 100644 (file)
@@ -114,7 +114,6 @@ void tka_TouchEvent(entity this, entity toucher) // runs any time that the ball
        }
        if(IS_INDEPENDENT_PLAYER(toucher)) { return; }
        if(IS_DEAD(toucher)) { return; }
-       if(STAT(FROZEN, toucher)) { return; }
        if (!IS_PLAYER(toucher))
        {  // The ball just touched an object, most likely the world
                Send_Effect(EFFECT_BALL_SPARKS, this.origin, '0 0 0', 1);
index 32a6da4de528dd0c51958917b4454a1224085155..b42ab4f637af703e6e4f1cb711419d59f86bfa13 100644 (file)
@@ -84,7 +84,8 @@ void M_Mage_Defend_Shield(entity this);
 
 bool M_Mage_Defend_Heal_Check(entity this, entity targ)
 {
-       if(targ == NULL)
+       // TODO: mutator hook to choose valid healing targets?
+       if(!targ)
                return false;
        if(GetResource(targ, RES_HEALTH) <= 0)
                return false;
index a0db8a80979332fd5efcfb11f4e13a784bb8782a..4a0b686a2968ceb27e77f9f23b9d350a4d7f7ff8 100644 (file)
@@ -114,7 +114,6 @@ bool Monster_ValidTarget(entity this, entity targ, bool skipfacing)
        || (!IS_VEHICLE(targ) && (targ.flags & FL_NOTARGET))
        || (!autocvar_g_monsters_typefrag && PHYS_INPUT_BUTTON_CHAT(targ))
        || (SAME_TEAM(targ, this))
-       || (STAT(FROZEN, targ))
        || (targ.alpha != 0 && targ.alpha < 0.5)
        || (autocvar_g_monsters_lineofsight && !checkpvs(this.origin + this.view_ofs, targ)) // enemy cannot be seen
        || (MUTATOR_CALLHOOK(MonsterValidTarget, this, targ))
@@ -785,7 +784,7 @@ void Monster_Move(entity this, float runspeed, float walkspeed, float stpspeed)
        if(this.target2 && this.target2 != "" && this.goalentity.targetname != this.target2)
                this.goalentity = find(NULL, targetname, this.target2);
 
-       if(STAT(FROZEN, this))
+       if(StatusEffects_active(STATUSEFFECT_Frozen, this))
        {
                movelib_brake_simple(this, stpspeed);
                setanim(this, this.anim_idle, true, false, false);
@@ -957,7 +956,6 @@ void Monster_Remove(entity this)
                if(this.(weaponentity))
                        delete(this.(weaponentity));
        }
-       if(this.iceblock) { delete(this.iceblock); }
        WaypointSprite_Kill(this.sprite);
        delete(this);
 }
@@ -1008,8 +1006,6 @@ void Monster_Reset(entity this)
        setorigin(this, this.pos1);
        this.angles = this.pos2;
 
-       Unfreeze(this, false); // remove any icy remains
-
        SetResourceExplicit(this, RES_HEALTH, this.max_health);
        this.velocity = '0 0 0';
        this.enemy = NULL;
@@ -1043,9 +1039,6 @@ void Monster_Dead(entity this, entity attacker, float gibbed)
        this.nextthink = time;
        this.monster_lifetime = time + 5;
 
-       if(STAT(FROZEN, this))
-               Unfreeze(this, false); // remove any icy remains
-
        monster_dropitem(this, attacker);
 
        Monster_Sound(this, monstersound_death, 0, false, CH_VOICE);
@@ -1095,9 +1088,6 @@ void Monster_Damage(entity this, entity inflictor, entity attacker, float damage
        if((this.spawnflags & MONSTERFLAG_INVINCIBLE) && deathtype != DEATH_KILL.m_id && !ITEM_DAMAGE_NEEDKILL(deathtype))
                return;
 
-       if(STAT(FROZEN, this) && deathtype != DEATH_KILL.m_id && deathtype != DEATH_NADE_ICE_FREEZE.m_id)
-               return;
-
        //if(time < this.pain_finished && deathtype != DEATH_KILL.m_id)
                //return;
 
@@ -1246,7 +1236,7 @@ void Monster_Anim(entity this)
                deadbits = 0;
        }
        int animbits = deadbits;
-       if(STAT(FROZEN, this))
+       if(StatusEffects_active(STATUSEFFECT_Frozen, this))
                animbits |= ANIMSTATE_FROZEN;
        if(IS_DUCKED(this))
                animbits |= ANIMSTATE_DUCK; // not that monsters can crouch currently...
@@ -1263,43 +1253,6 @@ void Monster_Anim(entity this)
        */
 }
 
-void Monster_Frozen_Think(entity this)
-{
-       if (STAT(FROZEN, this) == FROZEN_TEMP_REVIVING)
-       {
-               STAT(REVIVE_PROGRESS, this) = bound(0, STAT(REVIVE_PROGRESS, this) + frametime * this.revive_speed, 1);
-               SetResourceExplicit(this, RES_HEALTH, max(1, STAT(REVIVE_PROGRESS, this) * this.max_health));
-               if (this.iceblock)
-                       this.iceblock.alpha = bound(0.2, 1 - STAT(REVIVE_PROGRESS, this), 1);
-
-               if(!(this.spawnflags & MONSTERFLAG_INVINCIBLE) && this.sprite)
-                       WaypointSprite_UpdateHealth(this.sprite, GetResource(this, RES_HEALTH));
-
-               if(STAT(REVIVE_PROGRESS, this) >= 1)
-                       Unfreeze(this, false);
-       }
-       else if (STAT(FROZEN, this) == FROZEN_TEMP_DYING)
-       {
-               STAT(REVIVE_PROGRESS, this) = bound(0, STAT(REVIVE_PROGRESS, this) - frametime * this.revive_speed, 1);
-               SetResourceExplicit(this, RES_HEALTH, max(0, autocvar_g_nades_ice_health + (this.max_health-autocvar_g_nades_ice_health) * STAT(REVIVE_PROGRESS, this)));
-
-               if(!(this.spawnflags & MONSTERFLAG_INVINCIBLE) && this.sprite)
-                       WaypointSprite_UpdateHealth(this.sprite, GetResource(this, RES_HEALTH));
-
-               if(GetResource(this, RES_HEALTH) < 1)
-               {
-                       Unfreeze(this, false);
-                       if(this.event_damage)
-                               this.event_damage(this, this, this.frozen_by, 1, DEATH_NADE_ICE_FREEZE.m_id, DMG_NOWEP, this.origin, '0 0 0');
-               }
-               else if ( STAT(REVIVE_PROGRESS, this) <= 0 )
-                       Unfreeze(this, false);
-       }
-       // otherwise, no revival!
-
-       this.enemy = NULL; // TODO: save enemy, and attack when revived?
-}
-
 void Monster_Enemy_Check(entity this)
 {
        if(this.enemy)
@@ -1309,6 +1262,7 @@ void Monster_Enemy_Check(entity this)
                WarpZone_TraceLine(this.origin, targ_origin, MOVE_NOMONSTERS, this);
 
                // cases where the enemy may have changed their state (don't need to check everything here)
+               // TODO: mutator hook
                if(    (IS_DEAD(this.enemy) || GetResource(this.enemy, RES_HEALTH) < 1)
                        || (STAT(FROZEN, this.enemy))
                        || (this.enemy.flags & FL_NOTARGET)
@@ -1351,8 +1305,11 @@ void Monster_Think(entity this)
                return;
        }
 
-       if(STAT(FROZEN, this))
-               Monster_Frozen_Think(this);
+       // TODO: mutator hook to control monster thinking
+       if(StatusEffects_active(STATUSEFFECT_Frozen, this))
+       {
+               this.enemy = NULL; // TODO: save enemy, and attack when revived?
+       }
        else if(time >= this.last_enemycheck)
        {
                Monster_Enemy_Check(this);
index cf4d890c1135cc2dcc7bb396b4fafcdbae1f816d..c75ac5d5739e07ce9df21d45873fb3152fb2e26a 100644 (file)
@@ -207,7 +207,6 @@ void buff_Touch(entity this, entity toucher)
                return; // incase mutator changed toucher
 
        if((this.team_forced && toucher.team != this.team_forced)
-       || (STAT(FROZEN, toucher))
        || (toucher.vehicle)
        || (!this.buffdef) // TODO: error out or maybe reset type if this occurs?
        || (time < toucher.buff_shield)
@@ -649,12 +648,6 @@ MUTATOR_HOOKFUNCTION(buffs, FilterItem)
        return false;
 }
 
-MUTATOR_HOOKFUNCTION(buffs, Freeze)
-{
-       entity targ = M_ARGV(0, entity);
-       buff_RemoveAll(targ, STATUSEFFECT_REMOVE_NORMAL);
-}
-
 MUTATOR_HOOKFUNCTION(buffs, PlayerPreThink)
 {
        entity player = M_ARGV(0, entity);
index 0daba58b19853907a2cc37a218916dce0c2c6ebb..2d679fad000bf00ba80b7428e9a6023fd7db7f8c 100644 (file)
@@ -3,11 +3,10 @@
 #ifdef SVQC
 void nade_ice_freeze(entity freezefield, entity frost_target, float freezetime)
 {
-       frost_target.frozen_by = freezefield.realowner;
        Send_Effect(EFFECT_ELECTRO_IMPACT, frost_target.origin, '0 0 0', 1);
-       Freeze(frost_target, 1 / freezetime, FROZEN_TEMP_DYING, false);
+       StatusEffects_apply(STATUSEFFECT_Frozen, frost_target, time + freezetime, 0);
 
-       Drop_Special_Items(frost_target);
+       //Drop_Special_Items(frost_target);
 }
 
 void nade_ice_think(entity this)
@@ -62,8 +61,9 @@ void nade_ice_think(entity this)
        float current_freeze_time = this.ltime - time - 0.1;
 
        FOREACH_ENTITY_RADIUS(this.origin, autocvar_g_nades_nade_radius, it != this && it.takedamage
-               && !IS_DEAD(it) && GetResource(it, RES_HEALTH) > 0 && current_freeze_time > 0
-               && (!it.revival_time || ((time - it.revival_time) >= 1.5)) && !STAT(FROZEN, it),
+               && (IS_PLAYER(it) || IS_MONSTER(it)) && !IS_DEAD(it) && GetResource(it, RES_HEALTH) > 0 && current_freeze_time > 0
+               && (!it.revival_time || ((time - it.revival_time) >= 1.5))
+               && !STAT(FROZEN, it) && !StatusEffects_active(STATUSEFFECT_Frozen, it),
        {
                switch (autocvar_g_nades_ice_teamcheck)
                {
index ed64a8303e36f58d16243b96362b949c939b71c4..9435c8d2a4dc04616e06c6220fc4a1cf00808774 100644 (file)
@@ -5,7 +5,6 @@
 #ifdef SVQC
 bool autocvar_g_nades_ice = true;
 float autocvar_g_nades_ice_freeze_time;
-float autocvar_g_nades_ice_health;
 bool autocvar_g_nades_ice_explode;
 bool autocvar_g_nades_ice_teamcheck;
 
index b97ea67e5d99f297208aba1500ac1051c171627e..0dc520c91f4e721cbcad132c77df4adbb220d62f 100644 (file)
@@ -708,15 +708,6 @@ MUTATOR_HOOKFUNCTION(nades, ForbidThrowCurrentWeapon, CBC_ORDER_LAST)
        }
 }
 
-#ifdef IN_REVIVING_RANGE
-       #undef IN_REVIVING_RANGE
-#endif
-
-// returns true if player is reviving it
-#define IN_REVIVING_RANGE(player, it, revive_extra_size) \
-       (it != player && !IS_DEAD(it) && SAME_TEAM(it, player) \
-       && boxesoverlap(player.absmin - revive_extra_size, player.absmax + revive_extra_size, it.absmin, it.absmax))
-
 MUTATOR_HOOKFUNCTION(nades, PlayerPreThink)
 {
        entity player = M_ARGV(0, entity);
@@ -781,64 +772,6 @@ MUTATOR_HOOKFUNCTION(nades, PlayerPreThink)
 
                nade_veil_Apply(player);
        }
-
-       if (!(frametime && IS_PLAYER(player)))
-               return true;
-
-       entity revivers_last = NULL;
-       entity revivers_first = NULL;
-
-       bool player_is_reviving = false;
-       int n = 0;
-       vector revive_extra_size = '1 1 1' * autocvar_g_freezetag_revive_extra_size;
-       FOREACH_CLIENT(IS_PLAYER(it) && IN_REVIVING_RANGE(player, it, revive_extra_size), {
-               // check if player is reviving anyone
-               if (STAT(FROZEN, it) == FROZEN_TEMP_DYING)
-               {
-                       if ((STAT(FROZEN, player) == FROZEN_TEMP_DYING))
-                               continue;
-                       if (!IN_REVIVING_RANGE(player, it, revive_extra_size))
-                               continue;
-                       player_is_reviving = true;
-                       break;
-               }
-
-               if (!(STAT(FROZEN, player) == FROZEN_TEMP_DYING))
-                       continue; // both player and it are NOT frozen
-               if (revivers_last)
-                       revivers_last.chain = it;
-               revivers_last = it;
-               if (!revivers_first)
-                       revivers_first = it;
-               ++n;
-       });
-       if (revivers_last)
-               revivers_last.chain = NULL;
-
-       if (!n) // no teammate nearby
-       {
-               // freezetag already resets revive progress
-               if (!g_freezetag && !STAT(FROZEN, player) && !player_is_reviving)
-                       STAT(REVIVE_PROGRESS, player) = 0; // thawing nobody
-       }
-       else if (n > 0 && STAT(FROZEN, player) == FROZEN_TEMP_DYING) // 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);
-               // undo what PlayerPreThink did
-               STAT(REVIVE_PROGRESS, player) = bound(0, STAT(REVIVE_PROGRESS, player) + frametime * player.revive_speed, 1);
-               SetResource(player, RES_HEALTH, max(1, STAT(REVIVE_PROGRESS, player) * start_health));
-
-               if(STAT(REVIVE_PROGRESS, player) >= 1)
-               {
-                       Unfreeze(player, false);
-
-                       Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_FREEZETAG_REVIVED, revivers_first.netname);
-                       Send_Notification(NOTIF_ONE, revivers_first, MSG_CENTER, CENTER_FREEZETAG_REVIVE, player.netname);
-               }
-
-               for(entity it = revivers_first; it; it = it.chain)
-                       STAT(REVIVE_PROGRESS, it) = STAT(REVIVE_PROGRESS, player);
-       }
 }
 
 MUTATOR_HOOKFUNCTION(nades, PlayerSpawn)
@@ -920,7 +853,7 @@ MUTATOR_HOOKFUNCTION(nades, Damage_Calculate)
        if(autocvar_g_freezetag_revive_nade && STAT(FROZEN, frag_target) && frag_attacker == frag_target && frag_deathtype == DEATH_NADE.m_id)
        if(time - frag_inflictor.toss_time <= 0.1)
        {
-               Unfreeze(frag_target, false);
+               freezetag_Unfreeze(frag_target, false);
                SetResource(frag_target, RES_HEALTH, autocvar_g_freezetag_revive_nade_health);
                Send_Effect(EFFECT_ICEORGLASS, frag_target.origin, '0 0 0', 3);
                M_ARGV(4, float) = 0;
index fd8a8bdc2d00a6415dfea7b58490fb4eb2e652a9..d6739ab36fd22e8d207a074d2d64112f81b94f2e 100644 (file)
@@ -138,7 +138,7 @@ MUTATOR_HOOKFUNCTION(ok, PlayerPreThink)
                return;
        }
        entity player = M_ARGV(0, entity);
-       if (!IS_PLAYER(player) || IS_DEAD(player) || STAT(FROZEN, player))
+       if (!IS_PLAYER(player) || IS_DEAD(player))
        {
                return;
        }
index 4c68c1bad6f4bca301bd3379d944e09ef0658853..d0e368edb15c6b67def860d0c77071b00892ad15 100644 (file)
@@ -2,6 +2,9 @@
 
 METHOD(StatusEffect, m_active, bool(StatusEffect this, entity actor))
 {
+       // TODO: allow per-entity status effects on the client side
+       actor = g_statuseffects;
+
        if(!actor) return false;
        TC(StatusEffect, this);
        return (actor.statuseffect_flags[this.m_id] & STATUSEFFECT_FLAG_ACTIVE);
index 41349d1e0a46067b3bbdaf461d409c2f22d6b65a..720c8bf736de17eede5c007c895e118141034959 100644 (file)
@@ -1,5 +1,6 @@
 // genmod.sh autogenerated file; do not modify
 #include <common/mutators/mutator/status_effects/status_effect/burning.qc>
+#include <common/mutators/mutator/status_effects/status_effect/frozen.qc>
 #include <common/mutators/mutator/status_effects/status_effect/spawnshield.qc>
 #include <common/mutators/mutator/status_effects/status_effect/stunned.qc>
 #include <common/mutators/mutator/status_effects/status_effect/superweapons.qc>
index ad12a0f297b2d4d3373f1d7f6426d931bb577879..a75df6058ceeeea4cac491a54109639fc16fbc6e 100644 (file)
@@ -1,5 +1,6 @@
 // genmod.sh autogenerated file; do not modify
 #include <common/mutators/mutator/status_effects/status_effect/burning.qh>
+#include <common/mutators/mutator/status_effects/status_effect/frozen.qh>
 #include <common/mutators/mutator/status_effects/status_effect/spawnshield.qh>
 #include <common/mutators/mutator/status_effects/status_effect/stunned.qh>
 #include <common/mutators/mutator/status_effects/status_effect/superweapons.qh>
diff --git a/qcsrc/common/mutators/mutator/status_effects/status_effect/frozen.qc b/qcsrc/common/mutators/mutator/status_effects/status_effect/frozen.qc
new file mode 100644 (file)
index 0000000..ef45a8b
--- /dev/null
@@ -0,0 +1,78 @@
+#include "frozen.qh"
+
+#ifdef SVQC
+.entity frozen_ice;
+
+void Frozen_ice_remove(entity this)
+{
+    if(this.frozen_ice)
+        delete(this.frozen_ice);
+    this.frozen_ice = NULL;
+}
+
+void Frozen_ice_think(entity this)
+{
+    if(this.owner.frozen_ice != this || wasfreed(this.owner))
+    {
+        delete(this);
+        return;
+    }
+    vector ice_org = this.owner.origin - '0 0 16';
+    if(this.origin != ice_org)
+        setorigin(this, ice_org);
+    this.nextthink = time;
+}
+
+void Frozen_ice_create(entity this)
+{
+    entity ice = new(ice);
+    // set_movetype(ice, MOVETYPE_FOLLOW) would rotate the ice model with the player
+    ice.owner = this;
+    ice.scale = this.scale;
+    setthink(ice, Frozen_ice_think);
+    ice.nextthink = time;
+    ice.frame = floor(random() * 21); // ice model has 20 different looking frames
+    // TODO: unique (less pronounced) model
+    setmodel(ice, MDL_ICE);
+    ice.alpha = 0.5;
+
+    this.frozen_ice = ice;
+
+    Frozen_ice_think(ice);
+}
+
+METHOD(Frozen, m_apply, void(StatusEffect this, entity actor, float eff_time, float eff_flags))
+{
+    bool wasactive = (actor.statuseffects && (actor.statuseffects.statuseffect_flags[this.m_id] & STATUSEFFECT_FLAG_ACTIVE));
+    if(!wasactive)
+    {
+        Frozen_ice_create(actor);
+        RemoveGrapplingHooks(actor);
+        // TODO: should hooks targeting this entity also be removed?
+        // TODO: special items as well?
+    }
+    SUPER(Frozen).m_apply(this, actor, eff_time, eff_flags);
+}
+METHOD(Frozen, m_remove, void(StatusEffect this, entity actor, int removal_type))
+{
+    Frozen_ice_remove(actor);
+    SUPER(Frozen).m_remove(this, actor, removal_type);
+}
+METHOD(Frozen, m_tick, void(StatusEffect this, entity actor))
+{
+    if(STAT(FROZEN, actor) || (actor.waterlevel && actor.watertype == CONTENT_LAVA))
+    {
+        this.m_remove(this, actor, STATUSEFFECT_REMOVE_NORMAL);
+        return;
+    }
+    SUPER(Frozen).m_tick(this, actor);
+}
+#endif
+#ifdef CSQC
+METHOD(Frozen, m_tick, void(StatusEffect this, entity actor))
+{
+    vector col = '0.25 0.90 1';
+    float alpha_fade = 0.3;
+    drawfill('0 0 0', vec2(vid_conwidth, vid_conheight), col, autocvar_hud_colorflash_alpha * alpha_fade, DRAWFLAG_ADDITIVE);
+}
+#endif
diff --git a/qcsrc/common/mutators/mutator/status_effects/status_effect/frozen.qh b/qcsrc/common/mutators/mutator/status_effects/status_effect/frozen.qh
new file mode 100644 (file)
index 0000000..be57a0a
--- /dev/null
@@ -0,0 +1,11 @@
+#pragma once
+
+#include <common/mutators/mutator/status_effects/all.qh>
+
+CLASS(Frozen, StatusEffect)
+    ATTRIB(Frozen, netname, string, "frozen");
+    ATTRIB(Frozen, m_color, vector, '0 0.62 1');
+    ATTRIB(Frozen, m_hidden, bool, true);
+    ATTRIB(Frozen, m_lifetime, float, 10);
+ENDCLASS(Frozen)
+REGISTER_STATUSEFFECT(Frozen, NEW(Frozen));
index 3f6d26a9e1c214901edc13d6656c6653081bfd92..51f15ecbe452555ddfbfbf982af5f059a0c20ee3 100644 (file)
@@ -121,7 +121,7 @@ void StatusEffects_Write(StatusEffect data, StatusEffect store)
 #undef G_MINOR
 
 #ifdef SVQC
-bool StatusEffects_Send(StatusEffect this, Client to, int sf)
+bool StatusEffects_Send(StatusEffect this, entity to, int sf)
 {
     TC(StatusEffect, this);
     WriteHeader(MSG_ENTITY, ENT_CLIENT_STATUSEFFECTS);
index 97b1cec68804a15fcd05117627be4f1a6960e72a..722a00d1ad883bafe5915daa784942ff69eb7c02 100644 (file)
@@ -70,6 +70,13 @@ MUTATOR_HOOKFUNCTION(status_effects, PlayerDies)
        StatusEffects_removeall(frag_target, STATUSEFFECT_REMOVE_NORMAL);
 }
 
+MUTATOR_HOOKFUNCTION(status_effects, MonsterDies)
+{
+       entity frag_target = M_ARGV(0, entity);
+
+       StatusEffects_removeall(frag_target, STATUSEFFECT_REMOVE_NORMAL);
+}
+
 MUTATOR_HOOKFUNCTION(status_effects, ClientDisconnect)
 {
        entity player = M_ARGV(0, entity);
index 519159d917b386b9488261845caeed486f4f378c..9707138c375dd2eb01b4cb92cea42e1d64f574f1 100644 (file)
@@ -42,7 +42,7 @@ MUTATOR_HOOKFUNCTION(walljump, PlayerJump)
        if(!IS_ONGROUND(player))
        if(player.move_movetype != MOVETYPE_NONE && player.move_movetype != MOVETYPE_FOLLOW && player.move_movetype != MOVETYPE_FLY && player.move_movetype != MOVETYPE_NOCLIP)
        if(!IS_JUMP_HELD(player))
-       if(!STAT(FROZEN, player))
+       if(!STAT(FROZEN, player) && !StatusEffects_active(STATUSEFFECT_Frozen, player))
        if(!IS_DEAD(player))
        {
                vector plane_normal = PlayerTouchWall(player);
index 836fef289551cffc3ecabd6652813d3cd8c5db97..1e475b1689d96ed55479d991e09afd85bb4870ec 100644 (file)
@@ -284,7 +284,6 @@ string multiteam_info_sprintf(string input, string teamname) { return ((input !=
     MSG_INFO_NOTIF(DEATH_MURDER_NADE,                       N_CONSOLE,  3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",               "nade_normal",          _("^BG%s%s^K1 was blown up by ^BG%s^K1's Nade%s%s"), "")
     MSG_INFO_NOTIF(DEATH_MURDER_NADE_NAPALM,                N_CONSOLE,  3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",               "nade_napalm",          _("^BG%s%s^K1 was burned to death by ^BG%s^K1's Napalm Nade%s%s"), _("^BG%s%s^K1 got too close to a napalm explosion%s%s"))
     MSG_INFO_NOTIF(DEATH_MURDER_NADE_ICE,                   N_CONSOLE,  3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",               "nade_ice",             _("^BG%s%s^K1 was blown up by ^BG%s^K1's Ice Nade%s%s"), "")
-    MSG_INFO_NOTIF(DEATH_MURDER_NADE_ICE_FREEZE,            N_CONSOLE,  3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",               "nade_ice",             _("^BG%s%s^K1 was frozen to death by ^BG%s^K1's Ice Nade%s%s"), "")
     MSG_INFO_NOTIF(DEATH_MURDER_NADE_HEAL,                  N_CONSOLE,  3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",               "nade_heal",            _("^BG%s%s^K1 has not been healed by ^BG%s^K1's Healing Nade%s%s"), "")
     MSG_INFO_NOTIF(DEATH_MURDER_SHOOTING_STAR,              N_CONSOLE,  3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",               "notify_shootingstar",  _("^BG%s%s^K1 was shot into space by ^BG%s^K1%s%s"), "")
     MSG_INFO_NOTIF(DEATH_MURDER_SLIME,                      N_CONSOLE,  3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1",               "notify_slime",         _("^BG%s%s^K1 was slimed by ^BG%s^K1%s%s"), "")
@@ -327,7 +326,6 @@ string multiteam_info_sprintf(string input, string teamname) { return ((input !=
     MSG_INFO_NOTIF(DEATH_SELF_NADE,                         N_CONSOLE,  2, 1, "s1 s2loc spree_lost", "s1",      "nade_normal",          _("^BG%s^K1 mastered the art of self-nading%s%s"), "")
     MSG_INFO_NOTIF(DEATH_SELF_NADE_NAPALM,                  N_CONSOLE,  2, 1, "s1 s2loc spree_lost", "s1",      "nade_napalm",          _("^BG%s^K1 was burned to death by their own Napalm Nade%s%s"), _("^BG%s^K1 decided to take a look at the results of their napalm explosion%s%s"))
     MSG_INFO_NOTIF(DEATH_SELF_NADE_ICE,                     N_CONSOLE,  2, 1, "s1 s2loc spree_lost", "s1",      "nade_ice",             _("^BG%s^K1 mastered the art of self-nading%s%s"), "")
-    MSG_INFO_NOTIF(DEATH_SELF_NADE_ICE_FREEZE,              N_CONSOLE,  2, 1, "s1 s2loc spree_lost", "s1",      "nade_ice",             _("^BG%s^K1 was frozen to death by their own Ice Nade%s%s"), _("^BG%s^K1 felt a little chilly%s%s"))
     MSG_INFO_NOTIF(DEATH_SELF_NADE_HEAL,                    N_CONSOLE,  2, 1, "s1 s2loc spree_lost", "s1",      "nade_heal",            _("^BG%s^K1's Healing Nade didn't quite heal them%s%s"), "")
     MSG_INFO_NOTIF(DEATH_SELF_NOAMMO,                       N_CONSOLE,  2, 1, "s1 s2loc spree_lost", "s1",      "notify_outofammo",     _("^BG%s^K1 died%s%s. What's the point of living without ammo?"), _("^BG%s^K1 ran out of ammo%s%s"))
     MSG_INFO_NOTIF(DEATH_SELF_ROT,                          N_CONSOLE,  2, 1, "s1 s2loc spree_lost", "s1",      "notify_death",         _("^BG%s^K1 rotted away%s%s"), "")
@@ -660,7 +658,6 @@ string multiteam_info_sprintf(string input, string teamname) { return ((input !=
     MSG_CENTER_NOTIF(DEATH_SELF_MONSTER,                N_ENABLE,    0, 0, "",               CPID_Null,              "0 0",  BOLD(_("^K1You were killed by a monster!")), BOLD(_("^K1You need to watch out for monsters!")))
     MSG_CENTER_NOTIF(DEATH_SELF_NADE,                   N_ENABLE,    0, 0, "",               CPID_Null,              "0 0",  BOLD(_("^K1You forgot to put the pin back in!")), BOLD(_("^K1Tastes like chicken!")))
     MSG_CENTER_NOTIF(DEATH_SELF_NADE_NAPALM,            N_ENABLE,    0, 0, "",               CPID_Null,              "0 0",  BOLD(_("^K1Hanging around a napalm explosion is bad!")), "")
-    MSG_CENTER_NOTIF(DEATH_SELF_NADE_ICE_FREEZE,        N_ENABLE,    0, 0, "",               CPID_Null,              "0 0",  BOLD(_("^K1You got a little bit too cold!")), BOLD(_("^K1You felt a little chilly!")))
     MSG_CENTER_NOTIF(DEATH_SELF_NADE_HEAL,              N_ENABLE,    0, 0, "",               CPID_Null,              "0 0",  BOLD(_("^K1Your Healing Nade is a bit defective")), "")
     MSG_CENTER_NOTIF(DEATH_SELF_NOAMMO,                 N_ENABLE,    0, 0, "",               CPID_Null,              "0 0",  BOLD(_("^K1You were killed for running out of ammo...")), BOLD(_("^K1You are respawning for running out of ammo...")))
     MSG_CENTER_NOTIF(DEATH_SELF_ROT,                    N_ENABLE,    0, 0, "",               CPID_Null,              "0 0",  BOLD(_("^K1You grew too old without taking your medicine")), BOLD(_("^K1You need to preserve your health")))
@@ -864,7 +861,6 @@ string multiteam_info_sprintf(string input, string teamname) { return ((input !=
     MSG_MULTI_NOTIF(DEATH_MURDER_NADE,                  N_ENABLE,  NULL,           INFO_DEATH_MURDER_NADE,                 NULL)
     MSG_MULTI_NOTIF(DEATH_MURDER_NADE_NAPALM,           N_ENABLE,  NULL,           INFO_DEATH_MURDER_NADE_NAPALM,          NULL)
     MSG_MULTI_NOTIF(DEATH_MURDER_NADE_ICE,              N_ENABLE,  NULL,           INFO_DEATH_MURDER_NADE_ICE,             NULL)
-    MSG_MULTI_NOTIF(DEATH_MURDER_NADE_ICE_FREEZE,       N_ENABLE,  NULL,           INFO_DEATH_MURDER_NADE_ICE_FREEZE,      NULL)
     MSG_MULTI_NOTIF(DEATH_MURDER_NADE_HEAL,             N_ENABLE,  NULL,           INFO_DEATH_MURDER_NADE_HEAL,            NULL)
     MSG_MULTI_NOTIF(DEATH_MURDER_SHOOTING_STAR,         N_ENABLE,  NULL,           INFO_DEATH_MURDER_SHOOTING_STAR,        NULL)
     MSG_MULTI_NOTIF(DEATH_MURDER_SLIME,                 N_ENABLE,  NULL,           INFO_DEATH_MURDER_SLIME,                NULL)
@@ -906,8 +902,7 @@ string multiteam_info_sprintf(string input, string teamname) { return ((input !=
     MSG_MULTI_NOTIF(DEATH_SELF_MON_ZOMBIE_MELEE,        N_ENABLE,  NULL,           INFO_DEATH_SELF_MON_ZOMBIE_MELEE,       CENTER_DEATH_SELF_MONSTER)
     MSG_MULTI_NOTIF(DEATH_SELF_NADE,                    N_ENABLE,  NULL,           INFO_DEATH_SELF_NADE,                   CENTER_DEATH_SELF_NADE)
     MSG_MULTI_NOTIF(DEATH_SELF_NADE_NAPALM,             N_ENABLE,  NULL,           INFO_DEATH_SELF_NADE_NAPALM,            CENTER_DEATH_SELF_NADE_NAPALM)
-    MSG_MULTI_NOTIF(DEATH_SELF_NADE_ICE,                N_ENABLE,  NULL,           INFO_DEATH_SELF_NADE_ICE,               CENTER_DEATH_SELF_NADE_ICE_FREEZE)
-    MSG_MULTI_NOTIF(DEATH_SELF_NADE_ICE_FREEZE,         N_ENABLE,  NULL,           INFO_DEATH_SELF_NADE_ICE_FREEZE,        CENTER_DEATH_SELF_NADE_ICE_FREEZE)
+    MSG_MULTI_NOTIF(DEATH_SELF_NADE_ICE,                N_ENABLE,  NULL,           INFO_DEATH_SELF_NADE_ICE,               CENTER_DEATH_SELF_NADE)
     MSG_MULTI_NOTIF(DEATH_SELF_NADE_HEAL,               N_ENABLE,  NULL,           INFO_DEATH_SELF_NADE_HEAL,              CENTER_DEATH_SELF_NADE_HEAL)
     MSG_MULTI_NOTIF(DEATH_SELF_NOAMMO,                  N_ENABLE,  NULL,           INFO_DEATH_SELF_NOAMMO,                 CENTER_DEATH_SELF_NOAMMO)
     MSG_MULTI_NOTIF(DEATH_SELF_ROT,                     N_ENABLE,  NULL,           INFO_DEATH_SELF_ROT,                    CENTER_DEATH_SELF_ROT)
index b007afaf1875342cfd2b5afa988ba81603bf8320..71279afd62e205b7321571dbbf501d62adfd4297 100644 (file)
@@ -186,7 +186,7 @@ void PM_ClientMovement_UpdateStatus(entity this)
                //do_crouch = false;
        } else if (PHYS_INVEHICLE(this)) {
                do_crouch = false;
-       } else if (STAT(FROZEN, this) || IS_DEAD(this)) {
+       } else if (PHYS_FROZEN(this) || IS_DEAD(this)) {
                do_crouch = false;
        }
 
index e2e907767992baeb7f156836548a78329cff9300..c6a2112dd2fb3ab5e546ee1eea056360e3fc306a 100644 (file)
@@ -113,7 +113,7 @@ REPLICATE_INIT(bool, cvar_cl_movement_track_canjump);
 #define PHYS_FRICTION_ONLAND(s)             STAT(MOVEVARS_FRICTION_ONLAND, s)
 #define PHYS_FRICTION_SLICK(s)              STAT(MOVEVARS_FRICTION_SLICK, s)
 
-#define PHYS_FROZEN(s)                      STAT(FROZEN, s)
+#define PHYS_FROZEN(s)                      (STAT(FROZEN, s) || StatusEffects_active(STATUSEFFECT_Frozen, s))
 
 #define PHYS_HIGHSPEED(s)                   STAT(MOVEVARS_HIGHSPEED, s)
 
index befeffc176212831cc924a6d986dc2f6ec2984ac..7bbc4ca6a0dbbb1f574952c40a99c4f674762b08 100644 (file)
@@ -126,7 +126,7 @@ REGISTER_STAT(NADE_BONUS, FLOAT)
 REGISTER_STAT(NADE_BONUS_TYPE, INT)
 REGISTER_STAT(NADE_BONUS_SCORE, FLOAT)
 REGISTER_STAT(NADE_DARKNESS_TIME, FLOAT)
-REGISTER_STAT(FROZEN, INT)
+REGISTER_STAT(FROZEN, BOOL)
 REGISTER_STAT(REVIVE_PROGRESS, FLOAT)
 REGISTER_STAT(ROUNDLOST, INT)
 REGISTER_STAT(CAPTURE_PROGRESS, FLOAT)
index 9a9a902c27cf6ed60170ecef8928b8fb65043913..3545e734c8659931b4c94879b859368f07e2c1ca 100644 (file)
@@ -716,8 +716,6 @@ float turret_validate_target(entity e_turret, entity e_target, float validate_fl
        // Cant touch this
        if (GetResource(e_target, RES_HEALTH) <= 0)
                return -6;
-       else if (STAT(FROZEN, e_target))
-               return -6;
 
        // vehicle
        if(IS_VEHICLE(e_target))
index f18d867c60876c0f6b32d1ba9b5c130ba2d6074d..c24bb677c96fc33e64ed1ed27ef2a017fd08d801 100644 (file)
@@ -941,10 +941,11 @@ void vehicles_enter(entity pl, entity veh)
        if((IS_BOT_CLIENT(pl) && !autocvar_g_vehicles_allow_bots))
                return;
 
+       // TODO: mutator hook to prevent entering vehicles
        if((!IS_PLAYER(pl))
        || (veh.phase >= time)
        || (pl.vehicle_enter_delay >= time)
-       || (STAT(FROZEN, pl))
+       || (STAT(FROZEN, pl) || StatusEffects_active(STATUSEFFECT_Frozen, pl))
        || (IS_DEAD(pl))
        || (pl.vehicle)
        ) { return; }
index 6e6cc8a689cf4df0aa336e4e5abc9c8c2793afef..c2a0a7c7bb5fe3e14fb8bbcf2e311bd4a0155c35 100644 (file)
@@ -107,7 +107,7 @@ void W_Fireball_LaserPlay(entity this, float dt, float dist, float damage, float
        RandomSelection_Init();
        for(e = WarpZone_FindRadius(this.origin, dist, true); e; e = e.chain)
        {
-               if(STAT(FROZEN, e)) continue;
+               if(STAT(FROZEN, e) || StatusEffects_active(STATUSEFFECT_Frozen, e)) continue;
                if(e == this.realowner) continue;
                if(IS_INDEPENDENT_PLAYER(e)) continue;
                if(e.takedamage != DAMAGE_AIM) continue;
index 0a02e50d1d9df85705a39d58ecd2a52b726f3d83..985cffebb07337884b2378a45eaf4efc8a76103c 100644 (file)
@@ -105,9 +105,6 @@ bool bot_shouldattack(entity this, entity targ)
                        return false;
        }
 
-       if(STAT(FROZEN, targ))
-               return false;
-
        if(teamplay)
        {
                if(targ.team==0)
index 5a9ef8f6286ff01afafbb8073fdc56f98dfd6785..802ccc06bf6186debacbf71751e652c6355171ef 100644 (file)
@@ -57,7 +57,7 @@ void havocbot_ai(entity this)
                }
                else
                {
-                       if (!this.jumppadcount && !STAT(FROZEN, this)
+                       if (!this.jumppadcount && !STAT(FROZEN, this) && !StatusEffects_active(STATUSEFFECT_Frozen, this)
                                && !(this.goalcurrent_prev && (this.goalcurrent_prev.wpflags & WAYPOINTFLAG_JUMP) && !IS_ONGROUND(this)))
                        {
                                // find a new goal
@@ -66,7 +66,7 @@ void havocbot_ai(entity this)
                }
 
                // if we don't have a goal and we're under water look for a waypoint near the "shore" and push it
-               if(!(IS_DEAD(this) || STAT(FROZEN, this)))
+               if(!(IS_DEAD(this) || STAT(FROZEN, this) || StatusEffects_active(STATUSEFFECT_Frozen, this)))
                if(!this.goalcurrent)
                if(this.waterlevel == WATERLEVEL_SWIMMING || (this.aistatus & AI_STATUS_OUT_WATER))
                {
@@ -110,7 +110,7 @@ void havocbot_ai(entity this)
                return;
        }
 
-       if(IS_DEAD(this) || STAT(FROZEN, this))
+       if(IS_DEAD(this) || STAT(FROZEN, this) || StatusEffects_active(STATUSEFFECT_Frozen, this))
        {
                if (this.goalcurrent)
                        navigation_clearroute(this);
index 8fc33448b9401cb5c1f5faf6f246c0c6b3429d28..c34af9389cb30fe8518680f4966a546ea922f4a3 100644 (file)
@@ -5,6 +5,7 @@
 #include <common/mapobjects/func/ladder.qh>
 #include <common/mapobjects/trigger/hurt.qh>
 #include <common/mapobjects/trigger/jumppads.qh>
+#include <common/mutators/mutator/status_effects/_mod.qh>
 #include <common/net_linked.qh>
 #include <common/stats.qh>
 #include <common/weapons/_all.qh>
index b3a9a806ccc7a65efe2a457ae9b2e3cf8a98884f..1f35e1503bfeb1874064ba07ab994fc336147f14 100644 (file)
@@ -312,7 +312,6 @@ void PutObserverInServer(entity this, bool is_forced, bool use_spawnpoint)
 
        RemoveGrapplingHooks(this);
        Portal_ClearAll(this);
-       Unfreeze(this, false);
        SetSpectatee(this, NULL);
 
        if (this.alivetime)
@@ -376,8 +375,6 @@ void PutObserverInServer(entity this, bool is_forced, bool use_spawnpoint)
        this.nextthink = 0;
        this.deadflag = DEAD_NO;
        UNSET_DUCKED(this);
-       STAT(REVIVE_PROGRESS, this) = 0;
-       this.revival_time = 0;
        this.draggable = drag_undraggable;
 
        player_powerups_remove_all(this, was_player);
@@ -690,9 +687,6 @@ void PutPlayerInServer(entity this)
        this.punchangle = '0 0 0';
        this.punchvector = '0 0 0';
 
-       STAT(REVIVE_PROGRESS, this) = 0;
-       this.revival_time = 0;
-
        STAT(AIR_FINISHED, this) = 0;
        this.waterlevel = WATERLEVEL_NONE;
        this.watertype = CONTENT_EMPTY;
@@ -795,8 +789,6 @@ void PutPlayerInServer(entity this)
                }
        });
 
-       Unfreeze(this, false);
-
        MUTATOR_CALLHOOK(PlayerSpawn, this, spot);
        {
                string s = spot.target;
@@ -1293,8 +1285,6 @@ void ClientDisconnect(entity this)
 
        Portal_ClearAll(this);
 
-       Unfreeze(this, false);
-
        RemoveGrapplingHooks(this);
 
        strfree(this.shootfromfixedorigin);
@@ -1697,7 +1687,6 @@ void player_regen(entity this)
        float rotstable, regenstable, rotframetime, regenframetime;
 
        if(!mutator_returnvalue)
-       if(!STAT(FROZEN, this))
        {
                regenstable = autocvar_g_balance_armor_regenstable;
                rotstable = autocvar_g_balance_armor_rotstable;
@@ -1813,8 +1802,6 @@ void SpectateCopy(entity this, entity spectatee)
        this.dmg_inflictor = spectatee.dmg_inflictor;
        this.v_angle = spectatee.v_angle;
        this.angles = spectatee.v_angle;
-       STAT(FROZEN, this) = STAT(FROZEN, spectatee);
-       STAT(REVIVE_PROGRESS, this) = STAT(REVIVE_PROGRESS, spectatee);
        this.viewloc = spectatee.viewloc;
        if(!PHYS_INPUT_BUTTON_USE(this) && STAT(CAMERA_SPECTATOR, this) != 2)
                this.fixangle = true;
@@ -2563,7 +2550,7 @@ void PlayerUseKey(entity this)
        }
        else if(autocvar_g_vehicles_enter)
        {
-               if(!game_stopped && !STAT(FROZEN, this) && !IS_DEAD(this) && !IS_INDEPENDENT_PLAYER(this))
+               if(!game_stopped && !STAT(FROZEN, this) && !StatusEffects_active(STATUSEFFECT_Frozen, this) && !IS_DEAD(this) && !IS_INDEPENDENT_PLAYER(this))
                {
                        entity head, closest_target = NULL;
                        head = WarpZone_FindRadius(this.origin, autocvar_g_vehicles_enter_radius, true);
@@ -2683,6 +2670,7 @@ void PlayerPreThink (entity this)
 
 void DrownPlayer(entity this)
 {
+       // TODO: mutator hook to prevent drowning?
        if(IS_DEAD(this) || game_stopped || time < game_starttime || this.vehicle
                || STAT(FROZEN, this) || this.watertype != CONTENT_WATER)
        {
@@ -2863,39 +2851,9 @@ void PlayerFrame (entity this)
                this.max_armorvalue = 0;
        }
 
-       // FreezeTag
-       if (IS_PLAYER(this) && time >= game_starttime)
-       {
-               if (STAT(FROZEN, this) == FROZEN_TEMP_REVIVING)
-               {
-                       STAT(REVIVE_PROGRESS, this) = bound(0, STAT(REVIVE_PROGRESS, this) + frametime * this.revive_speed, 1);
-                       SetResourceExplicit(this, RES_HEALTH, max(1, STAT(REVIVE_PROGRESS, this) * start_health));
-                       if (this.iceblock)
-                               this.iceblock.alpha = bound(0.2, 1 - STAT(REVIVE_PROGRESS, this), 1);
-
-                       if (STAT(REVIVE_PROGRESS, this) >= 1)
-                               Unfreeze(this, false);
-               }
-               else if (STAT(FROZEN, this) == FROZEN_TEMP_DYING)
-               {
-                       STAT(REVIVE_PROGRESS, this) = bound(0, STAT(REVIVE_PROGRESS, this) - frametime * this.revive_speed, 1);
-                       SetResourceExplicit(this, RES_HEALTH, max(0, autocvar_g_nades_ice_health + (start_health-autocvar_g_nades_ice_health) * STAT(REVIVE_PROGRESS, this)));
-
-                       if (GetResource(this, RES_HEALTH) < 1)
-                       {
-                               if (this.vehicle)
-                                       vehicles_exit(this.vehicle, VHEF_RELEASE);
-                               if(this.event_damage)
-                                       this.event_damage(this, this, this.frozen_by, 1, DEATH_NADE_ICE_FREEZE.m_id, DMG_NOWEP, this.origin, '0 0 0');
-                       }
-                       else if (STAT(REVIVE_PROGRESS, this) <= 0)
-                               Unfreeze(this, false);
-               }
-       }
-
        // Vehicles
        if(autocvar_g_vehicles_enter && (time > this.last_vehiclecheck) && !game_stopped && !this.vehicle)
-       if(IS_PLAYER(this) && !STAT(FROZEN, this) && !IS_DEAD(this) && !IS_INDEPENDENT_PLAYER(this))
+       if(IS_PLAYER(this) && !STAT(FROZEN, this) && !StatusEffects_active(STATUSEFFECT_Frozen, this) && !IS_DEAD(this) && !IS_INDEPENDENT_PLAYER(this))
        {
                FOREACH_ENTITY_RADIUS(this.origin, autocvar_g_vehicles_enter_radius, IS_VEHICLE(it) && !IS_DEAD(it) && it.takedamage != DAMAGE_NO,
                {
index 92bbfde4bb335f5a4aa85fe1a816c99fe33cf2e6..3d04c13c0854054971c07a4ca883897a163dcf91 100644 (file)
@@ -236,7 +236,7 @@ void ClientKill_Silent(entity this, float _delay)
 // Called when a client types 'kill' in the console
 void ClientKill(entity this)
 {
-       if (game_stopped || this.player_blocked || STAT(FROZEN, this))
+       if (game_stopped || this.player_blocked)
                return;
 
        ClientKill_TeamChange(this, 0);
index a197312c51a9046561b102e14c24a1010f7a40b3..bc2e655de872e6e7081685f66fad490a7c8dca75 100644 (file)
@@ -369,7 +369,6 @@ void CommonCommand_editmob(int request, entity caller, int argc)
                                        if (!IS_PLAYER(caller)) { print_to(caller, "You must be playing to spawn a monster"); return; }
                                        if (MUTATOR_CALLHOOK(AllowMobSpawning, caller)) { print_to(caller, M_ARGV(1, string)); return; }
                                        if (caller.vehicle) { print_to(caller, "You can't spawn monsters while driving a vehicle"); return; }
-                                       if (STAT(FROZEN, caller)) { print_to(caller, "You can't spawn monsters while frozen"); return; }
                                        if (IS_DEAD(caller)) { print_to(caller, "You can't spawn monsters while dead"); return; }
                                        if (tmp_moncount >= autocvar_g_monsters_max) { print_to(caller, "The maximum monster count has been reached"); return; }
                                        if (tmp_moncount >= autocvar_g_monsters_max_perplayer) { print_to(caller, "You can't spawn any more monsters"); return; }
index 7c008c10e9a981570df10b6d4bf0e700d4c24dd0..e2df71b7ffa1eb175104eff3518e1b7422b44e9b 100644 (file)
@@ -373,8 +373,6 @@ void reset_map(bool is_fake_round_start)
                        accuracy_reset(it); // for spectators too because weapon accuracy is persistent
                if (!IS_PLAYER(it))
                        continue;
-               if (STAT(FROZEN, it))
-                       Unfreeze(it, false);
                player_powerups_remove_all(it, true);
                entity store = PS(it);
                if (store)
index cc7d68a425d9691fd9ca73a748e4d336e27b7b9c..fac8bb731ad74e84ea672bbfb9bbe9b1a8f3feed 100644 (file)
@@ -498,108 +498,6 @@ void Obituary(entity attacker, entity inflictor, entity targ, int deathtype, .en
        CS(targ).killcount = 0;
 }
 
-void Ice_Think(entity this)
-{
-       if(!STAT(FROZEN, this.owner) || this.owner.iceblock != this)
-       {
-               delete(this);
-               return;
-       }
-       vector ice_org = this.owner.origin - '0 0 16';
-       if (this.origin != ice_org)
-               setorigin(this, ice_org);
-       this.nextthink = time;
-}
-
-void Freeze(entity targ, float revivespeed, int frozen_type, bool show_waypoint)
-{
-       if(!IS_PLAYER(targ) && !IS_MONSTER(targ)) // TODO: only specified entities can be freezed
-               return;
-
-       if(STAT(FROZEN, targ))
-               return;
-
-       float targ_maxhealth = ((IS_MONSTER(targ)) ? targ.max_health : start_health);
-
-       STAT(FROZEN, targ) = frozen_type;
-       STAT(REVIVE_PROGRESS, targ) = ((frozen_type == FROZEN_TEMP_DYING) ? 1 : 0);
-       SetResource(targ, RES_HEALTH, ((frozen_type == FROZEN_TEMP_DYING) ? targ_maxhealth : 1));
-       targ.revive_speed = revivespeed;
-       if(targ.bot_attack)
-               IL_REMOVE(g_bot_targets, targ);
-       targ.bot_attack = false;
-       targ.freeze_time = time;
-
-       entity ice = new(ice);
-       ice.owner = targ;
-       ice.scale = targ.scale;
-       // set_movetype(ice, MOVETYPE_FOLLOW) would rotate the ice model with the player
-       setthink(ice, Ice_Think);
-       ice.nextthink = time;
-       ice.frame = floor(random() * 21); // ice model has 20 different looking frames
-       setmodel(ice, MDL_ICE);
-       ice.alpha = 1;
-       ice.colormod = Team_ColorRGB(targ.team);
-       ice.glowmod = ice.colormod;
-       targ.iceblock = ice;
-       targ.revival_time = 0;
-
-       Ice_Think(ice);
-
-       RemoveGrapplingHooks(targ);
-
-       FOREACH_CLIENT(IS_PLAYER(it),
-       {
-               for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
-               {
-                       .entity weaponentity = weaponentities[slot];
-                       if(it.(weaponentity).hook.aiment == targ)
-                               RemoveHook(it.(weaponentity).hook);
-               }
-       });
-
-       // add waypoint
-       if(MUTATOR_CALLHOOK(Freeze, targ, revivespeed, frozen_type) || show_waypoint)
-               WaypointSprite_Spawn(WP_Frozen, 0, 0, targ, '0 0 64', NULL, targ.team, targ, waypointsprite_attached, true, RADARICON_WAYPOINT);
-}
-
-void Unfreeze(entity targ, bool reset_health)
-{
-       if(!STAT(FROZEN, targ))
-               return;
-
-       if (reset_health && STAT(FROZEN, targ) != FROZEN_TEMP_DYING)
-               SetResource(targ, RES_HEALTH, ((IS_PLAYER(targ)) ? start_health : targ.max_health));
-
-       targ.pauseregen_finished = time + autocvar_g_balance_pause_health_regen;
-
-       STAT(FROZEN, targ) = 0;
-       STAT(REVIVE_PROGRESS, targ) = 0;
-       targ.revival_time = time;
-       if(!targ.bot_attack)
-               IL_PUSH(g_bot_targets, targ);
-       targ.bot_attack = true;
-
-       WaypointSprite_Kill(targ.waypointsprite_attached);
-
-       FOREACH_CLIENT(IS_PLAYER(it),
-       {
-               for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
-               {
-                       .entity weaponentity = weaponentities[slot];
-                       if(it.(weaponentity).hook.aiment == targ)
-                               RemoveHook(it.(weaponentity).hook);
-               }
-       });
-
-       // remove the ice block
-       if(targ.iceblock)
-               delete(targ.iceblock);
-       targ.iceblock = NULL;
-
-       MUTATOR_CALLHOOK(Unfreeze, targ);
-}
-
 void Damage(entity targ, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force)
 {
        float complainteamdamage = 0;
@@ -737,55 +635,6 @@ void Damage(entity targ, entity inflictor, entity attacker, float damage, int de
                        }
                }
 
-               if(STAT(FROZEN, targ) && !ITEM_DAMAGE_NEEDKILL(deathtype)
-                       && deathtype != DEATH_TEAMCHANGE.m_id && deathtype != DEATH_AUTOTEAMCHANGE.m_id)
-               {
-                       if(autocvar_g_frozen_revive_falldamage > 0 && deathtype == DEATH_FALL.m_id && damage >= autocvar_g_frozen_revive_falldamage)
-                       {
-                               Unfreeze(targ, false);
-                               SetResource(targ, RES_HEALTH, autocvar_g_frozen_revive_falldamage_health);
-                               Send_Effect(EFFECT_ICEORGLASS, targ.origin, '0 0 0', 3);
-                               Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_FREEZETAG_REVIVED_FALL, targ.netname);
-                               Send_Notification(NOTIF_ONE, targ, MSG_CENTER, CENTER_FREEZETAG_REVIVE_SELF);
-                       }
-
-                       damage = 0;
-                       force *= autocvar_g_frozen_force;
-               }
-
-               if(IS_PLAYER(targ) && STAT(FROZEN, targ)
-                       && ITEM_DAMAGE_NEEDKILL(deathtype) && !autocvar_g_frozen_damage_trigger)
-               {
-                       Send_Effect(EFFECT_TELEPORT, targ.origin, '0 0 0', 1);
-
-                       entity spot = SelectSpawnPoint(targ, false);
-                       if(spot)
-                       {
-                               damage = 0;
-                               targ.deadflag = DEAD_NO;
-
-                               targ.angles = spot.angles;
-
-                               targ.effects = 0;
-                               targ.effects |= EF_TELEPORT_BIT;
-
-                               targ.angles_z = 0; // never spawn tilted even if the spot says to
-                               targ.fixangle = true; // turn this way immediately
-                               targ.velocity = '0 0 0';
-                               targ.avelocity = '0 0 0';
-                               targ.punchangle = '0 0 0';
-                               targ.punchvector = '0 0 0';
-                               targ.oldvelocity = targ.velocity;
-
-                               targ.spawnorigin = spot.origin;
-                               setorigin(targ, spot.origin + '0 0 1' * (1 - targ.mins.z - 24));
-                               // don't reset back to last position, even if new position is stuck in solid
-                               targ.oldorigin = targ.origin;
-
-                               Send_Effect(EFFECT_TELEPORT, targ.origin, '0 0 0', 1);
-                       }
-               }
-
                if (targ == attacker)
                        damage = damage * autocvar_g_balance_selfdamagepercent; // Partial damage if the attacker hits himself
 
@@ -802,6 +651,7 @@ void Damage(entity targ, entity inflictor, entity attacker, float damage, int de
                        else
                                victim = targ;
 
+                       // TODO: allow the mutator hook to tell if the hit sound should be team or not
                        if(IS_PLAYER(victim) || (IS_TURRET(victim) && victim.active == ACTIVE_ACTIVE) || IS_MONSTER(victim) || MUTATOR_CALLHOOK(PlayHitsound, victim, attacker))
                        {
                                if (DIFF_TEAM(victim, attacker))
@@ -1134,6 +984,7 @@ float RadiusDamage(entity inflictor, entity attacker, float coredamage, float ed
 
 bool Heal(entity targ, entity inflictor, float amount, float limit)
 {
+       // TODO: mutator hook to control healing
        if(game_stopped || (IS_CLIENT(targ) && CS(targ).killcount == FRAGS_SPECTATOR) || STAT(FROZEN, targ) || IS_DEAD(targ))
                return false;
 
@@ -1279,7 +1130,7 @@ void Fire_ApplyDamage(entity e)
        }
        e.fire_hitsound = true;
 
-       if(!IS_INDEPENDENT_PLAYER(e) && !STAT(FROZEN, e))
+       if(!IS_INDEPENDENT_PLAYER(e) && !STAT(FROZEN, e) && !StatusEffects_active(STATUSEFFECT_Frozen, e))
        {
                IL_EACH(g_damagedbycontents, it.damagedbycontents && it != e,
                {
index 93805b16af3d959db16416f46ebb4cd0c0c83ece..1b3c59dbd54a69ce3b846262d98dfb54100c72c1 100644 (file)
@@ -25,10 +25,6 @@ float autocvar_g_balance_selfdamagepercent;
 float autocvar_g_friendlyfire;
 float autocvar_g_friendlyfire_virtual;
 float autocvar_g_friendlyfire_virtual_force;
-float autocvar_g_frozen_revive_falldamage;
-int autocvar_g_frozen_revive_falldamage_health;
-bool autocvar_g_frozen_damage_trigger;
-float autocvar_g_frozen_force;
 
 .void(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) event_damage;
 
@@ -103,24 +99,6 @@ float Obituary_WeaponDeath(
 
 void Obituary(entity attacker, entity inflictor, entity targ, int deathtype, .entity weaponentity);
 
-// Frozen status effect
-//const int FROZEN_NOT              = 0;
-const int FROZEN_NORMAL             = 1;
-const int FROZEN_TEMP_REVIVING      = 2;
-const int FROZEN_TEMP_DYING         = 3;
-
-.float revival_time; // time at which player was last revived
-.float revive_speed; // NOTE: multiplier (anything above 1 is instaheal)
-.float freeze_time;
-.entity iceblock;
-.entity frozen_by; // for ice fields
-
-void Ice_Think(entity this);
-
-void Freeze(entity targ, float revivespeed, int frozen_type, bool show_waypoint);
-
-void Unfreeze(entity targ, bool reset_health);
-
 // WEAPONTODO
 #define DMG_NOWEP (weaponentities[0])
 
index 26753d95ec61c98ac26c8e0187120e333989354b..749afb1c851541fc87a4e4fbf58ef0cff308f316 100644 (file)
@@ -2,6 +2,7 @@
 
 #include <common/constants.qh>
 #include <common/effects/all.qh>
+#include <common/mutators/mutator/status_effects/_mod.qh>
 #include <common/net_linked.qh>
 #include <common/physics/player.qh>
 #include <common/state.qh>
@@ -167,6 +168,7 @@ void GrapplingHookThink(entity this)
                // bit to control the rope
 
                bool frozen_pulling = (autocvar_g_grappling_hook_tarzan >= 2 && autocvar_g_balance_grapplehook_pull_frozen);
+               bool target_isfrozen = (STAT(FROZEN, this.aiment) || StatusEffects_active(STATUSEFFECT_Frozen, this.aiment));
 
                vector dir = this.origin - myorg;
                dist = vlen(dir);
@@ -215,7 +217,7 @@ void GrapplingHookThink(entity this)
                                        {
                                                entity aim_ent = ((IS_VEHICLE(this.aiment) && this.aiment.owner) ? this.aiment.owner : this.aiment);
                                                v = v - dv * 0.5;
-                                               if((frozen_pulling && STAT(FROZEN, this.aiment)) || !frozen_pulling)
+                                               if((frozen_pulling && target_isfrozen) || !frozen_pulling)
                                                {
                                                        this.aiment.velocity = this.aiment.velocity - dv * 0.5;
                                                        UNSET_ONGROUND(this.aiment);
@@ -236,7 +238,7 @@ void GrapplingHookThink(entity this)
                        if(!frozen_pulling && !(this.aiment.flags & FL_PROJECTILE))
                                pull_entity.velocity = WarpZone_RefSys_TransformVelocity(this, pull_entity, v * velocity_multiplier);
 
-                       if(frozen_pulling && autocvar_g_balance_grapplehook_pull_frozen == 2 && !STAT(FROZEN, this.aiment))
+                       if(frozen_pulling && autocvar_g_balance_grapplehook_pull_frozen == 2 && !target_isfrozen)
                        {
                                RemoveHook(this);
                                return;
index 0e9836fd00aafc662bd5098f993569dc62e9e585..b778bdd97e4330db38a003f641de815b4a7ea952 100644 (file)
@@ -701,7 +701,6 @@ void Item_Touch(entity this, entity toucher)
        }
 
        if(!(toucher.flags & FL_PICKUPITEMS)
-       || STAT(FROZEN, toucher)
        || IS_DEAD(toucher)
        || (this.solid != SOLID_TRIGGER)
        || (this.owner == toucher)
index b0433b9ea5990b5131a759f544a63e404d2de8ca..0cdc16fb1ebeb0a9d982592d03c0a4ba9d80d582 100644 (file)
@@ -85,6 +85,7 @@ void CreatureFrame_hotliquids(entity this)
        }
        else
        {
+               // TODO: do we even need this hack? frozen players still die in lava!
                if (STAT(FROZEN, this))
                {
                        if (this.watertype == CONTENT_LAVA)
index 19f84a3a811d0cf281dda985c7cd3c8f07682e80..b8f4c3addd05515793e209b6de46595a4766e135 100644 (file)
@@ -53,6 +53,14 @@ MUTATOR_HOOKABLE(PlayerSpawn, EV_PlayerSpawn);
     /**/
 MUTATOR_HOOKABLE(PlayerWeaponSelect, EV_PlayerWeaponSelect);
 
+/** called when setting the player's animation state */
+#define EV_PlayerAnim(i, o) \
+    /** player    */ i(entity, MUTATOR_ARGV_0_entity) \
+    /** anim bits */ i(int, MUTATOR_ARGV_1_int) \
+    /**           */ o(int, MUTATOR_ARGV_1_int) \
+    /**/
+MUTATOR_HOOKABLE(PlayerAnim, EV_PlayerAnim);
+
 /** called in reset_map */
 #define EV_reset_map_global(i, o) \
     /**/
@@ -1242,24 +1250,6 @@ enum {
     MUT_VOTEPARSE_UNACCEPTABLE // return 0 (vote parameter counted as unacceptable, warns caller)
 };
 
-/**
- * Called when freezing an entity (monster or player), return true to force showing a waypoint
- */
-#define EV_Freeze(i, o) \
-    /** targ */             i(entity, MUTATOR_ARGV_0_entity) \
-    /** revive speed */     i(float, MUTATOR_ARGV_1_float) \
-    /** frozen type */      i(int, MUTATOR_ARGV_2_int) \
-    /**/
-MUTATOR_HOOKABLE(Freeze, EV_Freeze);
-
-/**
- * Called when an entity (monster or player) is defrosted
- */
-#define EV_Unfreeze(i, o) \
-    /** targ */             i(entity, MUTATOR_ARGV_0_entity) \
-    /**/
-MUTATOR_HOOKABLE(Unfreeze, EV_Unfreeze);
-
 /**
  * Called when a player is trying to join, argument is the number of players allowed to join the match
  */
index 060f68541ecc4dd23efff50b1ac6e8a8715a90d4..e2793e92a12597eb96011c21e16d056a3f5ee79e 100644 (file)
@@ -166,12 +166,16 @@ void player_anim(entity this)
                deadbits = 0;
        }
        int animbits = deadbits;
-       if(STAT(FROZEN, this))
+       if(StatusEffects_active(STATUSEFFECT_Frozen, this))
                animbits |= ANIMSTATE_FROZEN;
        if(this.move_movetype == MOVETYPE_FOLLOW)
                animbits |= ANIMSTATE_FOLLOW;
        if(IS_DUCKED(this))
                animbits |= ANIMSTATE_DUCK;
+
+       MUTATOR_CALLHOOK(PlayerAnim, this, animbits);
+       animbits = M_ARGV(1, int);
+
        animdecide_setstate(this, animbits, false);
        animdecide_setimplicitstate(this, IS_ONGROUND(this));
 }
@@ -245,12 +249,7 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage,
                        }
                }
 
-               if (STAT(FROZEN, this))
-               {
-                       if (!ITEM_DAMAGE_NEEDKILL(deathtype))
-                               damage = 0;
-               }
-               else if (StatusEffects_active(STATUSEFFECT_SpawnShield, this) && autocvar_g_spawnshield_blockdamage < 1)
+               if (StatusEffects_active(STATUSEFFECT_SpawnShield, this) && autocvar_g_spawnshield_blockdamage < 1)
                        damage *= 1 - bound(0, autocvar_g_spawnshield_blockdamage, 1);
 
                if(deathtype & HITTYPE_SOUND) // sound based attacks cause bleeding from the ears
@@ -350,7 +349,7 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage,
                        if(take)
                                this.pauseregen_finished = max(this.pauseregen_finished, time + autocvar_g_balance_pause_health_regen);
 
-                       if (time > this.pain_finished && !STAT(FROZEN, this)) // Don't switch pain sequences like crazy
+                       if (time > this.pain_finished && !STAT(FROZEN, this) && !StatusEffects_active(STATUSEFFECT_Frozen, this)) // Don't switch pain sequences like crazy
                        {
                                this.pain_finished = time + 0.5;        //Supajoe
 
@@ -433,7 +432,7 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage,
        if ((dh || da) && !forbid_logging_damage)
        {
                float realdmg = damage - excess;
-               if ((this != attacker || deathtype == DEATH_KILL.m_id) && realdmg && !STAT(FROZEN, this)
+               if ((this != attacker || deathtype == DEATH_KILL.m_id) && realdmg
                        && (!(round_handler_IsActive() && !round_handler_IsRoundStarted()) && time >= game_starttime))
                {
                        // accumulate damage, it will be logged later in this frame
@@ -516,7 +515,6 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage,
                        ClientKill_Now_TeamChange(this); // can turn player into spectator
 
                // player could have been miraculously resuscitated ;)
-               // e.g. players in freezetag get frozen, they don't really die
                if(GetResource(this, RES_HEALTH) >= 1 || !(IS_PLAYER(this) || this.classname == "body"))
                        return;
 
@@ -525,8 +523,6 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage,
 
                // when we get here, player actually dies
 
-               Unfreeze(this, false); // remove any icy remains
-
                // clear waypoints
                WaypointSprite_PlayerDead(this);
                // throw a weapon
index fee006ab212d834fffbeee2ec85b84840ab3f88a..89feab8156629b0ff042b050d70310c237e641ab 100644 (file)
@@ -22,7 +22,7 @@ const string STR_OBSERVER = "observer";
 #define IS_VEHICLE(v) (v.vehicle_flags & VHF_ISVEHICLE)
 #define IS_TURRET(v) (v.turret_flags & TUR_FLAG_ISTURRET)
 
-#define IS_MOVABLE(v) ((IS_PLAYER(v) || IS_MONSTER(v)) && !STAT(FROZEN, v))
+#define IS_MOVABLE(v) ((IS_PLAYER(v) || IS_MONSTER(v)) && !STAT(FROZEN, v) && !StatusEffects_active(STATUSEFFECT_Frozen, v))
 #define IS_DEAD(s) ((s).deadflag != DEAD_NO)
 #define IS_INVISIBLE(v) (v.alpha <= 0.25 || StatusEffects_active(STATUSEFFECT_Invisibility, v) || MUTATOR_IS_ENABLED(cloaked))
 
index 7916a76e023bd55e908362533596d2b69b33bddf..3573fe813b3bf964ac0296b74ffc1f4a72670140 100644 (file)
@@ -115,10 +115,9 @@ bool accuracy_isgooddamage(entity attacker, entity targ)
 
        if (warmup_stage || game_stopped) return false;
 
-       // damage to dead/frozen players is good only if it happens in the frame they get killed / frozen
+       // damage to dead players is good only if it happens in the frame they get killed
        // so that stats for weapons that shoot multiple projectiles per shot are properly counted
        if (IS_DEAD(targ) && time > targ.death_time) return false;
-       if (STAT(FROZEN, targ) && time > targ.freeze_time) return false;
        if (SAME_TEAM(attacker, targ)) return false;
 
        if (mutator_check == MUT_ACCADD_INVALID) return true;
index dccec6c4803bb2adb36aff899479baaf86e1e2a4..148b1733fc5fd9b39a48ced03a81e1c76a23dded 100644 (file)
@@ -428,7 +428,7 @@ bool weaponLocked(entity player)
        if (time < game_starttime && !sv_ready_restart_after_countdown) return true;
        if (player.player_blocked) return true;
        if (game_stopped) return true;
-       if (STAT(FROZEN, player)) return true;
+       if (StatusEffects_active(STATUSEFFECT_Frozen, player)) return true;
        if (MUTATOR_CALLHOOK(LockWeapon, player)) return true;
        return false;
 }