mirror of
https://git.code.sf.net/p/quake/prozac-qfcc
synced 2024-11-27 14:32:03 +00:00
99190cb79a
tests for foo == world -> !foo and for != world -> foo).
807 lines
24 KiB
C++
807 lines
24 KiB
C++
#include "defs.qh"
|
|
/*======================================================
|
|
SENTRY.QC Custom TeamFortress v2.1
|
|
|
|
(c) TeamFortress Software Pty Ltd 29/2/97
|
|
(c) William Kerney 21/10/99
|
|
(c) Craig Hauser 19/3/00
|
|
========================================================
|
|
Weapons and functions for the Sentry Guns
|
|
======================================================*/
|
|
// This is a temporary hack... they'll be replaced with real bots when
|
|
// the bot code is in (should that be if? :)
|
|
// WK If is right. :)
|
|
// SentryGun AI Functions
|
|
void() Sentry_Rotate;
|
|
float() Sentry_FindTarget;
|
|
void() Sentry_FoundTarget;
|
|
void() Sentry_HuntTarget;
|
|
void(entity attacker, float damage) Sentry_Pain;
|
|
void() Sentry_Die;
|
|
float() Sentry_Fire;
|
|
//WK - Will kill enemies on touch, and adds support for flying.
|
|
void() Sentry_Touch;
|
|
/* WK==============================================
|
|
#ifndef COOP_MODE
|
|
void() ai_face =
|
|
{
|
|
local vector temp_vect,temp_ang;
|
|
self.ideal_yaw = vectoyaw(self.enemy.origin - self.origin);
|
|
self.ideal_yaw = anglemod(self.ideal_yaw);
|
|
//WK Turrets swivel instanter
|
|
if (self.tf_items & NIT_TURRET) { //Auto-swivel
|
|
self.angles_y = self.ideal_yaw;
|
|
}
|
|
else
|
|
ChangeYaw ();
|
|
};
|
|
#endif
|
|
*/
|
|
//==============================================
|
|
$cd /quake/fortress/progs/turrgun
|
|
$origin 0 -6 24
|
|
$base base
|
|
$skin skin
|
|
|
|
$frame lvl1_stand1
|
|
$frame lvl1_shoot1 lvl1_shoot2
|
|
$frame lvl2_stand1
|
|
$frame lvl2_shoot1 lvl2_shoot2
|
|
$frame lvl3_stand1
|
|
$frame lvl3_shoot1 lvl3_shoot2
|
|
|
|
/* WK -- This is how you unhack the turret's position :Þ
|
|
if (self.tf_items & NIT_TURRET) self.origin_z = self.origin_z + 40;
|
|
*/
|
|
|
|
void() RemoveGlow = {
|
|
self.effects = self.effects - (self.effects & EF_DIMLIGHT);
|
|
};
|
|
|
|
//===========================
|
|
// Level 1 Sentry Gun Frames
|
|
void() lvl1_sentry_atk3;
|
|
void() lvl1_sentry_stand =[ $lvl1_stand1, lvl1_sentry_stand ] {RemoveGlow(); Sentry_Rotate();};
|
|
void() lvl1_sentry_atk1 =[ $lvl1_shoot1, lvl1_sentry_atk3 ]
|
|
{
|
|
ai_face();
|
|
if (!self.enemy || self.enemy.health <= 0 || /* !visible(self.enemy) || */ self.enemy.has_disconnected == TRUE) //CH
|
|
lvl1_sentry_stand();
|
|
else if (self.ammo_shells <= 0)
|
|
lvl1_sentry_stand();
|
|
else if (Sentry_Fire() == FALSE)
|
|
//WK lvl1_sentry_atk3();
|
|
lvl1_sentry_stand();
|
|
};
|
|
void() lvl1_sentry_atk2 =[ $lvl1_shoot2, lvl1_sentry_atk3 ]
|
|
{
|
|
ai_face();
|
|
Sentry_Fire();
|
|
};
|
|
void() lvl1_sentry_atk3 =[ $lvl1_stand1, lvl1_sentry_atk1 ] {ai_face();};
|
|
|
|
//===========================
|
|
// Level 2 Sentry Gun Frames
|
|
void() lvl2_sentry_atk3;
|
|
void() lvl2_sentry_stand= [ $lvl2_stand1, lvl2_sentry_stand ] {RemoveGlow(); Sentry_Rotate();};
|
|
void() lvl2_sentry_atk1 = [ $lvl2_shoot1, lvl2_sentry_atk2 ]
|
|
{
|
|
ai_face();
|
|
if (!self.enemy || self.enemy.health <= 0 || /* !visible(self.enemy) || */ self.enemy.has_disconnected == TRUE) //CH
|
|
lvl2_sentry_stand();
|
|
else if (self.ammo_shells <= 0)
|
|
lvl2_sentry_stand();
|
|
else if (Sentry_Fire() == FALSE)
|
|
//WK lvl2_sentry_atk3();
|
|
lvl2_sentry_stand();
|
|
};
|
|
void() lvl2_sentry_atk2 =[ $lvl2_shoot2, lvl2_sentry_atk3 ]
|
|
{
|
|
ai_face();
|
|
Sentry_Fire();
|
|
};
|
|
void() lvl2_sentry_atk3 =[ $lvl2_stand1, lvl2_sentry_atk1 ]
|
|
{
|
|
ai_face();
|
|
Sentry_Fire();
|
|
};
|
|
|
|
//===========================
|
|
// Level 3 Sentry Gun Frames
|
|
void() lvl3_sentry_atk3;
|
|
void() lvl3_sentry_atk4;
|
|
void() lvl3_sentry_stand= [ $lvl3_stand1, lvl3_sentry_stand ] {RemoveGlow(); Sentry_Rotate();};
|
|
void() lvl3_sentry_atk1 = [ $lvl3_shoot1, lvl3_sentry_atk2 ]
|
|
{
|
|
ai_face();
|
|
if (!self.enemy || self.enemy.health <= 0 || /* !visible(self.enemy) || */ self.enemy.has_disconnected == TRUE) //CH
|
|
lvl3_sentry_stand();
|
|
else if (self.ammo_shells <= 0 && self.ammo_rockets <= 0)
|
|
lvl3_sentry_stand();
|
|
else if (self.ammo_shells <= 0 && self.ammo_rockets > 0) //CH rocket check
|
|
{
|
|
RemoveGlow();
|
|
lvl3_sentry_atk4();
|
|
}
|
|
else if (Sentry_Fire() == FALSE)
|
|
//WK lvl3_sentry_atk3();
|
|
lvl3_sentry_stand();
|
|
};
|
|
void() lvl3_sentry_atk2 =[ $lvl3_shoot2, lvl3_sentry_atk3 ]
|
|
{
|
|
ai_face();
|
|
Sentry_Fire();
|
|
};
|
|
void() lvl3_sentry_atk3 =[ $lvl3_stand1, lvl3_sentry_atk1 ]
|
|
{
|
|
ai_face();
|
|
Sentry_Fire();
|
|
};
|
|
void() lvl3_sentry_atk4 = [ $lvl3_stand1, lvl3_sentry_atk5 ]
|
|
{
|
|
ai_face();
|
|
if (!self.enemy || self.enemy.health <= 0 || /* !visible(self.enemy) || */ self.enemy.has_disconnected == TRUE) //CH
|
|
lvl3_sentry_stand();
|
|
else if (self.ammo_shells <= 0 && self.ammo_rockets <= 0)
|
|
lvl3_sentry_stand();
|
|
else if (Sentry_Fire() == FALSE)
|
|
//WK lvl3_sentry_atk3();
|
|
lvl3_sentry_stand();
|
|
};
|
|
void() lvl3_sentry_atk5 =[ $lvl3_stand1, lvl3_sentry_atk4 ]
|
|
{
|
|
ai_face();
|
|
Sentry_Fire();
|
|
};
|
|
|
|
//=============
|
|
void() Sentry_Rotate =
|
|
{
|
|
/*
|
|
// don't let them exist in walls/doors
|
|
// FIXME: pointcontents is wrong for doors
|
|
local vector testpoint = self.origin;
|
|
if (self.tf_items & NIT_TURRET)
|
|
testpoint_z -= 40;
|
|
if (pointcontents (testpoint) == CONTENTS_SOLID) {
|
|
TF_T_Damage (self, NIL, NIL, self.health + 100,
|
|
TF_TD_IGNOREARMOUR, TF_TD_OTHER);
|
|
return;
|
|
}
|
|
*/
|
|
|
|
if (self.is_malfunctioning & SCREWUP_ONE)
|
|
{
|
|
self.ideal_yaw = self.ideal_yaw + 10;
|
|
ChangeYaw();
|
|
return;
|
|
}
|
|
|
|
if (Sentry_FindTarget())
|
|
return;
|
|
|
|
//- OfN this does the sentry to not return to rotate status after enemy becomes not visible or died
|
|
// it doesn't alter the real behaviour only the look
|
|
if (self.attack_finished > time)
|
|
{
|
|
self.has_holo=1;
|
|
return;
|
|
}
|
|
|
|
if (self.has_holo==1)
|
|
{
|
|
self.has_holo=0;
|
|
sound (self, CHAN_ITEM, "weapons/trr2lost.wav", 0.25, ATTN_NORM);
|
|
}
|
|
|
|
if (self.heat == 0)
|
|
{
|
|
self.ideal_yaw = anglemod(self.waitmin);
|
|
ChangeYaw();
|
|
// 15 // 16 or 14
|
|
//was if ((anglemod(self.angles_y) <= (self.waitmin + 1)) && (anglemod(self.angles_y) >= (self.waitmin - 2))) //CH after the && fixed spaz sent!!
|
|
if ((anglemod(self.angles_y) <= (self.waitmin + 1)) && (anglemod(self.angles_y) >= (self.waitmin - 2))) //CH after the && fixed spaz sent!!
|
|
{
|
|
self.heat = 1;
|
|
if (random() < 0.1)
|
|
sound (self, CHAN_ITEM, "weapons/turridle.wav", 1, ATTN_NORM);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
self.ideal_yaw = anglemod(self.waitmax);
|
|
ChangeYaw();
|
|
|
|
//was if ((anglemod(self.angles_y) >= (self.waitmax - 1 )) && (anglemod(self.angles_y) <= (self.waitmax + 2))) //CH after the && fixed spaz sent!!
|
|
if ((anglemod(self.angles_y) >= (self.waitmax - 1 )) && (anglemod(self.angles_y) <= (self.waitmax + 2))) //CH after the && fixed spaz sent!!
|
|
self.heat = 0;
|
|
}
|
|
};
|
|
|
|
//================================================0
|
|
// new RANGE_VERYFAR hacked sentries can shot at it
|
|
|
|
float(entity targ) rangesentry =
|
|
{
|
|
local vector spot1, spot2;
|
|
local float r;
|
|
|
|
spot1 = self.origin + self.view_ofs;
|
|
spot2 = targ.origin + targ.view_ofs;
|
|
|
|
r = vlen (spot1 - spot2);
|
|
if (r < 120)
|
|
return RANGE_MELEE;
|
|
if (r < 500)
|
|
return RANGE_NEAR;
|
|
if (r < 1000)
|
|
return RANGE_MID;
|
|
if (r < 2000)
|
|
return RANGE_FAR;
|
|
return RANGE_VERYFAR;
|
|
};
|
|
|
|
|
|
float() Sentry_FindTarget =
|
|
{
|
|
self.enemy = NIL; //CH for sbar
|
|
|
|
if (infokey(NIL,"ceasefire")=="on")
|
|
return FALSE;
|
|
|
|
local entity client = NIL;
|
|
local float r, gotone, loopc;
|
|
|
|
//WK Hack to get floating sentry working
|
|
if (self.tf_items & NIT_TURRET) {
|
|
self.origin_z = self.origin_z - 40;
|
|
}
|
|
|
|
// Try a few checks to make it react faster
|
|
r = 0;
|
|
loopc = 0;
|
|
gotone = FALSE;
|
|
//WK Theortetically this will check every client on the server now
|
|
while (loopc < 32 && gotone == FALSE) //WK 3
|
|
{
|
|
client = checkclient();
|
|
|
|
gotone = TRUE;
|
|
|
|
if (!client)
|
|
gotone = FALSE;
|
|
|
|
if (!Pharse_Client(client, self, 1, 0, 0, 1))
|
|
gotone = FALSE;
|
|
|
|
r = rangesentry (client);
|
|
|
|
//- ofn
|
|
//if (r == RANGE_FAR)
|
|
// gotone = FALSE;
|
|
|
|
//WK Hack, turret should be able to see in all directions...
|
|
if (!(self.tf_items & NIT_TURRET)) {
|
|
|
|
if (r == RANGE_NEAR)
|
|
{
|
|
if (client.show_hostile < time && !infront (client))
|
|
gotone = FALSE;
|
|
}
|
|
else if (r == RANGE_MID)
|
|
{
|
|
if (!infront (client))
|
|
gotone = FALSE;
|
|
}
|
|
else if (r == RANGE_FAR) //- OfN - Sentries with enhanced circuits can shot at far range
|
|
{
|
|
if (!(self.num_mines & IMPROVED_FOUR) || !infront (client))
|
|
gotone = FALSE;
|
|
}
|
|
}
|
|
else //- OfN - a turretized sentry gun...
|
|
{
|
|
if (r == RANGE_FAR && !(self.num_mines & IMPROVED_FOUR))
|
|
gotone = FALSE;
|
|
}
|
|
|
|
//- OfN ----------------//
|
|
if (r == RANGE_VERYFAR)
|
|
gotone = FALSE;
|
|
//----------------------//
|
|
|
|
loopc = loopc + 1;
|
|
//WK Solve a bug in sentry targetting?
|
|
if (gotone) loopc = 1000;
|
|
}
|
|
|
|
if (!gotone) //if no player target found lets scan for monsters and army..
|
|
{
|
|
local entity te;
|
|
|
|
te = find(NIL, message,"XX");
|
|
|
|
while (te && gotone == FALSE)
|
|
{
|
|
gotone = TRUE;
|
|
|
|
if (Teammate(self.real_owner.team_no,te.real_owner.team_no))
|
|
gotone = FALSE;
|
|
else if (!IsMonster(te))
|
|
gotone = FALSE;
|
|
else if (te.health <= 0)
|
|
gotone = FALSE;
|
|
else if ( (!(self.tf_items & NIT_TURRET) && !visible2x(self,te)) || ((self.tf_items & NIT_TURRET) && !visible2(self,te)))
|
|
gotone = FALSE;
|
|
else
|
|
{
|
|
r = rangesentry(te);
|
|
|
|
//WK Hack, turret should be able to see in all directions...
|
|
if (!(self.tf_items & NIT_TURRET)) {
|
|
|
|
if (r == RANGE_NEAR)
|
|
{
|
|
if (!infront (te))
|
|
gotone = FALSE;
|
|
}
|
|
else if (r == RANGE_MID)
|
|
{
|
|
if (!infront (te))
|
|
gotone = FALSE;
|
|
}
|
|
else if (r == RANGE_FAR) //- OfN - Sentries with enhanced circuits can shot at far range
|
|
{
|
|
if (!(self.num_mines & IMPROVED_FOUR) || !infront (te))
|
|
gotone = FALSE;
|
|
}
|
|
}
|
|
else //- OfN - a turretized sentry gun...
|
|
{
|
|
if (r == RANGE_FAR && !(self.num_mines & IMPROVED_FOUR))
|
|
gotone = FALSE;
|
|
}
|
|
|
|
//- OfN ----------------//
|
|
if (r == RANGE_VERYFAR)
|
|
gotone = FALSE;
|
|
//----------------------//
|
|
|
|
} // this is the ELSE of !isMonster()
|
|
|
|
if (gotone)
|
|
client=te;
|
|
|
|
te = find(te, message, "XX");
|
|
|
|
} // while
|
|
|
|
} //monster scanning
|
|
|
|
//WK Unhack our hack
|
|
if (self.tf_items & NIT_TURRET) {
|
|
self.origin_z = self.origin_z + 40;
|
|
}
|
|
|
|
if (!gotone)
|
|
return FALSE;
|
|
|
|
// Found a Target
|
|
self.enemy = client;
|
|
|
|
//- OfN - what was this for??
|
|
/*if (self.enemy.classname != "player")
|
|
{
|
|
self.enemy = self.enemy.enemy;
|
|
if (self.enemy.classname != "player")
|
|
{
|
|
self.enemy = NIL;
|
|
return FALSE;
|
|
}
|
|
}*/
|
|
|
|
Sentry_FoundTarget ();
|
|
|
|
return TRUE;
|
|
};
|
|
|
|
void() Sentry_FoundTarget =
|
|
{
|
|
// Cannon Powerup Sound?
|
|
if (self.ammo_shells > 0 || (self.ammo_rockets > 0 && self.weapon == 3))
|
|
{
|
|
if (self.attack_finished < time) //- OfN -
|
|
sound (self, CHAN_VOICE, "weapons/turrspot.wav", 1, ATTN_NORM);
|
|
}
|
|
|
|
Sentry_HuntTarget ();
|
|
if (self.super_damage_finished < time)
|
|
{
|
|
self.super_damage_finished = time + 1.0; //WK Was at .5, which was too fast
|
|
|
|
if (self.num_mines & IMPROVED_FOUR)
|
|
self.super_damage_finished = time + 0.65; //- OfN - improved circuits! heh
|
|
}
|
|
};
|
|
|
|
void() Sentry_HuntTarget =
|
|
{
|
|
self.goalentity = self.enemy;
|
|
if (self.weapon == 1)
|
|
self.think = lvl1_sentry_atk1;
|
|
else if (self.weapon == 2)
|
|
self.think = lvl2_sentry_atk1;
|
|
else // if (self.weapon == 3)
|
|
{
|
|
if (self.ammo_shells <= 0 && self.ammo_rockets > 0) //CH has rockets but no shells
|
|
self.think = lvl3_sentry_atk4;
|
|
else
|
|
self.think = lvl3_sentry_atk1;
|
|
}
|
|
self.ideal_yaw = vectoyaw(self.enemy.origin - self.origin);
|
|
self.nextthink = time + 0.1;
|
|
|
|
//SUB_AttackFinished (0.5); // WK Does this do anything?
|
|
// - OfN - Nope lol it didnt
|
|
local float tfactor;
|
|
tfactor=1;
|
|
|
|
//- OfN - Does this sentry has enhanced circuits? if so.. cut down lock time
|
|
if (self.num_mines & IMPROVED_FOUR) tfactor=0.5;
|
|
|
|
//WK --- Invisible people take longer for sentries to lock onto
|
|
// because they have to use their heat sensors
|
|
if (self.enemy.modelindex == modelindex_null)
|
|
self.nextthink = time + (2*tfactor); // Must acquire a heat signal
|
|
else if (self.enemy.modelindex == modelindex_eyes)
|
|
self.nextthink = time + (1.5*tfactor); // Some visual, so its a little easier
|
|
else
|
|
self.nextthink = time + (0.5*tfactor); // Some visual, so its a little easier
|
|
};
|
|
|
|
void(entity attacker, float damage) Sentry_Pain =
|
|
{
|
|
// Update the owner's status bar
|
|
self.real_owner.StatusRefreshTime = time + 0.2;
|
|
//CH special sbar for eng.
|
|
self.real_owner.StatusBarScreen = 1;
|
|
};
|
|
|
|
void() Sentry_Die =
|
|
{
|
|
sprint(self.real_owner, PRINT_HIGH, "Your sentry gun was destroyed.\n");
|
|
self.real_owner.has_sentry = self.real_owner.has_sentry - 1;
|
|
if (self.real_owner.has_sentry < 0)
|
|
self.real_owner.has_sentry = 0;
|
|
|
|
ThrowGib("progs/t2gib1.mdl", -70);
|
|
ThrowGib("progs/t2gib2.mdl", -70);
|
|
ThrowGib("progs/t2gib3.mdl", -70);
|
|
ThrowGib("progs/t2gib4.mdl", self.skin);
|
|
|
|
if (self.classname == "building_sentrygun_base")
|
|
{
|
|
if (self.oldenemy)
|
|
dremove(self.oldenemy);
|
|
}
|
|
else
|
|
{
|
|
if (self.trigger_field)
|
|
dremove(self.trigger_field);
|
|
}
|
|
|
|
WriteByte (MSG_MULTICAST, SVC_TEMPENTITY);
|
|
WriteByte (MSG_MULTICAST, TE_EXPLOSION);
|
|
WriteCoord (MSG_MULTICAST, self.origin_x);
|
|
WriteCoord (MSG_MULTICAST, self.origin_y);
|
|
WriteCoord (MSG_MULTICAST, self.origin_z);
|
|
multicast (self.origin, MULTICAST_PHS);
|
|
dremove(self);
|
|
};
|
|
|
|
float() Sentry_Fire =
|
|
{
|
|
local vector dir;
|
|
local vector miss_factor;
|
|
|
|
//- OfN -
|
|
local vector soffset;
|
|
soffset = '0 0 0';
|
|
|
|
if (!(self.tf_items & NIT_TURRET))
|
|
soffset = '0 0 20';
|
|
|
|
// this on the end..
|
|
//self.attack_finished = time + SENTRY_UNLOCKTIME; // don't rotate immediately after target invisible or dead
|
|
|
|
if (infokey(NIL,"ceasefire")=="on") //Cyto
|
|
return FALSE;
|
|
|
|
//WK Stop gun from shooting at dead spies
|
|
if (self.enemy.is_feigning)
|
|
return FALSE;
|
|
|
|
//WK Hack to get floating sentry working - reset before all the returns
|
|
if (self.tf_items & NIT_TURRET) self.origin_z = self.origin_z - 40;
|
|
|
|
// Only fire if they're within sight
|
|
dir = self.enemy.origin - self.origin;
|
|
if (vlen(dir) > 2048) //WK Don't fire if they're too far away
|
|
{
|
|
if (self.tf_items & NIT_TURRET) self.origin_z = self.origin_z + 40;
|
|
return FALSE;
|
|
}
|
|
dir = normalize(dir);
|
|
|
|
//WK Turret can shoot in any direction
|
|
if (!(self.tf_items & NIT_TURRET)) {
|
|
if ((self.ideal_yaw - anglemod(self.angles_y)) < -10 || (self.ideal_yaw - anglemod(self.angles_y)) > 10) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
if (self.ammo_shells >= 1)
|
|
self.ammo_shells = self.ammo_shells - 1;
|
|
if (self.ammo_shells < 1) //WK Disallow half-shells
|
|
self.ammo_shells = 0;
|
|
if ((self.ammo_shells <= 0 && self.weapon != 3) || (self.ammo_shells <= 0 && self.ammo_rockets <= 0 && self.weapon == 3)) //CH stay on target if have rockets
|
|
{
|
|
if (self.tf_items & NIT_TURRET) self.origin_z = self.origin_z + 40;
|
|
return FALSE;
|
|
}
|
|
//CH this further checks while firing
|
|
traceline (self.origin + soffset, self.enemy.origin, TRUE, self);
|
|
if (trace_fraction != 1 || trace_endpos != self.enemy.origin)
|
|
{
|
|
if (self.tf_items & NIT_TURRET) self.origin_z = self.origin_z + 40;
|
|
return FALSE;
|
|
}
|
|
|
|
if (self.ammo_shells > 0)
|
|
{
|
|
//WK muzzleflash();
|
|
self.effects = self.effects | EF_DIMLIGHT;
|
|
/* if (self.tf_items & NIT_TURRET)
|
|
sound (self ,CHAN_WEAPON, "weapons/asscan2.wav", 1, ATTN_NORM);
|
|
else */
|
|
//sound (self ,CHAN_WEAPON, "weapons/sniper.wav", 1, ATTN_NORM);
|
|
sound (self ,CHAN_WEAPON, "weapons/sntr666.wav", 1, ATTN_NORM);
|
|
deathmsg = DMSG_SENTRYGUN_BULLET;
|
|
}
|
|
|
|
|
|
|
|
////////////
|
|
//WK Our hacked attempt to make sentries shoot right
|
|
//Make base not shootable //CH if its not floating it needs this
|
|
if (!(self.tf_items & NIT_TURRET))
|
|
{
|
|
if (self.trigger_field) self.trigger_field.solid = SOLID_NOT;
|
|
//soffset = '0 0 20'; // non-turretized sentries correction
|
|
}
|
|
|
|
if (self.is_malfunctioning & SCREWUP_TWO)
|
|
{
|
|
miss_factor_z = random() * 200 - 100;
|
|
miss_factor_y = random() * 200 - 100;
|
|
miss_factor_x = random() * 200 - 100;
|
|
traceline (self.origin + soffset, self.enemy.origin + miss_factor, FALSE, self);
|
|
}
|
|
else
|
|
traceline (self.origin + soffset, self.enemy.origin, FALSE, self);
|
|
if (trace_fraction != 1.0 && trace_ent.takedamage && self.ammo_shells > 0) //Hit something and has shells
|
|
{
|
|
SpawnBlood (trace_endpos, 50);
|
|
|
|
local float thedmg;
|
|
local float therange;
|
|
|
|
therange = rangesentry(trace_ent);
|
|
thedmg = 6;
|
|
|
|
// OfN - damage now depends on distance to target
|
|
if (therange == RANGE_MELEE)
|
|
thedmg = 12;
|
|
else if (therange == RANGE_NEAR)
|
|
thedmg = 6;
|
|
else if (therange == RANGE_MID)
|
|
thedmg = 4;
|
|
else if (therange == RANGE_FAR) // only circuit hacked sentries shot at this range
|
|
thedmg = 3;
|
|
else thedmg = 2; // this shouldnt happen ever (RANGE_VERYFAR)
|
|
|
|
// OfN - hacked sentries do more damage (improved circuits)
|
|
if (self.num_mines & IMPROVED_FOUR)
|
|
thedmg = thedmg * 1.25; // 1.25 too high?
|
|
|
|
TF_T_Damage (trace_ent, self, self, thedmg, TF_TD_NOTTEAM, TF_TD_SHOT);
|
|
}
|
|
else if (trace_fraction != 1.0 && trace_ent.classname == "force_field")
|
|
{
|
|
FieldExplosion(trace_ent,trace_endpos,trace_ent);
|
|
PutFieldWork(trace_ent);
|
|
}
|
|
|
|
|
|
//FireBullets (1, dir, '0.1 0.1 0');
|
|
//CH if its not floating it needs this
|
|
if (!(self.tf_items & NIT_TURRET))
|
|
{
|
|
if (self.trigger_field) self.trigger_field.solid = SOLID_BBOX;
|
|
}
|
|
/////////////
|
|
|
|
// Level 3 Turrets fire rockets every 3 seconds
|
|
if (self.weapon == 3 && self.ammo_rockets > 0 && self.super_damage_finished < time)
|
|
{
|
|
//sound (self, CHAN_WEAPON, "weapons/rocket1i.wav", 1, ATTN_NORM); UGLY AND STOPS COOL SOUND
|
|
// wtf happens here, why is this the supernailgun sound?
|
|
if (self.is_malfunctioning & SCREWUP_THREE)
|
|
{
|
|
local float damg;
|
|
damg = random() * 50 + 120;
|
|
T_RadiusDamage(self, self, damg, NIL);
|
|
WriteByte (MSG_MULTICAST, SVC_TEMPENTITY);
|
|
WriteByte (MSG_MULTICAST, TE_EXPLOSION);
|
|
WriteCoord (MSG_MULTICAST, self.origin_x);
|
|
WriteCoord (MSG_MULTICAST, self.origin_y);
|
|
WriteCoord (MSG_MULTICAST, self.origin_z);
|
|
if (self.tf_items & NIT_TURRET)
|
|
self.origin_z = self.origin_z + 40;
|
|
return FALSE;
|
|
}
|
|
|
|
newmis = spawn ();
|
|
newmis.owner = self;
|
|
newmis.movetype = MOVETYPE_FLYMISSILE;
|
|
newmis.solid = SOLID_BBOX;
|
|
|
|
// set newmis speed
|
|
if (self.is_malfunctioning & SCREWUP_TWO) // SB - oh no we've been hax0red
|
|
{
|
|
miss_factor_z = random() * 200 - 100;
|
|
miss_factor_y = random() * 200 - 100;
|
|
miss_factor_x = random() * 200 - 100;
|
|
}
|
|
else
|
|
{
|
|
miss_factor_z = 0;
|
|
miss_factor_y = 0;
|
|
miss_factor_x = 0;
|
|
}
|
|
|
|
if (self.tf_items & NIT_TURRET)
|
|
newmis.velocity = normalize(self.enemy.origin + miss_factor - (self.origin - '0 0 16')) * 800;
|
|
else
|
|
newmis.velocity = normalize(self.enemy.origin + miss_factor - (self.origin + '0 0 16')) * 800;
|
|
newmis.angles = vectoangles(newmis.velocity);
|
|
newmis.weapon = DMSG_SENTRYGUN_ROCKET;
|
|
newmis.touch = T_MissileTouch;
|
|
|
|
// set newmis duration
|
|
newmis.nextthink = time + 5;
|
|
newmis.think = SUB_Remove;
|
|
|
|
setmodel (newmis, "progs/missile.mdl");
|
|
setsize (newmis, '0 0 0', '0 0 0');
|
|
if (self.tf_items & NIT_TURRET) //Determines if rocket should spawn above or below sent
|
|
setorigin (newmis, self.origin + v_forward*8 - '0 0 16'); //CH make rocket 16 below
|
|
else
|
|
setorigin (newmis, self.origin + v_forward*8 + '0 0 16'); //CH make rocket 16 above
|
|
|
|
self.super_damage_finished = time + 3;
|
|
|
|
// - OfN - if this sentry has enhanced circuits then delay between rockets firing is cut half
|
|
if (self.num_mines & IMPROVED_FOUR) self.super_damage_finished = time + 1.5;
|
|
|
|
self.ammo_rockets = self.ammo_rockets - 1;
|
|
|
|
if (self.ammo_rockets == 5) //CH 10 was too high
|
|
sprint(self.real_owner, PRINT_HIGH, "Sentry Gun is low on rockets.\n");
|
|
}
|
|
// Warn owner that it's low on ammo
|
|
//WK if (self.ammo_shells == 0 && (random() < 0.1))
|
|
if ((self.ammo_shells == 0 && self.weapon != 3) || (self.ammo_shells == 0 && self.weapon == 3 && self.ammo_rockets > 0 && (random() < 0.05))) //CH .05 seems to be good.
|
|
sprint(self.real_owner, PRINT_HIGH, "Sentry Gun is out of shells.\n");
|
|
else if (self.ammo_shells == 20)
|
|
sprint(self.real_owner, PRINT_HIGH, "Sentry Gun is low on shells.\n");
|
|
|
|
//WK < 0.1
|
|
if (self.ammo_rockets == 0 && self.weapon == 3 && (random() <= 0.1))
|
|
sprint(self.real_owner, PRINT_HIGH, "Sentry Gun is out of rockets.\n");
|
|
|
|
if (self.tf_items & NIT_TURRET) self.origin_z = self.origin_z + 40;
|
|
|
|
self.attack_finished = time + SENTRY_UNLOCKTIME + 2 * random() - 1; // don't rotate immediately after target invisible or dead
|
|
|
|
return TRUE;
|
|
};
|
|
|
|
//WK Returns TRUE if the vector is not a spot a turret can attach to
|
|
float(vector vec) BadSpot =
|
|
{
|
|
/*
|
|
if (pointcontents(vec) == CONTENTS_SOLID)
|
|
sprint(self.real_owner,PRINT_HIGH,"SOLID Point\n");
|
|
else if (pointcontents(vec) == CONTENTS_EMPTY)
|
|
sprint(self.real_owner,PRINT_HIGH,"EMPTY Point\n");
|
|
else if (pointcontents(vec) == CONTENTS_SKY)
|
|
sprint(self.real_owner,PRINT_HIGH,"SKY Point\n");
|
|
else if (pointcontents(vec) == CONTENTS_WATER)
|
|
sprint(self.real_owner,PRINT_HIGH,"WATER Point\n");
|
|
else
|
|
sprint(self.real_owner,PRINT_HIGH,"BUG: Other Point\n");
|
|
*/
|
|
if (pointcontents(vec) == CONTENTS_SKY)
|
|
return TRUE;
|
|
return FALSE;
|
|
};
|
|
|
|
//WK Sentry Touch function
|
|
//Will kill bad guy
|
|
void() Sentry_Touch =
|
|
{
|
|
//WK Check for blockage
|
|
if (pointcontents(self.origin) == CONTENTS_SKY)
|
|
{
|
|
sprint(self.real_owner, PRINT_HIGH, "Your sentry gun flew away.\n");
|
|
Sentry_Die();
|
|
return;
|
|
}
|
|
|
|
if (other.takedamage && !(self.tf_items & NIT_TURRET)) // OfN - fixme: doesn't check for enemy disguised spies
|
|
{
|
|
deathmsg = DMSG_BUG_ZAPPER;
|
|
|
|
if (IsMonster(other))
|
|
{
|
|
if (!Teammate(other.real_owner.team_no, self.real_owner.team_no))
|
|
{
|
|
TF_T_Damage (other, self, self, 400, TF_TD_NOTTEAM, TF_TD_ELECTRICITY);
|
|
return;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (!Teammate(other.team_no,self.real_owner.team_no) && (other.is_undercover != 1)) // <== FIXME
|
|
//TF_T_Damage (other, self, self.real_owner, 400, TF_TD_NOTTEAM, TF_TD_ELECTRICITY);
|
|
TF_T_Damage (other, self, self, 400, TF_TD_NOTTEAM, TF_TD_ELECTRICITY);
|
|
return;
|
|
}
|
|
//I'm in midflight and hit something
|
|
if (self.tf_items & NIT_TURRET && self.movetype == MOVETYPE_FLY)
|
|
{
|
|
//WK Check to see if we are blocked
|
|
//WK These numbers seem to be about right
|
|
if ( BadSpot(self.origin + '0 0 20') || BadSpot(self.origin + '0 0 22'))
|
|
{
|
|
sprint(self.real_owner, PRINT_HIGH, "Your sentry gun flew away.\n");
|
|
Sentry_Die();
|
|
return;
|
|
}
|
|
if (!other) { //The eagle has landed!
|
|
// sprint(self.real_owner, PRINT_HIGH, "The eagle has landed!\n");
|
|
self.flags = self.flags | FL_ONGROUND;
|
|
self.movetype = MOVETYPE_STEP;
|
|
self.origin_z = self.origin_z + 32;//32; // was 40, old custom bug? fixed
|
|
return;
|
|
}
|
|
else if (other.classname == "player")
|
|
{
|
|
deathmsg = DMSG_BUG_ZAPPER;
|
|
//TF_T_Damage (other, self, self.real_owner, 400, TF_TD_NOTTEAM, TF_TD_ELECTRICITY);
|
|
TF_T_Damage (other, self, self, 400, TF_TD_NOTTEAM, TF_TD_ELECTRICITY);
|
|
|
|
self.velocity_z = 200; //- OfN reset velocity, not stop
|
|
}
|
|
else
|
|
{
|
|
deathmsg = DMSG_BUG_ZAPPER;
|
|
//TF_T_Damage (other, self, self.real_owner, 400, TF_TD_NOTTEAM, TF_TD_ELECTRICITY);
|
|
if (!Teammate(other.real_owner.team_no,self.real_owner.team_no))
|
|
TF_T_Damage (other, self, self, 400, TF_TD_NOTTEAM, TF_TD_ELECTRICITY);
|
|
Sentry_Die();
|
|
}
|
|
//sprint(self.real_owner, PRINT_HIGH, "The eagle has died.\n");
|
|
}
|
|
};
|