game: sync ctf g_cmds

This commit is contained in:
Denis Pauk 2023-11-04 17:32:56 +02:00
parent 2b69f3774f
commit 802a874f64
6 changed files with 520 additions and 1518 deletions

View file

@ -1482,7 +1482,7 @@ CTF_OBJS_ = \
src/common/shared/shared.o \
src/ctf/g_ai.o \
src/game/g_chase.o \
src/ctf/g_cmds.o \
src/game/g_cmds.o \
src/ctf/g_combat.o \
src/game/g_ctf.o \
src/game/g_func.o \

File diff suppressed because it is too large Load diff

View file

@ -142,16 +142,82 @@ void
Killed(edict_t *targ, edict_t *inflictor, edict_t *attacker,
int damage, vec3_t point)
{
if (!targ || !inflictor || !attacker)
{
return;
}
if (targ->health < -999)
{
targ->health = -999;
}
/* Reset AI flag for being ducked. This fixes a corner case
were the monster is ressurected by a medic and get's stuck
in the next frame for mmove_t not matching the AI state. */
if (targ->monsterinfo.aiflags & AI_DUCKED)
{
targ->monsterinfo.aiflags &= ~AI_DUCKED;
}
if (targ->monsterinfo.aiflags & AI_MEDIC)
{
if (targ->enemy)
{
cleanupHealTarget(targ->enemy);
}
/* clean up self */
targ->monsterinfo.aiflags &= ~AI_MEDIC;
targ->enemy = attacker;
}
else
{
targ->enemy = attacker;
}
if ((targ->svflags & SVF_MONSTER) && (targ->deadflag != DEAD_DEAD))
{
if (!(targ->monsterinfo.aiflags & AI_GOOD_GUY))
/* free up slot for spawned monster if it's spawned */
if (targ->monsterinfo.aiflags & AI_SPAWNED_CARRIER)
{
if (targ->monsterinfo.commander &&
targ->monsterinfo.commander->inuse &&
!strcmp(targ->monsterinfo.commander->classname, "monster_carrier"))
{
targ->monsterinfo.commander->monsterinfo.monster_slots++;
}
}
if (targ->monsterinfo.aiflags & AI_SPAWNED_MEDIC_C)
{
if (targ->monsterinfo.commander)
{
if (targ->monsterinfo.commander->inuse &&
!strcmp(targ->monsterinfo.commander->classname, "monster_medic_commander"))
{
targ->monsterinfo.commander->monsterinfo.monster_slots++;
}
}
}
if (targ->monsterinfo.aiflags & AI_SPAWNED_WIDOW)
{
/* need to check this because we can
have variable numbers of coop players */
if (targ->monsterinfo.commander &&
targ->monsterinfo.commander->inuse &&
!strncmp(targ->monsterinfo.commander->classname, "monster_widow", 13))
{
if (targ->monsterinfo.commander->monsterinfo.monster_used > 0)
{
targ->monsterinfo.commander->monsterinfo.monster_used--;
}
}
}
if ((!(targ->monsterinfo.aiflags & AI_GOOD_GUY)) &&
(!(targ->monsterinfo.aiflags & AI_DO_NOT_COUNT)))
{
level.killed_monsters++;
@ -161,7 +227,7 @@ Killed(edict_t *targ, edict_t *inflictor, edict_t *attacker,
}
/* medics won't heal monsters that they kill themselves */
if (strcmp(attacker->classname, "monster_medic") == 0)
if (attacker->classname && strcmp(attacker->classname, "monster_medic") == 0)
{
targ->owner = attacker;
}
@ -169,7 +235,8 @@ Killed(edict_t *targ, edict_t *inflictor, edict_t *attacker,
}
if ((targ->movetype == MOVETYPE_PUSH) ||
(targ->movetype == MOVETYPE_STOP) || (targ->movetype == MOVETYPE_NONE))
(targ->movetype == MOVETYPE_STOP) ||
(targ->movetype == MOVETYPE_NONE))
{
/* doors, triggers, etc */
targ->die(targ, inflictor, attacker, damage, point);
@ -301,7 +368,15 @@ CheckPowerArmor(edict_t *ent, vec3_t point, vec3_t normal,
}
else
{
damagePerCell = 1; /* power armor is weaker in CTF */
if (ctf->value)
{
/* power armor is weaker in CTF */
damagePerCell = 1;
}
else
{
damagePerCell = 2;
}
pa_te_type = TE_SHIELD_SPARKS;
damage = (2 * damage) / 3;
}
@ -418,19 +493,47 @@ CheckArmor(edict_t *ent, vec3_t point, vec3_t normal, int damage,
void
M_ReactToDamage(edict_t *targ, edict_t *attacker, edict_t *inflictor)
{
qboolean new_tesla;
if (!targ || !attacker || !inflictor)
{
return;
}
if (targ->health <= 0)
{
return;
}
if (!(attacker->client) && !(attacker->svflags & SVF_MONSTER))
{
return;
}
/* logic for tesla - if you are hit by a tesla,
and can't see who you should be mad at (attacker)
attack the tesla also, target the tesla if it's
a "new" tesla */
if (!strcmp(inflictor->classname, "tesla"))
{
new_tesla = MarkTeslaArea(targ, inflictor);
if (new_tesla || !targ->enemy)
{
TargetTesla(targ, inflictor);
}
return;
}
if ((attacker == targ) || (attacker == targ->enemy))
{
return;
}
/* if we are a good guy monster and our
attacker is a player or another good
guy, do not get mad at them */
/* if we are a good guy monster and
our attacker is a player or another
good guy, do not get mad at them */
if (targ->monsterinfo.aiflags & AI_GOOD_GUY)
{
if (attacker->client || (attacker->monsterinfo.aiflags & AI_GOOD_GUY))
@ -439,14 +542,55 @@ M_ReactToDamage(edict_t *targ, edict_t *attacker, edict_t *inflictor)
}
}
/* if we're currently mad at something
a target_anger made us mad at, ignore
damage */
if (targ->enemy && targ->monsterinfo.aiflags & AI_TARGET_ANGER)
{
float percentHealth;
/* make sure whatever we were pissed at is still around. */
if (targ->enemy->inuse)
{
percentHealth = (float)(targ->health) / (float)(targ->max_health);
if (percentHealth > 0.33)
{
return;
}
}
/* remove the target anger flag */
targ->monsterinfo.aiflags &= ~AI_TARGET_ANGER;
}
/* if we're healing someone, do like above and try to stay with them */
if ((targ->enemy) && (targ->monsterinfo.aiflags & AI_MEDIC))
{
float percentHealth;
percentHealth = (float)(targ->health) / (float)(targ->max_health);
/* ignore it some of the time */
if (targ->enemy->inuse && (percentHealth > 0.25))
{
return;
}
/* remove the medic flag */
targ->monsterinfo.aiflags &= ~AI_MEDIC;
cleanupHealTarget(targ->enemy);
}
/* if attacker is a client, get mad at
them because he's good and we're not */
if (attacker->client)
{
/* this can only happen in coop (both
new and old enemies are clients)
only switch if can't see the
current enemy */
targ->monsterinfo.aiflags &= ~AI_SOUND_TARGET;
/* this can only happen in coop (both new and
old enemies are clients) only switch if can't
see the current enemy */
if (targ->enemy && targ->enemy->client)
{
if (visible(targ, targ->enemy))
@ -468,8 +612,8 @@ M_ReactToDamage(edict_t *targ, edict_t *attacker, edict_t *inflictor)
return;
}
/* it's the same base (walk/swim/fly) type
and a different classname and it's not a tank
/* it's the same base (walk/swim/fly) type and a
different classname and it's not a tank
(they spray too much), get mad at them */
if (((targ->flags & (FL_FLY | FL_SWIM)) ==
(attacker->flags & (FL_FLY | FL_SWIM))) &&
@ -477,15 +621,14 @@ M_ReactToDamage(edict_t *targ, edict_t *attacker, edict_t *inflictor)
(strcmp(attacker->classname, "monster_tank") != 0) &&
(strcmp(attacker->classname, "monster_supertank") != 0) &&
(strcmp(attacker->classname, "monster_makron") != 0) &&
(strcmp(attacker->classname, "monster_jorg") != 0))
(strcmp(attacker->classname, "monster_jorg") != 0) &&
!(attacker->monsterinfo.aiflags & AI_IGNORE_SHOTS) &&
!(targ->monsterinfo.aiflags & AI_IGNORE_SHOTS))
{
if (targ->enemy)
{
if (targ->enemy->client)
if (targ->enemy && targ->enemy->client)
{
targ->oldenemy = targ->enemy;
}
}
targ->enemy = attacker;
@ -494,20 +637,38 @@ M_ReactToDamage(edict_t *targ, edict_t *attacker, edict_t *inflictor)
FoundTarget(targ);
}
}
else /* otherwise get mad at whoever they are mad at (help our buddy) */
/* if they *meant* to shoot us, then shoot back */
else if (attacker->enemy == targ)
{
if (targ->enemy)
{
if (targ->enemy->client)
if (targ->enemy && targ->enemy->client)
{
targ->oldenemy = targ->enemy;
}
targ->enemy = attacker;
if (!(targ->monsterinfo.aiflags & AI_DUCKED))
{
FoundTarget(targ);
}
}
/* otherwise get mad at whoever they are mad
at (help our buddy) unless it is us! */
else if (attacker->enemy)
{
if (targ->enemy && targ->enemy->client)
{
targ->oldenemy = targ->enemy;
}
targ->enemy = attacker->enemy;
if (!(targ->monsterinfo.aiflags & AI_DUCKED))
{
FoundTarget(targ);
}
}
}
qboolean
CheckTeamDamage(edict_t *targ, edict_t *attacker)
@ -524,10 +685,28 @@ CheckTeamDamage(edict_t *targ, edict_t *attacker)
return false;
}
static void
apply_knockback(edict_t *targ, vec3_t dir, float knockback, float scale)
{
vec3_t kvel;
float mass;
if (!knockback)
{
return;
}
mass = (targ->mass < 50) ? 50.0f : (float)targ->mass;
VectorNormalize2(dir, kvel);
VectorScale(kvel, scale * (knockback / mass), kvel);
VectorAdd(targ->velocity, kvel, targ->velocity);
}
void
T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker,
vec3_t dir, vec3_t point, vec3_t normal, int damage,
int knockback, int dflags, int mod)
T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker, vec3_t dir,
vec3_t point, vec3_t normal, int damage, int knockback, int dflags,
int mod)
{
gclient_t *client;
int take;
@ -535,24 +714,32 @@ T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker,
int asave;
int psave;
int te_sparks;
int sphere_notified;
if (!targ || !inflictor || !attacker)
{
return;
}
if (!targ->takedamage)
{
return;
}
/* friendly fire avoidance
if enabled you can't hurt
teammates (but you can hurt
yourself) knockback still occurs */
if ((targ != attacker) &&
((deathmatch->value &&
sphere_notified = false;
/* friendly fire avoidance. If enabled you can't
hurt teammates (but you can hurt yourself)
knockback still occurs */
if ((targ != attacker) && ((deathmatch->value &&
((int)(dmflags->value) & (DF_MODELTEAMS | DF_SKINTEAMS))) ||
coop->value))
{
if (OnSameTeam(targ, attacker))
{
if ((int)(dmflags->value) & DF_NO_FRIENDLY_FIRE)
/* nukes kill everyone */
if (((int)(dmflags->value) & DF_NO_FRIENDLY_FIRE) &&
(mod != MOD_NUKE))
{
damage = 0;
}
@ -565,6 +752,25 @@ T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker,
meansOfDeath = mod;
/* allow the deathmatch game to change values */
if (deathmatch->value && gamerules && gamerules->value)
{
if (DMGame.ChangeDamage)
{
damage = DMGame.ChangeDamage(targ, attacker, damage, mod);
}
if (DMGame.ChangeKnockback)
{
knockback = DMGame.ChangeKnockback(targ, attacker, knockback, mod);
}
if (!damage)
{
return;
}
}
/* easy mode takes half damage */
if ((skill->value == SKILL_EASY) && (deathmatch->value == 0) && targ->client)
{
@ -578,6 +784,18 @@ T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker,
client = targ->client;
/* defender sphere takes half damage */
if ((client) && (client->owned_sphere) &&
(client->owned_sphere->spawnflags == 1))
{
damage *= 0.5;
if (!damage)
{
damage = 1;
}
}
if (dflags & DAMAGE_BULLET)
{
te_sparks = TE_BULLET_SPARKS;
@ -651,11 +869,28 @@ T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker,
/* check for invincibility */
if ((client &&
(client->invincible_framenum > level.framenum)) &&
!(dflags & DAMAGE_NO_PROTECTION) && (mod != MOD_TRAP))
{
if (targ->pain_debounce_time < level.time)
{
gi.sound(targ, CHAN_ITEM, gi.soundindex(
"items/protect4.wav"), 1, ATTN_NORM, 0);
targ->pain_debounce_time = level.time + 2;
}
take = 0;
save = damage;
}
/* check for monster invincibility */
if (((targ->svflags & SVF_MONSTER) &&
(targ->monsterinfo.invincible_framenum > level.framenum)) &&
!(dflags & DAMAGE_NO_PROTECTION))
{
if (targ->pain_debounce_time < level.time)
{
gi.sound(targ, CHAN_ITEM, gi.soundindex("items/protect4.wav"), 1, ATTN_NORM, 0);
gi.sound(targ, CHAN_ITEM, gi.soundindex(
"items/protect4.wav"), 1, ATTN_NORM, 0);
targ->pain_debounce_time = level.time + 2;
}
@ -696,10 +931,26 @@ T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker,
/* do the damage */
if (take)
{
if ((targ->svflags & SVF_MONSTER) || (client))
/* need more blood for chainfist. */
if (targ->flags & FL_MECHANICAL)
{
SpawnDamage(TE_ELECTRIC_SPARKS, point, normal);
}
else if ((targ->svflags & SVF_MONSTER) || (client))
{
if (strcmp(targ->classname, "monster_gekk") == 0)
{
SpawnDamage(TE_GREENBLOOD, point, normal);
}
else if (mod == MOD_CHAINFIST)
{
SpawnDamage(TE_MOREBLOOD, point, normal);
}
else
{
SpawnDamage(TE_BLOOD, point, normal);
}
}
else
{
SpawnDamage(te_sparks, point, normal);
@ -710,6 +961,17 @@ T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker,
targ->health = targ->health - take;
}
/* spheres need to know who to shoot at */
if (client && client->owned_sphere)
{
sphere_notified = true;
if (client->owned_sphere->pain)
{
client->owned_sphere->pain(client->owned_sphere, attacker, 0, 0);
}
}
if (targ->health <= 0)
{
if ((targ->svflags & SVF_MONSTER) || (client))
@ -722,6 +984,19 @@ T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker,
}
}
/* spheres need to know who to shoot at */
if (!sphere_notified)
{
if (client && client->owned_sphere)
{
if (client->owned_sphere->pain)
{
client->owned_sphere->pain(client->owned_sphere, attacker, 0,
0);
}
}
}
if (targ->svflags & SVF_MONSTER)
{
M_ReactToDamage(targ, attacker, inflictor);
@ -776,6 +1051,11 @@ T_RadiusDamage(edict_t *inflictor, edict_t *attacker, float damage,
vec3_t v;
vec3_t dir;
if (!inflictor || !attacker)
{
return;
}
while ((ent = findradius(ent, inflictor->s.origin, radius)) != NULL)
{
if (ent == ignore)

View file

@ -28,6 +28,9 @@
#include "../header/local.h"
#include "../monster/misc/player.h"
edict_t *pm_passent;
void ClientUserinfoChanged(edict_t *ent, char *userinfo);
void SP_misc_teleporter_dest(edict_t *ent);
void Touch_Item(edict_t *ent, edict_t *other, cplane_t *plane, csurface_t *surf);
@ -2370,3 +2373,34 @@ ClientBeginServerFrame(edict_t *ent)
client->latched_buttons = 0;
}
/*
* This is called to clean up the pain daemons that
* the disruptor attaches to clients to damage them.
*/
void
RemoveAttackingPainDaemons(edict_t *self)
{
edict_t *tracker;
if (!self)
{
return;
}
tracker = G_Find(NULL, FOFS(classname), "pain daemon");
while (tracker)
{
if (tracker->enemy == self)
{
G_FreeEdict(tracker);
}
tracker = G_Find(tracker, FOFS(classname), "pain daemon");
}
if (self->client)
{
self->client->tracker_pain_framenum = 0;
}
}

View file

@ -28,6 +28,8 @@
#include "header/local.h"
#include "monster/misc/player.h"
gitem_t *CTFWhat_Tech(edict_t *ent);
static char *
ClientTeam(edict_t *ent, char* value)
{
@ -103,7 +105,12 @@ SelectNextItem(edict_t *ent, int itflags)
cl = ent->client;
if (cl->chase_target)
if (cl->menu)
{
PMenu_Next(ent);
return;
}
else if (cl->chase_target)
{
ChaseNext(ent);
return;
@ -152,7 +159,12 @@ SelectPrevItem(edict_t *ent, int itflags)
cl = ent->client;
if (cl->chase_target)
if (cl->menu)
{
PMenu_Prev(ent);
return;
}
else if (cl->chase_target)
{
ChasePrev(ent);
return;
@ -622,6 +634,12 @@ Cmd_Drop_f(edict_t *ent)
return;
}
if ((Q_stricmp(gi.args(), "tech") == 0) && ((it = CTFWhat_Tech(ent)) != NULL))
{
it->drop(ent, it);
return;
}
s = gi.args();
it = FindItem(s);
@ -700,6 +718,7 @@ Cmd_Score_f(edict_t *ent)
if (ent->client->showscores)
{
ent->client->showscores = false;
ent->client->update_chase = true;
return;
}
@ -708,6 +727,9 @@ Cmd_Score_f(edict_t *ent)
gi.unicast(ent, true);
}
/*
* Display the current help message
*/
void
Cmd_Help_f(edict_t *ent)
{
@ -726,7 +748,8 @@ Cmd_Help_f(edict_t *ent)
ent->client->showinventory = false;
ent->client->showscores = false;
if (ent->client->showhelp)
if (ent->client->showhelp &&
(ent->client->resp.game_helpchanged == game.helpchanged))
{
ent->client->showhelp = false;
return;
@ -753,12 +776,25 @@ Cmd_Inven_f(edict_t *ent)
cl->showscores = false;
cl->showhelp = false;
if (ent->client->menu)
{
PMenu_Close(ent);
ent->client->update_chase = true;
return;
}
if (cl->showinventory)
{
cl->showinventory = false;
return;
}
if (ctf->value && (cl->resp.ctf_team == CTF_NOTEAM))
{
CTFOpenJoinMenu(ent);
return;
}
cl->showinventory = true;
InventoryMessage(ent);
@ -775,6 +811,12 @@ Cmd_InvUse_f(edict_t *ent)
return;
}
if (ent->client->menu)
{
PMenu_Select(ent);
return;
}
ValidateSelectedItem(ent);
if (ent->client->pers.selected_item == -1)
@ -794,6 +836,21 @@ Cmd_InvUse_f(edict_t *ent)
it->use(ent, it);
}
void
Cmd_LastWeap_f(edict_t *ent)
{
gclient_t *cl;
cl = ent->client;
if (!cl->pers.weapon || !cl->pers.lastweapon)
{
return;
}
cl->pers.lastweapon->use(ent, cl->pers.lastweapon);
}
void
Cmd_WeapPrev_f(edict_t *ent)
{
@ -982,6 +1039,11 @@ Cmd_Kill_f(edict_t *ent)
return;
}
if (ent->solid == SOLID_NOT)
{
return;
}
if (((level.time - ent->client->respawn_time) < 5) ||
(ent->client->resp.spectator))
{
@ -1018,6 +1080,13 @@ Cmd_PutAway_f(edict_t *ent)
ent->client->showscores = false;
ent->client->showhelp = false;
ent->client->showinventory = false;
if (ent->client->menu)
{
PMenu_Close(ent);
}
ent->client->update_chase = true;
}
int
@ -1973,9 +2042,9 @@ ClientCommand(edict_t *ent)
return;
}
if (Q_stricmp(cmd, "say_team") == 0)
if ((Q_stricmp(cmd, "say_team") == 0) || (Q_stricmp(cmd, "steam") == 0))
{
Cmd_Say_f(ent, true, false);
CTFSay_Team(ent, gi.args());
return;
}
@ -2080,10 +2149,62 @@ ClientCommand(edict_t *ent)
{
Cmd_Wave_f(ent);
}
/* ZOID */
else if (Q_stricmp(cmd, "team") == 0)
{
CTFTeam_f(ent);
}
else if (Q_stricmp(cmd, "id") == 0)
{
CTFID_f(ent);
}
else if (Q_stricmp(cmd, "yes") == 0)
{
CTFVoteYes(ent);
}
else if (Q_stricmp(cmd, "no") == 0)
{
CTFVoteNo(ent);
}
else if (Q_stricmp(cmd, "ready") == 0)
{
CTFReady(ent);
}
else if (Q_stricmp(cmd, "notready") == 0)
{
CTFNotReady(ent);
}
else if (Q_stricmp(cmd, "ghost") == 0)
{
CTFGhost(ent);
}
else if (Q_stricmp(cmd, "admin") == 0)
{
CTFAdmin(ent);
}
else if (Q_stricmp(cmd, "stats") == 0)
{
CTFStats(ent);
}
else if (Q_stricmp(cmd, "warp") == 0)
{
CTFWarp(ent);
}
else if (Q_stricmp(cmd, "boot") == 0)
{
CTFBoot(ent);
}
else if (Q_stricmp(cmd, "playerlist") == 0)
{
if (ctf->value)
{
CTFPlayerList(ent);
}
else
{
Cmd_PlayerList_f(ent);
}
}
else if (Q_stricmp(cmd, "entcount") == 0)
{
Cmd_Ent_Count_f(ent);
@ -2116,6 +2237,10 @@ ClientCommand(edict_t *ent)
{
Cmd_PrefWeap_f(ent);
}
else if (Q_stricmp(cmd, "observer") == 0)
{
CTFObserver(ent);
}
else /* anything that doesn't match a command will be a chat */
{
Cmd_Say_f(ent, false, true);

View file

@ -367,8 +367,16 @@ CheckPowerArmor(edict_t *ent, vec3_t point, vec3_t normal,
damage = damage / 3;
}
else
{
if (ctf->value)
{
/* power armor is weaker in CTF */
damagePerCell = 1;
}
else
{
damagePerCell = 2;
}
pa_te_type = TE_SHIELD_SPARKS;
damage = (2 * damage) / 3;
}
@ -863,11 +871,21 @@ T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker, vec3_t dir,
save = damage;
}
/* team armor protect */
if (ctf->value && targ->client && attacker->client &&
(targ->client->resp.ctf_team == attacker->client->resp.ctf_team) &&
(targ != attacker) && ((int)dmflags->value & DF_ARMOR_PROTECT))
{
psave = asave = 0;
}
else
{
psave = CheckPowerArmor(targ, point, normal, take, dflags);
take -= psave;
asave = CheckArmor(targ, point, normal, take, te_sparks, dflags);
take -= asave;
}
/* treat cheat/powerup savings the same as armor */
asave += save;
@ -911,7 +929,10 @@ T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker, vec3_t dir,
SpawnDamage(te_sparks, point, normal);
}
if (!CTFMatchSetup())
{
targ->health = targ->health - take;
}
/* spheres need to know who to shoot at */
if (client && client->owned_sphere)
@ -966,7 +987,7 @@ T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker, vec3_t dir,
}
else if (client)
{
if (!(targ->flags & FL_GODMODE) && (take))
if (!(targ->flags & FL_GODMODE) && (take) && !CTFMatchSetup())
{
targ->pain(targ, attacker, knockback, take);
}
@ -979,9 +1000,11 @@ T_Damage(edict_t *targ, edict_t *inflictor, edict_t *attacker, vec3_t dir,
}
}
/* add to the damage inflicted on a player this frame
the total will be turned into screen blends and view
angle kicks at the end of the frame */
/* add to the damage inflicted on a
player this frame the total will
be turned into screen blends and
view angle kicks at the end of
the frame */
if (client)
{
client->damage_parmor += psave;