codemp/game/NPC_AI_GalakMech.c File Reference

#include "b_local.h"
#include "g_nav.h"
#include "anims.h"
#include "w_saber.h"
#include "../namespace_begin.h"
#include "../namespace_end.h"

Go to the source code of this file.

Defines

#define MELEE_DIST_SQUARED   6400
#define MIN_LOB_DIST_SQUARED   65536
#define MAX_LOB_DIST_SQUARED   200704
#define REPEATER_ALT_SIZE   3
#define GENERATOR_HEALTH   25
#define TURN_ON   0x00000000
#define TURN_OFF   0x00000100
#define GALAK_SHIELD_HEALTH   500

Functions

void G_AddVoiceEvent (gentity_t *self, int event, int speakDebounceTime)
void NPC_AimAdjust (int change)
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, float maxSpeed, float idealSpeed, qboolean mustHit)
void G_SoundOnEnt (gentity_t *ent, soundChannel_t channel, const char *soundPath)
qboolean BG_CrouchAnim (int anim)
qboolean NPC_CheckPlayerTeamStealth (void)
void NPC_GalakMech_Precache (void)
void NPC_GalakMech_Init (gentity_t *ent)
void GM_Dying (gentity_t *self)
void NPC_SetPainEvent (gentity_t *self)
void NPC_GM_Pain (gentity_t *self, gentity_t *attacker, int damage)
void NPC_BSGM_Patrol (void)
void NPC_GM_StartLaser (void)
void GM_StartGloat (void)
void NPC_BSGM_Attack (void)
void NPC_BSGM_Default (void)


Define Documentation

#define GALAK_SHIELD_HEALTH   500
 

Definition at line 26 of file NPC_AI_GalakMech.c.

Referenced by NPC_BSGM_Default(), and NPC_GalakMech_Init().

#define GENERATOR_HEALTH   25
 

Definition at line 23 of file NPC_AI_GalakMech.c.

Referenced by NPC_BSGM_Attack(), and NPC_BSGM_Default().

#define MAX_LOB_DIST_SQUARED   200704
 

Definition at line 21 of file NPC_AI_GalakMech.c.

Referenced by NPC_BSGM_Attack().

#define MELEE_DIST_SQUARED   6400
 

Definition at line 19 of file NPC_AI_GalakMech.c.

Referenced by NPC_BSGM_Attack().

#define MIN_LOB_DIST_SQUARED   65536
 

Definition at line 20 of file NPC_AI_GalakMech.c.

Referenced by NPC_BSGM_Attack().

#define REPEATER_ALT_SIZE   3
 

Definition at line 22 of file NPC_AI_GalakMech.c.

#define TURN_OFF   0x00000100
 

Definition at line 25 of file NPC_AI_GalakMech.c.

#define TURN_ON   0x00000000
 

Definition at line 24 of file NPC_AI_GalakMech.c.


Function Documentation

qboolean BG_CrouchAnim int  anim  ) 
 

Definition at line 49 of file bg_panimate.c.

00050 {
00051         switch ( anim )
00052         {
00053         case BOTH_SIT1:                         //# Normal chair sit.
00054         case BOTH_SIT2:                         //# Lotus position.
00055         case BOTH_SIT3:                         //# Sitting in tired position: elbows on knees
00056         case BOTH_CROUCH1:                      //# Transition from standing to crouch
00057         case BOTH_CROUCH1IDLE:          //# Crouching idle
00058         case BOTH_CROUCH1WALK:          //# Walking while crouched
00059         case BOTH_CROUCH1WALKBACK:      //# Walking while crouched
00060         case BOTH_CROUCH2TOSTAND1:      //# going from crouch2 to stand1
00061         case BOTH_CROUCH3:                      //# Desann crouching down to Kyle (cin 9)
00062         case BOTH_KNEES1:                       //# Tavion on her knees
00063         case BOTH_CROUCHATTACKBACK1://FIXME: not if in middle of anim?
00064         case BOTH_ROLL_STAB:
00065                 return qtrue;
00066                 break;
00067         }
00068         return qfalse;
00069 }

void G_AddVoiceEvent gentity_t self,
int  event,
int  speakDebounceTime
 

Definition at line 23 of file NPC_sounds.c.

00024 {
00025         if ( !self->NPC )
00026         {
00027                 return;
00028         }
00029 
00030         if ( !self->client || self->client->ps.pm_type >= PM_DEAD )
00031         {
00032                 return;
00033         }
00034 
00035         if ( self->NPC->blockedSpeechDebounceTime > level.time )
00036         {
00037                 return;
00038         }
00039 
00040         if ( trap_ICARUS_TaskIDPending( self, TID_CHAN_VOICE ) )
00041         {
00042                 return;
00043         }
00044 
00045         
00046         if ( (self->NPC->scriptFlags&SCF_NO_COMBAT_TALK) && ( (event >= EV_ANGER1 && event <= EV_VICTORY3) || (event >= EV_CHASE1 && event <= EV_SUSPICIOUS5) ) )//(event < EV_FF_1A || event > EV_FF_3C) && (event < EV_RESPOND1 || event > EV_MISSION3) )
00047         {
00048                 return;
00049         }
00050         
00051         if ( (self->NPC->scriptFlags&SCF_NO_ALERT_TALK) && (event >= EV_GIVEUP1 && event <= EV_SUSPICIOUS5) )
00052         {
00053                 return;
00054         }
00055         //FIXME: Also needs to check for teammates. Don't want
00056         //              everyone babbling at once
00057 
00058         //NOTE: was losing too many speech events, so we do it directly now, screw networking!
00059         //G_AddEvent( self, event, 0 );
00060         G_SpeechEvent( self, event );
00061 
00062         //won't speak again for 5 seconds (unless otherwise specified)
00063         self->NPC->blockedSpeechDebounceTime = level.time + ((speakDebounceTime==0) ? 5000 : speakDebounceTime);
00064 }

void G_SoundOnEnt gentity_t ent,
soundChannel_t  channel,
const char *  soundPath
 

void GM_Dying gentity_t self  ) 
 

Definition at line 133 of file NPC_AI_GalakMech.c.

References gentity_s::client, entityShared_t::currentOrigin, playerState_s::electrifyTime, FRAMETIME, G_EffectIndex(), G_FreeEntity(), G_PlayEffectID(), gentity_t, gentity_s::ghoul2, renderInfo_s::headBolt, level, gentity_s::nextthink, NPC_SetSurfaceOnOff(), gclient_s::ps, Q_irand(), qfalse, qtrue, gentity_s::r, gclient_s::renderInfo, gentity_s::s, gentity_s::think, entityState_s::time, level_locals_t::time, TIMER_Done(), TIMER_Set(), trap_G2API_AddBolt(), trap_G2API_GetSurfaceRenderStatus(), TURN_OFF, and vec3_origin.

Referenced by CorpsePhysics().

00134 {
00135         if ( level.time - self->s.time < 4000 )
00136         {//FIXME: need a real effect
00137                 //self->s.powerups |= ( 1 << PW_SHOCKED );
00138                 //self->client->ps.powerups[PW_SHOCKED] = level.time + 1000;
00139                 self->client->ps.electrifyTime = level.time + 1000;
00140                 if ( TIMER_Done( self, "dyingExplosion" ) )
00141                 {
00142                         int     newBolt;
00143                         switch ( Q_irand( 1, 14 ) )
00144                         {
00145                         // Find place to generate explosion
00146                         case 1:
00147                                 if (!trap_G2API_GetSurfaceRenderStatus( self->ghoul2, 0, "r_hand" ))
00148                                 {//r_hand still there
00149                                         GM_CreateExplosion( self, trap_G2API_AddBolt(self->ghoul2, 0, "*flasha"), qtrue );
00150                                         NPC_SetSurfaceOnOff( self, "r_hand", TURN_OFF );
00151                                 }
00152                                 else if (!trap_G2API_GetSurfaceRenderStatus( self->ghoul2, 0, "r_arm_middle" ))
00153                                 {//r_arm_middle still there
00154                                         newBolt = trap_G2API_AddBolt( self->ghoul2, 0, "*r_arm_elbow" );
00155                                         NPC_SetSurfaceOnOff( self, "r_arm_middle", TURN_OFF );
00156                                 }
00157                                 break;
00158                         case 2:
00159                                 //FIXME: do only once?
00160                                 if (!trap_G2API_GetSurfaceRenderStatus( self->ghoul2, 0, "l_hand" ))
00161                                 {//l_hand still there
00162                                         GM_CreateExplosion( self, trap_G2API_AddBolt(self->ghoul2, 0, "*flashc"), qfalse );
00163                                         NPC_SetSurfaceOnOff( self, "l_hand", TURN_OFF );
00164                                 }
00165                                 else if (!trap_G2API_GetSurfaceRenderStatus( self->ghoul2, 0, "l_arm_wrist" ))
00166                                 {//l_arm_wrist still there
00167                                         newBolt = trap_G2API_AddBolt( self->ghoul2, 0, "*l_arm_cap_l_hand" );
00168                                         NPC_SetSurfaceOnOff( self, "l_arm_wrist", TURN_OFF );
00169                                 }
00170                                 else if (!trap_G2API_GetSurfaceRenderStatus( self->ghoul2, 0, "l_arm_middle" ))
00171                                 {//l_arm_middle still there
00172                                         newBolt = trap_G2API_AddBolt( self->ghoul2, 0, "*l_arm_cap_l_hand" );
00173                                         NPC_SetSurfaceOnOff( self, "l_arm_middle", TURN_OFF );
00174                                 }
00175                                 else if (!trap_G2API_GetSurfaceRenderStatus( self->ghoul2, 0, "l_arm_augment" ))
00176                                 {//l_arm_augment still there
00177                                         newBolt = trap_G2API_AddBolt( self->ghoul2, 0, "*l_arm_elbow" );
00178                                         NPC_SetSurfaceOnOff( self, "l_arm_augment", TURN_OFF );
00179                                 }
00180                                 break;
00181                         case 3:
00182                         case 4:
00183                                 newBolt = trap_G2API_AddBolt( self->ghoul2, 0, "*hip_fr" );
00184                                 GM_CreateExplosion( self, newBolt, qfalse );
00185                                 break;
00186                         case 5:
00187                         case 6:
00188                                 newBolt = trap_G2API_AddBolt( self->ghoul2, 0, "*shldr_l" );
00189                                 GM_CreateExplosion( self, newBolt, qfalse );
00190                                 break;
00191                         case 7:
00192                         case 8:
00193                                 newBolt = trap_G2API_AddBolt( self->ghoul2, 0, "*uchest_r" );
00194                                 GM_CreateExplosion( self, newBolt, qfalse );
00195                                 break;
00196                         case 9:
00197                         case 10:
00198                                 GM_CreateExplosion( self, self->client->renderInfo.headBolt, qfalse );
00199                                 break;
00200                         case 11:
00201                                 newBolt = trap_G2API_AddBolt( self->ghoul2, 0, "*l_leg_knee" );
00202                                 GM_CreateExplosion( self, newBolt, qtrue );
00203                                 break;
00204                         case 12:
00205                                 newBolt = trap_G2API_AddBolt( self->ghoul2, 0, "*r_leg_knee" );
00206                                 GM_CreateExplosion( self, newBolt, qtrue );
00207                                 break;
00208                         case 13:
00209                                 newBolt = trap_G2API_AddBolt( self->ghoul2, 0, "*l_leg_foot" );
00210                                 GM_CreateExplosion( self, newBolt, qtrue );
00211                                 break;
00212                         case 14:
00213                                 newBolt = trap_G2API_AddBolt( self->ghoul2, 0, "*r_leg_foot" );
00214                                 GM_CreateExplosion( self, newBolt, qtrue );
00215                                 break;
00216                         }
00217 
00218                         TIMER_Set( self, "dyingExplosion", Q_irand( 300, 1100 ) );
00219                 }
00220         }
00221         else
00222         {//one final, huge explosion
00223                 G_PlayEffectID( G_EffectIndex("galak/explode"), self->r.currentOrigin, vec3_origin );
00224 //              G_PlayEffect( "small_chunks", self->r.currentOrigin );
00225 //              G_PlayEffect( "env/exp_trail_comp", self->r.currentOrigin, self->currentAngles );
00226                 self->nextthink = level.time + FRAMETIME;
00227                 self->think = G_FreeEntity;
00228         }
00229 }

