State Machine 기반의 로직을 작성할 때

정답이라고 할 수는 없지만 개인적인 생각에는

class StateMachine {
  std::list<StatePtr> stateStack;
public:
  StateMachine(State* baseState) {
    stateStack.push_back(baseState);
  }

  void Push(StatePtr newState) {
    stateStack.front()->Pause();
    stateStack.push_front(newState);
    stateStack.front()->Start();
  }

  void Change(StatePtr newState) {
    if (stateStack.size() <= 1)
      return Push(newState);
    stateStack.front()->Stop();
    stateStack.pop_front();
    stateStack.push_front(newState);
    stateStack.front()->Start();
  }

  void Pop() {
    if (stateStack.size() <= 1)
      return;
    stateStack.front()->Stop();
    stateStack.pop_front();
    stateStack.front()->Resume();
  }

  void Reset() {
    while (stateStack.size() > 1) {
      stateStack.front()->Stop();
      stateStack.pop_front();
    }
  }

  void SendMessage(Message& msg) {
    for (auto& state : stateStack) {
      if (state->OnMessage(msg))
        return;
    }
  }
};

위와 같이 스택 구조로 만들면… 이전 상태가 기록되어 좋다고 생각합니다. 뭐 이런 느낌???

// 선공, 비선공으로 NPC AI 를 시작
aiStateMachine.Push(offensive ? new OffensiveState(this) : new PeacefulState(this));
// 어떤 조건으로 인해 전투 상태로 변경
aiStateMachine.Push(new CombatState(this));
// 다른 조건으로 NPC가 스킬을 시전
aiStateMachine.Push(new UseSkillState(this));

// 스킬 사용 상태
class UseSkillState : public State {
public:
  virtual bool OnMessage(Message& msg) override {
    switch (msg.GetType()) {
    case MessageType::GET_TARGET:
      // 이전 상태에서 구한다
      return false;
    // ...
    }
  }

  virtual void Start() override {
    // 대상을 구한다
    GetTargetMessage msg;
    owner->GetStateMachine().SendMessage(msg);
    if (!msg.HasTarget()) return owner->GetStateMachine().Pop(); // 대상을 못 구했으므로 종료
    // 대상에게 스킬 시전
    // ...
  }
}