HalfLifeAI

// 몬스터의 state index
typedef enum
{

    MONSTERSTATE_NONE = 0,
    MONSTERSTATE_IDLE,
    MONSTERSTATE_COMBAT,
    MONSTERSTATE_ALERT,
    MONSTERSTATE_HUNT,
    MONSTERSTATE_PRONE,
    MONSTERSTATE_SCRIPT,
    MONSTERSTATE_PLAYDEAD,
    MONSTERSTATE_DEAD

} MONSTERSTATE;

//=========================================================
// These are the schedule types
//=========================================================
typedef enum
{
        SCHED_NONE = 0,
        SCHED_IDLE_STAND,
        SCHED_IDLE_WALK,
        SCHED_WAKE_ANGRY,
        SCHED_WAKE_CALLED,
        SCHED_ALERT_FACE,
        SCHED_ALERT_SMALL_FLINCH,
        SCHED_ALERT_BIG_FLINCH,
        SCHED_ALERT_STAND,
        SCHED_INVESTIGATE_SOUND,
        SCHED_COMBAT_FACE,
        SCHED_COMBAT_STAND,
        SCHED_CHASE_ENEMY,
        SCHED_CHASE_ENEMY_FAILED,
        SCHED_VICTORY_DANCE,
        SCHED_TARGET_FACE,
        SCHED_TARGET_CHASE,
        SCHED_SMALL_FLINCH,
        SCHED_TAKE_COVER_FROM_ENEMY,
        SCHED_TAKE_COVER_FROM_BEST_SOUND,
        SCHED_TAKE_COVER_FROM_ORIGIN,
        SCHED_COWER, // usually a last resort!
        SCHED_MELEE_ATTACK1,
        SCHED_MELEE_ATTACK2,
        SCHED_RANGE_ATTACK1,
        SCHED_RANGE_ATTACK2,
        SCHED_SPECIAL_ATTACK1,
        SCHED_SPECIAL_ATTACK2,
        SCHED_STANDOFF,
        SCHED_ARM_WEAPON,
        SCHED_RELOAD,
        SCHED_GUARD,
        SCHED_AMBUSH,
        SCHED_DIE,
        SCHED_WAIT_TRIGGER,
        SCHED_FOLLOW,
        SCHED_SLEEP,
        SCHED_WAKE,
        SCHED_BARNACLE_VICTIM_GRAB,
        SCHED_BARNACLE_VICTIM_CHOMP,
        SCHED_AISCRIPT,
        SCHED_FAIL,

        LAST_COMMON_SCHEDULE            // Leave this at the bottom
} SCHEDULE_TYPE;

//=========================================================
// These are the shared tasks
//=========================================================
typedef enum
{
        TASK_INVALID = 0,
        TASK_WAIT,
        TASK_WAIT_FACE_ENEMY,
        TASK_WAIT_PVS,
        TASK_SUGGEST_STATE,
        TASK_WALK_TO_TARGET,
        TASK_RUN_TO_TARGET,
        TASK_MOVE_TO_TARGET_RANGE,
        TASK_GET_PATH_TO_ENEMY,
        TASK_GET_PATH_TO_ENEMY_LKP,
        TASK_GET_PATH_TO_ENEMY_CORPSE,
        TASK_GET_PATH_TO_LEADER,
        TASK_GET_PATH_TO_SPOT,
        TASK_GET_PATH_TO_TARGET,
        TASK_GET_PATH_TO_HINTNODE,
        TASK_GET_PATH_TO_LASTPOSITION,
        TASK_GET_PATH_TO_BESTSOUND,
        TASK_GET_PATH_TO_BESTSCENT,
        TASK_RUN_PATH,
        TASK_WALK_PATH,
        TASK_STRAFE_PATH,
        TASK_CLEAR_MOVE_WAIT,
        TASK_STORE_LASTPOSITION,
        TASK_CLEAR_LASTPOSITION,
        TASK_PLAY_ACTIVE_IDLE,
        TASK_FIND_HINTNODE,
        TASK_CLEAR_HINTNODE,
        TASK_SMALL_FLINCH,
        TASK_FACE_IDEAL,
        TASK_FACE_ROUTE,
        TASK_FACE_ENEMY,
        TASK_FACE_HINTNODE,
        TASK_FACE_TARGET,
        TASK_FACE_LASTPOSITION,
        TASK_RANGE_ATTACK1,
        TASK_RANGE_ATTACK2,
        TASK_MELEE_ATTACK1,
        TASK_MELEE_ATTACK2,
        TASK_RELOAD,
        TASK_RANGE_ATTACK1_NOTURN,
        TASK_RANGE_ATTACK2_NOTURN,
        TASK_MELEE_ATTACK1_NOTURN,
        TASK_MELEE_ATTACK2_NOTURN,
        TASK_RELOAD_NOTURN,
        TASK_SPECIAL_ATTACK1,
        TASK_SPECIAL_ATTACK2,
        TASK_CROUCH,
        TASK_STAND,
        TASK_GUARD,
        TASK_STEP_LEFT,
        TASK_STEP_RIGHT,
        TASK_STEP_FORWARD,
        TASK_STEP_BACK,
        TASK_DODGE_LEFT,
        TASK_DODGE_RIGHT,
        TASK_SOUND_ANGRY,
        TASK_SOUND_DEATH,
        TASK_SET_ACTIVITY,
        TASK_SET_SCHEDULE,
        TASK_SET_FAIL_SCHEDULE,
        TASK_CLEAR_FAIL_SCHEDULE,
        TASK_PLAY_SEQUENCE,
        TASK_PLAY_SEQUENCE_FACE_ENEMY,
        TASK_PLAY_SEQUENCE_FACE_TARGET,
        TASK_SOUND_IDLE,
        TASK_SOUND_WAKE,
        TASK_SOUND_PAIN,
        TASK_SOUND_DIE,
        TASK_FIND_COVER_FROM_BEST_SOUND,// tries lateral cover first, then node cover
        TASK_FIND_COVER_FROM_ENEMY,// tries lateral cover first, then node cover
        TASK_FIND_LATERAL_COVER_FROM_ENEMY,
        TASK_FIND_NODE_COVER_FROM_ENEMY,
        TASK_FIND_NEAR_NODE_COVER_FROM_ENEMY,// data for this one is the MAXIMUM acceptable distance to the cover.
        TASK_FIND_FAR_NODE_COVER_FROM_ENEMY,// data for this one is there MINIMUM aceptable distance to the cover.
        TASK_FIND_COVER_FROM_ORIGIN,
        TASK_EAT,
        TASK_DIE,
        TASK_WAIT_FOR_SCRIPT,
        TASK_PLAY_SCRIPT,
        TASK_ENABLE_SCRIPT,
        TASK_PLANT_ON_SCRIPT,
        TASK_FACE_SCRIPT,
        TASK_WAIT_RANDOM,
        TASK_WAIT_INDEFINITE,
        TASK_STOP_MOVING,
        TASK_TURN_LEFT,
        TASK_TURN_RIGHT,
        TASK_REMEMBER,
        TASK_FORGET,
        TASK_WAIT_FOR_MOVEMENT,         // wait until MovementIsComplete()
        LAST_COMMON_TASK, // LEAVE THIS AT THE BOTTOM!! (sjb)
} SHARED_TASKS;