void GM_StartGloat void   ) 
 

Definition at line 575 of file NPC_AI_GalakMech.c.

References BOTH_STAND2TO1, gentity_s::client, playerState_s::legsTimer, NPC, NPC_SetAnim(), NPC_SetSurfaceOnOff(), gclient_s::ps, SETANIM_BOTH, SETANIM_FLAG_HOLD, SETANIM_FLAG_OVERRIDE, playerState_s::torsoTimer, TURN_ON, and gentity_s::wait.

Referenced by NPC_BSGM_Attack().

00576 {
00577         NPC->wait = 0;
00578         NPC_SetSurfaceOnOff( NPC, "torso_galakface", TURN_ON );
00579         NPC_SetSurfaceOnOff( NPC, "torso_galakhead", TURN_ON );
00580         NPC_SetSurfaceOnOff( NPC, "torso_eyes_mouth", TURN_ON );
00581         NPC_SetSurfaceOnOff( NPC, "torso_collar", TURN_ON );
00582         NPC_SetSurfaceOnOff( NPC, "torso_galaktorso", TURN_ON );
00583 
00584         NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_STAND2TO1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
00585         NPC->client->ps.legsTimer += 500;
00586         NPC->client->ps.torsoTimer += 500;
00587 }

void NPC_AimAdjust int  change  ) 
 

Definition at line 3098 of file NPC_combat.c.

03099 {
03100         if ( !TIMER_Exists( NPC, "aimDebounce" ) )
03101         {
03102                 int debounce = 500+(3-g_spskill.integer)*100;
03103                 TIMER_Set( NPC, "aimDebounce", Q_irand( debounce,debounce+1000 ) );
03104                 //int debounce = 1000+(3-g_spskill.integer)*500;
03105                 //TIMER_Set( NPC, "aimDebounce", Q_irand( debounce, debounce+2000 ) );
03106                 return;
03107         }
03108         if ( TIMER_Done( NPC, "aimDebounce" ) )
03109         {
03110                 int debounce;
03111 
03112                 NPCInfo->currentAim += change;
03113                 if ( NPCInfo->currentAim > NPCInfo->stats.aim )
03114                 {//can never be better than max aim
03115                         NPCInfo->currentAim = NPCInfo->stats.aim;
03116                 }
03117                 else if ( NPCInfo->currentAim < -30 )
03118                 {//can never be worse than this
03119                         NPCInfo->currentAim = -30;
03120                 }
03121 
03122                 //Com_Printf( "%s new aim = %d\n", NPC->NPC_type, NPCInfo->currentAim );
03123 
03124                 debounce = 500+(3-g_spskill.integer)*100;
03125                 TIMER_Set( NPC, "aimDebounce", Q_irand( debounce,debounce+1000 ) );
03126                 //int debounce = 1000+(3-g_spskill.integer)*500;
03127                 //TIMER_Set( NPC, "aimDebounce", Q_irand( debounce, debounce+2000 ) );
03128         }
03129 }

void NPC_BSGM_Attack void   ) 
 

Definition at line 594 of file NPC_AI_GalakMech.c.

References entityShared_t::absmax, entityShared_t::absmin, trace_t::allsolid, gentity_s::alt_fire, AngleNormalize360(), ARMOR_EFFECT_TIME, BG_CrouchAnim(), gNPC_t::blockedDebounceTime, gNPC_t::blockedSpeechDebounceTime, BOTH_ATTACK1, BOTH_ATTACK2, BOTH_ATTACK4, BOTH_ATTACK5, BOTH_ATTACK6, BOTH_KNOCKDOWN1, BOTH_KNOCKDOWN4, BOTH_KNOCKDOWN5, BOTH_STAND1, BOTH_STAND2TO1, usercmd_s::buttons, CalcEntitySpot(), CHAN_AUTO, gentity_s::classname, gentity_s::client, CONTENTS_LIGHTSABER, gNPC_t::coverTarg, crandom, gNPC_t::currentAim, entityShared_t::currentOrigin, DAMAGE_NO_ARMOR, DAMAGE_NO_KNOCKBACK, gNPC_t::desiredPitch, gNPC_t::desiredYaw, DistanceHorizontalSquared(), playerState_s::electrifyTime, trace_t::endpos, gentity_s::enemy, gNPC_t::enemyCheckDebounceTime, gNPC_t::enemyLastSeenLocation, gNPC_t::enemyLastSeenTime, gclient_s::enemyTeam, trace_t::entityNum, EV_ANGER1, EV_ANGER2, EV_ANGER3, EV_CHASE1, EV_COVER1, EV_ESCAPING1, EV_VICTORY1, EV_VICTORY3, renderInfo_s::eyePoint, flrand(), trace_t::fraction, G_AddVoiceEvent(), G_BoundsOverlap(), G_Damage(), G_EffectIndex(), g_entities, G_FreeEntity(), G_PlayEffectID(), G_SetOrigin(), G_Sound(), G_SoundAtLoc(), G_SoundIndex(), G_Spawn(), g_spskill, G_Throw(), GENERATOR_HEALTH, gentity_t, GM_StartGloat(), gNPC_t::goalEntity, gentity_s::health, gclient_s::hiddenDir, gclient_s::hiddenDist, HL_GENERIC1, InFront(), vmCvar_t::integer, gNPC_t::lastPathAngles, playerState_s::legsAnim, playerState_s::legsTimer, level, gentity_s::localAnimIndex, gentity_s::locationDamage, gentity_s::lockCount, entityState_s::loopSound, MASK_SHOT, MAX_LOB_DIST_SQUARED, MELEE_DIST_SQUARED, MIN_LOB_DIST_SQUARED, MOD_CRUSH, MOD_UNKNOWN, gNPC_t::movementSpeech, renderInfo_s::muzzleDir, renderInfo_s::muzzlePoint, NPC, NPC_AimAdjust(), NPC_BSGM_Patrol(), NPC_ChangeWeapon(), NPC_CheckEnemyExt(), NPC_ClearLOS4(), NPC_FaceEnemy(), NPC_GM_StartLaser(), NPC_SetAnim(), NPC_ShotEntity(), NPC_UpdateAngles(), NPCInfo, NULL, entityState_s::number, gentity_s::painDebounceTime, PITCH, gclient_s::playerTeam, playerState_s::powerups, gclient_s::ps, PW_BATTLESUIT, Q_irand(), Q_stricmp(), qboolean, qfalse, qtrue, gentity_s::r, gclient_s::renderInfo, REPEATER_ALT_SIZE, gentity_s::s, SCF_ALT_FIRE, SCF_CHASE_ENEMIES, SCF_DONT_FIRE, SCF_FIRE_WEAPON, gNPC_t::scriptFlags, SETANIM_BOTH, SETANIM_FLAG_HOLD, SETANIM_FLAG_OVERRIDE, SETANIM_TORSO, SPOT_WEAPON, trace_t::startsolid, SVF_BROADCAST, entityShared_t::svFlags, gentity_s::takedamage, level_locals_t::time, TIMER_Done(), TIMER_Set(), playerState_s::torsoAnim, playerState_s::torsoTimer, gNPC_t::touchedByPlayer, trap_InPVS(), trap_Trace(), ucmd, vec3_origin, vec3_t, vectoangles(), VectorClear, VectorCopy, VectorMA, VectorNormalize(), VectorSubtract, playerState_s::viewangles, gentity_s::wait, playerState_s::weapon, entityState_s::weapon, WeaponThink(), WP_LobFire(), WP_NONE, WP_REPEATER, WP_SABER, WP_TURRET, and YAW.

