Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- //////////////////////////////////////////////////////////////////////////////////
- //////////////////// SPECIAL AI COMBAT FOR SHOOTERS NPC ////////////////////////////////////
- // WORKING ALPHA VERSION
- //////////////////////////////////////////////////////////////////////////////////
- //////////////////// SPECIAL AI COMBAT FOR SHOOTERS NPC ////////////////////////////////////
- void NPC_Precache( void );
- void NPC_DustFallNear(const vec3_t origin, int dustcount);
- void NPC_ChangeWeapon2(int wp);
- qboolean NPC_StopKnockdown(gentity_t *self, gentity_t *pusher, const vec3_t pushDir, qboolean forceKnockdown = qfalse);
- // Flight Related Functions (used Only by flying classes)
- //--------------------------------------------------------
- extern qboolean NPC_Flying( gentity_t *self );
- extern void NPC_FlyStart( gentity_t *self );
- extern void NPC_FlyStop( gentity_t *self );
- // Called From NPC_Pain()
- //-----------------------------
- void NPC_Pain2( gentity_t *self, gentity_t *inflictor, int damage, int mod);
- // Local: Other Tactics
- //----------------------
- void NPC_DoAmbushWait( gentity_t *self);
- void NPC_DoSniper( gentity_t *self);
- // Local: Respawning
- //-------------------
- bool NPC_Respawn();
- // Called From Within AI_Jedi && AI_Seeker
- //-----------------------------------------
- void NPC_Fire();
- void NPC_FireDecide();
- // Local: Called From Tactics()
- //----------------------------
- void NPC_TacticsSelect();
- bool NPC_CanSeeEnemy( gentity_t *self );
- // Called From NPC_RunBehavior()
- //-------------------------------
- void NPC_Update(); // Always Called First, Before Any Other Thinking
- bool NPC_Tactics(); // If returns true, Jedi and Seeker AI not used
- bool NPC_Flee(); // If returns true, Jedi and Seeker AI not used
- ////////////////////////////////////////////////////////////////////////////////////////
- // External Functions
- ////////////////////////////////////////////////////////////////////////////////////////
- extern void G_SoundAtSpot( vec3_t org, int soundIndex, qboolean broadcast );
- extern void G_CreateG2AttachedWeaponModel( gentity_t *ent, const char *weaponModel, int boltNum, int weaponNum );
- extern void ChangeWeapon( gentity_t *ent, int newWeapon );
- extern void WP_ResistForcePush( gentity_t *self, gentity_t *pusher, qboolean noPenalty );
- extern void ForceJump( gentity_t *self, usercmd_t *ucmd );
- extern void G_Knockdown( gentity_t *self, gentity_t *attacker, const vec3_t pushDir, float strength, qboolean breakSaberLock );
- extern void WP_SaberAddG2SaberModels( gentity_t *ent, int specificSaberNum );
- // DUSTY ADD
- extern qboolean heavyWeap(int);
- extern qboolean blasterWeap(int);
- extern int ChooseBestWeapon(gentity_t* ent);
- extern int ChooseWeaponRandom(gentity_t *ent, int wpnGroup);
- extern qboolean lightBlasterWeap(int);
- extern qboolean heavyBlasterWeap(int);
- extern qboolean meleeWeap(int);
- ////////////////////////////////////////////////////////////////////////////////////////
- // Defines
- ////////////////////////////////////////////////////////////////////////////////////////
- #define NPC_SHOOTRANGEMIN 512
- #define NPC_SHOOTRANGEMED 1024 // DISTANCE FOR SWITCHING BY SABER TO SHOOTING
- #define NPC_SHOOTRANGEMAX 65536
- ////////////////////////////////////////////////////////////////////////////////////////
- // Global Data
- ////////////////////////////////////////////////////////////////////////////////////////
- bool NPCHadDeathScript = false;
- bool NPCActive = false;
- vec3_t NPCFootStepLoc;
- int NPCFootStepCount = 0;
- vec3_t AverageEnemyDirection2;
- int AverageEnemyDirectionSamples2;
- ////////////////////////////////////////////////////////////////////////////////////////
- // NPC NEW AGILITY SYSTEM FOR AVOID ATTACKS!
- ////////////////////////////////////////////////////////////////////////////////////////
- enum NpcTacticsState
- {
- NPC_NONE,
- // Attack
- //--------
- NPC_GLYPHIC, // Replace Rifle, alternate medium distance atk
- NPC_MAGIC, // Replace Rifle, medium distance atk
- NPC_ARROWS, // Replace sniper, large distance atk archers
- NPC_NEC_HOLY, // replace missile, large distance atk
- NPC_WARRIOR, // atk for short distance > Glyphic magic
- NPC_FENCER, // saberist NPC!
- NPC_BURNER, // cannonier NPC
- /// Waiting
- //---------
- NPC_AMBUSHWAIT, // Goto CP & Wait
- NPC_MAX
- };
- void NPC_Precache( void )
- {
- G_SoundIndex( "sound/chars/boba/bf_blast-off.wav" );
- G_SoundIndex( "sound/chars/boba/bf_jetpack_lp.wav" );
- G_SoundIndex( "sound/chars/boba/bf_land.wav" );
- G_SoundIndex( "sound/weapons/boba/bf_flame.mp3" );
- G_SoundIndex( "sound/player/footsteps/boot1" );
- G_SoundIndex( "sound/player/footsteps/boot2" );
- G_SoundIndex( "sound/player/footsteps/boot3" );
- G_SoundIndex( "sound/player/footsteps/boot4" );
- G_EffectIndex( "boba/jetSP" );
- G_EffectIndex( "boba/fthrw" );
- G_EffectIndex( "volumetric/black_smoke" );
- G_EffectIndex( "chunks/dustFall" );
- AverageEnemyDirectionSamples2 = 0;
- VectorClear(AverageEnemyDirection2);
- NPCHadDeathScript = false;
- NPCActive = true;
- NPCFootStepCount = 0;
- }
- ////////////////////////////////////////////////////////////////////////////////////////
- //
- ////////////////////////////////////////////////////////////////////////////////////////
- void NPC_DustFallNear(const vec3_t origin, int dustcount)
- {
- if (!NPCActive)
- {
- return;
- }
- trace_t testTrace;
- vec3_t testDirection;
- vec3_t testStartPos;
- vec3_t testEndPos;
- VectorCopy(origin, testStartPos);
- for (int i=0; i<dustcount; i++)
- {
- testDirection[0] = (random() * 2.0f) - 1.0f;
- testDirection[1] = (random() * 2.0f) - 1.0f;
- testDirection[2] = 1.0f;
- VectorMA(origin, 1000.0f, testDirection, testEndPos);
- gi.trace (&testTrace, origin, NULL, NULL, testEndPos, (player && player->inuse)?(0):(ENTITYNUM_NONE), MASK_SHOT, (EG2_Collision)0, 0 );
- if (!testTrace.startsolid &&
- !testTrace.allsolid &&
- testTrace.fraction>0.1f &&
- testTrace.fraction<0.9f)
- {
- G_PlayEffect( "chunks/dustFall", testTrace.endpos, testTrace.plane.normal );
- }
- }
- }
- void NPC_ChangeWeapon2( int wp )
- {
- if ( NPC->s.weapon == wp )
- {
- return;
- }
- NPC_ChangeWeapon2( wp );
- G_AddEvent( NPC, EV_GENERAL_SOUND, G_SoundIndex( "sound/weapons/change.wav" ));
- }
- qboolean NPC_StopKnockdown( gentity_t *self, gentity_t *pusher, const vec3_t pushDir, qboolean forceKnockdown )
- {
- if ( self->client->NPC_class == CLASS_BOBAFETT )// for others, not for boba.
- {
- return qfalse;
- }
- if ( self->client->moveType == MT_FLYSWIM )
- {//can't knock me down when I'm flying
- return qtrue;
- }
- vec3_t pDir, fwd, right, ang = {0, self->currentAngles[YAW], 0};
- float fDot, rDot;
- int strafeTime = Q_irand( 1000, 2000 );
- AngleVectors( ang, fwd, right, NULL );
- VectorNormalize2( pushDir, pDir );
- fDot = DotProduct( pDir, fwd );
- rDot = DotProduct( pDir, right );
- if ( Q_irand( 0, 2 ) )
- {//flip or roll with it
- usercmd_t tempCmd;
- if ( fDot >= 0.4f )
- {
- tempCmd.forwardmove = 127;
- TIMER_Set( self, "moveforward", strafeTime );
- }
- else if ( fDot <= -0.4f )
- {
- tempCmd.forwardmove = -127;
- TIMER_Set( self, "moveback", strafeTime );
- }
- else if ( rDot > 0 )
- {
- tempCmd.rightmove = 127;
- TIMER_Set( self, "strafeRight", strafeTime );
- TIMER_Set( self, "strafeLeft", -1 );
- }
- else
- {
- tempCmd.rightmove = -127;
- TIMER_Set( self, "strafeLeft", strafeTime );
- TIMER_Set( self, "strafeRight", -1 );
- }
- G_AddEvent( self, EV_JUMP, 0 );
- if ( !Q_irand( 0, 1 ) )
- {//flip
- self->client->ps.forceJumpCharge = 280;//FIXME: calc this intelligently?
- //ForceJump( self, &tempCmd );
- }
- else
- {//roll
- TIMER_Set( self, "duck", strafeTime );
- }
- self->painDebounceTime = 0;//so we do something
- }
- else
- {//fall down
- return qfalse;
- }
- return qtrue;
- }
- qboolean NPC_Flying( gentity_t *self )
- {
- assert(self && self->client && self->client->NPC_class==CLASS_BOBAFETT);//self->NPC &&
- // ONLY FOR FLYING CLASSES
- return ((qboolean)(self->client->moveType==MT_FLYSWIM));
- }
- ////////////////////////////////////////////////////////////////////////////////////////
- //
- ////////////////////////////////////////////////////////////////////////////////////////
- bool NPC_CanSeeEnemy( gentity_t *self )
- {
- assert(self && self->NPC && self->client && self->client->NPC_class==CLASS_BOBAFETT ||
- self && self->NPC && self->client && self->client->NPC_class==CLASS_SITH ||
- self && self->NPC && self->client && self->client->NPC_class==CLASS_SITHTROOPER );
- return ((level.time - self->NPC->enemyLastSeenTime)<1000);
- }
- ////////////////////////////////////////////////////////////////////////////////////////
- //
- ////////////////////////////////////////////////////////////////////////////////////////
- void NPC_Pain( gentity_t *self, gentity_t *inflictor, int damage, int mod)
- {
- if ( mod==MOD_SABER || mod !=MOD_SABER /*!(NPCInfo->aiFlags&NPCAI_FLAMETHROW)*/)
- {
- TIMER_Set( self, "NPC_TacticsSelect", 0); // Hurt By The Saber, Time To Try Something New
- }
- /*if (self->NPC->aiFlags&NPCAI_FLAMETHROW)
- {
- NPC_SetAnim( self, SETANIM_TORSO, BOTH_FORCELIGHTNING_HOLD, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD );
- self->client->ps.torsoAnimTimer = level.time - TIMER_Get(self, "falmeTime");
- }*/
- }
- ////////////////////////////////////////////////////////////////////////////////////////
- //
- ////////////////////////////////////////////////////////////////////////////////////////
- void NPC_FlyStart( gentity_t *self )
- {//switch to seeker AI for a while
- if ( TIMER_Done( self, "flyRecharge" )
- && !NPC_Flying( self ) )
- {
- self->client->ps.gravity = 0;
- self->svFlags |= SVF_CUSTOM_GRAVITY;
- self->client->moveType = MT_FLYSWIM;
- //start jet effect
- self->client->jetPackTime = level.time + Q_irand( 3000, 10000 );
- //take-off sound
- // CASE SWITCH FOR CLASS (different sound for different fly ways)
- G_SoundOnEnt( self, CHAN_ITEM, "sound/chars/boba/bf_blast-off.wav" );
- //jet loop sound
- self->s.loopSound = G_SoundIndex( "sound/chars/boba/bf_jetpack_lp.wav" );
- if ( self->NPC )
- {
- self->count = Q3_INFINITE; // SEEKER shot ammo count
- }
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////
- //
- ////////////////////////////////////////////////////////////////////////////////////////
- void NPC_FlyStop( gentity_t *self )
- {
- self->client->ps.gravity = g_gravity->value;
- self->svFlags &= ~SVF_CUSTOM_GRAVITY;
- self->client->moveType = MT_RUNJUMP;
- //Stop effect
- self->client->jetPackTime = 0;
- //stop jet loop sound
- G_SoundOnEnt( self, CHAN_ITEM, "sound/chars/boba/bf_land.wav" );
- self->s.loopSound = 0;
- if ( self->NPC )
- {
- self->count = 0; // SEEKER shot ammo count
- TIMER_Set( self, "flyRecharge", Q_irand( 1000, 5000 ) );
- //TIMER_Set( self, "flyChaseDebounce", Q_irand( 500, 2000 ) );// seems not used
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////
- //
- ////////////////////////////////////////////////////////////////////////////////////////
- void NPC_DoAmbushWait( gentity_t *self)
- {
- NPC_TacticsSelect();
- }
- ////////////////////////////////////////////////////////////////////////////////////////
- //
- ////////////////////////////////////////////////////////////////////////////////////////
- void NPC_DoSniper( gentity_t *self)
- {
- if (TIMER_Done(NPC, "PickNewSniperPoint"))
- {
- TIMER_Set(NPC, "PickNewSniperPoint", Q_irand(15000, 25000));
- int SniperPoint = NPC_FindCombatPoint(NPC->currentOrigin, 0, NPC->currentOrigin, CP_SNIPE|CP_CLEAR|CP_HAS_ROUTE|CP_TRYFAR|CP_HORZ_DIST_COLL, 0, -1);
- if (SniperPoint!=-1)
- {
- NPC_SetCombatPoint(SniperPoint);
- NPC_SetMoveGoal( NPC, level.combatPoints[SniperPoint].origin, 20, qtrue, SniperPoint );
- }
- }
- if (Distance(NPC->currentOrigin, level.combatPoints[NPCInfo->combatPoint].origin)<50.0f)
- {
- NPC_FireDecide();
- }
- else
- {
- NPC_FireDecide();
- }
- bool IsOnAPath = !!NPC_MoveToGoal(qtrue);
- // Resolve Blocked Problems
- //--------------------------
- if (NPCInfo->aiFlags&NPCAI_BLOCKED &&
- NPC->client->moveType!=MT_FLYSWIM &&
- ((level.time - NPCInfo->blockedDebounceTime)>3000)
- )
- {
- ////NPC_Printf("BLOCKED: Attempting Jump");
- if (IsOnAPath)
- {
- if (!NPC_TryJump(NPCInfo->blockedTargetPosition))
- {
- // //NPC_Printf(" Failed");
- }
- }
- }
- NPC_FaceEnemy(qtrue);
- NPC_UpdateAngles( qtrue, qtrue );
- }
- ////////////////////////////////////////////////////////////////////////////////////////
- // Call This function to make Boba actually shoot his current weapon
- ////////////////////////////////////////////////////////////////////////////////////////
- void NPC_Fire()
- {
- WeaponThink(qtrue);
- // If Actually Fired, Decide To Apply Alt Fire And Calc Next Attack Delay
- //------------------------------------------------------------------------
- if (ucmd.buttons&BUTTON_ATTACK)
- {
- // DUSTY ADD - THIS WORK WITHOUT TROUBLE!
- if (heavyWeap(NPC->s.weapon))
- {
- if (NPC->s.weapon == WP_ROCKET_LAUNCHER)
- TIMER_Set(NPC, "nextAttackDelay", Q_irand(1000, 2000));
- // Occasionally Shoot A Homing Missile
- //-------------------------------------
- if (!Q_irand(0, 3))
- {
- ucmd.buttons &= ~BUTTON_ATTACK;
- ucmd.buttons |= BUTTON_ALT_ATTACK;
- if (NPC->s.weapon == WP_ROCKET_LAUNCHER)
- NPC->client->fireDelay = Q_irand(1000, 3000);
- }
- }
- else if ( NPC->s.weapon == WP_TUSKEN_RIFLE || NPC->s.weapon == WP_STUN_BATON ||
- NPC->s.weapon == WP_NOGHRI_STICK )
- {
- NPC->client->fireDelay = Q_irand(1000, 3000); // NO ALT FIRE FOR THESE WEAPONS
- }
- else if (NPC->s.weapon == WP_DISRUPTOR)
- {
- TIMER_Set(NPC, "nextAttackDelay", Q_irand(1000, 4000));
- // Occasionally Alt-Fire
- //-----------------------
- if (!Q_irand(0, 3))
- {
- ucmd.buttons &= ~BUTTON_ATTACK;
- ucmd.buttons |= BUTTON_ALT_ATTACK;
- NPC->client->fireDelay = Q_irand(1000, 3000);;
- }
- }
- else
- {//blaster-type weapon
- if (TIMER_Done(NPC, "nextBlasterAltFireDecide"))
- {
- if (Q_irand(0, (NPC->count * 2) + 3)>2)
- {
- TIMER_Set(NPC, "nextBlasterAltFireDecide", Q_irand(3000, 8000));
- if (!(NPCInfo->scriptFlags&SCF_ALT_FIRE))
- {
- // //NPC_Printf("ALT FIRE On");
- NPCInfo->scriptFlags |= SCF_ALT_FIRE;
- NPC_ChangeWeapon(NPC->s.weapon); // Update Delay Timers
- }
- }
- else
- {
- TIMER_Set(NPC, "nextBlasterAltFireDecide", Q_irand(2000, 5000));
- if ( (NPCInfo->scriptFlags&SCF_ALT_FIRE))
- {
- // //NPC_Printf("ALT FIRE Off");
- NPCInfo->scriptFlags &= ~SCF_ALT_FIRE;
- NPC_ChangeWeapon(NPC->s.weapon); // Update Delay Timers
- }
- }
- }
- // Occasionally Alt Fire
- //-----------------------
- if ((NPCInfo->scriptFlags&SCF_ALT_FIRE))
- {
- ucmd.buttons &= ~BUTTON_ATTACK;
- ucmd.buttons |= BUTTON_ALT_ATTACK;
- }
- }
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////
- // Call this function to see if Fett should fire his current weapon
- ////////////////////////////////////////////////////////////////////////////////////////
- //helper function
- qboolean isShooterClass(int className)
- {
- if (NPC->client->NPC_class != CLASS_BOBAFETT || NPC->client->NPC_class == CLASS_SITH || NPC->client->NPC_class == CLASS_COMMANDO ||
- NPC->client->NPC_class == CLASS_SITHTROOPER )// Add other special classes )
- return qtrue;
- return qfalse;
- }
- void NPC_FireDecide( void )
- {
- // Any Reason Not To Shoot?
- //--------------------------
- if (!NPC || // Only NPCs
- !NPC->client || // Only Clients
- //NPC->client->NPC_class!=CLASS_BOBAFETT || // Only Boba
- !isShooterClass(NPC->client->NPC_class) || // Only Boba
- !NPC->enemy || // Only If There Is An Enemy
- NPC->s.weapon == WP_NONE || // Only If Using A Valid Weapon
- !TIMER_Done(NPC, "nextAttackDelay") || // Only If Ready To Shoot Again
- !NPC_CanSeeEnemy(NPC) // Only If Enemy Recently Seen
- )
- {
- return;
- }
- // Now Check Weapon Specific Parameters To See If We Should Shoot Or Not
- //-----------------------------------------------------------------------
- // GOOD CODE
- // DUSTY VARIANTE
- if (heavyWeap(NPC->s.weapon) || blasterWeap(NPC->s.weapon) ||
- lightBlasterWeap(NPC->s.weapon) || heavyBlasterWeap(NPC->s.weapon)||
- meleeWeap(NPC->s.weapon))
- {
- // HERE PUT THE SWITCH FOR CLASS RAZIEL SWORD
- if (Distance(NPC->currentOrigin, NPC->enemy->currentOrigin) > 400.0f)
- {
- NPC_Fire();
- }
- else
- {
- NPC_Fire();
- }
- }
- else
- {
- NPC_Fire();
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////
- // Tactics avaliable to Boba Fett:
- // --------------------------------
- // NPC_MAGIC, // Uses arcane magic, medium range
- // NPC_GLYPHIC, // Uses Glyphic weapon, medium range
- // NPC_ARROWS, // Uses Arrows, large range of atk
- // NPC_NEC_HOLY, // Uses powerful magic, far range
- // NPC_WARRIOR, // Use melee atk.
- // NPC_AMBUSHWAIT, // Goto CP & Wait
- //
- extern void WP_DeactivateSaber( gentity_t *self, qboolean clearLength );
- ////////////////////////////////////////////////////////////////////////////////////////
- void NPC_TacticsSelect()
- {
- // Don't Change Tactics For A Little While
- //------------------------------------------
- TIMER_Set(NPC, "NPC_TacticsSelect", Q_irand(8000, 15000));
- int nextState = NPCInfo->localState;
- // Get Some Data That Will Help With The Selection Of The Next Tactic
- //--------------------------------------------------------------------
- bool enemyAlive2 = (NPC->enemy->health>0);
- float enemyDistance2 = Distance(NPC->currentOrigin, NPC->enemy->currentOrigin);
- bool enemyCloseRange2 = (enemyDistance2<NPC_SHOOTRANGEMIN);
- bool enemyInShootRange2 = (enemyDistance2>NPC_SHOOTRANGEMIN && enemyDistance2<NPC_SHOOTRANGEMAX);
- bool enemyRecentlySeen2 = NPC_CanSeeEnemy(NPC);
- if (!enemyAlive2)
- {
- nextState = NPC_MAGIC;
- }
- else if (enemyCloseRange2)// Enemy is pretty near, so use Saber!
- {
- if ( HaveWeapon(NPC, WP_SABER ))// If Enemy is near, and NPC have a sword in his inventory, using that sword.
- {
- nextState = NPC_FENCER;
- }
- else if ( HaveWeapon(NPC, WEAPS_MELEE ))// If Enemy is near, and NPC have a sword in his inventory, using that sword.
- {
- nextState = NPC_WARRIOR;
- }
- // If It's Been Long Enough Since Our Last Flame Blast, Try To Torch The Enemy
- //-----------------------------------------------------------------------------
- //if (TIMER_Done(NPC, "nextFlameDelay"))
- // DUSTY ADD
- else if (TIMER_Done(NPC, "nextFlameDelay") && (NPC->client->NPC_class == CLASS_BOBAFETT || NPC->client->NPC_class == CLASS_SITH || NPC->client->NPC_class == CLASS_SITHTROOPER ))
- {
- nextState = NPC_GLYPHIC;
- }
- // Otherwise, He's Probably Too Close, So Try To Get Clear Of Him
- //----------------------------------------------------------------
- else
- {
- nextState = NPC_MAGIC;
- }
- }
- else if (enemyInShootRange2)// Enemy is distance so use shoot atk
- {
- if ( HaveWeapon(NPC, WP_SABER ) || HaveWeapon(NPC, WP_MELEE ) ||
- HaveWeapon(NPC, WP_TUSKEN_STAFF ) || HaveWeapon(NPC, WP_STUN_BATON ))
- // You are a warrior and have a sword, but enemy is far and at shoot distance
- // Put away the sword and use shoot weapons!
- {
- if ( HaveWeapon(NPC, WEAPS_BLASTER ))// Enemy is distance, you are a wizard? shoot with magic
- {
- nextState = NPC_MAGIC;
- }
- else if ( HaveWeapon(NPC, WEAPS_HEAVY ))// Enemy is distance? You are an Hunter? use glyphic weapons
- {
- nextState = NPC_GLYPHIC;
- }
- else if ( HaveWeapon(NPC, WEAPS_LIGHTBLASTER ))// You are an archer? use arrows!
- {
- nextState = NPC_ARROWS;
- }
- else if ( HaveWeapon(NPC, WEAPS_HEAVYBLASTER ))// You are a Necromancer or a chieric? Use this attacks!
- {
- nextState = NPC_NEC_HOLY;
- }
- }
- else
- { // If you have shooting weapons, use that!
- if ( HaveWeapon(NPC, WEAPS_BLASTER ))// Enemy is distance, you are a wizard? shoot with magic
- {
- nextState = NPC_MAGIC;
- }
- else if ( HaveWeapon(NPC, WEAPS_HEAVY ))// Enemy is distance? You are an Hunter? use glyphic weapons
- {
- nextState = NPC_GLYPHIC;
- }
- else if ( HaveWeapon(NPC, WEAPS_LIGHTBLASTER ))// You are an archer? use arrows!
- {
- nextState = NPC_ARROWS;
- }
- else if ( HaveWeapon(NPC, WEAPS_HEAVYBLASTER ))// You are a Necromancer or a chieric? Use this attacks!
- {
- nextState = NPC_NEC_HOLY;
- }
- else if ( HaveWeapon(NPC, WEAPS_MELEE ))// this is really stupid but okay...
- {
- nextState = NPC_WARRIOR;
- }
- }
- }
- else if (!enemyInShootRange2 && !enemyCloseRange2) // Enemy is pretty far, try distance atk
- {
- // If It's Been Long Enough Since Our Last Flame Blast, Try To Torch The Enemy
- //-----------------------------------------------------------------------------
- //if (TIMER_Done(NPC, "nextFlameDelay"))
- // DUSTY ADD
- if ( HaveWeapon(NPC, WP_SABER ) || HaveWeapon(NPC, WP_MELEE ) ||
- HaveWeapon(NPC, WP_TUSKEN_STAFF ) || HaveWeapon(NPC, WP_STUN_BATON ))
- // You are a warrior and have a sword, but enemy is far and at shoot distance
- // Put away the sword and use shoot weapons!
- {
- if ( HaveWeapon(NPC, WEAPS_BLASTER ))// Enemy is distance, you are a wizard? shoot with magic
- {
- nextState = NPC_MAGIC;
- }
- else if ( HaveWeapon(NPC, WEAPS_HEAVY ))// Enemy is distance? You are an Hunter? use glyphic weapons
- {
- nextState = NPC_GLYPHIC;
- }
- else if ( HaveWeapon(NPC, WEAPS_LIGHTBLASTER ))// You are an archer? use arrows!
- {
- nextState = NPC_ARROWS;
- }
- else if ( HaveWeapon(NPC, WEAPS_HEAVYBLASTER ))// You are a Necromancer or a chieric? Use this attacks!
- {
- nextState = NPC_NEC_HOLY;
- }
- }
- else
- { // If you have shooting weapons, use that!
- if ( HaveWeapon(NPC, WEAPS_BLASTER ))// Enemy is distance, you are a wizard? shoot with magic
- {
- nextState = NPC_MAGIC;
- }
- else if ( HaveWeapon(NPC, WEAPS_HEAVY ))// Enemy is distance? You are an Hunter? use glyphic weapons
- {
- nextState = NPC_GLYPHIC;
- }
- else if ( HaveWeapon(NPC, WEAPS_LIGHTBLASTER ))// You are an archer? use arrows!
- {
- nextState = NPC_ARROWS;
- }
- else if ( HaveWeapon(NPC, WEAPS_HEAVYBLASTER ))// You are a Necromancer or a chieric? Use this attacks!
- {
- nextState = NPC_NEC_HOLY;
- }
- else if ( HaveWeapon(NPC, WEAPS_MELEE ))// this is really stupid but okay...
- {
- nextState = NPC_WARRIOR;
- }
- }
- }
- // Recently Saw The Enemy, Time For Some Good Ole Fighten!
- //---------------------------------------------------------
- else if (enemyRecentlySeen2)
- {
- // At First, Boba will prefer to use his blaster against the player, but
- // the more times he is driven away (NPC->count), he will be less likely to
- // choose the blaster, and more likely to go for the missile launcher
- if (!enemyCloseRange2 && NPC->client->ps.weapon == WP_SABER )
- {
- nextState = NPC_MAGIC;
- }
- nextState = (!enemyCloseRange2 || Q_irand(0, NPC->count)<1)?(NPC_FENCER):(NPC_MAGIC);
- //nextState = (!enemyInShootRange || Q_irand(0, NPC->count)<1)?(NPC_MAGIC):(NPC_FENCER);
- }
- // Hmmm... Havn't Seen The Player In A While, We Might Want To Try Something Sneaky
- //-----------------------------------------------------------------------------------
- else
- {
- bool SnipePointsNear = false; // TODO
- bool AmbushPointNear = false; // TODO
- if (Q_irand(0, NPC->count)>0)
- {
- int SniperPoint = NPC_FindCombatPoint(NPC->currentOrigin, 0, NPC->currentOrigin, CP_SNIPE|CP_CLEAR|CP_HAS_ROUTE|CP_TRYFAR|CP_HORZ_DIST_COLL, 0, -1);
- if (SniperPoint!=-1)
- {
- NPC_SetCombatPoint(SniperPoint);
- NPC_SetMoveGoal( NPC, level.combatPoints[SniperPoint].origin, 20, qtrue, SniperPoint );
- TIMER_Set(NPC, "PickNewSniperPoint", Q_irand(15000, 25000));
- SnipePointsNear = true;
- }
- }
- // DUSTY ADD
- //if (SnipePointsNear && TIMER_Done(NPC, "NPC_NoSniperTime"))
- if (SnipePointsNear && TIMER_Done(NPC, "NPC_NoSniperTime") && HaveWeapon(NPC, WP_DISRUPTOR))
- {
- TIMER_Set(NPC, "NPC_NoSniperTime", 120000); // Don't snipe again for a while
- TIMER_Set(NPC, "NPC_TacticsSelect", Q_irand(35000, 45000));// More patience here
- nextState = NPC_ARROWS;
- }
- else if (AmbushPointNear)
- {
- TIMER_Set(NPC, "NPC_TacticsSelect", Q_irand(15000, 25000));// More patience here
- nextState = NPC_AMBUSHWAIT;
- }
- else
- {
- nextState = (!enemyInShootRange2 || Q_irand(0, NPC->count)<1)?(NPC_GLYPHIC):(NPC_ARROWS);
- }
- }
- // The Next State Has Been Selected, Now Change Weapon If Necessary
- //------------------------------------------------------------------
- // DUSTY ADD VERSION
- // The Next State Has Been Selected, Now Change Weapon If Necessary
- //------------------------------------------------------------------
- NPCInfo->localState = nextState;
- int weapon = 0;
- switch (NPCInfo->localState)
- {
- case NPC_FENCER:
- if (HaveWeapon(NPC, WP_SABER))
- {
- //NPC_Printf("NEW TACTIC: Sniper");
- NPC_ChangeWeapon(WP_SABER);
- break;
- }
- case NPC_MAGIC:
- weapon = ChooseWeaponRandom(NPC, WEAPS_BLASTER);
- if (weapon)
- {
- //NPC_Printf("NEW TACTIC: Rifle");
- NPC_ChangeWeapon(weapon);
- break;
- }
- case NPC_NEC_HOLY:
- weapon = ChooseWeaponRandom(NPC, WEAPS_HEAVYBLASTER);
- if (weapon)
- {
- //NPC_Printf("NEW TACTIC: Rocket Launcher");
- NPC_ChangeWeapon(weapon);
- break;
- }
- case NPC_GLYPHIC: //kinda stuck at this point if doesn't have flamethrower
- //NPC_Printf("NEW TACTIC: Flame Thrower");
- weapon = ChooseWeaponRandom(NPC, WEAPS_HEAVY);
- if (weapon)
- {
- //NPC_Printf("NEW TACTIC: Rocket Launcher");
- NPC_ChangeWeapon(weapon);
- break;
- }
- case NPC_WARRIOR: //kinda stuck at this point if doesn't have flamethrower
- //NPC_Printf("NEW TACTIC: Flame Thrower");
- weapon = ChooseWeaponRandom(NPC, WEAPS_MELEE);
- if (weapon)
- {
- //NPC_Printf("NEW TACTIC: Rocket Launcher");
- NPC_ChangeWeapon(weapon);
- break;
- }
- case NPC_ARROWS: //kinda stuck at this point if doesn't have flamethrower
- //NPC_Printf("NEW TACTIC: Flame Thrower");
- weapon = ChooseWeaponRandom(NPC, WEAPS_LIGHTBLASTER);
- if (weapon)
- {
- //NPC_Printf("NEW TACTIC: Rocket Launcher");
- NPC_ChangeWeapon(weapon);
- break;
- }
- case NPC_AMBUSHWAIT:
- //NPC_Printf("NEW TACTIC: Ambush");
- weapon = ChooseWeaponRandom(NPC, WEAPS_ALL);
- if (weapon)
- {
- //NPC_Printf("NEW TACTIC: Rocket Launcher");
- NPC_ChangeWeapon(weapon);
- break;
- }
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////
- // Tactics
- //
- // This function is called right after Update()
- // If returns true, Jedi and Seeker AI not used for movement
- //
- ////////////////////////////////////////////////////////////////////////////////////////
- bool NPC_Tactics()
- {
- if (!NPC->enemy)
- {
- return false;
- }
- // Think About Changing Tactics
- //------------------------------
- if (TIMER_Done(NPC, "NPC_TacticsSelect"))
- {
- NPC_TacticsSelect();
- }
- // These Tactics Require Seeker & Jedi Movement
- //----------------------------------------------
- if (!NPCInfo->localState ||
- NPCInfo->localState==NPC_MAGIC ||
- NPCInfo->localState==NPC_NEC_HOLY ||
- NPCInfo->localState==NPC_FENCER||
- NPCInfo->localState==NPC_ARROWS ||
- NPCInfo->localState==NPC_WARRIOR ||
- NPCInfo->localState==NPC_GLYPHIC )
- {
- return false;
- }
- // Flame Thrower - Locked In Place
- //---------------------------------
- if (NPCInfo->localState==NPC_GLYPHIC )
- {
- NPC_TacticsSelect();
- }
- // Sniper - Move Around, And Take Shots
- //--------------------------------------
- else if (NPCInfo->localState==NPC_ARROWS)
- {
- NPC_DoSniper( NPC );
- }
- // Ambush Wait
- //------------
- else if (NPCInfo->localState==NPC_AMBUSHWAIT)
- {
- NPC_DoAmbushWait( NPC );
- }
- NPC_FacePosition( NPC->enemy->currentOrigin, qtrue);
- NPC_UpdateAngles(qtrue, qtrue);
- return true; // Do Not Use Normal Jedi Or Seeker Movement
- }
- ////////////////////////////////////////////////////////////////////////////////////////
- //
- ////////////////////////////////////////////////////////////////////////////////////////
- bool NPC_Respawn()
- {
- int cp = -1;
- // Try To Predict Where The Enemy Is Going
- //-----------------------------------------
- if (AverageEnemyDirectionSamples2 && NPC->behaviorSet[BSET_DEATH]==0)
- {
- vec3_t endPos;
- VectorMA(NPC->enemy->currentOrigin, 1000.0f / (float)AverageEnemyDirectionSamples2, AverageEnemyDirection2, endPos);
- cp = NPC_FindCombatPoint(endPos, 0, endPos, CP_FLEE|CP_TRYFAR|CP_HORZ_DIST_COLL, 0, -1);
- //NPC_Printf("Attempting Predictive Spawn Point");
- }
- // If That Failed, Try To Go Directly To The Enemy
- //-------------------------------------------------
- if (cp==-1)
- {
- cp = NPC_FindCombatPoint(NPC->enemy->currentOrigin, 0, NPC->enemy->currentOrigin, CP_FLEE|CP_TRYFAR|CP_HORZ_DIST_COLL, 0, -1);
- //NPC_Printf("Attempting Closest Current Spawn Point");
- }
- // If We've Found One, Go There
- //------------------------------
- if (cp!=-1)
- {
- NPC_SetCombatPoint( cp );
- NPCInfo->surrenderTime = 0;
- NPC->health = NPC->max_health;
- NPC->svFlags &=~SVF_NOCLIENT;
- NPC->count ++; // This is the number of times spawned
- G_SetOrigin(NPC, level.combatPoints[cp].origin);
- AverageEnemyDirectionSamples2 = 0;
- VectorClear(AverageEnemyDirection2);
- //NPC_Printf("Found Spawn Point (%d)", cp);
- return true;
- }
- assert(0); // Yea, that's bad...
- //NPC_Printf("FAILED TO FIND SPAWN POINT");
- return false;
- }
- ////////////////////////////////////////////////////////////////////////////////////////
- //
- ////////////////////////////////////////////////////////////////////////////////////////
- void NPC_Update()
- {
- // Hey, That Tests The Trace All The Time
- //----------------------------------------------------
- if (NPC->enemy)
- {
- // Never Forget The Player... Never. NEVER!!!
- NPC->svFlags |= SVF_LOCKEDENEMY; // Don't forget about the enemy once you've found him
- if (!(NPC->svFlags&SVF_NOCLIENT))
- {
- trace_t testTrace;
- vec3_t eyes;
- CalcEntitySpot( NPC, SPOT_HEAD_LEAN, eyes );
- gi.trace (&testTrace, eyes, NULL, NULL, NPC->enemy->currentOrigin, NPC->s.number, MASK_SHOT, (EG2_Collision)0, 0);
- bool wasSeen = NPC_CanSeeEnemy(NPC);
- if (!testTrace.startsolid &&
- !testTrace.allsolid &&
- testTrace.entityNum == NPC->enemy->s.number)
- {
- NPCInfo->enemyLastSeenTime = level.time;
- NPCInfo->enemyLastHeardTime = level.time;
- VectorCopy(NPC->enemy->currentOrigin, NPCInfo->enemyLastSeenLocation);
- VectorCopy(NPC->enemy->currentOrigin, NPCInfo->enemyLastHeardLocation);
- }
- else if (gi.inPVS( NPC->enemy->currentOrigin, NPC->currentOrigin))
- {
- NPCInfo->enemyLastHeardTime = level.time;
- VectorCopy(NPC->enemy->currentOrigin, NPCInfo->enemyLastHeardLocation);
- }
- }
- if (!NPCInfo->surrenderTime)
- {
- if ((level.time - NPCInfo->enemyLastSeenTime)>20000 && TIMER_Done(NPC, "TooLongGoneRespawn"))
- {
- TIMER_Set(NPC, "TooLongGoneRespawn", 30000); // Give him some time to get to you before trying again
- //NPC_Printf("Gone Too Long, Attempting Respawn Even Though Not Hiding");
- NPC_Respawn();
- }
- }
- }
- // Make Sure He Always Appears In The Last Area With Full Health When His Death Script Is Turned On
- //--------------------------------------------------------------------------------------------------
- if (!NPCHadDeathScript && NPC->behaviorSet[BSET_DEATH]!=0)
- {
- if (!gi.inPVS(NPC->enemy->currentOrigin, NPC->currentOrigin))
- {
- //NPC_Printf("Attempting Final Battle Spawn...");
- if (NPC_Respawn())
- {
- NPCHadDeathScript = true;
- }
- else
- {
- //NPC_Printf("Failed");
- }
- }
- }
- // Occasionally A Jump Turns Into A Rocket Fly
- //---------------------------------------------
- if ( NPC->client->ps.groundEntityNum == ENTITYNUM_NONE
- && NPC->client->ps.forceJumpZStart && NPC->client->NPC_class == CLASS_ROCKETTROOPER
- && !Q_irand( 0, 10 ) )
- {//take off
- NPC_FlyStart( NPC );
- }
- // If Hurting, Try To Run Away
- //-----------------------------
- if (!NPCInfo->surrenderTime && (NPC->health<NPC->max_health/10))
- {
- //NPC_Printf("Time To Surrender, Searching For Flee Point");
- // Find The Closest Flee Point That I Can Get To
- //-----------------------------------------------
- int cp = NPC_FindCombatPoint(NPC->currentOrigin, 0, NPC->currentOrigin, CP_FLEE|CP_HAS_ROUTE|CP_TRYFAR|CP_HORZ_DIST_COLL, 0, -1);
- if (cp!=-1)
- {
- NPC_SetCombatPoint( cp );
- NPC_SetMoveGoal( NPC, level.combatPoints[cp].origin, 8, qtrue, cp );
- if (NPC->count<6)
- {
- NPCInfo->surrenderTime = level.time + Q_irand(5000, 10000) + 1000*(6-NPC->count);
- }
- else
- {
- NPCInfo->surrenderTime = level.time + Q_irand(5000, 10000);
- }
- }
- else
- {
- //NPC_Printf(" Failure");
- }
- }
- }
- ////////////////////////////////////////////////////////////////////////////////////////
- //
- ////////////////////////////////////////////////////////////////////////////////////////
- bool NPC_Flee()
- {
- bool EnemyRecentlySeen = ((level.time - NPCInfo->enemyLastSeenTime)<10000);
- bool ReachedEscapePoint = (Distance(level.combatPoints[NPCInfo->combatPoint].origin, NPC->currentOrigin)<50.0f);
- bool HasBeenGoneEnough = (level.time>NPCInfo->surrenderTime || (level.time - NPCInfo->enemyLastSeenTime)>400000);
- // Is It Time To Come Back For Some More?
- //----------------------------------------
- if (!EnemyRecentlySeen || ReachedEscapePoint)
- {
- NPC->svFlags |= SVF_NOCLIENT;
- if (HasBeenGoneEnough)
- {
- if ((level.time - NPCInfo->enemyLastSeenTime)>400000)
- {
- //NPC_Printf(" Gone Too Long, Attempting Respawn");
- }
- if (NPC_Respawn())
- {
- return true;
- }
- }
- else if (ReachedEscapePoint && (NPCInfo->surrenderTime - level.time)>3000)
- {
- if (TIMER_Done(NPC, "SpookPlayerTimer"))
- {
- vec3_t testDirection;
- TIMER_Set(NPC, "SpookPlayerTimer", Q_irand(2000, 10000));
- switch(Q_irand(0, 1))
- {
- case 0:
- //NPC_Printf("SPOOK: Dust");
- NPC_DustFallNear(NPC->enemy->currentOrigin, Q_irand(1,2));
- break;
- case 1:
- //NPC_Printf("SPOOK: Footsteps");
- testDirection[0] = (random() * 0.5f) - 1.0f;
- testDirection[0] += (testDirection[0]>0.0f)?(0.5f):(-0.5f);
- testDirection[1] = (random() * 0.5f) - 1.0f;
- testDirection[1] += (testDirection[1]>0.0f)?(0.5f):(-0.5f);
- testDirection[2] = 1.0f;
- VectorMA(NPC->enemy->currentOrigin, 400.0f, testDirection, NPCFootStepLoc);
- NPCFootStepCount = Q_irand(3,8);
- break;
- }
- }
- if (NPCFootStepCount && TIMER_Done(NPC, "NPCFootStepFakeTimer"))
- {
- TIMER_Set(NPC, "NPCFootStepFakeTimer", Q_irand(300, 800));
- NPCFootStepCount --;
- G_SoundAtSpot(NPCFootStepLoc, G_SoundIndex(va("sound/player/footsteps/boot%d", Q_irand(1,4))), qtrue);
- }
- if (TIMER_Done(NPC, "ResampleEnemyDirection") && NPC->enemy->resultspeed>10.0f)
- {
- TIMER_Set(NPC, "ResampleEnemyDirection", Q_irand(500, 1000));
- AverageEnemyDirectionSamples2 ++;
- vec3_t moveDir;
- VectorCopy(NPC->enemy->client->ps.velocity, moveDir);
- VectorNormalize(moveDir);
- VectorAdd(AverageEnemyDirection2, moveDir, AverageEnemyDirection2);
- }
- }
- }
- else
- {
- NPCInfo->surrenderTime += 100;
- }
- // Finish The Flame Thrower First...
- //-----------------------------------
- if (NPCInfo->aiFlags&NPCAI_FLAMETHROW)
- {
- NPC_TacticsSelect();
- NPC_FacePosition( NPC->enemy->currentOrigin, qtrue);
- NPC_UpdateAngles(qtrue, qtrue);
- return true;
- }
- bool IsOnAPath = !!NPC_MoveToGoal(qtrue);
- if (!ReachedEscapePoint &&
- NPCInfo->aiFlags&NPCAI_BLOCKED &&
- NPC->client->moveType!=MT_FLYSWIM &&
- ((level.time - NPCInfo->blockedDebounceTime)>1000)
- )
- {
- if (!NPC_CanSeeEnemy(NPC) && Distance(NPC->currentOrigin, level.combatPoints[NPCInfo->combatPoint].origin)<200)
- {
- //NPC_Printf("BLOCKED: Just Teleporting There");
- G_SetOrigin(NPC, level.combatPoints[NPCInfo->combatPoint].origin);
- }
- else
- {
- //NPC_Printf("BLOCKED: Attempting Jump");
- if (IsOnAPath)
- {
- if (NPC_TryJump(NPCInfo->blockedTargetPosition))
- {
- }
- else
- {
- //NPC_Printf(" Failed");
- }
- }
- else if (EnemyRecentlySeen)
- {
- if (NPC_TryJump(NPCInfo->enemyLastSeenLocation))
- {
- }
- else
- {
- //NPC_Printf(" Failed");
- }
- }
- }
- }
- NPC_UpdateAngles( qtrue, qtrue );
- return true;
- }
Advertisement
Add Comment
Please, Sign In to add comment