mirror of
https://git.code.sf.net/p/quake/prozac-qfcc
synced 2024-11-27 22:42:07 +00:00
897 lines
26 KiB
C++
897 lines
26 KiB
C++
/*======================================================
|
|
COMBAT.QC Custom TeamFortress v3.2
|
|
|
|
(c) TeamFortress Software Pty Ltd 29/2/97
|
|
(c) William Kerney 5/19/00
|
|
========================================================
|
|
All the functions pertaining to killing people
|
|
======================================================*/
|
|
|
|
|
|
void(string gibname, float dm) ThrowGib;
|
|
void() T_MissileTouch;
|
|
void() info_player_start;
|
|
void(entity targ, entity attacker) ClientObituary;
|
|
|
|
// TeamFortress Prototypes
|
|
void(entity Goal, entity AP, float addb) DoResults;
|
|
float(entity Goal, entity AP) Activated;
|
|
void() monster_death_use;
|
|
float (entity targ, entity attacker, float damage) TeamEqualiseDamage;
|
|
void(entity bastard,float threshold) createBastard;
|
|
void (entity targ,float pain) RevealThief;
|
|
|
|
//WK
|
|
void() GuerillaExplode;
|
|
float(entity tester) IsBuilding;
|
|
//float(entity happyboy,float newtime) makeImmune;
|
|
|
|
//- OfN -
|
|
float(entity thing) IsMonster;
|
|
void(entity body) ExpBody;
|
|
string(entity thebuilding) GetBuildingName;
|
|
string(entity thething) GetEnemyName;
|
|
void(entity tfield, vector where, entity thing) FieldExplosion;
|
|
void(entity field) PutFieldWork;
|
|
|
|
#ifndef COOP_MODE
|
|
|
|
/*================
|
|
monster_death_use (from monsters.qc)
|
|
|
|
When a monster dies, it fires all of its targets with the current
|
|
enemy as activator.
|
|
================
|
|
*/
|
|
void() monster_death_use =
|
|
{
|
|
local entity ent, otemp, stemp;
|
|
|
|
// fall to ground
|
|
if (self.flags & #FL_FLY)
|
|
self.flags = self.flags - #FL_FLY;
|
|
if (self.flags & #FL_SWIM)
|
|
self.flags = self.flags - #FL_SWIM;
|
|
|
|
if (!self.target)
|
|
return;
|
|
|
|
activator = self.enemy;
|
|
SUB_UseTargets ();
|
|
};
|
|
#endif
|
|
|
|
|
|
//============================================================================
|
|
|
|
/*
|
|
============
|
|
CanDamage
|
|
|
|
Returns true if the inflictor can directly damage the target. Used for
|
|
explosions and melee attacks.
|
|
============
|
|
*/
|
|
float(entity targ, entity inflictor) CanDamage =
|
|
{
|
|
local vector soffset; // - OfN - hackish fix for turretized sentry guns
|
|
soffset='0 0 0';
|
|
|
|
// bmodels need special checking because their origin is 0,0,0
|
|
if (targ.movetype == #MOVETYPE_PUSH)
|
|
{
|
|
traceline(inflictor.origin, 0.5 * (targ.absmin + targ.absmax), #TRUE, self);
|
|
if (trace_fraction == 1)
|
|
return #TRUE;
|
|
if (trace_ent == targ)
|
|
return #TRUE;
|
|
return #FALSE;
|
|
}
|
|
|
|
if (targ.classname == "building_sentrygun" && (targ.tf_items & #NIT_TURRET))
|
|
soffset='0 0 -20';
|
|
|
|
// OfN - Force field
|
|
traceline(inflictor.origin, targ.origin + soffset, #FALSE, self);
|
|
if (trace_ent.classname == "force_field")
|
|
{
|
|
FieldExplosion(trace_ent,trace_endpos,trace_ent);
|
|
PutFieldWork(trace_ent);
|
|
return #FALSE;
|
|
}
|
|
|
|
traceline(inflictor.origin, targ.origin + soffset, #TRUE, self);
|
|
if (trace_fraction == 1)
|
|
return #TRUE;
|
|
/*traceline(inflictor.origin, targ.origin + '15 15 0' + soffset, #TRUE, self);
|
|
if (trace_fraction == 1)
|
|
return #TRUE;
|
|
traceline(inflictor.origin, targ.origin + '-15 -15 0' + soffset, #TRUE, self);
|
|
if (trace_fraction == 1)
|
|
return #TRUE;
|
|
traceline(inflictor.origin, targ.origin + '-15 15 0' + soffset, #TRUE, self);
|
|
if (trace_fraction == 1)
|
|
return #TRUE;
|
|
traceline(inflictor.origin, targ.origin + '15 -15 0' + soffset, #TRUE, self);
|
|
if (trace_fraction == 1)
|
|
return #TRUE;*/ // OfN - Fixes dets thru wall?
|
|
|
|
return #FALSE;
|
|
};
|
|
|
|
|
|
/*
|
|
============
|
|
Killed
|
|
============
|
|
*/
|
|
void(entity targ, entity attacker) Killed =
|
|
{
|
|
// SOLVES BUG ???? // Stack overflow?
|
|
if (targ.is_killed == #TRUE)
|
|
{
|
|
if (IsBuilding(targ))
|
|
{
|
|
local string st;
|
|
RPrint("(OfN WARNING:) Building: '");
|
|
st=GetBuildingName(targ);
|
|
RPrint(st);
|
|
RPrint("' was going to be Killed() again!\n");
|
|
RPrint("Attacker: '");
|
|
st=GetEnemyName(attacker);
|
|
RPrint(st);
|
|
RPrint("\n");
|
|
return;
|
|
}
|
|
|
|
if (targ.classname == "player")
|
|
{
|
|
local string st;
|
|
RPrint("(OfN WARNING:) Player: '");
|
|
RPrint(targ.netname);
|
|
|
|
if (targ.cutf_items & #CUTF_EXPBODY)
|
|
{
|
|
RPrint(" (with expbody) ");
|
|
}
|
|
else
|
|
{
|
|
RPrint(" (without expbody) ");
|
|
}
|
|
|
|
|
|
RPrint("' is Killed() again!\n");
|
|
RPrint("Attacker: '");
|
|
st=GetEnemyName(attacker);
|
|
RPrint(st);
|
|
|
|
if (attacker.classname == "player" && attacker.cutf_items & #CUTF_EXPBODY)
|
|
{
|
|
RPrint(" (with expbody) ");
|
|
}
|
|
else
|
|
{
|
|
RPrint(" (without expbody) ");
|
|
}
|
|
RPrint("\n");
|
|
}
|
|
else
|
|
{
|
|
// NOT BUGS, HAPPEN OFTEN WITH BUTTONS ETC..
|
|
|
|
/*RPrint("(OfN WARNING:) Object: '");
|
|
RPrint(targ.classname);
|
|
RPrint("' is Killed() again!\n");
|
|
RPrint("Attacker: '");
|
|
st=GetEnemyName(attacker);
|
|
RPrint(st);
|
|
if (attacker.classname == "player" && attacker.cutf_items & #CUTF_EXPBODY)
|
|
{
|
|
RPrint(" (with expbody) ");
|
|
}
|
|
else
|
|
{
|
|
RPrint(" (without expbody) ");
|
|
}
|
|
RPrint("\n"); */
|
|
}
|
|
}
|
|
|
|
targ.is_killed = #TRUE;
|
|
/////////////////////////////////////
|
|
|
|
local entity oself;
|
|
local string db;
|
|
|
|
//WK Have cursed person respawn immediately
|
|
if (targ.classname == "player") {
|
|
if (targ.penance_time > time) {
|
|
targ.real_frags = targ.real_frags - 1;
|
|
if (!(toggleflags & #TFLAG_TEAMFRAGS))
|
|
targ.frags = targ.real_frags;
|
|
targ.health = 50; //Give em a little so they can die again
|
|
targ.is_killed = #FALSE;
|
|
return;
|
|
}
|
|
}
|
|
|
|
oself = self;
|
|
self = targ;
|
|
|
|
// don't let sbar look bad if a player
|
|
if (self.health < -99)
|
|
self.health = -99;
|
|
|
|
// doors, triggers, etc // - ofn added sensor, it now calls clientobituary when dead
|
|
if ((self.movetype == #MOVETYPE_PUSH || self.movetype == #MOVETYPE_NONE) && self.classname != "building_camera" && self.classname!="building_sensor") //CH stop bug.
|
|
{
|
|
self.th_die ();
|
|
self = oself;
|
|
return;
|
|
}
|
|
|
|
self.enemy = attacker;
|
|
|
|
// bump the monster counter
|
|
if (self.flags & #FL_MONSTER)
|
|
{
|
|
killed_monsters = killed_monsters + 1;
|
|
WriteByte (#MSG_ALL, #SVC_KILLEDMONSTER);
|
|
}
|
|
|
|
ClientObituary(self, attacker);
|
|
|
|
self.takedamage = #DAMAGE_NO;
|
|
self.touch = SUB_Null;
|
|
|
|
monster_death_use();
|
|
self.th_die ();
|
|
|
|
self = oself;
|
|
};
|
|
|
|
|
|
/*
|
|
============
|
|
T_Damage
|
|
|
|
The damage is coming from inflictor, but get mad at attacker
|
|
This should be the only function that ever reduces health.
|
|
============
|
|
*/
|
|
void(entity targ, entity inflictor, entity attacker, float damage) T_Damage =
|
|
{
|
|
//WK Just call TF_T_Damage instead, so no updating two functions
|
|
//WK that do exactly the same thing.
|
|
TF_T_Damage(targ,inflictor,attacker,damage,0,0);
|
|
};
|
|
|
|
/*
|
|
============
|
|
TF_T_Damage
|
|
same thing as T_Damage (see above), just with some more details
|
|
|
|
T_Flags:
|
|
#TF_TD_IGNOREARMOUR: bypasses the armour of the target
|
|
#TF_TD_NOTTEAM: doesn't damage a team member
|
|
#TF_TD_NOTSELF: doesn't damage self
|
|
|
|
The following is used to determine whether this attack is affected
|
|
the type of armor the defender is wearing.
|
|
T_AttackType:
|
|
#TF_TD_OTHER : type ignored
|
|
#TF_TD_SHOT : bullet damage
|
|
#TF_TD_NAIL : nailgun damage
|
|
#TF_TD_EXPLOSION : explosion damage
|
|
#TF_TD_ELECTRICITY : electricity damage
|
|
#TF_TD_FIRE : fire damage
|
|
|
|
#TF_TD_NOSOUND : Special Value. Health is adjusted without
|
|
any sound, painframe, etc
|
|
Health is _set_ to damage, not altered.
|
|
|
|
============
|
|
*/
|
|
void(entity targ, entity inflictor, entity attacker, float damage, float T_flags, float T_AttackType) TF_T_Damage =
|
|
{
|
|
local vector dir;
|
|
local entity oldself, te;
|
|
local float save;
|
|
local float take;
|
|
local float mirror;
|
|
local float dist;
|
|
local string output;
|
|
local float knockem;
|
|
|
|
//WK -- For LPB calculation
|
|
local string foo;
|
|
local float ping;
|
|
|
|
mirror = 0;
|
|
#ifdef QUAKE_WORLD // culled by KK. was QUAKE_WORLD
|
|
if (infokey(world,"ceasefire")=="on") //Cyto
|
|
return;
|
|
#endif
|
|
if (targ == world)
|
|
return;
|
|
if (!targ.takedamage)
|
|
return;
|
|
|
|
//WK Set off land mines
|
|
if (targ.classname == "grenade" && targ.netname == "land_mine")
|
|
{
|
|
targ.think = GuerillaExplode;
|
|
targ.nextthink = time + 0.1;
|
|
return;
|
|
}
|
|
|
|
//BOOKKEEPING
|
|
//WK Store last person who shot martyr into .enemy
|
|
//(This conflicts with cameraman, but hey...)
|
|
//Remember last person who shot us, so we can give him a frag
|
|
if (targ.cutf_items & #CUTF_EXPBODY && !targ.is_abouttodie)
|
|
{
|
|
//if (targ.classname == "player" && attacker.classname == "player" && !Teammate(targ.team_no, attacker.team_no)) {
|
|
targ.martyr_enemy = attacker;
|
|
targ.stored_deathmsg = deathmsg; //- OfN - UNUSED?
|
|
}
|
|
if (targ.classname == "player") //WK Time holding the last time we've been shot
|
|
targ.last_attacked_time = time; //WK For chaplan healing purposes
|
|
|
|
|
|
//BUTTON TRIGGERING
|
|
if (attacker.classname == "player")
|
|
{
|
|
//if (targ.classname != "player" && !IsBuilding(targ) && targ.classname != "monster_demon1")
|
|
if (targ.classname != "player" && !IsBuilding(targ) && !IsMonster(targ))
|
|
{
|
|
if (!Activated(targ,attacker))
|
|
{
|
|
// If an else goal should be activated, activate it
|
|
if (targ.else_goal != 0)
|
|
{
|
|
te = Findgoal(targ.else_goal);
|
|
if (te)
|
|
DoResults(te, attacker, (targ.goal_result & #TFGR_ADD_BONUSES));
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
// used by buttons and triggers to set activator for target firing
|
|
damage_attacker = attacker;
|
|
|
|
|
|
// INVINCIBILITY
|
|
if (targ.flags & #FL_GODMODE)
|
|
return;
|
|
if (targ.invincible_finished >= time)
|
|
{
|
|
if (self.invincible_sound < time)
|
|
{
|
|
sound (targ, #CHAN_ITEM, "items/protect3.wav", 1, #ATTN_NORM);
|
|
self.invincible_sound = time + 2;
|
|
}
|
|
return;
|
|
}
|
|
else if (self.aura == #AURA_RESISTANCE)
|
|
{
|
|
if (self.invincible_sound < time)
|
|
{
|
|
// sound (targ, #CHAN_ITEM, "auras/aura1.wav", 1, #ATTN_NORM);
|
|
self.invincible_sound = time + 1.5;
|
|
}
|
|
return;
|
|
}
|
|
|
|
//CHAPLAIN CALCULATIONS
|
|
// WK See if they're close enough to chap to get protection
|
|
if (attacker.classname == "player" && attacker.tfstate & #TFSTATE_INSPIRED) {
|
|
dist = vlen(attacker.origin - attacker.inspirator.origin);
|
|
output = ftos(dist);
|
|
if (dist > #CHAPLAN_RADIUS) { //We've strayed from the flock
|
|
sprint(attacker,#PRINT_HIGH,"You have strayed from the flock\n");
|
|
attacker.tfstate = attacker.tfstate - #TFSTATE_INSPIRED;
|
|
attacker.super_damage_finished = 0;
|
|
attacker.items = attacker.items - (attacker.items & #IT_QUAD);
|
|
attacker.effects = attacker.effects - (attacker.effects & #EF_DIMLIGHT);
|
|
}
|
|
}
|
|
if (targ.classname == "player" && targ.tfstate & #TFSTATE_INSPIRED) {
|
|
dist = vlen(targ.origin - targ.inspirator.origin);
|
|
if (dist > #CHAPLAN_RADIUS) { //We've strayed from the flock
|
|
sprint(targ,#PRINT_HIGH,"You have strayed from the flock\n");
|
|
targ.tfstate = targ.tfstate - #TFSTATE_INSPIRED;
|
|
targ.super_damage_finished = 0;
|
|
targ.items = targ.items - (targ.items & #IT_QUAD);
|
|
targ.effects = targ.effects - (targ.effects & #EF_DIMLIGHT);
|
|
}
|
|
}
|
|
|
|
// QUAD DAMAGE
|
|
if (attacker.super_damage_finished > time) {
|
|
if (attacker.tfstate & #TFSTATE_INSPIRED)
|
|
damage = damage * 1.5;
|
|
if (attacker.job & #JOB_BERSERKER && attacker.job & #JOB_ACTIVE)
|
|
damage = damage * 2;
|
|
else
|
|
damage = damage * 4;
|
|
}
|
|
|
|
if (attacker.aura == #AURA_POWER || (attacker.job == #JOB_CRUSADER && IsMonsterNonArmy(targ)))
|
|
damage = damage * 1.5;
|
|
|
|
if (targ.aura == #AURA_RESISTANCE || (attacker.job == #JOB_CRUSADER && IsMonsterNonArmy(targ)))
|
|
damage = damage * 0.66;
|
|
|
|
|
|
//DAMAGE ADJUSTMENT
|
|
//- OfN - if (deathmsg != #DMSG_MARTYR && targ.classname != "monster_demon1" && targ.classname != "monster_shambler" && targ.classname != "monster_army")
|
|
if (deathmsg != #DMSG_MARTYR && !IsMonster(targ))
|
|
{
|
|
if (targ.tfstate & #TFSTATE_INSPIRED) //Chaplan defense
|
|
damage = damage * 0.66;
|
|
//WK Ping fairness code. LPB is < 200 ping
|
|
if (attacker.classname == "player" && targ.classname == "player" && attacker != targ)
|
|
{
|
|
foo = infokey(attacker,"ping");
|
|
ping = 200;
|
|
if (foo != string_null)
|
|
ping = stof(foo);
|
|
ping = (ping + 1000) / 1200;
|
|
if (ping < 0.8) ping = 0.8;
|
|
if (ping > 1.2) ping = 1.2;
|
|
damage = damage * ping;
|
|
}
|
|
if (teamplay & (#TEAMPLAY_LESSSCOREHELP | #TEAMPLAY_LESSPLAYERSHELP))
|
|
damage = TeamEqualiseDamage(targ, attacker, damage);
|
|
}
|
|
|
|
//MIRROR DAMAGE
|
|
//WK - Check to see if we hit a friend
|
|
if (Teammate(targ.team_no, attacker.team_no) && (targ != attacker) && attacker.classname == "player" && targ.classname == "player") {
|
|
//We just hit someone on our team!
|
|
if (T_flags & #TF_TD_NOTTEAM)
|
|
{
|
|
//Direct damage
|
|
if (teamplay & #TEAMPLAY_FULLMIRRORDIRECT)
|
|
mirror = mirror + damage;
|
|
if (teamplay & #TEAMPLAY_HALFMIRRORDIRECT)
|
|
mirror = mirror + damage / 2;
|
|
}
|
|
else
|
|
{
|
|
//Explosive damage
|
|
if (teamplay & #TEAMPLAY_FULLMIRROREXPLOSIVE)
|
|
mirror = mirror + damage;
|
|
if (teamplay & #TEAMPLAY_HALFMIRROREXPLOSIVE)
|
|
mirror = mirror + damage / 2;
|
|
}
|
|
//Don't inflict damage on a (teamkiller or spy)-hitter
|
|
if (mirror > 0 && !(targ.is_feigning || targ.is_undercover) && !(targ.penance_time > time)) //Hurt the jerk!
|
|
TF_T_Damage (attacker, attacker, attacker, mirror, 0, #TF_TD_OTHER);
|
|
}
|
|
//WK Slight mirror demon protection
|
|
//Do 2 points of damage to a friendly teammate shooting a friendly demon
|
|
//SB 2 damage? no way, we're doing the full mirror damage
|
|
//- Ofn- if ((targ.classname == "monster_demon1" || targ.classname == "monster_army" || targ.classname == "monster_shambler") && targ.real_owner != world) {
|
|
if (IsMonster(targ) && targ.real_owner != world) {
|
|
targ.armorvalue = 1;
|
|
if (T_flags & #TF_TD_NOTTEAM)
|
|
{
|
|
//Direct damage
|
|
if (teamplay & #TEAMPLAY_FULLMIRRORDIRECT)
|
|
mirror = mirror + damage;
|
|
if (teamplay & #TEAMPLAY_HALFMIRRORDIRECT)
|
|
mirror = mirror + damage / 2;
|
|
}
|
|
else
|
|
{
|
|
//Explosive damage
|
|
if (teamplay & #TEAMPLAY_FULLMIRROREXPLOSIVE)
|
|
mirror = mirror + damage;
|
|
if (teamplay & #TEAMPLAY_HALFMIRROREXPLOSIVE)
|
|
mirror = mirror + damage / 2;
|
|
}
|
|
if (Teammate(targ.real_owner.team_no,attacker.team_no) && (targ.real_owner != attacker) && attacker.classname == "player")
|
|
TF_T_Damage (attacker, attacker, attacker, mirror, 0, #TF_TD_OTHER);
|
|
}
|
|
//WK Friendly Sentrygun protection - you can't hurt friendly sentries
|
|
if (IsBuilding(targ)) {
|
|
if (Teammate(targ.team_no, attacker.team_no) && (targ != attacker) && (!(targ.tf_items & #NIT_SECURITY_CAMERA))) {
|
|
return;
|
|
}
|
|
targ.armorvalue = 1; //CH so that the armors are counted
|
|
}
|
|
|
|
// SPECIAL ARMOR CALCULATIONS
|
|
if ((targ.armorclass != 0) && (T_AttackType != 0))
|
|
{
|
|
if ((targ.armorclass & #AT_SAVESHOT) && (T_AttackType == #TF_TD_SHOT)) {
|
|
damage = floor(damage * 0.5);
|
|
//WK Cap max damage you can take with kevlar on, like in Real Life(tm)
|
|
//WK The purpose being to cut down on the power of snipers
|
|
if(targ.classname == "player") // SB kevlar fixed so it caps damage now, also spacing much nicer :)
|
|
{
|
|
local float olddamage; //- OfN - special cap damage with otr! heh
|
|
olddamage=damage;
|
|
|
|
if (light_damage)
|
|
{
|
|
if (damage > 75)
|
|
damage = 75;
|
|
}
|
|
else
|
|
if (damage > 100) damage = 100;
|
|
|
|
if (attacker.classname == "player" && (deathmsg == #DMSG_SNIPERRIFLE || deathmsg == #DMSG_SNIPERHEADSHOT || deathmsg == #DMSG_SNIPERLEGSHOT))
|
|
{
|
|
sprint(attacker,#PRINT_HIGH,"You hit kevlar\n");
|
|
if (attacker.cutf_items & #CUTF_OTR) //- OfN -
|
|
{
|
|
if (olddamage > 100)
|
|
olddamage=olddamage - ((olddamage - 100)*0.4);
|
|
|
|
damage = olddamage;
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
else if ((targ.armorclass & #AT_SAVEMELEE) && (deathmsg == #DMSG_JUDOKA || deathmsg == #DMSG_AXE || deathmsg == #DMSG_BACKSTAB || deathmsg == #DMSG_SPANNER))
|
|
{
|
|
damage = floor(damage * 0.5);
|
|
if ((targ.velocity_x > 0 || targ.velocity_y > 0 || targ.velocity_z > 0) && random() > 0.6)
|
|
return;
|
|
if (deathmsg == #DMSG_BACKSTAB)
|
|
T_flags = T_flags - (T_flags & #TF_TD_IGNOREARMOUR);
|
|
}
|
|
else if ((targ.armorclass & #AT_SAVEEXPLOSION) && (T_AttackType == #TF_TD_EXPLOSION))
|
|
damage = floor(damage * 0.5);
|
|
else if ((targ.armorclass & #AT_SAVEELECTRICITY) && (T_AttackType == #TF_TD_ELECTRICITY || T_AttackType == #TF_TD_NAIL))
|
|
damage = floor(damage * 0.5);
|
|
else if ((targ.armorclass & #AT_SAVEFIRE) && (T_AttackType == #TF_TD_FIRE))
|
|
damage = floor(damage * 0.5);
|
|
}
|
|
|
|
//ARMOR PROTECTION CALCULATION
|
|
// save damage based on the target's armor level
|
|
if (T_flags & #TF_TD_IGNOREARMOUR)
|
|
{
|
|
take = damage;
|
|
save = 0;
|
|
}
|
|
else
|
|
{
|
|
save = ceil(targ.armortype*damage);
|
|
if (save >= targ.armorvalue)
|
|
{
|
|
save = targ.armorvalue;
|
|
targ.armortype = 0; // lost all armor
|
|
targ.armorclass = 0; // lost special armor
|
|
targ.items = targ.items - (targ.items & (#IT_ARMOR1 | #IT_ARMOR2 | #IT_ARMOR3));
|
|
}
|
|
//WK Flags prevent armor damage too
|
|
if (T_flags & #TF_TD_NOTTEAM)
|
|
{
|
|
if (Teammate(targ.team_no,attacker.team_no) && (targ != attacker))
|
|
{
|
|
if (teamplay & #TEAMPLAY_NOARMORDIRECT)
|
|
targ.armorvalue = targ.armorvalue;
|
|
else if (teamplay & #TEAMPLAY_HALFARMORDIRECT)
|
|
targ.armorvalue = targ.armorvalue - save / 2;
|
|
else
|
|
targ.armorvalue = targ.armorvalue - save;
|
|
}
|
|
else
|
|
targ.armorvalue = targ.armorvalue - save;
|
|
}
|
|
else
|
|
{
|
|
if (Teammate(targ.team_no, attacker.team_no) && (targ != attacker))
|
|
{
|
|
if (teamplay & #TEAMPLAY_NOARMOREXPLOSIVE)
|
|
targ.armorvalue = targ.armorvalue;
|
|
else if (teamplay & #TEAMPLAY_HALFARMOREXPLOSIVE)
|
|
targ.armorvalue = targ.armorvalue - save / 2;
|
|
else
|
|
targ.armorvalue = targ.armorvalue - save;
|
|
}
|
|
else
|
|
targ.armorvalue = targ.armorvalue - save;
|
|
}
|
|
// targ.armorvalue = targ.armorvalue - save;
|
|
take = ceil(damage-save);
|
|
}
|
|
// add to the damage total for clients, which will be sent as a single
|
|
// message at the end of the frame
|
|
// FIXME: remove after combining shotgun blasts?
|
|
if (targ.flags & #FL_CLIENT)
|
|
{
|
|
targ.dmg_take = targ.dmg_take + take;
|
|
targ.dmg_save = targ.dmg_save + save;
|
|
targ.dmg_inflictor = inflictor;
|
|
}
|
|
|
|
// KNOCKATION CALCULATION
|
|
if ( (inflictor != world) && (targ.movetype == #MOVETYPE_WALK) )
|
|
{
|
|
// Nail Gren doesn't knock ppl
|
|
//WK People with Aspect of HWGUY are immune to knockation
|
|
//WK Martyrs are immune to bullet damage, so AC's and Turrets
|
|
// can't be annoying and just simply stop them.
|
|
//SB That's annoying. They can now :)
|
|
knockem = 1;
|
|
if (targ.cutf_items & #CUTF_HWGUY) knockem = 0;
|
|
//if (targ.job & #JOB_MARTYR && targ.job & #JOB_ACTIVE && T_AttackType != #TF_TD_SHOT) knockem = 1;
|
|
if (deathmsg == #DMSG_GREN_NAIL) knockem = 0;
|
|
if (knockem)
|
|
{
|
|
// give them some immunity to cheat check
|
|
//targ.immune_to_check = time + (damage / 20);
|
|
makeImmune(targ,time+(damage/20));
|
|
|
|
dir = targ.origin - (inflictor.absmin + inflictor.absmax) * 0.5;
|
|
dir = normalize(dir);
|
|
|
|
#ifdef QUAKE_WORLD
|
|
// Increase kickback for smaller weapons
|
|
if ( (damage < 60) & ((attacker.classname == "player") & (targ.classname == "player")) & ( attacker.netname != targ.netname))
|
|
targ.velocity = targ.velocity + dir * damage * 11;
|
|
else
|
|
targ.velocity = targ.velocity + dir*damage*8;
|
|
if ( (rj > 1) & ((attacker.classname == "player") & (targ.classname == "player")) & ( attacker.netname == targ.netname))
|
|
targ.velocity = targ.velocity + dir * damage * rj;
|
|
if (targ.classname == "player" && targ.cutf_items & #CUTF_GYMNAST)
|
|
targ.velocity = targ.velocity + dir * damage * 8;
|
|
#else
|
|
targ.velocity = targ.velocity + dir*damage*8;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
// TEAMPLAY FLAGS
|
|
if (attacker.classname == "player" && targ.classname == "player") {
|
|
if (T_flags & #TF_TD_NOTTEAM) {
|
|
if (Teammate(targ.team_no, attacker.team_no) && (targ != attacker)) {
|
|
if (teamplay & #TEAMPLAY_NODIRECT) return;
|
|
else if (teamplay & #TEAMPLAY_HALFDIRECT) take = take / 2;
|
|
}
|
|
}
|
|
else {
|
|
if (Teammate(targ.team_no, attacker.team_no) && (targ != attacker)) {
|
|
if (teamplay & #TEAMPLAY_NOEXPLOSIVE) return;
|
|
else if (teamplay & #TEAMPLAY_HALFEXPLOSIVE) take = take / 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (T_flags & #TF_TD_NOTSELF) {
|
|
if (targ == attacker) return;
|
|
}
|
|
|
|
// do the damage, min 1
|
|
if (take < 1) take = 1;
|
|
|
|
//Make thief visible when he's shot
|
|
if (take > 1 && targ != attacker && (targ.job & #JOB_THIEF && (targ.job & #JOB_ACTIVE || targ.job & #JOB_FULL_HIDE)))
|
|
if (!Teammate(targ.team_no, attacker.team_no))
|
|
RevealThief(targ,#TRUE);
|
|
|
|
targ.health = targ.health - take;
|
|
|
|
if (targ.armorvalue < 1) {
|
|
targ.armorclass = 0; // lost special armor
|
|
targ.armorvalue = 0;
|
|
}
|
|
|
|
if (targ.health < 1 && targ.health > 0) //WK Stop the scoreboard coming up
|
|
targ.health = 1;
|
|
|
|
if (targ.health <= 0) {
|
|
if (inflictor.classname == "detpack" && inflictor.weaponmode == 1 && inflictor.enemy == targ)
|
|
deathmsg = #DMSG_DETPACK_DIS;
|
|
|
|
//WK Autotrigger martyr - OfN - Now exp.body
|
|
if ((targ.cutf_items & #CUTF_EXPBODY) && attacker != world && targ.is_abouttodie == #FALSE)
|
|
{
|
|
//oldself = self;
|
|
//self = targ;
|
|
targ.health = 1;
|
|
targ.martyr_enemy=attacker;
|
|
ExpBody(targ);
|
|
//self = oldself;
|
|
targ.is_abouttodie = #TRUE;
|
|
return;
|
|
}// /////// OFTEN ///////////
|
|
|
|
//WK Make sure the world doesn't have a bastard level
|
|
if (attacker.classname != "player" || targ.classname != "player") {
|
|
//bprint(#PRINT_MEDIUM,"Non player death or kill\n");
|
|
Killed(targ, attacker);
|
|
return;
|
|
}
|
|
|
|
#ifdef QUAKE_WORLD
|
|
//WK Handle Total Bastard Checking
|
|
//If they kill more than a certain number of friends in a certain period, they are booted
|
|
if (Teammate(targ.team_no, attacker.team_no) && targ != attacker && !(targ.penance_time > time) && !(targ.is_undercover) && prematch < time)
|
|
{
|
|
//A teamkill
|
|
local string st;
|
|
local float threshold;
|
|
threshold = 0;
|
|
st = infokey(world, "curse");
|
|
if (st != string_null)
|
|
threshold = stof(st);
|
|
|
|
|
|
attacker.ff_count = attacker.ff_count + 1; //Increase their bastard rating
|
|
if (threshold >= 1) {
|
|
if (attacker.ff_count >= threshold) createBastard(attacker,threshold);
|
|
if ((attacker.ff_count == threshold - 1) || (attacker.ff_count == threshold - 0.5)) {
|
|
sprint (attacker, #PRINT_MEDIUM, "One more teamkill and you will be cursed.\n");
|
|
}
|
|
}
|
|
}
|
|
if (!Teammate(targ.team_no, attacker.team_no) || targ.penance_time > time) {
|
|
attacker.ff_count = attacker.ff_count - 0.5;
|
|
if (attacker.ff_count < 0) attacker.ff_count = 0;
|
|
}
|
|
#endif
|
|
|
|
if (targ.penance_time > time) //A penitent loses frags
|
|
Killed(targ, targ);
|
|
else
|
|
Killed(targ, attacker);
|
|
return;
|
|
|
|
}
|
|
|
|
// react to the damage
|
|
oldself = self;
|
|
self = targ;
|
|
|
|
if (self.th_pain)
|
|
{
|
|
self.th_pain (attacker, take);
|
|
// nightmare mode monsters don't go into pain frames often
|
|
if (skill >= 3)
|
|
self.pain_finished = time + 5;
|
|
}
|
|
|
|
self = oldself;
|
|
};
|
|
|
|
/*
|
|
============
|
|
T_RadiusDamage
|
|
============
|
|
*/
|
|
void(entity inflictor, entity attacker, float damage, entity ignore) T_RadiusDamage =
|
|
{
|
|
local float points;
|
|
local entity head, te;
|
|
local vector org;
|
|
|
|
head = findradius(inflictor.origin, damage+40);
|
|
|
|
while (head)
|
|
{
|
|
if (head != ignore)
|
|
{
|
|
//WK Set off all land mines in blast radius
|
|
if (head.classname == "grenade" && head.netname == "land_mine")
|
|
{
|
|
if (deathmsg != #DMSG_LAND_MINE) {
|
|
//head.has_tesla = 2; //- ofn - ur mine explodes better than is destroyed
|
|
head.think = GuerillaExplode;
|
|
head.nextthink = time + 0.1;
|
|
}
|
|
}
|
|
// Check for TeamFortress Goals that can be triggered by Detpacks
|
|
else if (head.classname == "info_tfgoal")
|
|
{
|
|
if (inflictor.classname == "detpack")
|
|
{
|
|
// Don't activate timer goals
|
|
if ((head.goal_activation & #TFGA_TOUCH_DETPACK) && (head.search_time == 0))
|
|
{
|
|
traceline (inflictor.origin, head.origin, #TRUE, inflictor);
|
|
|
|
if (trace_fraction == 1)
|
|
{
|
|
// Does the AP match the AP Criteria?
|
|
if (Activated(head,attacker))
|
|
{
|
|
DoResults(head, attacker, #TRUE);
|
|
}
|
|
else
|
|
{
|
|
// If an else goal should be activated, activate it
|
|
if (head.else_goal != 0)
|
|
{
|
|
te = Findgoal(head.else_goal);
|
|
if (te)
|
|
DoResults(te, attacker, (head.goal_result & #TFGR_ADD_BONUSES));
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (head.takedamage)
|
|
{
|
|
org = head.origin + (head.mins + head.maxs)*0.5;
|
|
points = 0.5*vlen (inflictor.origin - org);
|
|
if (points < 0)
|
|
points = 0;
|
|
points = damage - points;
|
|
if (head == attacker)
|
|
points = rint(points * 0.75);
|
|
if (head.classname == "building_camera")
|
|
points = 10;
|
|
if (points > 0)
|
|
{
|
|
if (CanDamage (head, inflictor))
|
|
{
|
|
TF_T_Damage (head, inflictor, attacker, points, #TF_TD_NOTTEAM, #TF_TD_EXPLOSION);
|
|
}
|
|
//Turrets have fucked up bounding boxes due to their natures
|
|
//So we have to make them susceptible to blast damage here
|
|
//But only 50% damage, since they are turrets...
|
|
else if ((head.classname == "building_sentrygun" || head.classname == "building_tesla") && head.tf_items & #NIT_TURRET) {
|
|
points = rint(points / 2);
|
|
TF_T_Damage (head, inflictor, attacker, points, #TF_TD_NOTTEAM, #TF_TD_EXPLOSION);
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
head = head.chain;
|
|
}
|
|
};
|
|
|
|
/*
|
|
============
|
|
T_BeamDamage // OfN - UNUSED so.. i coment it out
|
|
============
|
|
*/
|
|
|
|
/*void(entity attacker, float damage) T_BeamDamage =
|
|
{
|
|
local float points;
|
|
local entity head;
|
|
|
|
head = findradius(attacker.origin, damage+40);
|
|
|
|
while (head)
|
|
{
|
|
if (head.takedamage)
|
|
{
|
|
points = 0.5*vlen (attacker.origin - head.origin);
|
|
if (points < 0)
|
|
points = 0;
|
|
points = damage - points;
|
|
if (head == attacker)
|
|
points = points * 0.5;
|
|
if (points > 0)
|
|
{
|
|
if (CanDamage (head, attacker))
|
|
{
|
|
|
|
//if (head.classname == "monster_shambler")
|
|
// T_Damage (head, attacker, attacker, points*0.5);
|
|
//else
|
|
|
|
T_Damage (head, attacker, attacker, points);
|
|
}
|
|
}
|
|
}
|
|
head = head.chain;
|
|
}
|
|
};*/
|