Referenced by NPC_BSGM_Default().

00595 {
00596         //Don't do anything if we're hurt
00597         if ( NPC->painDebounceTime > level.time )
00598         {
00599                 NPC_UpdateAngles( qtrue, qtrue );
00600                 return;
00601         }
00602 
00603 #if 0
00604         //FIXME: if killed enemy, use victory anim
00605         if ( NPC->enemy && NPC->enemy->health <= 0 
00606                 && !NPC->enemy->s.number )
00607         {//my enemy is dead
00608                 if ( NPC->client->ps.torsoAnim == BOTH_STAND2TO1 )
00609                 {
00610                         if ( NPC->client->ps.torsoTimer <= 500 )
00611                         {
00612                                 G_AddVoiceEvent( NPC, Q_irand( EV_VICTORY1, EV_VICTORY3 ), 3000 );
00613                                 NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_TRIUMPHANT1START, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
00614                                 NPC->client->ps.legsTimer += 500;
00615                                 NPC->client->ps.torsoTimer += 500;
00616                         }
00617                 }
00618                 else if ( NPC->client->ps.torsoAnim == BOTH_TRIUMPHANT1START )
00619                 {
00620                         if ( NPC->client->ps.torsoTimer <= 500 )
00621                         {
00622                                 NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_TRIUMPHANT1STARTGESTURE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
00623                                 NPC->client->ps.legsTimer += 500;
00624                                 NPC->client->ps.torsoTimer += 500;
00625                         }
00626                 }
00627                 else if ( NPC->client->ps.torsoAnim == BOTH_TRIUMPHANT1STARTGESTURE )
00628                 {
00629                         if ( NPC->client->ps.torsoTimer <= 500 )
00630                         {
00631                                 NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_TRIUMPHANT1STOP, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
00632                                 NPC->client->ps.legsTimer += 500;
00633                                 NPC->client->ps.torsoTimer += 500;
00634                         }
00635                 }
00636                 else if ( NPC->client->ps.torsoAnim == BOTH_TRIUMPHANT1STOP )
00637                 {
00638                         if ( NPC->client->ps.torsoTimer <= 500 )
00639                         {
00640                                 NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_STAND1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
00641                                 NPC->client->ps.legsTimer = -1;
00642                                 NPC->client->ps.torsoTimer = -1;
00643                         }
00644                 }
00645                 else if ( NPC->wait )
00646                 {
00647                         if ( TIMER_Done( NPC, "gloatTime" ) )
00648                         {
00649                                 GM_StartGloat();
00650                         }
00651                         else if ( DistanceHorizontalSquared( NPC->client->renderInfo.eyePoint, NPC->enemy->r.currentOrigin ) > 4096 && (NPCInfo->scriptFlags&SCF_CHASE_ENEMIES) )//64 squared
00652                         {
00653                                 NPCInfo->goalEntity = NPC->enemy;
00654                                 GM_Move();
00655                         }
00656                         else
00657                         {//got there
00658                                 GM_StartGloat();
00659                         }
00660                 }
00661                 NPC_FaceEnemy( qtrue );
00662                 NPC_UpdateAngles( qtrue, qtrue );
00663                 return;
00664         }
00665 #endif
00666 
00667         //If we don't have an enemy, just idle
00668         if ( NPC_CheckEnemyExt(qfalse) == qfalse || !NPC->enemy )
00669         {
00670                 NPC->enemy = NULL;
00671                 NPC_BSGM_Patrol();
00672                 return;
00673         }
00674 
00675         enemyLOS4 = enemyCS4 = qfalse;
00676         move4 = qtrue;
00677         faceEnemy4 = qfalse;
00678         shoot4 = qfalse;
00679         hitAlly4 = qfalse;
00680         VectorClear( impactPos4 );
00681         enemyDist4 = DistanceSquared( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin );
00682 
00683         //if ( NPC->client->ps.torsoAnim == BOTH_ATTACK4 ||
00684         //      NPC->client->ps.torsoAnim == BOTH_ATTACK5 )
00685         if (0)
00686         {
00687                 shoot4 = qfalse;
00688                 if ( TIMER_Done( NPC, "smackTime" ) && !NPCInfo->blockedDebounceTime )
00689                 {//time to smack
00690                         //recheck enemyDist4 and InFront
00691                         if ( enemyDist4 < MELEE_DIST_SQUARED && InFront( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, NPC->client->ps.viewangles, 0.3f ) )
00692                         {
00693                                 vec3_t  smackDir;
00694                                 VectorSubtract( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, smackDir );
00695                                 smackDir[2] += 30;
00696                                 VectorNormalize( smackDir );
00697                                 //hurt them
00698                                 G_Sound( NPC->enemy, CHAN_AUTO, G_SoundIndex( "sound/weapons/galak/skewerhit.wav" ) );
00699                                 G_Damage( NPC->enemy, NPC, NPC, smackDir, NPC->r.currentOrigin, (g_spskill.integer+1)*Q_irand( 5, 10), DAMAGE_NO_ARMOR|DAMAGE_NO_KNOCKBACK, MOD_CRUSH ); 
00700                                 if ( NPC->client->ps.torsoAnim == BOTH_ATTACK4 )
00701                                 {//smackdown
00702                                         int knockAnim = BOTH_KNOCKDOWN1;
00703                                         if ( BG_CrouchAnim( NPC->enemy->client->ps.legsAnim ) )
00704                                         {//knockdown from crouch
00705                                                 knockAnim = BOTH_KNOCKDOWN4;
00706                                         }
00707                                         //throw them
00708                                         smackDir[2] = 1;
00709                                         VectorNormalize( smackDir );
00710                                         G_Throw( NPC->enemy, smackDir, 50 );
00711                                         NPC_SetAnim( NPC->enemy, SETANIM_BOTH, knockAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
00712                                 }
00713                                 else
00714                                 {//uppercut
00715                                         //throw them
00716                                         G_Throw( NPC->enemy, smackDir, 100 );
00717                                         //make them backflip
00718                                         NPC_SetAnim( NPC->enemy, SETANIM_BOTH, BOTH_KNOCKDOWN5, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
00719                                 }
00720                                 //done with the damage
00721                                 NPCInfo->blockedDebounceTime = 1;
00722                         }
00723                 }
00724         }
00725         else if ( NPC->lockCount ) //already shooting laser
00726         {//sometimes use the laser beam attack, but only after he's taken down our generator
00727                 shoot4 = qfalse;
00728                 if ( NPC->lockCount == 1 )
00729                 {//charging up
00730                         if ( TIMER_Done( NPC, "beamDelay" ) )
00731                         {//time to start the beam
00732                                 int laserAnim;
00733                                 //if ( Q_irand( 0, 1 ) )
00734                                 if (1)
00735                                 {
00736                                         laserAnim = BOTH_ATTACK2;
00737                                 }
00738                                 /*
00739                                 else
00740                                 {
00741                                         laserAnim = BOTH_ATTACK7;
00742                                 }
00743                                 */
00744                                 NPC_SetAnim( NPC, SETANIM_BOTH, laserAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
00745                                 TIMER_Set( NPC, "attackDelay", NPC->client->ps.torsoTimer + Q_irand( 1000, 3000 ) );
00746                                 //turn on beam effect
00747                                 NPC->lockCount = 2;
00748                                 G_PlayEffectID( G_EffectIndex("galak/trace_beam"), NPC->r.currentOrigin, vec3_origin );
00749                                 NPC->s.loopSound = G_SoundIndex( "sound/weapons/galak/lasercutting.wav" );
00750                                 if ( !NPCInfo->coverTarg )
00751                                 {//for moving looping sound at end of trace
00752                                         NPCInfo->coverTarg = G_Spawn();
00753                                         if ( NPCInfo->coverTarg )
00754                                         {
00755                                                 G_SetOrigin( NPCInfo->coverTarg, NPC->client->renderInfo.muzzlePoint );
00756                                                 NPCInfo->coverTarg->r.svFlags |= SVF_BROADCAST;
00757                                                 NPCInfo->coverTarg->s.loopSound = G_SoundIndex( "sound/weapons/galak/lasercutting.wav" );
00758                                         }
00759                                 }
00760                         }
00761                 }
00762                 else
00763                 {//in the actual attack now
00764                         if ( NPC->client->ps.torsoTimer <= 0 )
00765                         {//attack done!
00766                                 NPC->lockCount = 0;
00767                                 G_FreeEntity( NPCInfo->coverTarg );
00768                                 NPC->s.loopSound = 0;
00769 #if 0
00770                                 NPC_SetAnim( NPC, SETANIM_TORSO, TORSO_DROPWEAP2, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
00771 #endif
00772                                 TIMER_Set( NPC, "attackDelay", NPC->client->ps.torsoTimer );
00773                         }
00774                         else
00775                         {//attack still going
00776                                 //do the trace and damage
00777                                 trace_t trace;
00778                                 vec3_t  end, mins={-3,-3,-3}, maxs={3,3,3};
00779                                 VectorMA( NPC->client->renderInfo.muzzlePoint, 1024, NPC->client->renderInfo.muzzleDir, end );
00780                                 trap_Trace( &trace, NPC->client->renderInfo.muzzlePoint, mins, maxs, end, NPC->s.number, MASK_SHOT );
00781                                 if ( trace.allsolid || trace.startsolid )
00782                                 {//oops, in a wall
00783                                         if ( NPCInfo->coverTarg )
00784                                         {
00785                                                 G_SetOrigin( NPCInfo->coverTarg, NPC->client->renderInfo.muzzlePoint );
00786                                         }
00787                                 }
00788                                 else
00789                                 {//clear
00790                                         if ( trace.fraction < 1.0f )
00791                                         {//hit something
00792                                                 gentity_t *traceEnt = &g_entities[trace.entityNum];
00793                                                 if ( traceEnt && traceEnt->takedamage )
00794                                                 {//damage it
00795                                                         G_SoundAtLoc( trace.endpos, CHAN_AUTO, G_SoundIndex( "sound/weapons/galak/laserdamage.wav" ) );
00796                                                         G_Damage( traceEnt, NPC, NPC, NPC->client->renderInfo.muzzleDir, trace.endpos, 10, 0, MOD_UNKNOWN );
00797                                                 }
00798                                         }
00799                                         if ( NPCInfo->coverTarg )
00800                                         {
00801                                                 G_SetOrigin( NPCInfo->coverTarg, trace.endpos );
00802                                         }
00803                                         if ( !Q_irand( 0, 5 ) )
00804                                         {
00805                                                 G_SoundAtLoc( trace.endpos, CHAN_AUTO, G_SoundIndex( "sound/weapons/galak/laserdamage.wav" ) );
00806                                         }
00807                                 }
00808                         }
00809                 }
00810         }
00811         else 
00812         {//Okay, we're not in a special attack, see if we should switch weapons or start a special attack
00813                 /*
00814                 if ( NPC->s.weapon == WP_REPEATER 
00815                         && !(NPCInfo->scriptFlags & SCF_ALT_FIRE)//using rapid-fire
00816                         && NPC->enemy->s.weapon == WP_SABER //enemy using saber
00817                         && NPC->client && (NPC->client->ps.saberEventFlags&SEF_DEFLECTED)
00818                         && !Q_irand( 0, 50 ) )
00819                 {//he's deflecting my shots, switch to the laser or the lob fire for a while
00820                         TIMER_Set( NPC, "noRapid", Q_irand( 2000, 6000 ) );
00821                         NPCInfo->scriptFlags |= SCF_ALT_FIRE;
00822                         NPC->alt_fire = qtrue;
00823                         if ( NPC->locationDamage[HL_GENERIC1] > GENERATOR_HEALTH && (Q_irand( 0, 1 )||enemyDist4 < MAX_LOB_DIST_SQUARED) )
00824                         {//shield down, use laser
00825                                 NPC_GM_StartLaser();
00826                         }
00827                 }
00828                 else*/
00829                 if (// !NPC->client->ps.powerups[PW_GALAK_SHIELD] 
00830                         1 //rwwFIXMEFIXME: just act like the shield is down til the effects and stuff are done
00831                         && enemyDist4 < MELEE_DIST_SQUARED 
00832                         && InFront( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, NPC->client->ps.viewangles, 0.3f ) 
00833                         && NPC->enemy->localAnimIndex <= 1 )//within 80 and in front
00834                 {//our shield is down, and enemy within 80, if very close, use melee attack to slap away
00835                         if ( TIMER_Done( NPC, "attackDelay" ) )
00836                         {
00837                                 //animate me
00838                                 int swingAnim = BOTH_ATTACK1;
00839 #if 0
00840                                 if ( NPC->locationDamage[HL_GENERIC1] > GENERATOR_HEALTH )
00841                                 {//generator down, use random melee
00842                                         swingAnim = Q_irand( BOTH_ATTACK4, BOTH_ATTACK5 );//smackdown or uppercut
00843                                 }
00844                                 else
00845                                 {//always knock-away
00846                                         swingAnim = BOTH_ATTACK5;//uppercut
00847                                 }
00848 #endif
00849                                 //FIXME: swing sound
00850                                 NPC_SetAnim( NPC, SETANIM_BOTH, swingAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
00851                                 TIMER_Set( NPC, "attackDelay", NPC->client->ps.torsoTimer + Q_irand( 1000, 3000 ) );
00852                                 //delay the hurt until the proper point in the anim
00853                                 TIMER_Set( NPC, "smackTime", 600 );
00854                                 NPCInfo->blockedDebounceTime = 0;
00855                                 //FIXME: say something?
00856                         }
00857                 }
00858                 else if ( !NPC->lockCount && NPC->locationDamage[HL_GENERIC1] > GENERATOR_HEALTH
00859                         && TIMER_Done( NPC, "attackDelay" )
00860                         && InFront( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, NPC->client->ps.viewangles, 0.3f )
00861                         && ((!Q_irand( 0, 10*(2-g_spskill.integer))&& enemyDist4 > MIN_LOB_DIST_SQUARED&& enemyDist4 < MAX_LOB_DIST_SQUARED)
00862                                 ||(!TIMER_Done( NPC, "noLob" )&&!TIMER_Done( NPC, "noRapid" ))) 
00863                         && NPC->enemy->s.weapon != WP_TURRET )
00864                 {//sometimes use the laser beam attack, but only after he's taken down our generator
00865                         shoot4 = qfalse;
00866                         NPC_GM_StartLaser();
00867                 }
00868                 else if ( enemyDist4 < MIN_LOB_DIST_SQUARED 
00869                         && (NPC->enemy->s.weapon != WP_TURRET || Q_stricmp( "PAS", NPC->enemy->classname ))
00870                         && TIMER_Done( NPC, "noRapid" ) )//256
00871                 {//enemy within 256
00872                         if ( (NPC->client->ps.weapon == WP_REPEATER) && (NPCInfo->scriptFlags & SCF_ALT_FIRE) )
00873                         {//shooting an explosive, but enemy too close, switch to primary fire
00874                                 NPCInfo->scriptFlags &= ~SCF_ALT_FIRE;
00875                                 NPC->alt_fire = qfalse;
00876                                 //FIXME: use weap raise & lower anims
00877                                 NPC_ChangeWeapon( WP_REPEATER );
00878                         }
00879                 }
00880                 else if ( (enemyDist4 > MAX_LOB_DIST_SQUARED || (NPC->enemy->s.weapon == WP_TURRET && !Q_stricmp( "PAS", NPC->enemy->classname )))
00881                         && TIMER_Done( NPC, "noLob" ) )//448
00882                 {//enemy more than 448 away and we are ready to try lob fire again
00883                         if ( (NPC->client->ps.weapon == WP_REPEATER) && !(NPCInfo->scriptFlags & SCF_ALT_FIRE) )
00884                         {//enemy far enough away to use lobby explosives
00885                                 NPCInfo->scriptFlags |= SCF_ALT_FIRE;
00886                                 NPC->alt_fire = qtrue;
00887                                 //FIXME: use weap raise & lower anims
00888                                 NPC_ChangeWeapon( WP_REPEATER );
00889                         }
00890                 }
00891         }
00892 
00893         //can we see our target?
00894         if ( NPC_ClearLOS4( NPC->enemy ) )
00895         {
00896                 NPCInfo->enemyLastSeenTime = level.time;//used here for aim debouncing, not always a clear LOS
00897                 enemyLOS4 = qtrue;
00898 
00899                 if ( NPC->client->ps.weapon == WP_NONE )
00900                 {
00901                         enemyCS4 = qfalse;//not true, but should stop us from firing
00902                         NPC_AimAdjust( -1 );//adjust aim worse longer we have no weapon
00903                 }
00904                 else
00905                 {//can we shoot our target?
00906                         if ( ((NPC->client->ps.weapon == WP_REPEATER && (NPCInfo->scriptFlags&SCF_ALT_FIRE))) && enemyDist4 < MIN_LOB_DIST_SQUARED )//256
00907                         {
00908                                 enemyCS4 = qfalse;//not true, but should stop us from firing
00909                                 hitAlly4 = qtrue;//us!
00910                                 //FIXME: if too close, run away!
00911                         }
00912                         else
00913                         {
00914                                 int hit = NPC_ShotEntity( NPC->enemy, impactPos4 );
00915                                 gentity_t *hitEnt = &g_entities[hit];
00916                                 if ( hit == NPC->enemy->s.number 
00917                                         || ( hitEnt && hitEnt->client && hitEnt->client->playerTeam == NPC->client->enemyTeam )
00918                                         || ( hitEnt && hitEnt->takedamage ) )
00919                                 {//can hit enemy or will hit glass or other breakable, so shoot anyway
00920                                         enemyCS4 = qtrue;
00921                                         NPC_AimAdjust( 2 );//adjust aim better longer we have clear shot at enemy
00922                                         VectorCopy( NPC->enemy->r.currentOrigin, NPCInfo->enemyLastSeenLocation );
00923                                 }
00924                                 else
00925                                 {//Hmm, have to get around this bastard
00926                                         NPC_AimAdjust( 1 );//adjust aim better longer we can see enemy
00927                                         if ( hitEnt && hitEnt->client && hitEnt->client->playerTeam == NPC->client->playerTeam )
00928                                         {//would hit an ally, don't fire!!!
00929                                                 hitAlly4 = qtrue;
00930                                         }
00931                                         else
00932                                         {//Check and see where our shot *would* hit... if it's not close to the enemy (within 256?), then don't fire
00933                                         }
00934                                 }
00935                         }
00936                 }
00937         }
00938         else if ( trap_InPVS( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin ) )
00939         {
00940                 int hit;
00941                 gentity_t *hitEnt;
00942 
00943                 if ( TIMER_Done( NPC, "talkDebounce" ) && !Q_irand( 0, 10 ) )
00944                 {
00945                         if ( NPCInfo->enemyCheckDebounceTime < 8 )
00946                         {
00947                                 int speech = -1;
00948                                 switch( NPCInfo->enemyCheckDebounceTime )
00949                                 {
00950                                 case 0:
00951                                 case 1:
00952                                 case 2:
00953                                         speech = EV_CHASE1 + NPCInfo->enemyCheckDebounceTime;
00954                                         break;
00955                                 case 3:
00956                                 case 4:
00957                                 case 5:
00958                                         speech = EV_COVER1 + NPCInfo->enemyCheckDebounceTime-3;
00959                                         break;
00960                                 case 6:
00961                                 case 7:
00962                                         speech = EV_ESCAPING1 + NPCInfo->enemyCheckDebounceTime-6;
00963                                         break;
00964                                 }
00965                                 NPCInfo->enemyCheckDebounceTime++;
00966                                 if ( speech != -1 )
00967                                 {
00968                                         G_AddVoiceEvent( NPC, speech, Q_irand( 3000, 5000 ) );
00969                                         TIMER_Set( NPC, "talkDebounce", Q_irand( 5000, 7000 ) );
00970                                 }
00971                         }
00972                 }
00973 
00974                 NPCInfo->enemyLastSeenTime = level.time;
00975 
00976                 hit = NPC_ShotEntity( NPC->enemy, impactPos4 );
00977                 hitEnt = &g_entities[hit];
00978                 if ( hit == NPC->enemy->s.number 
00979                         || ( hitEnt && hitEnt->client && hitEnt->client->playerTeam == NPC->client->enemyTeam )
00980                         || ( hitEnt && hitEnt->takedamage ) )
00981                 {//can hit enemy or will hit glass or other breakable, so shoot anyway
00982                         enemyCS4 = qtrue;
00983                 }
00984                 else
00985                 {
00986                         faceEnemy4 = qtrue;
00987                         NPC_AimAdjust( -1 );//adjust aim worse longer we cannot see enemy
00988                 }
00989         }
00990 
00991         if ( enemyLOS4 )
00992         {
00993                 faceEnemy4 = qtrue;
00994         }
00995         else
00996         {
00997                 if ( !NPCInfo->goalEntity )
00998                 {
00999                         NPCInfo->goalEntity = NPC->enemy;
01000                 }
01001                 if ( NPCInfo->goalEntity == NPC->enemy )
01002                 {//for now, always chase the enemy
01003                         move4 = qtrue;
01004                 }
01005         }
01006         if ( enemyCS4 )
01007         {
01008                 shoot4 = qtrue;
01009                 //NPCInfo->enemyCheckDebounceTime = level.time;//actually used here as a last actual LOS
01010         }
01011         else
01012         {
01013                 if ( !NPCInfo->goalEntity )
01014                 {
01015                         NPCInfo->goalEntity = NPC->enemy;
01016                 }
01017                 if ( NPCInfo->goalEntity == NPC->enemy )
01018                 {//for now, always chase the enemy
01019                         move4 = qtrue;
01020                 }
01021         }
01022 
01023         //Check for movement to take care of
01024         GM_CheckMoveState();
01025 
01026         //See if we should override shooting decision with any special considerations
01027         GM_CheckFireState();
01028 
01029         if ( NPC->client->ps.weapon == WP_REPEATER && (NPCInfo->scriptFlags&SCF_ALT_FIRE) && shoot4 && TIMER_Done( NPC, "attackDelay" ) )
01030         {
01031                 vec3_t  muzzle;
01032                 vec3_t  angles;
01033                 vec3_t  target;
01034                 vec3_t velocity = {0,0,0};
01035                 vec3_t mins = {-REPEATER_ALT_SIZE,-REPEATER_ALT_SIZE,-REPEATER_ALT_SIZE}, maxs = {REPEATER_ALT_SIZE,REPEATER_ALT_SIZE,REPEATER_ALT_SIZE};
01036                 qboolean clearshot;
01037 
01038                 CalcEntitySpot( NPC, SPOT_WEAPON, muzzle );
01039                 
01040                 VectorCopy( NPC->enemy->r.currentOrigin, target );
01041 
01042                 target[0] += flrand( -5, 5 )+(crandom()*(6-NPCInfo->currentAim)*2);
01043                 target[1] += flrand( -5, 5 )+(crandom()*(6-NPCInfo->currentAim)*2);
01044                 target[2] += flrand( -5, 5 )+(crandom()*(6-NPCInfo->currentAim)*2);
01045 
01046                 //Find the desired angles
01047                 clearshot = WP_LobFire( NPC, muzzle, target, mins, maxs, MASK_SHOT|CONTENTS_LIGHTSABER, 
01048                         velocity, qtrue, NPC->s.number, NPC->enemy->s.number,
01049                         300, 1100, 1500, qtrue );
01050                 if ( VectorCompare( vec3_origin, velocity ) || (!clearshot&&enemyLOS4&&enemyCS4)  )
01051                 {//no clear lob shot and no lob shot that will hit something breakable
01052                         if ( enemyLOS4 && enemyCS4 && TIMER_Done( NPC, "noRapid" ) )
01053                         {//have a clear straight shot, so switch to primary
01054                                 NPCInfo->scriptFlags &= ~SCF_ALT_FIRE;
01055                                 NPC->alt_fire = qfalse;
01056                                 NPC_ChangeWeapon( WP_REPEATER );
01057                                 //keep this weap for a bit
01058                                 TIMER_Set( NPC, "noLob", Q_irand( 500, 1000 ) );
01059                         }
01060                         else
01061                         {
01062                                 shoot4 = qfalse;
01063                         }
01064                 }
01065                 else
01066                 {
01067                         vectoangles( velocity, angles );
01068 
01069                         NPCInfo->desiredYaw             = AngleNormalize360( angles[YAW] );
01070                         NPCInfo->desiredPitch   = AngleNormalize360( angles[PITCH] );
01071 
01072                         VectorCopy( velocity, NPC->client->hiddenDir );
01073                         NPC->client->hiddenDist = VectorNormalize ( NPC->client->hiddenDir );
01074                 }
01075         }
01076         else if ( faceEnemy4 )
01077         {//face the enemy
01078                 NPC_FaceEnemy( qtrue );
01079         }
01080 
01081         if ( !TIMER_Done( NPC, "standTime" ) )
01082         {
01083                 move4 = qfalse;
01084         }
01085         if ( !(NPCInfo->scriptFlags&SCF_CHASE_ENEMIES) )
01086         {//not supposed to chase my enemies
01087                 if ( NPCInfo->goalEntity == NPC->enemy )
01088                 {//goal is my entity, so don't move
01089                         move4 = qfalse;
01090                 }
01091         }
01092 
01093         if ( move4 && !NPC->lockCount )
01094         {//move toward goal
01095                 if ( NPCInfo->goalEntity 
01096                         /*&& NPC->client->ps.legsAnim != BOTH_ALERT1
01097                         && NPC->client->ps.legsAnim != BOTH_ATTACK2 
01098                         && NPC->client->ps.legsAnim != BOTH_ATTACK4
01099                         && NPC->client->ps.legsAnim != BOTH_ATTACK5 
01100                         && NPC->client->ps.legsAnim != BOTH_ATTACK7*/ )
01101                 {
01102                         move4 = GM_Move();
01103                 }
01104                 else
01105                 {
01106                         move4 = qfalse;
01107                 }
01108         }
01109 
01110         if ( !TIMER_Done( NPC, "flee" ) )
01111         {//running away
01112                 faceEnemy4 = qfalse;
01113         }
01114 
01115         //FIXME: check scf_face_move_dir here?
01116 
01117         if ( !faceEnemy4 )
01118         {//we want to face in the dir we're running
01119                 if ( !move4 )
01120                 {//if we haven't moved, we should look in the direction we last looked?
01121                         VectorCopy( NPC->client->ps.viewangles, NPCInfo->lastPathAngles );
01122                 }
01123                 if ( move4 )
01124                 {//don't run away and shoot
01125                         NPCInfo->desiredYaw = NPCInfo->lastPathAngles[YAW];
01126                         NPCInfo->desiredPitch = 0;
01127                         shoot4 = qfalse;
01128                 }
01129         }
01130         NPC_UpdateAngles( qtrue, qtrue );
01131 
01132         if ( NPCInfo->scriptFlags & SCF_DONT_FIRE )
01133         {
01134                 shoot4 = qfalse;
01135         }
01136 
01137         if ( NPC->enemy && NPC->enemy->enemy )
01138         {
01139                 if ( NPC->enemy->s.weapon == WP_SABER && NPC->enemy->enemy->s.weapon == WP_SABER )
01140                 {//don't shoot at an enemy jedi who is fighting another jedi, for fear of injuring one or causing rogue blaster deflections (a la Obi Wan/Vader duel at end of ANH)
01141                         shoot4 = qfalse;
01142                 }
01143         }
01144         //FIXME: don't shoot right away!
01145         if ( shoot4 )
01146         {//try to shoot if it's time
01147                 if ( TIMER_Done( NPC, "attackDelay" ) )
01148                 {
01149                         if( !(NPCInfo->scriptFlags & SCF_FIRE_WEAPON) ) // we've already fired, no need to do it again here
01150                         {
01151                                 WeaponThink( qtrue );
01152                         }
01153                 }
01154         }
01155 
01156         //also:
01157         if ( NPC->enemy->s.weapon == WP_TURRET && !Q_stricmp( "PAS", NPC->enemy->classname ) )
01158         {//crush turrets
01159                 if ( G_BoundsOverlap( NPC->r.absmin, NPC->r.absmax, NPC->enemy->r.absmin, NPC->enemy->r.absmax ) )
01160                 {//have to do this test because placed turrets are not solid to NPCs (so they don't obstruct navigation)
01161                         //if ( NPC->client->ps.powerups[PW_GALAK_SHIELD] > 0 )
01162                         if (0)
01163                         {
01164                                 NPC->client->ps.powerups[PW_BATTLESUIT] = level.time + ARMOR_EFFECT_TIME;
01165                                 G_Damage( NPC->enemy, NPC, NPC, NULL, NPC->r.currentOrigin, 100, DAMAGE_NO_KNOCKBACK, MOD_UNKNOWN ); 
01166                         }
01167                         else
01168                         {
01169                                 G_Damage( NPC->enemy, NPC, NPC, NULL, NPC->r.currentOrigin, 100, DAMAGE_NO_KNOCKBACK, MOD_CRUSH ); 
01170                         }
01171                 }
01172         }
01173         else if ( NPCInfo->touchedByPlayer != NULL && NPCInfo->touchedByPlayer == NPC->enemy )
01174         {//touched enemy
01175                 //if ( NPC->client->ps.powerups[PW_GALAK_SHIELD] > 0 )
01176                 if (0)
01177                 {//zap him!
01178                         vec3_t  smackDir;
01179 
01180                         //animate me
01181 #if 0
01182                         NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK6, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
01183 #endif
01184                         TIMER_Set( NPC, "attackDelay", NPC->client->ps.torsoTimer );
01185                         TIMER_Set( NPC, "standTime", NPC->client->ps.legsTimer );
01186                         //FIXME: debounce this?
01187                         NPCInfo->touchedByPlayer = NULL;
01188                         //FIXME: some shield effect?
01189                         NPC->client->ps.powerups[PW_BATTLESUIT] = level.time + ARMOR_EFFECT_TIME;
01190 
01191                         VectorSubtract( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, smackDir );
01192                         smackDir[2] += 30;
01193                         VectorNormalize( smackDir );
01194                         G_Damage( NPC->enemy, NPC, NPC, smackDir, NPC->r.currentOrigin, (g_spskill.integer+1)*Q_irand( 5, 10), DAMAGE_NO_KNOCKBACK, MOD_UNKNOWN ); 
01195                         //throw them
01196                         G_Throw( NPC->enemy, smackDir, 100 );
01197                         //NPC->enemy->s.powerups |= ( 1 << PW_SHOCKED );
01198                         if ( NPC->enemy->client )
01199                         {
01200                         //      NPC->enemy->client->ps.powerups[PW_SHOCKED] = level.time + 1000;
01201                                 NPC->enemy->client->ps.electrifyTime = level.time + 1000;
01202                         }
01203                         //stop any attacks
01204                         ucmd.buttons = 0;
01205                 }
01206         }
01207 
01208         if ( NPCInfo->movementSpeech < 3 && NPCInfo->blockedSpeechDebounceTime <= level.time )
01209         {
01210                 if ( NPC->enemy && NPC->enemy->health > 0 && NPC->enemy->painDebounceTime > level.time )
01211                 {
01212                         if ( NPC->enemy->health < 50 && NPCInfo->movementSpeech == 2 )
01213                         {
01214                                 G_AddVoiceEvent( NPC, EV_ANGER2, Q_irand( 2000, 4000 ) );
01215                                 NPCInfo->movementSpeech = 3;
01216                         }
01217                         else if ( NPC->enemy->health < 75 && NPCInfo->movementSpeech == 1 )
01218                         {
01219                                 G_AddVoiceEvent( NPC, EV_ANGER1, Q_irand( 2000, 4000 ) );
01220                                 NPCInfo->movementSpeech = 2;
01221                         }
01222                         else if ( NPC->enemy->health < 100 && NPCInfo->movementSpeech == 0 )
01223                         {
01224                                 G_AddVoiceEvent( NPC, EV_ANGER3, Q_irand( 2000, 4000 ) );
01225                                 NPCInfo->movementSpeech = 1;
01226                         }
01227                 }
01228         }
01229 }

void NPC_BSGM_Default void   ) 
 

Definition at line 1231 of file NPC_AI_GalakMech.c.

References gentity_s::client, gentity_s::clipmask, playerState_s::crouchheight, entityShared_t::currentOrigin, gentity_s::enemy, FL_SHIELDED, gentity_s::flags, GALAK_SHIELD_HEALTH, GENERATOR_HEALTH, HL_GENERIC1, gNPC_t::investigateCount, gNPC_t::investigateDebounceTime, level, gentity_s::locationDamage, entityShared_t::maxs, entityShared_t::mins, NPC, NPC_BSGM_Attack(), NPC_BSGM_Patrol(), NPC_SetSurfaceOnOff(), NPCInfo, entityState_s::number, gclient_s::ps, qtrue, gentity_s::r, gentity_s::s, SCF_FIRE_WEAPON, gNPC_t::scriptFlags, playerState_s::standheight, trace_t::startsolid, STAT_ARMOR, playerState_s::stats, level_locals_t::time, trap_Trace(), TURN_OFF, TURN_ON, VectorCopy, VectorSet, and WeaponThink().

Referenced by NPC_RunBehavior().

01232 {
01233         if( NPCInfo->scriptFlags & SCF_FIRE_WEAPON )
01234         {
01235                 WeaponThink( qtrue );
01236         }
01237         
01238         if ( NPC->client->ps.stats[STAT_ARMOR] <= 0 )
01239         {//armor gone
01240         //      if ( !NPCInfo->investigateDebounceTime )
01241                 if (0)
01242                 {//start regenerating the armor
01243                         NPC_SetSurfaceOnOff( NPC, "torso_shield", TURN_OFF );
01244                         NPC->flags &= ~FL_SHIELDED;//no more reflections
01245                         VectorSet( NPC->r.mins, -20, -20, -24 );
01246                         VectorSet( NPC->r.maxs, 20, 20, 64 );
01247                         NPC->client->ps.crouchheight = NPC->client->ps.standheight = 64;
01248                         if ( NPC->locationDamage[HL_GENERIC1] < GENERATOR_HEALTH )
01249                         {//still have the generator bolt-on
01250                                 if ( NPCInfo->investigateCount < 12 )
01251                                 {
01252                                         NPCInfo->investigateCount++;
01253                                 }
01254                                 NPCInfo->investigateDebounceTime = level.time + (NPCInfo->investigateCount * 5000);
01255                         }
01256                 }
01257                 else if ( NPCInfo->investigateDebounceTime < level.time )
01258                 {//armor regenerated, turn shield back on
01259                         //do a trace and make sure we can turn this back on?
01260                         trace_t tr;
01261                         trap_Trace( &tr, NPC->r.currentOrigin, shieldMins, shieldMaxs, NPC->r.currentOrigin, NPC->s.number, NPC->clipmask );
01262                         if ( !tr.startsolid )
01263                         {
01264                                 VectorCopy( shieldMins, NPC->r.mins );
01265                                 VectorCopy( shieldMaxs, NPC->r.maxs );
01266                                 NPC->client->ps.crouchheight = NPC->client->ps.standheight = shieldMaxs[2];
01267                                 NPC->client->ps.stats[STAT_ARMOR] = GALAK_SHIELD_HEALTH;
01268                                 NPCInfo->investigateDebounceTime = 0;
01269                                 NPC->flags |= FL_SHIELDED;//reflect normal shots
01270                         //      NPC->fx_time = level.time;
01271                                 NPC_SetSurfaceOnOff( NPC, "torso_shield", TURN_ON );
01272                         }
01273                 }
01274         }
01275         /*
01276         if ( NPC->client->ps.stats[STAT_ARMOR] > 0 )
01277         {//armor present
01278                 NPC->client->ps.powerups[PW_GALAK_SHIELD] = Q3_INFINITE;//temp, for effect
01279                 NPC_SetSurfaceOnOff( NPC, "torso_shield", TURN_ON );
01280         }
01281         else
01282         {
01283                 NPC_SetSurfaceOnOff( NPC, "torso_shield", TURN_OFF );
01284         }
01285         */
01286         //rwwFIXMEFIXME: Allow this stuff, and again, going to have to let the client know about it.
01287         //Maybe a surface-off bitflag of some sort in the entity state?
01288 
01289         if( !NPC->enemy )
01290         {//don't have an enemy, look for one
01291                 NPC_BSGM_Patrol();
01292         }
01293         else //if ( NPC->enemy )
01294         {//have an enemy
01295                 NPC_BSGM_Attack();
01296         }
01297 }

void NPC_BSGM_Patrol void   ) 
 

Definition at line 416 of file NPC_AI_GalakMech.c.

References BUTTON_WALKING, usercmd_s::buttons, NPC_CheckPlayerTeamStealth(), NPC_MoveToGoal(), NPC_UpdateAngles(), qtrue, ucmd, and UpdateGoal().

Referenced by NPC_BSGM_Attack(), and NPC_BSGM_Default().

00417 {
00418         if ( NPC_CheckPlayerTeamStealth() )
00419         {
00420                 NPC_UpdateAngles( qtrue, qtrue );
00421                 return;
00422         }
00423 
00424         //If we have somewhere to go, then do that
00425         if ( UpdateGoal() )
00426         {
00427                 ucmd.buttons |= BUTTON_WALKING;
00428                 NPC_MoveToGoal( qtrue );
00429         }
00430 
00431         NPC_UpdateAngles( qtrue, qtrue );
00432 }

qboolean NPC_CheckPlayerTeamStealth void   ) 
 

void NPC_GalakMech_Init gentity_t ent  ) 
 

Definition at line 59 of file NPC_AI_GalakMech.c.

References gNPC_t::behaviorState, BS_CINEMATIC, gentity_s::client, FL_NO_KNOCKBACK, FL_SHIELDED, gentity_s::flags, GALAK_SHIELD_HEALTH, gentity_t, gNPC_t::investigateCount, gNPC_t::investigateDebounceTime, entityShared_t::maxs, entityShared_t::mins, gentity_s::NPC, NPC_SetSurfaceOnOff(), gclient_s::ps, gentity_s::r, STAT_ARMOR, playerState_s::stats, TIMER_Set(), TURN_OFF, TURN_ON, and VectorSet.

Referenced by NPC_SetMiscDefaultData().

00060 {
00061         if (ent->NPC->behaviorState != BS_CINEMATIC)
00062         {
00063                 ent->client->ps.stats[STAT_ARMOR] = GALAK_SHIELD_HEALTH;
00064                 ent->NPC->investigateCount = ent->NPC->investigateDebounceTime = 0;
00065                 ent->flags |= FL_SHIELDED;//reflect normal shots
00066                 //rwwFIXMEFIXME: Support PW_GALAK_SHIELD
00067                 //ent->client->ps.powerups[PW_GALAK_SHIELD] = Q3_INFINITE;//temp, for effect
00068                 //ent->fx_time = level.time;
00069                 VectorSet( ent->r.mins, -60, -60, -24 );
00070                 VectorSet( ent->r.maxs, 60, 60, 80 );
00071                 ent->flags |= FL_NO_KNOCKBACK;//don't get pushed
00072                 TIMER_Set( ent, "attackDelay", 0 );     //FIXME: Slant for difficulty levels
00073                 TIMER_Set( ent, "flee", 0 );
00074                 TIMER_Set( ent, "smackTime", 0 );
00075                 TIMER_Set( ent, "beamDelay", 0 );
00076                 TIMER_Set( ent, "noLob", 0 );
00077                 TIMER_Set( ent, "noRapid", 0 );
00078                 TIMER_Set( ent, "talkDebounce", 0 );
00079 
00080                 NPC_SetSurfaceOnOff( ent, "torso_shield", TURN_ON );
00081                 NPC_SetSurfaceOnOff( ent, "torso_galakface", TURN_OFF );
00082                 NPC_SetSurfaceOnOff( ent, "torso_galakhead", TURN_OFF );
00083                 NPC_SetSurfaceOnOff( ent, "torso_eyes_mouth", TURN_OFF );
00084                 NPC_SetSurfaceOnOff( ent, "torso_collar", TURN_OFF );
00085                 NPC_SetSurfaceOnOff( ent, "torso_galaktorso", TURN_OFF );
00086         }
00087         else
00088         {
00089 //              NPC_SetSurfaceOnOff( ent, "helmet", TURN_OFF );
00090                 NPC_SetSurfaceOnOff( ent, "torso_shield", TURN_OFF );
00091                 NPC_SetSurfaceOnOff( ent, "torso_galakface", TURN_ON );
00092                 NPC_SetSurfaceOnOff( ent, "torso_galakhead", TURN_ON );
00093                 NPC_SetSurfaceOnOff( ent, "torso_eyes_mouth", TURN_ON );
00094                 NPC_SetSurfaceOnOff( ent, "torso_collar", TURN_ON );
00095                 NPC_SetSurfaceOnOff( ent, "torso_galaktorso", TURN_ON );
00096         }
00097 
00098 }

void NPC_GalakMech_Precache void   ) 
 

