//========================================================= // SetTurnActivity - measures the difference between the way // the monster is facing and determines whether or not to // select one of the 180 turn animations. //========================================================= void CBaseMonster :: SetTurnActivity ( void ) { float flYD; flYD = FlYawDiff(); if ( flYD <= -45 && LookupActivity ( ACT_TURN_RIGHT ) != ACTIVITY_NOT_AVAILABLE ) {// big right turn m_IdealActivity = ACT_TURN_RIGHT; } else if ( flYD > 45 && LookupActivity ( ACT_TURN_LEFT ) != ACTIVITY_NOT_AVAILABLE ) {// big left turn m_IdealActivity = ACT_TURN_LEFT; } }
float CIchthyosaur::ChangeYaw( int speed ) { if ( pev->movetype == MOVETYPE_FLY ) { float diff = FlYawDiff(); float target = 0; if ( m_IdealActivity != GetStoppedActivity() ) { if ( diff < -20 ) target = 20; else if ( diff > 20 ) target = -20; } pev->angles.z = UTIL_Approach( target, pev->angles.z, 220.0 * 0.1 ); } return CFlyingMonster::ChangeYaw( speed ); }
float CFlyingMonster :: ChangeYaw( int speed ) { if ( pev->movetype == MOVETYPE_FLY ) { float diff = FlYawDiff(); float target = 0; if ( m_IdealActivity != GetStoppedActivity() ) { if ( diff < -20 ) target = 90; else if ( diff > 20 ) target = -90; } pev->angles.z = UTIL_Approach( target, pev->angles.z, 220.0 * gpGlobals->frametime ); } return CBaseMonster::ChangeYaw( speed ); }
//========================================================= // GetSchedule - Decides which type of schedule best suits // the monster's current state and conditions. Then calls // monster's member function to get a pointer to a schedule // of the proper type. //========================================================= Schedule_t *CBaseMonster :: GetSchedule ( void ) { switch ( m_MonsterState ) { case MONSTERSTATE_PRONE: { return GetScheduleOfType( SCHED_BARNACLE_VICTIM_GRAB ); break; } case MONSTERSTATE_NONE: { ALERT ( at_aiconsole, "MONSTERSTATE IS NONE!\n" ); break; } case MONSTERSTATE_IDLE: { if ( HasConditions ( bits_COND_HEAR_SOUND ) ) { return GetScheduleOfType( SCHED_ALERT_FACE ); } else if ( FRouteClear() ) { // no valid route! return GetScheduleOfType( SCHED_IDLE_STAND ); } else { // valid route. Get moving return GetScheduleOfType( SCHED_IDLE_WALK ); } break; } case MONSTERSTATE_ALERT: { if ( HasConditions( bits_COND_ENEMY_DEAD ) && LookupActivity( ACT_VICTORY_DANCE ) != ACTIVITY_NOT_AVAILABLE ) { return GetScheduleOfType ( SCHED_VICTORY_DANCE ); } if ( HasConditions(bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE) ) { if ( fabs( FlYawDiff() ) < (1.0 - m_flFieldOfView) * 60 ) // roughly in the correct direction { return GetScheduleOfType( SCHED_TAKE_COVER_FROM_ORIGIN ); } else { return GetScheduleOfType( SCHED_ALERT_SMALL_FLINCH ); } } else if ( HasConditions ( bits_COND_HEAR_SOUND ) ) { return GetScheduleOfType( SCHED_ALERT_FACE ); } else { return GetScheduleOfType( SCHED_ALERT_STAND ); } break; } case MONSTERSTATE_COMBAT: { if ( HasConditions( bits_COND_ENEMY_DEAD ) ) { // clear the current (dead) enemy and try to find another. m_hEnemy = NULL; if ( GetEnemy() ) { ClearConditions( bits_COND_ENEMY_DEAD ); return GetSchedule(); } else { SetState( MONSTERSTATE_ALERT ); return GetSchedule(); } } if ( HasConditions(bits_COND_NEW_ENEMY) ) { return GetScheduleOfType ( SCHED_WAKE_ANGRY ); } else if (HasConditions(bits_COND_LIGHT_DAMAGE) && !HasMemory( bits_MEMORY_FLINCHED) ) { return GetScheduleOfType( SCHED_SMALL_FLINCH ); } else if ( !HasConditions(bits_COND_SEE_ENEMY) ) { // we can't see the enemy if ( !HasConditions(bits_COND_ENEMY_OCCLUDED) ) { // enemy is unseen, but not occluded! // turn to face enemy return GetScheduleOfType( SCHED_COMBAT_FACE ); } else { // chase! return GetScheduleOfType( SCHED_CHASE_ENEMY ); } } else { // we can see the enemy if ( HasConditions(bits_COND_CAN_RANGE_ATTACK1) ) { return GetScheduleOfType( SCHED_RANGE_ATTACK1 ); } if ( HasConditions(bits_COND_CAN_RANGE_ATTACK2) ) { return GetScheduleOfType( SCHED_RANGE_ATTACK2 ); } if ( HasConditions(bits_COND_CAN_MELEE_ATTACK1) ) { return GetScheduleOfType( SCHED_MELEE_ATTACK1 ); } if ( HasConditions(bits_COND_CAN_MELEE_ATTACK2) ) { return GetScheduleOfType( SCHED_MELEE_ATTACK2 ); } if ( !HasConditions(bits_COND_CAN_RANGE_ATTACK1 | bits_COND_CAN_MELEE_ATTACK1) ) { // if we can see enemy but can't use either attack type, we must need to get closer to enemy return GetScheduleOfType( SCHED_CHASE_ENEMY ); } else if ( !FacingIdeal() ) { //turn return GetScheduleOfType( SCHED_COMBAT_FACE ); } else { ALERT ( at_aiconsole, "No suitable combat schedule!\n" ); } } break; } case MONSTERSTATE_DEAD: { return GetScheduleOfType( SCHED_DIE ); break; } case MONSTERSTATE_SCRIPT: { ASSERT( m_pCine != NULL ); if ( !m_pCine ) { ALERT( at_aiconsole, "Script failed for %s\n", STRING(pev->classname) ); // ALERT( at_console, "Script failed for %s\n", STRING(pev->classname) ); CineCleanup(); return GetScheduleOfType( SCHED_IDLE_STAND ); } return GetScheduleOfType( SCHED_AISCRIPT ); } default: { ALERT ( at_aiconsole, "Invalid State for GetSchedule!\n" ); break; } } return &slError[ 0 ]; }
void CTalkMonster :: RunTask( const Task_t& task ) { switch( task.iTask ) { case TASK_TLK_CLIENT_STARE: case TASK_TLK_LOOK_AT_CLIENT: { edict_t *pPlayer; // track head to the client for a while. if( m_MonsterState == MONSTERSTATE_IDLE && !IsMoving() && !IsTalking() ) { // Get edict for one player pPlayer = g_engfuncs.pfnPEntityOfEntIndex( 1 ); if( pPlayer ) { IdleHeadTurn( pPlayer->v.origin ); } } else { // started moving or talking TaskFail(); return; } if( pPlayer ) { if( task.iTask == TASK_TLK_CLIENT_STARE ) { // fail out if the player looks away or moves away. if( ( pPlayer->v.origin - GetAbsOrigin() ).Length2D() > TLK_STARE_DIST ) { // player moved away. TaskFail(); } UTIL_MakeVectors( pPlayer->v.angles ); if( UTIL_DotPoints( pPlayer->v.origin, GetAbsOrigin(), gpGlobals->v_forward ) < m_flFieldOfView ) { // player looked away TaskFail(); } } } else { TaskFail(); } if( gpGlobals->time > m_flWaitFinished ) { TaskComplete(); } break; } case TASK_FACE_PLAYER: { // Get edict for one player edict_t *pPlayer = g_engfuncs.pfnPEntityOfEntIndex( 1 ); if( pPlayer ) { MakeIdealYaw( pPlayer->v.origin ); ChangeYaw( GetYawSpeed() ); IdleHeadTurn( pPlayer->v.origin ); if( gpGlobals->time > m_flWaitFinished && FlYawDiff() < 10 ) { TaskComplete(); } } else { TaskFail(); } break; } case TASK_TLK_EYECONTACT: { if( !IsMoving() && IsTalking() && m_hTalkTarget != NULL ) { // ALERT( at_console, "waiting %f\n", m_flStopTalkTime - gpGlobals->time ); IdleHeadTurn( m_hTalkTarget->GetAbsOrigin() ); } else { TaskComplete(); } break; } case TASK_WALK_PATH_FOR_UNITS: { float distance; distance = (m_vecLastPosition - GetAbsOrigin()).Length2D(); // Walk path until far enough away if ( distance > task.flData || MovementIsComplete() ) { TaskComplete(); RouteClear(); // Stop moving } break; } case TASK_WAIT_FOR_MOVEMENT: { if( IsTalking() && m_hTalkTarget != NULL ) { // ALERT(at_console, "walking, talking\n"); IdleHeadTurn( m_hTalkTarget->GetAbsOrigin() ); } else { IdleHeadTurn( GetAbsOrigin() ); // override so that during walk, a scientist may talk and greet player FIdleHello(); if( RANDOM_LONG( 0, m_nSpeak * 20 ) == 0 ) { FIdleSpeak(); } } CBaseMonster::RunTask( task ); if( TaskIsComplete() ) IdleHeadTurn( GetAbsOrigin() ); break; } default: { if( IsTalking() && m_hTalkTarget != NULL ) { IdleHeadTurn( m_hTalkTarget->GetAbsOrigin() ); } else { SetBoneController( 0, 0 ); } CBaseMonster::RunTask( task ); break; } } }