mirror of
https://github.com/blendogames/thirtyflightsofloving.git
synced 2025-01-18 06:22:30 +00:00
95295401a4
Added weapon balancing and tech control cvars to 3ZB2 DLL. Added Vampire and Ammogen techs to 3ZB2 game DLL. Fixed func_door_secret movement bug (when built with origin brush) in Zaero DLL. Fixed armor stacking bug in default Lazarus, missionpack, and Zaero DLLs. Removed unused tech cvars in default Lazarus DLL. Changed parsing of TE_BLASTER_COLORED tempent.
545 lines
13 KiB
C
545 lines
13 KiB
C
// g_combat.c
|
|
|
|
#include "g_local.h"
|
|
#include "bot.h"
|
|
/*
|
|
============
|
|
CanDamage
|
|
|
|
Returns true if the inflictor can directly damage the target. Used for
|
|
explosions and melee attacks.
|
|
============
|
|
*/
|
|
qboolean CanDamage (edict_t *targ, edict_t *inflictor)
|
|
{
|
|
vec3_t dest;
|
|
trace_t trace;
|
|
|
|
// bmodels need special checking because their origin is 0,0,0
|
|
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);
|
|
if (trace.fraction == 1.0)
|
|
return true;
|
|
if (trace.ent == targ)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, targ->s.origin, inflictor, MASK_SOLID);
|
|
if (trace.fraction == 1.0)
|
|
return true;
|
|
|
|
VectorCopy (targ->s.origin, dest);
|
|
dest[0] += 15.0;
|
|
dest[1] += 15.0;
|
|
trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID);
|
|
if (trace.fraction == 1.0)
|
|
return true;
|
|
|
|
VectorCopy (targ->s.origin, dest);
|
|
dest[0] += 15.0;
|
|
dest[1] -= 15.0;
|
|
trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID);
|
|
if (trace.fraction == 1.0)
|
|
return true;
|
|
|
|
VectorCopy (targ->s.origin, dest);
|
|
dest[0] -= 15.0;
|
|
dest[1] += 15.0;
|
|
trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID);
|
|
if (trace.fraction == 1.0)
|
|
return true;
|
|
|
|
VectorCopy (targ->s.origin, dest);
|
|
dest[0] -= 15.0;
|
|
dest[1] -= 15.0;
|
|
trace = gi.trace (inflictor->s.origin, vec3_origin, vec3_origin, dest, inflictor, MASK_SOLID);
|
|
if (trace.fraction == 1.0)
|
|
return true;
|
|
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/*
|
|
============
|
|
Killed
|
|
============
|
|
*/
|
|
void Killed (edict_t *targ, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point)
|
|
{
|
|
if (targ->health < -999)
|
|
targ->health = -999;
|
|
|
|
targ->enemy = attacker;
|
|
|
|
if ((targ->svflags & SVF_MONSTER) && (targ->deadflag != DEAD_DEAD))
|
|
{
|
|
// targ->svflags |= SVF_DEADMONSTER; // now treat as a different content type
|
|
if (!(targ->monsterinfo.aiflags & AI_GOOD_GUY))
|
|
{
|
|
level.killed_monsters++;
|
|
if (coop->value && attacker->client)
|
|
attacker->client->resp.score++;
|
|
// medics won't heal monsters that they kill themselves
|
|
if (strcmp(attacker->classname, "monster_medic") == 0)
|
|
targ->owner = attacker;
|
|
}
|
|
}
|
|
|
|
if (targ->movetype == MOVETYPE_PUSH || targ->movetype == MOVETYPE_STOP || targ->movetype == MOVETYPE_NONE)
|
|
{ // doors, triggers, etc
|
|
targ->die (targ, inflictor, attacker, damage, point);
|
|
return;
|
|
}
|
|
|
|
/* if ((targ->svflags & SVF_MONSTER) && (targ->deadflag != DEAD_DEAD))
|
|
{
|
|
targ->touch = NULL;
|
|
monster_death_use (targ);
|
|
}
|
|
*/
|
|
targ->die (targ, inflictor, attacker, damage, point);
|
|
}
|
|
|
|
|
|
/*
|
|
================
|
|
SpawnDamage
|
|
================
|
|
*/
|
|
void SpawnDamage (int type, vec3_t origin, vec3_t normal, int damage)
|
|
{
|
|
if (damage > 255)
|
|
damage = 255;
|
|
gi.WriteByte (svc_temp_entity);
|
|
gi.WriteByte (type);
|
|
// gi.WriteByte (damage);
|
|
gi.WritePosition (origin);
|
|
gi.WriteDir (normal);
|
|
gi.multicast (origin, MULTICAST_PVS);
|
|
}
|
|
|
|
|
|
/*
|
|
============
|
|
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
|
|
============
|
|
*/
|
|
static int CheckPowerArmor (edict_t *ent, vec3_t point, vec3_t normal, int damage, int dflags)
|
|
{
|
|
gclient_t *client;
|
|
int save;
|
|
int power_armor_type;
|
|
int index;
|
|
int damagePerCell;
|
|
int pa_te_type;
|
|
int power;
|
|
int power_used;
|
|
|
|
if (!damage)
|
|
return 0;
|
|
|
|
client = ent->client;
|
|
|
|
if (dflags & DAMAGE_NO_ARMOR)
|
|
return 0;
|
|
|
|
if (client)
|
|
{
|
|
power_armor_type = PowerArmorType (ent);
|
|
if (power_armor_type != POWER_ARMOR_NONE)
|
|
{
|
|
index = ITEM_INDEX(Fdi_CELLS/*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
|
|
return 0;
|
|
|
|
if (power_armor_type == POWER_ARMOR_NONE)
|
|
return 0;
|
|
if (!power)
|
|
return 0;
|
|
|
|
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);
|
|
if (dot <= 0.3)
|
|
return 0;
|
|
|
|
damagePerCell = 1;
|
|
pa_te_type = TE_SCREEN_SPARKS;
|
|
damage = damage / 3;
|
|
}
|
|
else
|
|
{
|
|
damagePerCell = 2;
|
|
pa_te_type = TE_SHIELD_SPARKS;
|
|
damage = (2 * damage) / 3;
|
|
}
|
|
|
|
save = power * damagePerCell;
|
|
if (!save)
|
|
return 0;
|
|
if (save > damage)
|
|
save = damage;
|
|
|
|
SpawnDamage (pa_te_type, point, normal, save);
|
|
ent->powerarmor_time = level.time + 0.2;
|
|
|
|
power_used = save / damagePerCell;
|
|
|
|
if (client)
|
|
client->pers.inventory[index] -= power_used;
|
|
else
|
|
ent->monsterinfo.power_armor_power -= power_used;
|
|
return save;
|
|
}
|
|
|
|
static int CheckArmor (edict_t *ent, vec3_t point, vec3_t normal, int damage, int te_sparks, int dflags)
|
|
{
|
|
gclient_t *client;
|
|
int save;
|
|
int index;
|
|
gitem_t *armor;
|
|
|
|
if (!damage)
|
|
return 0;
|
|
|
|
client = ent->client;
|
|
|
|
if (!client)
|
|
return 0;
|
|
|
|
if (dflags & DAMAGE_NO_ARMOR)
|
|
return 0;
|
|
|
|
index = ArmorIndex (ent);
|
|
if (!index)
|
|
return 0;
|
|
|
|
armor = GetItemByIndex (index);
|
|
|
|
if (dflags & DAMAGE_ENERGY)
|
|
save = ceil(((gitem_armor_t *)armor->info)->energy_protection*damage);
|
|
else
|
|
save = ceil(((gitem_armor_t *)armor->info)->normal_protection*damage);
|
|
if (save >= client->pers.inventory[index])
|
|
save = client->pers.inventory[index];
|
|
|
|
if (!save)
|
|
return 0;
|
|
|
|
client->pers.inventory[index] -= save;
|
|
SpawnDamage (te_sparks, point, normal, save);
|
|
|
|
return save;
|
|
}
|
|
|
|
qboolean CheckTeamDamage (edict_t *targ, edict_t *attacker)
|
|
{
|
|
//ZOID
|
|
if (ctf->value && targ->client && attacker->client)
|
|
if (targ->client->resp.ctf_team == attacker->client->resp.ctf_team &&
|
|
targ != attacker)
|
|
return true;
|
|
//ZOID
|
|
|
|
//FIXME make the next line real and uncomment this block
|
|
// if ((ability to damage a teammate == OFF) && (targ's team == attacker's team))
|
|
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)
|
|
{
|
|
gclient_t *client;
|
|
int take;
|
|
int save;
|
|
int asave;
|
|
int psave;
|
|
int te_sparks;
|
|
|
|
if (!targ->takedamage)
|
|
return;
|
|
|
|
if(mod == MOD_CRUSH)
|
|
{
|
|
//bot's state change
|
|
if((targ->svflags & SVF_MONSTER) && targ->client)
|
|
{
|
|
if((targ->client->zc.waitin_obj == inflictor && targ->client->zc.zcstate)
|
|
|| targ->groundentity == inflictor)
|
|
{
|
|
// gi.bprintf(PRINT_HIGH,"MOOOOOOOOOOOOOOOOOOOO\n");
|
|
targ->client->zc.zcstate |= STS_W_DONT;
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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)
|
|
damage = 0;
|
|
else
|
|
mod |= MOD_FRIENDLY_FIRE;
|
|
}
|
|
else if(targ->client && !(targ->svflags & SVF_MONSTER))
|
|
{
|
|
if(attacker->client) targ->client->zc.first_target = attacker;
|
|
}
|
|
}
|
|
meansOfDeath = mod;
|
|
|
|
// easy mode takes half damage
|
|
if (skill->value == 0 && deathmatch->value == 0 && targ->client)
|
|
{
|
|
damage *= 0.5;
|
|
if (!damage)
|
|
damage = 1;
|
|
}
|
|
|
|
client = targ->client;
|
|
|
|
if (dflags & DAMAGE_BULLET)
|
|
te_sparks = TE_BULLET_SPARKS;
|
|
else
|
|
te_sparks = TE_SPARKS;
|
|
|
|
VectorNormalize(dir);
|
|
|
|
// bonus damage for suprising a monster
|
|
if (!(dflags & DAMAGE_RADIUS) && (targ->svflags & SVF_MONSTER) && (attacker->client) && (!targ->enemy) && (targ->health > 0))
|
|
damage *= 2;
|
|
|
|
//ZOID
|
|
//strength tech
|
|
damage = CTFApplyStrength(attacker, damage);
|
|
//ZOID
|
|
|
|
if (!OnSameTeam(targ, attacker))
|
|
CTFApplyVampire (attacker, damage);
|
|
|
|
if (targ->flags & FL_NO_KNOCKBACK)
|
|
knockback = 0;
|
|
|
|
// figure momentum add
|
|
if (!(dflags & DAMAGE_NO_KNOCKBACK))
|
|
{
|
|
if ((knockback) && (targ->movetype != MOVETYPE_NONE) && (targ->movetype != MOVETYPE_BOUNCE) && (targ->movetype != MOVETYPE_PUSH) && (targ->movetype != MOVETYPE_STOP))
|
|
{
|
|
vec3_t kvel;
|
|
float mass;
|
|
|
|
if (targ->mass < 50)
|
|
mass = 50;
|
|
else
|
|
mass = targ->mass;
|
|
|
|
if (targ->client && attacker == targ)
|
|
VectorScale (dir, 1600.0 * (float)knockback / mass, kvel); // the rocket jump hack...
|
|
else
|
|
VectorScale (dir, 500.0 * (float)knockback / mass, kvel);
|
|
|
|
VectorAdd (targ->velocity, kvel, targ->velocity);
|
|
}
|
|
}
|
|
|
|
take = damage;
|
|
save = 0;
|
|
|
|
// check for godmode
|
|
if ( (targ->flags & FL_GODMODE) && !(dflags & DAMAGE_NO_PROTECTION) )
|
|
{
|
|
take = 0;
|
|
save = damage;
|
|
SpawnDamage (te_sparks, point, normal, save);
|
|
}
|
|
|
|
// check for invincibility
|
|
if ((client && client->invincible_framenum > level.framenum ) && !(dflags & DAMAGE_NO_PROTECTION))
|
|
{
|
|
if (targ->pain_debounce_time < level.time)
|
|
{
|
|
gi.sound(targ, CHAN_ITEM, gi.soundindex("items/protect3.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;
|
|
}
|
|
take = 0;
|
|
save = damage;
|
|
}
|
|
|
|
//ZOID
|
|
//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 {
|
|
//ZOID
|
|
|
|
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;
|
|
|
|
//ZOID
|
|
//resistance tech
|
|
take = CTFApplyResistance(targ, take);
|
|
//ZOID
|
|
|
|
// team damage avoidance
|
|
if (!(dflags & DAMAGE_NO_PROTECTION) && CheckTeamDamage (targ, attacker))
|
|
return;
|
|
|
|
//ZOID
|
|
CTFCheckHurtCarrier(targ, attacker);
|
|
//ZOID
|
|
|
|
// do the damage
|
|
if (take)
|
|
{
|
|
if ((targ->svflags & SVF_MONSTER) || (client))
|
|
{
|
|
SpawnDamage (TE_BLOOD, point, normal, take);
|
|
if(client && (targ->svflags & SVF_MONSTER) && attacker)
|
|
{
|
|
if(client->zc.battlemode & FIRE_CHIKEN) client->zc.battlemode &= ~FIRE_CHIKEN;
|
|
|
|
if(mod == MOD_RAILGUN
|
|
|| mod == MOD_BFG_LASER
|
|
|| mod == MOD_ROCKET
|
|
|| mod == MOD_BLASTER
|
|
|| mod == MOD_RIPPER
|
|
|| mod == MOD_HYPERBLASTER
|
|
|| mod == MOD_PHALANX)
|
|
{
|
|
if(attacker->client
|
|
&& (9 * random() < Bot[client->zc.botindex].param[BOP_REACTION])
|
|
&& !client->zc.first_target)
|
|
{
|
|
if(!OnSameTeam (targ, attacker)) client->zc.first_target = attacker;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
SpawnDamage (te_sparks, point, normal, take);
|
|
|
|
|
|
targ->health = targ->health - take;
|
|
|
|
if (targ->health <= 0)
|
|
{
|
|
if ((targ->svflags & SVF_MONSTER) || (client))
|
|
targ->flags |= FL_NO_KNOCKBACK;
|
|
Killed (targ, inflictor, attacker, take, point);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (client)
|
|
{
|
|
if (!(targ->flags & FL_GODMODE) && (take))
|
|
targ->pain (targ, attacker, knockback, take);
|
|
}
|
|
else if (take)
|
|
{
|
|
if (targ->pain)
|
|
targ->pain (targ, attacker, knockback, take);
|
|
}
|
|
|
|
// 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;
|
|
client->damage_armor += asave;
|
|
client->damage_blood += take;
|
|
client->damage_knockback += knockback;
|
|
VectorCopy (point, client->damage_from);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
============
|
|
T_RadiusDamage
|
|
============
|
|
*/
|
|
void T_RadiusDamage (edict_t *inflictor, edict_t *attacker, float damage, edict_t *ignore, float radius, int mod)
|
|
{
|
|
float points;
|
|
edict_t *ent = NULL;
|
|
vec3_t v;
|
|
vec3_t dir;
|
|
|
|
while ((ent = findradius(ent, inflictor->s.origin, radius)) != NULL)
|
|
{
|
|
if (ent == ignore)
|
|
continue;
|
|
if (!ent->takedamage)
|
|
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);
|
|
if (ent == attacker)
|
|
points = points * 0.5;
|
|
if (points > 0)
|
|
{
|
|
if (CanDamage (ent, inflictor))
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|