Definition at line 42 of file NPC_AI_GalakMech.c.

References G_EffectIndex(), and G_SoundIndex().

Referenced by NPC_SpawnType(), and SP_NPC_Galak().

00043 {
00044         G_SoundIndex( "sound/weapons/galak/skewerhit.wav" );
00045         G_SoundIndex( "sound/weapons/galak/lasercharge.wav" );
00046         G_SoundIndex( "sound/weapons/galak/lasercutting.wav" );
00047         G_SoundIndex( "sound/weapons/galak/laserdamage.wav" );
00048 
00049         G_EffectIndex( "galak/trace_beam" );
00050         G_EffectIndex( "galak/beam_warmup" );
00051 //      G_EffectIndex( "small_chunks");
00052         G_EffectIndex( "env/med_explode2");
00053         G_EffectIndex( "env/small_explode2");
00054         G_EffectIndex( "galak/explode");
00055         G_EffectIndex( "blaster/smoke_bolton");
00056 //      G_EffectIndex( "env/exp_trail_comp");
00057 }

void NPC_GM_Pain gentity_t self,
gentity_t attacker,
int  damage
 

Definition at line 238 of file NPC_AI_GalakMech.c.

References gentity_s::alt_fire, gNPC_t::blockedSpeechDebounceTime, gentity_s::client, gentity_s::count, gentity_s::delay, playerState_s::electrifyTime, EV_DETECTED1, EV_PUSHED1, EV_PUSHED2, EV_PUSHED3, G_AddVoiceEvent(), gentity_t, gPainMOD, gPainPoint, gentity_s::health, HL_GENERIC1, gentity_s::lastEnemy, level, gentity_s::lockCount, MOD_REPEATER, MOD_REPEATER_ALT, gentity_s::NPC, NPC_Pain(), NPC_SetPainEvent(), gclient_s::ps, Q_irand(), qfalse, qtrue, SCF_ALT_FIRE, gNPC_t::scriptFlags, level_locals_t::time, TIMER_Done(), TIMER_Set(), playerState_s::torsoTimer, vec3_t, and VectorCopy.

