/* ------------------------- NPC_CheckSightEvents ------------------------- */ static int G_CheckSightEvents( gentity_t *self, int hFOV, int vFOV, float maxSeeDist, int ignoreAlert, qboolean mustHaveOwner, int minAlertLevel ) { int bestEvent = -1; int bestAlert = -1; int bestTime = -1; float dist, radius; maxSeeDist *= maxSeeDist; for ( int i = 0; i < level.numAlertEvents; i++ ) { //are we purposely ignoring this alert? if ( level.alertEvents[i].ID == ignoreAlert ) continue; //We're only concerned about sounds if ( level.alertEvents[i].type != AET_SIGHT ) continue; //must be at least this noticable if ( level.alertEvents[i].level < minAlertLevel ) continue; //must have an owner? if ( mustHaveOwner && !level.alertEvents[i].owner ) continue; //Must be within range dist = DistanceSquared( level.alertEvents[i].position, self->currentOrigin ); //can't see it if ( dist > maxSeeDist ) continue; radius = level.alertEvents[i].radius * level.alertEvents[i].radius; if ( dist > radius ) continue; //Must be visible if ( InFOV( level.alertEvents[i].position, self, hFOV, vFOV ) == qfalse ) continue; if ( G_ClearLOS( self, level.alertEvents[i].position ) == qfalse ) continue; //FIXME: possibly have the light level at this point affect the // visibility/alert level of this event? Would also // need to take into account how bright the event // itself is. A lightsaber would stand out more // in the dark... maybe pass in a light level that // is added to the actual light level at this position? //See if this one takes precedence over the previous one if ( level.alertEvents[i].level >= bestAlert //higher alert level || (level.alertEvents[i].level==bestAlert&&level.alertEvents[i].timestamp >= bestTime) )//same alert level, but this one is newer {//NOTE: equal is better because it's later in the array bestEvent = i; bestAlert = level.alertEvents[i].level; bestTime = level.alertEvents[i].timestamp; } } return bestEvent; }
static qboolean NPC_CheckPlayerDistance( void ) { //Make sure we have an enemy if ( NPC->enemy == NULL ) return qfalse; //Only do this for non-players if ( NPC->enemy->s.number == 0 ) return qfalse; //must be set up to get mad at player if ( !NPC->client || NPC->client->enemyTeam != TEAM_PLAYER ) return qfalse; //Must be within our FOV if ( InFOV( &g_entities[0], NPC, NPCInfo->stats.hfov, NPCInfo->stats.vfov ) == qfalse ) return qfalse; float distance = DistanceSquared( NPC->currentOrigin, NPC->enemy->currentOrigin ); if ( distance > DistanceSquared( NPC->currentOrigin, g_entities[0].currentOrigin ) ) { G_SetEnemy( NPC, &g_entities[0] ); return qtrue; } return qfalse; }
qboolean SensoryPerception::CanSeePosition( const Vector &start, const Vector &position, qboolean useFOV , qboolean useVisionDistance ) { // Check if This Actor can even see at all if ( !ShouldRespondToStimuli( StimuliSight ) ) return false; // Check for FOV if ( useFOV ) { if ( !InFOV( position ) ) return false; } // Check for vision distance if ( useVisionDistance ) { if ( !WithinVisionDistance( position ) ) return false; } // Do Trace // Check if he's visible auto trace = G_Trace( start, vec_zero, vec_zero, position, act, MASK_OPAQUE, false, "SensoryPerception::CanSeeEntity" ); if ( trace.fraction == 1.0f ) return true; return false; }
//rww - cheap check to see if an armed client is looking in our general direction qboolean NPC_SomeoneLookingAtMe(gentity_t *ent) { int i = 0; gentity_t *pEnt; while (i < MAX_CLIENTS) { pEnt = &g_entities[i]; if (pEnt && pEnt->inuse && pEnt->client && pEnt->client->sess.sessionTeam != TEAM_SPECTATOR && !(pEnt->client->ps.pm_flags & PMF_FOLLOW) && pEnt->s.weapon != WP_NONE) { if (trap_InPVS(ent->r.currentOrigin, pEnt->r.currentOrigin)) { if (InFOV( ent, pEnt, 30, 30 )) { //I'm in a 30 fov or so cone from this player.. that's enough I guess. return qtrue; } } } i++; } return qfalse; }
// // Name: CanSeeEntity() // Parameters: Entity *start - The entity trying to see // Entity *target - The entity that needs to be seen // qboolean useFOV - take FOV into consideration or not // qboolean useVisionDistance - take visionDistance into consideration // Description: Wraps a lot of the different CanSee Functions into one // qboolean SensoryPerception::CanSeeEntity( Entity *start, const Entity *target, qboolean useFOV, qboolean useVisionDistance ) { // Check for NULL if ( !start || !target ) return false; // Check if This Actor can even see at all if ( !ShouldRespondToStimuli( StimuliSight ) ) return false; // Check for FOV if ( useFOV ) { if ( !InFOV( target ) ) return false; } // Check for vision distance if ( useVisionDistance ) { if ( !WithinVisionDistance( target ) ) return false; } // Do Trace auto p = target->centroid; auto eyePos = vec_zero; // If the start entity is an actor, then we want to add in the eyeposition if ( start->isSubclassOf ( Actor ) ) { Actor *a; a = dynamic_cast<Actor*>(start); if ( !a ) return false; eyePos = a->EyePosition(); } // Check if he's visible auto trace = G_Trace( eyePos, vec_zero, vec_zero, p, target, MASK_OPAQUE, false, "SensoryPerception::CanSeeEntity" ); if ( trace.fraction == 1.0f || trace.ent == target->edict ) return true; // Check if his head is visible p.z = target->absmax.z; trace = G_Trace( eyePos, vec_zero, vec_zero, p, target, MASK_OPAQUE, false, "SensoryPerception::CanSeeEntity" ); if ( trace.fraction == 1.0f || trace.ent == target->edict ) return true; return false; }
// // Name: CanSeeEntity() // Parameters: Vector &start -- The starting position // Entity *target - The entity that needs to be seen // qboolean useFOV - take FOV into consideration or not // qboolean useVisionDistance - take visionDistance into consideration // Description: Wraps a lot of the different CanSee Functions into one // qboolean SensoryPerception::CanSeeEntity( const Vector &start , const Entity *target, qboolean useFOV , qboolean useVisionDistance ) { // Check for NULL if ( !target ) return false; // Check if This Actor can even see at all if ( !ShouldRespondToStimuli( StimuliSight ) ) return false; // Check for FOV if ( useFOV ) { if ( !InFOV( target ) ) return false; } // Check for vision distance if ( useVisionDistance ) { if ( !WithinVisionDistance( target ) ) return false; } // Do Trace auto realStart = start; auto p = target->centroid; // Add in the eye offset auto eyePos = act->EyePosition() - act->origin; realStart += eyePos; // Check if he's visible auto trace = G_Trace( realStart, vec_zero, vec_zero, p, act, MASK_OPAQUE, false, "SensoryPerception::CanSeeEntity" ); if ( trace.fraction == 1.0f || trace.ent == target->edict ) return true; // Check if his head is visible p.z = target->absmax.z; trace = G_Trace( realStart, vec_zero, vec_zero, p, act, MASK_OPAQUE, false, "SensoryPerception::CanSeeEntity" ); if ( trace.fraction == 1.0f || trace.ent == target->edict ) return true; return false; }
qboolean NPC_TargetVisible( gentity_t *ent ) { //Make sure we're in a valid range if ( DistanceSquared( ent->currentOrigin, NPC->currentOrigin ) > ( NPCInfo->stats.visrange * NPCInfo->stats.visrange ) ) return qfalse; //Check our FOV if ( InFOV( ent, NPC, NPCInfo->stats.hfov, NPCInfo->stats.vfov ) == qfalse ) return qfalse; //Check for sight if ( NPC_ClearLOS( ent ) == qfalse ) return qfalse; return qtrue; }
qboolean InFOV( vec3_t origin, gentity_t *from, int hFOV, int vFOV ) { vec3_t fromAngles, eyes; if( from->client ) { VectorCopy(from->client->ps.viewangles, fromAngles); } else { VectorCopy(from->s.angles, fromAngles); } CalcEntitySpot( from, SPOT_HEAD, eyes ); return InFOV( origin, eyes, fromAngles, hFOV, vFOV ); }
void NPC_RemoveBody( gentity_t *self ) { // Only do all of this nonsense for Scav boys ( and girls ) if ( self->client->playerTeam == TEAM_SCAVENGERS || self->client->playerTeam == TEAM_KLINGON || self->client->playerTeam == TEAM_HIROGEN || self->client->playerTeam == TEAM_MALON ) { self->nextthink = level.time + 1000; // try back in a second if ( DistanceSquared( g_entities[0].currentOrigin, self->currentOrigin ) <= REMOVE_DISTANCE_SQR ) { return; } if ( (InFOV( self, &g_entities[0], 110, 90 )) ) // generous FOV check { if ( (NPC_ClearLOS( &g_entities[0], self->currentOrigin )) ) { return; } } } G_FreeEntity( self ); }
qboolean NPC_CheckSurrender( void ) { if ( !g_AIsurrender->integer ) {//not enabled return qfalse; } if ( !Q3_TaskIDPending( NPC, TID_MOVE_NAV ) && NPC->client->ps.groundEntityNum != ENTITYNUM_NONE && !NPC->client->ps.weaponTime && !PM_InKnockDown( &NPC->client->ps ) && NPC->enemy && NPC->enemy->client && NPC->enemy->enemy == NPC && NPC->enemy->s.weapon != WP_NONE && NPC->enemy->s.weapon != WP_MELEE && NPC->enemy->health > 20 && NPC->enemy->painDebounceTime < level.time - 3000 && NPC->enemy->client->ps.forcePowerDebounce[FP_SABER_DEFENSE] < level.time - 1000 ) {//don't surrender if scripted to run somewhere or if we're in the air or if we're busy or if we don't have an enemy or if the enemy is not mad at me or is hurt or not a threat or busy being attacked //FIXME: even if not in a group, don't surrender if there are other enemies in the PVS and within a certain range? if ( NPC->s.weapon != WP_ROCKET_LAUNCHER && NPC->s.weapon != WP_REPEATER && NPC->s.weapon != WP_FLECHETTE && NPC->s.weapon != WP_SABER ) {//jedi and heavy weapons guys never surrender //FIXME: rework all this logic into some orderly fashion!!! if ( NPC->s.weapon != WP_NONE ) {//they have a weapon so they'd have to drop it to surrender //don't give up unless low on health if ( NPC->health > 25 || NPC->health >= NPC->max_health ) { return qfalse; } if ( g_crosshairEntNum == NPC->s.number && NPC->painDebounceTime > level.time ) {//if he just shot me, always give up //fall through } else {//don't give up unless facing enemy and he's very close if ( !InFOV( player, NPC, 60, 30 ) ) {//I'm not looking at them return qfalse; } else if ( DistanceSquared( NPC->currentOrigin, player->currentOrigin ) < 65536/*256*256*/ ) {//they're not close return qfalse; } else if ( !gi.inPVS( NPC->currentOrigin, player->currentOrigin ) ) {//they're not in the same room return qfalse; } } } if ( NPCInfo->group && NPCInfo->group->numGroup <= 1 ) {//I'm alone but I was in a group//FIXME: surrender anyway if just melee or no weap? if ( NPC->s.weapon == WP_NONE //NPC has a weapon || NPC->enemy == player || (NPC->enemy->s.weapon == WP_SABER&&NPC->enemy->client&&NPC->enemy->client->ps.saberActive) || (NPC->enemy->NPC && NPC->enemy->NPC->group && NPC->enemy->NPC->group->numGroup > 2) ) {//surrender only if have no weapon or fighting a player or jedi or if we are outnumbered at least 3 to 1 if ( NPC->enemy == player ) {//player is the guy I'm running from if ( g_crosshairEntNum == NPC->s.number ) {//give up if player is aiming at me NPC_Surrender(); NPC_UpdateAngles( qtrue, qtrue ); return qtrue; } else if ( player->s.weapon == WP_SABER ) {//player is using saber if ( InFOV( NPC, player, 60, 30 ) ) {//they're looking at me if ( DistanceSquared( NPC->currentOrigin, player->currentOrigin ) < 16384/*128*128*/ ) {//they're close if ( gi.inPVS( NPC->currentOrigin, player->currentOrigin ) ) {//they're in the same room NPC_Surrender(); NPC_UpdateAngles( qtrue, qtrue ); return qtrue; } } } } } else if ( NPC->enemy ) {//??? //should NPC's surrender to others? if ( InFOV( NPC, NPC->enemy, 30, 30 ) ) {//they're looking at me if ( DistanceSquared( NPC->currentOrigin, NPC->enemy->currentOrigin ) < 4096 ) {//they're close if ( gi.inPVS( NPC->currentOrigin, NPC->enemy->currentOrigin ) ) {//they're in the same room //FIXME: should player-team NPCs not fire on surrendered NPCs? NPC_Surrender(); NPC_UpdateAngles( qtrue, qtrue ); return qtrue; } } } } } } } } return qfalse; }
visibility_t NPC_CheckVisibility ( gentity_t *ent, int flags ) { // flags should never be 0 if ( !flags ) { return VIS_NOT; } // check PVS if ( flags & CHECK_PVS ) { if ( !gi.inPVS ( ent->currentOrigin, NPC->currentOrigin ) ) { return VIS_NOT; } } if ( !(flags & (CHECK_360|CHECK_FOV|CHECK_SHOOT)) ) { return VIS_PVS; } // check within visrange if (flags & CHECK_VISRANGE) { if( !InVisrange ( ent ) ) { return VIS_PVS; } } // check 360 degree visibility //Meaning has to be a direct line of site if ( flags & CHECK_360 ) { if ( !CanSee ( ent ) ) { return VIS_PVS; } } if ( !(flags & (CHECK_FOV|CHECK_SHOOT)) ) { return VIS_360; } // check FOV if ( flags & CHECK_FOV ) { if ( !InFOV ( ent, NPC, NPCInfo->stats.hfov, NPCInfo->stats.vfov) ) { return VIS_360; } } if ( !(flags & CHECK_SHOOT) ) { return VIS_FOV; } // check shootability if ( flags & CHECK_SHOOT ) { if ( !CanShoot ( ent, NPC ) ) { return VIS_FOV; } } return VIS_SHOOT; }
static qboolean NPC_CheckPlayerDistance( void ) {//racc - check for a closer player to the NPC than it's current enemy. //qtrue = changed to closer player //qfalse = no change. //[CoOp] added SP Code //also set it up to work for multiple players. //return qfalse;//MOOT in MP int i; //counter //closest current distance gentity_t* player; float ClosestDistance; int ClosestPlayer = -1; //current closest player float distance; //Make sure we have an enemy if ( NPC->enemy == NULL ) return qfalse; ClosestDistance = DistanceSquared( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin ); /* //Only do this for non-players if ( NPC->enemy->s.number == 0 ) return qfalse; */ //must be set up to get mad at player if ( !NPC->client || NPC->client->enemyTeam != NPCTEAM_PLAYER ) return qfalse; //go into the scan loop. for( i = 0; i < MAX_CLIENTS; i++ ) { player = &g_entities[i]; if(!player->inuse || !player->client || player->client->pers.connected != CON_CONNECTED || player->client->sess.sessionTeam == TEAM_SPECTATOR || player->health < 0) {//not a good player continue; } //Must be within our FOV if ( InFOV( player, NPC, NPCInfo->stats.hfov, NPCInfo->stats.vfov ) == qfalse ) continue; distance = DistanceSquared( NPC->r.currentOrigin, player->r.currentOrigin ); if( distance < ClosestDistance ) {//we're closer than the current closest ClosestDistance = distance; ClosestPlayer = player->s.number; } } if(ClosestPlayer != -1) { if(ClosestDistance + 128 * 128 < DistanceSquared( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin ) ) {//player has too be reasonably closer than the current enemy G_SetEnemy( NPC, &g_entities[ClosestPlayer] ); return qtrue; } } /* //Must be within our FOV if ( InFOV( &g_entities[0], NPC, NPCInfo->stats.hfov, NPCInfo->stats.vfov ) == qfalse ) return qfalse; distance = DistanceSquared( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin ); if ( distance > DistanceSquared( NPC->r.currentOrigin, g_entities[0].r.currentOrigin ) ) { //rwwFIXMEFIXME: care about all clients not just client 0 G_SetEnemy( NPC, &g_entities[0] ); return qtrue; } */ //[/CoOp] SP Code return qfalse; }
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 ); } } } }
// // Name: InFOV() // Parameters: Entity* ent -- Provides the Position to check // Description: Calls another version InFOV // qboolean SensoryPerception::InFOV( const Entity *ent ) { return InFOV( ent->centroid ); }
// // Name: InFOV() // Parameters: Vector &pos -- The position to be checked // Description: Calls another version of InFOV // qboolean SensoryPerception::InFOV( const Vector &pos ) { return InFOV( pos, _fov, _fovdot ); }