123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443 |
- // leave this line at the top of all AI_xxxx.cpp files for PCH reasons...
- #include "g_headers.h"
- // 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"
- #include "anims.h"
- #include "g_navigator.h"
- #include "wp_saber.h"
- //extern void G_AddVoiceEvent( gentity_t *self, int event, int speakDebounceTime );
- extern void WP_DeactivateSaber( gentity_t *self, qboolean clearLength = qfalse );
- extern int PM_AnimLength( int index, animNumber_t anim );
- qboolean NPC_CheckPlayerTeamStealth( void );
- static qboolean enemyLOS;
- static qboolean enemyCS;
- static qboolean faceEnemy;
- static qboolean move;
- static qboolean shoot;
- static float enemyDist;
- /*
- void NPC_SaberDroid_PlayConfusionSound( gentity_t *self )
- {//FIXME: make this a custom sound in sound set
- if ( self->health > 0 )
- {
- G_AddVoiceEvent( self, Q_irand(EV_CONFUSE1, EV_CONFUSE3), 2000 );
- }
- //reset him to be totally unaware again
- TIMER_Set( self, "enemyLastVisible", 0 );
- TIMER_Set( self, "flee", 0 );
- self->NPC->squadState = SQUAD_IDLE;
- self->NPC->tempBehavior = BS_DEFAULT;
-
- //self->NPC->behaviorState = BS_PATROL;
- G_ClearEnemy( self );//FIXME: or just self->enemy = NULL;?
- self->NPC->investigateCount = 0;
- }
- */
- /*
- -------------------------
- ST_Move
- -------------------------
- */
- static qboolean SaberDroid_Move( void )
- {
- NPCInfo->combatMove = qtrue;//always move straight toward our goal
- UpdateGoal();
- if ( !NPCInfo->goalEntity )
- {
- NPCInfo->goalEntity = NPC->enemy;
- }
- NPCInfo->goalRadius = 30.0f;
- qboolean moved = NPC_MoveToGoal( qtrue );
- // navInfo_t info;
-
- //Get the move info
- // NAV_GetLastMove( info );
- //FIXME: if we bump into another one of our guys and can't get around him, just stop!
- //If we hit our target, then stop and fire!
- // if ( info.flags & NIF_COLLISION )
- // {
- // if ( info.blocker == NPC->enemy )
- // {
- // SaberDroid_HoldPosition();
- // }
- // }
- //If our move failed, then reset
- /*
- if ( moved == qfalse )
- {//couldn't get to enemy
- //just hang here
- SaberDroid_HoldPosition();
- }
- */
- return moved;
- }
- /*
- -------------------------
- NPC_BSSaberDroid_Patrol
- -------------------------
- */
- void NPC_BSSaberDroid_Patrol( void )
- {//FIXME: pick up on bodies of dead buddies?
- if ( NPCInfo->confusionTime < level.time )
- {
- //Look for any enemies
- if ( NPCInfo->scriptFlags&SCF_LOOK_FOR_ENEMIES )
- {
- if ( NPC_CheckPlayerTeamStealth() )
- {
- //NPCInfo->behaviorState = BS_HUNT_AND_KILL;//should be automatic now
- //NPC_AngerSound();
- NPC_UpdateAngles( qtrue, qtrue );
- return;
- }
- }
- if ( !(NPCInfo->scriptFlags&SCF_IGNORE_ALERTS) )
- {
- //Is there danger nearby
- int alertEvent = NPC_CheckAlertEvents( qtrue, qtrue, -1, qfalse, AEL_SUSPICIOUS );
- //There is an event to look at
- if ( alertEvent >= 0 )//&& level.alertEvents[alertEvent].ID != NPCInfo->lastAlertID )
- {
- //NPCInfo->lastAlertID = level.alertEvents[alertEvent].ID;
- if ( level.alertEvents[alertEvent].level >= AEL_DISCOVERED )
- {
- if ( level.alertEvents[alertEvent].owner &&
- level.alertEvents[alertEvent].owner->client &&
- level.alertEvents[alertEvent].owner->health >= 0 &&
- level.alertEvents[alertEvent].owner->client->playerTeam == NPC->client->enemyTeam )
- {//an enemy
- G_SetEnemy( NPC, level.alertEvents[alertEvent].owner );
- //NPCInfo->enemyLastSeenTime = level.time;
- TIMER_Set( NPC, "attackDelay", Q_irand( 500, 2500 ) );
- }
- }
- else
- {//FIXME: get more suspicious over time?
- //Save the position for movement (if necessary)
- VectorCopy( level.alertEvents[alertEvent].position, NPCInfo->investigateGoal );
- NPCInfo->investigateDebounceTime = level.time + Q_irand( 500, 1000 );
- if ( level.alertEvents[alertEvent].level == AEL_SUSPICIOUS )
- {//suspicious looks longer
- NPCInfo->investigateDebounceTime += Q_irand( 500, 2500 );
- }
- }
- }
- if ( NPCInfo->investigateDebounceTime > level.time )
- {//FIXME: walk over to it, maybe? Not if not chase enemies
- //NOTE: stops walking or doing anything else below
- vec3_t dir, angles;
- float o_yaw, o_pitch;
-
- VectorSubtract( NPCInfo->investigateGoal, NPC->client->renderInfo.eyePoint, dir );
- vectoangles( dir, angles );
-
- o_yaw = NPCInfo->desiredYaw;
- o_pitch = NPCInfo->desiredPitch;
- NPCInfo->desiredYaw = angles[YAW];
- NPCInfo->desiredPitch = angles[PITCH];
-
- NPC_UpdateAngles( qtrue, qtrue );
- NPCInfo->desiredYaw = o_yaw;
- NPCInfo->desiredPitch = o_pitch;
- return;
- }
- }
- }
- //If we have somewhere to go, then do that
- if ( UpdateGoal() )
- {
- ucmd.buttons |= BUTTON_WALKING;
- NPC_MoveToGoal( qtrue );
- }
- else if ( !NPC->client->ps.weaponTime
- && TIMER_Done( NPC, "attackDelay" )
- && TIMER_Done( NPC, "inactiveDelay" ) )
- {
- if ( NPC->client->ps.SaberActive() )
- {
- WP_DeactivateSaber( NPC, qfalse );
- NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_TURNOFF, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
- }
- }
- NPC_UpdateAngles( qtrue, qtrue );
- }
- int SaberDroid_PowerLevelForSaberAnim( gentity_t *self )
- {
- switch ( self->client->ps.legsAnim )
- {
- case BOTH_A2_TR_BL:
- if ( self->client->ps.torsoAnimTimer <= 200 )
- {//end of anim
- return FORCE_LEVEL_0;
- }
- else if ( PM_AnimLength( self->client->clientInfo.animFileIndex, (animNumber_t)self->client->ps.legsAnim ) - self->client->ps.torsoAnimTimer < 200 )
- {//beginning of anim
- return FORCE_LEVEL_0;
- }
- return FORCE_LEVEL_2;
- break;
- case BOTH_A1_BL_TR:
- if ( self->client->ps.torsoAnimTimer <= 300 )
- {//end of anim
- return FORCE_LEVEL_0;
- }
- else if ( PM_AnimLength( self->client->clientInfo.animFileIndex, (animNumber_t)self->client->ps.legsAnim ) - self->client->ps.torsoAnimTimer < 200 )
- {//beginning of anim
- return FORCE_LEVEL_0;
- }
- return FORCE_LEVEL_1;
- break;
- case BOTH_A1__L__R:
- if ( self->client->ps.torsoAnimTimer <= 250 )
- {//end of anim
- return FORCE_LEVEL_0;
- }
- else if ( PM_AnimLength( self->client->clientInfo.animFileIndex, (animNumber_t)self->client->ps.legsAnim ) - self->client->ps.torsoAnimTimer < 150 )
- {//beginning of anim
- return FORCE_LEVEL_0;
- }
- return FORCE_LEVEL_1;
- break;
- case BOTH_A3__L__R:
- if ( self->client->ps.torsoAnimTimer <= 200 )
- {//end of anim
- return FORCE_LEVEL_0;
- }
- else if ( PM_AnimLength( self->client->clientInfo.animFileIndex, (animNumber_t)self->client->ps.legsAnim ) - self->client->ps.torsoAnimTimer < 300 )
- {//beginning of anim
- return FORCE_LEVEL_0;
- }
- return FORCE_LEVEL_3;
- break;
- }
- return FORCE_LEVEL_0;
- }
- /*
- -------------------------
- NPC_BSSaberDroid_Attack
- -------------------------
- */
- void NPC_SaberDroid_PickAttack( void )
- {
- int attackAnim = Q_irand( 0, 3 );
- switch ( attackAnim )
- {
- case 0:
- default:
- attackAnim = BOTH_A2_TR_BL;
- NPC->client->ps.saberMove = LS_A_TR2BL;
- NPC->client->ps.saberAnimLevel = SS_MEDIUM;
- break;
- case 1:
- attackAnim = BOTH_A1_BL_TR;
- NPC->client->ps.saberMove = LS_A_BL2TR;
- NPC->client->ps.saberAnimLevel = SS_FAST;
- break;
- case 2:
- attackAnim = BOTH_A1__L__R;
- NPC->client->ps.saberMove = LS_A_L2R;
- NPC->client->ps.saberAnimLevel = SS_FAST;
- break;
- case 3:
- attackAnim = BOTH_A3__L__R;
- NPC->client->ps.saberMove = LS_A_L2R;
- NPC->client->ps.saberAnimLevel = SS_STRONG;
- break;
- }
- NPC->client->ps.saberBlocking = saberMoveData[NPC->client->ps.saberMove].blocking;
- if ( saberMoveData[NPC->client->ps.saberMove].trailLength > 0 )
- {
- NPC->client->ps.SaberActivateTrail( saberMoveData[NPC->client->ps.saberMove].trailLength ); // saber trail lasts for 75ms...feel free to change this if you want it longer or shorter
- }
- else
- {
- NPC->client->ps.SaberDeactivateTrail( 0 );
- }
- NPC_SetAnim( NPC, SETANIM_BOTH, attackAnim, SETANIM_FLAG_HOLD|SETANIM_FLAG_OVERRIDE );
- NPC->client->ps.torsoAnim = NPC->client->ps.legsAnim;//need to do this because we have no anim split but saber code checks torsoAnim
- NPC->client->ps.weaponTime = NPC->client->ps.torsoAnimTimer = NPC->client->ps.legsAnimTimer;
- NPC->client->ps.weaponstate = WEAPON_FIRING;
- }
- void NPC_BSSaberDroid_Attack( void )
- {
- //Don't do anything if we're hurt
- if ( NPC->painDebounceTime > level.time )
- {
- NPC_UpdateAngles( qtrue, qtrue );
- return;
- }
- //NPC_CheckEnemy( qtrue, qfalse );
- //If we don't have an enemy, just idle
- if ( NPC_CheckEnemyExt() == qfalse )//!NPC->enemy )//
- {
- NPC->enemy = NULL;
- NPC_BSSaberDroid_Patrol();//FIXME: or patrol?
- return;
- }
- if ( !NPC->enemy )
- {//WTF? somehow we lost our enemy?
- NPC_BSSaberDroid_Patrol();//FIXME: or patrol?
- return;
- }
- enemyLOS = enemyCS = qfalse;
- move = qtrue;
- faceEnemy = qfalse;
- shoot = qfalse;
- enemyDist = DistanceSquared( NPC->enemy->currentOrigin, NPC->currentOrigin );
- //can we see our target?
- if ( NPC_ClearLOS( NPC->enemy ) )
- {
- NPCInfo->enemyLastSeenTime = level.time;
- enemyLOS = qtrue;
- if ( enemyDist <= 4096 && InFOV( NPC->enemy->currentOrigin, NPC->currentOrigin, NPC->client->ps.viewangles, 90, 45 ) )//within 64 & infront
- {
- VectorCopy( NPC->enemy->currentOrigin, NPCInfo->enemyLastSeenLocation );
- enemyCS = qtrue;
- }
- }
- /*
- else if ( gi.inPVS( NPC->enemy->currentOrigin, NPC->currentOrigin ) )
- {
- NPCInfo->enemyLastSeenTime = level.time;
- faceEnemy = qtrue;
- }
- */
- if ( enemyLOS )
- {//FIXME: no need to face enemy if we're moving to some other goal and he's too far away to shoot?
- faceEnemy = qtrue;
- }
- if ( !TIMER_Done( NPC, "taunting" ) )
- {
- move = qfalse;
- }
- else if ( enemyCS )
- {
- shoot = qtrue;
- if ( enemyDist < (NPC->maxs[0]+NPC->enemy->maxs[0]+32)*(NPC->maxs[0]+NPC->enemy->maxs[0]+32) )
- {//close enough
- move = qfalse;
- }
- }//this should make him chase enemy when out of range...?
- if ( NPC->client->ps.legsAnimTimer
- && NPC->client->ps.legsAnim != BOTH_A3__L__R )//this one is a running attack
- {//in the middle of a held, stationary anim, can't move
- move = qfalse;
- }
- if ( move )
- {//move toward goal
- move = SaberDroid_Move();
- if ( move )
- {//if we had to chase him, be sure to attack as soon as possible
- TIMER_Set( NPC, "attackDelay", NPC->client->ps.weaponTime );
- }
- }
- if ( !faceEnemy )
- {//we want to face in the dir we're running
- if ( move )
- {//don't run away and shoot
- NPCInfo->desiredYaw = NPCInfo->lastPathAngles[YAW];
- NPCInfo->desiredPitch = 0;
- shoot = qfalse;
- }
- NPC_UpdateAngles( qtrue, qtrue );
- }
- else// if ( faceEnemy )
- {//face the enemy
- NPC_FaceEnemy();
- }
- if ( NPCInfo->scriptFlags&SCF_DONT_FIRE )
- {
- shoot = qfalse;
- }
-
- //FIXME: need predicted blocking?
- //FIXME: don't shoot right away!
- if ( shoot )
- {//try to shoot if it's time
- if ( TIMER_Done( NPC, "attackDelay" ) )
- {
- if( !(NPCInfo->scriptFlags & SCF_FIRE_WEAPON) ) // we've already fired, no need to do it again here
- {
- NPC_SaberDroid_PickAttack();
- if ( NPCInfo->rank > RANK_CREWMAN )
- {
- TIMER_Set( NPC, "attackDelay", NPC->client->ps.weaponTime+Q_irand(0, 1000) );
- }
- else
- {
- TIMER_Set( NPC, "attackDelay", NPC->client->ps.weaponTime+Q_irand( 0, 1000 )+(Q_irand( 0, (3-g_spskill->integer)*2 )*500) );
- }
- }
- }
- }
- }
- void NPC_BSSD_Default( void )
- {
- if( !NPC->enemy )
- {//don't have an enemy, look for one
- NPC_BSSaberDroid_Patrol();
- }
- else//if ( NPC->enemy )
- {//have an enemy
- if ( !NPC->client->ps.SaberActive() )
- {
- NPC->client->ps.SaberActivate();
- if ( NPC->client->ps.legsAnim == BOTH_TURNOFF
- || NPC->client->ps.legsAnim == BOTH_STAND1 )
- {
- NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_TURNON, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
- }
- }
- NPC_BSSaberDroid_Attack();
- TIMER_Set( NPC, "inactiveDelay", Q_irand( 2000, 4000 ) );
- }
- if ( !NPC->client->ps.weaponTime )
- {
- NPC->client->ps.saberMove = LS_READY;
- NPC->client->ps.saberBlocking = saberMoveData[LS_READY].blocking;
- NPC->client->ps.SaberDeactivateTrail( 0 );
- NPC->client->ps.saberAnimLevel = SS_MEDIUM;
- NPC->client->ps.weaponstate = WEAPON_READY;
- }
- }
|