Referenced by NPC_PainFunc().

00239 {
00240         vec3_t point;
00241         gentity_t *inflictor = attacker;
00242         int hitLoc = 1;
00243         int mod = gPainMOD;
00244 
00245         VectorCopy(gPainPoint, point);
00246 
00247         //if ( self->client->ps.powerups[PW_GALAK_SHIELD] == 0 )
00248         if (0) //rwwFIXMEFIXME: do all of this
00249         {//shield is currently down
00250                 //FIXME: allow for radius damage?
00251                 /*
00252                 if ( (hitLoc==HL_GENERIC1) && (self->locationDamage[HL_GENERIC1] > GENERATOR_HEALTH) )
00253                 {
00254                         int newBolt = gi.G2API_AddBolt( &self->ghoul2[self->playerModel], "*antenna_base" );
00255                         if ( newBolt != -1 )
00256                         {
00257                                 GM_CreateExplosion( self, newBolt, qfalse );
00258                         }
00259 
00260                         NPC_SetSurfaceOnOff( self, "torso_shield", TURN_OFF );
00261                         NPC_SetSurfaceOnOff( self, "torso_antenna", TURN_OFF );
00262                         NPC_SetSurfaceOnOff( self, "torso_antenna_base_cap", TURN_ON );
00263                         self->client->ps.powerups[PW_GALAK_SHIELD] = 0;//temp, for effect
00264                         self->client->ps.stats[STAT_ARMOR] = 0;//no more armor
00265                         self->NPC->investigateDebounceTime = 0;//stop recharging
00266 
00267                         NPC_SetAnim( self, SETANIM_BOTH, BOTH_ALERT1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
00268                         TIMER_Set( self, "attackDelay", self->client->ps.torsoTimer );
00269                         G_AddEvent( self, Q_irand( EV_DEATH1, EV_DEATH3 ), self->health );
00270                 }
00271                 */
00272         }
00273         else
00274         {//store the point for shield impact
00275                 if ( point )
00276                 {
00277                 //      VectorCopy( point, self->pos4 );
00278                 //      self->client->poisonTime = level.time;
00279                         //rwwFIXMEFIXME: ..do this is as well.
00280                 }
00281         }
00282 
00283         if ( !self->lockCount && self->client->ps.torsoTimer <= 0 )
00284         {//don't interrupt laser sweep attack or other special attacks/moves
00285                 if ( self->count < 4 && self->health > 100 && hitLoc != HL_GENERIC1 )
00286                 {
00287                         if ( self->delay < level.time )
00288                         {
00289                                 int speech;
00290                                 switch( self->count )
00291                                 {
00292                                 default:
00293                                 case 0:
00294                                         speech = EV_PUSHED1;
00295                                         break;
00296                                 case 1:
00297                                         speech = EV_PUSHED2;
00298                                         break;
00299                                 case 2:
00300                                         speech = EV_PUSHED3;
00301                                         break;
00302                                 case 3:
00303                                         speech = EV_DETECTED1;
00304                                         break;
00305                                 }
00306                                 self->count++;
00307                                 self->NPC->blockedSpeechDebounceTime = 0;
00308                                 G_AddVoiceEvent( self, speech, Q_irand( 3000, 5000 ) );
00309                                 self->delay = level.time + Q_irand( 5000, 7000 );
00310                         }
00311                 }
00312                 else
00313                 {
00314                         NPC_Pain(self, attacker, damage);
00315                 }
00316         }
00317         else if ( hitLoc == HL_GENERIC1 )
00318         {
00319                 NPC_SetPainEvent( self );
00320                 //self->s.powerups |= ( 1 << PW_SHOCKED );
00321                 //self->client->ps.powerups[PW_SHOCKED] = level.time + Q_irand( 500, 2500 );
00322                 self->client->ps.electrifyTime = level.time + Q_irand(500, 2500);
00323         }
00324 
00325         if ( inflictor && inflictor->lastEnemy == self )
00326         {//He force-pushed my own lobfires back at me
00327                 if ( mod == MOD_REPEATER_ALT && !Q_irand( 0, 2 ) )
00328                 {
00329                         if ( TIMER_Done( self, "noRapid" ) )
00330                         {
00331                                 self->NPC->scriptFlags &= ~SCF_ALT_FIRE;
00332                                 self->alt_fire = qfalse;
00333                                 TIMER_Set( self, "noLob", Q_irand( 2000, 6000 ) );
00334                         }
00335                         else
00336                         {//hopefully this will make us fire the laser
00337                                 TIMER_Set( self, "noLob", Q_irand( 1000, 2000 ) );
00338                         }
00339                 }
00340                 else if ( mod == MOD_REPEATER && !Q_irand( 0, 5 ) )
00341                 {
00342                         if ( TIMER_Done( self, "noLob" ) )
00343                         {
00344                                 self->NPC->scriptFlags |= SCF_ALT_FIRE;
00345                                 self->alt_fire = qtrue;
00346                                 TIMER_Set( self, "noRapid", Q_irand( 2000, 6000 ) );
00347                         }
00348                         else
00349                         {//hopefully this will make us fire the laser
00350                                 TIMER_Set( self, "noRapid", Q_irand( 1000, 2000 ) );
00351                         }
00352                 }
00353         }
00354 }

