123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598 |
- // leave this line at the top of all AI_xxxx.cpp files for PCH reasons...
- #include "g_headers.h"
- #include "b_local.h"
- #include "g_nav.h"
- gentity_t *CreateMissile( vec3_t org, vec3_t dir, float vel, int life, gentity_t *owner, qboolean altFire = qfalse );
- extern gitem_t *FindItemForAmmo( ammo_t ammo );
- //Local state enums
- enum
- {
- LSTATE_NONE = 0,
- LSTATE_BACKINGUP,
- LSTATE_SPINNING,
- LSTATE_PAIN,
- LSTATE_DROP
- };
- void ImperialProbe_Idle( void );
- void NPC_Probe_Precache(void)
- {
- for ( int i = 1; i < 4; i++)
- {
- G_SoundIndex( va( "sound/chars/probe/misc/probetalk%d", i ) );
- }
- G_SoundIndex( "sound/chars/probe/misc/probedroidloop" );
- G_SoundIndex("sound/chars/probe/misc/anger1");
- G_SoundIndex("sound/chars/probe/misc/fire");
- G_EffectIndex( "chunks/probehead" );
- G_EffectIndex( "env/med_explode2" );
- G_EffectIndex( "explosions/probeexplosion1");
- G_EffectIndex( "bryar/muzzle_flash" );
- RegisterItem( FindItemForAmmo( AMMO_BLASTER ));
- RegisterItem( FindItemForWeapon( WP_BRYAR_PISTOL ) );
- }
- /*
- -------------------------
- Hunter_MaintainHeight
- -------------------------
- */
- #define VELOCITY_DECAY 0.85f
- void ImperialProbe_MaintainHeight( void )
- {
- float dif;
- // vec3_t endPos;
- // trace_t trace;
- // Update our angles regardless
- NPC_UpdateAngles( qtrue, qtrue );
- // If we have an enemy, we should try to hover at about enemy eye level
- if ( NPC->enemy )
- {
- // Find the height difference
- dif = NPC->enemy->currentOrigin[2] - NPC->currentOrigin[2];
- // cap to prevent dramatic height shifts
- if ( fabs( dif ) > 8 )
- {
- if ( fabs( dif ) > 16 )
- {
- dif = ( dif < 0 ? -16 : 16 );
- }
- NPC->client->ps.velocity[2] = (NPC->client->ps.velocity[2]+dif)/2;
- }
- }
- else
- {
- gentity_t *goal = NULL;
- if ( NPCInfo->goalEntity ) // Is there a goal?
- {
- goal = NPCInfo->goalEntity;
- }
- else
- {
- goal = NPCInfo->lastGoalEntity;
- }
- if ( goal )
- {
- dif = goal->currentOrigin[2] - NPC->currentOrigin[2];
- if ( fabs( dif ) > 24 )
- {
- ucmd.upmove = ( ucmd.upmove < 0 ? -4 : 4 );
- }
- else
- {
- if ( NPC->client->ps.velocity[2] )
- {
- NPC->client->ps.velocity[2] *= VELOCITY_DECAY;
- if ( fabs( NPC->client->ps.velocity[2] ) < 2 )
- {
- NPC->client->ps.velocity[2] = 0;
- }
- }
- }
- }
- // Apply friction
- else if ( NPC->client->ps.velocity[2] )
- {
- NPC->client->ps.velocity[2] *= VELOCITY_DECAY;
- if ( fabs( NPC->client->ps.velocity[2] ) < 1 )
- {
- NPC->client->ps.velocity[2] = 0;
- }
- }
- // Stay at a given height until we take on an enemy
- /* VectorSet( endPos, NPC->currentOrigin[0], NPC->currentOrigin[1], NPC->currentOrigin[2] - 512 );
- gi.trace( &trace, NPC->currentOrigin, NULL, NULL, endPos, NPC->s.number, MASK_SOLID );
- if ( trace.fraction != 1.0f )
- {
- float length = ( trace.fraction * 512 );
- if ( length < 80 )
- {
- ucmd.upmove = 32;
- }
- else if ( length > 120 )
- {
- ucmd.upmove = -32;
- }
- else
- {
- if ( NPC->client->ps.velocity[2] )
- {
- NPC->client->ps.velocity[2] *= VELOCITY_DECAY;
- if ( fabs( NPC->client->ps.velocity[2] ) < 1 )
- {
- NPC->client->ps.velocity[2] = 0;
- }
- }
- }
- } */
- }
- // Apply friction
- if ( NPC->client->ps.velocity[0] )
- {
- NPC->client->ps.velocity[0] *= VELOCITY_DECAY;
- if ( fabs( NPC->client->ps.velocity[0] ) < 1 )
- {
- NPC->client->ps.velocity[0] = 0;
- }
- }
- if ( NPC->client->ps.velocity[1] )
- {
- NPC->client->ps.velocity[1] *= VELOCITY_DECAY;
- if ( fabs( NPC->client->ps.velocity[1] ) < 1 )
- {
- NPC->client->ps.velocity[1] = 0;
- }
- }
- }
- /*
- -------------------------
- ImperialProbe_Strafe
- -------------------------
- */
- #define HUNTER_STRAFE_VEL 256
- #define HUNTER_STRAFE_DIS 200
- #define HUNTER_UPWARD_PUSH 32
- void ImperialProbe_Strafe( void )
- {
- int dir;
- vec3_t end, right;
- trace_t tr;
- AngleVectors( NPC->client->renderInfo.eyeAngles, NULL, right, NULL );
- // Pick a random strafe direction, then check to see if doing a strafe would be
- // reasonable valid
- dir = ( rand() & 1 ) ? -1 : 1;
- VectorMA( NPC->currentOrigin, HUNTER_STRAFE_DIS * dir, right, end );
- gi.trace( &tr, NPC->currentOrigin, NULL, NULL, end, NPC->s.number, MASK_SOLID );
- // Close enough
- if ( tr.fraction > 0.9f )
- {
- VectorMA( NPC->client->ps.velocity, HUNTER_STRAFE_VEL * dir, right, NPC->client->ps.velocity );
- // Add a slight upward push
- NPC->client->ps.velocity[2] += HUNTER_UPWARD_PUSH;
- // Set the strafe start time so we can do a controlled roll
- NPC->fx_time = level.time;
- NPCInfo->standTime = level.time + 3000 + random() * 500;
- }
- }
- /*
- -------------------------
- ImperialProbe_Hunt
- -------------------------`
- */
- #define HUNTER_FORWARD_BASE_SPEED 10
- #define HUNTER_FORWARD_MULTIPLIER 5
- void ImperialProbe_Hunt( qboolean visible, qboolean advance )
- {
- float distance, speed;
- vec3_t forward;
- NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_RUN1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
- //If we're not supposed to stand still, pursue the player
- if ( NPCInfo->standTime < level.time )
- {
- // Only strafe when we can see the player
- if ( visible )
- {
- ImperialProbe_Strafe();
- return;
- }
- }
- //If we don't want to advance, stop here
- if ( advance == qfalse )
- return;
- //Only try and navigate if the player is visible
- if ( visible == qfalse )
- {
- // Move towards our goal
- NPCInfo->goalEntity = NPC->enemy;
- NPCInfo->goalRadius = 12;
- NPC_MoveToGoal(qtrue);
- return;
- }
- else
- {
- VectorSubtract( NPC->enemy->currentOrigin, NPC->currentOrigin, forward );
- distance = VectorNormalize( forward );
- }
- speed = HUNTER_FORWARD_BASE_SPEED + HUNTER_FORWARD_MULTIPLIER * g_spskill->integer;
- VectorMA( NPC->client->ps.velocity, speed, forward, NPC->client->ps.velocity );
- }
- /*
- -------------------------
- ImperialProbe_FireBlaster
- -------------------------
- */
- void ImperialProbe_FireBlaster(void)
- {
- vec3_t muzzle1,enemy_org1,delta1,angleToEnemy1;
- static vec3_t forward, vright, up;
- static vec3_t muzzle;
- gentity_t *missile;
- mdxaBone_t boltMatrix;
- //FIXME: use {0, NPC->client->ps.legsYaw, 0}
- gi.G2API_GetBoltMatrix( NPC->ghoul2, NPC->playerModel,
- NPC->genericBolt1,
- &boltMatrix, NPC->currentAngles, NPC->currentOrigin, (cg.time?cg.time:level.time),
- NULL, NPC->s.modelScale );
- gi.G2API_GiveMeVectorFromMatrix( boltMatrix, ORIGIN, muzzle1 );
- G_PlayEffect( "bryar/muzzle_flash", muzzle1 );
- G_Sound( NPC, G_SoundIndex( "sound/chars/probe/misc/fire" ));
- if (NPC->health)
- {
- CalcEntitySpot( NPC->enemy, SPOT_CHEST, enemy_org1 );
- enemy_org1[0]+= Q_irand(0,10);
- enemy_org1[1]+= Q_irand(0,10);
- VectorSubtract (enemy_org1, muzzle1, delta1);
- vectoangles ( delta1, angleToEnemy1 );
- AngleVectors (angleToEnemy1, forward, vright, up);
- }
- else
- {
- AngleVectors (NPC->currentAngles, forward, vright, up);
- }
- missile = CreateMissile( muzzle1, forward, 1600, 10000, NPC );
- missile->classname = "bryar_proj";
- missile->s.weapon = WP_BRYAR_PISTOL;
- if ( g_spskill->integer <= 1 )
- {
- missile->damage = 5;
- }
- else
- {
- missile->damage = 10;
- }
- missile->dflags = DAMAGE_DEATH_KNOCKBACK;
- missile->methodOfDeath = MOD_ENERGY;
- missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
- }
- /*
- -------------------------
- ImperialProbe_Ranged
- -------------------------
- */
- void ImperialProbe_Ranged( qboolean visible, qboolean advance )
- {
- int delay_min,delay_max;
- if ( TIMER_Done( NPC, "attackDelay" ) ) // Attack?
- {
- if ( g_spskill->integer == 0 )
- {
- delay_min = 500;
- delay_max = 3000;
- }
- else if ( g_spskill->integer > 1 )
- {
- delay_min = 500;
- delay_max = 2000;
- }
- else
- {
- delay_min = 300;
- delay_max = 1500;
- }
- TIMER_Set( NPC, "attackDelay", Q_irand( 500, 3000 ) );
- ImperialProbe_FireBlaster();
- // ucmd.buttons |= BUTTON_ATTACK;
- }
- if ( NPCInfo->scriptFlags & SCF_CHASE_ENEMIES )
- {
- ImperialProbe_Hunt( visible, advance );
- }
- }
- /*
- -------------------------
- ImperialProbe_AttackDecision
- -------------------------
- */
- #define MIN_MELEE_RANGE 320
- #define MIN_MELEE_RANGE_SQR ( MIN_MELEE_RANGE * MIN_MELEE_RANGE )
- #define MIN_DISTANCE 128
- #define MIN_DISTANCE_SQR ( MIN_DISTANCE * MIN_DISTANCE )
- void ImperialProbe_AttackDecision( void )
- {
- // Always keep a good height off the ground
- ImperialProbe_MaintainHeight();
- //randomly talk
- if ( TIMER_Done(NPC,"patrolNoise") )
- {
- if (TIMER_Done(NPC,"angerNoise"))
- {
- G_SoundOnEnt( NPC, CHAN_AUTO, va("sound/chars/probe/misc/probetalk%d", Q_irand(1, 3)) );
- TIMER_Set( NPC, "patrolNoise", Q_irand( 4000, 10000 ) );
- }
- }
- // If we don't have an enemy, just idle
- if ( NPC_CheckEnemyExt() == qfalse )
- {
- ImperialProbe_Idle();
- return;
- }
- NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_RUN1, SETANIM_FLAG_NORMAL);
- // Rate our distance to the target, and our visibilty
- float distance = (int) DistanceHorizontalSquared( NPC->currentOrigin, NPC->enemy->currentOrigin );
- // distance_e distRate = ( distance > MIN_MELEE_RANGE_SQR ) ? DIST_LONG : DIST_MELEE;
- qboolean visible = NPC_ClearLOS( NPC->enemy );
- qboolean advance = (qboolean)(distance > MIN_DISTANCE_SQR);
- // If we cannot see our target, move to see it
- if ( visible == qfalse )
- {
- if ( NPCInfo->scriptFlags & SCF_CHASE_ENEMIES )
- {
- ImperialProbe_Hunt( visible, advance );
- return;
- }
- }
- // Sometimes I have problems with facing the enemy I'm attacking, so force the issue so I don't look dumb
- NPC_FaceEnemy( qtrue );
- // Decide what type of attack to do
- ImperialProbe_Ranged( visible, advance );
- }
- /*
- -------------------------
- NPC_BSDroid_Pain
- -------------------------
- */
- void NPC_Probe_Pain( gentity_t *self, gentity_t *inflictor, gentity_t *other, const vec3_t point, int damage, int mod,int hitLoc )
- {
- float pain_chance;
-
- VectorCopy( self->NPC->lastPathAngles, self->s.angles );
- if ( self->health < 30 || mod == MOD_DEMP2 || mod == MOD_DEMP2_ALT ) // demp2 always messes them up real good
- {
- vec3_t endPos;
- trace_t trace;
- VectorSet( endPos, self->currentOrigin[0], self->currentOrigin[1], self->currentOrigin[2] - 128 );
- gi.trace( &trace, self->currentOrigin, NULL, NULL, endPos, self->s.number, MASK_SOLID );
- if ( trace.fraction == 1.0f || mod == MOD_DEMP2 ) // demp2 always does this
- {
- if (self->client->clientInfo.headModel != 0)
- {
- vec3_t origin;
- VectorCopy(self->currentOrigin,origin);
- origin[2] +=50;
- // G_PlayEffect( "small_chunks", origin );
- G_PlayEffect( "chunks/probehead", origin );
- G_PlayEffect( "env/med_explode2", origin );
- self->client->clientInfo.headModel = 0;
- self->client->moveType = MT_RUNJUMP;
- self->client->ps.gravity = g_gravity->value*.1;
- }
-
- if ( (mod == MOD_DEMP2 || mod == MOD_DEMP2_ALT) && other )
- {
- vec3_t dir;
- NPC_SetAnim( self, SETANIM_BOTH, BOTH_PAIN1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD);
- VectorSubtract( self->currentOrigin, other->currentOrigin, dir );
- VectorNormalize( dir );
- VectorMA( self->client->ps.velocity, 550, dir, self->client->ps.velocity );
- self->client->ps.velocity[2] -= 127;
- }
- self->s.powerups |= ( 1 << PW_SHOCKED );
- self->client->ps.powerups[PW_SHOCKED] = level.time + 3000;
- self->NPC->localState = LSTATE_DROP;
- }
- }
- else
- {
- pain_chance = NPC_GetPainChance( self, damage );
- if ( random() < pain_chance ) // Spin around in pain?
- {
- NPC_SetAnim( self, SETANIM_BOTH, BOTH_PAIN1, SETANIM_FLAG_OVERRIDE);
- }
- }
- NPC_Pain( self, inflictor, other, point, damage, mod);
- }
- /*
- -------------------------
- ImperialProbe_Idle
- -------------------------
- */
- void ImperialProbe_Idle( void )
- {
- ImperialProbe_MaintainHeight();
- NPC_BSIdle();
- }
- /*
- -------------------------
- NPC_BSImperialProbe_Patrol
- -------------------------
- */
- void ImperialProbe_Patrol( void )
- {
- ImperialProbe_MaintainHeight();
- if ( NPC_CheckPlayerTeamStealth() )
- {
- NPC_UpdateAngles( qtrue, qtrue );
- return;
- }
- //If we have somewhere to go, then do that
- if (!NPC->enemy)
- {
- NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_RUN1, SETANIM_FLAG_NORMAL );
- if ( UpdateGoal() )
- {
- //start loop sound once we move
- NPC->s.loopSound = G_SoundIndex( "sound/chars/probe/misc/probedroidloop" );
- ucmd.buttons |= BUTTON_WALKING;
- NPC_MoveToGoal( qtrue );
- }
- //randomly talk
- if (TIMER_Done(NPC,"patrolNoise"))
- {
- G_SoundOnEnt( NPC, CHAN_AUTO, va("sound/chars/probe/misc/probetalk%d", Q_irand(1, 3)) );
- TIMER_Set( NPC, "patrolNoise", Q_irand( 2000, 4000 ) );
- }
- }
- else // He's got an enemy. Make him angry.
- {
- G_SoundOnEnt( NPC, CHAN_AUTO, "sound/chars/probe/misc/anger1" );
- TIMER_Set( NPC, "angerNoise", Q_irand( 2000, 4000 ) );
- //NPCInfo->behaviorState = BS_HUNT_AND_KILL;
- }
- NPC_UpdateAngles( qtrue, qtrue );
- }
- /*
- -------------------------
- ImperialProbe_Wait
- -------------------------
- */
- void ImperialProbe_Wait(void)
- {
- if ( NPCInfo->localState == LSTATE_DROP )
- {
- vec3_t endPos;
- trace_t trace;
- NPCInfo->desiredYaw = AngleNormalize360( NPCInfo->desiredYaw + 25 );
- VectorSet( endPos, NPC->currentOrigin[0], NPC->currentOrigin[1], NPC->currentOrigin[2] - 32 );
- gi.trace( &trace, NPC->currentOrigin, NULL, NULL, endPos, NPC->s.number, MASK_SOLID );
- if ( trace.fraction != 1.0f )
- {
- G_Damage(NPC, NPC->enemy, NPC->enemy, NULL, NULL, 2000, 0,MOD_UNKNOWN);
- }
- }
- NPC_UpdateAngles( qtrue, qtrue );
- }
- /*
- -------------------------
- NPC_BSImperialProbe_Default
- -------------------------
- */
- void NPC_BSImperialProbe_Default( void )
- {
- if ( NPC->enemy )
- {
- NPCInfo->goalEntity = NPC->enemy;
- ImperialProbe_AttackDecision();
- }
- else if ( NPCInfo->scriptFlags & SCF_LOOK_FOR_ENEMIES )
- {
- ImperialProbe_Patrol();
- }
- else if ( NPCInfo->localState == LSTATE_DROP )
- {
- ImperialProbe_Wait();
- }
- else
- {
- ImperialProbe_Idle();
- }
- }
|