/* ============== AICast_QueryThink ============== */ void AICast_QueryThink( cast_state_t *cs ) { gentity_t *ent; qboolean visible; cast_state_t *ocs; vec3_t vec; ent = &g_entities[cs->entityNum]; ocs = AICast_GetCastState( cs->bs->enemy ); // never crouch while in this state (by choice anyway) cs->bs->attackcrouch_time = 0; // look at where we last (thought we) saw them VectorSubtract( cs->vislist[cs->bs->enemy].visible_pos, cs->bs->origin, vec ); VectorNormalize( vec ); vectoangles( vec, cs->bs->ideal_viewangles ); // are they visible now? visible = AICast_VisibleFromPos( cs->bs->origin, cs->entityNum, g_entities[cs->bs->enemy].r.currentOrigin, cs->bs->enemy, qfalse ); // make sure we dont process the sighting of this enemy by going into query mode again, without them being visible again after we leave here cs->vislist[cs->bs->enemy].flags &= ~AIVIS_PROCESS_SIGHTING; // look towards where we last saw them AICast_AimAtEnemy( cs ); // if visible and alert time has expired, go POSTAL if ( ( cs->queryAlertSightTime < 0 ) || ( ( cs->queryAlertSightTime < level.time ) && visible ) ) { if ( !cs->queryAlertSightTime ) { // set the "short reaction" condition BG_UpdateConditionValue( cs->entityNum, ANIM_COND_SHORT_REACTION, qtrue, qfalse ); } AICast_StateChange( cs, AISTATE_COMBAT ); BG_UpdateConditionValue( cs->entityNum, ANIM_COND_SHORT_REACTION, qfalse, qfalse ); AIFunc_BattleStart( cs ); return; } // if they've fired since the start of the query mode, go POSTAL if ( ocs->lastWeaponFired > cs->queryStartTime ) { // set the "short reaction" condition BG_UpdateConditionValue( cs->entityNum, ANIM_COND_SHORT_REACTION, qtrue, qfalse ); AICast_StateChange( cs, AISTATE_COMBAT ); BG_UpdateConditionValue( cs->entityNum, ANIM_COND_SHORT_REACTION, qfalse, qfalse ); AIFunc_BattleStart( cs ); return; } // if not visible, then kill the Lock On timer if ( ( cs->queryAlertSightTime > 0 ) && !visible ) { cs->queryAlertSightTime = 0; } // if the query has expired, go back to relaxed if ( !ent->client->ps.legsTimer ) { AICast_StateChange( cs, AISTATE_RELAXED ); } }
/* ============== AICast_CheckVisibility ============== */ qboolean AICast_CheckVisibility( gentity_t *srcent, gentity_t *destent ) { vec3_t dir, entangles, middle, eye, viewangles; cast_state_t *cs, *ocs; float fov, dist; int viewer, ent; cast_visibility_t *vis; orientation_t or; if ( destent->flags & FL_NOTARGET ) { return qfalse; } // viewer = srcent->s.number; ent = destent->s.number; // cs = AICast_GetCastState( viewer ); ocs = AICast_GetCastState( ent ); // vis = &cs->vislist[ent]; // // if the destent is the client, and they have just loaded a savegame, ignore them temporarily if ( !destent->aiCharacter && level.lastLoadTime && ( level.lastLoadTime > level.time - 2000 ) && !vis->visible_timestamp ) { return qfalse; } // if we heard them /* if ( (vis->lastcheck_timestamp) && (ocs->lastWeaponFired) && (ocs->lastWeaponFired >= vis->lastcheck_timestamp) && (AICast_GetWeaponSoundRange( ocs->lastWeaponFiredWeaponNum ) > Distance( srcent->r.currentOrigin, ocs->lastWeaponFiredPos ))) { return qtrue; } */ // // set the FOV fov = cs->attributes[FOV] * aiStateFovScales[cs->aiState]; if ( !fov ) { // assume it's a player, give them a generic fov fov = 180; } if ( cs->aiFlags & AIFL_ZOOMING ) { fov *= 0.8; } else { if ( cs->lastEnemy >= 0 ) { // they've already been in a fight, so give them a very large fov if ( fov < 270 ) { fov = 270; } } } // RF, if they were visible last check, then give us a full FOV, since we are aware of them if ( cs->aiState >= AISTATE_ALERT && vis->visible_timestamp == vis->lastcheck_timestamp ) { fov = 360; } //calculate middle of bounding box VectorAdd( destent->r.mins, destent->r.maxs, middle ); VectorScale( middle, 0.5, middle ); VectorAdd( destent->client->ps.origin, middle, middle ); // calculate eye position if ( ( level.lastLoadTime < level.time - 4000 ) && ( srcent->r.svFlags & SVF_CASTAI ) ) { if ( clientHeadTagTimes[srcent->s.number] == level.time ) { // use the actual direction the head is facing vectoangles( clientHeadTags[srcent->s.number].axis[0], viewangles ); // and the actual position of the head VectorCopy( clientHeadTags[srcent->s.number].origin, eye ); } else if ( trap_GetTag( srcent->s.number, "tag_head", &or ) ) { // use the actual direction the head is facing vectoangles( or.axis[0], viewangles ); // and the actual position of the head VectorCopy( or.origin, eye ); VectorMA( eye, 12, or.axis[2], eye ); // save orientation data memcpy( &clientHeadTags[srcent->s.number], &or, sizeof( orientation_t ) ); clientHeadTagTimes[srcent->s.number] = level.time; } else { VectorCopy( srcent->client->ps.origin, eye ); eye[2] += srcent->client->ps.viewheight; VectorCopy( srcent->client->ps.viewangles, viewangles ); // save orientation data (so we dont keep checking for a tag when it doesn't exist) VectorCopy( eye, clientHeadTags[srcent->s.number].origin ); AnglesToAxis( viewangles, clientHeadTags[srcent->s.number].axis ); clientHeadTagTimes[srcent->s.number] = level.time; } } else { VectorCopy( srcent->client->ps.origin, eye ); eye[2] += srcent->client->ps.viewheight; VectorCopy( srcent->client->ps.viewangles, viewangles ); } //check if entity is within field of vision VectorSubtract( middle, eye, dir ); vectoangles( dir, entangles ); // dist = VectorLength( dir ); // // alertness is visible range if ( cs->bs && dist > cs->attributes[ALERTNESS] ) { return qfalse; } // check FOV if ( !AICast_InFieldOfVision( viewangles, fov, entangles ) ) { return qfalse; } // if ( !AICast_VisibleFromPos( srcent->client->ps.origin, srcent->s.number, destent->client->ps.origin, destent->s.number, qtrue ) ) { return qfalse; } // return qtrue; }
/* ============== AICast_CheckVisibility ============== */ qboolean AICast_CheckVisibility( gentity_t *srcent, gentity_t *destent ) { vec3_t dir, entangles, middle, eye, viewangles; cast_state_t *cs; float fov, dist; int viewer, ent; orientation_t or; if ( destent->flags & FL_NOTARGET ) { return qfalse; } viewer = srcent->s.number; ent = destent->s.number; // cs = AICast_GetCastState( viewer ); AICast_GetCastState( ent ); // set the FOV fov = cs->attributes[FOV] * aiStateFovScales[cs->aiState]; if ( !fov ) { // assume it's a player, give them a generic fov fov = 180; } if ( cs->aiFlags & AIFL_ZOOMING ) { fov *= 0.8; } //calculate middle of bounding box VectorAdd( destent->r.mins, destent->r.maxs, middle ); VectorScale( middle, 0.5, middle ); VectorAdd( destent->client->ps.origin, middle, middle ); // calculate eye position if ( srcent->r.svFlags & SVF_CASTAI ) { if ( trap_GetTag( srcent->s.number, "tag_head", &or ) ) { // use the actual direction the head is facing vectoangles( or.axis[0], viewangles ); // and the actual position of the head VectorCopy( or.origin, eye ); } else { VectorCopy( srcent->client->ps.origin, eye ); eye[2] += srcent->client->ps.viewheight; VectorCopy( srcent->client->ps.viewangles, viewangles ); } } else { VectorCopy( srcent->client->ps.origin, eye ); eye[2] += srcent->client->ps.viewheight; VectorCopy( srcent->client->ps.viewangles, viewangles ); } //check if entity is within field of vision VectorSubtract( middle, eye, dir ); vectoangles( dir, entangles ); // dist = VectorLength( dir ); // // alertness is visible range if ( cs->bs && dist > cs->attributes[ALERTNESS] ) { return qfalse; } // check FOV if ( !AICast_InFieldOfVision( viewangles, fov, entangles ) ) { return qfalse; } // if ( !AICast_VisibleFromPos( srcent->client->ps.origin, srcent->s.number, destent->client->ps.origin, destent->s.number, qtrue ) ) { return qfalse; } // return qtrue; }