1 /* Copyright (C) 2023 Wildfire Games.
2 * This file is part of 0 A.D.
4 * 0 A.D. is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
9 * 0 A.D. is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
18 #include "precompiled.h"
22 CFsmEvent::CFsmEvent(unsigned int type
)
28 CFsmEvent::~CFsmEvent()
33 void CFsmEvent::SetParamRef(void* pParam
)
38 CFsmTransition::CFsmTransition(unsigned int state
)
43 CFsmTransition::~CFsmTransition()
49 void CFsmTransition::RegisterAction(void* pAction
, void* pContext
)
51 CallbackFunction callback
;
53 // Add action at the end of actions list
54 callback
.pFunction
= pAction
;
55 callback
.pContext
= pContext
;
57 m_Actions
.push_back(callback
);
60 void CFsmTransition::RegisterCondition(void* pCondition
, void* pContext
)
62 CallbackFunction callback
;
64 // Add condition at the end of conditions list
65 callback
.pFunction
= pCondition
;
66 callback
.pContext
= pContext
;
68 m_Conditions
.push_back(callback
);
71 void CFsmTransition::SetEvent(CFsmEvent
* pEvent
)
76 void CFsmTransition::SetNextState(unsigned int nextState
)
78 m_NextState
= nextState
;
81 bool CFsmTransition::ApplyConditions() const
85 CallbackList::const_iterator it
= m_Conditions
.begin();
86 for (; it
!= m_Conditions
.end(); ++it
)
91 Condition
* condition
= reinterpret_cast<Condition
*>(it
->pFunction
);
92 eval
&= condition(it
->pContext
);
99 bool CFsmTransition::RunActions() const
103 CallbackList::const_iterator it
= m_Actions
.begin();
104 for (; it
!= m_Actions
.end(); ++it
)
109 Action
* action
= reinterpret_cast<Action
*>(it
->pFunction
);
110 result
&= action(it
->pContext
, m_Event
);
120 m_FirstState
= FSM_INVALID_STATE
;
121 m_CurrState
= FSM_INVALID_STATE
;
122 m_NextState
= FSM_INVALID_STATE
;
132 // Does nothing by default
135 void CFsm::Shutdown()
137 // Release transitions
138 TransitionList::iterator itTransition
= m_Transitions
.begin();
139 for (; itTransition
< m_Transitions
.end(); ++itTransition
)
140 delete *itTransition
;
143 EventMap::iterator itEvent
= m_Events
.begin();
144 for (; itEvent
!= m_Events
.end(); ++itEvent
)
145 delete itEvent
->second
;
149 m_Transitions
.clear();
152 m_FirstState
= FSM_INVALID_STATE
;
153 m_CurrState
= FSM_INVALID_STATE
;
154 m_NextState
= FSM_INVALID_STATE
;
157 void CFsm::AddState(unsigned int state
)
159 m_States
.insert(state
);
162 CFsmEvent
* CFsm::AddEvent(unsigned int eventType
)
164 CFsmEvent
* pEvent
= nullptr;
166 // Lookup event by type
167 EventMap::iterator it
= m_Events
.find(eventType
);
168 if (it
!= m_Events
.end())
174 pEvent
= new CFsmEvent(eventType
);
176 // Store new event into internal map
177 m_Events
[eventType
] = pEvent
;
183 CFsmTransition
* CFsm::AddTransition(unsigned int state
, unsigned int eventType
, unsigned int nextState
)
185 // Make sure we store the current state
188 // Make sure we store the next state
191 // Make sure we store the event
192 CFsmEvent
* pEvent
= AddEvent(eventType
);
196 // Create new transition
197 CFsmTransition
* pNewTransition
= new CFsmTransition(state
);
199 // Setup new transition
200 pNewTransition
->SetEvent(pEvent
);
201 pNewTransition
->SetNextState(nextState
);
203 // Store new transition
204 m_Transitions
.push_back(pNewTransition
);
206 return pNewTransition
;
209 CFsmTransition
* CFsm::AddTransition(unsigned int state
, unsigned int eventType
, unsigned int nextState
,
210 void* pAction
, void* pContext
)
212 CFsmTransition
* pTransition
= AddTransition(state
, eventType
, nextState
);
216 // If action specified, register it
218 pTransition
->RegisterAction(pAction
, pContext
);
223 CFsmTransition
* CFsm::GetTransition(unsigned int state
, unsigned int eventType
) const
225 if (!IsValidState(state
))
228 if (!IsValidEvent(eventType
))
231 TransitionList::const_iterator it
= m_Transitions
.begin();
232 for (; it
!= m_Transitions
.end(); ++it
)
234 CFsmTransition
* pCurrTransition
= *it
;
235 if (!pCurrTransition
)
238 CFsmEvent
* pCurrEvent
= pCurrTransition
->GetEvent();
242 // Is it our transition?
243 if (pCurrTransition
->GetCurrState() == state
&& pCurrEvent
->GetType() == eventType
)
244 return pCurrTransition
;
247 // No transition found
251 void CFsm::SetFirstState(unsigned int firstState
)
253 m_FirstState
= firstState
;
256 void CFsm::SetCurrState(unsigned int state
)
261 bool CFsm::IsFirstTime() const
263 return (m_CurrState
== FSM_INVALID_STATE
);
266 bool CFsm::Update(unsigned int eventType
, void* pEventParam
)
268 if (!IsValidEvent(eventType
))
272 m_CurrState
= m_FirstState
;
275 CFsmTransition
* pTransition
= GetTransition(m_CurrState
, eventType
);
279 // Setup event parameter
280 EventMap::iterator it
= m_Events
.find(eventType
);
281 if (it
!= m_Events
.end())
283 CFsmEvent
* pEvent
= it
->second
;
285 pEvent
->SetParamRef(pEventParam
);
289 if (!pTransition
->ApplyConditions())
292 // Save the default state transition (actions might call SetNextState
294 SetNextState(pTransition
->GetNextState());
296 if (!pTransition
->RunActions())
299 SetCurrState(GetNextState());
301 // Reset the next state since it's no longer valid
302 SetNextState(FSM_INVALID_STATE
);
307 bool CFsm::IsDone() const
309 // By default the internal flag m_Done is tested
313 bool CFsm::IsValidState(unsigned int state
) const
315 StateSet::const_iterator it
= m_States
.find(state
);
316 if (it
== m_States
.end())
322 bool CFsm::IsValidEvent(unsigned int eventType
) const
324 EventMap::const_iterator it
= m_Events
.find(eventType
);
325 if (it
== m_Events
.end())