2013-04-19 02:52:48 +00:00
// g_weapon.c
// perform the server side effects of a weapon firing
// leave this line at the top for all g_xxxx.cpp files...
# include "g_headers.h"
# include "g_local.h"
# include "g_functions.h"
# include "anims.h"
# include "b_local.h"
# include "wp_saber.h"
# include "g_vehicles.h"
static vec3_t forward , vright , up ;
static vec3_t muzzle ;
void drop_charge ( gentity_t * ent , vec3_t start , vec3_t dir ) ;
void ViewHeightFix ( const gentity_t * const ent ) ;
qboolean LogAccuracyHit ( gentity_t * target , gentity_t * attacker ) ;
extern qboolean G_BoxInBounds ( const vec3_t point , const vec3_t mins , const vec3_t maxs , const vec3_t boundsMins , const vec3_t boundsMaxs ) ;
extern qboolean Jedi_DodgeEvasion ( gentity_t * self , gentity_t * shooter , trace_t * tr , int hitLoc ) ;
extern qboolean PM_DroidMelee ( int npc_class ) ;
extern void G_Knockdown ( gentity_t * self , gentity_t * attacker , const vec3_t pushDir , float strength , qboolean breakSaberLock ) ;
extern qboolean G_HasKnockdownAnims ( gentity_t * ent ) ;
static gentity_t * ent_list [ MAX_GENTITIES ] ;
extern cvar_t * g_debugMelee ;
// Bryar Pistol
//--------
# define BRYAR_PISTOL_VEL 1800
# define BRYAR_PISTOL_DAMAGE 14
# define BRYAR_CHARGE_UNIT 200.0f // bryar charging gives us one more unit every 200ms--if you change this, you'll have to do the same in bg_pmove
// E11 Blaster
//---------
# define BLASTER_MAIN_SPREAD 0.5f
# define BLASTER_ALT_SPREAD 1.5f
# define BLASTER_NPC_SPREAD 0.5f
# define BLASTER_VELOCITY 2300
# define BLASTER_NPC_VEL_CUT 0.5f
# define BLASTER_NPC_HARD_VEL_CUT 0.7f
# define BLASTER_DAMAGE 20
# define BLASTER_NPC_DAMAGE_EASY 6
# define BLASTER_NPC_DAMAGE_NORMAL 12 // 14
# define BLASTER_NPC_DAMAGE_HARD 16 // 18
// Tenloss Disruptor
//----------
# define DISRUPTOR_MAIN_DAMAGE 14
# define DISRUPTOR_NPC_MAIN_DAMAGE_EASY 5
# define DISRUPTOR_NPC_MAIN_DAMAGE_MEDIUM 10
# define DISRUPTOR_NPC_MAIN_DAMAGE_HARD 15
# define DISRUPTOR_ALT_DAMAGE 12
# define DISRUPTOR_NPC_ALT_DAMAGE_EASY 15
# define DISRUPTOR_NPC_ALT_DAMAGE_MEDIUM 25
# define DISRUPTOR_NPC_ALT_DAMAGE_HARD 30
# define DISRUPTOR_ALT_TRACES 3 // can go through a max of 3 entities
# define DISRUPTOR_CHARGE_UNIT 150.0f // distruptor charging gives us one more unit every 150ms--if you change this, you'll have to do the same in bg_pmove
// Wookie Bowcaster
//----------
# define BOWCASTER_DAMAGE 45
# define BOWCASTER_VELOCITY 1300
# define BOWCASTER_NPC_DAMAGE_EASY 12
# define BOWCASTER_NPC_DAMAGE_NORMAL 24
# define BOWCASTER_NPC_DAMAGE_HARD 36
# define BOWCASTER_SPLASH_DAMAGE 0
# define BOWCASTER_SPLASH_RADIUS 0
# define BOWCASTER_SIZE 2
# define BOWCASTER_ALT_SPREAD 5.0f
# define BOWCASTER_VEL_RANGE 0.3f
# define BOWCASTER_CHARGE_UNIT 200.0f // bowcaster charging gives us one more unit every 200ms--if you change this, you'll have to do the same in bg_pmove
// Heavy Repeater
//----------
# define REPEATER_SPREAD 1.4f
# define REPEATER_NPC_SPREAD 0.7f
# define REPEATER_DAMAGE 8
# define REPEATER_VELOCITY 1600
# define REPEATER_NPC_DAMAGE_EASY 2
# define REPEATER_NPC_DAMAGE_NORMAL 4
# define REPEATER_NPC_DAMAGE_HARD 6
# define REPEATER_ALT_SIZE 3 // half of bbox size
# define REPEATER_ALT_DAMAGE 60
# define REPEATER_ALT_SPLASH_DAMAGE 60
# define REPEATER_ALT_SPLASH_RADIUS 128
# define REPEATER_ALT_VELOCITY 1100
# define REPEATER_ALT_NPC_DAMAGE_EASY 15
# define REPEATER_ALT_NPC_DAMAGE_NORMAL 30
# define REPEATER_ALT_NPC_DAMAGE_HARD 45
// DEMP2
//----------
# define DEMP2_DAMAGE 15
# define DEMP2_VELOCITY 1800
# define DEMP2_NPC_DAMAGE_EASY 6
# define DEMP2_NPC_DAMAGE_NORMAL 12
# define DEMP2_NPC_DAMAGE_HARD 18
# define DEMP2_SIZE 2 // half of bbox size
# define DEMP2_ALT_DAMAGE 15
# define DEMP2_CHARGE_UNIT 500.0f // demp2 charging gives us one more unit every 500ms--if you change this, you'll have to do the same in bg_pmove
# define DEMP2_ALT_RANGE 4096
# define DEMP2_ALT_SPLASHRADIUS 256
// Golan Arms Flechette
//---------
# define FLECHETTE_SHOTS 6
# define FLECHETTE_SPREAD 4.0f
# define FLECHETTE_DAMAGE 15
# define FLECHETTE_VEL 3500
# define FLECHETTE_SIZE 1
# define FLECHETTE_ALT_DAMAGE 20
# define FLECHETTE_ALT_SPLASH_DAM 20
# define FLECHETTE_ALT_SPLASH_RAD 128
// NOT CURRENTLY USED
# define FLECHETTE_MINE_RADIUS_CHECK 200
# define FLECHETTE_MINE_VEL 1000
# define FLECHETTE_MINE_DAMAGE 100
# define FLECHETTE_MINE_SPLASH_DAMAGE 200
# define FLECHETTE_MINE_SPLASH_RADIUS 200
// Personal Rocket Launcher
//---------
# define ROCKET_VELOCITY 900
# define ROCKET_DAMAGE 100
# define ROCKET_SPLASH_DAMAGE 100
# define ROCKET_SPLASH_RADIUS 160
# define ROCKET_NPC_DAMAGE_EASY 20
# define ROCKET_NPC_DAMAGE_NORMAL 40
# define ROCKET_NPC_DAMAGE_HARD 60
# define ROCKET_SIZE 3
# define ROCKET_ALT_VELOCITY (ROCKET_VELOCITY*0.5)
# define ROCKET_ALT_THINK_TIME 100
// some naughty little things that are used cg side
int g_rocketLockEntNum = ENTITYNUM_NONE ;
int g_rocketLockTime = 0 ;
int g_rocketSlackTime = 0 ;
// Concussion Rifle
//---------
//primary
# define CONC_VELOCITY 3000
# define CONC_DAMAGE 150
# define CONC_NPC_SPREAD 0.7f
# define CONC_NPC_DAMAGE_EASY 15
# define CONC_NPC_DAMAGE_NORMAL 30
# define CONC_NPC_DAMAGE_HARD 50
# define CONC_SPLASH_DAMAGE 50
# define CONC_SPLASH_RADIUS 300
//alt
# define CONC_ALT_DAMAGE 225 //100
# define CONC_ALT_NPC_DAMAGE_EASY 10
# define CONC_ALT_NPC_DAMAGE_MEDIUM 20
# define CONC_ALT_NPC_DAMAGE_HARD 30
// Emplaced Gun
//--------------
# define EMPLACED_VEL 6000 // very fast
# define EMPLACED_DAMAGE 150 // and very damaging
# define EMPLACED_SIZE 5 // make it easier to hit things
// ATST Main Gun
//--------------
# define ATST_MAIN_VEL 4000 //
# define ATST_MAIN_DAMAGE 25 //
# define ATST_MAIN_SIZE 3 // make it easier to hit things
// ATST Side Gun
//---------------
# define ATST_SIDE_MAIN_DAMAGE 75
# define ATST_SIDE_MAIN_VELOCITY 1300
# define ATST_SIDE_MAIN_NPC_DAMAGE_EASY 30
# define ATST_SIDE_MAIN_NPC_DAMAGE_NORMAL 40
# define ATST_SIDE_MAIN_NPC_DAMAGE_HARD 50
# define ATST_SIDE_MAIN_SIZE 4
# define ATST_SIDE_MAIN_SPLASH_DAMAGE 10 // yeah, pretty small, either zero out or make it worth having?
# define ATST_SIDE_MAIN_SPLASH_RADIUS 16 // yeah, pretty small, either zero out or make it worth having?
# define ATST_SIDE_ALT_VELOCITY 1100
# define ATST_SIDE_ALT_NPC_VELOCITY 600
# define ATST_SIDE_ALT_DAMAGE 130
# define ATST_SIDE_ROCKET_NPC_DAMAGE_EASY 30
# define ATST_SIDE_ROCKET_NPC_DAMAGE_NORMAL 50
# define ATST_SIDE_ROCKET_NPC_DAMAGE_HARD 90
# define ATST_SIDE_ALT_SPLASH_DAMAGE 130
# define ATST_SIDE_ALT_SPLASH_RADIUS 200
# define ATST_SIDE_ALT_ROCKET_SIZE 5
# define ATST_SIDE_ALT_ROCKET_SPLASH_SCALE 0.5f // scales splash for NPC's
// Stun Baton
//--------------
# define STUN_BATON_DAMAGE 22
# define STUN_BATON_ALT_DAMAGE 22
# define STUN_BATON_RANGE 25
// Laser Trip Mine
//--------------
# define LT_DAMAGE 150
# define LT_SPLASH_RAD 256.0f
# define LT_SPLASH_DAM 90
# define LT_VELOCITY 250.0f
# define LT_ALT_VELOCITY 1000.0f
# define PROX_MINE_RADIUS_CHECK 190
# define LT_SIZE 3.0f
# define LT_ALT_TIME 2000
# define LT_ACTIVATION_DELAY 1000
# define LT_DELAY_TIME 50
// Thermal Detonator
//--------------
# define TD_DAMAGE 100
# define TD_NPC_DAMAGE_CUT 0.6f // NPC thrown dets deliver only 60% of the damage that a player thrown one does
# define TD_SPLASH_RAD 128
# define TD_SPLASH_DAM 90
# define TD_VELOCITY 900
# define TD_MIN_CHARGE 0.15f
# define TD_TIME 4000
# define TD_THINK_TIME 300 // don't think too often?
# define TD_TEST_RAD (TD_SPLASH_RAD * 0.8f) // no sense in auto-blowing up if exactly on the radius edge--it would hardly do any damage
# define TD_ALT_TIME 3000
# define TD_ALT_DAMAGE 100
# define TD_ALT_SPLASH_RAD 128
# define TD_ALT_SPLASH_DAM 90
# define TD_ALT_VELOCITY 600
# define TD_ALT_MIN_CHARGE 0.15f
# define TD_ALT_TIME 3000
// Tusken Rifle Shot
//--------------
# define TUSKEN_RIFLE_VEL 3000 // fast
# define TUSKEN_RIFLE_DAMAGE_EASY 20 // damaging
# define TUSKEN_RIFLE_DAMAGE_MEDIUM 30 // very damaging
# define TUSKEN_RIFLE_DAMAGE_HARD 50 // extremely damaging
// Weapon Helper Functions
float weaponSpeed [ WP_NUM_WEAPONS ] [ 2 ] =
{
0 , 0 , //WP_NONE,
0 , 0 , //WP_SABER, // NOTE: lots of code assumes this is the first weapon (... which is crap) so be careful -Ste.
BRYAR_PISTOL_VEL , BRYAR_PISTOL_VEL , //WP_BLASTER_PISTOL,
BLASTER_VELOCITY , BLASTER_VELOCITY , //WP_BLASTER,
Q3_INFINITE , Q3_INFINITE , //WP_DISRUPTOR,
BOWCASTER_VELOCITY , BOWCASTER_VELOCITY , //WP_BOWCASTER,
REPEATER_VELOCITY , REPEATER_ALT_VELOCITY , //WP_REPEATER,
DEMP2_VELOCITY , DEMP2_ALT_RANGE , //WP_DEMP2,
FLECHETTE_VEL , FLECHETTE_MINE_VEL , //WP_FLECHETTE,
ROCKET_VELOCITY , ROCKET_ALT_VELOCITY , //WP_ROCKET_LAUNCHER,
TD_VELOCITY , TD_ALT_VELOCITY , //WP_THERMAL,
0 , 0 , //WP_TRIP_MINE,
0 , 0 , //WP_DET_PACK,
CONC_VELOCITY , Q3_INFINITE , //WP_CONCUSSION,
0 , 0 , //WP_MELEE, // Any ol' melee attack
0 , 0 , //WP_STUN_BATON,
BRYAR_PISTOL_VEL , BRYAR_PISTOL_VEL , //WP_BRYAR_PISTOL,
EMPLACED_VEL , EMPLACED_VEL , //WP_EMPLACED_GUN,
BRYAR_PISTOL_VEL , BRYAR_PISTOL_VEL , //WP_BOT_LASER, // Probe droid - Laser blast
0 , 0 , //WP_TURRET, // turret guns
ATST_MAIN_VEL , ATST_MAIN_VEL , //WP_ATST_MAIN,
ATST_SIDE_MAIN_VELOCITY , ATST_SIDE_ALT_NPC_VELOCITY , //WP_ATST_SIDE,
EMPLACED_VEL , EMPLACED_VEL , //WP_TIE_FIGHTER,
EMPLACED_VEL , REPEATER_ALT_VELOCITY , //WP_RAPID_FIRE_CONC,
0 , 0 , //WP_JAWA,
TUSKEN_RIFLE_VEL , TUSKEN_RIFLE_VEL , //WP_TUSKEN_RIFLE,
0 , 0 , //WP_TUSKEN_STAFF,
0 , 0 , //WP_SCEPTER,
0 , 0 , //WP_NOGHRI_STICK,
} ;
float WP_SpeedOfMissileForWeapon ( int wp , qboolean alt_fire )
{
if ( alt_fire )
{
return weaponSpeed [ wp ] [ 1 ] ;
}
return weaponSpeed [ wp ] [ 0 ] ;
}
//-----------------------------------------------------------------------------
static void WP_TraceSetStart ( const gentity_t * ent , vec3_t start , const vec3_t mins , const vec3_t maxs )
//-----------------------------------------------------------------------------
{
//make sure our start point isn't on the other side of a wall
trace_t tr ;
vec3_t entMins , newstart ;
vec3_t entMaxs ;
VectorSet ( entMaxs , 5 , 5 , 5 ) ;
VectorScale ( entMaxs , - 1 , entMins ) ;
if ( ! ent - > client )
{
return ;
}
VectorCopy ( ent - > currentOrigin , newstart ) ;
newstart [ 2 ] = start [ 2 ] ; // force newstart to be on the same plane as the muzzle ( start )
gi . trace ( & tr , newstart , entMins , entMaxs , start , ent - > s . number , MASK_SOLID | CONTENTS_SHOTCLIP ) ;
if ( tr . startsolid | | tr . allsolid )
{
// there is a problem here..
return ;
}
if ( tr . fraction < 1.0f )
{
VectorCopy ( tr . endpos , start ) ;
}
}
extern Vehicle_t * G_IsRidingVehicle ( gentity_t * ent ) ;
//-----------------------------------------------------------------------------
gentity_t * CreateMissile ( vec3_t org , vec3_t dir , float vel , int life , gentity_t * owner , qboolean altFire = qfalse )
//-----------------------------------------------------------------------------
{
gentity_t * missile ;
missile = G_Spawn ( ) ;
missile - > nextthink = level . time + life ;
missile - > e_ThinkFunc = thinkF_G_FreeEntity ;
missile - > s . eType = ET_MISSILE ;
missile - > owner = owner ;
Vehicle_t * pVeh = G_IsRidingVehicle ( owner ) ;
missile - > alt_fire = altFire ;
missile - > s . pos . trType = TR_LINEAR ;
missile - > s . pos . trTime = level . time ; // - 10; // move a bit on the very first frame
VectorCopy ( org , missile - > s . pos . trBase ) ;
VectorScale ( dir , vel , missile - > s . pos . trDelta ) ;
if ( pVeh )
{
missile - > s . eFlags | = EF_USE_ANGLEDELTA ;
vectoangles ( missile - > s . pos . trDelta , missile - > s . angles ) ;
VectorMA ( missile - > s . pos . trDelta , 2.0f , pVeh - > m_pParentEntity - > client - > ps . velocity , missile - > s . pos . trDelta ) ;
}
VectorCopy ( org , missile - > currentOrigin ) ;
gi . linkentity ( missile ) ;
return missile ;
}
//-----------------------------------------------------------------------------
static void WP_Stick ( gentity_t * missile , trace_t * trace , float fudge_distance = 0.0f )
//-----------------------------------------------------------------------------
{
vec3_t org , ang ;
// not moving or rotating
missile - > s . pos . trType = TR_STATIONARY ;
VectorClear ( missile - > s . pos . trDelta ) ;
VectorClear ( missile - > s . apos . trDelta ) ;
// so we don't stick into the wall
VectorMA ( trace - > endpos , fudge_distance , trace - > plane . normal , org ) ;
G_SetOrigin ( missile , org ) ;
vectoangles ( trace - > plane . normal , ang ) ;
G_SetAngles ( missile , ang ) ;
// I guess explode death wants me as the normal?
// VectorCopy( trace->plane.normal, missile->pos1 );
gi . linkentity ( missile ) ;
}
// This version shares is in the thinkFunc format
//-----------------------------------------------------------------------------
void WP_Explode ( gentity_t * self )
//-----------------------------------------------------------------------------
{
gentity_t * attacker = self ;
vec3_t forward = { 0 , 0 , 1 } ;
// stop chain reaction runaway loops
self - > takedamage = qfalse ;
self - > s . loopSound = 0 ;
// VectorCopy( self->currentOrigin, self->s.pos.trBase );
if ( ! self - > client )
{
AngleVectors ( self - > s . angles , forward , NULL , NULL ) ;
}
if ( self - > fxID > 0 )
{
G_PlayEffect ( self - > fxID , self - > currentOrigin , forward ) ;
}
if ( self - > owner )
{
attacker = self - > owner ;
}
else if ( self - > activator )
{
attacker = self - > activator ;
}
if ( self - > splashDamage > 0 & & self - > splashRadius > 0 )
{
G_RadiusDamage ( self - > currentOrigin , attacker , self - > splashDamage , self - > splashRadius , 0 /*don't ignore attacker*/ , MOD_EXPLOSIVE_SPLASH ) ;
}
if ( self - > target )
{
G_UseTargets ( self , attacker ) ;
}
G_SetOrigin ( self , self - > currentOrigin ) ;
self - > nextthink = level . time + 50 ;
self - > e_ThinkFunc = thinkF_G_FreeEntity ;
}
// We need to have a dieFunc, otherwise G_Damage won't actually make us die. I could modify G_Damage, but that entails too many changes
//-----------------------------------------------------------------------------
void WP_ExplosiveDie ( gentity_t * self , gentity_t * inflictor , gentity_t * attacker , int damage , int meansOfDeath , int dFlags , int hitLoc )
//-----------------------------------------------------------------------------
{
self - > enemy = attacker ;
if ( attacker & & ! attacker - > s . number )
{
// less damage when shot by player
self - > splashDamage / = 3 ;
self - > splashRadius / = 3 ;
}
self - > s . eFlags & = ~ EF_FIRING ; // don't draw beam if we are dead
WP_Explode ( self ) ;
}
/*
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PLAYER ITEMS
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
/*
# define SEEKER_RADIUS 500
gentity_t * SeekerAcquiresTarget ( gentity_t * ent , vec3_t pos )
{
vec3_t seekerPos ;
float angle ;
gentity_t * entityList [ MAX_GENTITIES ] ; // targets within inital radius
gentity_t * visibleTargets [ MAX_GENTITIES ] ; // final filtered target list
int numListedEntities ;
int i , e ;
gentity_t * target ;
vec3_t mins , maxs ;
angle = cg . time * 0.004f ;
// must match cg_effects ( CG_Seeker ) & g_weapon ( SeekerAcquiresTarget ) & cg_weapons ( CG_FireSeeker )
seekerPos [ 0 ] = ent - > currentOrigin [ 0 ] + 18 * cos ( angle ) ;
seekerPos [ 1 ] = ent - > currentOrigin [ 1 ] + 18 * sin ( angle ) ;
seekerPos [ 2 ] = ent - > currentOrigin [ 2 ] + ent - > client - > ps . viewheight + 8 + ( 3 * cos ( level . time * 0.001f ) ) ;
for ( i = 0 ; i < 3 ; i + + )
{
mins [ i ] = seekerPos [ i ] - SEEKER_RADIUS ;
maxs [ i ] = seekerPos [ i ] + SEEKER_RADIUS ;
}
// get potential targets within radius
numListedEntities = gi . EntitiesInBox ( mins , maxs , entityList , MAX_GENTITIES ) ;
i = 0 ; // reset counter
for ( e = 0 ; e < numListedEntities ; e + + )
{
target = entityList [ e ] ;
// seeker owner not a valid target
if ( target = = ent )
{
continue ;
}
// only players are valid targets
if ( ! target - > client )
{
continue ;
}
// teammates not valid targets
if ( OnSameTeam ( ent , target ) )
{
continue ;
}
// don't shoot at dead things
if ( target - > health < = 0 )
{
continue ;
}
if ( CanDamage ( target , seekerPos ) ) // visible target, so add it to the list
{
visibleTargets [ i + + ] = entityList [ e ] ;
}
}
if ( i )
{
// ok, now we know there are i visible targets. Pick one as the seeker's target
target = visibleTargets [ Q_irand ( 0 , i - 1 ) ] ;
VectorCopy ( seekerPos , pos ) ;
return target ;
}
return NULL ;
}
static void WP_FireBlasterMissile ( gentity_t * ent , vec3_t start , vec3_t dir , qboolean altFire ) ;
void FireSeeker ( gentity_t * owner , gentity_t * target , vec3_t origin , vec3_t dir )
{
VectorSubtract ( target - > currentOrigin , origin , dir ) ;
VectorNormalize ( dir ) ;
// for now I'm just using the scavenger bullet.
WP_FireBlasterMissile ( owner , origin , dir , qfalse ) ;
}
*/
# ifdef _XBOX // Auto-aim
static float VectorDistanceSquared ( vec3_t p1 , vec3_t p2 )
{
vec3_t dir ;
VectorSubtract ( p2 , p1 , dir ) ;
return VectorLengthSquared ( dir ) ;
}
int WP_FindClosestBodyPart ( gentity_t * ent , gentity_t * other , vec3_t point , vec3_t out , int c = 0 )
{
int shortestLen = 509999 ;
char where = - 1 ;
int len ;
renderInfo_t * ri = NULL ;
ri = & ent - > client - > renderInfo ;
if ( ent - > client )
{
if ( c > 0 )
{
where = c - 1 ; // Fail safe, set to torso
}
else
{
len = VectorDistanceSquared ( point , ri - > eyePoint ) ;
if ( len < shortestLen ) {
shortestLen = len ; where = 0 ;
}
len = VectorDistanceSquared ( point , ri - > headPoint ) ;
if ( len < shortestLen ) {
shortestLen = len ; where = 1 ;
}
len = VectorDistanceSquared ( point , ri - > handRPoint ) ;
if ( len < shortestLen ) {
shortestLen = len ; where = 2 ;
}
len = VectorDistanceSquared ( point , ri - > handLPoint ) ;
if ( len < shortestLen ) {
shortestLen = len ; where = 3 ;
}
len = VectorDistanceSquared ( point , ri - > crotchPoint ) ;
if ( len < shortestLen ) {
shortestLen = len ; where = 4 ;
}
len = VectorDistanceSquared ( point , ri - > footRPoint ) ;
if ( len < shortestLen ) {
shortestLen = len ; where = 5 ;
}
len = VectorDistanceSquared ( point , ri - > footLPoint ) ;
if ( len < shortestLen ) {
shortestLen = len ; where = 6 ;
}
len = VectorDistanceSquared ( point , ri - > torsoPoint ) ;
if ( len < shortestLen ) {
shortestLen = len ; where = 7 ;
}
}
if ( where < 2 & & c = = 0 )
{
if ( random ( ) < .75f ) // 25% chance to actualy hit the head or eye
where = 7 ;
}
switch ( where )
{
case 0 :
VectorCopy ( ri - > eyePoint , out ) ;
break ;
case 1 :
VectorCopy ( ri - > headPoint , out ) ;
break ;
case 2 :
VectorCopy ( ri - > handRPoint , out ) ;
break ;
case 3 :
VectorCopy ( ri - > handLPoint , out ) ;
break ;
case 4 :
VectorCopy ( ri - > crotchPoint , out ) ;
break ;
case 5 :
VectorCopy ( ri - > footRPoint , out ) ;
break ;
case 6 :
VectorCopy ( ri - > footLPoint , out ) ;
break ;
case 7 :
VectorCopy ( ri - > torsoPoint , out ) ;
break ;
}
}
else
{
VectorCopy ( ent - > s . pos . trBase , out ) ;
// Really bad hack
if ( strcmp ( ent - > classname , " misc_turret " ) = = 0 )
{
out [ 2 ] = point [ 2 ] ;
}
}
if ( ent & & ent - > client & & ent - > client - > NPC_class = = CLASS_MINEMONSTER )
{
out [ 2 ] - = 24 ; // not a clue???
return shortestLen ; // mine critters are too small to randomize
}
if ( ent - > NPC_type & & ! Q_stricmp ( ent - > NPC_type , " atst " ) )
{
// Dont randomize those atst's they have some pretty small legs
return shortestLen ;
}
if ( c = = 0 )
{
// Add a bit of chance to the actual location
float r = random ( ) * 8.0f - 4.0f ;
float r2 = random ( ) * 8.0f - 4.0f ;
float r3 = random ( ) * 10.0f - 5.0f ;
out [ 0 ] + = r ;
out [ 1 ] + = r2 ;
out [ 2 ] + = r3 ;
}
return shortestLen ;
}
# endif // Auto-aim
//extern cvar_t *cv_autoAim;
# ifdef _XBOX // Auto-aim
static bool cv_autoAim = qtrue ;
# endif // Auto-aim
bool WP_MissileTargetHint ( gentity_t * shooter , vec3_t start , vec3_t out )
{
# ifdef _XBOX
extern short cg_crossHairStatus ;
extern int g_crosshairEntNum ;
// int allow = 0;
// allow = Cvar_VariableIntegerValue("cv_autoAim");
// if ((!cg.snap) || !allow ) return false;
if ( ( ! cg . snap ) | | ! cv_autoAim ) return false ;
if ( shooter - > s . clientNum ! = 0 ) return false ; // assuming shooter must be client, using 0 for cg_entities[0] a few lines down if you change this
// if (cg_crossHairStatus != 1 || cg_crosshairEntNum < 0 || cg_crosshairEntNum >= ENTITYNUM_WORLD) return false;
if ( cg_crossHairStatus ! = 1 | | g_crosshairEntNum < 0 | | g_crosshairEntNum > = ENTITYNUM_WORLD ) return false ;
gentity_t * traceEnt = & g_entities [ g_crosshairEntNum ] ;
vec3_t d_f , d_rt , d_up ;
vec3_t end ;
trace_t trace ;
// Calculate the end point
AngleVectors ( cg_entities [ 0 ] . lerpAngles , d_f , d_rt , d_up ) ;
VectorMA ( start , 8192 , d_f , end ) ; //4028 is max for mind trick
// This will get a detailed trace
gi . trace ( & trace , start , vec3_origin , vec3_origin , end , cg . snap - > ps . clientNum , MASK_OPAQUE | CONTENTS_SHOTCLIP | CONTENTS_BODY | CONTENTS_ITEM , G2_COLLIDE , 10 ) ;
// If the trace came up with a different entity then our crosshair, then you are not actualy over the enemy
if ( trace . entityNum ! = g_crosshairEntNum )
{
// Must trace again to find out where the crosshair will end up
gi . trace ( & trace , start , vec3_origin , vec3_origin , end , cg . snap - > ps . clientNum , MASK_OPAQUE | CONTENTS_SHOTCLIP | CONTENTS_BODY | CONTENTS_ITEM , G2_NOCOLLIDE , 10 ) ;
// Find the closest body part to the trace
WP_FindClosestBodyPart ( traceEnt , shooter , trace . endpos , out ) ;
// Compute the direction vector between the shooter and the guy being shot
VectorSubtract ( out , start , out ) ;
VectorNormalize ( out ) ;
for ( int i = 1 ; i < 8 ; i + + ) /// do this 7 times to make sure we get it
{
/// Where will this direction end up?
VectorMA ( start , 8192 , out , end ) ; //4028 is max for mind trick
// Try it one more time, ??? are we trying to shoot through solid space??
gi . trace ( & trace , start , vec3_origin , vec3_origin , end , cg . snap - > ps . clientNum , MASK_OPAQUE | CONTENTS_SHOTCLIP | CONTENTS_BODY | CONTENTS_ITEM , G2_COLLIDE , 10 ) ;
if ( trace . entityNum ! = g_crosshairEntNum )
{
// Find the closest body part to the trace
WP_FindClosestBodyPart ( traceEnt , shooter , trace . endpos , out , i ) ;
// Computer the direction vector between the shooter and the guy being shot
VectorSubtract ( out , start , out ) ;
VectorNormalize ( out ) ;
}
else
{
break ; /// a hit wahoo
}
}
}
return true ;
# else // Auto-aim
return false ;
# endif
}
/*
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
PLAYER WEAPONS
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
//---------------
// Bryar Pistol
//---------------
//---------------------------------------------------------
static void WP_FireBryarPistol ( gentity_t * ent , qboolean alt_fire )
//---------------------------------------------------------
{
vec3_t start ;
int damage = BRYAR_PISTOL_DAMAGE ;
VectorCopy ( muzzle , start ) ;
WP_TraceSetStart ( ent , start , vec3_origin , vec3_origin ) ; //make sure our start point isn't on the other side of a wall
if ( ! ( ent - > client - > ps . forcePowersActive & ( 1 < < FP_SEE ) )
| | ent - > client - > ps . forcePowerLevel [ FP_SEE ] < FORCE_LEVEL_2 )
{ //force sight 2+ gives perfect aim
//FIXME: maybe force sight level 3 autoaims some?
if ( ent - > NPC & & ent - > NPC - > currentAim < 5 )
{
vec3_t angs ;
vectoangles ( forward , angs ) ;
if ( ent - > client - > NPC_class = = CLASS_IMPWORKER )
{ //*sigh*, hack to make impworkers less accurate without affecteing imperial officer accuracy
angs [ PITCH ] + = ( crandom ( ) * ( BLASTER_NPC_SPREAD + ( 6 - ent - > NPC - > currentAim ) * 0.25f ) ) ; //was 0.5f
angs [ YAW ] + = ( crandom ( ) * ( BLASTER_NPC_SPREAD + ( 6 - ent - > NPC - > currentAim ) * 0.25f ) ) ; //was 0.5f
}
else
{
angs [ PITCH ] + = ( crandom ( ) * ( ( 5 - ent - > NPC - > currentAim ) * 0.25f ) ) ;
angs [ YAW ] + = ( crandom ( ) * ( ( 5 - ent - > NPC - > currentAim ) * 0.25f ) ) ;
}
AngleVectors ( angs , forward , NULL , NULL ) ;
}
}
WP_MissileTargetHint ( ent , start , forward ) ;
gentity_t * missile = CreateMissile ( start , forward , BRYAR_PISTOL_VEL , 10000 , ent , alt_fire ) ;
missile - > classname = " bryar_proj " ;
if ( ent - > s . weapon = = WP_BLASTER_PISTOL
| | ent - > s . weapon = = WP_JAWA )
{ //*SIGH*... I hate our weapon system...
missile - > s . weapon = ent - > s . weapon ;
}
else
{
missile - > s . weapon = WP_BRYAR_PISTOL ;
}
if ( alt_fire )
{
int count = ( level . time - ent - > client - > ps . weaponChargeTime ) / BRYAR_CHARGE_UNIT ;
if ( count < 1 )
{
count = 1 ;
}
else if ( count > 5 )
{
count = 5 ;
}
damage * = count ;
missile - > count = count ; // this will get used in the projectile rendering code to make a beefier effect
}
// if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time )
// {
// // in overcharge mode, so doing double damage
// missile->flags |= FL_OVERCHARGED;
// damage *= 2;
// }
missile - > damage = damage ;
missile - > dflags = DAMAGE_DEATH_KNOCKBACK ;
if ( alt_fire )
{
missile - > methodOfDeath = MOD_BRYAR_ALT ;
}
else
{
missile - > methodOfDeath = MOD_BRYAR ;
}
missile - > clipmask = MASK_SHOT | CONTENTS_LIGHTSABER ;
// we don't want it to bounce forever
missile - > bounceCount = 8 ;
if ( ent - > weaponModel [ 1 ] > 0 )
{ //dual pistols, toggle the muzzle point back and forth between the two pistols each time he fires
ent - > count = ( ent - > count ) ? 0 : 1 ;
}
}
//---------------
// Blaster
//---------------
//---------------------------------------------------------
static void WP_FireBlasterMissile ( gentity_t * ent , vec3_t start , vec3_t dir , qboolean altFire )
//---------------------------------------------------------
{
int velocity = BLASTER_VELOCITY ;
int damage = BLASTER_DAMAGE ;
if ( ent & & ent - > client & & ent - > client - > NPC_class = = CLASS_VEHICLE )
{
damage * = 3 ;
velocity = ATST_MAIN_VEL + ent - > client - > ps . speed ;
}
else
{
// If an enemy is shooting at us, lower the velocity so you have a chance to evade
if ( ent - > client & & ent - > client - > ps . clientNum ! = 0 & & ent - > client - > NPC_class ! = CLASS_BOBAFETT )
{
if ( g_spskill - > integer < 2 )
{
velocity * = BLASTER_NPC_VEL_CUT ;
}
else
{
velocity * = BLASTER_NPC_HARD_VEL_CUT ;
}
}
}
WP_TraceSetStart ( ent , start , vec3_origin , vec3_origin ) ; //make sure our start point isn't on the other side of a wall
WP_MissileTargetHint ( ent , start , dir ) ;
gentity_t * missile = CreateMissile ( start , dir , velocity , 10000 , ent , altFire ) ;
missile - > classname = " blaster_proj " ;
missile - > s . weapon = WP_BLASTER ;
// Do the damages
if ( ent - > s . number ! = 0 & & ent - > client - > NPC_class ! = CLASS_BOBAFETT )
{
if ( g_spskill - > integer = = 0 )
{
damage = BLASTER_NPC_DAMAGE_EASY ;
}
else if ( g_spskill - > integer = = 1 )
{
damage = BLASTER_NPC_DAMAGE_NORMAL ;
}
else
{
damage = BLASTER_NPC_DAMAGE_HARD ;
}
}
// if ( ent->client )
// {
// if ( ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time )
// {
// // in overcharge mode, so doing double damage
// missile->flags |= FL_OVERCHARGED;
// damage *= 2;
// }
// }
missile - > damage = damage ;
missile - > dflags = DAMAGE_DEATH_KNOCKBACK ;
if ( altFire )
{
missile - > methodOfDeath = MOD_BLASTER_ALT ;
}
else
{
missile - > methodOfDeath = MOD_BLASTER ;
}
missile - > clipmask = MASK_SHOT | CONTENTS_LIGHTSABER ;
// we don't want it to bounce forever
missile - > bounceCount = 8 ;
}
//---------------------------------------------------------
void WP_FireTurboLaserMissile ( gentity_t * ent , vec3_t start , vec3_t dir )
//---------------------------------------------------------
{
int velocity = ent - > mass ; //FIXME: externalize
gentity_t * missile ;
missile = CreateMissile ( start , dir , velocity , 10000 , ent , qfalse ) ;
//use a custom shot effect
//missile->s.otherEntityNum2 = G_EffectIndex( "turret/turb_shot" );
//use a custom impact effect
//missile->s.emplacedOwner = G_EffectIndex( "turret/turb_impact" );
missile - > classname = " turbo_proj " ;
missile - > s . weapon = WP_TIE_FIGHTER ;
missile - > damage = ent - > damage ; //FIXME: externalize
missile - > splashDamage = ent - > splashDamage ; //FIXME: externalize
missile - > splashRadius = ent - > splashRadius ; //FIXME: externalize
missile - > dflags = DAMAGE_DEATH_KNOCKBACK ;
missile - > methodOfDeath = MOD_EMPLACED ; //MOD_TURBLAST; //count as a heavy weap
missile - > splashMethodOfDeath = MOD_EMPLACED ; //MOD_TURBLAST;// ?SPLASH;
missile - > clipmask = MASK_SHOT ;
// we don't want it to bounce forever
missile - > bounceCount = 8 ;
//set veh as cgame side owner for purpose of fx overrides
//missile->s.owner = ent->s.number;
//don't let them last forever
missile - > e_ThinkFunc = thinkF_G_FreeEntity ;
missile - > nextthink = level . time + 10000 ; //at 20000 speed, that should be more than enough
}
//---------------------------------------------------------
static void WP_FireBlaster ( gentity_t * ent , qboolean alt_fire )
//---------------------------------------------------------
{
vec3_t dir , angs ;
vectoangles ( forward , angs ) ;
if ( ent - > client & & ent - > client - > NPC_class = = CLASS_VEHICLE )
{ //no inherent aim screw up
}
else if ( ! ( ent - > client - > ps . forcePowersActive & ( 1 < < FP_SEE ) )
| | ent - > client - > ps . forcePowerLevel [ FP_SEE ] < FORCE_LEVEL_2 )
{ //force sight 2+ gives perfect aim
//FIXME: maybe force sight level 3 autoaims some?
if ( alt_fire )
{
// add some slop to the alt-fire direction
angs [ PITCH ] + = crandom ( ) * BLASTER_ALT_SPREAD ;
angs [ YAW ] + = crandom ( ) * BLASTER_ALT_SPREAD ;
}
else
{
// Troopers use their aim values as well as the gun's inherent inaccuracy
// so check for all classes of stormtroopers and anyone else that has aim error
if ( ent - > client & & ent - > NPC & &
( ent - > client - > NPC_class = = CLASS_STORMTROOPER | |
ent - > client - > NPC_class = = CLASS_SWAMPTROOPER ) )
{
angs [ PITCH ] + = ( crandom ( ) * ( BLASTER_NPC_SPREAD + ( 6 - ent - > NPC - > currentAim ) * 0.25f ) ) ; //was 0.5f
angs [ YAW ] + = ( crandom ( ) * ( BLASTER_NPC_SPREAD + ( 6 - ent - > NPC - > currentAim ) * 0.25f ) ) ; //was 0.5f
}
else
{
// add some slop to the main-fire direction
angs [ PITCH ] + = crandom ( ) * BLASTER_MAIN_SPREAD ;
angs [ YAW ] + = crandom ( ) * BLASTER_MAIN_SPREAD ;
}
}
}
AngleVectors ( angs , dir , NULL , NULL ) ;
// FIXME: if temp_org does not have clear trace to inside the bbox, don't shoot!
WP_FireBlasterMissile ( ent , muzzle , dir , alt_fire ) ;
}
//---------------------
// Tenloss Disruptor
//---------------------
int G_GetHitLocFromTrace ( trace_t * trace , int mod )
{
int hitLoc = HL_NONE ;
for ( int i = 0 ; i < MAX_G2_COLLISIONS ; i + + )
{
if ( trace - > G2CollisionMap [ i ] . mEntityNum = = - 1 )
{
break ;
}
CCollisionRecord & coll = trace - > G2CollisionMap [ i ] ;
if ( ( coll . mFlags & G2_FRONTFACE ) )
{
G_GetHitLocFromSurfName ( & g_entities [ coll . mEntityNum ] , gi . G2API_GetSurfaceName ( & g_entities [ coll . mEntityNum ] . ghoul2 [ coll . mModelIndex ] , coll . mSurfaceIndex ) , & hitLoc , coll . mCollisionPosition , NULL , NULL , mod ) ;
//we only want the first "entrance wound", so break
break ;
}
}
return hitLoc ;
}
//---------------------------------------------------------
static void WP_DisruptorMainFire ( gentity_t * ent )
//---------------------------------------------------------
{
int damage = DISRUPTOR_MAIN_DAMAGE ;
qboolean render_impact = qtrue ;
vec3_t start , end , spot ;
trace_t tr ;
gentity_t * traceEnt = NULL , * tent ;
float dist , shotDist , shotRange = 8192 ;
if ( ent - > NPC )
{
switch ( g_spskill - > integer )
{
case 0 :
damage = DISRUPTOR_NPC_MAIN_DAMAGE_EASY ;
break ;
case 1 :
damage = DISRUPTOR_NPC_MAIN_DAMAGE_MEDIUM ;
break ;
case 2 :
default :
damage = DISRUPTOR_NPC_MAIN_DAMAGE_HARD ;
break ;
}
}
VectorCopy ( muzzle , start ) ;
WP_TraceSetStart ( ent , start , vec3_origin , vec3_origin ) ;
// if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time )
// {
// // in overcharge mode, so doing double damage
// damage *= 2;
// }
WP_MissileTargetHint ( ent , start , forward ) ;
VectorMA ( start , shotRange , forward , end ) ;
int ignore = ent - > s . number ;
int traces = 0 ;
while ( traces < 10 )
{ //need to loop this in case we hit a Jedi who dodges the shot
gi . trace ( & tr , start , NULL , NULL , end , ignore , MASK_SHOT , G2_RETURNONHIT , 0 ) ;
traceEnt = & g_entities [ tr . entityNum ] ;
if ( traceEnt
& & ( traceEnt - > s . weapon = = WP_SABER | | ( traceEnt - > client & & ( traceEnt - > client - > NPC_class = = CLASS_BOBAFETT | | traceEnt - > client - > NPC_class = = CLASS_REBORN ) ) ) )
{ //FIXME: need a more reliable way to know we hit a jedi?
if ( Jedi_DodgeEvasion ( traceEnt , ent , & tr , HL_NONE ) )
{ //act like we didn't even hit him
VectorCopy ( tr . endpos , start ) ;
ignore = tr . entityNum ;
traces + + ;
continue ;
}
}
//a Jedi is not dodging this shot
break ;
}
if ( tr . surfaceFlags & SURF_NOIMPACT )
{
render_impact = qfalse ;
}
// always render a shot beam, doing this the old way because I don't much feel like overriding the effect.
tent = G_TempEntity ( tr . endpos , EV_DISRUPTOR_MAIN_SHOT ) ;
tent - > svFlags | = SVF_BROADCAST ;
VectorCopy ( muzzle , tent - > s . origin2 ) ;
if ( render_impact )
{
if ( tr . entityNum < ENTITYNUM_WORLD & & traceEnt - > takedamage )
{
// Create a simple impact type mark that doesn't last long in the world
G_PlayEffect ( G_EffectIndex ( " disruptor/flesh_impact " ) , tr . endpos , tr . plane . normal ) ;
if ( traceEnt - > client & & LogAccuracyHit ( traceEnt , ent ) )
{
ent - > client - > ps . persistant [ PERS_ACCURACY_HITS ] + + ;
}
int hitLoc = G_GetHitLocFromTrace ( & tr , MOD_DISRUPTOR ) ;
if ( traceEnt & & traceEnt - > client & & traceEnt - > client - > NPC_class = = CLASS_GALAKMECH )
{ //hehe
G_Damage ( traceEnt , ent , ent , forward , tr . endpos , 3 , DAMAGE_DEATH_KNOCKBACK , MOD_DISRUPTOR , hitLoc ) ;
}
else
{
G_Damage ( traceEnt , ent , ent , forward , tr . endpos , damage , DAMAGE_DEATH_KNOCKBACK , MOD_DISRUPTOR , hitLoc ) ;
}
}
else
{
G_PlayEffect ( G_EffectIndex ( " disruptor/wall_impact " ) , tr . endpos , tr . plane . normal ) ;
}
}
shotDist = shotRange * tr . fraction ;
for ( dist = 0 ; dist < shotDist ; dist + = 64 )
{
//FIXME: on a really long shot, this could make a LOT of alerts in one frame...
VectorMA ( start , dist , forward , spot ) ;
AddSightEvent ( ent , spot , 256 , AEL_DISCOVERED , 50 ) ;
}
VectorMA ( start , shotDist - 4 , forward , spot ) ;
AddSightEvent ( ent , spot , 256 , AEL_DISCOVERED , 50 ) ;
}
//---------------------------------------------------------
void WP_DisruptorAltFire ( gentity_t * ent )
//---------------------------------------------------------
{
int damage = DISRUPTOR_ALT_DAMAGE , skip , traces = DISRUPTOR_ALT_TRACES ;
qboolean render_impact = qtrue ;
vec3_t start , end ;
vec3_t muzzle2 , spot , dir ;
trace_t tr ;
gentity_t * traceEnt , * tent ;
float dist , shotDist , shotRange = 8192 ;
qboolean hitDodged = qfalse , fullCharge = qfalse ;
VectorCopy ( muzzle , muzzle2 ) ; // making a backup copy
// The trace start will originate at the eye so we can ensure that it hits the crosshair.
if ( ent - > NPC )
{
switch ( g_spskill - > integer )
{
case 0 :
damage = DISRUPTOR_NPC_ALT_DAMAGE_EASY ;
break ;
case 1 :
damage = DISRUPTOR_NPC_ALT_DAMAGE_MEDIUM ;
break ;
case 2 :
default :
damage = DISRUPTOR_NPC_ALT_DAMAGE_HARD ;
break ;
}
VectorCopy ( muzzle , start ) ;
fullCharge = qtrue ;
}
else
{
VectorCopy ( ent - > client - > renderInfo . eyePoint , start ) ;
AngleVectors ( ent - > client - > renderInfo . eyeAngles , forward , NULL , NULL ) ;
// don't let NPC's do charging
int count = ( level . time - ent - > client - > ps . weaponChargeTime - 50 ) / DISRUPTOR_CHARGE_UNIT ;
if ( count < 1 )
{
count = 1 ;
}
else if ( count > = 10 )
{
count = 10 ;
fullCharge = qtrue ;
}
// more powerful charges go through more things
if ( count < 3 )
{
traces = 1 ;
}
else if ( count < 6 )
{
traces = 2 ;
}
//else do full traces
damage = damage * count + DISRUPTOR_MAIN_DAMAGE * 0.5f ; // give a boost to low charge shots
}
skip = ent - > s . number ;
// if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time )
// {
// // in overcharge mode, so doing double damage
// damage *= 2;
// }
for ( int i = 0 ; i < traces ; i + + )
{
VectorMA ( start , shotRange , forward , end ) ;
//NOTE: if you want to be able to hit guys in emplaced guns, use "G2_COLLIDE, 10" instead of "G2_RETURNONHIT, 0"
//alternately, if you end up hitting an emplaced_gun that has a sitter, just redo this one trace with the "G2_COLLIDE, 10" to see if we it the sitter
gi . trace ( & tr , start , NULL , NULL , end , skip , MASK_SHOT , G2_COLLIDE , 10 ) ; //G2_RETURNONHIT, 0 );
if ( tr . surfaceFlags & SURF_NOIMPACT )
{
render_impact = qfalse ;
}
if ( tr . entityNum = = ent - > s . number )
{
// should never happen, but basically we don't want to consider a hit to ourselves?
// Get ready for an attempt to trace through another person
VectorCopy ( tr . endpos , muzzle2 ) ;
VectorCopy ( tr . endpos , start ) ;
skip = tr . entityNum ;
# ifdef _DEBUG
gi . Printf ( " BAD! Disruptor gun shot somehow traced back and hit the owner! \n " ) ;
# endif
continue ;
}
// always render a shot beam, doing this the old way because I don't much feel like overriding the effect.
//NOTE: let's just draw one beam, at the end
//tent = G_TempEntity( tr.endpos, EV_DISRUPTOR_SNIPER_SHOT );
//tent->svFlags |= SVF_BROADCAST;
//tent->alt_fire = fullCharge; // mark us so we can alter the effect
//VectorCopy( muzzle2, tent->s.origin2 );
if ( tr . fraction > = 1.0f )
{
// draw the beam but don't do anything else
break ;
}
traceEnt = & g_entities [ tr . entityNum ] ;
if ( traceEnt //&& traceEnt->NPC
& & ( traceEnt - > s . weapon = = WP_SABER | | ( traceEnt - > client & & ( traceEnt - > client - > NPC_class = = CLASS_BOBAFETT | | traceEnt - > client - > NPC_class = = CLASS_REBORN ) ) ) )
{ //FIXME: need a more reliable way to know we hit a jedi?
hitDodged = Jedi_DodgeEvasion ( traceEnt , ent , & tr , HL_NONE ) ;
//acts like we didn't even hit him
}
if ( ! hitDodged )
{
if ( render_impact )
{
if ( ( tr . entityNum < ENTITYNUM_WORLD & & traceEnt - > takedamage )
| | ! Q_stricmp ( traceEnt - > classname , " misc_model_breakable " )
| | traceEnt - > s . eType = = ET_MOVER )
{
// Create a simple impact type mark that doesn't last long in the world
G_PlayEffect ( G_EffectIndex ( " disruptor/alt_hit " ) , tr . endpos , tr . plane . normal ) ;
if ( traceEnt - > client & & LogAccuracyHit ( traceEnt , ent ) )
{ //NOTE: hitting multiple ents can still get you over 100% accuracy
ent - > client - > ps . persistant [ PERS_ACCURACY_HITS ] + + ;
}
int hitLoc = G_GetHitLocFromTrace ( & tr , MOD_DISRUPTOR ) ;
if ( traceEnt & & traceEnt - > client & & traceEnt - > client - > NPC_class = = CLASS_GALAKMECH )
{ //hehe
G_Damage ( traceEnt , ent , ent , forward , tr . endpos , 10 , DAMAGE_NO_KNOCKBACK | DAMAGE_NO_HIT_LOC , fullCharge ? MOD_SNIPER : MOD_DISRUPTOR , hitLoc ) ;
break ;
}
G_Damage ( traceEnt , ent , ent , forward , tr . endpos , damage , DAMAGE_NO_KNOCKBACK | DAMAGE_NO_HIT_LOC , fullCharge ? MOD_SNIPER : MOD_DISRUPTOR , hitLoc ) ;
if ( traceEnt - > s . eType = = ET_MOVER )
{ //stop the traces on any mover
break ;
}
}
else
{
// we only make this mark on things that can't break or move
tent = G_TempEntity ( tr . endpos , EV_DISRUPTOR_SNIPER_MISS ) ;
tent - > svFlags | = SVF_BROADCAST ;
VectorCopy ( tr . plane . normal , tent - > pos1 ) ;
break ; // hit solid, but doesn't take damage, so stop the shot...we _could_ allow it to shoot through walls, might be cool?
}
}
else // not rendering impact, must be a skybox or other similar thing?
{
break ; // don't try anymore traces
}
}
// Get ready for an attempt to trace through another person
VectorCopy ( tr . endpos , muzzle2 ) ;
VectorCopy ( tr . endpos , start ) ;
skip = tr . entityNum ;
hitDodged = qfalse ;
}
//just draw one solid beam all the way to the end...
tent = G_TempEntity ( tr . endpos , EV_DISRUPTOR_SNIPER_SHOT ) ;
tent - > svFlags | = SVF_BROADCAST ;
tent - > alt_fire = fullCharge ; // mark us so we can alter the effect
VectorCopy ( muzzle , tent - > s . origin2 ) ;
// now go along the trail and make sight events
VectorSubtract ( tr . endpos , muzzle , dir ) ;
shotDist = VectorNormalize ( dir ) ;
//FIXME: if shoot *really* close to someone, the alert could be way out of their FOV
for ( dist = 0 ; dist < shotDist ; dist + = 64 )
{
//FIXME: on a really long shot, this could make a LOT of alerts in one frame...
VectorMA ( muzzle , dist , dir , spot ) ;
AddSightEvent ( ent , spot , 256 , AEL_DISCOVERED , 50 ) ;
}
//FIXME: spawn a temp ent that continuously spawns sight alerts here? And 1 sound alert to draw their attention?
VectorMA ( start , shotDist - 4 , forward , spot ) ;
AddSightEvent ( ent , spot , 256 , AEL_DISCOVERED , 50 ) ;
}
//---------------------------------------------------------
static void WP_FireDisruptor ( gentity_t * ent , qboolean alt_fire )
//---------------------------------------------------------
{
if ( alt_fire )
{
WP_DisruptorAltFire ( ent ) ;
}
else
{
WP_DisruptorMainFire ( ent ) ;
}
G_PlayEffect ( G_EffectIndex ( " disruptor/line_cap " ) , muzzle , forward ) ;
}
//-------------------
// Wookiee Bowcaster
//-------------------
//---------------------------------------------------------
static void WP_BowcasterMainFire ( gentity_t * ent )
//---------------------------------------------------------
{
int damage = BOWCASTER_DAMAGE , count ;
float vel ;
vec3_t angs , dir , start ;
gentity_t * missile ;
VectorCopy ( muzzle , start ) ;
WP_TraceSetStart ( ent , start , vec3_origin , vec3_origin ) ; //make sure our start point isn't on the other side of a wall
// Do the damages
if ( ent - > s . number ! = 0 )
{
if ( g_spskill - > integer = = 0 )
{
damage = BOWCASTER_NPC_DAMAGE_EASY ;
}
else if ( g_spskill - > integer = = 1 )
{
damage = BOWCASTER_NPC_DAMAGE_NORMAL ;
}
else
{
damage = BOWCASTER_NPC_DAMAGE_HARD ;
}
}
count = ( level . time - ent - > client - > ps . weaponChargeTime ) / BOWCASTER_CHARGE_UNIT ;
if ( count < 1 )
{
count = 1 ;
}
else if ( count > 5 )
{
count = 5 ;
}
if ( ! ( count & 1 ) )
{
// if we aren't odd, knock us down a level
count - - ;
}
// if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time )
// {
// // in overcharge mode, so doing double damage
// damage *= 2;
// }
WP_MissileTargetHint ( ent , start , forward ) ;
for ( int i = 0 ; i < count ; i + + )
{
// create a range of different velocities
vel = BOWCASTER_VELOCITY * ( crandom ( ) * BOWCASTER_VEL_RANGE + 1.0f ) ;
vectoangles ( forward , angs ) ;
if ( ! ( ent - > client - > ps . forcePowersActive & ( 1 < < FP_SEE ) )
| | ent - > client - > ps . forcePowerLevel [ FP_SEE ] < FORCE_LEVEL_2 )
{ //force sight 2+ gives perfect aim
//FIXME: maybe force sight level 3 autoaims some?
// add some slop to the fire direction
angs [ PITCH ] + = crandom ( ) * BOWCASTER_ALT_SPREAD * 0.2f ;
angs [ YAW ] + = ( ( i + 0.5f ) * BOWCASTER_ALT_SPREAD - count * 0.5f * BOWCASTER_ALT_SPREAD ) ;
if ( ent - > NPC )
{
angs [ PITCH ] + = ( crandom ( ) * ( BLASTER_NPC_SPREAD + ( 6 - ent - > NPC - > currentAim ) * 0.25f ) ) ;
angs [ YAW ] + = ( crandom ( ) * ( BLASTER_NPC_SPREAD + ( 6 - ent - > NPC - > currentAim ) * 0.25f ) ) ;
}
}
AngleVectors ( angs , dir , NULL , NULL ) ;
missile = CreateMissile ( start , dir , vel , 10000 , ent ) ;
missile - > classname = " bowcaster_proj " ;
missile - > s . weapon = WP_BOWCASTER ;
VectorSet ( missile - > maxs , BOWCASTER_SIZE , BOWCASTER_SIZE , BOWCASTER_SIZE ) ;
VectorScale ( missile - > maxs , - 1 , missile - > mins ) ;
// if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time )
// {
// missile->flags |= FL_OVERCHARGED;
// }
missile - > damage = damage ;
missile - > dflags = DAMAGE_DEATH_KNOCKBACK ;
missile - > methodOfDeath = MOD_BOWCASTER ;
missile - > clipmask = MASK_SHOT | CONTENTS_LIGHTSABER ;
missile - > splashDamage = BOWCASTER_SPLASH_DAMAGE ;
missile - > splashRadius = BOWCASTER_SPLASH_RADIUS ;
// we don't want it to bounce
missile - > bounceCount = 0 ;
ent - > client - > sess . missionStats . shotsFired + + ;
}
}
//---------------------------------------------------------
static void WP_BowcasterAltFire ( gentity_t * ent )
//---------------------------------------------------------
{
vec3_t start ;
int damage = BOWCASTER_DAMAGE ;
VectorCopy ( muzzle , start ) ;
WP_TraceSetStart ( ent , start , vec3_origin , vec3_origin ) ; //make sure our start point isn't on the other side of a wall
WP_MissileTargetHint ( ent , start , forward ) ;
gentity_t * missile = CreateMissile ( start , forward , BOWCASTER_VELOCITY , 10000 , ent , qtrue ) ;
missile - > classname = " bowcaster_alt_proj " ;
missile - > s . weapon = WP_BOWCASTER ;
// Do the damages
if ( ent - > s . number ! = 0 )
{
if ( g_spskill - > integer = = 0 )
{
damage = BOWCASTER_NPC_DAMAGE_EASY ;
}
else if ( g_spskill - > integer = = 1 )
{
damage = BOWCASTER_NPC_DAMAGE_NORMAL ;
}
else
{
damage = BOWCASTER_NPC_DAMAGE_HARD ;
}
}
VectorSet ( missile - > maxs , BOWCASTER_SIZE , BOWCASTER_SIZE , BOWCASTER_SIZE ) ;
VectorScale ( missile - > maxs , - 1 , missile - > mins ) ;
// if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time )
// {
// // in overcharge mode, so doing double damage
// missile->flags |= FL_OVERCHARGED;
// damage *= 2;
// }
missile - > s . eFlags | = EF_BOUNCE ;
missile - > bounceCount = 3 ;
missile - > damage = damage ;
missile - > dflags = DAMAGE_DEATH_KNOCKBACK ;
missile - > methodOfDeath = MOD_BOWCASTER_ALT ;
missile - > clipmask = MASK_SHOT | CONTENTS_LIGHTSABER ;
missile - > splashDamage = BOWCASTER_SPLASH_DAMAGE ;
missile - > splashRadius = BOWCASTER_SPLASH_RADIUS ;
}
//---------------------------------------------------------
static void WP_FireBowcaster ( gentity_t * ent , qboolean alt_fire )
//---------------------------------------------------------
{
if ( alt_fire )
{
WP_BowcasterAltFire ( ent ) ;
}
else
{
WP_BowcasterMainFire ( ent ) ;
}
}
//-------------------
// Heavy Repeater
//-------------------
//---------------------------------------------------------
static void WP_RepeaterMainFire ( gentity_t * ent , vec3_t dir )
//---------------------------------------------------------
{
vec3_t start ;
int damage = REPEATER_DAMAGE ;
VectorCopy ( muzzle , start ) ;
WP_TraceSetStart ( ent , start , vec3_origin , vec3_origin ) ; //make sure our start point isn't on the other side of a wall
WP_MissileTargetHint ( ent , start , dir ) ;
gentity_t * missile = CreateMissile ( start , dir , REPEATER_VELOCITY , 10000 , ent ) ;
missile - > classname = " repeater_proj " ;
missile - > s . weapon = WP_REPEATER ;
// Do the damages
if ( ent - > s . number ! = 0 )
{
if ( g_spskill - > integer = = 0 )
{
damage = REPEATER_NPC_DAMAGE_EASY ;
}
else if ( g_spskill - > integer = = 1 )
{
damage = REPEATER_NPC_DAMAGE_NORMAL ;
}
else
{
damage = REPEATER_NPC_DAMAGE_HARD ;
}
}
// if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time )
// {
// // in overcharge mode, so doing double damage
// missile->flags |= FL_OVERCHARGED;
// damage *= 2;
// }
missile - > damage = damage ;
missile - > dflags = DAMAGE_DEATH_KNOCKBACK ;
missile - > methodOfDeath = MOD_REPEATER ;
missile - > clipmask = MASK_SHOT | CONTENTS_LIGHTSABER ;
// we don't want it to bounce forever
missile - > bounceCount = 8 ;
}
//---------------------------------------------------------
static void WP_RepeaterAltFire ( gentity_t * ent )
//---------------------------------------------------------
{
vec3_t start ;
int damage = REPEATER_ALT_DAMAGE ;
gentity_t * missile = NULL ;
VectorCopy ( muzzle , start ) ;
WP_TraceSetStart ( ent , start , vec3_origin , vec3_origin ) ; //make sure our start point isn't on the other side of a wall
if ( ent - > client & & ent - > client - > NPC_class = = CLASS_GALAKMECH )
{
missile = CreateMissile ( start , ent - > client - > hiddenDir , ent - > client - > hiddenDist , 10000 , ent , qtrue ) ;
}
else
{
WP_MissileTargetHint ( ent , start , forward ) ;
missile = CreateMissile ( start , forward , REPEATER_ALT_VELOCITY , 10000 , ent , qtrue ) ;
}
missile - > classname = " repeater_alt_proj " ;
missile - > s . weapon = WP_REPEATER ;
missile - > mass = 10 ;
// Do the damages
if ( ent - > s . number ! = 0 )
{
if ( g_spskill - > integer = = 0 )
{
damage = REPEATER_ALT_NPC_DAMAGE_EASY ;
}
else if ( g_spskill - > integer = = 1 )
{
damage = REPEATER_ALT_NPC_DAMAGE_NORMAL ;
}
else
{
damage = REPEATER_ALT_NPC_DAMAGE_HARD ;
}
}
VectorSet ( missile - > maxs , REPEATER_ALT_SIZE , REPEATER_ALT_SIZE , REPEATER_ALT_SIZE ) ;
VectorScale ( missile - > maxs , - 1 , missile - > mins ) ;
missile - > s . pos . trType = TR_GRAVITY ;
missile - > s . pos . trDelta [ 2 ] + = 40.0f ; //give a slight boost in the upward direction
// if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time )
// {
// // in overcharge mode, so doing double damage
// missile->flags |= FL_OVERCHARGED;
// damage *= 2;
// }
missile - > damage = damage ;
missile - > dflags = DAMAGE_DEATH_KNOCKBACK ;
missile - > methodOfDeath = MOD_REPEATER_ALT ;
missile - > splashMethodOfDeath = MOD_REPEATER_ALT ;
missile - > clipmask = MASK_SHOT | CONTENTS_LIGHTSABER ;
missile - > splashDamage = REPEATER_ALT_SPLASH_DAMAGE ;
missile - > splashRadius = REPEATER_ALT_SPLASH_RADIUS ;
// we don't want it to bounce forever
missile - > bounceCount = 8 ;
}
//---------------------------------------------------------
static void WP_FireRepeater ( gentity_t * ent , qboolean alt_fire )
//---------------------------------------------------------
{
vec3_t dir , angs ;
vectoangles ( forward , angs ) ;
if ( alt_fire )
{
WP_RepeaterAltFire ( ent ) ;
}
else
{
if ( ! ( ent - > client - > ps . forcePowersActive & ( 1 < < FP_SEE ) )
| | ent - > client - > ps . forcePowerLevel [ FP_SEE ] < FORCE_LEVEL_2 )
{ //force sight 2+ gives perfect aim
//FIXME: maybe force sight level 3 autoaims some?
// Troopers use their aim values as well as the gun's inherent inaccuracy
// so check for all classes of stormtroopers and anyone else that has aim error
if ( ent - > client & & ent - > NPC & &
( ent - > client - > NPC_class = = CLASS_STORMTROOPER | |
ent - > client - > NPC_class = = CLASS_SWAMPTROOPER | |
ent - > client - > NPC_class = = CLASS_SHADOWTROOPER ) )
{
angs [ PITCH ] + = ( crandom ( ) * ( REPEATER_NPC_SPREAD + ( 6 - ent - > NPC - > currentAim ) * 0.25f ) ) ;
angs [ YAW ] + = ( crandom ( ) * ( REPEATER_NPC_SPREAD + ( 6 - ent - > NPC - > currentAim ) * 0.25f ) ) ;
}
else
{
// add some slop to the alt-fire direction
angs [ PITCH ] + = crandom ( ) * REPEATER_SPREAD ;
angs [ YAW ] + = crandom ( ) * REPEATER_SPREAD ;
}
}
AngleVectors ( angs , dir , NULL , NULL ) ;
// FIXME: if temp_org does not have clear trace to inside the bbox, don't shoot!
WP_RepeaterMainFire ( ent , dir ) ;
}
}
//-------------------
// DEMP2
//-------------------
//---------------------------------------------------------
static void WP_DEMP2_MainFire ( gentity_t * ent )
//---------------------------------------------------------
{
vec3_t start ;
int damage = DEMP2_DAMAGE ;
VectorCopy ( muzzle , start ) ;
WP_TraceSetStart ( ent , start , vec3_origin , vec3_origin ) ; //make sure our start point isn't on the other side of a wall
WP_MissileTargetHint ( ent , start , forward ) ;
gentity_t * missile = CreateMissile ( start , forward , DEMP2_VELOCITY , 10000 , ent ) ;
missile - > classname = " demp2_proj " ;
missile - > s . weapon = WP_DEMP2 ;
// Do the damages
if ( ent - > s . number ! = 0 )
{
if ( g_spskill - > integer = = 0 )
{
damage = DEMP2_NPC_DAMAGE_EASY ;
}
else if ( g_spskill - > integer = = 1 )
{
damage = DEMP2_NPC_DAMAGE_NORMAL ;
}
else
{
damage = DEMP2_NPC_DAMAGE_HARD ;
}
}
VectorSet ( missile - > maxs , DEMP2_SIZE , DEMP2_SIZE , DEMP2_SIZE ) ;
VectorScale ( missile - > maxs , - 1 , missile - > mins ) ;
// if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time )
// {
// // in overcharge mode, so doing double damage
// missile->flags |= FL_OVERCHARGED;
// damage *= 2;
// }
missile - > damage = damage ;
missile - > dflags = DAMAGE_DEATH_KNOCKBACK ;
missile - > methodOfDeath = MOD_DEMP2 ;
missile - > clipmask = MASK_SHOT | CONTENTS_LIGHTSABER ;
// we don't want it to ever bounce
missile - > bounceCount = 0 ;
}
// NOTE: this is 100% for the demp2 alt-fire effect, so changes to the visual effect will affect game side demp2 code
//--------------------------------------------------
void DEMP2_AltRadiusDamage ( gentity_t * ent )
{
float frac = ( level . time - ent - > fx_time ) / 1300.0f ; // synchronize with demp2 effect
float dist , radius ;
gentity_t * gent ;
gentity_t * entityList [ MAX_GENTITIES ] ;
int numListedEntities , i , e ;
vec3_t mins , maxs ;
vec3_t v , dir ;
frac * = frac * frac ; // yes, this is completely ridiculous...but it causes the shell to grow slowly then "explode" at the end
radius = frac * 200.0f ; // 200 is max radius...the model is aprox. 100 units tall...the fx draw code mults. this by 2.
for ( i = 0 ; i < 3 ; i + + )
{
mins [ i ] = ent - > currentOrigin [ i ] - radius ;
maxs [ i ] = ent - > currentOrigin [ i ] + radius ;
}
numListedEntities = gi . EntitiesInBox ( mins , maxs , entityList , MAX_GENTITIES ) ;
for ( e = 0 ; e < numListedEntities ; e + + )
{
gent = entityList [ e ] ;
if ( ! gent - > takedamage | | ! gent - > contents )
{
continue ;
}
// find the distance from the edge of the bounding box
for ( i = 0 ; i < 3 ; i + + )
{
if ( ent - > currentOrigin [ i ] < gent - > absmin [ i ] )
{
v [ i ] = gent - > absmin [ i ] - ent - > currentOrigin [ i ] ;
}
else if ( ent - > currentOrigin [ i ] > gent - > absmax [ i ] )
{
v [ i ] = ent - > currentOrigin [ i ] - gent - > absmax [ i ] ;
}
else
{
v [ i ] = 0 ;
}
}
// shape is an ellipsoid, so cut vertical distance in half`
v [ 2 ] * = 0.5f ;
dist = VectorLength ( v ) ;
if ( dist > = radius )
{
// shockwave hasn't hit them yet
continue ;
}
if ( dist < ent - > radius )
{
// shockwave has already hit this thing...
continue ;
}
VectorCopy ( gent - > currentOrigin , v ) ;
VectorSubtract ( v , ent - > currentOrigin , dir ) ;
// push the center of mass higher than the origin so players get knocked into the air more
dir [ 2 ] + = 12 ;
G_Damage ( gent , ent , ent - > owner , dir , ent - > currentOrigin , DEMP2_ALT_DAMAGE , DAMAGE_DEATH_KNOCKBACK , ent - > splashMethodOfDeath ) ;
if ( gent - > takedamage & & gent - > client )
{
gent - > s . powerups | = ( 1 < < PW_SHOCKED ) ;
gent - > client - > ps . powerups [ PW_SHOCKED ] = level . time + 2000 ;
Saboteur_Decloak ( gent , Q_irand ( 3000 , 10000 ) ) ;
}
}
// store the last fraction so that next time around we can test against those things that fall between that last point and where the current shockwave edge is
ent - > radius = radius ;
if ( frac < 1.0f )
{
// shock is still happening so continue letting it expand
ent - > nextthink = level . time + 50 ;
}
}
//---------------------------------------------------------
void DEMP2_AltDetonate ( gentity_t * ent )
//---------------------------------------------------------
{
G_SetOrigin ( ent , ent - > currentOrigin ) ;
// start the effects, unfortunately, I wanted to do some custom things that I couldn't easily do with the fx system, so part of it uses an event and localEntities
G_PlayEffect ( " demp2/altDetonate " , ent - > currentOrigin , ent - > pos1 ) ;
G_AddEvent ( ent , EV_DEMP2_ALT_IMPACT , ent - > count * 2 ) ;
ent - > fx_time = level . time ;
ent - > radius = 0 ;
ent - > nextthink = level . time + 50 ;
ent - > e_ThinkFunc = thinkF_DEMP2_AltRadiusDamage ;
ent - > s . eType = ET_GENERAL ; // make us a missile no longer
}
//---------------------------------------------------------
static void WP_DEMP2_AltFire ( gentity_t * ent )
//---------------------------------------------------------
{
int damage = DEMP2_ALT_DAMAGE ;
int count ;
vec3_t start ;
trace_t tr ;
VectorCopy ( muzzle , start ) ;
WP_TraceSetStart ( ent , start , vec3_origin , vec3_origin ) ; //make sure our start point isn't on the other side of a wall
count = ( level . time - ent - > client - > ps . weaponChargeTime ) / DEMP2_CHARGE_UNIT ;
if ( count < 1 )
{
count = 1 ;
}
else if ( count > 3 )
{
count = 3 ;
}
damage * = ( 1 + ( count * ( count - 1 ) ) ) ; // yields damage of 12,36,84...gives a higher bonus for longer charge
// the shot can travel a whopping 4096 units in 1 second. Note that the shot will auto-detonate at 4096 units...we'll see if this looks cool or not
WP_MissileTargetHint ( ent , start , forward ) ;
gentity_t * missile = CreateMissile ( start , forward , DEMP2_ALT_RANGE , 1000 , ent , qtrue ) ;
// letting it know what the charge size is.
missile - > count = count ;
// missile->speed = missile->nextthink;
VectorCopy ( tr . plane . normal , missile - > pos1 ) ;
missile - > classname = " demp2_alt_proj " ;
missile - > s . weapon = WP_DEMP2 ;
missile - > e_ThinkFunc = thinkF_DEMP2_AltDetonate ;
missile - > splashDamage = missile - > damage = damage ;
missile - > splashMethodOfDeath = missile - > methodOfDeath = MOD_DEMP2_ALT ;
missile - > splashRadius = DEMP2_ALT_SPLASHRADIUS ;
missile - > dflags = DAMAGE_DEATH_KNOCKBACK ;
missile - > clipmask = MASK_SHOT | CONTENTS_LIGHTSABER ;
// we don't want it to ever bounce
missile - > bounceCount = 0 ;
}
//---------------------------------------------------------
static void WP_FireDEMP2 ( gentity_t * ent , qboolean alt_fire )
//---------------------------------------------------------
{
if ( alt_fire )
{
WP_DEMP2_AltFire ( ent ) ;
}
else
{
WP_DEMP2_MainFire ( ent ) ;
}
}
//-----------------------
// Golan Arms Flechette
//-----------------------
//---------------------------------------------------------
static void WP_FlechetteMainFire ( gentity_t * ent )
//---------------------------------------------------------
{
vec3_t fwd , angs , start ;
gentity_t * missile ;
float damage = FLECHETTE_DAMAGE , vel = FLECHETTE_VEL ;
VectorCopy ( muzzle , start ) ;
WP_TraceSetStart ( ent , start , vec3_origin , vec3_origin ) ; //make sure our start point isn't on the other side of a wall
// If we aren't the player, we will cut the velocity and damage of the shots
if ( ent - > s . number )
{
damage * = 0.75f ;
vel * = 0.5f ;
}
// if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time )
// {
// // in overcharge mode, so doing double damage
// damage *= 2;
// }
for ( int i = 0 ; i < FLECHETTE_SHOTS ; i + + )
{
vectoangles ( forward , angs ) ;
if ( i = = 0 & & ent - > s . number = = 0 )
{
// do nothing on the first shot for the player, this one will hit the crosshairs
}
else
{
angs [ PITCH ] + = crandom ( ) * FLECHETTE_SPREAD ;
angs [ YAW ] + = crandom ( ) * FLECHETTE_SPREAD ;
}
AngleVectors ( angs , fwd , NULL , NULL ) ;
WP_MissileTargetHint ( ent , start , fwd ) ;
missile = CreateMissile ( start , fwd , vel , 10000 , ent ) ;
missile - > classname = " flech_proj " ;
missile - > s . weapon = WP_FLECHETTE ;
VectorSet ( missile - > maxs , FLECHETTE_SIZE , FLECHETTE_SIZE , FLECHETTE_SIZE ) ;
VectorScale ( missile - > maxs , - 1 , missile - > mins ) ;
missile - > damage = damage ;
// if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time )
// {
// missile->flags |= FL_OVERCHARGED;
// }
missile - > dflags = ( DAMAGE_DEATH_KNOCKBACK | DAMAGE_EXTRA_KNOCKBACK ) ;
missile - > methodOfDeath = MOD_FLECHETTE ;
missile - > clipmask = MASK_SHOT | CONTENTS_LIGHTSABER ;
// we don't want it to bounce forever
missile - > bounceCount = Q_irand ( 1 , 2 ) ;
missile - > s . eFlags | = EF_BOUNCE_SHRAPNEL ;
ent - > client - > sess . missionStats . shotsFired + + ;
}
}
//---------------------------------------------------------
void prox_mine_think ( gentity_t * ent )
//---------------------------------------------------------
{
int count ;
qboolean blow = qfalse ;
// if it isn't time to auto-explode, do a small proximity check
if ( ent - > delay > level . time )
{
count = G_RadiusList ( ent - > currentOrigin , FLECHETTE_MINE_RADIUS_CHECK , ent , qtrue , ent_list ) ;
for ( int i = 0 ; i < count ; i + + )
{
if ( ent_list [ i ] - > client & & ent_list [ i ] - > health > 0 & & ent - > activator & & ent_list [ i ] - > s . number ! = ent - > activator - > s . number )
{
blow = qtrue ;
break ;
}
}
}
else
{
// well, we must die now
blow = qtrue ;
}
if ( blow )
{
// G_Sound( ent, G_SoundIndex( "sound/weapons/flechette/warning.wav" ));
ent - > e_ThinkFunc = thinkF_WP_Explode ;
ent - > nextthink = level . time + 200 ;
}
else
{
// we probably don't need to do this thinking logic very often...maybe this is fast enough?
ent - > nextthink = level . time + 500 ;
}
}
//---------------------------------------------------------
void prox_mine_stick ( gentity_t * self , gentity_t * other , trace_t * trace )
//---------------------------------------------------------
{
// turn us into a generic entity so we aren't running missile code
self - > s . eType = ET_GENERAL ;
self - > s . modelindex = G_ModelIndex ( " models/weapons2/golan_arms/prox_mine.md3 " ) ;
self - > e_TouchFunc = touchF_NULL ;
self - > contents = CONTENTS_SOLID ;
self - > takedamage = qtrue ;
self - > health = 5 ;
self - > e_DieFunc = dieF_WP_ExplosiveDie ;
VectorSet ( self - > maxs , 5 , 5 , 5 ) ;
VectorScale ( self - > maxs , - 1 , self - > mins ) ;
self - > activator = self - > owner ;
self - > owner = NULL ;
WP_Stick ( self , trace ) ;
self - > e_ThinkFunc = thinkF_prox_mine_think ;
self - > nextthink = level . time + 450 ;
// sticks for twenty seconds, then auto blows.
self - > delay = level . time + 20000 ;
gi . linkentity ( self ) ;
}
/* Old Flechette alt-fire code....
//---------------------------------------------------------
static void WP_FlechetteProxMine ( gentity_t * ent )
//---------------------------------------------------------
{
gentity_t * missile = CreateMissile ( muzzle , forward , FLECHETTE_MINE_VEL , 10000 , ent , qtrue ) ;
missile - > fxID = G_EffectIndex ( " flechette/explosion " ) ;
missile - > classname = " proxMine " ;
missile - > s . weapon = WP_FLECHETTE ;
missile - > s . pos . trType = TR_GRAVITY ;
missile - > s . eFlags | = EF_MISSILE_STICK ;
missile - > e_TouchFunc = touchF_prox_mine_stick ;
missile - > damage = FLECHETTE_MINE_DAMAGE ;
missile - > methodOfDeath = MOD_EXPLOSIVE ;
missile - > splashDamage = FLECHETTE_MINE_SPLASH_DAMAGE ;
missile - > splashRadius = FLECHETTE_MINE_SPLASH_RADIUS ;
missile - > splashMethodOfDeath = MOD_EXPLOSIVE_SPLASH ;
missile - > clipmask = MASK_SHOT ;
// we don't want it to bounce forever
missile - > bounceCount = 0 ;
}
*/
//----------------------------------------------
void WP_flechette_alt_blow ( gentity_t * ent )
//----------------------------------------------
{
EvaluateTrajectory ( & ent - > s . pos , level . time , ent - > currentOrigin ) ; // Not sure if this is even necessary, but correct origins are cool?
G_RadiusDamage ( ent - > currentOrigin , ent - > owner , ent - > splashDamage , ent - > splashRadius , NULL , MOD_EXPLOSIVE_SPLASH ) ;
G_PlayEffect ( " flechette/alt_blow " , ent - > currentOrigin ) ;
G_FreeEntity ( ent ) ;
}
//------------------------------------------------------------------------------
static void WP_CreateFlechetteBouncyThing ( vec3_t start , vec3_t fwd , gentity_t * self )
//------------------------------------------------------------------------------
{
gentity_t * missile = CreateMissile ( start , fwd , 950 + random ( ) * 700 , 1500 + random ( ) * 2000 , self , qtrue ) ;
missile - > e_ThinkFunc = thinkF_WP_flechette_alt_blow ;
missile - > s . weapon = WP_FLECHETTE ;
missile - > classname = " flech_alt " ;
missile - > mass = 4 ;
// How 'bout we give this thing a size...
VectorSet ( missile - > mins , - 3.0f , - 3.0f , - 3.0f ) ;
VectorSet ( missile - > maxs , 3.0f , 3.0f , 3.0f ) ;
missile - > clipmask = MASK_SHOT ;
missile - > clipmask & = ~ CONTENTS_CORPSE ;
// normal ones bounce, alt ones explode on impact
missile - > s . pos . trType = TR_GRAVITY ;
missile - > s . eFlags | = EF_BOUNCE_HALF ;
missile - > damage = FLECHETTE_ALT_DAMAGE ;
missile - > dflags = 0 ;
missile - > splashDamage = FLECHETTE_ALT_SPLASH_DAM ;
missile - > splashRadius = FLECHETTE_ALT_SPLASH_RAD ;
missile - > svFlags = SVF_USE_CURRENT_ORIGIN ;
missile - > methodOfDeath = MOD_FLECHETTE_ALT ;
missile - > splashMethodOfDeath = MOD_FLECHETTE_ALT ;
VectorCopy ( start , missile - > pos2 ) ;
}
//---------------------------------------------------------
static void WP_FlechetteAltFire ( gentity_t * self )
//---------------------------------------------------------
{
vec3_t dir , fwd , start , angs ;
vectoangles ( forward , angs ) ;
VectorCopy ( muzzle , start ) ;
WP_TraceSetStart ( self , start , vec3_origin , vec3_origin ) ; //make sure our start point isn't on the other side of a wall
for ( int i = 0 ; i < 2 ; i + + )
{
VectorCopy ( angs , dir ) ;
dir [ PITCH ] - = random ( ) * 4 + 8 ; // make it fly upwards
dir [ YAW ] + = crandom ( ) * 2 ;
AngleVectors ( dir , fwd , NULL , NULL ) ;
WP_CreateFlechetteBouncyThing ( start , fwd , self ) ;
self - > client - > sess . missionStats . shotsFired + + ;
}
}
//---------------------------------------------------------
static void WP_FireFlechette ( gentity_t * ent , qboolean alt_fire )
//---------------------------------------------------------
{
if ( alt_fire )
{
WP_FlechetteAltFire ( ent ) ;
}
else
{
WP_FlechetteMainFire ( ent ) ;
}
}
//-----------------------
// Rocket Launcher
//-----------------------
//---------------------------------------------------------
void rocketThink ( gentity_t * ent )
//---------------------------------------------------------
{
vec3_t newdir , targetdir ,
up = { 0 , 0 , 1 } , right ;
vec3_t org ;
float dot , dot2 ;
if ( ent - > disconnectDebounceTime & & ent - > disconnectDebounceTime < level . time )
{ //time's up, we're done, remove us
if ( ent - > lockCount )
{ //explode when die
WP_ExplosiveDie ( ent , ent - > owner , ent - > owner , 0 , MOD_UNKNOWN , 0 , HL_NONE ) ;
}
else
{ //just remove when die
G_FreeEntity ( ent ) ;
}
return ;
}
if ( ent - > enemy & & ent - > enemy - > inuse )
{
float vel = ( ent - > spawnflags & 1 ) ? ent - > speed : ROCKET_VELOCITY ;
float newDirMult = ent - > angle ? ent - > angle * 2.0f : 1.0f ;
float oldDirMult = ent - > angle ? ( 1.0f - ent - > angle ) * 2.0f : 1.0f ;
if ( ( ent - > spawnflags & 1 ) )
{ //vehicle rocket
if ( ent - > enemy - > client & & ent - > enemy - > client - > NPC_class = = CLASS_VEHICLE )
{ //tracking another vehicle
if ( ent - > enemy - > client - > ps . speed + ent - > speed > vel )
{
vel = ent - > enemy - > client - > ps . speed + ent - > speed ;
}
}
}
VectorCopy ( ent - > enemy - > currentOrigin , org ) ;
org [ 2 ] + = ( ent - > enemy - > mins [ 2 ] + ent - > enemy - > maxs [ 2 ] ) * 0.5f ;
if ( ent - > enemy - > client )
{
switch ( ent - > enemy - > client - > NPC_class )
{
case CLASS_ATST :
org [ 2 ] + = 80 ;
break ;
case CLASS_MARK1 :
org [ 2 ] + = 40 ;
break ;
case CLASS_PROBE :
org [ 2 ] + = 60 ;
break ;
}
if ( ! TIMER_Done ( ent - > enemy , " flee " ) )
{
TIMER_Set ( ent - > enemy , " rocketChasing " , 500 ) ;
}
}
VectorSubtract ( org , ent - > currentOrigin , targetdir ) ;
VectorNormalize ( targetdir ) ;
// Now the rocket can't do a 180 in space, so we'll limit the turn to about 45 degrees.
dot = DotProduct ( targetdir , ent - > movedir ) ;
// a dot of 1.0 means right-on-target.
if ( dot < 0.0f )
{
// Go in the direction opposite, start a 180.
CrossProduct ( ent - > movedir , up , right ) ;
dot2 = DotProduct ( targetdir , right ) ;
if ( dot2 > 0 )
{
// Turn 45 degrees right.
VectorMA ( ent - > movedir , 0.3f * newDirMult , right , newdir ) ;
}
else
{
// Turn 45 degrees left.
VectorMA ( ent - > movedir , - 0.3f * newDirMult , right , newdir ) ;
}
// Yeah we've adjusted horizontally, but let's split the difference vertically, so we kinda try to move towards it.
newdir [ 2 ] = ( ( targetdir [ 2 ] * newDirMult ) + ( ent - > movedir [ 2 ] * oldDirMult ) ) * 0.5 ;
// slowing down coupled with fairly tight turns can lead us to orbit an enemy..looks bad so don't do it!
// vel *= 0.5f;
}
else if ( dot < 0.70f )
{
// Still a bit off, so we turn a bit softer
VectorMA ( ent - > movedir , 0.5f * newDirMult , targetdir , newdir ) ;
}
else
{
// getting close, so turn a bit harder
VectorMA ( ent - > movedir , 0.9f * newDirMult , targetdir , newdir ) ;
}
// add crazy drunkenness
for ( int i = 0 ; i < 3 ; i + + )
{
newdir [ i ] + = crandom ( ) * ent - > random * 0.25f ;
}
// decay the randomness
ent - > random * = 0.9f ;
if ( ent - > enemy - > client
& & ent - > enemy - > client - > ps . groundEntityNum ! = ENTITYNUM_NONE )
{ //tracking a client who's on the ground, aim at the floor...?
// Try to crash into the ground if we get close enough to do splash damage
float dis = Distance ( ent - > currentOrigin , org ) ;
if ( dis < 128 )
{
// the closer we get, the more we push the rocket down, heh heh.
newdir [ 2 ] - = ( 1.0f - ( dis / 128.0f ) ) * 0.6f ;
}
}
VectorNormalize ( newdir ) ;
VectorScale ( newdir , vel * 0.5f , ent - > s . pos . trDelta ) ;
VectorCopy ( newdir , ent - > movedir ) ;
SnapVector ( ent - > s . pos . trDelta ) ; // save net bandwidth
VectorCopy ( ent - > currentOrigin , ent - > s . pos . trBase ) ;
ent - > s . pos . trTime = level . time ;
}
ent - > nextthink = level . time + ROCKET_ALT_THINK_TIME ; // Nothing at all spectacular happened, continue.
return ;
}
//---------------------------------------------------------
static void WP_FireRocket ( gentity_t * ent , qboolean alt_fire )
//---------------------------------------------------------
{
vec3_t start ;
int damage = ROCKET_DAMAGE ;
float vel = ROCKET_VELOCITY ;
if ( alt_fire )
{
vel * = 0.5f ;
}
VectorCopy ( muzzle , start ) ;
WP_TraceSetStart ( ent , start , vec3_origin , vec3_origin ) ; //make sure our start point isn't on the other side of a wall
gentity_t * missile = CreateMissile ( start , forward , vel , 10000 , ent , alt_fire ) ;
missile - > classname = " rocket_proj " ;
missile - > s . weapon = WP_ROCKET_LAUNCHER ;
missile - > mass = 10 ;
// Do the damages
if ( ent - > s . number ! = 0 )
{
if ( g_spskill - > integer = = 0 )
{
damage = ROCKET_NPC_DAMAGE_EASY ;
}
else if ( g_spskill - > integer = = 1 )
{
damage = ROCKET_NPC_DAMAGE_NORMAL ;
}
else
{
damage = ROCKET_NPC_DAMAGE_HARD ;
}
if ( ent - > client & & ent - > client - > NPC_class = = CLASS_BOBAFETT )
{
damage = damage / 2 ;
}
}
if ( alt_fire )
{
int lockEntNum , lockTime ;
if ( ent - > NPC & & ent - > enemy )
{
lockEntNum = ent - > enemy - > s . number ;
lockTime = Q_irand ( 600 , 1200 ) ;
}
else
{
lockEntNum = g_rocketLockEntNum ;
lockTime = g_rocketLockTime ;
}
// we'll consider attempting to lock this little poochie onto some baddie.
if ( ( lockEntNum > 0 | | ent - > NPC & & lockEntNum > = 0 ) & & lockEntNum < ENTITYNUM_WORLD & & lockTime > 0 )
{
// take our current lock time and divide that by 8 wedge slices to get the current lock amount
int dif = ( level . time - lockTime ) / ( 1200.0f / 8.0f ) ;
if ( dif < 0 )
{
dif = 0 ;
}
else if ( dif > 8 )
{
dif = 8 ;
}
// if we are fully locked, always take on the enemy.
// Also give a slight advantage to higher, but not quite full charges.
// Finally, just give any amount of charge a very slight random chance of locking.
if ( dif = = 8 | | random ( ) * dif > 2 | | random ( ) > 0.97f )
{
missile - > enemy = & g_entities [ lockEntNum ] ;
if ( missile - > enemy
& & missile - > enemy - > inuse ) //&& DistanceSquared( missile->currentOrigin, missile->enemy->currentOrigin ) < 262144 && InFOV( missile->currentOrigin, missile->enemy->currentOrigin, missile->enemy->client->ps.viewangles, 45, 45 ) )
{
if ( missile - > enemy - > client
& & ( missile - > enemy - > client - > ps . forcePowersKnown & ( 1 < < FP_PUSH ) )
& & missile - > enemy - > client - > ps . forcePowerLevel [ FP_PUSH ] > FORCE_LEVEL_0 )
{ //have force push, don't flee from homing rockets
}
else
{
vec3_t dir , dir2 ;
AngleVectors ( missile - > enemy - > currentAngles , dir , NULL , NULL ) ;
AngleVectors ( ent - > client - > renderInfo . eyeAngles , dir2 , NULL , NULL ) ;
if ( DotProduct ( dir , dir2 ) < 0.0f )
{
G_StartFlee ( missile - > enemy , ent , missile - > enemy - > currentOrigin , AEL_DANGER_GREAT , 3000 , 5000 ) ;
if ( ! TIMER_Done ( missile - > enemy , " flee " ) )
{
TIMER_Set ( missile - > enemy , " rocketChasing " , 500 ) ;
}
}
}
}
}
}
VectorCopy ( forward , missile - > movedir ) ;
missile - > e_ThinkFunc = thinkF_rocketThink ;
missile - > random = 1.0f ;
missile - > nextthink = level . time + ROCKET_ALT_THINK_TIME ;
}
// Make it easier to hit things
VectorSet ( missile - > maxs , ROCKET_SIZE , ROCKET_SIZE , ROCKET_SIZE ) ;
VectorScale ( missile - > maxs , - 1 , missile - > mins ) ;
missile - > damage = damage ;
missile - > dflags = DAMAGE_DEATH_KNOCKBACK ;
if ( alt_fire )
{
missile - > methodOfDeath = MOD_ROCKET_ALT ;
missile - > splashMethodOfDeath = MOD_ROCKET_ALT ; // ?SPLASH;
}
else
{
missile - > methodOfDeath = MOD_ROCKET ;
missile - > splashMethodOfDeath = MOD_ROCKET ; // ?SPLASH;
}
missile - > clipmask = MASK_SHOT | CONTENTS_LIGHTSABER ;
missile - > splashDamage = ROCKET_SPLASH_DAMAGE ;
missile - > splashRadius = ROCKET_SPLASH_RADIUS ;
// we don't want it to ever bounce
missile - > bounceCount = 0 ;
}
static void WP_FireConcussionAlt ( gentity_t * ent )
{ //a rail-gun-like beam
int damage = CONC_ALT_DAMAGE , skip , traces = DISRUPTOR_ALT_TRACES ;
qboolean render_impact = qtrue ;
vec3_t start , end ;
vec3_t muzzle2 , spot , dir ;
trace_t tr ;
gentity_t * traceEnt , * tent ;
float dist , shotDist , shotRange = 8192 ;
qboolean hitDodged = qfalse ;
if ( ent - > s . number > = MAX_CLIENTS )
{
vec3_t angles ;
vectoangles ( forward , angles ) ;
angles [ PITCH ] + = ( crandom ( ) * ( CONC_NPC_SPREAD + ( 6 - ent - > NPC - > currentAim ) * 0.25f ) ) ; //was 0.5f
angles [ YAW ] + = ( crandom ( ) * ( CONC_NPC_SPREAD + ( 6 - ent - > NPC - > currentAim ) * 0.25f ) ) ; //was 0.5f
AngleVectors ( angles , forward , vright , up ) ;
}
//Shove us backwards for half a second
VectorMA ( ent - > client - > ps . velocity , - 200 , forward , ent - > client - > ps . velocity ) ;
ent - > client - > ps . groundEntityNum = ENTITYNUM_NONE ;
if ( ( ent - > client - > ps . pm_flags & PMF_DUCKED ) )
{ //hunkered down
ent - > client - > ps . pm_time = 100 ;
}
else
{
ent - > client - > ps . pm_time = 250 ;
}
ent - > client - > ps . pm_flags | = PMF_TIME_KNOCKBACK | PMF_TIME_NOFRICTION ;
//FIXME: only if on ground? So no "rocket jump"? Or: (see next FIXME)
//FIXME: instead, set a forced ucmd backmove instead of this sliding
VectorCopy ( muzzle , muzzle2 ) ; // making a backup copy
// The trace start will originate at the eye so we can ensure that it hits the crosshair.
if ( ent - > NPC )
{
switch ( g_spskill - > integer )
{
case 0 :
damage = CONC_ALT_NPC_DAMAGE_EASY ;
break ;
case 1 :
damage = CONC_ALT_NPC_DAMAGE_MEDIUM ;
break ;
case 2 :
default :
damage = CONC_ALT_NPC_DAMAGE_HARD ;
break ;
}
}
VectorCopy ( muzzle , start ) ;
WP_TraceSetStart ( ent , start , vec3_origin , vec3_origin ) ;
skip = ent - > s . number ;
// if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time )
// {
// // in overcharge mode, so doing double damage
// damage *= 2;
// }
//Make it a little easier to hit guys at long range
vec3_t shot_mins , shot_maxs ;
VectorSet ( shot_mins , - 1 , - 1 , - 1 ) ;
VectorSet ( shot_maxs , 1 , 1 , 1 ) ;
for ( int i = 0 ; i < traces ; i + + )
{
VectorMA ( start , shotRange , forward , end ) ;
//NOTE: if you want to be able to hit guys in emplaced guns, use "G2_COLLIDE, 10" instead of "G2_RETURNONHIT, 0"
//alternately, if you end up hitting an emplaced_gun that has a sitter, just redo this one trace with the "G2_COLLIDE, 10" to see if we it the sitter
//gi.trace( &tr, start, NULL, NULL, end, skip, MASK_SHOT, G2_COLLIDE, 10 );//G2_RETURNONHIT, 0 );
gi . trace ( & tr , start , shot_mins , shot_maxs , end , skip , MASK_SHOT , G2_COLLIDE , 10 ) ; //G2_RETURNONHIT, 0 );
if ( tr . surfaceFlags & SURF_NOIMPACT )
{
render_impact = qfalse ;
}
if ( tr . entityNum = = ent - > s . number )
{
// should never happen, but basically we don't want to consider a hit to ourselves?
// Get ready for an attempt to trace through another person
VectorCopy ( tr . endpos , muzzle2 ) ;
VectorCopy ( tr . endpos , start ) ;
skip = tr . entityNum ;
# ifdef _DEBUG
gi . Printf ( " BAD! Concussion gun shot somehow traced back and hit the owner! \n " ) ;
# endif
continue ;
}
// always render a shot beam, doing this the old way because I don't much feel like overriding the effect.
//NOTE: let's just draw one beam at the end
//tent = G_TempEntity( tr.endpos, EV_CONC_ALT_SHOT );
//tent->svFlags |= SVF_BROADCAST;
//VectorCopy( muzzle2, tent->s.origin2 );
if ( tr . fraction > = 1.0f )
{
// draw the beam but don't do anything else
break ;
}
traceEnt = & g_entities [ tr . entityNum ] ;
if ( traceEnt //&& traceEnt->NPC
& & ( traceEnt - > s . weapon = = WP_SABER | | ( traceEnt - > client & & ( traceEnt - > client - > NPC_class = = CLASS_BOBAFETT | | traceEnt - > client - > NPC_class = = CLASS_REBORN ) ) ) )
{ //FIXME: need a more reliable way to know we hit a jedi?
hitDodged = Jedi_DodgeEvasion ( traceEnt , ent , & tr , HL_NONE ) ;
//acts like we didn't even hit him
}
if ( ! hitDodged )
{
if ( render_impact )
{
if ( ( tr . entityNum < ENTITYNUM_WORLD & & traceEnt - > takedamage )
| | ! Q_stricmp ( traceEnt - > classname , " misc_model_breakable " )
| | traceEnt - > s . eType = = ET_MOVER )
{
// Create a simple impact type mark that doesn't last long in the world
G_PlayEffect ( G_EffectIndex ( " concussion/alt_hit " ) , tr . endpos , tr . plane . normal ) ;
if ( traceEnt - > client & & LogAccuracyHit ( traceEnt , ent ) )
{ //NOTE: hitting multiple ents can still get you over 100% accuracy
ent - > client - > ps . persistant [ PERS_ACCURACY_HITS ] + + ;
}
int hitLoc = G_GetHitLocFromTrace ( & tr , MOD_CONC_ALT ) ;
qboolean noKnockBack = ( traceEnt - > flags & FL_NO_KNOCKBACK ) ; //will be set if they die, I want to know if it was on *before* they died
if ( traceEnt & & traceEnt - > client & & traceEnt - > client - > NPC_class = = CLASS_GALAKMECH )
{ //hehe
G_Damage ( traceEnt , ent , ent , forward , tr . endpos , 10 , DAMAGE_NO_KNOCKBACK | DAMAGE_NO_HIT_LOC , MOD_CONC_ALT , hitLoc ) ;
break ;
}
G_Damage ( traceEnt , ent , ent , forward , tr . endpos , damage , DAMAGE_NO_KNOCKBACK | DAMAGE_NO_HIT_LOC , MOD_CONC_ALT , hitLoc ) ;
//do knockback and knockdown manually
if ( traceEnt - > client )
{ //only if we hit a client
vec3_t pushDir ;
VectorCopy ( forward , pushDir ) ;
if ( pushDir [ 2 ] < 0.2f )
{
pushDir [ 2 ] = 0.2f ;
} //hmm, re-normalize? nah...
//if ( traceEnt->NPC || Q_irand(0,g_spskill->integer+1) )
{
if ( ! noKnockBack )
{ //knock-backable
G_Throw ( traceEnt , pushDir , 200 ) ;
if ( traceEnt - > client - > NPC_class = = CLASS_ROCKETTROOPER )
{
traceEnt - > client - > ps . pm_time = Q_irand ( 1500 , 3000 ) ;
}
}
if ( traceEnt - > health > 0 )
{ //alive
if ( G_HasKnockdownAnims ( traceEnt ) )
{ //knock-downable
G_Knockdown ( traceEnt , ent , pushDir , 400 , qtrue ) ;
}
}
}
}
if ( traceEnt - > s . eType = = ET_MOVER )
{ //stop the traces on any mover
break ;
}
}
else
{
// we only make this mark on things that can't break or move
tent = G_TempEntity ( tr . endpos , EV_CONC_ALT_MISS ) ;
tent - > svFlags | = SVF_BROADCAST ;
VectorCopy ( tr . plane . normal , tent - > pos1 ) ;
break ; // hit solid, but doesn't take damage, so stop the shot...we _could_ allow it to shoot through walls, might be cool?
}
}
else // not rendering impact, must be a skybox or other similar thing?
{
break ; // don't try anymore traces
}
}
// Get ready for an attempt to trace through another person
VectorCopy ( tr . endpos , muzzle2 ) ;
VectorCopy ( tr . endpos , start ) ;
skip = tr . entityNum ;
hitDodged = qfalse ;
}
//just draw one beam all the way to the end
tent = G_TempEntity ( tr . endpos , EV_CONC_ALT_SHOT ) ;
tent - > svFlags | = SVF_BROADCAST ;
VectorCopy ( muzzle , tent - > s . origin2 ) ;
// now go along the trail and make sight events
VectorSubtract ( tr . endpos , muzzle , dir ) ;
shotDist = VectorNormalize ( dir ) ;
//FIXME: if shoot *really* close to someone, the alert could be way out of their FOV
for ( dist = 0 ; dist < shotDist ; dist + = 64 )
{
//FIXME: on a really long shot, this could make a LOT of alerts in one frame...
VectorMA ( muzzle , dist , dir , spot ) ;
AddSightEvent ( ent , spot , 256 , AEL_DISCOVERED , 50 ) ;
//FIXME: creates *way* too many effects, make it one effect somehow?
G_PlayEffect ( G_EffectIndex ( " concussion/alt_ring " ) , spot , forward ) ;
}
//FIXME: spawn a temp ent that continuously spawns sight alerts here? And 1 sound alert to draw their attention?
VectorMA ( start , shotDist - 4 , forward , spot ) ;
AddSightEvent ( ent , spot , 256 , AEL_DISCOVERED , 50 ) ;
G_PlayEffect ( G_EffectIndex ( " concussion/altmuzzle_flash " ) , muzzle , forward ) ;
}
static void WP_FireConcussion ( gentity_t * ent )
{ //a fast rocket-like projectile
vec3_t start ;
int damage = CONC_DAMAGE ;
float vel = CONC_VELOCITY ;
if ( ent - > s . number > = MAX_CLIENTS )
{
vec3_t angles ;
vectoangles ( forward , angles ) ;
angles [ PITCH ] + = ( crandom ( ) * ( CONC_NPC_SPREAD + ( 6 - ent - > NPC - > currentAim ) * 0.25f ) ) ; //was 0.5f
angles [ YAW ] + = ( crandom ( ) * ( CONC_NPC_SPREAD + ( 6 - ent - > NPC - > currentAim ) * 0.25f ) ) ; //was 0.5f
AngleVectors ( angles , forward , vright , up ) ;
}
//hold us still for a bit
ent - > client - > ps . pm_time = 300 ;
ent - > client - > ps . pm_flags | = PMF_TIME_KNOCKBACK ;
//add viewkick
if ( ent - > s . number < MAX_CLIENTS //player only
& & ! cg . renderingThirdPerson ) //gives an advantage to being in 3rd person, but would look silly otherwise
{ //kick the view back
cg . kick_angles [ PITCH ] = Q_flrand ( - 10 , - 15 ) ;
cg . kick_time = level . time ;
}
VectorCopy ( muzzle , start ) ;
WP_TraceSetStart ( ent , start , vec3_origin , vec3_origin ) ; //make sure our start point isn't on the other side of a wall
gentity_t * missile = CreateMissile ( start , forward , vel , 10000 , ent , qfalse ) ;
missile - > classname = " conc_proj " ;
missile - > s . weapon = WP_CONCUSSION ;
missile - > mass = 10 ;
// Do the damages
if ( ent - > s . number ! = 0 )
{
if ( g_spskill - > integer = = 0 )
{
damage = CONC_NPC_DAMAGE_EASY ;
}
else if ( g_spskill - > integer = = 1 )
{
damage = CONC_NPC_DAMAGE_NORMAL ;
}
else
{
damage = CONC_NPC_DAMAGE_HARD ;
}
}
// Make it easier to hit things
VectorSet ( missile - > maxs , ROCKET_SIZE , ROCKET_SIZE , ROCKET_SIZE ) ;
VectorScale ( missile - > maxs , - 1 , missile - > mins ) ;
missile - > damage = damage ;
missile - > dflags = DAMAGE_EXTRA_KNOCKBACK ;
missile - > methodOfDeath = MOD_CONC ;
missile - > splashMethodOfDeath = MOD_CONC ;
missile - > clipmask = MASK_SHOT | CONTENTS_LIGHTSABER ;
missile - > splashDamage = CONC_SPLASH_DAMAGE ;
missile - > splashRadius = CONC_SPLASH_RADIUS ;
// we don't want it to ever bounce
missile - > bounceCount = 0 ;
}
//-----------------------
// Det Pack
//-----------------------
//---------------------------------------------------------
void charge_stick ( gentity_t * self , gentity_t * other , trace_t * trace )
//---------------------------------------------------------
{
self - > s . eType = ET_GENERAL ;
// make us so we can take damage
self - > clipmask = MASK_SHOT ;
self - > contents = CONTENTS_SHOTCLIP ;
self - > takedamage = qtrue ;
self - > health = 25 ;
self - > e_DieFunc = dieF_WP_ExplosiveDie ;
VectorSet ( self - > maxs , 10 , 10 , 10 ) ;
VectorScale ( self - > maxs , - 1 , self - > mins ) ;
self - > activator = self - > owner ;
self - > owner = NULL ;
self - > e_TouchFunc = touchF_NULL ;
self - > e_ThinkFunc = thinkF_NULL ;
self - > nextthink = - 1 ;
WP_Stick ( self , trace , 1.0f ) ;
}
//---------------------------------------------------------
static void WP_DropDetPack ( gentity_t * self , vec3_t start , vec3_t dir )
//---------------------------------------------------------
{
// Chucking a new one
AngleVectors ( self - > client - > ps . viewangles , forward , vright , up ) ;
CalcMuzzlePoint ( self , forward , vright , up , muzzle , 0 ) ;
VectorNormalize ( forward ) ;
VectorMA ( muzzle , - 4 , forward , muzzle ) ;
VectorCopy ( muzzle , start ) ;
WP_TraceSetStart ( self , start , vec3_origin , vec3_origin ) ; //make sure our start point isn't on the other side of a wall
gentity_t * missile = CreateMissile ( start , forward , 300 , 10000 , self , qfalse ) ;
missile - > fxID = G_EffectIndex ( " detpack/explosion " ) ; // if we set an explosion effect, explode death can use that instead
missile - > classname = " detpack " ;
missile - > s . weapon = WP_DET_PACK ;
missile - > s . pos . trType = TR_GRAVITY ;
missile - > s . eFlags | = EF_MISSILE_STICK ;
missile - > e_TouchFunc = touchF_charge_stick ;
missile - > damage = FLECHETTE_MINE_DAMAGE ;
missile - > methodOfDeath = MOD_DETPACK ;
missile - > splashDamage = FLECHETTE_MINE_SPLASH_DAMAGE ;
missile - > splashRadius = FLECHETTE_MINE_SPLASH_RADIUS ;
missile - > splashMethodOfDeath = MOD_DETPACK ; // ?SPLASH;
missile - > clipmask = ( CONTENTS_SOLID | CONTENTS_BODY | CONTENTS_SHOTCLIP ) ; //MASK_SHOT;
// we don't want it to ever bounce
missile - > bounceCount = 0 ;
missile - > s . radius = 30 ;
VectorSet ( missile - > s . modelScale , 1.0f , 1.0f , 1.0f ) ;
gi . G2API_InitGhoul2Model ( missile - > ghoul2 , weaponData [ WP_DET_PACK ] . missileMdl , G_ModelIndex ( weaponData [ WP_DET_PACK ] . missileMdl ) ) ;
AddSoundEvent ( NULL , missile - > currentOrigin , 128 , AEL_MINOR , qtrue ) ;
AddSightEvent ( NULL , missile - > currentOrigin , 128 , AEL_SUSPICIOUS , 10 ) ;
}
//---------------------------------------------------------
static void WP_FireDetPack ( gentity_t * ent , qboolean alt_fire )
//---------------------------------------------------------
{
if ( ! ent | | ! ent - > client )
{
return ;
}
if ( alt_fire )
{
if ( ent - > client - > ps . eFlags & EF_PLANTED_CHARGE )
{
gentity_t * found = NULL ;
// loop through all ents and blow the crap out of them!
while ( ( found = G_Find ( found , FOFS ( classname ) , " detpack " ) ) ! = NULL )
{
if ( found - > activator = = ent )
{
VectorCopy ( found - > currentOrigin , found - > s . origin ) ;
found - > e_ThinkFunc = thinkF_WP_Explode ;
found - > nextthink = level . time + 100 + random ( ) * 100 ;
G_Sound ( found , G_SoundIndex ( " sound/weapons/detpack/warning.wav " ) ) ;
// would be nice if this actually worked?
AddSoundEvent ( NULL , found - > currentOrigin , found - > splashRadius * 2 , AEL_DANGER , qfalse , qtrue ) ; //FIXME: are we on ground or not?
AddSightEvent ( NULL , found - > currentOrigin , found - > splashRadius * 2 , AEL_DISCOVERED , 100 ) ;
}
}
ent - > client - > ps . eFlags & = ~ EF_PLANTED_CHARGE ;
}
}
else
{
WP_DropDetPack ( ent , muzzle , forward ) ;
ent - > client - > ps . eFlags | = EF_PLANTED_CHARGE ;
}
}
//-----------------------
// Laser Trip Mine
//-----------------------
# define PROXIMITY_STYLE 1
# define TRIPWIRE_STYLE 2
//---------------------------------------------------------
void touchLaserTrap ( gentity_t * ent , gentity_t * other , trace_t * trace )
//---------------------------------------------------------
{
ent - > s . eType = ET_GENERAL ;
// a tripwire so add draw line flag
VectorCopy ( trace - > plane . normal , ent - > movedir ) ;
// make it shootable
VectorSet ( ent - > mins , - 4 , - 4 , - 4 ) ;
VectorSet ( ent - > maxs , 4 , 4 , 4 ) ;
ent - > clipmask = MASK_SHOT ;
ent - > contents = CONTENTS_SHOTCLIP ;
ent - > takedamage = qtrue ;
ent - > health = 15 ;
ent - > e_DieFunc = dieF_WP_ExplosiveDie ;
ent - > e_TouchFunc = touchF_NULL ;
// so we can trip it too
ent - > activator = ent - > owner ;
ent - > owner = NULL ;
WP_Stick ( ent , trace ) ;
if ( ent - > count = = TRIPWIRE_STYLE )
{
vec3_t mins = { - 4 , - 4 , - 4 } , maxs = { 4 , 4 , 4 } ; //FIXME: global these
trace_t tr ;
VectorMA ( ent - > currentOrigin , 32 , ent - > movedir , ent - > s . origin2 ) ;
gi . trace ( & tr , ent - > s . origin2 , mins , maxs , ent - > currentOrigin , ent - > s . number , MASK_SHOT , G2_RETURNONHIT , 0 ) ;
VectorCopy ( tr . endpos , ent - > s . origin2 ) ;
ent - > e_ThinkFunc = thinkF_laserTrapThink ;
}
else
{
ent - > e_ThinkFunc = thinkF_WP_prox_mine_think ;
}
ent - > nextthink = level . time + LT_ACTIVATION_DELAY ;
}
// copied from flechette prox above...which will not be used if this gets approved
//---------------------------------------------------------
void WP_prox_mine_think ( gentity_t * ent )
//---------------------------------------------------------
{
int count ;
qboolean blow = qfalse ;
// first time through?
if ( ent - > count )
{
// play activated warning
ent - > count = 0 ;
ent - > s . eFlags | = EF_PROX_TRIP ;
G_Sound ( ent , G_SoundIndex ( " sound/weapons/laser_trap/warning.wav " ) ) ;
}
// if it isn't time to auto-explode, do a small proximity check
if ( ent - > delay > level . time )
{
count = G_RadiusList ( ent - > currentOrigin , PROX_MINE_RADIUS_CHECK , ent , qtrue , ent_list ) ;
for ( int i = 0 ; i < count ; i + + )
{
if ( ent_list [ i ] - > client & & ent_list [ i ] - > health > 0 & & ent - > activator & & ent_list [ i ] - > s . number ! = ent - > activator - > s . number )
{
blow = qtrue ;
break ;
}
}
}
else
{
// well, we must die now
blow = qtrue ;
}
if ( blow )
{
// G_Sound( ent, G_SoundIndex( "sound/weapons/flechette/warning.wav" ));
ent - > e_ThinkFunc = thinkF_WP_Explode ;
ent - > nextthink = level . time + 200 ;
}
else
{
// we probably don't need to do this thinking logic very often...maybe this is fast enough?
ent - > nextthink = level . time + 500 ;
}
}
//---------------------------------------------------------
void laserTrapThink ( gentity_t * ent )
//---------------------------------------------------------
{
gentity_t * traceEnt ;
vec3_t end , mins = { - 4 , - 4 , - 4 } , maxs = { 4 , 4 , 4 } ;
trace_t tr ;
// turn on the beam effect
if ( ! ( ent - > s . eFlags & EF_FIRING ) )
{
// arm me
G_Sound ( ent , G_SoundIndex ( " sound/weapons/laser_trap/warning.wav " ) ) ;
ent - > s . loopSound = G_SoundIndex ( " sound/weapons/laser_trap/hum_loop.wav " ) ;
ent - > s . eFlags | = EF_FIRING ;
}
ent - > e_ThinkFunc = thinkF_laserTrapThink ;
ent - > nextthink = level . time + FRAMETIME ;
// Find the main impact point
VectorMA ( ent - > s . pos . trBase , 2048 , ent - > movedir , end ) ;
gi . trace ( & tr , ent - > s . origin2 , mins , maxs , end , ent - > s . number , MASK_SHOT , G2_RETURNONHIT , 0 ) ;
traceEnt = & g_entities [ tr . entityNum ] ;
// Adjust this so that the effect has a relatively fresh endpoint
VectorCopy ( tr . endpos , ent - > pos4 ) ;
if ( traceEnt - > client | | tr . startsolid )
{
// go boom
WP_Explode ( ent ) ;
ent - > s . eFlags & = ~ EF_FIRING ; // don't draw beam if we are dead
}
else
{
/*
// FIXME: they need to avoid the beam!
AddSoundEvent ( ent - > owner , ent - > currentOrigin , ent - > splashRadius * 2 , AEL_DANGER ) ;
AddSightEvent ( ent - > owner , ent - > currentOrigin , ent - > splashRadius * 2 , AEL_DANGER , 50 ) ;
*/
}
}
//---------------------------------------------------------
void CreateLaserTrap ( gentity_t * laserTrap , vec3_t start , gentity_t * owner )
//---------------------------------------------------------
{
if ( ! VALIDSTRING ( laserTrap - > classname ) )
{
// since we may be coming from a map placed trip mine, we don't want to override that class name....
// That would be bad because the player drop code tries to limit number of placed items...so it would have removed map placed ones as well.
laserTrap - > classname = " tripmine " ;
}
laserTrap - > splashDamage = LT_SPLASH_DAM ;
laserTrap - > splashRadius = LT_SPLASH_RAD ;
laserTrap - > damage = LT_DAMAGE ;
laserTrap - > methodOfDeath = MOD_LASERTRIP ;
laserTrap - > splashMethodOfDeath = MOD_LASERTRIP ; //? SPLASH;
laserTrap - > s . eType = ET_MISSILE ;
laserTrap - > svFlags = SVF_USE_CURRENT_ORIGIN ;
laserTrap - > s . weapon = WP_TRIP_MINE ;
laserTrap - > owner = owner ;
// VectorSet( laserTrap->mins, -LT_SIZE, -LT_SIZE, -LT_SIZE );
// VectorSet( laserTrap->maxs, LT_SIZE, LT_SIZE, LT_SIZE );
laserTrap - > clipmask = ( CONTENTS_SOLID | CONTENTS_BODY | CONTENTS_SHOTCLIP ) ; //MASK_SHOT;
laserTrap - > s . pos . trTime = level . time ; // move a bit on the very first frame
VectorCopy ( start , laserTrap - > s . pos . trBase ) ;
VectorCopy ( start , laserTrap - > currentOrigin ) ;
VectorCopy ( start , laserTrap - > pos2 ) ; // ?? wtf ?
laserTrap - > fxID = G_EffectIndex ( " tripMine/explosion " ) ;
laserTrap - > e_TouchFunc = touchF_touchLaserTrap ;
laserTrap - > s . radius = 60 ;
VectorSet ( laserTrap - > s . modelScale , 1.0f , 1.0f , 1.0f ) ;
gi . G2API_InitGhoul2Model ( laserTrap - > ghoul2 , weaponData [ WP_TRIP_MINE ] . missileMdl , G_ModelIndex ( weaponData [ WP_TRIP_MINE ] . missileMdl ) ) ;
}
//---------------------------------------------------------
static void WP_RemoveOldTraps ( gentity_t * ent )
//---------------------------------------------------------
{
gentity_t * found = NULL ;
int trapcount = 0 , i ;
int foundLaserTraps [ MAX_GENTITIES ] = { ENTITYNUM_NONE } ;
int trapcount_org , lowestTimeStamp ;
int removeMe ;
// see how many there are now
while ( ( found = G_Find ( found , FOFS ( classname ) , " tripmine " ) ) ! = NULL )
{
if ( found - > activator ! = ent ) // activator is really the owner?
{
continue ;
}
foundLaserTraps [ trapcount + + ] = found - > s . number ;
}
// now remove first ones we find until there are only 9 left
found = NULL ;
trapcount_org = trapcount ;
lowestTimeStamp = level . time ;
while ( trapcount > 9 )
{
removeMe = - 1 ;
for ( i = 0 ; i < trapcount_org ; i + + )
{
if ( foundLaserTraps [ i ] = = ENTITYNUM_NONE )
{
continue ;
}
found = & g_entities [ foundLaserTraps [ i ] ] ;
if ( found - > setTime < lowestTimeStamp )
{
removeMe = i ;
lowestTimeStamp = found - > setTime ;
}
}
if ( removeMe ! = - 1 )
{
//remove it... or blow it?
if ( & g_entities [ foundLaserTraps [ removeMe ] ] = = NULL )
{
break ;
}
else
{
G_FreeEntity ( & g_entities [ foundLaserTraps [ removeMe ] ] ) ;
}
foundLaserTraps [ removeMe ] = ENTITYNUM_NONE ;
trapcount - - ;
}
else
{
break ;
}
}
}
//---------------------------------------------------------
void WP_PlaceLaserTrap ( gentity_t * ent , qboolean alt_fire )
//---------------------------------------------------------
{
vec3_t start ;
gentity_t * laserTrap ;
// limit to 10 placed at any one time
WP_RemoveOldTraps ( ent ) ;
//FIXME: surface must be within 64
laserTrap = G_Spawn ( ) ;
if ( laserTrap )
{
// now make the new one
VectorCopy ( muzzle , start ) ;
WP_TraceSetStart ( ent , start , vec3_origin , vec3_origin ) ; //make sure our start point isn't on the other side of a wall
CreateLaserTrap ( laserTrap , start , ent ) ;
// set player-created-specific fields
laserTrap - > setTime = level . time ; //remember when we placed it
laserTrap - > s . eFlags | = EF_MISSILE_STICK ;
laserTrap - > s . pos . trType = TR_GRAVITY ;
VectorScale ( forward , LT_VELOCITY , laserTrap - > s . pos . trDelta ) ;
if ( alt_fire )
{
laserTrap - > count = PROXIMITY_STYLE ;
laserTrap - > delay = level . time + 40000 ; // will auto-blow in 40 seconds.
laserTrap - > methodOfDeath = MOD_LASERTRIP_ALT ;
laserTrap - > splashMethodOfDeath = MOD_LASERTRIP_ALT ; //? SPLASH;
}
else
{
laserTrap - > count = TRIPWIRE_STYLE ;
}
}
}
//---------------------
// Thermal Detonator
//---------------------
//---------------------------------------------------------
void thermalDetonatorExplode ( gentity_t * ent )
//---------------------------------------------------------
{
if ( ( ent - > s . eFlags & EF_HELD_BY_SAND_CREATURE ) )
{
ent - > takedamage = qfalse ; // don't allow double deaths!
G_Damage ( ent - > activator , ent , ent - > owner , vec3_origin , ent - > currentOrigin , TD_ALT_DAMAGE , 0 , MOD_EXPLOSIVE ) ;
G_PlayEffect ( " thermal/explosion " , ent - > currentOrigin ) ;
G_PlayEffect ( " thermal/shockwave " , ent - > currentOrigin ) ;
G_FreeEntity ( ent ) ;
}
else if ( ! ent - > count )
{
G_Sound ( ent , G_SoundIndex ( " sound/weapons/thermal/warning.wav " ) ) ;
ent - > count = 1 ;
ent - > nextthink = level . time + 800 ;
ent - > svFlags | = SVF_BROADCAST ; //so everyone hears/sees the explosion?
}
else
{
vec3_t pos ;
VectorSet ( pos , ent - > currentOrigin [ 0 ] , ent - > currentOrigin [ 1 ] , ent - > currentOrigin [ 2 ] + 8 ) ;
ent - > takedamage = qfalse ; // don't allow double deaths!
G_RadiusDamage ( ent - > currentOrigin , ent - > owner , TD_SPLASH_DAM , TD_SPLASH_RAD , NULL , MOD_EXPLOSIVE_SPLASH ) ;
G_PlayEffect ( " thermal/explosion " , ent - > currentOrigin ) ;
G_PlayEffect ( " thermal/shockwave " , ent - > currentOrigin ) ;
G_FreeEntity ( ent ) ;
}
}
//-------------------------------------------------------------------------------------------------------------
void thermal_die ( gentity_t * self , gentity_t * inflictor , gentity_t * attacker , int damage , int mod , int dFlags , int hitLoc )
//-------------------------------------------------------------------------------------------------------------
{
thermalDetonatorExplode ( self ) ;
}
//---------------------------------------------------------
qboolean WP_LobFire ( gentity_t * self , vec3_t start , vec3_t target , vec3_t mins , vec3_t maxs , int clipmask ,
vec3_t velocity , qboolean tracePath , int ignoreEntNum , int enemyNum ,
float minSpeed = 0 , float maxSpeed = 0 , float idealSpeed = 0 , qboolean mustHit = qfalse )
//---------------------------------------------------------
{
float targetDist , shotSpeed , speedInc = 100 , travelTime , impactDist , bestImpactDist = Q3_INFINITE ; //fireSpeed,
2013-05-01 14:52:09 +00:00
vec3_t targetDir , shotVel , failCase = { 0 , 0 , 0 } ;
2013-04-19 02:52:48 +00:00
trace_t trace ;
trajectory_t tr ;
qboolean blocked ;
int elapsedTime , skipNum , timeStep = 500 , hitCount = 0 , maxHits = 7 ;
vec3_t lastPos , testPos ;
gentity_t * traceEnt ;
if ( ! idealSpeed )
{
idealSpeed = 300 ;
}
else if ( idealSpeed < speedInc )
{
idealSpeed = speedInc ;
}
shotSpeed = idealSpeed ;
skipNum = ( idealSpeed - speedInc ) / speedInc ;
if ( ! minSpeed )
{
minSpeed = 100 ;
}
if ( ! maxSpeed )
{
maxSpeed = 900 ;
}
while ( hitCount < maxHits )
{
VectorSubtract ( target , start , targetDir ) ;
targetDist = VectorNormalize ( targetDir ) ;
VectorScale ( targetDir , shotSpeed , shotVel ) ;
travelTime = targetDist / shotSpeed ;
shotVel [ 2 ] + = travelTime * 0.5 * g_gravity - > value ;
if ( ! hitCount )
{ //save the first (ideal) one as the failCase (fallback value)
if ( ! mustHit )
{ //default is fine as a return value
VectorCopy ( shotVel , failCase ) ;
}
}
if ( tracePath )
{ //do a rough trace of the path
blocked = qfalse ;
VectorCopy ( start , tr . trBase ) ;
VectorCopy ( shotVel , tr . trDelta ) ;
tr . trType = TR_GRAVITY ;
tr . trTime = level . time ;
travelTime * = 1000.0f ;
VectorCopy ( start , lastPos ) ;
//This may be kind of wasteful, especially on long throws... use larger steps? Divide the travelTime into a certain hard number of slices? Trace just to apex and down?
for ( elapsedTime = timeStep ; elapsedTime < floor ( travelTime ) + timeStep ; elapsedTime + = timeStep )
{
if ( ( float ) elapsedTime > travelTime )
{ //cap it
elapsedTime = floor ( travelTime ) ;
}
EvaluateTrajectory ( & tr , level . time + elapsedTime , testPos ) ;
gi . trace ( & trace , lastPos , mins , maxs , testPos , ignoreEntNum , clipmask ) ;
if ( trace . allsolid | | trace . startsolid )
{
blocked = qtrue ;
break ;
}
if ( trace . fraction < 1.0f )
{ //hit something
if ( trace . entityNum = = enemyNum )
{ //hit the enemy, that's perfect!
break ;
}
else if ( trace . plane . normal [ 2 ] > 0.7 & & DistanceSquared ( trace . endpos , target ) < 4096 ) //hit within 64 of desired location, should be okay
{ //close enough!
break ;
}
else
{ //FIXME: maybe find the extents of this brush and go above or below it on next try somehow?
impactDist = DistanceSquared ( trace . endpos , target ) ;
if ( impactDist < bestImpactDist )
{
bestImpactDist = impactDist ;
VectorCopy ( shotVel , failCase ) ;
}
blocked = qtrue ;
//see if we should store this as the failCase
if ( trace . entityNum < ENTITYNUM_WORLD )
{ //hit an ent
traceEnt = & g_entities [ trace . entityNum ] ;
if ( traceEnt & & traceEnt - > takedamage & & ! OnSameTeam ( self , traceEnt ) )
{ //hit something breakable, so that's okay
//we haven't found a clear shot yet so use this as the failcase
VectorCopy ( shotVel , failCase ) ;
}
}
break ;
}
}
if ( elapsedTime = = floor ( travelTime ) )
{ //reached end, all clear
break ;
}
else
{
//all clear, try next slice
VectorCopy ( testPos , lastPos ) ;
}
}
if ( blocked )
{ //hit something, adjust speed (which will change arc)
hitCount + + ;
shotSpeed = idealSpeed + ( ( hitCount - skipNum ) * speedInc ) ; //from min to max (skipping ideal)
if ( hitCount > = skipNum )
{ //skip ideal since that was the first value we tested
shotSpeed + = speedInc ;
}
}
else
{ //made it!
break ;
}
}
else
{ //no need to check the path, go with first calc
break ;
}
}
if ( hitCount > = maxHits )
{ //NOTE: worst case scenario, use the one that impacted closest to the target (or just use the first try...?)
VectorCopy ( failCase , velocity ) ;
return qfalse ;
}
VectorCopy ( shotVel , velocity ) ;
return qtrue ;
}
//---------------------------------------------------------
void WP_ThermalThink ( gentity_t * ent )
//---------------------------------------------------------
{
int count ;
qboolean blow = qfalse ;
// Thermal detonators for the player do occasional radius checks and blow up if there are entities in the blast radius
// This is done so that the main fire is actually useful as an attack. We explode anyway after delay expires.
if ( ( ent - > s . eFlags & EF_HELD_BY_SAND_CREATURE ) )
{ //blow once creature is underground (done with anim)
//FIXME: chance of being spit out? Especially if lots of delay left...
ent - > e_TouchFunc = NULL ; //don't impact on anything
if ( ! ent - > activator
| | ! ent - > activator - > client
| | ! ent - > activator - > client - > ps . legsAnimTimer )
{ //either something happened to the sand creature or it's done with it's attack anim
//blow!
ent - > e_ThinkFunc = thinkF_thermalDetonatorExplode ;
ent - > nextthink = level . time + Q_irand ( 50 , 2000 ) ;
}
else
{ //keep checking
ent - > nextthink = level . time + TD_THINK_TIME ;
}
return ;
}
else if ( ent - > delay > level . time )
{
// Finally, we force it to bounce at least once before doing the special checks, otherwise it's just too easy for the player?
if ( ent - > has_bounced )
{
count = G_RadiusList ( ent - > currentOrigin , TD_TEST_RAD , ent , qtrue , ent_list ) ;
for ( int i = 0 ; i < count ; i + + )
{
if ( ent_list [ i ] - > s . number = = 0 )
{
// avoid deliberately blowing up next to the player, no matter how close any enemy is..
// ...if the delay time expires though, there is no saving the player...muwhaaa haa ha
blow = qfalse ;
break ;
}
else if ( ent_list [ i ] - > client
& & ent_list [ i ] - > client - > NPC_class ! = CLASS_SAND_CREATURE //ignore sand creatures
& & ent_list [ i ] - > health > 0 )
{
//FIXME! sometimes the ent_list order changes, so we should make sure that the player isn't anywhere in this list
blow = qtrue ;
}
}
}
}
else
{
// our death time has arrived, even if nothing is near us
blow = qtrue ;
}
if ( blow )
{
ent - > e_ThinkFunc = thinkF_thermalDetonatorExplode ;
ent - > nextthink = level . time + 50 ;
}
else
{
// we probably don't need to do this thinking logic very often...maybe this is fast enough?
ent - > nextthink = level . time + TD_THINK_TIME ;
}
}
//---------------------------------------------------------
gentity_t * WP_FireThermalDetonator ( gentity_t * ent , qboolean alt_fire )
//---------------------------------------------------------
{
gentity_t * bolt ;
vec3_t dir , start ;
float damageScale = 1.0f ;
VectorCopy ( forward , dir ) ;
VectorCopy ( muzzle , start ) ;
bolt = G_Spawn ( ) ;
bolt - > classname = " thermal_detonator " ;
if ( ent - > s . number ! = 0 )
{
// If not the player, cut the damage a bit so we don't get pounded on so much
damageScale = TD_NPC_DAMAGE_CUT ;
}
if ( ! alt_fire & & ent - > s . number = = 0 )
{
// Main fires for the players do a little bit of extra thinking
bolt - > e_ThinkFunc = thinkF_WP_ThermalThink ;
bolt - > nextthink = level . time + TD_THINK_TIME ;
bolt - > delay = level . time + TD_TIME ; // How long 'til she blows
}
else
{
bolt - > e_ThinkFunc = thinkF_thermalDetonatorExplode ;
bolt - > nextthink = level . time + TD_TIME ; // How long 'til she blows
}
bolt - > mass = 10 ;
// How 'bout we give this thing a size...
VectorSet ( bolt - > mins , - 4.0f , - 4.0f , - 4.0f ) ;
VectorSet ( bolt - > maxs , 4.0f , 4.0f , 4.0f ) ;
bolt - > clipmask = MASK_SHOT ;
bolt - > clipmask & = ~ CONTENTS_CORPSE ;
bolt - > contents = CONTENTS_SHOTCLIP ;
bolt - > takedamage = qtrue ;
bolt - > health = 15 ;
bolt - > e_DieFunc = dieF_thermal_die ;
WP_TraceSetStart ( ent , start , bolt - > mins , bolt - > maxs ) ; //make sure our start point isn't on the other side of a wall
float chargeAmount = 1.0f ; // default of full charge
if ( ent - > client )
{
chargeAmount = level . time - ent - > client - > ps . weaponChargeTime ;
}
// get charge amount
chargeAmount = chargeAmount / ( float ) TD_VELOCITY ;
if ( chargeAmount > 1.0f )
{
chargeAmount = 1.0f ;
}
else if ( chargeAmount < TD_MIN_CHARGE )
{
chargeAmount = TD_MIN_CHARGE ;
}
float thrownSpeed = TD_VELOCITY ;
const qboolean thisIsAShooter = ! Q_stricmp ( " misc_weapon_shooter " , ent - > classname ) ;
if ( thisIsAShooter )
{
if ( ent - > delay ! = 0 )
{
thrownSpeed = ent - > delay ;
}
}
// normal ones bounce, alt ones explode on impact
bolt - > s . pos . trType = TR_GRAVITY ;
bolt - > owner = ent ;
VectorScale ( dir , thrownSpeed * chargeAmount , bolt - > s . pos . trDelta ) ;
if ( ent - > health > 0 )
{
bolt - > s . pos . trDelta [ 2 ] + = 120 ;
if ( ( ent - > NPC | | ( ent - > s . number & & thisIsAShooter ) )
& & ent - > enemy )
{ //NPC or misc_weapon_shooter
//FIXME: we're assuming he's actually facing this direction...
vec3_t target ;
VectorCopy ( ent - > enemy - > currentOrigin , target ) ;
if ( target [ 2 ] < = start [ 2 ] )
{
vec3_t vec ;
VectorSubtract ( target , start , vec ) ;
VectorNormalize ( vec ) ;
VectorMA ( target , Q_flrand ( 0 , - 32 ) , vec , target ) ; //throw a little short
}
target [ 0 ] + = Q_flrand ( - 5 , 5 ) + ( crandom ( ) * ( 6 - ent - > NPC - > currentAim ) * 2 ) ;
target [ 1 ] + = Q_flrand ( - 5 , 5 ) + ( crandom ( ) * ( 6 - ent - > NPC - > currentAim ) * 2 ) ;
target [ 2 ] + = Q_flrand ( - 5 , 5 ) + ( crandom ( ) * ( 6 - ent - > NPC - > currentAim ) * 2 ) ;
WP_LobFire ( ent , start , target , bolt - > mins , bolt - > maxs , bolt - > clipmask , bolt - > s . pos . trDelta , qtrue , ent - > s . number , ent - > enemy - > s . number ) ;
}
else if ( thisIsAShooter & & ent - > target & & ! VectorCompare ( ent - > pos1 , vec3_origin ) )
{ //misc_weapon_shooter firing at a position
WP_LobFire ( ent , start , ent - > pos1 , bolt - > mins , bolt - > maxs , bolt - > clipmask , bolt - > s . pos . trDelta , qtrue , ent - > s . number , ent - > enemy - > s . number ) ;
}
}
if ( alt_fire )
{
bolt - > alt_fire = qtrue ;
}
else
{
bolt - > s . eFlags | = EF_BOUNCE_HALF ;
}
bolt - > s . loopSound = G_SoundIndex ( " sound/weapons/thermal/thermloop.wav " ) ;
bolt - > damage = TD_DAMAGE * damageScale ;
bolt - > dflags = 0 ;
bolt - > splashDamage = TD_SPLASH_DAM * damageScale ;
bolt - > splashRadius = TD_SPLASH_RAD ;
bolt - > s . eType = ET_MISSILE ;
bolt - > svFlags = SVF_USE_CURRENT_ORIGIN ;
bolt - > s . weapon = WP_THERMAL ;
if ( alt_fire )
{
bolt - > methodOfDeath = MOD_THERMAL_ALT ;
bolt - > splashMethodOfDeath = MOD_THERMAL_ALT ; //? SPLASH;
}
else
{
bolt - > methodOfDeath = MOD_THERMAL ;
bolt - > splashMethodOfDeath = MOD_THERMAL ; //? SPLASH;
}
bolt - > s . pos . trTime = level . time ; // move a bit on the very first frame
VectorCopy ( start , bolt - > s . pos . trBase ) ;
SnapVector ( bolt - > s . pos . trDelta ) ; // save net bandwidth
VectorCopy ( start , bolt - > currentOrigin ) ;
VectorCopy ( start , bolt - > pos2 ) ;
return bolt ;
}
//---------------------------------------------------------
gentity_t * WP_DropThermal ( gentity_t * ent )
//---------------------------------------------------------
{
AngleVectors ( ent - > client - > ps . viewangles , forward , vright , up ) ;
CalcEntitySpot ( ent , SPOT_WEAPON , muzzle ) ;
return ( WP_FireThermalDetonator ( ent , qfalse ) ) ;
}
// Bot Laser
//---------------------------------------------------------
void WP_BotLaser ( gentity_t * ent )
//---------------------------------------------------------
{
gentity_t * missile = CreateMissile ( muzzle , forward , BRYAR_PISTOL_VEL , 10000 , ent ) ;
missile - > classname = " bryar_proj " ;
missile - > s . weapon = WP_BRYAR_PISTOL ;
missile - > damage = BRYAR_PISTOL_DAMAGE ;
missile - > dflags = DAMAGE_DEATH_KNOCKBACK ;
missile - > methodOfDeath = MOD_ENERGY ;
missile - > clipmask = MASK_SHOT | CONTENTS_LIGHTSABER ;
}
// Emplaced Gun
//---------------------------------------------------------
void WP_EmplacedFire ( gentity_t * ent )
//---------------------------------------------------------
{
float damage = EMPLACED_DAMAGE * ( ent - > NPC ? 0.1f : 1.0f ) ;
float vel = EMPLACED_VEL * ( ent - > NPC ? 0.4f : 1.0f ) ;
WP_MissileTargetHint ( ent , muzzle , forward ) ;
gentity_t * missile = CreateMissile ( muzzle , forward , vel , 10000 , ent ) ;
missile - > classname = " emplaced_proj " ;
missile - > s . weapon = WP_EMPLACED_GUN ;
missile - > damage = damage ;
missile - > dflags = DAMAGE_DEATH_KNOCKBACK | DAMAGE_HEAVY_WEAP_CLASS ;
missile - > methodOfDeath = MOD_EMPLACED ;
missile - > clipmask = MASK_SHOT | CONTENTS_LIGHTSABER ;
// do some weird switchery on who the real owner is, we do this so the projectiles don't hit the gun object
if ( ent & & ent - > client & & ! ( ent - > client - > ps . eFlags & EF_LOCKED_TO_WEAPON ) )
{
missile - > owner = ent ;
}
else
{
missile - > owner = ent - > owner ;
}
if ( missile - > owner - > e_UseFunc = = useF_eweb_use )
{
missile - > alt_fire = qtrue ;
}
VectorSet ( missile - > maxs , EMPLACED_SIZE , EMPLACED_SIZE , EMPLACED_SIZE ) ;
VectorScale ( missile - > maxs , - 1 , missile - > mins ) ;
// alternate muzzles
ent - > fxID = ! ent - > fxID ;
}
// ATST Main
//---------------------------------------------------------
void WP_ATSTMainFire ( gentity_t * ent )
//---------------------------------------------------------
{
float vel = ATST_MAIN_VEL ;
// if ( ent->client && (ent->client->ps.eFlags & EF_IN_ATST ))
// {
// vel = 4500.0f;
// }
if ( ! ent - > s . number )
{
// player shoots faster
vel * = 1.6f ;
}
WP_MissileTargetHint ( ent , muzzle , forward ) ;
gentity_t * missile = CreateMissile ( muzzle , forward , vel , 10000 , ent ) ;
missile - > classname = " atst_main_proj " ;
missile - > s . weapon = WP_ATST_MAIN ;
missile - > damage = ATST_MAIN_DAMAGE ;
missile - > dflags = DAMAGE_DEATH_KNOCKBACK | DAMAGE_HEAVY_WEAP_CLASS ;
missile - > methodOfDeath = MOD_ENERGY ;
missile - > clipmask = MASK_SHOT | CONTENTS_LIGHTSABER ;
missile - > owner = ent ;
VectorSet ( missile - > maxs , ATST_MAIN_SIZE , ATST_MAIN_SIZE , ATST_MAIN_SIZE ) ;
VectorScale ( missile - > maxs , - 1 , missile - > mins ) ;
}
// ATST Alt Side
//---------------------------------------------------------
void WP_ATSTSideAltFire ( gentity_t * ent )
//---------------------------------------------------------
{
int damage = ATST_SIDE_ALT_DAMAGE ;
float vel = ATST_SIDE_ALT_NPC_VELOCITY ;
if ( ent - > client & & ( ent - > client - > ps . eFlags & EF_IN_ATST ) )
{
vel = ATST_SIDE_ALT_VELOCITY ;
}
gentity_t * missile = CreateMissile ( muzzle , forward , vel , 10000 , ent , qtrue ) ;
missile - > classname = " atst_rocket " ;
missile - > s . weapon = WP_ATST_SIDE ;
missile - > mass = 10 ;
// Do the damages
if ( ent - > s . number ! = 0 )
{
if ( g_spskill - > integer = = 0 )
{
damage = ATST_SIDE_ROCKET_NPC_DAMAGE_EASY ;
}
else if ( g_spskill - > integer = = 1 )
{
damage = ATST_SIDE_ROCKET_NPC_DAMAGE_NORMAL ;
}
else
{
damage = ATST_SIDE_ROCKET_NPC_DAMAGE_HARD ;
}
}
VectorCopy ( forward , missile - > movedir ) ;
// Make it easier to hit things
VectorSet ( missile - > maxs , ATST_SIDE_ALT_ROCKET_SIZE , ATST_SIDE_ALT_ROCKET_SIZE , ATST_SIDE_ALT_ROCKET_SIZE ) ;
VectorScale ( missile - > maxs , - 1 , missile - > mins ) ;
missile - > damage = damage ;
missile - > dflags = DAMAGE_DEATH_KNOCKBACK | DAMAGE_HEAVY_WEAP_CLASS ;
missile - > methodOfDeath = MOD_EXPLOSIVE ;
missile - > splashMethodOfDeath = MOD_EXPLOSIVE_SPLASH ;
missile - > clipmask = MASK_SHOT | CONTENTS_LIGHTSABER ;
// Scale damage down a bit if it is coming from an NPC
missile - > splashDamage = ATST_SIDE_ALT_SPLASH_DAMAGE * ( ent - > s . number = = 0 ? 1.0f : ATST_SIDE_ALT_ROCKET_SPLASH_SCALE ) ;
missile - > splashRadius = ATST_SIDE_ALT_SPLASH_RADIUS ;
// we don't want it to ever bounce
missile - > bounceCount = 0 ;
}
// ATST Side
//---------------------------------------------------------
void WP_ATSTSideFire ( gentity_t * ent )
//---------------------------------------------------------
{
int damage = ATST_SIDE_MAIN_DAMAGE ;
gentity_t * missile = CreateMissile ( muzzle , forward , ATST_SIDE_MAIN_VELOCITY , 10000 , ent , qfalse ) ;
missile - > classname = " atst_side_proj " ;
missile - > s . weapon = WP_ATST_SIDE ;
// Do the damages
if ( ent - > s . number ! = 0 )
{
if ( g_spskill - > integer = = 0 )
{
damage = ATST_SIDE_MAIN_NPC_DAMAGE_EASY ;
}
else if ( g_spskill - > integer = = 1 )
{
damage = ATST_SIDE_MAIN_NPC_DAMAGE_NORMAL ;
}
else
{
damage = ATST_SIDE_MAIN_NPC_DAMAGE_HARD ;
}
}
VectorSet ( missile - > maxs , ATST_SIDE_MAIN_SIZE , ATST_SIDE_MAIN_SIZE , ATST_SIDE_MAIN_SIZE ) ;
VectorScale ( missile - > maxs , - 1 , missile - > mins ) ;
missile - > damage = damage ;
missile - > dflags = DAMAGE_DEATH_KNOCKBACK | DAMAGE_HEAVY_WEAP_CLASS ;
missile - > methodOfDeath = MOD_ENERGY ;
missile - > clipmask = MASK_SHOT | CONTENTS_LIGHTSABER ;
missile - > splashDamage = ATST_SIDE_MAIN_SPLASH_DAMAGE * ( ent - > s . number = = 0 ? 1.0f : 0.6f ) ;
missile - > splashRadius = ATST_SIDE_MAIN_SPLASH_RADIUS ;
// we don't want it to bounce
missile - > bounceCount = 0 ;
}
//---------------------------------------------------------
void WP_FireStunBaton ( gentity_t * ent , qboolean alt_fire )
{
gentity_t * tr_ent ;
trace_t tr ;
vec3_t mins , maxs , end , start ;
G_Sound ( ent , G_SoundIndex ( " sound/weapons/baton/fire " ) ) ;
# ifdef _IMMERSION
G_Force ( ent , G_ForceIndex ( " fffx/weapons/baton/fire " , FF_CHANNEL_WEAPON ) ) ;
# endif // _IMMERSION
VectorCopy ( muzzle , start ) ;
WP_TraceSetStart ( ent , start , vec3_origin , vec3_origin ) ;
VectorMA ( start , STUN_BATON_RANGE , forward , end ) ;
VectorSet ( maxs , 5 , 5 , 5 ) ;
VectorScale ( maxs , - 1 , mins ) ;
gi . trace ( & tr , start , mins , maxs , end , ent - > s . number , CONTENTS_SOLID | CONTENTS_BODY | CONTENTS_SHOTCLIP ) ;
if ( tr . entityNum > = ENTITYNUM_WORLD | | tr . entityNum < 0 )
{
return ;
}
tr_ent = & g_entities [ tr . entityNum ] ;
if ( tr_ent & & tr_ent - > takedamage & & tr_ent - > client )
{
G_PlayEffect ( " stunBaton/flesh_impact " , tr . endpos , tr . plane . normal ) ;
// TEMP!
// G_Sound( tr_ent, G_SoundIndex( va("sound/weapons/melee/punch%d", Q_irand(1, 4)) ) );
tr_ent - > client - > ps . powerups [ PW_SHOCKED ] = level . time + 1500 ;
G_Damage ( tr_ent , ent , ent , forward , tr . endpos , STUN_BATON_DAMAGE , DAMAGE_NO_KNOCKBACK , MOD_MELEE ) ;
}
else if ( tr_ent - > svFlags & SVF_GLASS_BRUSH | | ( tr_ent - > svFlags & SVF_BBRUSH & & tr_ent - > material = = 12 ) ) // material grate...we are breaking a grate!
{
G_Damage ( tr_ent , ent , ent , forward , tr . endpos , 999 , DAMAGE_NO_KNOCKBACK , MOD_MELEE ) ; // smash that puppy
}
}
void WP_Melee ( gentity_t * ent )
//---------------------------------------------------------
{
gentity_t * tr_ent ;
trace_t tr ;
vec3_t mins , maxs , end ;
int damage = ent - > s . number ? ( g_spskill - > integer * 2 ) + 1 : 3 ;
float range = ent - > s . number ? 64 : 32 ;
VectorMA ( muzzle , range , forward , end ) ;
VectorSet ( maxs , 6 , 6 , 6 ) ;
VectorScale ( maxs , - 1 , mins ) ;
gi . trace ( & tr , muzzle , mins , maxs , end , ent - > s . number , MASK_SHOT ) ;
if ( tr . entityNum > = ENTITYNUM_WORLD )
{
return ;
}
tr_ent = & g_entities [ tr . entityNum ] ;
if ( ent - > client & & ! PM_DroidMelee ( ent - > client - > NPC_class ) )
{
if ( ent - > s . number | | ent - > alt_fire )
{
damage * = Q_irand ( 2 , 3 ) ;
}
else
{
damage * = Q_irand ( 1 , 2 ) ;
}
}
if ( tr_ent & & tr_ent - > takedamage )
{
int dflags = DAMAGE_NO_KNOCKBACK ;
G_PlayEffect ( G_EffectIndex ( " melee/punch_impact " ) , tr . endpos , forward ) ;
//G_Sound( tr_ent, G_SoundIndex( va("sound/weapons/melee/punch%d", Q_irand(1, 4)) ) );
if ( ent - > NPC & & ( ent - > NPC - > aiFlags & NPCAI_HEAVY_MELEE ) )
{ //4x damage for heavy melee class
damage * = 4 ;
dflags & = ~ DAMAGE_NO_KNOCKBACK ;
dflags | = DAMAGE_DISMEMBER ;
}
G_Damage ( tr_ent , ent , ent , forward , tr . endpos , damage , dflags , MOD_MELEE ) ;
}
}
//---------------------------------------------------------
static void WP_FireTuskenRifle ( gentity_t * ent )
//---------------------------------------------------------
{
vec3_t start ;
VectorCopy ( muzzle , start ) ;
WP_TraceSetStart ( ent , start , vec3_origin , vec3_origin ) ; //make sure our start point isn't on the other side of a wall
if ( ! ( ent - > client - > ps . forcePowersActive & ( 1 < < FP_SEE ) )
| | ent - > client - > ps . forcePowerLevel [ FP_SEE ] < FORCE_LEVEL_2 )
{ //force sight 2+ gives perfect aim
//FIXME: maybe force sight level 3 autoaims some?
if ( ent - > NPC & & ent - > NPC - > currentAim < 5 )
{
vec3_t angs ;
vectoangles ( forward , angs ) ;
if ( ent - > client - > NPC_class = = CLASS_IMPWORKER )
{ //*sigh*, hack to make impworkers less accurate without affecteing imperial officer accuracy
angs [ PITCH ] + = ( crandom ( ) * ( BLASTER_NPC_SPREAD + ( 6 - ent - > NPC - > currentAim ) * 0.25f ) ) ; //was 0.5f
angs [ YAW ] + = ( crandom ( ) * ( BLASTER_NPC_SPREAD + ( 6 - ent - > NPC - > currentAim ) * 0.25f ) ) ; //was 0.5f
}
else
{
angs [ PITCH ] + = ( crandom ( ) * ( ( 5 - ent - > NPC - > currentAim ) * 0.25f ) ) ;
angs [ YAW ] + = ( crandom ( ) * ( ( 5 - ent - > NPC - > currentAim ) * 0.25f ) ) ;
}
AngleVectors ( angs , forward , NULL , NULL ) ;
}
}
WP_MissileTargetHint ( ent , start , forward ) ;
gentity_t * missile = CreateMissile ( start , forward , TUSKEN_RIFLE_VEL , 10000 , ent , qfalse ) ;
missile - > classname = " trifle_proj " ;
missile - > s . weapon = WP_TUSKEN_RIFLE ;
if ( ent - > s . number < MAX_CLIENTS | | g_spskill - > integer > = 2 )
{
missile - > damage = TUSKEN_RIFLE_DAMAGE_HARD ;
}
else if ( g_spskill - > integer > 0 )
{
missile - > damage = TUSKEN_RIFLE_DAMAGE_MEDIUM ;
}
else
{
missile - > damage = TUSKEN_RIFLE_DAMAGE_EASY ;
}
missile - > dflags = DAMAGE_DEATH_KNOCKBACK ;
missile - > methodOfDeath = MOD_BRYAR ; //???
missile - > clipmask = MASK_SHOT | CONTENTS_LIGHTSABER ;
// we don't want it to bounce forever
missile - > bounceCount = 8 ;
}
//---------------------------------------------------------
static void WP_FireNoghriStick ( gentity_t * ent )
//---------------------------------------------------------
{
vec3_t dir , angs ;
vectoangles ( forward , angs ) ;
if ( ! ( ent - > client - > ps . forcePowersActive & ( 1 < < FP_SEE ) )
| | ent - > client - > ps . forcePowerLevel [ FP_SEE ] < FORCE_LEVEL_2 )
{ //force sight 2+ gives perfect aim
//FIXME: maybe force sight level 3 autoaims some?
// add some slop to the main-fire direction
angs [ PITCH ] + = ( crandom ( ) * ( BLASTER_NPC_SPREAD + ( 6 - ent - > NPC - > currentAim ) * 0.25f ) ) ; //was 0.5f
angs [ YAW ] + = ( crandom ( ) * ( BLASTER_NPC_SPREAD + ( 6 - ent - > NPC - > currentAim ) * 0.25f ) ) ; //was 0.5f
}
AngleVectors ( angs , dir , NULL , NULL ) ;
// FIXME: if temp_org does not have clear trace to inside the bbox, don't shoot!
int velocity = 1200 ;
WP_TraceSetStart ( ent , muzzle , vec3_origin , vec3_origin ) ; //make sure our start point isn't on the other side of a wall
WP_MissileTargetHint ( ent , muzzle , dir ) ;
gentity_t * missile = CreateMissile ( muzzle , dir , velocity , 10000 , ent , qfalse ) ;
missile - > classname = " noghri_proj " ;
missile - > s . weapon = WP_NOGHRI_STICK ;
// Do the damages
if ( ent - > s . number ! = 0 )
{
if ( g_spskill - > integer = = 0 )
{
missile - > damage = 1 ;
}
else if ( g_spskill - > integer = = 1 )
{
missile - > damage = 5 ;
}
else
{
missile - > damage = 10 ;
}
}
// if ( ent->client )
// {
// if ( ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time )
// {
// // in overcharge mode, so doing double damage
// missile->flags |= FL_OVERCHARGED;
// damage *= 2;
// }
// }
missile - > dflags = DAMAGE_NO_KNOCKBACK ;
missile - > methodOfDeath = MOD_BLASTER ;
missile - > clipmask = MASK_SHOT | CONTENTS_LIGHTSABER ;
missile - > splashDamage = 0 ;
missile - > splashRadius = 100 ;
missile - > splashMethodOfDeath = MOD_GAS ;
//Hmm: maybe spew gas on impact?
}
//---------------------------------------------------------
void AddLeanOfs ( const gentity_t * const ent , vec3_t point )
//---------------------------------------------------------
{
if ( ent - > client )
{
if ( ent - > client - > ps . leanofs )
{
vec3_t right ;
//add leaning offset
AngleVectors ( ent - > client - > ps . viewangles , NULL , right , NULL ) ;
VectorMA ( point , ( float ) ent - > client - > ps . leanofs , right , point ) ;
}
}
}
//---------------------------------------------------------
void SubtractLeanOfs ( const gentity_t * const ent , vec3_t point )
//---------------------------------------------------------
{
if ( ent - > client )
{
if ( ent - > client - > ps . leanofs )
{
vec3_t right ;
//add leaning offset
AngleVectors ( ent - > client - > ps . viewangles , NULL , right , NULL ) ;
VectorMA ( point , ent - > client - > ps . leanofs * - 1 , right , point ) ;
}
}
}
//---------------------------------------------------------
void ViewHeightFix ( const gentity_t * const ent )
//---------------------------------------------------------
{
//FIXME: this is hacky and doesn't need to be here. Was only put here to make up
//for the times a crouch anim would be used but not actually crouching.
//When we start calcing eyepos (SPOT_HEAD) from the tag_eyes, we won't need
//this (or viewheight at all?)
if ( ! ent )
return ;
if ( ! ent - > client | | ! ent - > NPC )
return ;
if ( ent - > client - > ps . stats [ STAT_HEALTH ] < = 0 )
return ; //dead
if ( ent - > client - > ps . legsAnim = = BOTH_CROUCH1IDLE | | ent - > client - > ps . legsAnim = = BOTH_CROUCH1 | | ent - > client - > ps . legsAnim = = BOTH_CROUCH1WALK )
{
if ( ent - > client - > ps . viewheight ! = ent - > client - > crouchheight + STANDARD_VIEWHEIGHT_OFFSET )
ent - > client - > ps . viewheight = ent - > client - > crouchheight + STANDARD_VIEWHEIGHT_OFFSET ;
}
else
{
if ( ent - > client - > ps . viewheight ! = ent - > client - > standheight + STANDARD_VIEWHEIGHT_OFFSET )
ent - > client - > ps . viewheight = ent - > client - > standheight + STANDARD_VIEWHEIGHT_OFFSET ;
}
}
qboolean W_AccuracyLoggableWeapon ( int weapon , qboolean alt_fire , int mod )
{
if ( mod ! = MOD_UNKNOWN )
{
switch ( mod )
{
//standard weapons
case MOD_BRYAR :
case MOD_BRYAR_ALT :
case MOD_BLASTER :
case MOD_BLASTER_ALT :
case MOD_DISRUPTOR :
case MOD_SNIPER :
case MOD_BOWCASTER :
case MOD_BOWCASTER_ALT :
case MOD_ROCKET :
case MOD_ROCKET_ALT :
case MOD_CONC :
case MOD_CONC_ALT :
return qtrue ;
break ;
//non-alt standard
case MOD_REPEATER :
case MOD_DEMP2 :
case MOD_FLECHETTE :
return qtrue ;
break ;
//emplaced gun
case MOD_EMPLACED :
return qtrue ;
break ;
//atst
case MOD_ENERGY :
case MOD_EXPLOSIVE :
if ( weapon = = WP_ATST_MAIN | | weapon = = WP_ATST_SIDE )
{
return qtrue ;
}
break ;
}
}
else if ( weapon ! = WP_NONE )
{
switch ( weapon )
{
case WP_BRYAR_PISTOL :
case WP_BLASTER_PISTOL :
case WP_BLASTER :
case WP_DISRUPTOR :
case WP_BOWCASTER :
case WP_ROCKET_LAUNCHER :
case WP_CONCUSSION :
return qtrue ;
break ;
//non-alt standard
case WP_REPEATER :
case WP_DEMP2 :
case WP_FLECHETTE :
if ( ! alt_fire )
{
return qtrue ;
}
break ;
//emplaced gun
case WP_EMPLACED_GUN :
return qtrue ;
break ;
//atst
case WP_ATST_MAIN :
case WP_ATST_SIDE :
return qtrue ;
break ;
}
}
return qfalse ;
}
/*
= = = = = = = = = = = = = = =
LogAccuracyHit
= = = = = = = = = = = = = = =
*/
qboolean LogAccuracyHit ( gentity_t * target , gentity_t * attacker ) {
if ( ! target - > takedamage ) {
return qfalse ;
}
if ( target = = attacker ) {
return qfalse ;
}
if ( ! target - > client ) {
return qfalse ;
}
if ( ! attacker - > client ) {
return qfalse ;
}
if ( target - > client - > ps . stats [ STAT_HEALTH ] < = 0 ) {
return qfalse ;
}
if ( OnSameTeam ( target , attacker ) ) {
return qfalse ;
}
return qtrue ;
}
//---------------------------------------------------------
void CalcMuzzlePoint ( gentity_t * const ent , vec3_t forward , vec3_t right , vec3_t up , vec3_t muzzlePoint , float lead_in )
//---------------------------------------------------------
{
vec3_t org ;
mdxaBone_t boltMatrix ;
if ( ! lead_in ) //&& ent->s.number != 0
{ //Not players or melee
if ( ent - > client )
{
if ( ent - > client - > renderInfo . mPCalcTime > = level . time - FRAMETIME * 2 )
{ //Our muzz point was calced no more than 2 frames ago
VectorCopy ( ent - > client - > renderInfo . muzzlePoint , muzzlePoint ) ;
return ;
}
}
}
VectorCopy ( ent - > currentOrigin , muzzlePoint ) ;
switch ( ent - > s . weapon )
{
case WP_BRYAR_PISTOL :
case WP_BLASTER_PISTOL :
ViewHeightFix ( ent ) ;
muzzlePoint [ 2 ] + = ent - > client - > ps . viewheight ; //By eyes
muzzlePoint [ 2 ] - = 16 ;
VectorMA ( muzzlePoint , 28 , forward , muzzlePoint ) ;
VectorMA ( muzzlePoint , 6 , vright , muzzlePoint ) ;
break ;
case WP_ROCKET_LAUNCHER :
case WP_CONCUSSION :
case WP_THERMAL :
ViewHeightFix ( ent ) ;
muzzlePoint [ 2 ] + = ent - > client - > ps . viewheight ; //By eyes
muzzlePoint [ 2 ] - = 2 ;
break ;
case WP_BLASTER :
ViewHeightFix ( ent ) ;
muzzlePoint [ 2 ] + = ent - > client - > ps . viewheight ; //By eyes
muzzlePoint [ 2 ] - = 1 ;
if ( ent - > s . number = = 0 )
VectorMA ( muzzlePoint , 12 , forward , muzzlePoint ) ; // player, don't set this any lower otherwise the projectile will impact immediately when your back is to a wall
else
VectorMA ( muzzlePoint , 2 , forward , muzzlePoint ) ; // NPC, don't set too far forward otherwise the projectile can go through doors
VectorMA ( muzzlePoint , 1 , vright , muzzlePoint ) ;
break ;
case WP_SABER :
if ( ent - > NPC ! = NULL & &
( ent - > client - > ps . torsoAnim = = TORSO_WEAPONREADY2 | |
ent - > client - > ps . torsoAnim = = BOTH_ATTACK2 ) ) //Sniper pose
{
ViewHeightFix ( ent ) ;
muzzle [ 2 ] + = ent - > client - > ps . viewheight ; //By eyes
}
else
{
muzzlePoint [ 2 ] + = 16 ;
}
VectorMA ( muzzlePoint , 8 , forward , muzzlePoint ) ;
VectorMA ( muzzlePoint , 16 , vright , muzzlePoint ) ;
break ;
case WP_BOT_LASER :
muzzlePoint [ 2 ] - = 16 ; //
break ;
case WP_ATST_MAIN :
if ( ent - > count > 0 )
{
ent - > count = 0 ;
gi . G2API_GetBoltMatrix ( ent - > ghoul2 , ent - > playerModel ,
ent - > handLBolt ,
& boltMatrix , ent - > s . angles , ent - > s . origin , ( cg . time ? cg . time : level . time ) ,
NULL , ent - > s . modelScale ) ;
}
else
{
ent - > count = 1 ;
gi . G2API_GetBoltMatrix ( ent - > ghoul2 , ent - > playerModel ,
ent - > handRBolt ,
& boltMatrix , ent - > s . angles , ent - > s . origin , ( cg . time ? cg . time : level . time ) ,
NULL , ent - > s . modelScale ) ;
}
gi . G2API_GiveMeVectorFromMatrix ( boltMatrix , ORIGIN , org ) ;
VectorCopy ( org , muzzlePoint ) ;
break ;
}
AddLeanOfs ( ent , muzzlePoint ) ;
}
// Muzzle point table...
vec3_t WP_MuzzlePoint [ WP_NUM_WEAPONS ] =
{ // Fwd, right, up.
{ 0 , 0 , 0 } , // WP_NONE,
{ 8 , 16 , 0 } , // WP_SABER,
{ 12 , 6 , - 6 } , // WP_BLASTER_PISTOL,
{ 12 , 6 , - 6 } , // WP_BLASTER,
{ 12 , 6 , - 6 } , // WP_DISRUPTOR,
{ 12 , 2 , - 6 } , // WP_BOWCASTER,
{ 12 , 4.5 , - 6 } , // WP_REPEATER,
{ 12 , 6 , - 6 } , // WP_DEMP2,
{ 12 , 6 , - 6 } , // WP_FLECHETTE,
{ 12 , 8 , - 4 } , // WP_ROCKET_LAUNCHER,
{ 12 , 0 , - 4 } , // WP_THERMAL,
{ 12 , 0 , - 10 } , // WP_TRIP_MINE,
{ 12 , 0 , - 4 } , // WP_DET_PACK,
{ 12 , 8 , - 4 } , // WP_CONCUSSION,
{ 0 , 8 , 0 } , // WP_MELEE,
{ 0 , 0 , 0 } , // WP_ATST_MAIN,
{ 0 , 0 , 0 } , // WP_ATST_SIDE,
{ 0 , 8 , 0 } , // WP_STUN_BATON,
{ 12 , 6 , - 6 } , // WP_BRYAR_PISTOL,
} ;
void WP_RocketLock ( gentity_t * ent , float lockDist )
{
// Not really a charge weapon, but we still want to delay fire until the button comes up so that we can
// implement our alt-fire locking stuff
vec3_t ang ;
trace_t tr ;
vec3_t muzzleOffPoint , muzzlePoint , forward , right , up ;
AngleVectors ( ent - > client - > ps . viewangles , forward , right , up ) ;
AngleVectors ( ent - > client - > ps . viewangles , ang , NULL , NULL ) ;
VectorCopy ( ent - > client - > ps . origin , muzzlePoint ) ;
VectorCopy ( WP_MuzzlePoint [ WP_ROCKET_LAUNCHER ] , muzzleOffPoint ) ;
VectorMA ( muzzlePoint , muzzleOffPoint [ 0 ] , forward , muzzlePoint ) ;
VectorMA ( muzzlePoint , muzzleOffPoint [ 1 ] , right , muzzlePoint ) ;
muzzlePoint [ 2 ] + = ent - > client - > ps . viewheight + muzzleOffPoint [ 2 ] ;
ang [ 0 ] = muzzlePoint [ 0 ] + ang [ 0 ] * lockDist ;
ang [ 1 ] = muzzlePoint [ 1 ] + ang [ 1 ] * lockDist ;
ang [ 2 ] = muzzlePoint [ 2 ] + ang [ 2 ] * lockDist ;
gi . trace ( & tr , muzzlePoint , NULL , NULL , ang , ent - > client - > ps . clientNum , MASK_PLAYERSOLID ) ;
if ( tr . fraction ! = 1 & & tr . entityNum < ENTITYNUM_NONE & & tr . entityNum ! = ent - > client - > ps . clientNum )
{
gentity_t * bgEnt = & g_entities [ tr . entityNum ] ;
if ( bgEnt & & ( bgEnt - > s . powerups & PW_CLOAKED ) )
{
ent - > client - > rocketLockIndex = ENTITYNUM_NONE ;
ent - > client - > rocketLockTime = 0 ;
}
else if ( bgEnt & & bgEnt - > s . eType = = ET_PLAYER )
{
if ( ent - > client - > rocketLockIndex = = ENTITYNUM_NONE )
{
ent - > client - > rocketLockIndex = tr . entityNum ;
ent - > client - > rocketLockTime = level . time ;
}
else if ( ent - > client - > rocketLockIndex ! = tr . entityNum & & ent - > client - > rocketTargetTime < level . time )
{
ent - > client - > rocketLockIndex = tr . entityNum ;
ent - > client - > rocketLockTime = level . time ;
}
else if ( ent - > client - > rocketLockIndex = = tr . entityNum )
{
if ( ent - > client - > rocketLockTime = = - 1 )
{
ent - > client - > rocketLockTime = ent - > client - > rocketLastValidTime ;
}
}
if ( ent - > client - > rocketLockIndex = = tr . entityNum )
{
ent - > client - > rocketTargetTime = level . time + 500 ;
}
}
}
else if ( ent - > client - > rocketTargetTime < level . time )
{
ent - > client - > rocketLockIndex = ENTITYNUM_NONE ;
ent - > client - > rocketLockTime = 0 ;
}
else
{
if ( ent - > client - > rocketLockTime ! = - 1 )
{
ent - > client - > rocketLastValidTime = ent - > client - > rocketLockTime ;
}
ent - > client - > rocketLockTime = - 1 ;
}
}
# define VEH_HOMING_MISSILE_THINK_TIME 100
void WP_FireVehicleWeapon ( gentity_t * ent , vec3_t start , vec3_t dir , vehWeaponInfo_t * vehWeapon )
{
if ( ! vehWeapon )
{ //invalid vehicle weapon
return ;
}
else if ( vehWeapon - > bIsProjectile )
{ //projectile entity
gentity_t * missile ;
vec3_t mins , maxs ;
VectorSet ( maxs , vehWeapon - > fWidth / 2.0f , vehWeapon - > fWidth / 2.0f , vehWeapon - > fHeight / 2.0f ) ;
VectorScale ( maxs , - 1 , mins ) ;
//make sure our start point isn't on the other side of a wall
WP_TraceSetStart ( ent , start , mins , maxs ) ;
//QUERY: alt_fire true or not? Does it matter?
missile = CreateMissile ( start , dir , vehWeapon - > fSpeed , 10000 , ent , qfalse ) ;
if ( vehWeapon - > bHasGravity )
{ //TESTME: is this all we need to do?
missile - > s . pos . trType = TR_GRAVITY ;
}
missile - > classname = " vehicle_proj " ;
missile - > damage = vehWeapon - > iDamage ;
missile - > splashDamage = vehWeapon - > iSplashDamage ;
missile - > splashRadius = vehWeapon - > fSplashRadius ;
// HUGE HORRIBLE HACK
if ( ent - > owner & & ent - > owner - > s . number = = 0 )
{
missile - > damage * = 20.0f ;
missile - > splashDamage * = 20.0f ;
missile - > splashRadius * = 20.0f ;
}
//FIXME: externalize some of these properties?
missile - > dflags = DAMAGE_DEATH_KNOCKBACK ;
missile - > clipmask = MASK_SHOT ;
//Maybe by checking flags...?
if ( vehWeapon - > bSaberBlockable )
{
missile - > clipmask | = CONTENTS_LIGHTSABER ;
}
/*
if ( ( vehWeapon - > iFlags & VWF_KNOCKBACK ) )
{
missile - > dflags & = ~ DAMAGE_DEATH_KNOCKBACK ;
}
if ( ( vehWeapon - > iFlags & VWF_DISTORTION_TRAIL ) )
{
missile - > s . eFlags | = EF_DISTORTION_TRAIL ;
}
if ( ( vehWeapon - > iFlags & VWF_RADAR ) )
{
missile - > s . eFlags | = EF_RADAROBJECT ;
}
*/
missile - > s . weapon = WP_BLASTER ; //does this really matter?
// Make it easier to hit things
VectorCopy ( mins , missile - > mins ) ;
VectorCopy ( maxs , missile - > maxs ) ;
//some slightly different stuff for things with bboxes
if ( vehWeapon - > fWidth | | vehWeapon - > fHeight )
{ //we assume it's a rocket-like thing
missile - > methodOfDeath = MOD_ROCKET ;
missile - > splashMethodOfDeath = MOD_ROCKET ; // ?SPLASH;
// we don't want it to ever bounce
missile - > bounceCount = 0 ;
missile - > mass = 10 ;
}
else
{ //a blaster-laser-like thing
missile - > s . weapon = WP_BLASTER ; //does this really matter?
missile - > methodOfDeath = MOD_EMPLACED ; //MOD_TURBLAST; //count as a heavy weap
missile - > splashMethodOfDeath = MOD_EMPLACED ; //MOD_TURBLAST;// ?SPLASH;
// we don't want it to bounce forever
missile - > bounceCount = 8 ;
}
if ( vehWeapon - > iHealth )
{ //the missile can take damage
missile - > health = vehWeapon - > iHealth ;
missile - > takedamage = qtrue ;
missile - > contents = MASK_SHOT ;
missile - > e_DieFunc = dieF_WP_ExplosiveDie ; //dieF_RocketDie;
}
//set veh as cgame side owner for purpose of fx overrides
if ( ent - > m_pVehicle & & ent - > m_pVehicle - > m_pPilot )
{
missile - > owner = ent - > m_pVehicle - > m_pPilot ;
}
else
{
missile - > owner = ent ;
}
missile - > s . otherEntityNum = ent - > s . number ;
if ( vehWeapon - > iLifeTime )
{ //expire after a time
if ( vehWeapon - > bExplodeOnExpire )
{ //blow up when your lifetime is up
missile - > e_ThinkFunc = thinkF_WP_Explode ; //FIXME: custom func?
}
else
{ //just remove yourself
missile - > e_ThinkFunc = thinkF_G_FreeEntity ; //FIXME: custom func?
}
missile - > nextthink = level . time + vehWeapon - > iLifeTime ;
}
if ( vehWeapon - > fHoming )
{ //homing missile
//crap, we need to set up the homing stuff like it is in MP...
WP_RocketLock ( ent , 16384 ) ;
if ( ent - > client & & ent - > client - > rocketLockIndex ! = ENTITYNUM_NONE )
{
int dif = 0 ;
float rTime ;
rTime = ent - > client - > rocketLockTime ;
if ( rTime = = - 1 )
{
rTime = ent - > client - > rocketLastValidTime ;
}
if ( ! vehWeapon - > iLockOnTime )
{ //no minimum lock-on time
dif = 10 ; //guaranteed lock-on
}
else
{
float lockTimeInterval = vehWeapon - > iLockOnTime / 16.0f ;
dif = ( level . time - rTime ) / lockTimeInterval ;
}
if ( dif < 0 )
{
dif = 0 ;
}
//It's 10 even though it locks client-side at 8, because we want them to have a sturdy lock first, and because there's a slight difference in time between server and client
if ( dif > = 10 & & rTime ! = - 1 )
{
missile - > enemy = & g_entities [ ent - > client - > rocketLockIndex ] ;
if ( missile - > enemy & & missile - > enemy - > client & & missile - > enemy - > health > 0 & & ! OnSameTeam ( ent , missile - > enemy ) )
{ //if enemy became invalid, died, or is on the same team, then don't seek it
missile - > spawnflags | = 1 ; //just to let it know it should be faster... FIXME: EXTERNALIZE
missile - > speed = vehWeapon - > fSpeed ;
missile - > angle = vehWeapon - > fHoming ;
if ( vehWeapon - > iLifeTime )
{ //expire after a time
missile - > disconnectDebounceTime = level . time + vehWeapon - > iLifeTime ;
missile - > lockCount = ( int ) ( vehWeapon - > bExplodeOnExpire ) ;
}
missile - > e_ThinkFunc = thinkF_rocketThink ;
missile - > nextthink = level . time + VEH_HOMING_MISSILE_THINK_TIME ;
//FIXME: implement radar in SP?
//missile->s.eFlags |= EF_RADAROBJECT;
}
}
ent - > client - > rocketLockIndex = ENTITYNUM_NONE ;
ent - > client - > rocketLockTime = 0 ;
ent - > client - > rocketTargetTime = 0 ;
VectorCopy ( dir , missile - > movedir ) ;
missile - > random = 1.0f ; //FIXME: externalize?
}
}
}
else
{ //traceline
//FIXME: implement
}
}
//---------------------------------------------------------
void FireVehicleWeapon ( gentity_t * ent , qboolean alt_fire )
//---------------------------------------------------------
{
Vehicle_t * pVeh = ent - > m_pVehicle ;
if ( ! pVeh )
{
return ;
}
if ( pVeh - > m_iRemovedSurfaces )
{ //can't fire when the thing is breaking apart
return ;
}
if ( ent - > owner & & ent - > owner - > client & & ent - > owner - > client - > ps . weapon ! = WP_NONE )
{
return ;
}
// TODO?: If possible (probably not enough time), it would be nice if secondary fire was actually a mode switch/toggle
// so that, for instance, an x-wing can have 4-gun fire, or individual muzzle fire. If you wanted a different weapon, you
// would actually have to press the 2 key or something like that (I doubt I'd get a graphic for it anyways though). -AReis
// If this is not the alternate fire, fire a normal blaster shot...
if ( true ) //(pVeh->m_ulFlags & VEH_FLYING) || (pVeh->m_ulFlags & VEH_WINGSOPEN) ) // NOTE: Wings open also denotes that it has already launched.
{
int weaponNum = 0 , vehWeaponIndex = VEH_WEAPON_NONE ;
int delay = 1000 ;
qboolean aimCorrect = qfalse ;
qboolean linkedFiring = qfalse ;
if ( ! alt_fire )
{
weaponNum = 0 ;
}
else
{
weaponNum = 1 ;
}
vehWeaponIndex = pVeh - > m_pVehicleInfo - > weapon [ weaponNum ] . ID ;
delay = pVeh - > m_pVehicleInfo - > weapon [ weaponNum ] . delay ;
aimCorrect = pVeh - > m_pVehicleInfo - > weapon [ weaponNum ] . aimCorrect ;
if ( pVeh - > m_pVehicleInfo - > weapon [ weaponNum ] . linkable = = 1 //optionally linkable
& & pVeh - > weaponStatus [ weaponNum ] . linked ) //linked
{ //we're linking the primary or alternate weapons, so we'll do *all* the muzzles
linkedFiring = qtrue ;
}
if ( vehWeaponIndex < = VEH_WEAPON_BASE | | vehWeaponIndex > = MAX_VEH_WEAPONS )
{ //invalid vehicle weapon
return ;
}
else
{
vehWeaponInfo_t * vehWeapon = & g_vehWeaponInfo [ vehWeaponIndex ] ;
int i , cumulativeDelay = 0 ;
for ( i = 0 ; i < MAX_VEHICLE_MUZZLES ; i + + )
{
if ( pVeh - > m_pVehicleInfo - > weapMuzzle [ i ] ! = vehWeaponIndex )
{ //this muzzle doesn't match the weapon we're trying to use
continue ;
}
// Fire this muzzle.
if ( pVeh - > m_iMuzzleTag [ i ] ! = - 1 & & pVeh - > m_Muzzles [ i ] . m_iMuzzleWait < level . time )
{
vec3_t start , dir ;
// Prepare weapon delay.
if ( linkedFiring )
{ //linked firing, add the delay up for each muzzle, then apply to all of them at the end
cumulativeDelay + = delay ;
}
else
{ //normal delay - NOTE: always-linked muzzles use normal delay, not cumulative
pVeh - > m_Muzzles [ i ] . m_iMuzzleWait = level . time + delay ;
}
if ( pVeh - > weaponStatus [ weaponNum ] . ammo < vehWeapon - > iAmmoPerShot )
{ //out of ammo!
}
else
{ //have enough ammo to shoot
//take the ammo
pVeh - > weaponStatus [ weaponNum ] . ammo - = vehWeapon - > iAmmoPerShot ;
//do the firing
//FIXME: do we still need to calc the muzzle here in SP?
//WP_CalcVehMuzzle(ent, i);
VectorCopy ( pVeh - > m_Muzzles [ i ] . m_vMuzzlePos , start ) ;
VectorCopy ( pVeh - > m_Muzzles [ i ] . m_vMuzzleDir , dir ) ;
if ( aimCorrect )
{ //auto-aim the missile at the crosshair
trace_t trace ;
vec3_t end ;
AngleVectors ( pVeh - > m_vOrientation , dir , NULL , NULL ) ;
//VectorMA( ent->currentOrigin, 32768, dir, end );
VectorMA ( ent - > currentOrigin , 8192 , dir , end ) ;
gi . trace ( & trace , ent - > currentOrigin , vec3_origin , vec3_origin , end , ent - > s . number , MASK_SHOT ) ;
//if ( trace.fraction < 1.0f && !trace.allsolid && !trace.startsolid )
//bah, always point at end of trace
{
VectorSubtract ( trace . endpos , start , dir ) ;
VectorNormalize ( dir ) ;
}
// Mounted lazer cannon auto aiming at enemy
//-------------------------------------------
if ( ent - > enemy )
{
Vehicle_t * enemyVeh = G_IsRidingVehicle ( ent - > enemy ) ;
// Don't Auto Aim At A Person Who Is Slide Breaking
if ( ! enemyVeh | | ! ( enemyVeh - > m_ulFlags & VEH_SLIDEBREAKING ) )
{
vec3_t dir2 ;
VectorSubtract ( ent - > enemy - > currentOrigin , start , dir2 ) ;
VectorNormalize ( dir2 ) ;
if ( DotProduct ( dir , dir2 ) > 0.95f )
{
VectorCopy ( dir2 , dir ) ;
}
}
}
}
//play the weapon's muzzle effect if we have one
if ( vehWeapon - > iMuzzleFX )
{
G_PlayEffect ( vehWeapon - > iMuzzleFX , pVeh - > m_Muzzles [ i ] . m_vMuzzlePos , pVeh - > m_Muzzles [ i ] . m_vMuzzleDir ) ;
}
WP_FireVehicleWeapon ( ent , start , dir , vehWeapon ) ;
}
if ( pVeh - > weaponStatus [ weaponNum ] . linked ) //NOTE: we don't check linkedFiring here because that does *not* get set if the cannons are *always* linked
{ //we're linking the weapon, so continue on and fire all appropriate muzzles
continue ;
}
//ok, just break, we'll get in here again next frame and try the next muzzle...
break ;
}
}
if ( cumulativeDelay )
{ //we linked muzzles so we need to apply the cumulative delay now, to each of the linked muzzles
for ( i = 0 ; i < MAX_VEHICLE_MUZZLES ; i + + )
{
if ( pVeh - > m_pVehicleInfo - > weapMuzzle [ i ] ! = vehWeaponIndex )
{ //this muzzle doesn't match the weapon we're trying to use
continue ;
}
//apply the cumulative delay
pVeh - > m_Muzzles [ i ] . m_iMuzzleWait = level . time + cumulativeDelay ;
}
}
}
}
}
void WP_FireScepter ( gentity_t * ent , qboolean alt_fire )
{ //just a straight beam
int damage = 1 ;
vec3_t start , end ;
trace_t tr ;
gentity_t * traceEnt = NULL , * tent ;
float shotRange = 8192 ;
qboolean render_impact = qtrue ;
VectorCopy ( muzzle , start ) ;
WP_TraceSetStart ( ent , start , vec3_origin , vec3_origin ) ;
WP_MissileTargetHint ( ent , start , forward ) ;
VectorMA ( start , shotRange , forward , end ) ;
gi . trace ( & tr , start , NULL , NULL , end , ent - > s . number , MASK_SHOT , G2_RETURNONHIT , 10 ) ;
traceEnt = & g_entities [ tr . entityNum ] ;
if ( tr . surfaceFlags & SURF_NOIMPACT )
{
render_impact = qfalse ;
}
// always render a shot beam, doing this the old way because I don't much feel like overriding the effect.
tent = G_TempEntity ( tr . endpos , EV_DISRUPTOR_MAIN_SHOT ) ;
tent - > svFlags | = SVF_BROADCAST ;
VectorCopy ( muzzle , tent - > s . origin2 ) ;
if ( render_impact )
{
if ( tr . entityNum < ENTITYNUM_WORLD & & traceEnt - > takedamage )
{
// Create a simple impact type mark that doesn't last long in the world
G_PlayEffect ( G_EffectIndex ( " disruptor/flesh_impact " ) , tr . endpos , tr . plane . normal ) ;
int hitLoc = G_GetHitLocFromTrace ( & tr , MOD_DISRUPTOR ) ;
G_Damage ( traceEnt , ent , ent , forward , tr . endpos , damage , DAMAGE_EXTRA_KNOCKBACK , MOD_DISRUPTOR , hitLoc ) ;
}
else
{
G_PlayEffect ( G_EffectIndex ( " disruptor/wall_impact " ) , tr . endpos , tr . plane . normal ) ;
}
}
/*
shotDist = shotRange * tr . fraction ;
for ( dist = 0 ; dist < shotDist ; dist + = 64 )
{
//FIXME: on a really long shot, this could make a LOT of alerts in one frame...
VectorMA ( start , dist , forward , spot ) ;
AddSightEvent ( ent , spot , 256 , AEL_DISCOVERED , 50 ) ;
}
VectorMA ( start , shotDist - 4 , forward , spot ) ;
AddSightEvent ( ent , spot , 256 , AEL_DISCOVERED , 50 ) ;
*/
}
extern Vehicle_t * G_IsRidingVehicle ( gentity_t * ent ) ;
//---------------------------------------------------------
void FireWeapon ( gentity_t * ent , qboolean alt_fire )
//---------------------------------------------------------
{
float alert = 256 ;
Vehicle_t * pVeh = NULL ;
// track shots taken for accuracy tracking.
ent - > client - > ps . persistant [ PERS_ACCURACY_SHOTS ] + + ;
// If this is a vehicle, fire it's weapon and we're done.
if ( ent & & ent - > client & & ent - > client - > NPC_class = = CLASS_VEHICLE )
{
FireVehicleWeapon ( ent , alt_fire ) ;
return ;
}
// set aiming directions
if ( ent - > s . weapon = = WP_DISRUPTOR & & alt_fire )
{
if ( ent - > NPC )
{
//snipers must use the angles they actually did their shot trace with
AngleVectors ( ent - > lastAngles , forward , vright , up ) ;
}
}
else if ( ent - > s . weapon = = WP_ATST_SIDE | | ent - > s . weapon = = WP_ATST_MAIN )
{
vec3_t delta1 , enemy_org1 , muzzle1 ;
vec3_t angleToEnemy1 ;
VectorCopy ( ent - > client - > renderInfo . muzzlePoint , muzzle1 ) ;
if ( ! ent - > s . number )
{ //player driving an AT-ST
//SIGH... because we can't anticipate alt-fire, must calc muzzle here and now
mdxaBone_t boltMatrix ;
int bolt ;
if ( ent - > client - > ps . weapon = = WP_ATST_MAIN )
{ //FIXME: alt_fire should fire both barrels, but slower?
if ( ent - > alt_fire )
{
bolt = ent - > handRBolt ;
}
else
{
bolt = ent - > handLBolt ;
}
}
else
{ // ATST SIDE weapons
if ( ent - > alt_fire )
{
if ( gi . G2API_GetSurfaceRenderStatus ( & ent - > ghoul2 [ ent - > playerModel ] , " head_light_blaster_cann " ) )
{ //don't have it!
return ;
}
bolt = ent - > genericBolt2 ;
}
else
{
if ( gi . G2API_GetSurfaceRenderStatus ( & ent - > ghoul2 [ ent - > playerModel ] , " head_concussion_charger " ) )
{ //don't have it!
return ;
}
bolt = ent - > genericBolt1 ;
}
}
vec3_t yawOnlyAngles = { 0 , ent - > currentAngles [ YAW ] , 0 } ;
if ( ent - > currentAngles [ YAW ] ! = ent - > client - > ps . legsYaw )
{
yawOnlyAngles [ YAW ] = ent - > client - > ps . legsYaw ;
}
gi . G2API_GetBoltMatrix ( ent - > ghoul2 , ent - > playerModel , bolt , & boltMatrix , yawOnlyAngles , ent - > currentOrigin , ( cg . time ? cg . time : level . time ) , NULL , ent - > s . modelScale ) ;
// work the matrix axis stuff into the original axis and origins used.
gi . G2API_GiveMeVectorFromMatrix ( boltMatrix , ORIGIN , ent - > client - > renderInfo . muzzlePoint ) ;
gi . G2API_GiveMeVectorFromMatrix ( boltMatrix , NEGATIVE_Y , ent - > client - > renderInfo . muzzleDir ) ;
ent - > client - > renderInfo . mPCalcTime = level . time ;
AngleVectors ( ent - > client - > ps . viewangles , forward , vright , up ) ;
//CalcMuzzlePoint( ent, forward, vright, up, muzzle, 0 );
}
else if ( ! ent - > enemy )
{ //an NPC with no enemy to auto-aim at
VectorCopy ( ent - > client - > renderInfo . muzzleDir , forward ) ;
}
else
{ //NPC, auto-aim at enemy
CalcEntitySpot ( ent - > enemy , SPOT_HEAD , enemy_org1 ) ;
VectorSubtract ( enemy_org1 , muzzle1 , delta1 ) ;
vectoangles ( delta1 , angleToEnemy1 ) ;
AngleVectors ( angleToEnemy1 , forward , vright , up ) ;
}
}
else if ( ent - > s . weapon = = WP_BOT_LASER & & ent - > enemy )
{
vec3_t delta1 , enemy_org1 , muzzle1 ;
vec3_t angleToEnemy1 ;
CalcEntitySpot ( ent - > enemy , SPOT_HEAD , enemy_org1 ) ;
CalcEntitySpot ( ent , SPOT_WEAPON , muzzle1 ) ;
VectorSubtract ( enemy_org1 , muzzle1 , delta1 ) ;
vectoangles ( delta1 , angleToEnemy1 ) ;
AngleVectors ( angleToEnemy1 , forward , vright , up ) ;
}
else
{
if ( ( pVeh = G_IsRidingVehicle ( ent ) ) ! = NULL ) //riding a vehicle
{ //use our muzzleDir, can't use viewangles or vehicle m_vOrientation because we may be animated to shoot left or right...
if ( ( ent - > s . eFlags & EF_NODRAW ) ) //we're inside it
{
vec3_t aimAngles ;
VectorCopy ( ent - > client - > renderInfo . muzzleDir , forward ) ;
vectoangles ( forward , aimAngles ) ;
//we're only keeping the yaw
aimAngles [ PITCH ] = ent - > client - > ps . viewangles [ PITCH ] ;
aimAngles [ ROLL ] = 0 ;
AngleVectors ( aimAngles , forward , vright , up ) ;
}
else
{
vec3_t actorRight ;
vec3_t actorFwd ;
VectorCopy ( ent - > client - > renderInfo . muzzlePoint , muzzle ) ;
AngleVectors ( ent - > currentAngles , actorFwd , actorRight , 0 ) ;
// Aiming Left
//-------------
if ( ent - > client - > ps . torsoAnim = = BOTH_VT_ATL_G | | ent - > client - > ps . torsoAnim = = BOTH_VS_ATL_G )
{
VectorScale ( actorRight , - 1.0f , forward ) ;
}
// Aiming Right
//--------------
else if ( ent - > client - > ps . torsoAnim = = BOTH_VT_ATR_G | | ent - > client - > ps . torsoAnim = = BOTH_VS_ATR_G )
{
VectorCopy ( actorRight , forward ) ;
}
// Aiming Forward
//----------------
else
{
VectorCopy ( actorFwd , forward ) ;
}
// If We Have An Enemy, Fudge The Aim To Hit The Enemy
if ( ent - > enemy )
{
vec3_t toEnemy ;
VectorSubtract ( ent - > enemy - > currentOrigin , ent - > currentOrigin , toEnemy ) ;
VectorNormalize ( toEnemy ) ;
if ( DotProduct ( toEnemy , forward ) > 0.75f & &
( ( ent - > s . number = = 0 & & ! Q_irand ( 0 , 2 ) ) | | // the player has a 1 in 3 chance
( ent - > s . number ! = 0 & & ! Q_irand ( 0 , 5 ) ) ) ) // other guys have a 1 in 6 chance
{
VectorCopy ( toEnemy , forward ) ;
}
else
{
forward [ 0 ] + = Q_flrand ( - 0.1f , 0.1f ) ;
forward [ 1 ] + = Q_flrand ( - 0.1f , 0.1f ) ;
forward [ 2 ] + = Q_flrand ( - 0.1f , 0.1f ) ;
}
}
}
}
else
{
AngleVectors ( ent - > client - > ps . viewangles , forward , vright , up ) ;
}
}
ent - > alt_fire = alt_fire ;
if ( ! pVeh )
{
if ( ent - > NPC & & ( ent - > NPC - > scriptFlags & SCF_FIRE_WEAPON_NO_ANIM ) )
{
VectorCopy ( ent - > client - > renderInfo . muzzlePoint , muzzle ) ;
VectorCopy ( ent - > client - > renderInfo . muzzleDir , forward ) ;
MakeNormalVectors ( forward , vright , up ) ;
}
else
{
CalcMuzzlePoint ( ent , forward , vright , up , muzzle , 0 ) ;
}
}
// fire the specific weapon
switch ( ent - > s . weapon )
{
// Player weapons
//-----------------
case WP_SABER :
return ;
break ;
case WP_BRYAR_PISTOL :
case WP_BLASTER_PISTOL :
WP_FireBryarPistol ( ent , alt_fire ) ;
break ;
case WP_BLASTER :
WP_FireBlaster ( ent , alt_fire ) ;
break ;
case WP_TUSKEN_RIFLE :
if ( alt_fire )
{
WP_FireTuskenRifle ( ent ) ;
}
else
{
WP_Melee ( ent ) ;
}
break ;
case WP_DISRUPTOR :
alert = 50 ; // if you want it to alert enemies, remove this
WP_FireDisruptor ( ent , alt_fire ) ;
break ;
case WP_BOWCASTER :
WP_FireBowcaster ( ent , alt_fire ) ;
break ;
case WP_REPEATER :
WP_FireRepeater ( ent , alt_fire ) ;
break ;
case WP_DEMP2 :
WP_FireDEMP2 ( ent , alt_fire ) ;
break ;
case WP_FLECHETTE :
WP_FireFlechette ( ent , alt_fire ) ;
break ;
case WP_ROCKET_LAUNCHER :
WP_FireRocket ( ent , alt_fire ) ;
break ;
case WP_CONCUSSION :
if ( alt_fire )
{
WP_FireConcussionAlt ( ent ) ;
}
else
{
WP_FireConcussion ( ent ) ;
}
break ;
case WP_THERMAL :
WP_FireThermalDetonator ( ent , alt_fire ) ;
break ;
case WP_TRIP_MINE :
alert = 0 ; // if you want it to alert enemies, remove this
WP_PlaceLaserTrap ( ent , alt_fire ) ;
break ;
case WP_DET_PACK :
alert = 0 ; // if you want it to alert enemies, remove this
WP_FireDetPack ( ent , alt_fire ) ;
break ;
case WP_BOT_LASER :
WP_BotLaser ( ent ) ;
break ;
case WP_EMPLACED_GUN :
// doesn't care about whether it's alt-fire or not. We can do an alt-fire if needed
WP_EmplacedFire ( ent ) ;
break ;
case WP_MELEE :
alert = 0 ; // if you want it to alert enemies, remove this
if ( ! alt_fire | | ! g_debugMelee - > integer )
{
WP_Melee ( ent ) ;
}
break ;
case WP_ATST_MAIN :
WP_ATSTMainFire ( ent ) ;
break ;
case WP_ATST_SIDE :
// TEMP
if ( alt_fire )
{
// WP_FireRocket( ent, qfalse );
WP_ATSTSideAltFire ( ent ) ;
}
else
{
// FIXME!
/* if ( ent->s.number == 0
& & ent - > client - > NPC_class = = CLASS_VEHICLE
& & vehicleData [ ( ( CVehicleNPC * ) ent - > NPC ) - > m_iVehicleTypeID ] . type = = VH_FIGHTER )
{
WP_ATSTMainFire ( ent ) ;
}
else */
{
WP_ATSTSideFire ( ent ) ;
}
}
break ;
case WP_TIE_FIGHTER :
// TEMP
WP_EmplacedFire ( ent ) ;
break ;
case WP_RAPID_FIRE_CONC :
// TEMP
if ( alt_fire )
{
WP_FireRepeater ( ent , alt_fire ) ;
}
else
{
WP_EmplacedFire ( ent ) ;
}
break ;
case WP_STUN_BATON :
WP_FireStunBaton ( ent , alt_fire ) ;
break ;
// case WP_BLASTER_PISTOL:
case WP_JAWA :
WP_FireBryarPistol ( ent , qfalse ) ; // never an alt-fire?
break ;
case WP_SCEPTER :
WP_FireScepter ( ent , alt_fire ) ;
break ;
case WP_NOGHRI_STICK :
if ( ! alt_fire )
{
WP_FireNoghriStick ( ent ) ;
}
//else does melee attack/damage/func
break ;
case WP_TUSKEN_STAFF :
default :
return ;
break ;
}
if ( ! ent - > s . number )
{
if ( ent - > s . weapon = = WP_FLECHETTE | | ( ent - > s . weapon = = WP_BOWCASTER & & ! alt_fire ) )
{ //these can fire multiple shots, count them individually within the firing functions
}
else if ( W_AccuracyLoggableWeapon ( ent - > s . weapon , alt_fire , MOD_UNKNOWN ) )
{
ent - > client - > sess . missionStats . shotsFired + + ;
}
}
// We should probably just use this as a default behavior, in special cases, just set alert to false.
if ( ent - > s . number = = 0 & & alert > 0 )
{
if ( ent - > client - > ps . groundEntityNum = = ENTITYNUM_WORLD //FIXME: check for sand contents type?
& & ent - > s . weapon ! = WP_STUN_BATON
& & ent - > s . weapon ! = WP_MELEE
& & ent - > s . weapon ! = WP_TUSKEN_STAFF
& & ent - > s . weapon ! = WP_THERMAL
& & ent - > s . weapon ! = WP_TRIP_MINE
& & ent - > s . weapon ! = WP_DET_PACK )
{ //the vibration of the shot carries through your feet into the ground
AddSoundEvent ( ent , muzzle , alert , AEL_DISCOVERED , qfalse , qtrue ) ;
}
else
{ //an in-air alert
AddSoundEvent ( ent , muzzle , alert , AEL_DISCOVERED ) ;
}
AddSightEvent ( ent , muzzle , alert * 2 , AEL_DISCOVERED , 20 ) ;
}
}
//NOTE: Emplaced gun moved to g_emplaced.cpp
/*QUAKED misc_weapon_shooter (1 0 0) (-8 -8 -8) (8 8 8) ALTFIRE TOGGLE
ALTFIRE - fire the alt - fire of the chosen weapon
TOGGLE - keep firing until used again ( fires at intervals of " wait " )
" wait " - debounce time between refires ( defaults to 500 )
" delay " - speed of WP_THERMAL ( default is 900 )
" random " - ranges from 0 to random , added to wait ( defaults to 0 )
" target " - what to aim at ( will update aim every frame if it ' s a moving target )
" weapon " - specify the weapon to use ( default is WP_BLASTER )
WP_BRYAR_PISTOL
WP_BLASTER
WP_DISRUPTOR
WP_BOWCASTER
WP_REPEATER
WP_DEMP2
WP_FLECHETTE
WP_ROCKET_LAUNCHER
WP_CONCUSSION
WP_THERMAL
WP_TRIP_MINE
WP_DET_PACK
WP_STUN_BATON
WP_EMPLACED_GUN
WP_BOT_LASER
WP_TURRET
WP_ATST_MAIN
WP_ATST_SIDE
WP_TIE_FIGHTER
WP_RAPID_FIRE_CONC
WP_BLASTER_PISTOL
*/
void misc_weapon_shooter_fire ( gentity_t * self )
{
FireWeapon ( self , ( self - > spawnflags & 1 ) ) ;
if ( ( self - > spawnflags & 2 ) )
{ //repeat
self - > e_ThinkFunc = thinkF_misc_weapon_shooter_fire ;
if ( self - > random )
{
self - > nextthink = level . time + self - > wait + ( int ) ( random ( ) * self - > random ) ;
}
else
{
self - > nextthink = level . time + self - > wait ;
}
}
}
void misc_weapon_shooter_use ( gentity_t * self , gentity_t * other , gentity_t * activator )
{
if ( self - > e_ThinkFunc = = thinkF_misc_weapon_shooter_fire )
{ //repeating fire, stop
self - > e_ThinkFunc = thinkF_NULL ;
self - > nextthink = - 1 ;
return ;
}
//otherwise, fire
misc_weapon_shooter_fire ( self ) ;
}
void misc_weapon_shooter_aim ( gentity_t * self )
{
//update my aim
if ( self - > target )
{
gentity_t * targ = G_Find ( NULL , FOFS ( targetname ) , self - > target ) ;
if ( targ )
{
self - > enemy = targ ;
VectorSubtract ( targ - > currentOrigin , self - > currentOrigin , self - > client - > renderInfo . muzzleDir ) ;
VectorCopy ( targ - > currentOrigin , self - > pos1 ) ;
vectoangles ( self - > client - > renderInfo . muzzleDir , self - > client - > ps . viewangles ) ;
SetClientViewAngle ( self , self - > client - > ps . viewangles ) ;
//FIXME: don't keep doing this unless target is a moving target?
self - > nextthink = level . time + FRAMETIME ;
}
else
{
self - > enemy = NULL ;
}
}
}
extern stringID_table_t WPTable [ ] ;
void SP_misc_weapon_shooter ( gentity_t * self )
{
//alloc a client just for the weapon code to use
self - > client = ( gclient_s * ) gi . Malloc ( sizeof ( gclient_s ) , TAG_G_ALLOC , qtrue ) ;
//set weapon
self - > s . weapon = self - > client - > ps . weapon = WP_BLASTER ;
if ( self - > paintarget )
{ //use a different weapon
self - > s . weapon = self - > client - > ps . weapon = GetIDForString ( WPTable , self - > paintarget ) ;
}
//set where our muzzle is
VectorCopy ( self - > s . origin , self - > client - > renderInfo . muzzlePoint ) ;
//permanently updated
self - > client - > renderInfo . mPCalcTime = Q3_INFINITE ;
//set up to link
if ( self - > target )
{
self - > e_ThinkFunc = thinkF_misc_weapon_shooter_aim ;
self - > nextthink = level . time + START_TIME_LINK_ENTS ;
}
else
{ //just set aim angles
VectorCopy ( self - > s . angles , self - > client - > ps . viewangles ) ;
AngleVectors ( self - > s . angles , self - > client - > renderInfo . muzzleDir , NULL , NULL ) ;
}
//set up to fire when used
self - > e_UseFunc = useF_misc_weapon_shooter_use ;
if ( ! self - > wait )
{
self - > wait = 500 ;
}
2013-05-01 14:52:09 +00:00
}