#include "Runtime/World/CStateMachine.hpp" #include "Runtime/CStateManager.hpp" #include "Runtime/World/CAi.hpp" #include namespace metaforce { namespace { logvisor::Module Log("metaforce::CStateMachine"); } CStateMachine::CStateMachine(CInputStream& in) { CAiTrigger* lastTrig = nullptr; u32 stateCount = in.ReadLong(); x0_states.reserve(stateCount); for (u32 i = 0; i < stateCount; ++i) { std::string name; while (name.size() < 31) { const auto chr = in.ReadChar(); name += chr; if (chr == '\0') { break; } } CAiStateFunc func = CAi::GetStateFunc(name); x0_states.emplace_back(func, name.c_str()); } x10_triggers.reserve(in.ReadLong()); for (u32 i = 0; i < stateCount; ++i) { x0_states[i].SetNumTriggers(in.ReadLong()); if (x0_states[i].GetNumTriggers() == 0) continue; CAiTrigger* firstTrig = x10_triggers.data() + x10_triggers.size(); x0_states[i].SetTriggers(firstTrig); x10_triggers.resize(x10_triggers.size() + x0_states[i].GetNumTriggers()); for (s32 j = 0; j < x0_states[i].GetNumTriggers(); ++j) { const u32 triggerCount = in.ReadLong(); const u32 lastTriggerIdx = triggerCount - 1; for (u32 k = 0; k < triggerCount; ++k) { std::string name; while (name.size() < 31) { const auto chr = in.ReadChar(); name += chr; if (chr == '\0') { break; } } const bool isNot = name.front() == '!'; const CAiTriggerFunc func = CAi::GetTriggerFunc(isNot ? name.c_str() + 1 : name.c_str()); const float arg = in.ReadFloat(); CAiTrigger* newTrig; if (k < lastTriggerIdx) { newTrig = &x10_triggers.emplace_back(); } else { newTrig = &firstTrig[j]; } if (k == 0) newTrig->Setup(func, isNot, arg, &x0_states[in.ReadLong()]); else newTrig->Setup(func, isNot, arg, lastTrig); lastTrig = newTrig; } } } } s32 CStateMachine::GetStateIndex(std::string_view state) const { auto it = std::find_if(x0_states.begin(), x0_states.end(), [state](const CAiState& st) { return (strncmp(st.GetName(), state.data(), 31) == 0); }); if (it == x0_states.end()) return 0; return it - x0_states.begin(); } void CStateMachineState::Update(CStateManager& mgr, CAi& ai, float delta) { if (x4_state) { x8_time += delta; x4_state->CallFunc(mgr, ai, EStateMsg::Update, delta); for (int i = 0; i < x4_state->GetNumTriggers(); ++i) { CAiTrigger* trig = x4_state->GetTrig(i); CAiState* state = nullptr; bool andPassed = true; while (andPassed && trig) { andPassed = false; if (trig->CallFunc(mgr, ai)) { andPassed = true; state = trig->GetState(); trig = trig->GetAnd(); } } if (andPassed && state != nullptr) { x4_state->CallFunc(mgr, ai, EStateMsg::Deactivate, 0.f); x4_state = state; Log.report(logvisor::Info, FMT_STRING("{} {} {} - {} {}"), ai.GetUniqueId(), ai.GetEditorId(), ai.GetName(), state->xc_name, int(state - x0_machine->GetStateVector().data())); x8_time = 0.f; x18_24_codeTrigger = false; xc_random = mgr.GetActiveRandom()->Float(); x4_state->CallFunc(mgr, ai, EStateMsg::Activate, delta); return; } } } } void CStateMachineState::SetState(CStateManager& mgr, CAi& ai, s32 idx) { CAiState* state = const_cast(&x0_machine->GetStateVector()[idx]); if (x4_state != state) { if (x4_state) x4_state->CallFunc(mgr, ai, EStateMsg::Deactivate, 0.f); x4_state = state; x8_time = 0.f; xc_random = mgr.GetActiveRandom()->Float(); x18_24_codeTrigger = false; x4_state->CallFunc(mgr, ai, EStateMsg::Activate, 0.f); } } void CStateMachineState::SetState(CStateManager& mgr, CAi& ai, const CStateMachine* machine, std::string_view state) { if (!machine) return; if (!x0_machine) Setup(machine); s32 idx = machine->GetStateIndex(state); SetState(mgr, ai, idx); } const std::vector* CStateMachineState::GetStateVector() const { if (!x0_machine) return nullptr; return &x0_machine->GetStateVector(); } void CStateMachineState::Setup(const CStateMachine* machine) { x0_machine = machine; x4_state = nullptr; x8_time = 0.f; xc_random = 0.f; x10_delay = 0.f; } CFactoryFnReturn FAiFiniteStateMachineFactory(const SObjectTag& tag, CInputStream& in, const CVParamTransfer& vparms, CObjectReference*) { return TToken::GetIObjObjectFor(std::make_unique(in)); } } // namespace metaforce