// an array of tasks is a task list
// an array of schedules is a schedule list
struct Task_t
{

    int     iTask;
    float   flData;
};

struct Schedule_t
{

    Task_t  *pTasklist;
    int     cTasks;
    int     iInterruptMask;// a bit mask of conditions that can interrupt this schedule

    // a more specific mask that indicates which TYPES of sounds will interrupt the schedule in the
    // event that the schedule is broken by COND_HEAR_SOUND
    int     iSoundMask;
    const   char *pName;
};

// 추측컨데 Task는 일련의 세부적인 항목을 Schedule은 여러개의 Task를 포함한 행동들을 나타낸다고
생각됩니다. 이런 식으로 세부행동과 그 세부행동을 포괄할 수 있는 개념의 Schedule과 같은 형식으로
정리를 한다면 하위 개념의 애니메이션을 일목 요연하게 패턴화해서 적용할 수 있다고 생각합니다.

//Schedule과 Task의 사용 예
//=========================================================
// AI Schedules Specific to talking monsters
//=========================================================

Task_t  tlIdleResponse[] =
{
    { TASK_SET_ACTIVITY,    (float)ACT_IDLE },// Stop and listen
    { TASK_WAIT,            (float)0.5      },// Wait until sure it's me they are talking to
    { TASK_TLK_EYECONTACT,  (float)0        },// Wait until speaker is done
    { TASK_TLK_RESPOND,     (float)0        },// Wait and then say my response
    { TASK_TLK_IDEALYAW,    (float)0        },// look at who I'm talking to
    { TASK_FACE_IDEAL,      (float)0        },
    { TASK_SET_ACTIVITY,    (float)ACT_SIGNAL3  },
    { TASK_TLK_EYECONTACT,  (float)0        },// Wait until speaker is done
};

Schedule_t  slIdleResponse[] =
{
    {
        tlIdleResponse,
        ARRAYSIZE ( tlIdleResponse ),
        bits_COND_NEW_ENEMY     |
        bits_COND_LIGHT_DAMAGE  |
        bits_COND_HEAVY_DAMAGE,
        0,
        'Idle Response'

    },
};

//=========================================================

// Monster Think - calls out to core AI functions and handles this

// monster's specific animation events

//=========================================================

void CBaseMonster :: MonsterThink ( void )
{
    pev->nextthink = gpGlobals->time + 0.1;// keep monster thinking. 판단시간을 지연시켜준다.

    RunAI();

    float flInterval = StudioFrameAdvance( ); // animate
// start or end a fidget

// This needs a better home -- switching animations over time should be encapsulated on a per-activity basis

// perhaps MaintainActivity() or a ShiftAnimationOverTime() or something.

    if ( m_MonsterState != MONSTERSTATE_SCRIPT && m_MonsterState != MONSTERSTATE_DEAD && m_Activity == ACT_IDLE && m_fSequenceFinished )
    {
        int iSequence;

        if ( m_fSequenceLoops )
        {
            // animation does loop, which means we're playing subtle idle. Might need to
            // fidget.
            iSequence = LookupActivity ( m_Activity );
        }
        else
        {
            // animation that just ended doesn't loop! That means we just finished a fidget
            // and should return to our heaviest weighted idle (the subtle one)
            iSequence = LookupActivityHeaviest ( m_Activity );
        }
        if ( iSequence != ACTIVITY_NOT_AVAILABLE )
        {
            pev->sequence = iSequence;  // Set to new anim (if it's there)
            ResetSequenceInfo( );
        }
    }

    DispatchAnimEvents( flInterval );

    if ( !MovementIsComplete() )
    {
        Move( flInterval );
    }
#if _DEBUG
    else
    {
        if ( !TaskIsRunning() && !TaskIsComplete() )
            ALERT( at_error, 'Schedule stalled!!
' );
    }
#endif
}