mirror of
https://git.code.sf.net/p/quake/prozac-qfcc
synced 2024-12-18 00:21:11 +00:00
747b0a4760
- make concs not affect shamblers - add a few items to BUGS and TODO
977 lines
26 KiB
C++
977 lines
26 KiB
C++
#include "defs.qh"
|
|
/*====================================================
|
|
SCOUT.QC
|
|
|
|
TeamFortress v2.5 29/2/97
|
|
Craig Hauser 26/3/00
|
|
======================================================
|
|
Functions for the SCOUT class and associated weaponry
|
|
======================================================*/
|
|
// Functions outside this file
|
|
|
|
// Functions inside this file
|
|
// Concussion Grenade Functions
|
|
void() ConcussionGrenadeTouch;
|
|
void() ConcussionGrenadeExplode;
|
|
void() ConcussionGrenadeTimer;
|
|
// Scanner Functions
|
|
void(float scanrange,float inAuto) TeamFortress_Scan;
|
|
void(entity inflictor, entity attacker, float bounce, entity ignore) T_RadiusBounce;
|
|
entity(entity scanner, float scanrange, float enemies, float friends) T_RadiusScan;
|
|
|
|
//void(entity pl, string s1) CenterPrint;
|
|
void(entity pl, float fTime, string s1) StatusPrint;
|
|
|
|
//=========================================================================
|
|
// Touch Function for Flash Grenade
|
|
void() FlashGrenadeTouch =
|
|
{
|
|
// If the Flash Grenade hits a player, it just bounces off
|
|
sound (self, CHAN_WEAPON, "weapons/bounce.wav", 1, ATTN_NORM);
|
|
if (self.velocity == '0 0 0')
|
|
self.avelocity = '0 0 0';
|
|
};
|
|
|
|
#ifdef NET_SERVER
|
|
#define SMOOTH_FLASH 1
|
|
#else
|
|
#define SMOOTH_FLASH 0.1
|
|
#endif
|
|
|
|
void() FlashTimer =
|
|
{
|
|
local entity te;
|
|
|
|
te = self.owner;
|
|
|
|
if (te.has_disconnected) //WK Safety, now that we call this to clean up after death
|
|
return;
|
|
|
|
te.FlashTime = te.FlashTime - SMOOTH_FLASH;
|
|
|
|
if (te.FlashTime < 4)
|
|
{
|
|
te.FlashTime = 0;
|
|
stuffcmd(te, "v_cshift 0\n");
|
|
stuffcmd(te, "r_norefresh 0;wait;echo;wait;echo;wait;echo;wait;echo\n"); //WK
|
|
// OfN- now we use the centerprint replacement
|
|
//centerprint(te,"Your eyes finally clear\n");
|
|
StatusPrint(te,2,"Your eyes finally clear\n");
|
|
return;
|
|
}
|
|
|
|
local string st;
|
|
|
|
st = ftos(te.FlashTime * 15); //WK Multiplier 10
|
|
|
|
stuffcmd(te, "v_cshift ");
|
|
stuffcmd(te, st);
|
|
stuffcmd(te, " ");
|
|
stuffcmd(te, st);
|
|
stuffcmd(te, " ");
|
|
stuffcmd(te, st);
|
|
stuffcmd(te, " ");
|
|
stuffcmd(te, st);
|
|
stuffcmd(te, "\n");
|
|
if (te.FlashTime > 15 || (te.FlashTime < 12 && te.FlashTime > 11) || (te.FlashTime < 9 && te.FlashTime > 7) || te.FlashTime < 5)
|
|
if (self.heat != 1) {
|
|
StatusPrint(te,2,"Your eyes burn\n with the pain\n of \n glowing phosphor\n \n \n");
|
|
stuffcmd(te, "r_norefresh 1;wait;echo;wait;echo;wait;echo;wait;echo\n");
|
|
self.heat = 1;
|
|
}
|
|
else
|
|
if (self.heat != 0) {
|
|
StatusPrint(te,2,"Your eyes water\n and momentarily lessen\n the \n Indescribable Pain\n \n \n");
|
|
stuffcmd(te, "r_norefresh 0;wait;echo;wait;echo;wait;echo;wait;echo\n");
|
|
self.heat = 0;
|
|
}
|
|
|
|
|
|
self.nextthink = time + SMOOTH_FLASH;
|
|
};
|
|
|
|
//=========================================================================
|
|
// Flash Grenade explode function, for when the PRIMETIME runs out
|
|
void() FlashGrenadeExplode =
|
|
{
|
|
local entity te;
|
|
|
|
self.effects = self.effects | EF_BRIGHTLIGHT;
|
|
|
|
WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
|
|
WriteByte (MSG_BROADCAST, TE_TAREXPLOSION);
|
|
WriteCoord (MSG_BROADCAST, self.origin_x);
|
|
WriteCoord (MSG_BROADCAST, self.origin_y);
|
|
WriteCoord (MSG_BROADCAST, self.origin_z);
|
|
multicast (self.origin, MULTICAST_PHS);
|
|
|
|
// Find all people in area
|
|
te = findradius(self.origin, 200);
|
|
while (te)
|
|
{
|
|
// Player?
|
|
if (te.classname == "player")
|
|
{
|
|
// Damage player and explode
|
|
deathmsg = DMSG_GREN_FLASH;
|
|
TF_T_Damage(te, self, self.owner, 60, 0, TF_TD_FIRE);
|
|
|
|
if (te.health > 0)
|
|
{
|
|
if (te.FlashTime == 0)
|
|
{
|
|
// create flash timer
|
|
newmis = spawn();
|
|
|
|
newmis.classname = "timer";
|
|
newmis.netname = "flashtimer";
|
|
newmis.team_no = self.owner.team_no;
|
|
newmis.owner = te;
|
|
newmis.think = FlashTimer;
|
|
newmis.nextthink = time + 1;
|
|
newmis.heat = 1;
|
|
}
|
|
|
|
te.FlashTime = 16; //WK 24 for non-owners
|
|
|
|
local string st;
|
|
|
|
st = ftos(te.FlashTime * 15);
|
|
stuffcmd(te, "v_cshift ");
|
|
stuffcmd(te, st);
|
|
stuffcmd(te, " ");
|
|
stuffcmd(te, st);
|
|
stuffcmd(te, " ");
|
|
stuffcmd(te, st);
|
|
stuffcmd(te, " ");
|
|
stuffcmd(te, st);
|
|
stuffcmd(te, "\n");
|
|
stuffcmd(te, "r_norefresh 1;wait;echo;wait;echo;wait;echo;wait;echo\n"); //WK
|
|
StatusPrint(te,2,"Your eyes burn\n with the pain\n of \n glowing phosphor\n \n \n");
|
|
}
|
|
}
|
|
|
|
te = te.chain;
|
|
}
|
|
|
|
#ifdef DEMO_STUFF
|
|
// Remove any camera's locks on this missile
|
|
if (self.enemy)
|
|
CamProjectileLockOff();
|
|
#endif
|
|
|
|
dremove(self);
|
|
};
|
|
|
|
|
|
|
|
//=========================================================================
|
|
// Touch function for a concussion grenade
|
|
void() ConcussionGrenadeTouch =
|
|
{
|
|
// concussion grenades bounce off other players now
|
|
|
|
sound (self, CHAN_WEAPON, "weapons/bounce.wav", 1, ATTN_NORM); // bounce sound
|
|
if (self.velocity == '0 0 0')
|
|
self.avelocity = '0 0 0';
|
|
};
|
|
|
|
//=========================================================================
|
|
// Concussion grenade explosion function
|
|
void() ConcussionGrenadeExplode =
|
|
{
|
|
T_RadiusBounce (self, self.owner, 240, world);
|
|
|
|
#ifdef DEMO_STUFF
|
|
// Remove any camera's locks on this missile
|
|
if (self.enemy)
|
|
CamProjectileLockOff();
|
|
#endif
|
|
|
|
WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
|
|
WriteByte (MSG_BROADCAST, TE_EXPLOSION);
|
|
WriteCoord (MSG_BROADCAST, self.origin_x);
|
|
WriteCoord (MSG_BROADCAST, self.origin_y);
|
|
WriteCoord (MSG_BROADCAST, self.origin_z);
|
|
multicast (self.origin, MULTICAST_PHS);
|
|
dremove(self);
|
|
};
|
|
|
|
//=========================================================================
|
|
// Concussion grenade timer to remove idlescale
|
|
void() ConcussionGrenadeTimer =
|
|
{
|
|
local string st;
|
|
|
|
if (self.owner.invincible_finished > time)
|
|
{
|
|
stuffcmd(self.owner, "v_idlescale 0\n");
|
|
dremove(self);
|
|
return;
|
|
}
|
|
|
|
// Bubble
|
|
newmis = spawn();
|
|
setmodel (newmis, "progs/s_bubble.spr");
|
|
setorigin (newmis, self.owner.origin);
|
|
newmis.movetype = MOVETYPE_NOCLIP;
|
|
newmis.solid = SOLID_NOT;
|
|
newmis.velocity = '0 0 15';
|
|
newmis.nextthink = time + 0.5;
|
|
newmis.think = bubble_bob;
|
|
newmis.touch = bubble_remove;
|
|
newmis.classname = "bubble";
|
|
newmis.frame = 0;
|
|
newmis.cnt = 0;
|
|
setsize(newmis, '-8 -8 -8', '8 8 8');
|
|
|
|
self.health = self.health - GR_CONCUSS_DEC;
|
|
|
|
// medic recovers twice as fast
|
|
if (self.owner.weapons_carried & WEAP_MEDIKIT) //WK
|
|
self.health = self.health - GR_CONCUSS_DEC;
|
|
if (self.owner.cutf_items & CUTF_STEALTH)
|
|
self.health = self.health - GR_CONCUSS_DEC;
|
|
if (self.owner.cutf_items & CUTF_GYMNAST)
|
|
self.health = self.health - GR_CONCUSS_DEC;
|
|
|
|
if (self.health < 0)
|
|
self.health = 0;
|
|
self.nextthink = time + GR_CONCUSS_TIME;
|
|
|
|
st = ftos(self.health);
|
|
stuffcmd(self.owner, "v_idlescale ");
|
|
stuffcmd(self.owner, st);
|
|
stuffcmd(self.owner, "\n");
|
|
|
|
if (self.health == 0)
|
|
dremove(self);
|
|
};
|
|
|
|
//=========================================================================
|
|
// Handles the scanner function for Scouts
|
|
void(float scanrange,float inAuto) TeamFortress_Scan =
|
|
{
|
|
local string power;
|
|
local entity list;
|
|
local float scen, scfr;
|
|
local vector lightningvec;
|
|
|
|
// added in for the direction scanner code
|
|
local float enemy_detected;
|
|
local float any_detected;
|
|
|
|
|
|
// prevent scan impulse from triggering anything else
|
|
self.impulse = 0;
|
|
self.last_impulse = 0;
|
|
|
|
if (self.classname == "player")
|
|
{
|
|
if (!(self.tf_items & NIT_SCANNER))
|
|
return;
|
|
|
|
// If Impulse is TF_SCAN_ENEMY, toggle Scanning for Enemies
|
|
if (scanrange == TF_SCAN_ENEMY)
|
|
{
|
|
if (self.tf_items_flags & NIT_SCANNER_ENEMY)
|
|
{
|
|
sprint (self, PRINT_HIGH, "Enemy Scanning disabled.\n");
|
|
self.tf_items_flags = self.tf_items_flags & ~NIT_SCANNER_ENEMY;
|
|
return;
|
|
}
|
|
sprint (self, PRINT_HIGH, "Enemy Scanning enabled.\n");
|
|
self.tf_items_flags = self.tf_items_flags | NIT_SCANNER_ENEMY;
|
|
return;
|
|
}
|
|
|
|
// If Impulse is TF_SCAN_FRIENDLY, toggle Scanning for Friendlies
|
|
if (scanrange == TF_SCAN_FRIENDLY)
|
|
{
|
|
if (self.tf_items_flags & NIT_SCANNER_FRIENDLY)
|
|
{
|
|
sprint (self, PRINT_HIGH, "Friendly Scanning disabled.\n");
|
|
self.tf_items_flags = self.tf_items_flags & ~NIT_SCANNER_FRIENDLY;
|
|
return;
|
|
}
|
|
sprint (self, PRINT_HIGH, "Friendly Scanning enabled.\n");
|
|
self.tf_items_flags = self.tf_items_flags | NIT_SCANNER_FRIENDLY;
|
|
return;
|
|
}
|
|
|
|
// If the user doesn't have as many cells as he/she specified, just
|
|
// use as many as they've got.
|
|
|
|
/* local float scancost;
|
|
|
|
scancost = ceil(scanrange / 20);
|
|
|
|
if (scancost > self.ammo_cells)
|
|
{
|
|
scanrange = self.ammo_cells * 20;
|
|
scancost = self.ammo_cells;
|
|
}
|
|
|
|
if (scanrange <= 0)
|
|
{
|
|
sprint(self, PRINT_HIGH, "No cells.\n");
|
|
return;
|
|
}
|
|
*/
|
|
if (scanrange > NIT_SCANNER_MAXCELL)
|
|
scanrange = NIT_SCANNER_MAXCELL;
|
|
|
|
scen = 0;
|
|
scfr = 0;
|
|
// Set the Scanner flags
|
|
if (self.tf_items_flags & NIT_SCANNER_ENEMY)
|
|
scen = 1;
|
|
if (self.tf_items_flags & NIT_SCANNER_FRIENDLY)
|
|
scfr = 1;
|
|
|
|
// If no entity type is enabled, don't scan
|
|
if ((scen == 0) && (scfr == 0))
|
|
{
|
|
sprint(self, PRINT_HIGH, "All scanner functions are disabled.\nEnable with 'scane' or 'scanf'.\n");
|
|
return;
|
|
}
|
|
|
|
// Use up cells to power the scanner
|
|
// additions:
|
|
// altered this so scanner could be more easily tested
|
|
//WK self.ammo_cells = self.ammo_cells - scancost;
|
|
scanrange = scanrange * NIT_SCANNER_POWER;
|
|
|
|
if (!inAuto) { //WK Only sprint() if not autoscanner
|
|
sprint (self, PRINT_HIGH, "Range: ");
|
|
power = ftos(ceil(scanrange));
|
|
sprint (self, PRINT_HIGH, power);
|
|
sprint (self, PRINT_HIGH, ". Scanning...\n");
|
|
}
|
|
|
|
// Get the list of entities the scanner finds
|
|
list = T_RadiusScan(self, scanrange, scen, scfr);
|
|
}
|
|
// Base Defence scanning code here
|
|
|
|
// Reset the entity counts
|
|
scen = 0;
|
|
scfr = 0;
|
|
|
|
// the vectors v_forward and v_right are required to
|
|
// 'triangulate' the enemies position
|
|
makevectors(self.v_angle);
|
|
|
|
// Walk the list
|
|
// For now, just count the entities.
|
|
// In the future, we'll display bearings :)
|
|
// additions: the future is now!
|
|
while (list)
|
|
{
|
|
if (list != self)
|
|
{
|
|
// sets the enemy_detected flag to TRUE if not on your team, FALSE if so
|
|
any_detected = TRUE; // this flag is set to false if bogie is moving
|
|
// too slow to be detected (and velocity checking is on)
|
|
|
|
if (vlen(list.origin - self.origin) <= scanrange) //CH Secondary check NEEDED!!!
|
|
{
|
|
// If this scanner is a motion detector, don't record
|
|
// object that don't have the required velocity to be detected.
|
|
if (self.tf_items_flags & NIT_SCANNER_MOVEMENT)
|
|
{
|
|
if (vlen(list.velocity) > NIT_SCANNER_MIN_MOVEMENT)
|
|
{
|
|
if (list.classname == "monster_demon1" && list.health > 0) //Because they dont have teams
|
|
{
|
|
if (Teammate(list.real_owner.team_no,self.team_no))
|
|
{
|
|
scfr = scfr + 1;
|
|
enemy_detected = FALSE;
|
|
}
|
|
else
|
|
{
|
|
scen = scen + 1;
|
|
enemy_detected = TRUE;
|
|
}
|
|
}
|
|
else if (list.classname == "monster_army" && list.health > 0) //Because they dont have teams
|
|
{
|
|
if (Teammate(list.real_owner.team_no,self.team_no))
|
|
{
|
|
scfr = scfr + 1;
|
|
enemy_detected = FALSE;
|
|
}
|
|
else
|
|
{
|
|
scen = scen + 1;
|
|
enemy_detected = TRUE;
|
|
}
|
|
}
|
|
else if (list.classname == "monster_shambler" && list.health > 0) //Because they dont have teams
|
|
{
|
|
if (Teammate(list.real_owner.team_no,self.team_no))
|
|
{
|
|
scfr = scfr + 1;
|
|
enemy_detected = FALSE;
|
|
}
|
|
else
|
|
{
|
|
scen = scen + 1;
|
|
enemy_detected = TRUE;
|
|
}
|
|
}
|
|
//- OfN -
|
|
else if (list.classname == "monster_wizard" && list.health > 0) //Because they dont have teams
|
|
{
|
|
if (Teammate(list.real_owner.team_no,self.team_no))
|
|
{
|
|
scfr = scfr + 1;
|
|
enemy_detected = FALSE;
|
|
}
|
|
else
|
|
{
|
|
scen = scen + 1;
|
|
enemy_detected = TRUE;
|
|
}
|
|
}
|
|
|
|
else if (list.classname == "item_tfgoal" && list.owned_by > 0 && list.team_no > 0 && self.team_no > 0) //Because they use owned_by
|
|
{
|
|
if (list.owned_by == self.team_no && list.team_no != self.team_no)
|
|
{
|
|
scfr = scfr + 1;
|
|
enemy_detected = FALSE;
|
|
}
|
|
else if (list.owned_by == self.team_no && list.team_no == self.team_no)
|
|
{
|
|
scfr = scfr + 1;
|
|
enemy_detected = FALSE;
|
|
}
|
|
else if (list.owned_by != self.team_no && list.team_no == self.team_no)
|
|
{
|
|
scen = scen + 1;
|
|
enemy_detected = TRUE;
|
|
}
|
|
else
|
|
any_detected = FALSE;
|
|
}
|
|
else if ((list.classname == "player" || list.classname == "building_sentrygun" || list.classname == "building_tesla" || list.classname == "building_teleporter") && list.health > 0 && !(list.cutf_items & CUTF_JAMMER))
|
|
{
|
|
if (Teammate(list.team_no, self.team_no))
|
|
{
|
|
scfr = scfr + 1;
|
|
enemy_detected = FALSE;
|
|
}
|
|
else
|
|
{
|
|
scen = scen + 1;
|
|
enemy_detected = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
any_detected = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
any_detected = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (list.classname == "monster_demon1" && list.health > 0) //Because they dont have teams
|
|
{
|
|
if (Teammate(list.real_owner.team_no,self.team_no))
|
|
{
|
|
scfr = scfr + 1;
|
|
enemy_detected = FALSE;
|
|
}
|
|
else
|
|
{
|
|
scen = scen + 1;
|
|
enemy_detected = TRUE;
|
|
}
|
|
}
|
|
if (list.classname == "monster_army" && list.health > 0) //Because they dont have teams
|
|
{
|
|
if (Teammate(list.real_owner.team_no, self.team_no))
|
|
{
|
|
scfr = scfr + 1;
|
|
enemy_detected = FALSE;
|
|
}
|
|
else
|
|
{
|
|
scen = scen + 1;
|
|
enemy_detected = TRUE;
|
|
}
|
|
}
|
|
if (list.classname == "monster_shambler" && list.health > 0) //Because they dont have teams
|
|
{
|
|
if (Teammate(list.real_owner.team_no, self.team_no))
|
|
{
|
|
scfr = scfr + 1;
|
|
enemy_detected = FALSE;
|
|
}
|
|
else
|
|
{
|
|
scen = scen + 1;
|
|
enemy_detected = TRUE;
|
|
}
|
|
}
|
|
//- OfN -
|
|
if (list.classname == "monster_wizard" && list.health > 0) //Because they dont have teams
|
|
{
|
|
if (Teammate(list.real_owner.team_no, self.team_no))
|
|
{
|
|
scfr = scfr + 1;
|
|
enemy_detected = FALSE;
|
|
}
|
|
else
|
|
{
|
|
scen = scen + 1;
|
|
enemy_detected = TRUE;
|
|
}
|
|
}
|
|
else if (list.classname == "item_tfgoal" && list.owned_by > 0 && list.team_no > 0 && self.team_no > 0) //Because they use owned_by
|
|
{
|
|
if (list.owned_by == self.team_no && list.team_no != self.team_no)
|
|
{
|
|
scfr = scfr + 1;
|
|
enemy_detected = FALSE;
|
|
}
|
|
else if (list.owned_by == self.team_no && list.team_no == self.team_no)
|
|
{
|
|
scfr = scfr + 1;
|
|
enemy_detected = FALSE;
|
|
}
|
|
else if (list.owned_by != self.team_no && list.team_no == self.team_no)
|
|
{
|
|
scen = scen + 1;
|
|
enemy_detected = TRUE;
|
|
}
|
|
else
|
|
any_detected = FALSE;
|
|
}
|
|
else if ((list.classname == "player" || list.classname == "building_sentrygun" || list.classname == "building_tesla" || list.classname == "building_teleporter") && list.health > 0 && !(list.cutf_items & CUTF_JAMMER))
|
|
{
|
|
if (Teammate(list.team_no, self.team_no))
|
|
{
|
|
scfr = scfr + 1;
|
|
enemy_detected = FALSE;
|
|
}
|
|
else
|
|
{
|
|
scen = scen + 1;
|
|
enemy_detected = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
any_detected = FALSE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
any_detected = FALSE;
|
|
}
|
|
|
|
// this displays the direction of the detected player
|
|
// using the cosine rule to find the angle
|
|
// cos theta = A.B divided by |A||B|
|
|
// it should return a value between 1 and -1
|
|
if (any_detected)
|
|
{
|
|
// Get the unit vector
|
|
lightningvec = normalize(list.origin - self.origin);
|
|
lightningvec = lightningvec * (vlen(list.origin - self.origin) / 5);
|
|
lightningvec = lightningvec + self.origin;
|
|
|
|
// Create the Lightning
|
|
msg_entity = self;
|
|
WriteByte (MSG_ONE, SVC_TEMPENTITY);
|
|
WriteByte (MSG_ONE, TE_LIGHTNING1);
|
|
WriteEntity (MSG_ONE, self);
|
|
WriteCoord (MSG_ONE, self.origin_x);
|
|
WriteCoord (MSG_ONE, self.origin_y);
|
|
WriteCoord (MSG_ONE, self.origin_z + 8);
|
|
WriteCoord (MSG_ONE, lightningvec_x);
|
|
WriteCoord (MSG_ONE, lightningvec_y);
|
|
WriteCoord (MSG_ONE, lightningvec_z + 8);
|
|
|
|
self.scaned = list; //CH for the sbar
|
|
if (!inAuto)
|
|
{
|
|
self.StatusRefreshTime = time + 0.2;
|
|
self.StatusBarScreen = 5;
|
|
}
|
|
} // end if(any_detected)
|
|
}
|
|
list = list.linked_list;
|
|
}
|
|
|
|
// Display the counts
|
|
// For Base Defences, it will display the counts to all team members
|
|
if ((scen == 0) && (scfr == 0) && (!inAuto))
|
|
{
|
|
sprint (self, PRINT_HIGH, "No blips.\n");
|
|
return;
|
|
}
|
|
|
|
// Update ammo levels
|
|
//W_SetCurrentAmmo ();
|
|
|
|
return;
|
|
};
|
|
|
|
//=========================================================================
|
|
// Acts just like T_RadiusDamage, but doesn't damage things, just pushes them away
|
|
// from the explosion at a speed relative to the distance from the explosion's origin.
|
|
void(entity inflictor, entity attacker, float bounce, entity ignore) T_RadiusBounce =
|
|
{
|
|
local float points;
|
|
local entity head, te;
|
|
local vector org;
|
|
|
|
head = findradius(inflictor.origin, bounce+40);
|
|
|
|
while (head)
|
|
{
|
|
if (head != ignore)
|
|
{
|
|
if (head.takedamage && head.classname != "monster_shambler")
|
|
{
|
|
org = head.origin + (head.mins + head.maxs)*0.5;
|
|
points = 0.5*vlen (org - inflictor.origin);
|
|
if (points < 0)
|
|
points = 0;
|
|
points = bounce - points;
|
|
if (head.cutf_items & CUTF_GYMNAST)
|
|
points = points * 2;
|
|
|
|
if (!IsBuilding(head) && points > 0)
|
|
{
|
|
// Bounce!!
|
|
head.velocity = org - inflictor.origin;
|
|
head.velocity = head.velocity * (points / 20);
|
|
|
|
if (head.classname != "player")
|
|
{
|
|
if(head.flags & FL_ONGROUND)
|
|
head.flags = head.flags - FL_ONGROUND;
|
|
}
|
|
else
|
|
{
|
|
//WK Add cheat immunity since they fly
|
|
makeImmune(head,time+3);
|
|
|
|
// Concuss 'em!!
|
|
// If they are already concussed, set the concussion back up
|
|
// Try to find a concusstimer entity for this player
|
|
te = find(world, classname, "timer");
|
|
while (((te.owner != head) || (te.think != ConcussionGrenadeTimer)) && (te != world))
|
|
te = find(te, classname, "timer");
|
|
if (te != world)
|
|
{
|
|
stuffcmd(head,"v_idlescale 100\n");
|
|
te.health = 100;
|
|
te.nextthink = time + GR_CONCUSS_TIME;
|
|
}
|
|
else
|
|
{
|
|
stuffcmd(head,"v_idlescale 100\n");
|
|
stuffcmd(head,"bf\n");
|
|
// Create a timer entity
|
|
te = spawn();
|
|
te.nextthink = time + GR_CONCUSS_TIME;
|
|
te.think = ConcussionGrenadeTimer;
|
|
te.team_no = attacker.team_no;
|
|
te.classname = "timer";
|
|
te.owner = head;
|
|
te.health = 100;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
head = head.chain;
|
|
}
|
|
};
|
|
//CH checks a player and returns True of False
|
|
float(entity scan, entity targ, float enemies, float friends) Scanner_Check_Player =
|
|
{
|
|
if (targ.playerclass == PC_UNDEFINED) {
|
|
return FALSE;
|
|
}
|
|
else if (targ.done_custom & CUSTOM_BUILDING) {
|
|
return FALSE;
|
|
}
|
|
else if (targ.health <= 0) {
|
|
return FALSE;
|
|
}
|
|
else if (targ.has_disconnected) {
|
|
return FALSE;
|
|
}
|
|
else if (targ == scan) {
|
|
return FALSE;
|
|
}
|
|
else if (targ.flags & FL_NOTARGET) {
|
|
return FALSE;
|
|
}
|
|
else if (targ.cutf_items & CUTF_JAMMER)
|
|
return FALSE;
|
|
//CH ALL NEW CHECKS ABOVE THIS LINE
|
|
if (teamplay)
|
|
{
|
|
if ( friends && Teammate(targ.team_no,scan.team_no) )
|
|
return TRUE;
|
|
if ( enemies && !Teammate(targ.team_no,scan.team_no) )
|
|
return TRUE;
|
|
}
|
|
else
|
|
return TRUE;
|
|
return FALSE;
|
|
};
|
|
//=========================================================================
|
|
// Returns a list of players within a radius around the origin, like findradius,
|
|
// except that some parsing of the list can be done based on the parameters passed in.
|
|
// Make sure you check that the return value is not NULL b4 using it.
|
|
entity(entity scanner, float scanrange, float enemies, float friends) T_RadiusScan =
|
|
{
|
|
local entity head;
|
|
local entity list_head;
|
|
local entity list;
|
|
local float gotatarget;
|
|
list_head = world;
|
|
list = world;
|
|
|
|
head = findradius(scanner.origin, scanrange+40);
|
|
|
|
while (head)
|
|
{
|
|
gotatarget = 0;
|
|
if (head != scanner && (friends || enemies)) // Don't pick up the entity that's scanning
|
|
{
|
|
if (head.takedamage) //item_tfgoal does not take dammage
|
|
{
|
|
if (head.classname == "player")
|
|
{
|
|
gotatarget = Scanner_Check_Player(scanner, head, enemies, friends);
|
|
}
|
|
else if ((head.classname == "building_tesla" || head.classname == "building_sentrygun" || head.classname == "building_teleporter") && (head.health > 0)) //CH uses team_no :)
|
|
{
|
|
if (teamplay)
|
|
{
|
|
if ( friends && Teammate(head.team_no, scanner.team_no) )
|
|
gotatarget = 1;
|
|
if ( enemies && !Teammate(head.team_no, scanner.team_no) )
|
|
gotatarget = 1;
|
|
}
|
|
else
|
|
gotatarget = 1;
|
|
}
|
|
else if ((head.classname == "monster_demon1") && head.health > 0) //CH demons trace back to real_owner
|
|
{
|
|
if (teamplay)
|
|
{
|
|
if ( friends && Teammate(head.real_owner.team_no, scanner.team_no) )
|
|
gotatarget = 1;
|
|
if ( enemies && !Teammate(head.real_owner.team_no, scanner.team_no) )
|
|
gotatarget = 1;
|
|
}
|
|
else
|
|
gotatarget = 1;
|
|
}
|
|
else if ((head.classname == "monster_army") && head.health > 0) //CH demons trace back to real_owner
|
|
{
|
|
if (teamplay)
|
|
{
|
|
if ( friends && Teammate(head.real_owner.team_no, scanner.team_no) )
|
|
gotatarget = 1;
|
|
if ( enemies && !Teammate(head.real_owner.team_no, scanner.team_no) )
|
|
gotatarget = 1;
|
|
}
|
|
else
|
|
gotatarget = 1;
|
|
}
|
|
else if ((head.classname == "monster_shambler") && head.health > 0) //CH demons trace back to real_owner
|
|
{
|
|
if (teamplay)
|
|
{
|
|
if ( friends && Teammate(head.real_owner.team_no, scanner.team_no) )
|
|
gotatarget = 1;
|
|
if ( enemies && !Teammate(head.real_owner.team_no, scanner.team_no) )
|
|
gotatarget = 1;
|
|
}
|
|
else
|
|
gotatarget = 1;
|
|
}
|
|
//- OfN -
|
|
else if ((head.classname == "monster_wizard") && head.health > 0) //CH demons trace back to real_owner
|
|
{
|
|
if (teamplay)
|
|
{
|
|
if ( friends && Teammate(head.real_owner.team_no, scanner.team_no) )
|
|
gotatarget = 1;
|
|
if ( enemies && !Teammate(head.real_owner.team_no, scanner.team_no) )
|
|
gotatarget = 1;
|
|
}
|
|
else
|
|
gotatarget = 1;
|
|
}
|
|
}
|
|
else if (head.classname == "item_tfgoal") //CH flags used owned_by for what team it is
|
|
{
|
|
if (teamplay)
|
|
{
|
|
if ( friends && (head.team_no > 0) && (head.owned_by > 0) && (scanner.team_no > 0) && (head.team_no == scanner.team_no) && (head.owned_by == scanner.team_no) )
|
|
gotatarget = 1;
|
|
if ( friends && (head.team_no > 0) && (head.owned_by > 0) && (scanner.team_no > 0) && (head.team_no != scanner.team_no) && (head.owned_by == scanner.team_no) )
|
|
gotatarget = 1;
|
|
if ( enemies && (head.team_no > 0) && (head.owned_by > 0) && (scanner.team_no > 0) && (head.team_no == scanner.team_no) && (head.owned_by != scanner.team_no) )
|
|
gotatarget = 1;
|
|
}
|
|
else
|
|
gotatarget = 1;
|
|
}
|
|
}
|
|
|
|
// Add this entity to the linked list if it matches the target criteria
|
|
if (gotatarget)
|
|
{
|
|
if (list)
|
|
{
|
|
list.linked_list = head;
|
|
list = list.linked_list;
|
|
}
|
|
else
|
|
{
|
|
list_head = head;
|
|
list = head;
|
|
}
|
|
}
|
|
|
|
head = head.chain;
|
|
}
|
|
|
|
return list_head;
|
|
};
|
|
|
|
//=========================================================================
|
|
// Caltrop Grenade explosion
|
|
|
|
#define GR_TYPE_CALTROP_NO 5
|
|
|
|
void (vector org, entity shooter) CreateCaltrop;
|
|
void() CaltropTouch;
|
|
|
|
void() CaltropGrenadeExplode =
|
|
{
|
|
local float i;
|
|
|
|
/* deathmsg = DMSG_GREN_CALTROP;
|
|
T_RadiusDamage (self, self.owner, 50, world);
|
|
|
|
WriteByte (MSG_BROADCAST, SVC_TEMPENTITY);
|
|
WriteByte (MSG_BROADCAST, TE_EXPLOSION);
|
|
WriteCoord (MSG_BROADCAST, self.origin_x);
|
|
WriteCoord (MSG_BROADCAST, self.origin_y);
|
|
WriteCoord (MSG_BROADCAST, self.origin_z);
|
|
multicast (self.origin, MULTICAST_PHS);
|
|
|
|
self.solid = SOLID_NOT;
|
|
*/
|
|
// Very well admiral. Engage the rebels.
|
|
i = 0;
|
|
while (i < GR_TYPE_CALTROP_NO)
|
|
{
|
|
CreateCaltrop(self.origin + '0 0 -1',self.owner);
|
|
i = i + 1;
|
|
}
|
|
|
|
#ifdef DEMO_STUFF
|
|
// Remove any camera's locks on this missile
|
|
if (self.enemy)
|
|
CamProjectileLockOff();
|
|
#endif
|
|
|
|
//BecomeExplosion();
|
|
dremove(self);
|
|
};
|
|
|
|
void (vector org, entity shooter) CreateCaltrop =
|
|
{
|
|
local float xdir,ydir,zdir;
|
|
|
|
xdir = 80 * random() - 40;
|
|
ydir = 80 * random() - 40;
|
|
zdir = 15 + 15 * random();
|
|
|
|
newmis = spawn ();
|
|
newmis.owner = shooter;
|
|
newmis.movetype = MOVETYPE_BOUNCE;
|
|
newmis.solid = SOLID_TRIGGER;
|
|
//newmis.solid = SOLID_BBOX;
|
|
|
|
newmis.classname = "caltrop";
|
|
newmis.weapon = DMSG_CALTROP;
|
|
|
|
newmis.touch = CaltropTouch;
|
|
newmis.think = SUB_Remove;
|
|
|
|
newmis.nextthink = time + 14 + random()*6; // was 7+random()
|
|
newmis.heat = time + 2; // The caltrop doesn't activate for 2 seconds
|
|
|
|
newmis.velocity_x = xdir * 2;
|
|
newmis.velocity_y = ydir * 2;
|
|
newmis.velocity_z = zdir * 15;
|
|
|
|
newmis.avelocity = '500 500 500';
|
|
|
|
setmodel (newmis, "progs/caltrop.mdl");
|
|
setsize (newmis, '-10 -10 -9', '0 0 0');
|
|
//setsize (newmis, '-5 -5 -4', '5 5 5');
|
|
|
|
setorigin (newmis, org);
|
|
};
|
|
|
|
void() CaltropTouch =
|
|
{
|
|
if (self.velocity == '0 0 0')
|
|
{
|
|
self.avelocity = '0 0 0';
|
|
self.angles = '0 0 0';
|
|
}
|
|
|
|
if (self.heat > time)
|
|
return;
|
|
|
|
if (other.takedamage && other.classname == "player")
|
|
{
|
|
if (Teammate(self.owner.team_no, other.team_no) && other != self.owner)
|
|
return;
|
|
|
|
//if (other.classname == "player")
|
|
|
|
|
|
if (self.velocity == '0 0 0') // supposedly on the ground..
|
|
{
|
|
sprint(other, PRINT_HIGH, "Ouch! Ouch! Caltrops!\n");
|
|
other.leg_damage = other.leg_damage + 2;
|
|
TeamFortress_SetSpeed(other);
|
|
deathmsg = DMSG_CALTROP;
|
|
TF_T_Damage(other, self, self.owner, 16, 0, TF_TD_OTHER);
|
|
}
|
|
else // if its moving...
|
|
{
|
|
sprint(other, PRINT_HIGH, "Woah! Caltrops!\n");
|
|
deathmsg = DMSG_FLYCALTROP;
|
|
TF_T_Damage(other, self, self.owner, 20 + random() * 9, 0, TF_TD_OTHER);
|
|
}
|
|
|
|
dremove(self);
|
|
}
|
|
};
|
|
|