static qboolean Sniper_Move( void ) { qboolean moved; navInfo_t info; NPCInfo->combatMove = qtrue;//always move straight toward our goal moved = NPC_MoveToGoal( qtrue ); //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 ) { Sniper_HoldPosition(); } } //If our move failed, then reset if ( moved == qfalse ) {//couldn't get to enemy if ( (NPCInfo->scriptFlags&SCF_CHASE_ENEMIES) && NPCInfo->goalEntity && NPCInfo->goalEntity == NPC->enemy ) {//we were running after enemy //Try to find a combat point that can hit the enemy int cpFlags = (CP_CLEAR|CP_HAS_ROUTE); int cp; if ( NPCInfo->scriptFlags&SCF_USE_CP_NEAREST ) { cpFlags &= ~(CP_FLANK|CP_APPROACH_ENEMY|CP_CLOSEST); cpFlags |= CP_NEAREST; } cp = NPC_FindCombatPoint( NPC->r.currentOrigin, NPC->r.currentOrigin, NPC->r.currentOrigin, cpFlags, 32, -1 ); if ( cp == -1 && !(NPCInfo->scriptFlags&SCF_USE_CP_NEAREST) ) {//okay, try one by the enemy cp = NPC_FindCombatPoint( NPC->r.currentOrigin, NPC->r.currentOrigin, NPC->enemy->r.currentOrigin, CP_CLEAR|CP_HAS_ROUTE|CP_HORZ_DIST_COLL, 32, -1 ); } //NOTE: there may be a perfectly valid one, just not one within CP_COLLECT_RADIUS of either me or him... if ( cp != -1 ) {//found a combat point that has a clear shot to enemy NPC_SetCombatPoint( cp ); NPC_SetMoveGoal( NPC, level.combatPoints[cp].origin, 8, qtrue, cp, NULL ); return moved; } } //just hang here Sniper_HoldPosition(); } return moved; }
static void Sniper_ResolveBlockedShot( void ) { if ( TIMER_Done( NPC, "duck" ) ) {//we're not ducking if ( TIMER_Done( NPC, "roamTime" ) ) {//not roaming //FIXME: try to find another spot from which to hit the enemy if ( (NPCInfo->scriptFlags&SCF_CHASE_ENEMIES) && (!NPCInfo->goalEntity || NPCInfo->goalEntity == NPC->enemy) ) {//we were running after enemy //Try to find a combat point that can hit the enemy int cpFlags = (CP_CLEAR|CP_HAS_ROUTE); if ( NPCInfo->scriptFlags&SCF_USE_CP_NEAREST ) { cpFlags &= ~(CP_FLANK|CP_APPROACH_ENEMY|CP_CLOSEST); cpFlags |= CP_NEAREST; } int cp = NPC_FindCombatPoint( NPC->currentOrigin, NPC->currentOrigin, NPC->currentOrigin, cpFlags, 32 ); if ( cp == -1 && !(NPCInfo->scriptFlags&SCF_USE_CP_NEAREST) ) {//okay, try one by the enemy cp = NPC_FindCombatPoint( NPC->currentOrigin, NPC->currentOrigin, NPC->enemy->currentOrigin, CP_CLEAR|CP_HAS_ROUTE|CP_HORZ_DIST_COLL, 32 ); } //NOTE: there may be a perfectly valid one, just not one within CP_COLLECT_RADIUS of either me or him... if ( cp != -1 ) {//found a combat point that has a clear shot to enemy NPC_SetCombatPoint( cp ); NPC_SetMoveGoal( NPC, level.combatPoints[cp].origin, 8, qtrue, cp ); TIMER_Set( NPC, "duck", -1 ); if ( NPC->client->NPC_class == CLASS_SABOTEUR ) { Saboteur_Decloak( NPC ); } TIMER_Set( NPC, "attackDelay", Q_irand( 1000, 3000 ) ); return; } } } } /* else {//maybe we should stand if ( TIMER_Done( NPC, "stand" ) ) {//stand for as long as we'll be here TIMER_Set( NPC, "stand", Q_irand( 500, 2000 ) ); return; } } //Hmm, can't resolve this by telling them to duck or telling me to stand //We need to doMove! TIMER_Set( NPC, "roamTime", -1 ); TIMER_Set( NPC, "stick", -1 ); TIMER_Set( NPC, "duck", -1 ); TIMER_Set( NPC, "attackDelay", Q_irand( 1000, 3000 ) ); */ }
bool Boba_Respawn() { int cp = -1; // Try To Predict Where The Enemy Is Going //----------------------------------------- if (AverageEnemyDirectionSamples && NPC->behaviorSet[BSET_DEATH]==0) { vec3_t endPos; VectorMA(NPC->enemy->currentOrigin, 1000.0f / (float)AverageEnemyDirectionSamples, AverageEnemyDirection, endPos); cp = NPC_FindCombatPoint(endPos, 0, endPos, CP_FLEE|CP_TRYFAR|CP_HORZ_DIST_COLL, 0, -1); Boba_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); Boba_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); AverageEnemyDirectionSamples = 0; VectorClear(AverageEnemyDirection); Boba_Printf("Found Spawn Point (%d)", cp); return true; } assert(0); // Yea, that's bad... Boba_Printf("FAILED TO FIND SPAWN POINT"); return false; }
void Boba_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) { Boba_FireDecide(); } bool IsOnAPath = !!NPC_MoveToGoal(qtrue); // Resolve Blocked Problems //-------------------------- if (NPCInfo->aiFlags&NPCAI_BLOCKED && NPC->client->moveType!=MT_FLYSWIM && ((level.time - NPCInfo->blockedDebounceTime)>3000) ) { Boba_Printf("BLOCKED: Attempting Jump"); if (IsOnAPath) { if (!NPC_TryJump(NPCInfo->blockedTargetPosition)) { Boba_Printf(" Failed"); } } } NPC_FaceEnemy(qtrue); NPC_UpdateAngles( qtrue, qtrue ); }
void Hirogen_Hunt( void ) { if ( NPCInfo->combatPoint == -1 ) { NPCInfo->combatPoint = NPC_FindCombatPoint( CP_CLEAR ); if ( NPCInfo->combatPoint == -1 ) { assert(0); return; } //Move there NPC_SetMoveGoal( NPC, level.combatPoints[NPCInfo->combatPoint].origin, 4, qtrue ); NPCInfo->combatMove = qtrue; NPC_MoveToGoal(); NPC_UpdateAngles( qtrue, qtrue ); return; } //See if we made it if ( NAV_HitNavGoal( NPC->currentOrigin, NPC->mins, NPC->maxs, level.combatPoints[NPCInfo->combatPoint].origin, 2 ) ) { NPCInfo->combatPoint = -1; //Hirogen_Hunt(); //NOTENOTE: Remove the 10Hz latency that would be introduced otherwise, but be careful with this one! return; } //Move there NPC_SetMoveGoal( NPC, level.combatPoints[NPCInfo->combatPoint].origin, 2, qtrue ); NPCInfo->combatMove = qtrue; NPC_MoveToGoal(); NPC_UpdateAngles( qtrue, qtrue ); }
void NPC_StartFlee( gentity_t *enemy, vec3_t dangerPoint, int dangerLevel, int fleeTimeMin, int fleeTimeMax ) { if ( Q3_TaskIDPending( NPC, TID_MOVE_NAV ) ) {//running somewhere that a script requires us to go, don't interrupt that! return; } //if have a fleescript, run that instead if ( G_ActivateBehavior( NPC, BSET_FLEE ) ) { return; } //FIXME: play a flee sound? Appropriate to situation? if ( enemy ) { G_SetEnemy( NPC, enemy ); } //FIXME: if don't have a weapon, find nearest one we have a route to and run for it? int cp = -1; if ( dangerLevel > AEL_DANGER || NPC->s.weapon == WP_NONE || ((!NPCInfo->group || NPCInfo->group->numGroup <= 1) && NPC->health <= 10 ) ) {//IF either great danger OR I have no weapon OR I'm alone and low on health, THEN try to find a combat point out of PVS cp = NPC_FindCombatPoint( NPC->currentOrigin, NPC->currentOrigin, dangerPoint, CP_COVER|CP_AVOID|CP_HAS_ROUTE|CP_NO_PVS, 128 ); } //FIXME: still happens too often... if ( cp == -1 ) {//okay give up on the no PVS thing cp = NPC_FindCombatPoint( NPC->currentOrigin, NPC->currentOrigin, dangerPoint, CP_COVER|CP_AVOID|CP_HAS_ROUTE, 128 ); if ( cp == -1 ) {//okay give up on the avoid cp = NPC_FindCombatPoint( NPC->currentOrigin, NPC->currentOrigin, dangerPoint, CP_COVER|CP_HAS_ROUTE, 128 ); if ( cp == -1 ) {//okay give up on the cover cp = NPC_FindCombatPoint( NPC->currentOrigin, NPC->currentOrigin, dangerPoint, CP_HAS_ROUTE, 128 ); } } } //see if we got a valid one if ( cp != -1 ) {//found a combat point NPC_SetCombatPoint( cp ); NPC_SetMoveGoal( NPC, level.combatPoints[cp].origin, 8, qtrue, cp ); NPCInfo->behaviorState = BS_HUNT_AND_KILL; NPCInfo->tempBehavior = BS_DEFAULT; } else {//need to just run like hell! if ( NPC->s.weapon != WP_NONE ) { return;//let's just not flee? } else { //FIXME: other evasion AI? Duck? Strafe? Dodge? NPCInfo->tempBehavior = BS_FLEE; //Run straight away from here... FIXME: really want to find farthest waypoint/navgoal from this pos... maybe based on alert event radius? NPC_SetMoveGoal( NPC, dangerPoint, 0, qtrue ); //store the danger point VectorCopy( dangerPoint, NPCInfo->investigateGoal );//FIXME: make a new field for this? } } //FIXME: localize this Timer? TIMER_Set( NPC, "attackDelay", Q_irand( 500, 2500 ) ); //FIXME: is this always applicable? NPCInfo->squadState = SQUAD_RETREAT; TIMER_Set( NPC, "flee", Q_irand( fleeTimeMin, fleeTimeMax ) ); TIMER_Set( NPC, "panic", Q_irand( 1000, 4000 ) );//how long to wait before trying to nav to a dropped weapon TIMER_Set( NPC, "duck", 0 ); }
//////////////////////////////////////////////////////////////////////////////////////// // Tactics avaliable to Boba Fett: // -------------------------------- // BTS_RIFLE, // Uses Jedi / Seeker Movement // BTS_MISSILE, // Uses Jedi / Seeker Movement // BTS_SNIPER, // Uses Special Movement Internal To This File // BTS_FLAMETHROW, // Locked In Place // BTS_AMBUSHWAIT, // Goto CP & Wait // // // Weapons available to Boba Fett: // -------------------------------- // WP_NONE (Flame Thrower) // WP_ROCKET_LAUNCHER // WP_BLASTER // WP_DISRUPTOR // //////////////////////////////////////////////////////////////////////////////////////// void Boba_TacticsSelect() { // Don't Change Tactics For A Little While //------------------------------------------ TIMER_Set(NPC, "Boba_TacticsSelect", Q_irand(8000, 15000)); int nextState = NPCInfo->localState; // Get Some Data That Will Help With The Selection Of The Next Tactic //-------------------------------------------------------------------- bool enemyAlive = (NPC->enemy->health>0); float enemyDistance = Distance(NPC->currentOrigin, NPC->enemy->currentOrigin); bool enemyInFlameRange = (enemyDistance<BOBA_FLAMETHROWRANGE); bool enemyInRocketRange = (enemyDistance>BOBA_ROCKETRANGEMIN && enemyDistance<BOBA_ROCKETRANGEMAX); bool enemyRecentlySeen = Boba_CanSeeEnemy(NPC); // Enemy Is Really Close //----------------------- if (!enemyAlive) { nextState = BTS_RIFLE; } else if (enemyInFlameRange) { // If It's Been Long Enough Since Our Last Flame Blast, Try To Torch The Enemy //----------------------------------------------------------------------------- if (TIMER_Done(NPC, "nextFlameDelay")) { nextState = BTS_FLAMETHROW; } // Otherwise, He's Probably Too Close, So Try To Get Clear Of Him //---------------------------------------------------------------- else { nextState = BTS_RIFLE; } } // Recently Saw The Enemy, Time For Some Good Ole Fighten! //--------------------------------------------------------- else if (enemyRecentlySeen) { // 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 nextState = (!enemyInRocketRange || Q_irand(0, NPC->count)<1)?(BTS_RIFLE):(BTS_MISSILE); } // 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; } } if (SnipePointsNear && TIMER_Done(NPC, "Boba_NoSniperTime")) { TIMER_Set(NPC, "Boba_NoSniperTime", 120000); // Don't snipe again for a while TIMER_Set(NPC, "Boba_TacticsSelect", Q_irand(35000, 45000));// More patience here nextState = BTS_SNIPER; } else if (AmbushPointNear) { TIMER_Set(NPC, "Boba_TacticsSelect", Q_irand(15000, 25000));// More patience here nextState = BTS_AMBUSHWAIT; } else { nextState = (!enemyInRocketRange || Q_irand(0, NPC->count)<1)?(BTS_RIFLE):(BTS_MISSILE); } } // The Next State Has Been Selected, Now Change Weapon If Necessary //------------------------------------------------------------------ if (nextState!=NPCInfo->localState) { NPCInfo->localState = nextState; switch (NPCInfo->localState) { case BTS_FLAMETHROW: Boba_Printf("NEW TACTIC: Flame Thrower"); Boba_ChangeWeapon(WP_NONE); Boba_DoFlameThrower(NPC); break; case BTS_RIFLE: Boba_Printf("NEW TACTIC: Rifle"); Boba_ChangeWeapon(WP_BLASTER); break; case BTS_MISSILE: Boba_Printf("NEW TACTIC: Rocket Launcher"); Boba_ChangeWeapon(WP_ROCKET_LAUNCHER); break; case BTS_SNIPER: Boba_Printf("NEW TACTIC: Sniper"); Boba_ChangeWeapon(WP_DISRUPTOR); break; case BTS_AMBUSHWAIT: Boba_Printf("NEW TACTIC: Ambush"); Boba_ChangeWeapon(WP_NONE); break; } } }
void Boba_Update() { // Never Forget The Player... Never. //----------------------------------- if (player && player->inuse && !NPC->enemy) { G_SetEnemy(NPC, player); NPC->svFlags |= SVF_LOCKEDENEMY; // Don't forget about the enemy once you've found him } // Hey, This Is Boba, He Tests The Trace All The Time //---------------------------------------------------- if (NPC->enemy) { 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 = Boba_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 (g_bobaDebug->integer) { bool nowSeen = Boba_CanSeeEnemy(NPC); if (!wasSeen && nowSeen) { Boba_Printf("Enemy Seen"); } if (wasSeen && !nowSeen) { Boba_Printf("Enemy Lost"); } CG_DrawEdge(NPC->currentOrigin, NPC->enemy->currentOrigin, (nowSeen)?(EDGE_IMPACT_SAFE):(EDGE_IMPACT_POSSIBLE)); } } 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 Boba_Printf("Gone Too Long, Attempting Respawn Even Though Not Hiding"); Boba_Respawn(); } } } // Make Sure He Always Appears In The Last Area With Full Health When His Death Script Is Turned On //-------------------------------------------------------------------------------------------------- if (!BobaHadDeathScript && NPC->behaviorSet[BSET_DEATH]!=0) { if (!gi.inPVS(NPC->enemy->currentOrigin, NPC->currentOrigin)) { Boba_Printf("Attempting Final Battle Spawn..."); if (Boba_Respawn()) { BobaHadDeathScript = true; } else { Boba_Printf("Failed"); } } } // Don't Forget To Turn Off That Flame Thrower, Mr. Fett - You're Waisting Precious Natural Gases //------------------------------------------------------------------------------------------------ if ((NPCInfo->aiFlags&NPCAI_FLAMETHROW) && (TIMER_Done(NPC, "flameTime"))) { Boba_StopFlameThrower(NPC); } // Occasionally A Jump Turns Into A Rocket Fly //--------------------------------------------- if ( NPC->client->ps.groundEntityNum == ENTITYNUM_NONE && NPC->client->ps.forceJumpZStart && !Q_irand( 0, 10 ) ) {//take off Boba_FlyStart( NPC ); } // If Hurting, Try To Run Away //----------------------------- if (!NPCInfo->surrenderTime && (NPC->health<NPC->max_health/10)) { Boba_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 { Boba_Printf(" Failure"); } } }