void NPC_GM_StartLaser void   ) 
 

Definition at line 558 of file NPC_AI_GalakMech.c.

References CHAN_AUTO, gentity_s::client, entityShared_t::currentOrigin, G_EffectIndex(), G_PlayEffectID(), G_SoundOnEnt(), gentity_s::lockCount, NPC, NPC_SetAnim(), gclient_s::ps, gentity_s::r, SETANIM_FLAG_HOLD, SETANIM_FLAG_OVERRIDE, SETANIM_TORSO, TIMER_Set(), playerState_s::torsoTimer, and vec3_origin.

Referenced by NPC_BSGM_Attack().

00559 {
00560         if ( !NPC->lockCount )
00561         {//haven't already started a laser attack
00562                 //warm up for the beam attack
00563 #if 0
00564                 NPC_SetAnim( NPC, SETANIM_TORSO, TORSO_RAISEWEAP2, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
00565 #endif
00566                 TIMER_Set( NPC, "beamDelay", NPC->client->ps.torsoTimer );
00567                 TIMER_Set( NPC, "attackDelay", NPC->client->ps.torsoTimer+3000 );
00568                 NPC->lockCount = 1;
00569                 //turn on warmup effect
00570                 G_PlayEffectID( G_EffectIndex("galak/beam_warmup"), NPC->r.currentOrigin, vec3_origin );
00571                 G_SoundOnEnt( NPC, CHAN_AUTO, "sound/weapons/galak/lasercharge.wav" );
00572         }
00573 }

void NPC_SetPainEvent gentity_t self  ) 
 

