1 // Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
2 // Copyright (C) 2010 Winch Gate Property Limited
4 // This source file has been modified by the following contributors:
5 // Copyright (C) 2020 Jan BOON (Kaetemi) <jan.boon@kaetemi.be>
7 // This program is free software: you can redistribute it and/or modify
8 // it under the terms of the GNU Affero General Public License as
9 // published by the Free Software Foundation, either version 3 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU Affero General Public License for more details.
17 // You should have received a copy of the GNU Affero General Public License
18 // along with this program. If not, see <http://www.gnu.org/licenses/>.
20 #ifndef RYAI_STATE_INSTANCE_H
21 #define RYAI_STATE_INSTANCE_H
23 #include "event_manager.h"
25 #include "debug_history.h"
26 #include "keyword_owner.h"
27 #include "event_manager.h"
28 #include "script_vm.h"
35 class CPersistentStateInstance
;
36 class CAliasTreeOwner
;
38 extern NLLIGO::CLigoConfig LigoConfig
;
40 //////////////////////////////////////////////////////////////////////////////
42 //////////////////////////////////////////////////////////////////////////////
45 : public AIVM::IScriptContext
49 CStateInstance(CAIState
* startState
);
51 void init(CAIState
* startState
);
53 virtual CPersistentStateInstance
* getPersistentStateInstance();
55 //////////////////////////////////////////////////////////////////////////
58 struct CStatePersistentObjEntry
60 CStatePersistentObjEntry();
61 CStatePersistentObjEntry(CAIState
const* state
, NLMISC::CSmartPtr
<NLMISC::CVirtualRefCount
> obj
);
62 virtual ~CStatePersistentObjEntry();
63 CAIState
const* _State
;
64 NLMISC::CSmartPtr
<NLMISC::CVirtualRefCount
> _Obj
;
66 typedef std::vector
<CStatePersistentObjEntry
> TStatePersistentObjList
;
68 TStatePersistentObjList _StatePersistentObjList
;
70 // Made to allow obj with life time less or equal to state affectation life time.
71 void addStatePersistentObj(CAIState
const* keyState
, NLMISC::CSmartPtr
<NLMISC::CVirtualRefCount
> anyObj
);
72 void removeExceptForState(CAIState
const* keyState
);
74 /** Try to obtain a group interface from the CStateInstance. Can return NULL if the
75 * CStateInstance if not implemeted by a group related objet.
78 virtual CGroup
* getGroup() = 0;
80 //////////////////////////////////////////////////////////////////////////
82 CAITimerExtended
& timerStateTimeout() { return _StateTimeout
; }
83 CAITimerExtended
& timerPunctTimeout() { return _PunctualStateTimeout
; }
84 CAITimerExtended
& timerUser(uint idx
);
86 CAIState
* getCAIState();
88 virtual CAliasTreeOwner
* aliasTreeOwner() = 0;
90 virtual void stateChange(CAIState
const* oldState
, CAIState
const* newState
) = 0;
92 CAIState
* getState() const { return _state
; }
93 void setNextState(CAIState
*);
95 CAIState
* getPunctualState() const { return _PunctualState
; }
96 CAIState
* getNextPunctualState() const { return _NextPunctualState
; }
97 void setNextPunctualState(CAIState
* state
);
98 void cancelPunctualState() { _CancelPunctualState
= true; }
100 std::string
buidStateInstanceDebugString() const;
102 void dumpVarsAndFunctions(CStringWriter
& sw
) const;
104 virtual CDebugHistory
* getDebugHistory () = 0;
106 CAIState
const* getActiveState() const;
108 sint32
getUserTimer(uint timerId
);
109 void setUserTimer(uint timerId
, sint32 time
);
111 void logicVarsToString(std::string
& str
) const;
113 float getNelVar(std::string
const& varId
);
114 void setNelVar(std::string
const& varId
, float value
);
115 void delNelVar(std::string
const& varId
);
117 std::string
getStrNelVar(std::string
const& varId
);
118 void setStrNelVar(std::string
const& varId
, std::string
const& value
);
119 void delStrNelVar(std::string
const& varId
);
121 static void setGlobalNelVar(std::string
const& varId
, float value
);
122 static void setGlobalNelVar(std::string
const& varId
, std::string value
);
124 CAITimerExtended
const& userTimer (uint32 index
) const;
126 bool advanceUserTimer(uint32 nbTicks
);
128 void processStateEvent(CAIEvent
const& stateEvent
, CAIState
const* state
= NULL
);
130 // callerStateInstance could be NULL;
131 void interpretCode(AIVM::IScriptContext
* callerStateInstance
, AIVM::CByteCodeEntry
const& codeScriptEntry
);
132 void interpretCode(AIVM::IScriptContext
* callerStateInstance
, NLMISC::CSmartPtr
<AIVM::CByteCode
const> const& codeScript
);
134 /// @name IScriptContext implementation
136 virtual std::string
getContextName();
137 virtual void interpretCodeOnChildren(AIVM::CByteCodeEntry
const& codeScriptEntry
);
139 float getLogicVar(NLMISC::TStringId varId
);
140 void setLogicVar(NLMISC::TStringId varId
, float value
);
141 std::string
getStrLogicVar(NLMISC::TStringId varId
);
142 void setStrLogicVar(NLMISC::TStringId varId
, std::string
const& value
);
143 AIVM::IScriptContext
* getCtxLogicVar(NLMISC::TStringId varId
);
144 void setCtxLogicVar(NLMISC::TStringId varId
, AIVM::IScriptContext
* value
);
145 void setFirstBotSpawned();
147 virtual AIVM::IScriptContext
* findContext(NLMISC::TStringId
const strId
);
149 virtual void setScriptCallBack(NLMISC::TStringId
const& eventName
, AIVM::CByteCodeEntry
const& codeScriptEntry
);
150 virtual AIVM::CByteCodeEntry
const* getScriptCallBackPtr(NLMISC::TStringId
const& eventName
) const;
151 virtual void callScriptCallBack(AIVM::IScriptContext
* caller
, NLMISC::TStringId
const& funcName
, int mode
= 0, std::string
const& inParamsSig
= "", std::string
const& outParamsSig
= "", AIVM::CScriptStack
* stack
= NULL
);
152 virtual void callNativeCallBack(AIVM::IScriptContext
* caller
, std::string
const& funcName
, int mode
= 0, std::string
const& inParamsSig
= "", std::string
const& outParamsSig
= "", AIVM::CScriptStack
* stack
= NULL
);
154 void blockUserEvent(uint32 eventId
);
155 void unblockUserEvent(uint32 eventId
);
156 bool isUserEventBlocked(uint32 eventId
) const;
161 typedef std::map
<NLMISC::TStringId
, float> TLogicVarList
;
162 typedef std::map
<NLMISC::TStringId
, std::string
> TStrLogicVarList
;
163 typedef std::map
<NLMISC::TStringId
, uint32
> TLogicVarIndex
;
164 typedef std::map
<NLMISC::TStringId
, AIVM::IScriptContext
*> TCtxLogicVarList
;
165 TLogicVarList _LogicVar
;
166 TStrLogicVarList _StrLogicVar
;
167 TCtxLogicVarList _CtxLogicVar
;
170 typedef std::map
<std::string
, NLMISC::CVariable
<float>*> TNelVarList
;
171 typedef std::map
<std::string
, NLMISC::CVariable
<std::string
>*> TStrNelVarList
;
173 TStrNelVarList _StrNelVar
;
176 typedef std::map
<NLMISC::TStringId
, AIVM::CByteCodeEntry
> TCallBackList
;
177 TCallBackList _CallBacks
;
179 /// Flag for variable modification
180 bool _LogicVarChanged
;
181 bool _LogicVarChangedList
[4];
182 //TLogicVarIndex _VarIndex;
183 // update logic timers ---------------------------------------------
184 /// 4 timers available for user logic
185 CAITimerExtended _UserTimer
[4];
187 /// timer for timing positional states
188 CAITimerExtended _StateTimeout
;
189 /// current state (index into manager's state vector)
191 /// variable set to request a state change (std::numeric_limits<uint32>::max() otherwise)
192 CAIState
* _NextState
;
194 /// timer for timing punctual states
195 CAITimerExtended _PunctualStateTimeout
;
197 CAIState
* _PunctualState
;
198 CAIState
* _NextPunctualState
;
200 /// Flag for leaving the punctual state, returning to normal behavior
201 bool _CancelPunctualState
;
202 bool _FirstBotSpawned
;
204 uint32 _UserEventBlocked
;
207 //////////////////////////////////////////////////////////////////////////////
208 // CPersistentStateInstance //
209 //////////////////////////////////////////////////////////////////////////////
211 class CPersistentStateInstance
212 : public NLMISC::CDbgRefCount
<CPersistentStateInstance
>
213 , public CKeyWordOwner
214 , public CStateInstance
217 CPersistentStateInstance(CStateMachine
& reactionContainer
);
218 virtual ~CPersistentStateInstance();
220 typedef std::vector
<NLMISC::CDbgPtr
<CPersistentStateInstance
> > TChildList
;
222 void setParentStateInstance(CPersistentStateInstance
* parentStateInstance
);
224 CPersistentStateInstance
* getParentStateInstance() const { return _ParentStateInstance
; }
226 void addChildStateInstance(CPersistentStateInstance
* parentStateInstance
);
228 void removeChildStateInstance(CPersistentStateInstance
* parentStateInstance
);
230 TChildList
& childs() { return _PSIChilds
; }
232 TChildList _PSIChilds
;
234 // Interface to state status variables -----------------------------
235 CAIState
* getStartState() { return _StartState
; }
236 void setStartState(CAIState
* state
);
238 CStateMachine
& getEventContainer() { return _Container
; }
240 void updateStateInstance();
242 //////////////////////////////////////////////////////////////////////////
244 CPersistentStateInstance
* getPersistentStateInstance() { return this; }
246 //////////////////////////////////////////////////////////////////////////
249 /// id of the state to use at startup
250 NLMISC::CDbgPtr
<CAIState
> _StartState
;
251 CStateMachine
& _Container
;
252 NLMISC::CDbgPtr
<CPersistentStateInstance
> _ParentStateInstance
;
255 /****************************************************************************/
256 /* Inlined methods */
257 /****************************************************************************/
259 //////////////////////////////////////////////////////////////////////////////
261 //////////////////////////////////////////////////////////////////////////////
264 CStateInstance::CStateInstance(CAIState
* startState
)
266 _UserEventBlocked
= 0;
267 _CtxLogicVar
[NLMISC::CStringMapper::map("@this")] = this;
272 CPersistentStateInstance
* CStateInstance::getPersistentStateInstance()
275 return (CPersistentStateInstance
*)NULL
;
279 CStateInstance::CStatePersistentObjEntry::CStatePersistentObjEntry()
284 CStateInstance::CStatePersistentObjEntry::CStatePersistentObjEntry(CAIState
const* state
, NLMISC::CSmartPtr
<NLMISC::CVirtualRefCount
> obj
)
291 CStateInstance::CStatePersistentObjEntry::~CStatePersistentObjEntry()
298 CAITimerExtended
& CStateInstance::timerUser(uint idx
)
300 nlassert(idx
<4); return _UserTimer
[idx
];
304 void CStateInstance::logicVarsToString(std::string
& str
) const
306 for (TLogicVarList::const_iterator it
=_LogicVar
.begin(), itEnd
=_LogicVar
.end();it
!=itEnd
;++it
)
307 str
+=*(it
->first
)+"="+NLMISC::toString(it
->second
)+" ";
311 float CStateInstance::getLogicVar(NLMISC::TStringId varId
)
313 TLogicVarList::iterator it
=_LogicVar
.find(varId
);
314 if (it
==_LogicVar
.end())
316 _LogicVar
[varId
]=0.f
;
319 return it
->second
; //_LogicVar[varId];
323 void CStateInstance::setLogicVar(NLMISC::TStringId varId
, float value
)
325 _LogicVar
[varId
] = value
;
326 _LogicVarChanged
= true;
328 if (varId
&& varId
->size() == 2 && (*varId
)[0] == 'v')
330 sint32 index
= (*varId
)[1] - '0';
331 if (0 <= index
&& index
< 4)
334 _LogicVarChangedList
[static_cast<uint32
>(index
)] = true;
342 std::string
CStateInstance::getStrLogicVar(NLMISC::TStringId varId
)
344 return _StrLogicVar
[varId
];
348 void CStateInstance::setStrLogicVar(NLMISC::TStringId varId
, std::string
const& value
)
350 _StrLogicVar
[varId
] = value
;
351 _LogicVarChanged
= true;
352 if (varId
&& varId
->size() == 2 && (*varId
)[0] == 'v')
354 sint32 index
= (*varId
)[1] - '0';
355 if (0 <= index
&& index
< 4)
358 _LogicVarChangedList
[static_cast<uint32
>(index
)] = true;
362 //_LogicVarChangedList[_VarIndex[varId]] = true;
366 AIVM::IScriptContext
* CStateInstance::getCtxLogicVar(NLMISC::TStringId varId
)
368 return _CtxLogicVar
[varId
];
372 void CStateInstance::setCtxLogicVar(NLMISC::TStringId varId
, AIVM::IScriptContext
* value
)
374 _CtxLogicVar
[varId
] = value
;
375 _LogicVarChanged
= true;
379 float CStateInstance::getNelVar(std::string
const& varId
)
381 TNelVarList::iterator it
= _NelVar
.find(varId
);
382 if (it
!= _NelVar
.end()) return it
->second
->get();
384 if (NLMISC::CVariable
<float>::exists(varId
))
386 nlwarning("Nel variable \"%s\" exists outside of this state instance", varId
.c_str());
390 _NelVar
[varId
] = new NLMISC::CVariable
<float>("StateInstance", varId
.c_str(), "", 0.f
);
391 return _NelVar
[varId
]->get();
395 void CStateInstance::setNelVar(std::string
const& varId
, float value
)
397 TNelVarList::iterator it
= _NelVar
.find(varId
);
398 if (it
==_NelVar
.end())
400 if (NLMISC::CVariable
<float>::exists(varId
))
402 nlwarning("Nel variable \"%s\" exists outside of this state instance", varId
.c_str());
405 _NelVar
[varId
] = new NLMISC::CVariable
<float>("StateInstance", varId
.c_str(), "", 0.f
);
407 _NelVar
[varId
]->set(value
);
411 void CStateInstance::delNelVar(std::string
const& varId
)
413 TNelVarList::iterator it
= _NelVar
.find(varId
);
414 if (it
!=_NelVar
.end())
421 nldebug("Trying to delete Nel variable that doesn't exist: \"%s\"", varId
.c_str());
426 std::string
CStateInstance::getStrNelVar(std::string
const& varId
)
428 TStrNelVarList::iterator it
= _StrNelVar
.find(varId
);
429 if (it
!= _StrNelVar
.end()) return it
->second
->get();
431 if (NLMISC::CVariable
<float>::exists(varId
))
433 nlwarning("Nel variable \"%s\" exists outside of this state instance", varId
.c_str());
437 _StrNelVar
[varId
] = new NLMISC::CVariable
<std::string
>("StateInstanceStrVar", varId
.c_str(), "", std::string());
438 return _StrNelVar
[varId
]->get();
442 void CStateInstance::setStrNelVar(std::string
const& varId
, std::string
const& value
)
444 TStrNelVarList::iterator it
= _StrNelVar
.find(varId
);
445 if (it
==_StrNelVar
.end())
447 if (NLMISC::CVariable
<float>::exists(varId
))
449 nlwarning("Nel variable \"%s\" exists outside of this state instance", varId
.c_str());
452 _StrNelVar
[varId
] = new NLMISC::CVariable
<std::string
>("StateInstanceStrVar", varId
.c_str(), "", std::string());
454 _StrNelVar
[varId
]->set(value
);
458 void CStateInstance::delStrNelVar(std::string
const& varId
)
460 TStrNelVarList::iterator it
= _StrNelVar
.find(varId
);
461 if (it
!=_StrNelVar
.end())
464 _StrNelVar
.erase(it
);
468 nldebug("Trying to delete Nel variable that doesn't exist: \"%s\"", varId
.c_str());
473 CAITimerExtended
const& CStateInstance::userTimer (uint32 index
) const
475 return _UserTimer
[index
];
479 bool CStateInstance::advanceUserTimer(uint32 nbTicks
)
481 for (uint k
=0; k
<4; ++k
)
483 const uint32 t
= getUserTimer(k
);
484 setUserTimer(k
, (t
>nbTicks
)?(t
-nbTicks
):0);
490 void CStateInstance::processStateEvent(CAIEvent
const& stateEvent
, CAIState
const* state
)
492 // NOTE: This is a quick and inefficient implementation of event treatment - needs to be re-worked
493 // note that it is OK for state to be 'NULL'
496 state
=getActiveState();
501 bool foundReaction
=false;
503 for (uint i
=0;i
<stateEvent
.reactionList().size();++i
)
505 const CAIEventReaction
&reaction
=*stateEvent
.reactionList()[i
];
506 if (!reaction
.testCompatibility(this,state
))
509 if (getDebugHistory()->isRecording())
511 nldebug("Reaction for event"); // FIXME: https://github.com/kaetemi/ryzomclassic/issues/152
512 nldebug("STATE: '%s' EVENT: '%s' REACTION: '%s'", state
->getAliasNode()->fullName().c_str(),
513 stateEvent
.getName().c_str(), reaction
.getAliasNode()->fullName().c_str()); // FIXME: https://github.com/kaetemi/ryzomclassic/issues/152
514 getDebugHistory()->addHistory("STATE: '%s' EVENT: '%s' REACTION: '%s'", state
->getAliasNode()->fullName().c_str(),
515 stateEvent
.getName().c_str(), reaction
.getAliasNode()->fullName().c_str());
520 if (!reaction
.getAction())
522 nldebug("Failed to find action for event"); // FIXME: https://github.com/kaetemi/ryzomclassic/issues/152
523 nlwarning("Failed to find action for event: %s",reaction
.getAliasNode()->fullName().c_str());
526 if (!reaction
.getAction()->executeAction(this, NULL
))
528 nldebug("Failed to execute action for event"); // FIXME: https://github.com/kaetemi/ryzomclassic/issues/152
529 nlwarning("Failed to execute action for event '%s': for stateInstance:'%s' in state:'%s'", stateEvent
.getName().c_str(),
530 aliasTreeOwner()->getAliasNode()->fullName().c_str(), state
->getAliasNode()->fullName().c_str());
537 if (getDebugHistory()->isRecording())
539 nldebug("No reaction for event"); // FIXME: https://github.com/kaetemi/ryzomclassic/issues/152
540 nldebug("STATE: '%s' EVENT: '%s' NO REACTION", state
->getAliasNode()->fullName().c_str(),
541 stateEvent
.getName().c_str()); // FIXME: https://github.com/kaetemi/ryzomclassic/issues/152
542 getDebugHistory()->addHistory("STATE: '%s' EVENT: '%s' NO REACTION", state
->getAliasNode()->fullName().c_str(),
543 stateEvent
.getName().c_str());
550 void CStateInstance::setNextState(CAIState
* state
)
552 // we're allowed to set state to 'no state'
559 // make sure the state is positional (not punctual)
560 if (!state
->isPositional())
562 nlwarning("setNextState(): State should not be punctual '%s'%s - setting state to std::numeric_limits<uint32>::max()",
563 state
->getAliasNode()->fullName().c_str(),
564 state
->getAliasString().c_str());
569 // set the next state
574 void CStateInstance::setNextPunctualState(CAIState
* state
)
576 // we're allowed to set state to 'no state'
580 // make sure the state is not positional (ie punctual)
581 if (state
->isPositional())
583 nlwarning("CStateInstance::setNextPunctualState(): State should be punctual '%s'%s - setting state to std::numeric_limits<uint32>::max()",
584 state
->getAliasNode()->fullName().c_str(),
585 state
->getAliasString().c_str());
590 // set the next state
591 _NextPunctualState
= state
;
594 //////////////////////////////////////////////////////////////////////////////
595 // CPersistentStateInstance //
596 //////////////////////////////////////////////////////////////////////////////
599 CPersistentStateInstance::CPersistentStateInstance(CStateMachine
& reactionContainer
)
601 , CStateInstance(NULL
)
603 , _Container(reactionContainer
)
608 CPersistentStateInstance::~CPersistentStateInstance()
611 if (_PSIChilds
.size()!=0)
612 nlwarning("a Npc group is dying and still have childs, which is not possible !!");
613 nlassert(_PSIChilds
.size()==0);
615 if (!_ParentStateInstance
.isNULL())
616 _ParentStateInstance
->removeChildStateInstance(this);
620 void CPersistentStateInstance::setParentStateInstance(CPersistentStateInstance
* parentStateInstance
)
622 if (!_ParentStateInstance
.isNULL())
623 _ParentStateInstance
->removeChildStateInstance(this);
624 _ParentStateInstance
=parentStateInstance
;
625 if (parentStateInstance
!=NULL
)
626 parentStateInstance
->addChildStateInstance(this);
630 void CPersistentStateInstance::addChildStateInstance(CPersistentStateInstance
* parentStateInstance
)
633 nlassert(std::find(_PSIChilds
.begin(), _PSIChilds
.end(), NLMISC::CDbgPtr
<CPersistentStateInstance
>(parentStateInstance
))==_PSIChilds
.end());
635 _PSIChilds
.push_back(parentStateInstance
);
639 void CPersistentStateInstance::removeChildStateInstance(CPersistentStateInstance
* parentStateInstance
)
641 TChildList::iterator it
= std::find(_PSIChilds
.begin(), _PSIChilds
.end(), NLMISC::CDbgPtr
<CPersistentStateInstance
>(parentStateInstance
));
643 nlassert(it
!=_PSIChilds
.end());
645 if (it
!=_PSIChilds
.end())
646 _PSIChilds
.erase(it
);
650 void CPersistentStateInstance::setStartState(CAIState
* state
)
653 CStateInstance::init(_StartState
);
656 #include "event_reaction_include.h"