rogue/src/g_combat.c

1177 lines
22 KiB
C
Raw Normal View History

/*
* =======================================================================
*
* Combat code like damage, death and so on.
*
* =======================================================================
*/
2009-03-12 20:03:41 +00:00
2011-10-11 11:40:43 +00:00
#include "header/local.h"
2009-03-12 20:03:41 +00:00
void M_SetEffects(edict_t *self);
2009-03-12 20:03:41 +00:00
/*
* clean up heal targets for medic
*/
void
cleanupHealTarget(edict_t *ent)
2014-02-22 12:20:22 +00:00
{
if (!ent)
{
return;
}
2014-02-22 12:20:22 +00:00
2009-03-12 20:03:41 +00:00
ent->monsterinfo.healer = NULL;
ent->takedamage = DAMAGE_YES;
ent->monsterinfo.aiflags &= ~AI_RESURRECTING;
M_SetEffects(ent);
2009-03-12 20:03:41 +00:00
}
2009-03-12 20:03:41 +00:00
/*
* Returns true if the inflictor can directly damage the
* target. Used for explosions and melee attacks.
*/
qboolean
CanDamage(edict_t *targ, edict_t *inflictor)
2009-03-12 20:03:41 +00:00
{
vec3_t dest;
trace_t trace;
2014-02-22 12:20:22 +00:00
if (!targ || !inflictor)
{
return false;
}
2014-02-22 12:20:22 +00:00
/* bmodels need special checking because their origin is 0,0,0 */
2009-03-12 20:03:41 +00:00
if (targ->movetype == MOVETYPE_PUSH)
{
VectorAdd(targ->absmin, targ->absmax, dest);
VectorScale(dest, 0.5, dest);
trace = gi.trace(inflictor->s.origin, vec3_origin,
vec3_origin, dest, inflictor, MASK_SOLID);
2009-03-12 20:03:41 +00:00
if (trace.fraction == 1.0)
{
2009-03-12 20:03:41 +00:00
return true;
}
2009-03-12 20:03:41 +00:00
if (trace.ent == targ)
{
2009-03-12 20:03:41 +00:00
return true;
}
2009-03-12 20:03:41 +00:00
return false;
}
trace = gi.trace(inflictor->s.origin, vec3_origin,
vec3_origin, targ->s.origin, inflictor,
MASK_SOLID);
2009-03-12 20:03:41 +00:00
if (trace.fraction == 1.0)
{
2009-03-12 20:03:41 +00:00
return true;
}
2009-03-12 20:03:41 +00:00
VectorCopy(targ->s.origin, dest);
2009-03-12 20:03:41 +00:00
dest[0] += 15.0;
dest[1] += 15.0;
trace = gi.trace(inflictor->s.origin, vec3_origin,
vec3_origin, dest, inflictor, MASK_SOLID);
2009-03-12 20:03:41 +00:00
if (trace.fraction == 1.0)
{
2009-03-12 20:03:41 +00:00
return true;
}
2009-03-12 20:03:41 +00:00
VectorCopy(targ->s.origin, dest);
2009-03-12 20:03:41 +00:00
dest[0] += 15.0;
dest[1] -= 15.0;
trace = gi.trace(inflictor->s.origin, vec3_origin, vec3_origin,
dest, inflictor, MASK_SOLID);
2009-03-12 20:03:41 +00:00
if (trace.fraction == 1.0)
{
2009-03-12 20:03:41 +00:00
return true;
}
2009-03-12 20:03:41 +00:00
VectorCopy(targ->s.origin, dest);
2009-03-12 20:03:41 +00:00
dest[0] -= 15.0;
dest[1] += 15.0;
trace = gi.trace(inflictor->s.origin, vec3_origin, vec3_origin,
dest, inflictor, MASK_SOLID);
2009-03-12 20:03:41 +00:00
if (trace.fraction == 1.0)
{
2009-03-12 20:03:41 +00:00
return true;
}
2009-03-12 20:03:41 +00:00
VectorCopy(targ->s.origin, dest);
2009-03-12 20:03:41 +00:00
dest[0] -= 15.0;
dest[1] -= 15.0;
trace = gi.trace(inflictor->s.origin, vec3_origin, vec3_origin,
dest, inflictor, MASK_SOLID);
2009-03-12 20:03:41 +00:00
if (trace.fraction == 1.0)
{
2009-03-12 20:03:41 +00:00
return true;
}
2009-03-12 20:03:41 +00:00
return false;
}
void
Killed(edict_t *targ, edict_t *inflictor, edict_t *attacker,
int damage, vec3_t point)
2009-03-12 20:03:41 +00:00
{
if (!targ || !inflictor || !attacker)
{
return;
}
2009-03-12 20:03:41 +00:00
if (targ->health < -999)
{
2009-03-12 20:03:41 +00:00
targ->health = -999;
}
2009-03-12 20:03:41 +00:00
/* 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;
}
2009-03-12 20:03:41 +00:00
if (targ->monsterinfo.aiflags & AI_MEDIC)
{
if (targ->enemy)
2009-03-12 20:03:41 +00:00
{
cleanupHealTarget(targ->enemy);
2009-03-12 20:03:41 +00:00
}
/* clean up self */
2009-03-12 20:03:41 +00:00
targ->monsterinfo.aiflags &= ~AI_MEDIC;
targ->enemy = attacker;
}
else
{
targ->enemy = attacker;
}
if ((targ->svflags & SVF_MONSTER) && (targ->deadflag != DEAD_DEAD))
{
/* free up slot for spawned monster if it's spawned */
2009-03-12 20:03:41 +00:00
if (targ->monsterinfo.aiflags & AI_SPAWNED_CARRIER)
{
if (targ->monsterinfo.commander &&
targ->monsterinfo.commander->inuse &&
2009-03-12 20:03:41 +00:00
!strcmp(targ->monsterinfo.commander->classname, "monster_carrier"))
{
targ->monsterinfo.commander->monsterinfo.monster_slots++;
}
}
2009-03-12 20:03:41 +00:00
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"))
2009-03-12 20:03:41 +00:00
{
targ->monsterinfo.commander->monsterinfo.monster_slots++;
}
}
}
2009-03-12 20:03:41 +00:00
if (targ->monsterinfo.aiflags & AI_SPAWNED_WIDOW)
{
2014-02-22 12:20:22 +00:00
/* need to check this because we can
have variable numbers of coop players */
if (targ->monsterinfo.commander &&
targ->monsterinfo.commander->inuse &&
2009-03-12 20:03:41 +00:00
!strncmp(targ->monsterinfo.commander->classname, "monster_widow", 13))
{
if (targ->monsterinfo.commander->monsterinfo.monster_used > 0)
{
2009-03-12 20:03:41 +00:00
targ->monsterinfo.commander->monsterinfo.monster_used--;
}
2009-03-12 20:03:41 +00:00
}
}
if ((!(targ->monsterinfo.aiflags & AI_GOOD_GUY)) &&
(!(targ->monsterinfo.aiflags & AI_DO_NOT_COUNT)))
2009-03-12 20:03:41 +00:00
{
level.killed_monsters++;
2009-03-12 20:03:41 +00:00
if (coop->value && attacker->client)
{
2009-03-12 20:03:41 +00:00
attacker->client->resp.score++;
}
2009-03-12 20:03:41 +00:00
}
}
if ((targ->movetype == MOVETYPE_PUSH) ||
(targ->movetype == MOVETYPE_STOP) || (targ->movetype == MOVETYPE_NONE))
{
targ->die(targ, inflictor, attacker, damage, point);
2009-03-12 20:03:41 +00:00
return;
}
if ((targ->svflags & SVF_MONSTER) && (targ->deadflag != DEAD_DEAD))
{
targ->touch = NULL;
monster_death_use(targ);
2009-03-12 20:03:41 +00:00
}
targ->die(targ, inflictor, attacker, damage, point);
2009-03-12 20:03:41 +00:00
}
void
SpawnDamage(int type, vec3_t origin, vec3_t normal, int damage)
2009-03-12 20:03:41 +00:00
{
gi.WriteByte(svc_temp_entity);
gi.WriteByte(type);
gi.WritePosition(origin);
gi.WriteDir(normal);
gi.multicast(origin, MULTICAST_PVS);
2009-03-12 20:03:41 +00:00
}
/*
* ============
* T_Damage
*
* targ entity that is being damaged
* inflictor entity that is causing the damage
* attacker entity that caused the inflictor to damage targ
* example: targ=monster, inflictor=rocket, attacker=player
*
* dir direction of the attack
* point point at which the damage is being inflicted
* normal normal vector from that point
* damage amount of damage being inflicted
* knockback force to be applied against targ as a result of the damage
*
* dflags these flags are used to control how T_Damage works
* DAMAGE_RADIUS damage was indirect (from a nearby explosion)
* DAMAGE_NO_ARMOR armor does not protect from this damage
* DAMAGE_ENERGY damage is from an energy based weapon
* DAMAGE_NO_KNOCKBACK do not affect velocity, just view angles
* DAMAGE_BULLET damage is from a bullet (used for ricochets)
* DAMAGE_NO_PROTECTION kills godmode, armor, everything
* ============
*/
int
CheckPowerArmor(edict_t *ent, vec3_t point, vec3_t normal,
int damage, int dflags)
2009-03-12 20:03:41 +00:00
{
gclient_t *client;
int save;
int power_armor_type;
int index = 0;
int damagePerCell;
int pa_te_type;
int power = 0;
int power_used;
2014-02-22 12:20:22 +00:00
if (!ent)
{
return 0;
}
2014-02-22 12:20:22 +00:00
2009-03-12 20:03:41 +00:00
if (!damage)
{
2009-03-12 20:03:41 +00:00
return 0;
}
2009-03-12 20:03:41 +00:00
client = ent->client;
if (dflags & (DAMAGE_NO_ARMOR | DAMAGE_NO_POWER_ARMOR))
{
return 0;
}
2009-03-12 20:03:41 +00:00
if (client)
{
power_armor_type = PowerArmorType(ent);
2009-03-12 20:03:41 +00:00
if (power_armor_type != POWER_ARMOR_NONE)
{
index = ITEM_INDEX(FindItem("Cells"));
power = client->pers.inventory[index];
}
}
else if (ent->svflags & SVF_MONSTER)
{
power_armor_type = ent->monsterinfo.power_armor_type;
power = ent->monsterinfo.power_armor_power;
}
else
{
2009-03-12 20:03:41 +00:00
return 0;
}
2009-03-12 20:03:41 +00:00
if (power_armor_type == POWER_ARMOR_NONE)
{
2009-03-12 20:03:41 +00:00
return 0;
}
2009-03-12 20:03:41 +00:00
if (!power)
{
2009-03-12 20:03:41 +00:00
return 0;
}
2009-03-12 20:03:41 +00:00
if (power_armor_type == POWER_ARMOR_SCREEN)
{
vec3_t vec;
float dot;
vec3_t forward;
/* only works if damage point is in front */
AngleVectors(ent->s.angles, forward, NULL, NULL);
VectorSubtract(point, ent->s.origin, vec);
VectorNormalize(vec);
dot = DotProduct(vec, forward);
2009-03-12 20:03:41 +00:00
if (dot <= 0.3)
{
2009-03-12 20:03:41 +00:00
return 0;
}
2009-03-12 20:03:41 +00:00
damagePerCell = 1;
pa_te_type = TE_SCREEN_SPARKS;
damage = damage / 3;
}
else
{
damagePerCell = 2;
pa_te_type = TE_SHIELD_SPARKS;
damage = (2 * damage) / 3;
}
/* etf rifle */
2009-03-12 20:03:41 +00:00
if (dflags & DAMAGE_NO_REG_ARMOR)
{
2009-03-12 20:03:41 +00:00
save = (power * damagePerCell) / 2;
}
2009-03-12 20:03:41 +00:00
else
{
2009-03-12 20:03:41 +00:00
save = power * damagePerCell;
}
2009-03-12 20:03:41 +00:00
if (!save)
{
2009-03-12 20:03:41 +00:00
return 0;
}
2009-03-12 20:03:41 +00:00
if (save > damage)
{
2009-03-12 20:03:41 +00:00
save = damage;
}
2009-03-12 20:03:41 +00:00
SpawnDamage(pa_te_type, point, normal, save);
2009-03-12 20:03:41 +00:00
ent->powerarmor_time = level.time + 0.2;
if (dflags & DAMAGE_NO_REG_ARMOR)
{
2009-03-12 20:03:41 +00:00
power_used = (save / damagePerCell) * 2;
}
2009-03-12 20:03:41 +00:00
else
{
2009-03-12 20:03:41 +00:00
power_used = save / damagePerCell;
}
2009-03-12 20:03:41 +00:00
if (client)
{
2009-03-12 20:03:41 +00:00
client->pers.inventory[index] -= power_used;
}
2009-03-12 20:03:41 +00:00
else
{
2009-03-12 20:03:41 +00:00
ent->monsterinfo.power_armor_power -= power_used;
}
2009-03-12 20:03:41 +00:00
return save;
}
int
CheckArmor(edict_t *ent, vec3_t point, vec3_t normal,
int damage, int te_sparks, int dflags)
2009-03-12 20:03:41 +00:00
{
gclient_t *client;
int save;
int index;
gitem_t *armor;
2014-02-22 12:20:22 +00:00
if (!ent)
{
return 0;
}
2014-02-22 12:20:22 +00:00
2009-03-12 20:03:41 +00:00
if (!damage)
{
2009-03-12 20:03:41 +00:00
return 0;
}
2009-03-12 20:03:41 +00:00
client = ent->client;
if (!client)
{
2009-03-12 20:03:41 +00:00
return 0;
}
2009-03-12 20:03:41 +00:00
if (dflags & (DAMAGE_NO_ARMOR | DAMAGE_NO_REG_ARMOR))
{
2009-03-12 20:03:41 +00:00
return 0;
}
index = ArmorIndex(ent);
2009-03-12 20:03:41 +00:00
if (!index)
{
2009-03-12 20:03:41 +00:00
return 0;
}
2009-03-12 20:03:41 +00:00
armor = GetItemByIndex(index);
2009-03-12 20:03:41 +00:00
if (dflags & DAMAGE_ENERGY)
{
save = ceil(((gitem_armor_t *)armor->info)->energy_protection * damage);
}
2009-03-12 20:03:41 +00:00
else
{
save = ceil(((gitem_armor_t *)armor->info)->normal_protection * damage);
}
2009-03-12 20:03:41 +00:00
if (save >= client->pers.inventory[index])
{
2009-03-12 20:03:41 +00:00
save = client->pers.inventory[index];
}
2009-03-12 20:03:41 +00:00
if (!save)
{
2009-03-12 20:03:41 +00:00
return 0;
}
2009-03-12 20:03:41 +00:00
client->pers.inventory[index] -= save;
SpawnDamage(te_sparks, point, normal, save);
2009-03-12 20:03:41 +00:00
return save;
}
void
M_ReactToDamage(edict_t *targ, edict_t *attacker, edict_t *inflictor)
2009-03-12 20:03:41 +00:00
{
qboolean new_tesla;
if (!targ || !attacker || !inflictor)
{
return;
}
2009-03-12 20:03:41 +00:00
if (!(attacker->client) && !(attacker->svflags & SVF_MONSTER))
{
2009-03-12 20:03:41 +00:00
return;
}
2009-03-12 20:03:41 +00:00
2014-02-22 12:20:22 +00:00
/* 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"))
2009-03-12 20:03:41 +00:00
{
new_tesla = MarkTeslaArea(targ, inflictor);
if (new_tesla || !targ->enemy)
{
TargetTesla(targ, inflictor);
}
2009-03-12 20:03:41 +00:00
return;
}
if ((attacker == targ) || (attacker == targ->enemy))
{
2009-03-12 20:03:41 +00:00
return;
}
2009-03-12 20:03:41 +00:00
2014-02-22 12:20:22 +00:00
/* if we are a good guy monster and
our attacker is a player or another
good guy, do not get mad at them */
2009-03-12 20:03:41 +00:00
if (targ->monsterinfo.aiflags & AI_GOOD_GUY)
{
if (attacker->client || (attacker->monsterinfo.aiflags & AI_GOOD_GUY))
{
2009-03-12 20:03:41 +00:00
return;
}
2009-03-12 20:03:41 +00:00
}
2014-02-22 12:20:22 +00:00
/* if we're currently mad at something
a target_anger made us mad at, ignore
damage */
2009-03-12 20:03:41 +00:00
if (targ->enemy && targ->monsterinfo.aiflags & AI_TARGET_ANGER)
{
float percentHealth;
2009-03-12 20:03:41 +00:00
/* make sure whatever we were pissed at is still around. */
if (targ->enemy->inuse)
2009-03-12 20:03:41 +00:00
{
percentHealth = (float)(targ->health) / (float)(targ->max_health);
if (percentHealth > 0.33)
{
2009-03-12 20:03:41 +00:00
return;
}
2009-03-12 20:03:41 +00:00
}
/* remove the target anger flag */
2009-03-12 20:03:41 +00:00
targ->monsterinfo.aiflags &= ~AI_TARGET_ANGER;
}
/* if we're healing someone, do like above and try to stay with them */
2009-03-12 20:03:41 +00:00
if ((targ->enemy) && (targ->monsterinfo.aiflags & AI_MEDIC))
{
float percentHealth;
2009-03-12 20:03:41 +00:00
percentHealth = (float)(targ->health) / (float)(targ->max_health);
/* ignore it some of the time */
if (targ->enemy->inuse && (percentHealth > 0.25))
{
2009-03-12 20:03:41 +00:00
return;
}
2009-03-12 20:03:41 +00:00
/* remove the medic flag */
2009-03-12 20:03:41 +00:00
targ->monsterinfo.aiflags &= ~AI_MEDIC;
cleanupHealTarget(targ->enemy);
2009-03-12 20:03:41 +00:00
}
2014-02-22 12:20:22 +00:00
/* if attacker is a client, get mad at them
because he's good and we're not */
2009-03-12 20:03:41 +00:00
if (attacker->client)
{
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 */
2009-03-12 20:03:41 +00:00
if (targ->enemy && targ->enemy->client)
{
if (visible(targ, targ->enemy))
{
targ->oldenemy = attacker;
return;
}
2009-03-12 20:03:41 +00:00
targ->oldenemy = targ->enemy;
}
2009-03-12 20:03:41 +00:00
targ->enemy = attacker;
2009-03-12 20:03:41 +00:00
if (!(targ->monsterinfo.aiflags & AI_DUCKED))
{
FoundTarget(targ);
}
2009-03-12 20:03:41 +00:00
return;
}
if (((targ->flags & (FL_FLY | FL_SWIM)) ==
(attacker->flags & (FL_FLY | FL_SWIM))) &&
(strcmp(targ->classname, attacker->classname) != 0) &&
2009-03-12 20:03:41 +00:00
!(attacker->monsterinfo.aiflags & AI_IGNORE_SHOTS) &&
!(targ->monsterinfo.aiflags & AI_IGNORE_SHOTS))
2009-03-12 20:03:41 +00:00
{
if (targ->enemy && targ->enemy->client)
{
2009-03-12 20:03:41 +00:00
targ->oldenemy = targ->enemy;
}
2009-03-12 20:03:41 +00:00
targ->enemy = attacker;
2009-03-12 20:03:41 +00:00
if (!(targ->monsterinfo.aiflags & AI_DUCKED))
{
FoundTarget(targ);
}
2009-03-12 20:03:41 +00:00
}
/* if they *meant* to shoot us, then shoot back */
2009-03-12 20:03:41 +00:00
else if (attacker->enemy == targ)
{
if (targ->enemy && targ->enemy->client)
{
2009-03-12 20:03:41 +00:00
targ->oldenemy = targ->enemy;
}
2009-03-12 20:03:41 +00:00
targ->enemy = attacker;
2009-03-12 20:03:41 +00:00
if (!(targ->monsterinfo.aiflags & AI_DUCKED))
{
FoundTarget(targ);
}
2009-03-12 20:03:41 +00:00
}
/* otherwise get mad at whoever they are mad at (help our buddy) unless it is us! */
else if (attacker->enemy)
2009-03-12 20:03:41 +00:00
{
if (targ->enemy && targ->enemy->client)
{
2009-03-12 20:03:41 +00:00
targ->oldenemy = targ->enemy;
}
2009-03-12 20:03:41 +00:00
targ->enemy = attacker->enemy;
2009-03-12 20:03:41 +00:00
if (!(targ->monsterinfo.aiflags & AI_DUCKED))
{
FoundTarget(targ);
}
2009-03-12 20:03:41 +00:00
}
}
qboolean
CheckTeamDamage(edict_t *targ, edict_t *attacker)
2009-03-12 20:03:41 +00:00
{
return false;
}
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)
2009-03-12 20:03:41 +00:00
{
gclient_t *client;
int take;
int save;
int asave;
int psave;
int te_sparks;
int sphere_notified;
if (!targ || !inflictor || !attacker)
{
return;
}
2009-03-12 20:03:41 +00:00
if (!targ->takedamage)
{
2009-03-12 20:03:41 +00:00
return;
}
2009-03-12 20:03:41 +00:00
sphere_notified = false;
2009-03-12 20:03:41 +00:00
/* 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))
2009-03-12 20:03:41 +00:00
{
if (OnSameTeam(targ, attacker))
2009-03-12 20:03:41 +00:00
{
/* nukes kill everyone */
if (((int)(dmflags->value) & DF_NO_FRIENDLY_FIRE) &&
(mod != MOD_NUKE))
{
2009-03-12 20:03:41 +00:00
damage = 0;
}
2009-03-12 20:03:41 +00:00
else
{
2009-03-12 20:03:41 +00:00
mod |= MOD_FRIENDLY_FIRE;
}
2009-03-12 20:03:41 +00:00
}
}
2009-03-12 20:03:41 +00:00
meansOfDeath = mod;
/* allow the deathmatch game to change values */
2009-03-12 20:03:41 +00:00
if (deathmatch->value && gamerules && gamerules->value)
{
if (DMGame.ChangeDamage)
{
2009-03-12 20:03:41 +00:00
damage = DMGame.ChangeDamage(targ, attacker, damage, mod);
}
if (DMGame.ChangeKnockback)
{
2009-03-12 20:03:41 +00:00
knockback = DMGame.ChangeKnockback(targ, attacker, knockback, mod);
}
2009-03-12 20:03:41 +00:00
if (!damage)
{
2009-03-12 20:03:41 +00:00
return;
}
2009-03-12 20:03:41 +00:00
}
/* easy mode takes half damage */
if ((skill->value == 0) && (deathmatch->value == 0) && targ->client)
2009-03-12 20:03:41 +00:00
{
damage *= 0.5;
2009-03-12 20:03:41 +00:00
if (!damage)
{
2009-03-12 20:03:41 +00:00
damage = 1;
}
2009-03-12 20:03:41 +00:00
}
client = targ->client;
/* defender sphere takes half damage */
if ((client) && (client->owned_sphere) &&
(client->owned_sphere->spawnflags == 1))
2009-03-12 20:03:41 +00:00
{
damage *= 0.5;
2009-03-12 20:03:41 +00:00
if (!damage)
{
2009-03-12 20:03:41 +00:00
damage = 1;
}
2009-03-12 20:03:41 +00:00
}
if (dflags & DAMAGE_BULLET)
{
2009-03-12 20:03:41 +00:00
te_sparks = TE_BULLET_SPARKS;
}
2009-03-12 20:03:41 +00:00
else
{
2009-03-12 20:03:41 +00:00
te_sparks = TE_SPARKS;
}
2009-03-12 20:03:41 +00:00
VectorNormalize(dir);
/* bonus damage for suprising a monster */
if (!(dflags & DAMAGE_RADIUS) && (targ->svflags & SVF_MONSTER) &&
(attacker->client) && (!targ->enemy) && (targ->health > 0))
{
2009-03-12 20:03:41 +00:00
damage *= 2;
}
2009-03-12 20:03:41 +00:00
if (targ->flags & FL_NO_KNOCKBACK)
{
2009-03-12 20:03:41 +00:00
knockback = 0;
}
2009-03-12 20:03:41 +00:00
/* figure momentum add */
2009-03-12 20:03:41 +00:00
if (!(dflags & DAMAGE_NO_KNOCKBACK))
{
if ((knockback) && (targ->movetype != MOVETYPE_NONE) &&
(targ->movetype != MOVETYPE_BOUNCE) &&
(targ->movetype != MOVETYPE_PUSH) &&
(targ->movetype != MOVETYPE_STOP))
2009-03-12 20:03:41 +00:00
{
vec3_t kvel;
float mass;
2009-03-12 20:03:41 +00:00
if (targ->mass < 50)
{
2009-03-12 20:03:41 +00:00
mass = 50;
}
2009-03-12 20:03:41 +00:00
else
{
2009-03-12 20:03:41 +00:00
mass = targ->mass;
}
2009-03-12 20:03:41 +00:00
if (targ->client && (attacker == targ))
{
/* the rocket jump hack... */
VectorScale(dir, 1600.0 * (float)knockback / mass, kvel);
}
2009-03-12 20:03:41 +00:00
else
{
VectorScale(dir, 500.0 * (float)knockback / mass, kvel);
}
2009-03-12 20:03:41 +00:00
VectorAdd(targ->velocity, kvel, targ->velocity);
2009-03-12 20:03:41 +00:00
}
}
take = damage;
save = 0;
/* check for godmode */
if ((targ->flags & FL_GODMODE) && !(dflags & DAMAGE_NO_PROTECTION))
2009-03-12 20:03:41 +00:00
{
take = 0;
save = damage;
SpawnDamage(te_sparks, point, normal, save);
2009-03-12 20:03:41 +00:00
}
/* check for invincibility */
if ((client &&
(client->invincible_framenum > level.framenum)) &&
!(dflags & DAMAGE_NO_PROTECTION))
2009-03-12 20:03:41 +00:00
{
if (targ->pain_debounce_time < level.time)
{
gi.sound(targ, CHAN_ITEM, gi.soundindex( "items/protect4.wav"), 1, ATTN_NORM, 0);
2009-03-12 20:03:41 +00:00
targ->pain_debounce_time = level.time + 2;
}
2009-03-12 20:03:41 +00:00
take = 0;
save = damage;
}
/* check for monster invincibility */
if (((targ->svflags & SVF_MONSTER) &&
(targ->monsterinfo.invincible_framenum > level.framenum)) &&
!(dflags & DAMAGE_NO_PROTECTION))
2009-03-12 20:03:41 +00:00
{
if (targ->pain_debounce_time < level.time)
{
gi.sound(targ, CHAN_ITEM, gi.soundindex( "items/protect4.wav"), 1, ATTN_NORM, 0);
2009-03-12 20:03:41 +00:00
targ->pain_debounce_time = level.time + 2;
}
2009-03-12 20:03:41 +00:00
take = 0;
save = damage;
}
psave = CheckPowerArmor(targ, point, normal, take, dflags);
2009-03-12 20:03:41 +00:00
take -= psave;
asave = CheckArmor(targ, point, normal, take, te_sparks, dflags);
2009-03-12 20:03:41 +00:00
take -= asave;
/* treat cheat/powerup savings the same as armor */
2009-03-12 20:03:41 +00:00
asave += save;
/* team damage avoidance */
if (!(dflags & DAMAGE_NO_PROTECTION) && CheckTeamDamage(targ, attacker))
{
2009-03-12 20:03:41 +00:00
return;
}
2009-03-12 20:03:41 +00:00
2014-02-22 12:20:22 +00:00
/* this option will do damage both to the armor
and person. originally for DPU rounds */
2009-03-12 20:03:41 +00:00
if (dflags & DAMAGE_DESTROY_ARMOR)
{
if (!(targ->flags & FL_GODMODE) && !(dflags & DAMAGE_NO_PROTECTION) &&
!(client && (client->invincible_framenum > level.framenum)))
2009-03-12 20:03:41 +00:00
{
take = damage;
}
}
/* do the damage */
2009-03-12 20:03:41 +00:00
if (take)
{
/* need more blood for chainfist. */
if (targ->flags & FL_MECHANICAL)
2009-03-12 20:03:41 +00:00
{
SpawnDamage(TE_ELECTRIC_SPARKS, point, normal, take);
2009-03-12 20:03:41 +00:00
}
else if ((targ->svflags & SVF_MONSTER) || (client))
{
if (mod == MOD_CHAINFIST)
{
SpawnDamage(TE_MOREBLOOD, point, normal, 255);
}
2009-03-12 20:03:41 +00:00
else
{
SpawnDamage(TE_BLOOD, point, normal, take);
}
2009-03-12 20:03:41 +00:00
}
else
{
SpawnDamage(te_sparks, point, normal, take);
}
2009-03-12 20:03:41 +00:00
targ->health = targ->health - take;
/* spheres need to know who to shoot at */
if (client && client->owned_sphere)
2009-03-12 20:03:41 +00:00
{
sphere_notified = true;
if (client->owned_sphere->pain)
{
client->owned_sphere->pain(client->owned_sphere, attacker, 0, 0);
}
2009-03-12 20:03:41 +00:00
}
if (targ->health <= 0)
{
if ((targ->svflags & SVF_MONSTER) || (client))
{
2009-03-12 20:03:41 +00:00
targ->flags |= FL_NO_KNOCKBACK;
}
Killed(targ, inflictor, attacker, take, point);
2009-03-12 20:03:41 +00:00
return;
}
}
/* spheres need to know who to shoot at */
2009-03-12 20:03:41 +00:00
if (!sphere_notified)
{
if (client && client->owned_sphere)
2009-03-12 20:03:41 +00:00
{
if (client->owned_sphere->pain)
{
client->owned_sphere->pain(client->owned_sphere, attacker, 0,
0);
}
2009-03-12 20:03:41 +00:00
}
}
if (targ->svflags & SVF_MONSTER)
{
M_ReactToDamage(targ, attacker, inflictor);
2009-03-12 20:03:41 +00:00
if (!(targ->monsterinfo.aiflags & AI_DUCKED) && (take))
{
targ->pain(targ, attacker, knockback, take);
/* nightmare mode monsters don't go into pain frames often */
2009-03-12 20:03:41 +00:00
if (skill->value == 3)
{
2009-03-12 20:03:41 +00:00
targ->pain_debounce_time = level.time + 5;
}
2009-03-12 20:03:41 +00:00
}
}
else if (client)
{
if (!(targ->flags & FL_GODMODE) && (take))
{
targ->pain(targ, attacker, knockback, take);
}
2009-03-12 20:03:41 +00:00
}
else if (take)
{
if (targ->pain)
{
targ->pain(targ, attacker, knockback, take);
}
2009-03-12 20:03:41 +00:00
}
/* 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 */
2009-03-12 20:03:41 +00:00
if (client)
{
client->damage_parmor += psave;
client->damage_armor += asave;
client->damage_blood += take;
client->damage_knockback += knockback;
VectorCopy(point, client->damage_from);
2009-03-12 20:03:41 +00:00
}
}
void
T_RadiusDamage(edict_t *inflictor, edict_t *attacker, float damage,
edict_t *ignore, float radius, int mod)
2009-03-12 20:03:41 +00:00
{
float points;
edict_t *ent = NULL;
vec3_t v;
vec3_t dir;
if (!inflictor || !attacker)
{
return;
}
2009-03-12 20:03:41 +00:00
while ((ent = findradius(ent, inflictor->s.origin, radius)) != NULL)
{
if (ent == ignore)
{
2009-03-12 20:03:41 +00:00
continue;
}
2009-03-12 20:03:41 +00:00
if (!ent->takedamage)
{
2009-03-12 20:03:41 +00:00
continue;
}
VectorAdd(ent->mins, ent->maxs, v);
VectorMA(ent->s.origin, 0.5, v, v);
VectorSubtract(inflictor->s.origin, v, v);
points = damage - 0.5 * VectorLength(v);
2009-03-12 20:03:41 +00:00
if (ent == attacker)
{
2009-03-12 20:03:41 +00:00
points = points * 0.5;
}
2009-03-12 20:03:41 +00:00
if (points > 0)
{
if (CanDamage(ent, inflictor))
2009-03-12 20:03:41 +00:00
{
VectorSubtract(ent->s.origin, inflictor->s.origin, dir);
T_Damage(ent, inflictor, attacker, dir, inflictor->s.origin, vec3_origin,
(int)points, (int)points, DAMAGE_RADIUS, mod);
2009-03-12 20:03:41 +00:00
}
}
}
}
void
T_RadiusNukeDamage(edict_t *inflictor, edict_t *attacker, float damage,
edict_t *ignore, float radius, int mod)
2009-03-12 20:03:41 +00:00
{
float points;
edict_t *ent = NULL;
vec3_t v;
vec3_t dir;
float len;
float killzone, killzone2;
trace_t tr;
float dist;
2009-03-12 20:03:41 +00:00
killzone = radius;
killzone2 = radius * 2.0;
if (!inflictor || !attacker || !ignore)
{
return;
}
2009-03-12 20:03:41 +00:00
while ((ent = findradius(ent, inflictor->s.origin, killzone2)) != NULL)
{
/* ignore nobody */
2009-03-12 20:03:41 +00:00
if (ent == ignore)
{
2009-03-12 20:03:41 +00:00
continue;
}
2009-03-12 20:03:41 +00:00
if (!ent->takedamage)
{
2009-03-12 20:03:41 +00:00
continue;
}
2009-03-12 20:03:41 +00:00
if (!ent->inuse)
{
2009-03-12 20:03:41 +00:00
continue;
}
if (!(ent->client || (ent->svflags & SVF_MONSTER) ||
(ent->svflags & SVF_DAMAGEABLE)))
{
2009-03-12 20:03:41 +00:00
continue;
}
2009-03-12 20:03:41 +00:00
VectorAdd(ent->mins, ent->maxs, v);
VectorMA(ent->s.origin, 0.5, v, v);
VectorSubtract(inflictor->s.origin, v, v);
2009-03-12 20:03:41 +00:00
len = VectorLength(v);
2009-03-12 20:03:41 +00:00
if (len <= killzone)
{
if (ent->client)
{
2009-03-12 20:03:41 +00:00
ent->flags |= FL_NOGIB;
}
2009-03-12 20:03:41 +00:00
points = 10000;
}
else if (len <= killzone2)
{
points = (damage / killzone) * (killzone2 - len);
}
2009-03-12 20:03:41 +00:00
else
{
2009-03-12 20:03:41 +00:00
points = 0;
}
2009-03-12 20:03:41 +00:00
if (points > 0)
{
if (ent->client)
{
ent->client->nuke_framenum = level.framenum + 20;
}
VectorSubtract(ent->s.origin, inflictor->s.origin, dir);
T_Damage(ent, inflictor, attacker, dir, inflictor->s.origin,
vec3_origin, (int)points, (int)points, DAMAGE_RADIUS,
mod);
2009-03-12 20:03:41 +00:00
}
}
/* skip the worldspawn */
ent = g_edicts + 1;
/* cycle through players */
2009-03-12 20:03:41 +00:00
while (ent)
{
if ((ent->client) &&
(ent->client->nuke_framenum != level.framenum + 20) && (ent->inuse))
2009-03-12 20:03:41 +00:00
{
tr = gi.trace(inflictor->s.origin, NULL, NULL, ent->s.origin,
inflictor, MASK_SOLID);
2009-03-12 20:03:41 +00:00
if (tr.fraction == 1.0)
{
ent->client->nuke_framenum = level.framenum + 20;
}
else
{
dist = realrange(ent, inflictor);
2009-03-12 20:03:41 +00:00
if (dist < 2048)
{
ent->client->nuke_framenum = max(ent->client->nuke_framenum,
level.framenum + 15);
}
2009-03-12 20:03:41 +00:00
else
{
ent->client->nuke_framenum = max(ent->client->nuke_framenum,
level.framenum + 10);
}
2009-03-12 20:03:41 +00:00
}
2009-03-12 20:03:41 +00:00
ent++;
}
else
{
2009-03-12 20:03:41 +00:00
ent = NULL;
}
2009-03-12 20:03:41 +00:00
}
}
/*
2014-02-22 12:20:22 +00:00
* Like T_RadiusDamage, but ignores
* anything with classname=ignoreClass
*/
void
T_RadiusClassDamage(edict_t *inflictor, edict_t *attacker, float damage,
char *ignoreClass, float radius, int mod)
2009-03-12 20:03:41 +00:00
{
float points;
edict_t *ent = NULL;
vec3_t v;
vec3_t dir;
if (!inflictor || !attacker || !ignoreClass)
{
return;
}
2009-03-12 20:03:41 +00:00
while ((ent = findradius(ent, inflictor->s.origin, radius)) != NULL)
{
if (ent->classname && !strcmp(ent->classname, ignoreClass))
{
2009-03-12 20:03:41 +00:00
continue;
}
2009-03-12 20:03:41 +00:00
if (!ent->takedamage)
{
2009-03-12 20:03:41 +00:00
continue;
}
VectorAdd(ent->mins, ent->maxs, v);
VectorMA(ent->s.origin, 0.5, v, v);
VectorSubtract(inflictor->s.origin, v, v);
points = damage - 0.5 * VectorLength(v);
2009-03-12 20:03:41 +00:00
if (ent == attacker)
{
2009-03-12 20:03:41 +00:00
points = points * 0.5;
}
2009-03-12 20:03:41 +00:00
if (points > 0)
{
if (CanDamage(ent, inflictor))
2009-03-12 20:03:41 +00:00
{
VectorSubtract(ent->s.origin, inflictor->s.origin, dir);
T_Damage(ent, inflictor, attacker, dir, inflictor->s.origin,
vec3_origin, (int)points, (int)points, DAMAGE_RADIUS,
mod);
2009-03-12 20:03:41 +00:00
}
}
}
}