Definition at line 133 of file NPC_reactions.c.

References gNPC_t::aiFlags, gentity_s::client, EV_PAIN, floor(), G_AddEvent(), gentity_t, gentity_s::health, gentity_s::NPC, NPCAI_DIE_ON_IMPACT, gclient_s::ps, STAT_MAX_HEALTH, playerState_s::stats, TID_CHAN_VOICE, and trap_ICARUS_TaskIDPending().

Referenced by NPC_ChoosePainAnimation(), and NPC_GM_Pain().

00134 {
00135         if ( !self->NPC || !(self->NPC->aiFlags&NPCAI_DIE_ON_IMPACT) )
00136         {
00137         // no more borg
00138         //      if( self->client->playerTeam != TEAM_BORG )
00139         //      {
00140                         //if ( !Q3_TaskIDPending( self, TID_CHAN_VOICE ) )
00141                         if (!trap_ICARUS_TaskIDPending(self, TID_CHAN_VOICE) && self->client)
00142                         {
00143                                 //G_AddEvent( self, EV_PAIN, floor((float)self->health/self->max_health*100.0f) );
00144                                 G_AddEvent( self, EV_PAIN, floor((float)self->health/self->client->ps.stats[STAT_MAX_HEALTH]*100.0f) );
00145                                 //rwwFIXMEFIXME: Do this properly?
00146                         }
00147         //      }
00148         }
00149 }

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,
float  maxSpeed,
float  idealSpeed,
qboolean  mustHit
 

