//[SPPortComplete] gentity_t *NPC_PickEnemyExt( qboolean checkAlerts ) { //Check for Hazard Team status and remove this check /* if ( NPC->client->playerTeam != TEAM_STARFLEET ) { //If we've found the player, return it if ( NPC_FindPlayer() ) return &g_entities[0]; } */ //If we've asked for the closest enemy int entID = NPC_FindNearestEnemy( NPC ); //If we have a valid enemy, use it if ( entID >= 0 ) return &g_entities[entID]; if ( checkAlerts ) { //[CoOp] int alertEvent = NPC_CheckAlertEvents( qtrue, qtrue, -1, qtrue, AEL_DISCOVERED, qfalse ); //int alertEvent = NPC_CheckAlertEvents( qtrue, qtrue, -1, qtrue, AEL_DISCOVERED ); //[/CoOp] //There is an event to look at if ( alertEvent >= 0 ) { alertEvent_t *event = &level.alertEvents[alertEvent]; //Don't pay attention to our own alerts if ( event->owner == NPC ) return NULL; if ( event->level >= AEL_DISCOVERED ) { //If it's the player, attack him //[CoOp] //account for all players if ( event->owner->s.number < MAX_CLIENTS ) //if ( event->owner == &g_entities[0] ) return event->owner; //If it's on our team, then take its enemy as well if ( ( event->owner->client ) && ( event->owner->client->playerTeam == NPC->client->playerTeam ) ) //racc - communicate enemies with allies if our allies are making a lot of noise. return event->owner->enemy; } } } return NULL; }
gentity_t *NPC_PickEnemyExt( qboolean checkAlerts ) { //Check for Hazard Team status and remove this check /* if ( NPC->client->playerTeam != TEAM_STARFLEET ) { //If we've found the player, return it if ( NPC_FindPlayer() ) return &g_entities[0]; } */ //If we've asked for the closest enemy int entID = NPC_FindNearestEnemy( NPCS.NPC ); //If we have a valid enemy, use it if ( entID >= 0 ) return &g_entities[entID]; if ( checkAlerts ) { int alertEvent = NPC_CheckAlertEvents( qtrue, qtrue, -1, qtrue, AEL_DISCOVERED ); //There is an event to look at if ( alertEvent >= 0 ) { alertEvent_t *event = &level.alertEvents[alertEvent]; //Don't pay attention to our own alerts if ( event->owner == NPCS.NPC ) return NULL; if ( event->level >= AEL_DISCOVERED ) { //If it's the player, attack him //OJKFIXME: clientnum 0 if ( event->owner == &g_entities[0] ) return event->owner; //If it's on our team, then take its enemy as well if ( ( event->owner->client ) && ( event->owner->client->playerTeam == NPCS.NPC->client->playerTeam ) ) return event->owner->enemy; } } } return NULL; }
void SandCreature_CheckAlerts( void ) { if ( !(NPCInfo->scriptFlags&SCF_IGNORE_ALERTS) ) { int alertEvent = NPC_CheckAlertEvents( qfalse, qtrue, NPCInfo->lastAlertID, qfalse, AEL_MINOR, qtrue ); //There is an event to look at if ( alertEvent >= 0 ) { //if ( level.alertEvents[alertEvent].ID != NPCInfo->lastAlertID ) { SandCreature_SeekAlert( alertEvent ); } } } }
/* void NPC_BSSleep( void ) */ void NPC_BSSleep( void ) { int alertEvent = NPC_CheckAlertEvents( qtrue, qfalse ); //There is an event to look at if ( alertEvent >= 0 ) { G_ActivateBehavior(NPC, BSET_AWAKE); return; } /* if ( level.time > NPCInfo->enemyCheckDebounceTime ) { if ( NPC_CheckSoundEvents() != -1 ) {//only 1 alert per second per 0.1 of vigilance NPCInfo->enemyCheckDebounceTime = level.time + (NPCInfo->stats.vigilance * 10000); G_ActivateBehavior(NPC, BSET_AWAKE); } } */ }
gentity_t *NPC_PickEnemyExt( qboolean checkAlerts = qfalse ) { //If we've asked for the closest enemy int entID = NPC_FindNearestEnemy( NPC ); //If we have a valid enemy, use it if ( entID >= 0 ) return &g_entities[entID]; if ( checkAlerts ) { int alertEvent = NPC_CheckAlertEvents( qtrue, qtrue, -1, qtrue, AEL_DISCOVERED ); //There is an event to look at if ( alertEvent >= 0 ) { alertEvent_t *event = &level.alertEvents[alertEvent]; //Don't pay attention to our own alerts if ( event->owner == NPC ) return NULL; if ( event->level >= AEL_DISCOVERED ) { //If it's the player, attack him if ( event->owner == &g_entities[0] ) return event->owner; //If it's on our team, then take its enemy as well if ( ( event->owner->client ) && ( event->owner->client->playerTeam == NPC->client->playerTeam ) ) return event->owner->enemy; } } } return NULL; }
void NPC_BSFollowLeader (void) { vec3_t vec; float leaderDist; visibility_t leaderVis; int curAnim; if ( !NPC->client->leader ) {//ok, stand guard until we find an enemy if( NPCInfo->tempBehavior == BS_HUNT_AND_KILL ) { NPCInfo->tempBehavior = BS_DEFAULT; } else { NPCInfo->tempBehavior = BS_STAND_GUARD; NPC_BSStandGuard(); } return; } if ( !NPC->enemy ) {//no enemy, find one NPC_CheckEnemy( NPCInfo->confusionTime<level.time, qfalse );//don't find new enemy if this is tempbehav if ( NPC->enemy ) {//just found one NPCInfo->enemyCheckDebounceTime = level.time + Q_irand( 3000, 10000 ); } else { if ( !(NPCInfo->scriptFlags&SCF_IGNORE_ALERTS) ) { int eventID = NPC_CheckAlertEvents( qtrue, qtrue ); if ( level.alertEvents[eventID].level >= AEL_SUSPICIOUS && (NPCInfo->scriptFlags&SCF_LOOK_FOR_ENEMIES) ) { NPCInfo->lastAlertID = level.alertEvents[eventID].ID; if ( !level.alertEvents[eventID].owner || !level.alertEvents[eventID].owner->client || level.alertEvents[eventID].owner->health <= 0 || level.alertEvents[eventID].owner->client->playerTeam != NPC->client->enemyTeam ) {//not an enemy } else { //FIXME: what if can't actually see enemy, don't know where he is... should we make them just become very alert and start looking for him? Or just let combat AI handle this... (act as if you lost him) G_SetEnemy( NPC, level.alertEvents[eventID].owner ); NPCInfo->enemyCheckDebounceTime = level.time + Q_irand( 3000, 10000 ); NPCInfo->enemyLastSeenTime = level.time; TIMER_Set( NPC, "attackDelay", Q_irand( 500, 1000 ) ); } } } } if ( !NPC->enemy ) { if ( NPC->client->leader && NPC->client->leader->enemy && NPC->client->leader->enemy != NPC && ( (NPC->client->leader->enemy->client&&NPC->client->leader->enemy->client->playerTeam==NPC->client->enemyTeam) ||(NPC->client->leader->enemy->svFlags&SVF_NONNPC_ENEMY&&NPC->client->leader->enemy->noDamageTeam==NPC->client->enemyTeam) ) && NPC->client->leader->enemy->health > 0 ) { G_SetEnemy( NPC, NPC->client->leader->enemy ); NPCInfo->enemyCheckDebounceTime = level.time + Q_irand( 3000, 10000 ); NPCInfo->enemyLastSeenTime = level.time; } } } else { if ( NPC->enemy->health <= 0 || (NPC->enemy->flags&FL_NOTARGET) ) { G_ClearEnemy( NPC ); if ( NPCInfo->enemyCheckDebounceTime > level.time + 1000 ) { NPCInfo->enemyCheckDebounceTime = level.time + Q_irand( 1000, 2000 ); } } else if ( NPC->client->ps.weapon && NPCInfo->enemyCheckDebounceTime < level.time ) { NPC_CheckEnemy( (NPCInfo->confusionTime<level.time||NPCInfo->tempBehavior!=BS_FOLLOW_LEADER), qfalse );//don't find new enemy if this is tempbehav } } if ( NPC->enemy && NPC->client->ps.weapon ) {//If have an enemy, face him and fire if ( NPC->client->ps.weapon == WP_SABER )//|| NPCInfo->confusionTime>level.time ) {//lightsaber user or charmed enemy if ( NPCInfo->tempBehavior != BS_FOLLOW_LEADER ) {//not already in a temp bState //go after the guy NPCInfo->tempBehavior = BS_HUNT_AND_KILL; NPC_UpdateAngles(qtrue, qtrue); return; } } enemyVisibility = NPC_CheckVisibility ( NPC->enemy, CHECK_FOV|CHECK_SHOOT );//CHECK_360|CHECK_PVS| if ( enemyVisibility > VIS_PVS ) {//face vec3_t enemy_org, muzzle, delta, angleToEnemy; float distanceToEnemy; CalcEntitySpot( NPC->enemy, SPOT_HEAD, enemy_org ); NPC_AimWiggle( enemy_org ); CalcEntitySpot( NPC, SPOT_WEAPON, muzzle ); VectorSubtract( enemy_org, muzzle, delta); vectoangles( delta, angleToEnemy ); distanceToEnemy = VectorNormalize( delta ); NPCInfo->desiredYaw = angleToEnemy[YAW]; NPCInfo->desiredPitch = angleToEnemy[PITCH]; NPC_UpdateFiringAngles( qtrue, qtrue ); if ( enemyVisibility >= VIS_SHOOT ) {//shoot NPC_AimAdjust( 2 ); if ( NPC_GetHFOVPercentage( NPC->enemy->currentOrigin, NPC->currentOrigin, NPC->client->ps.viewangles, NPCInfo->stats.hfov ) > 0.6f && NPC_GetHFOVPercentage( NPC->enemy->currentOrigin, NPC->currentOrigin, NPC->client->ps.viewangles, NPCInfo->stats.vfov ) > 0.5f ) {//actually withing our front cone WeaponThink( qtrue ); } } else { NPC_AimAdjust( 1 ); } //NPC_CheckCanAttack(1.0, qfalse); } else { NPC_AimAdjust( -1 ); } } else {//FIXME: combine with vector calc below vec3_t head, leaderHead, delta, angleToLeader; CalcEntitySpot( NPC->client->leader, SPOT_HEAD, leaderHead ); CalcEntitySpot( NPC, SPOT_HEAD, head ); VectorSubtract (leaderHead, head, delta); vectoangles ( delta, angleToLeader ); VectorNormalize(delta); NPC->NPC->desiredYaw = angleToLeader[YAW]; NPC->NPC->desiredPitch = angleToLeader[PITCH]; NPC_UpdateAngles(qtrue, qtrue); } //leader visible? leaderVis = NPC_CheckVisibility( NPC->client->leader, CHECK_PVS|CHECK_360|CHECK_SHOOT );// ent->e_UseFunc = useF_NULL; //Follow leader, stay within visibility and a certain distance, maintain a distance from. curAnim = NPC->client->ps.legsAnim; if ( curAnim != BOTH_ATTACK1 && curAnim != BOTH_ATTACK2 && curAnim != BOTH_ATTACK3 && curAnim != BOTH_MELEE1 && curAnim != BOTH_MELEE2 ) {//Don't move toward leader if we're in a full-body attack anim //FIXME, use IdealDistance to determine if we need to close distance float followDist = 96.0f;//FIXME: If there are enmies, make this larger? float backupdist, walkdist, minrundist; if ( NPCInfo->followDist ) { followDist = NPCInfo->followDist; } backupdist = followDist/2.0f; walkdist = followDist*0.83; minrundist = followDist*1.33; VectorSubtract(NPC->client->leader->currentOrigin, NPC->currentOrigin, vec); leaderDist = VectorLength( vec );//FIXME: make this just nav distance? //never get within their radius horizontally vec[2] = 0; float leaderHDist = VectorLength( vec ); if( leaderHDist > backupdist && (leaderVis != VIS_SHOOT || leaderDist > walkdist) ) {//We should close in? NPCInfo->goalEntity = NPC->client->leader; NPC_SlideMoveToGoal(); if ( leaderVis == VIS_SHOOT && leaderDist < minrundist ) { ucmd.buttons |= BUTTON_WALKING; } } else if ( leaderDist < backupdist ) {//We should back off? NPCInfo->goalEntity = NPC->client->leader; NPC_SlideMoveToGoal(); //reversing direction ucmd.forwardmove = -ucmd.forwardmove; ucmd.rightmove = -ucmd.rightmove; VectorScale( NPC->client->ps.moveDir, -1, NPC->client->ps.moveDir ); }//otherwise, stay where we are //check for do not enter and stop if there's one there... if ( ucmd.forwardmove || ucmd.rightmove || VectorCompare( vec3_origin, NPC->client->ps.moveDir ) ) { NPC_MoveDirClear( ucmd.forwardmove, ucmd.rightmove, qtrue ); } } }
void NPC_BSSniper_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_BSSniper_Patrol();//FIXME: or patrol? return; } if ( TIMER_Done( NPC, "flee" ) && NPC_CheckForDanger( NPC_CheckAlertEvents( qtrue, qtrue, -1, qfalse, AEL_DANGER ) ) ) {//going to run NPC_UpdateAngles( qtrue, qtrue ); return; } if ( !NPC->enemy ) {//WTF? somehow we lost our enemy? NPC_BSSniper_Patrol();//FIXME: or patrol? return; } enemyLOS = enemyCS = qfalse; AImove = qtrue; faceEnemy = qfalse; shoot = qfalse; enemyDist = DistanceSquared( NPC->currentOrigin, NPC->enemy->currentOrigin ); if ( enemyDist < 16384 )//128 squared {//too close, so switch to primary fire if ( NPC->client->ps.weapon == WP_DISRUPTOR ) {//sniping... should be assumed if ( NPCInfo->scriptFlags & SCF_ALT_FIRE ) {//use primary fire trace_t trace; gi.trace ( &trace, NPC->enemy->currentOrigin, NPC->enemy->mins, NPC->enemy->maxs, NPC->currentOrigin, NPC->enemy->s.number, NPC->enemy->clipmask, G2_NOCOLLIDE, 0 ); if ( !trace.allsolid && !trace.startsolid && (trace.fraction == 1.0 || trace.entityNum == NPC->s.number ) ) {//he can get right to me NPCInfo->scriptFlags &= ~SCF_ALT_FIRE; //reset fire-timing variables NPC_ChangeWeapon( WP_DISRUPTOR ); NPC_UpdateAngles( qtrue, qtrue ); return; } } //FIXME: switch back if he gets far away again? } } else if ( enemyDist > 65536 )//256 squared { if ( NPC->client->ps.weapon == WP_DISRUPTOR ) {//sniping... should be assumed if ( !(NPCInfo->scriptFlags&SCF_ALT_FIRE) ) {//use primary fire NPCInfo->scriptFlags |= SCF_ALT_FIRE; //reset fire-timing variables NPC_ChangeWeapon( WP_DISRUPTOR ); NPC_UpdateAngles( qtrue, qtrue ); return; } } } Sniper_UpdateEnemyPos(); //can we see our target? if ( NPC_ClearLOS( NPC->enemy ) )//|| (NPCInfo->stats.aim >= 5 && gi.inPVS( NPC->client->renderInfo.eyePoint, NPC->enemy->currentOrigin )) ) { NPCInfo->enemyLastSeenTime = level.time; VectorCopy( NPC->enemy->currentOrigin, NPCInfo->enemyLastSeenLocation ); enemyLOS = qtrue; float maxShootDist = NPC_MaxDistSquaredForWeapon(); if ( enemyDist < maxShootDist ) { vec3_t fwd, right, up, muzzle, end; trace_t tr; AngleVectors( NPC->client->ps.viewangles, fwd, right, up ); CalcMuzzlePoint( NPC, fwd, right, up, muzzle, 0 ); VectorMA( muzzle, 8192, fwd, end ); gi.trace ( &tr, muzzle, NULL, NULL, end, NPC->s.number, MASK_SHOT, G2_RETURNONHIT, 0 ); int hit = tr.entityNum; //can we shoot our target? if ( Sniper_EvaluateShot( hit ) ) { 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 ( enemyCS ) { shoot = qtrue; } else if ( level.time - NPCInfo->enemyLastSeenTime > 3000 ) {//Hmm, have to get around this bastard... FIXME: this NPCInfo->enemyLastSeenTime builds up when ducked seems to make them want to run when they uncrouch Sniper_ResolveBlockedShot(); } //Check for movement to take care of Sniper_CheckMoveState(); //See if we should override shooting decision with any special considerations Sniper_CheckFireState(); if ( AImove ) {//move toward goal if ( NPCInfo->goalEntity )//&& ( NPCInfo->goalEntity != NPC->enemy || enemyDist > 10000 ) )//100 squared { AImove = Sniper_Move(); } else { AImove = qfalse; } } if ( !AImove ) { if ( !TIMER_Done( NPC, "duck" ) ) { if ( TIMER_Done( NPC, "watch" ) ) {//not while watching ucmd.upmove = -127; } } //FIXME: what about leaning? //FIXME: also, when stop ducking, start looking, if enemy can see me, chance of ducking back down again } else {//stop ducking! TIMER_Set( NPC, "duck", -1 ); } if ( TIMER_Done( NPC, "duck" ) && TIMER_Done( NPC, "watch" ) && (TIMER_Get( NPC, "attackDelay" )-level.time) > 1000 && NPC->attackDebounceTime < level.time ) { if ( enemyLOS && (NPCInfo->scriptFlags&SCF_ALT_FIRE) ) { if ( NPC->fly_sound_debounce_time < level.time ) { NPC->fly_sound_debounce_time = level.time + 2000; } } } if ( !faceEnemy ) {//we want to face in the dir we're running if ( AImove ) {//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 Sniper_FaceEnemy(); } if ( NPCInfo->scriptFlags&SCF_DONT_FIRE ) { shoot = qfalse; } //FIXME: don't shoot right away! if ( shoot ) {//try to shoot if it's time if ( TIMER_Done( NPC, "attackDelay" ) ) { WeaponThink( qtrue ); if ( ucmd.buttons&(BUTTON_ATTACK|BUTTON_ALT_ATTACK) ) { G_SoundOnEnt( NPC, CHAN_WEAPON, "sound/null.wav" ); } //took a shot, now hide if ( !(NPC->spawnflags&SPF_NO_HIDE) && !Q_irand( 0, 1 ) ) { //FIXME: do this if in combat point and combat point has duck-type cover... also handle lean-type cover Sniper_StartHide(); } else { TIMER_Set( NPC, "attackDelay", NPCInfo->shotTime-level.time ); } } } }
void NPC_BSSniper_Patrol( void ) {//FIXME: pick up on bodies of dead buddies? NPC->count = 0; 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 auto 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 ); if ( NPC_CheckForDanger( alertEvent ) ) { NPC_UpdateAngles( qtrue, qtrue ); return; } else {//check for other alert events //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( (6-NPCInfo->stats.aim)*100, (6-NPCInfo->stats.aim)*500 ) ); } } else {//FIXME: get more suspicious over time? //Save the position for movement (if necessary) //FIXME: sound? 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 flag //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 ); } NPC_UpdateAngles( qtrue, qtrue ); }
void NPC_BSDefault( void ) { // vec3_t enemyDir; // float enemyDist; // float shootDist; // qboolean enemyFOV = qfalse; // qboolean enemyShotFOV = qfalse; // qboolean enemyPVS = qfalse; // vec3_t enemyHead; // vec3_t muzzle; // qboolean enemyLOS = qfalse; // qboolean enemyCS = qfalse; qboolean move = qtrue; // qboolean shoot = qfalse; if( NPCInfo->scriptFlags & SCF_FIRE_WEAPON ) { WeaponThink( qtrue ); } if ( NPCInfo->scriptFlags & SCF_FORCED_MARCH ) {//being forced to walk if( NPC->client->ps.torsoAnim != TORSO_SURRENDER_START ) { NPC_SetAnim( NPC, SETANIM_TORSO, TORSO_SURRENDER_START, SETANIM_FLAG_HOLD ); } } //look for a new enemy if don't have one and are allowed to look, validate current enemy if have one NPC_CheckEnemy( (NPCInfo->scriptFlags&SCF_LOOK_FOR_ENEMIES), qfalse, qtrue ); if ( !NPC->enemy ) {//still don't have an enemy if ( !(NPCInfo->scriptFlags&SCF_IGNORE_ALERTS) ) {//check for alert events //FIXME: Check Alert events, see if we should investigate or just look at it int alertEvent = NPC_CheckAlertEvents( qtrue, qtrue, -1, qtrue, AEL_DISCOVERED ); //There is an event to look at if ( alertEvent >= 0 && level.alertEvents[alertEvent].ID != NPCInfo->lastAlertID ) {//heard/saw something if ( level.alertEvents[alertEvent].level >= AEL_DISCOVERED && (NPCInfo->scriptFlags&SCF_LOOK_FOR_ENEMIES) ) {//was a big event 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 ); } } else {//FIXME: investigate lesser events } } //FIXME: also check our allies' condition? } } if ( NPC->enemy && !(NPCInfo->scriptFlags&SCF_FORCED_MARCH) ) { // just use the stormtrooper attack AI... NPC_CheckGetNewWeapon(); if ( NPC->client->leader && NPCInfo->goalEntity == NPC->client->leader && !trap_ICARUS_TaskIDPending( NPC, TID_MOVE_NAV ) ) { NPC_ClearGoal(); } NPC_BSST_Attack(); return; /* //have an enemy //FIXME: if one of these fails, meaning we can't shoot, do we really need to do the rest? VectorSubtract( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, enemyDir ); enemyDist = VectorNormalize( enemyDir ); enemyDist *= enemyDist; shootDist = NPC_MaxDistSquaredForWeapon(); enemyFOV = InFOV( NPC->enemy, NPC, NPCInfo->stats.hfov, NPCInfo->stats.vfov ); enemyShotFOV = InFOV( NPC->enemy, NPC, 20, 20 ); enemyPVS = gi.inPVS( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin ); if ( enemyPVS ) {//in the pvs trace_t tr; CalcEntitySpot( NPC->enemy, SPOT_HEAD, enemyHead ); enemyHead[2] -= Q_flrand( 0.0f, NPC->enemy->maxs[2]*0.5f ); CalcEntitySpot( NPC, SPOT_WEAPON, muzzle ); enemyLOS = NPC_ClearLOS( muzzle, enemyHead ); gi.trace ( &tr, muzzle, vec3_origin, vec3_origin, enemyHead, NPC->s.number, MASK_SHOT ); enemyCS = NPC_EvaluateShot( tr.entityNum, qtrue ); } else {//skip thr 2 traces since they would have to fail enemyLOS = qfalse; enemyCS = qfalse; } if ( enemyCS && enemyShotFOV ) {//can hit enemy if we want NPC->cantHitEnemyCounter = 0; } else {//can't hit NPC->cantHitEnemyCounter++; } if ( enemyCS && enemyShotFOV && enemyDist < shootDist ) {//can shoot shoot = qtrue; if ( NPCInfo->goalEntity == NPC->enemy ) {//my goal is my enemy and I have a clear shot, no need to chase right now move = qfalse; } } else {//don't shoot yet, keep chasing shoot = qfalse; move = qtrue; } //shoot decision if ( !(NPCInfo->scriptFlags&SCF_DONT_FIRE) ) {//try to shoot if ( NPC->enemy ) { if ( shoot ) { if( !(NPCInfo->scriptFlags & SCF_FIRE_WEAPON) ) // we've already fired, no need to do it again here { WeaponThink( qtrue ); } } } } //chase decision if ( NPCInfo->scriptFlags & SCF_CHASE_ENEMIES ) {//go after him NPCInfo->goalEntity = NPC->enemy; //FIXME: don't need to chase when have a clear shot and in range? if ( !enemyCS && NPC->cantHitEnemyCounter > 60 ) {//haven't been able to shoot enemy for about 6 seconds, need to do something //FIXME: combat points? Just chase? if ( enemyPVS ) {//in my PVS, just pick a combat point //FIXME: implement } else {//just chase him } } //FIXME: in normal behavior, should we use combat Points? Do we care? Is anyone actually going to ever use this AI? } else if ( NPC->cantHitEnemyCounter > 60 ) {//pick a new one NPC_CheckEnemy( qtrue, qfalse, qtrue ); } if ( enemyPVS && enemyLOS )//&& !enemyShotFOV ) {//have a clear LOS to him//, but not looking at him //Find the desired angles vec3_t angles; GetAnglesForDirection( muzzle, enemyHead, angles ); NPCInfo->desiredYaw = AngleNormalize180( angles[YAW] ); NPCInfo->desiredPitch = AngleNormalize180( angles[PITCH] ); } */ } if ( UpdateGoal() ) {//have a goal if ( !NPC->enemy && NPC->client->leader && NPCInfo->goalEntity == NPC->client->leader && !trap_ICARUS_TaskIDPending( NPC, TID_MOVE_NAV ) ) { NPC_BSFollowLeader(); } else { //set angles if ( (NPCInfo->scriptFlags & SCF_FACE_MOVE_DIR) || NPCInfo->goalEntity != NPC->enemy ) {//face direction of movement, NOTE: default behavior when not chasing enemy NPCInfo->combatMove = qfalse; } else {//face goal.. FIXME: what if have a navgoal but want to face enemy while moving? Will this do that? vec3_t dir, angles; NPCInfo->combatMove = qfalse; VectorSubtract( NPCInfo->goalEntity->r.currentOrigin, NPC->r.currentOrigin, dir ); vectoangles( dir, angles ); NPCInfo->desiredYaw = angles[YAW]; if ( NPCInfo->goalEntity == NPC->enemy ) { NPCInfo->desiredPitch = angles[PITCH]; } } //set movement //override default walk/run behavior //NOTE: redundant, done in NPC_ApplyScriptFlags if ( NPCInfo->scriptFlags & SCF_RUNNING ) { ucmd.buttons &= ~BUTTON_WALKING; } else if ( NPCInfo->scriptFlags & SCF_WALKING ) { ucmd.buttons |= BUTTON_WALKING; } else if ( NPCInfo->goalEntity == NPC->enemy ) { ucmd.buttons &= ~BUTTON_WALKING; } else { ucmd.buttons |= BUTTON_WALKING; } if ( NPCInfo->scriptFlags & SCF_FORCED_MARCH ) {//being forced to walk //if ( g_crosshairEntNum != NPC->s.number ) if (!NPC_SomeoneLookingAtMe(NPC)) {//don't walk if player isn't aiming at me move = qfalse; } } if ( move ) { //move toward goal NPC_MoveToGoal( qtrue ); } } } else if ( !NPC->enemy && NPC->client->leader ) { NPC_BSFollowLeader(); } //update angles NPC_UpdateAngles( qtrue, qtrue ); }
void SandCreature_Chase( void ) { if ( !NPC->enemy->inuse ) {//freed NPC->enemy = NULL; return; } if ( (NPC->svFlags&SVF_LOCKEDENEMY) ) {//always know where he is NPCInfo->enemyLastSeenTime = level.time; } if ( !(NPC->svFlags&SVF_LOCKEDENEMY) ) { if ( level.time-NPCInfo->enemyLastSeenTime > 10000 ) { NPC->enemy = NULL; return; } } if ( NPC->enemy->client ) { if ( (NPC->enemy->client->ps.eFlags&EF_HELD_BY_SAND_CREATURE) || (NPC->enemy->client->ps.eFlags&EF_HELD_BY_RANCOR) || (NPC->enemy->client->ps.eFlags&EF_HELD_BY_WAMPA) ) {//was picked up by another monster, forget about him NPC->enemy = NULL; NPC->svFlags &= ~SVF_LOCKEDENEMY; return; } } //chase the enemy if ( NPC->enemy->client && NPC->enemy->client->ps.groundEntityNum != ENTITYNUM_WORLD && !(NPC->svFlags&SVF_LOCKEDENEMY) ) {//off the ground! //FIXME: keep moving in the dir we were moving for a little bit... } else { float enemyScore = SandCreature_EntScore( NPC->enemy ); if ( enemyScore < MIN_SCORE && !(NPC->svFlags&SVF_LOCKEDENEMY) ) {//too slow or too far away } else { float moveSpeed; if ( NPC->enemy->client ) { moveSpeed = VectorLengthSquared( NPC->enemy->client->ps.velocity ); } else { moveSpeed = VectorLengthSquared( NPC->enemy->s.pos.trDelta ); } if ( moveSpeed ) {//he's still moving, update my goalEntity's origin SandCreature_SeekEnt( NPC->enemy, 0 ); NPCInfo->enemyLastSeenTime = level.time; } } } if ( (level.time-NPCInfo->enemyLastSeenTime) > 5000 && !(NPC->svFlags&SVF_LOCKEDENEMY) ) {//enemy hasn't moved in about 5 seconds, see if there's anything else of interest SandCreature_CheckAlerts(); SandCreature_CheckMovingEnts(); } float enemyDistSq = SandCreature_DistSqToGoal( qtrue ); //FIXME: keeps chasing goalEntity even when it's already reached it...? if ( enemyDistSq >= MIN_ATTACK_DIST_SQ//NPCInfo->goalEntity && && (level.time-NPCInfo->enemyLastSeenTime) <= 3000 ) {//sensed enemy (or something) less than 3 seconds ago ucmd.buttons &= ~BUTTON_WALKING; if ( SandCreature_Move() ) { SandCreature_MoveEffect(); } } else if ( (level.time-NPCInfo->enemyLastSeenTime) <= 5000 && !(NPC->svFlags&SVF_LOCKEDENEMY) ) {//NOTE: this leaves a 2-second dead zone in which they'll just sit there unless their enemy moves //If there is an event we might be interested in if we weren't still interested in our enemy if ( NPC_CheckAlertEvents( qfalse, qtrue, NPCInfo->lastAlertID, qfalse, AEL_MINOR, qtrue ) >= 0 ) {//just stir SandCreature_MoveEffect(); } } if ( enemyDistSq < MIN_ATTACK_DIST_SQ ) { if ( NPC->enemy->client ) { NPC->client->ps.viewangles[YAW] = NPC->enemy->client->ps.viewangles[YAW]; } if ( TIMER_Done( NPC, "breaching" ) ) { //okay to attack SandCreature_Attack( qfalse ); } } else if ( enemyDistSq < MAX_MISS_DIST_SQ && enemyDistSq > MIN_MISS_DIST_SQ && NPC->enemy->client && TIMER_Done( NPC, "breaching" ) && TIMER_Done( NPC, "missDebounce" ) && !VectorCompare( NPC->pos1, NPC->currentOrigin ) //so we don't come up again in the same spot && !Q_irand( 0, 10 ) ) { if ( !(NPC->svFlags&SVF_LOCKEDENEMY) ) { //miss them SandCreature_Attack( qtrue ); VectorCopy( NPC->currentOrigin, NPC->pos1 ); TIMER_Set( NPC, "missDebounce", Q_irand( 3000, 10000 ) ); } } }
void NPC_BSGrenadier_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_BSGrenadier_Patrol();//FIXME: or patrol? return; } if ( TIMER_Done( NPC, "flee" ) && NPC_CheckForDanger( NPC_CheckAlertEvents( qtrue, qtrue, -1, qfalse, AEL_DANGER ) ) ) {//going to run NPC_UpdateAngles( qtrue, qtrue ); return; } if ( !NPC->enemy ) {//WTF? somehow we lost our enemy? NPC_BSGrenadier_Patrol();//FIXME: or patrol? return; } enemyLOS = enemyCS = qfalse; move = qtrue; faceEnemy = qfalse; shoot = qfalse; enemyDist = DistanceSquared( NPC->enemy->currentOrigin, NPC->currentOrigin ); //See if we should switch to melee attack if ( enemyDist < 16384 && (!NPC->enemy->client||NPC->enemy->client->ps.weapon != WP_SABER||!NPC->enemy->client->ps.saberActive) )//128 {//enemy is close and not using saber if ( NPC->client->ps.weapon == WP_THERMAL ) {//grenadier trace_t trace; gi.trace ( &trace, NPC->currentOrigin, NPC->enemy->mins, NPC->enemy->maxs, NPC->enemy->currentOrigin, NPC->s.number, NPC->enemy->clipmask ); if ( !trace.allsolid && !trace.startsolid && (trace.fraction == 1.0 || trace.entityNum == NPC->enemy->s.number ) ) {//I can get right to him //reset fire-timing variables NPC_ChangeWeapon( WP_MELEE ); if ( !(NPCInfo->scriptFlags&SCF_CHASE_ENEMIES) )//NPCInfo->behaviorState == BS_STAND_AND_SHOOT ) {//FIXME: should we be overriding scriptFlags? NPCInfo->scriptFlags |= SCF_CHASE_ENEMIES;//NPCInfo->behaviorState = BS_HUNT_AND_KILL; } } } } else if ( enemyDist > 65536 || (NPC->enemy->client && NPC->enemy->client->ps.weapon == WP_SABER && NPC->enemy->client->ps.saberActive) )//256 {//enemy is far or using saber if ( NPC->client->ps.weapon == WP_MELEE && (NPC->client->ps.stats[STAT_WEAPONS]&(1<<WP_THERMAL)) ) {//fisticuffs, make switch to thermal if have it //reset fire-timing variables NPC_ChangeWeapon( WP_THERMAL ); } } //can we see our target? if ( NPC_ClearLOS( NPC->enemy ) ) { NPCInfo->enemyLastSeenTime = level.time; enemyLOS = qtrue; if ( NPC->client->ps.weapon == WP_MELEE ) { 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 ( InFOV( NPC->enemy->currentOrigin, NPC->currentOrigin, NPC->client->ps.viewangles, 45, 90 ) ) {//in front of me //can we shoot our target? //FIXME: how accurate/necessary is this check? int hit = NPC_ShotEntity( NPC->enemy ); gentity_t *hitEnt = &g_entities[hit]; if ( hit == NPC->enemy->s.number || ( hitEnt && hitEnt->client && hitEnt->client->playerTeam == NPC->client->enemyTeam ) ) { VectorCopy( NPC->enemy->currentOrigin, NPCInfo->enemyLastSeenLocation ); float enemyHorzDist = DistanceHorizontalSquared( NPC->enemy->currentOrigin, NPC->currentOrigin ); if ( enemyHorzDist < 1048576 ) {//within 1024 enemyCS = qtrue; NPC_AimAdjust( 2 );//adjust aim better longer we have clear shot at enemy } else { NPC_AimAdjust( 1 );//adjust aim better longer we can see enemy } } } } else { NPC_AimAdjust( -1 );//adjust aim worse longer we cannot see enemy } /* 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 ( enemyCS ) { shoot = qtrue; if ( NPC->client->ps.weapon == WP_THERMAL ) {//don't chase and throw move = qfalse; } else if ( NPC->client->ps.weapon == WP_MELEE && enemyDist < (NPC->maxs[0]+NPC->enemy->maxs[0]+16)*(NPC->maxs[0]+NPC->enemy->maxs[0]+16) ) {//close enough move = qfalse; } }//this should make him chase enemy when out of range...? //Check for movement to take care of Grenadier_CheckMoveState(); //See if we should override shooting decision with any special considerations Grenadier_CheckFireState(); if ( move ) {//move toward goal if ( NPCInfo->goalEntity )//&& ( NPCInfo->goalEntity != NPC->enemy || enemyDist > 10000 ) )//100 squared { move = Grenadier_Move(); } else { move = qfalse; } } if ( !move ) { if ( !TIMER_Done( NPC, "duck" ) ) { ucmd.upmove = -127; } //FIXME: what about leaning? } else {//stop ducking! TIMER_Set( NPC, "duck", -1 ); } 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: 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 { WeaponThink( qtrue ); TIMER_Set( NPC, "attackDelay", NPCInfo->shotTime-level.time ); } } } }
void NPC_BSSaberDroid_Patrol( void ) {//FIXME: pick up on bodies of dead buddies? if ( NPCInfo->confusionTime < level.time ) {//not confused by mindtrick //Look for any enemies if ( NPCInfo->scriptFlags&SCF_LOOK_FOR_ENEMIES ) { if ( NPC_CheckPlayerTeamStealth() ) {//found an enemy //NPCInfo->behaviorState = BS_HUNT_AND_KILL;//should be automatic now //NPC_AngerSound(); NPC_UpdateAngles( qtrue, qtrue ); return; } } if ( !(NPCInfo->scriptFlags&SCF_IGNORE_ALERTS) ) {//alert reaction behavior. //Is there danger nearby //[CoOp] int alertEvent = NPC_CheckAlertEvents( qtrue, qtrue, -1, qfalse, AEL_SUSPICIOUS, qfalse ); //int alertEvent = NPC_CheckAlertEvents( qtrue, qtrue, -1, qfalse, AEL_SUSPICIOUS ); //[/CoOp] //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" ) ) {//we want to turn off our saber if we need to. if ( !NPC->client->ps.saberHolstered ) {//saber is on. WP_DeactivateSaber( NPC, qfalse ); NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_TURNOFF, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); } } NPC_UpdateAngles( qtrue, qtrue ); }
/* ------------------------- NPC_BSAnimal_Default ------------------------- */ void NPC_BSAnimal_Default( void ) { if (!NPC || !NPC->client) { return; } // Update Some Positions //----------------------- CVec3 CurrentLocation(NPC->currentOrigin); // Update The Leader //------------------- gentity_t* leader = NPC_AnimalUpdateLeader(); // Select Closest Threat Location //-------------------------------- CVec3 ThreatLocation(0,0,0); qboolean PlayerSpawned = G_PlayerSpawned(); if ( PlayerSpawned ) {//player is actually in the level now ThreatLocation = player->currentOrigin; } int alertEvent = NPC_CheckAlertEvents(qtrue, qtrue, -1, qfalse, AEL_MINOR, qfalse); if ( alertEvent >= 0 ) { alertEvent_t *event = &level.alertEvents[alertEvent]; if (event->owner!=NPC && Distance(event->position, CurrentLocation.v)<event->radius) { ThreatLocation = event->position; } } // float DistToThreat = CurrentLocation.Dist(ThreatLocation); // float DistFromHome = CurrentLocation.Dist(mHome); bool EvadeThreat = (level.time<NPCInfo->investigateSoundDebounceTime); bool CharmedDocile = (level.time<NPCInfo->confusionTime); bool CharmedApproach = (level.time<NPCInfo->charmedTime); // If Not Already Evading, Test To See If We Should "Know" About The Threat //-------------------------------------------------------------------------- /* if (false && !EvadeThreat && PlayerSpawned && (DistToThreat<FRIGHTEN_DISTANCE)) { CVec3 LookAim(NPC->currentAngles); LookAim.AngToVec(); CVec3 MyPos(CurrentLocation); MyPos -= ThreatLocation; MyPos.SafeNorm(); float DirectionSimilarity = MyPos.Dot(LookAim); if (fabsf(DirectionSimilarity)<0.8f) { EvadeThreat = true; NPCInfo->investigateSoundDebounceTime = level.time + Q_irand(0, 1000); VectorCopy(ThreatLocation.v, NPCInfo->investigateGoal); } }*/ STEER::Activate(NPC); { // Charmed Approach - Walk TOWARD The Threat Location //---------------------------------------------------- if (CharmedApproach) { NAV::GoTo(NPC, NPCInfo->investigateGoal); } // Charmed Docile - Stay Put //--------------------------- else if (CharmedDocile) { NAV::ClearPath(NPC); STEER::Stop(NPC); } // Run Away From This Threat //--------------------------- else if (EvadeThreat) { NAV::ClearPath(NPC); STEER::Flee(NPC, NPCInfo->investigateGoal); } // Normal Behavior //----------------- else { // Follow Our Pack Leader! //------------------------- if (leader && leader!=NPC) { float followDist = 100.0f; float curDist = Distance(NPC->currentOrigin, leader->followPos); // Update The Leader's Follow Position //------------------------------------- STEER::FollowLeader(NPC, leader, followDist); bool inSeekRange = (curDist<followDist*10.0f); bool onNbrPoints = (NAV::OnNeighboringPoints(NAV::GetNearestNode(NPC), leader->followPosWaypoint)); bool leaderStop = ((level.time - leader->lastMoveTime)>500); // If Close Enough, Dump Any Existing Path //----------------------------------------- if (inSeekRange || onNbrPoints) { NAV::ClearPath(NPC); // If The Leader Isn't Moving, Stop //---------------------------------- if (leaderStop) { STEER::Stop(NPC); } // Otherwise, Try To Get To The Follow Position //---------------------------------------------- else { STEER::Seek(NPC, leader->followPos, fabsf(followDist)/2.0f/*slowing distance*/, 1.0f/*wight*/, leader->resultspeed); } } // Otherwise, Get A Path To The Follow Position //---------------------------------------------- else { NAV::GoTo(NPC, leader->followPosWaypoint); } STEER::Separation(NPC, 4.0f); STEER::AvoidCollisions(NPC, leader); } // Leader AI - Basically Wander //------------------------------ else { // Are We Doing A Path? //---------------------- bool HasPath = NAV::HasPath(NPC); if (HasPath) { HasPath = NAV::UpdatePath(NPC); if (HasPath) { STEER::Path(NPC); // Follow The Path STEER::AvoidCollisions(NPC); } } if (!HasPath) { // If Debounce Time Has Expired, Choose A New Sub State //------------------------------------------------------ if (NPCInfo->investigateDebounceTime<level.time) { // Clear Out Flags From The Previous Substate //-------------------------------------------- NPCInfo->aiFlags &= ~NPCAI_OFF_PATH; NPCInfo->aiFlags &= ~NPCAI_WALKING; // Pick Another Spot //------------------- int NEXTSUBSTATE = Q_irand(0, 10); bool RandomPathNode = (NEXTSUBSTATE<8); //(NEXTSUBSTATE<9); bool PathlessWander = (NEXTSUBSTATE<9); //false; // Random Path Node //------------------ if (RandomPathNode) { // Sometimes, Walk //----------------- if (Q_irand(0, 1)==0) { NPCInfo->aiFlags |= NPCAI_WALKING; } NPCInfo->investigateDebounceTime = level.time + Q_irand(3000, 10000); NAV::FindPath(NPC, NAV::ChooseRandomNeighbor(NAV::GetNearestNode(NPC)));//, mHome.v, WANDER_RANGE)); } // Pathless Wandering //-------------------- else if (PathlessWander) { // Sometimes, Walk //----------------- if (Q_irand(0, 1)==0) { NPCInfo->aiFlags |= NPCAI_WALKING; } NPCInfo->investigateDebounceTime = level.time + Q_irand(3000, 10000); NPCInfo->aiFlags |= NPCAI_OFF_PATH; } // Just Stand Here //----------------- else { NPCInfo->investigateDebounceTime = level.time + Q_irand(2000, 6000); //NPC_SetAnim(NPC, SETANIM_BOTH, ((Q_irand(0, 1)==0)?(BOTH_GUARD_LOOKAROUND1):(BOTH_GUARD_IDLE1)), SETANIM_FLAG_NORMAL); } } // Ok, So We Don't Have A Path, And Debounce Time Is Still Active, So We Are Either Wandering Or Looking Around //-------------------------------------------------------------------------------------------------------------- else { // if (DistFromHome>(WANDER_RANGE)) // { // STEER::Seek(NPC, mHome); // } // else { if (NPCInfo->aiFlags & NPCAI_OFF_PATH) { STEER::Wander(NPC); STEER::AvoidCollisions(NPC); } else { STEER::Stop(NPC); } } } } } } } STEER::DeActivate(NPC, &ucmd); NPC_UpdateAngles( qtrue, qtrue ); }