Definition at line 2080 of file g_weapon.c.

References trace_t::allsolid, BG_EvaluateTrajectory(), trace_t::endpos, trace_t::entityNum, ENTITYNUM_WORLD, floor(), trace_t::fraction, g_entities, g_gravity, gentity_t, level, cplane_s::normal, OnSameTeam(), trace_t::plane, Q3_INFINITE, qboolean, qfalse, qtrue, trace_t::startsolid, gentity_s::takedamage, level_locals_t::time, TR_GRAVITY, trap_Trace(), trajectory_t::trBase, trajectory_t::trDelta, trajectory_t::trTime, trajectory_t::trType, vmCvar_t::value, vec3_t, VectorCopy, VectorNormalize(), VectorScale, and VectorSubtract.

Referenced by NPC_BSGM_Attack().

02084 { //for the galak mech NPC
02085         float   targetDist, shotSpeed, speedInc = 100, travelTime, impactDist, bestImpactDist = Q3_INFINITE;//fireSpeed, 
02086         vec3_t  targetDir, shotVel, failCase; 
02087         trace_t trace;
02088         trajectory_t    tr;
02089         qboolean        blocked;
02090         int             elapsedTime, skipNum, timeStep = 500, hitCount = 0, maxHits = 7;
02091         vec3_t  lastPos, testPos;
02092         gentity_t       *traceEnt;
02093         
02094         if ( !idealSpeed )
02095         {
02096                 idealSpeed = 300;
02097         }
02098         else if ( idealSpeed < speedInc )
02099         {
02100                 idealSpeed = speedInc;
02101         }
02102         shotSpeed = idealSpeed;
02103         skipNum = (idealSpeed-speedInc)/speedInc;
02104         if ( !minSpeed )
02105         {
02106                 minSpeed = 100;
02107         }
02108         if ( !maxSpeed )
02109         {
02110                 maxSpeed = 900;
02111         }
02112         while ( hitCount < maxHits )
02113         {
02114                 VectorSubtract( target, start, targetDir );
02115                 targetDist = VectorNormalize( targetDir );
02116 
02117                 VectorScale( targetDir, shotSpeed, shotVel );
02118                 travelTime = targetDist/shotSpeed;
02119                 shotVel[2] += travelTime * 0.5 * g_gravity.value;
02120 
02121                 if ( !hitCount )                
02122                 {//save the first (ideal) one as the failCase (fallback value)
02123                         if ( !mustHit )
02124                         {//default is fine as a return value
02125                                 VectorCopy( shotVel, failCase );
02126                         }
02127                 }
02128 
02129                 if ( tracePath )
02130                 {//do a rough trace of the path
02131                         blocked = qfalse;
02132 
02133                         VectorCopy( start, tr.trBase );
02134                         VectorCopy( shotVel, tr.trDelta );
02135                         tr.trType = TR_GRAVITY;
02136                         tr.trTime = level.time;
02137                         travelTime *= 1000.0f;
02138                         VectorCopy( start, lastPos );
02139                         
02140                         //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?
02141                         for ( elapsedTime = timeStep; elapsedTime < floor(travelTime)+timeStep; elapsedTime += timeStep )
02142                         {
02143                                 if ( (float)elapsedTime > travelTime )
02144                                 {//cap it
02145                                         elapsedTime = floor( travelTime );
02146                                 }
02147                                 BG_EvaluateTrajectory( &tr, level.time + elapsedTime, testPos );
02148                                 trap_Trace( &trace, lastPos, mins, maxs, testPos, ignoreEntNum, clipmask );
02149 
02150                                 if ( trace.allsolid || trace.startsolid )
02151                                 {
02152                                         blocked = qtrue;
02153                                         break;
02154                                 }
02155                                 if ( trace.fraction < 1.0f )
02156                                 {//hit something
02157                                         if ( trace.entityNum == enemyNum )
02158                                         {//hit the enemy, that's perfect!
02159                                                 break;
02160                                         }
02161                                         else if ( trace.plane.normal[2] > 0.7 && DistanceSquared( trace.endpos, target ) < 4096 )//hit within 64 of desired location, should be okay
02162                                         {//close enough!
02163                                                 break;
02164                                         }
02165                                         else
02166                                         {//FIXME: maybe find the extents of this brush and go above or below it on next try somehow?
02167                                                 impactDist = DistanceSquared( trace.endpos, target );
02168                                                 if ( impactDist < bestImpactDist )
02169                                                 {
02170                                                         bestImpactDist = impactDist;
02171                                                         VectorCopy( shotVel, failCase );
02172                                                 }
02173                                                 blocked = qtrue;
02174                                                 //see if we should store this as the failCase
02175                                                 if ( trace.entityNum < ENTITYNUM_WORLD )
02176                                                 {//hit an ent
02177                                                         traceEnt = &g_entities[trace.entityNum];
02178                                                         if ( traceEnt && traceEnt->takedamage && !OnSameTeam( self, traceEnt ) )
02179                                                         {//hit something breakable, so that's okay
02180                                                                 //we haven't found a clear shot yet so use this as the failcase
02181                                                                 VectorCopy( shotVel, failCase );
02182                                                         }
02183                                                 }
02184                                                 break;
02185                                         }
02186                                 }
02187                                 if ( elapsedTime == floor( travelTime ) )
02188                                 {//reached end, all clear
02189                                         break;
02190                                 }
02191                                 else
02192                                 {
02193                                         //all clear, try next slice
02194                                         VectorCopy( testPos, lastPos );
02195                                 }
02196                         }
02197                         if ( blocked )
02198                         {//hit something, adjust speed (which will change arc)
02199                                 hitCount++;
02200                                 shotSpeed = idealSpeed + ((hitCount-skipNum) * speedInc);//from min to max (skipping ideal)
02201                                 if ( hitCount >= skipNum )
02202                                 {//skip ideal since that was the first value we tested
02203                                         shotSpeed += speedInc;
02204                                 }
02205                         }
02206                         else
02207                         {//made it!
02208                                 break;
02209                         }
02210                 }
02211                 else
02212                 {//no need to check the path, go with first calc
02213                         break;
02214                 }
02215         }
02216 
02217         if ( hitCount >= maxHits )
02218         {//NOTE: worst case scenario, use the one that impacted closest to the target (or just use the first try...?)
02219                 VectorCopy( failCase, velocity );
02220                 return qfalse;
02221         }
02222         VectorCopy( shotVel, velocity );
02223         return qtrue;
02224 }