// Ryzom - MMORPG Framework
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see .
#ifndef RYAI_STATE_INSTANCE_H
#define RYAI_STATE_INSTANCE_H
#include "event_manager.h"
#include "timer.h"
#include "debug_history.h"
#include "keyword_owner.h"
#include "event_manager.h"
#include "script_vm.h"
#include "ai_grp.h"
class CStateMachine;
class CGroup;
class CAIState;
class CPersistentStateInstance;
class CAliasTreeOwner;
extern NLLIGO::CLigoConfig LigoConfig;
//////////////////////////////////////////////////////////////////////////////
// CStateInstance //
//////////////////////////////////////////////////////////////////////////////
class CStateInstance
: public AIVM::IScriptContext
{
public:
inline
CStateInstance(CAIState* startState);
void init(CAIState* startState);
virtual CPersistentStateInstance* getPersistentStateInstance();
//////////////////////////////////////////////////////////////////////////
// State Persistent.
struct CStatePersistentObjEntry
{
CStatePersistentObjEntry();
CStatePersistentObjEntry(CAIState const* state, NLMISC::CSmartPtr obj);
virtual ~CStatePersistentObjEntry();
CAIState const* _State;
NLMISC::CSmartPtr _Obj;
};
typedef std::vector TStatePersistentObjList;
TStatePersistentObjList _StatePersistentObjList;
// Made to allow obj with life time less or equal to state affectation life time.
void addStatePersistentObj(CAIState const* keyState, NLMISC::CSmartPtr anyObj);
void removeExceptForState(CAIState const* keyState);
/** Try to obtain a group interface from the CStateInstance. Can return NULL if the
* CStateInstance if not implemeted by a group related objet.
*/
// Bad, Bad, Bad ..
virtual CGroup* getGroup() = 0;
//////////////////////////////////////////////////////////////////////////
CAITimerExtended& timerStateTimeout() { return _StateTimeout; }
CAITimerExtended& timerPunctTimeout() { return _PunctualStateTimeout; }
CAITimerExtended& timerUser(uint idx);
CAIState* getCAIState();
virtual CAliasTreeOwner* aliasTreeOwner() = 0;
virtual void stateChange(CAIState const* oldState, CAIState const* newState) = 0;
CAIState* getState() const { return _state; }
void setNextState(CAIState*);
CAIState* getPunctualState() const { return _PunctualState; }
CAIState* getNextPunctualState() const { return _NextPunctualState; }
void setNextPunctualState(CAIState* state);
void cancelPunctualState() { _CancelPunctualState = true; }
std::string buidStateInstanceDebugString() const;
void dumpVarsAndFunctions(CStringWriter& sw) const;
virtual CDebugHistory* getDebugHistory () = 0;
CAIState const* getActiveState() const;
sint32 getUserTimer(uint timerId);
void setUserTimer(uint timerId, sint32 time);
void logicVarsToString(std::string& str) const;
float getNelVar(std::string const& varId);
void setNelVar(std::string const& varId, float value);
void delNelVar(std::string const& varId);
std::string getStrNelVar(std::string const& varId);
void setStrNelVar(std::string const& varId, std::string const& value);
void delStrNelVar(std::string const& varId);
static void setGlobalNelVar(std::string const& varId, float value);
static void setGlobalNelVar(std::string const& varId, std::string value);
CAITimerExtended const& userTimer (uint32 index) const;
bool advanceUserTimer(uint32 nbTicks);
void processStateEvent(CAIEvent const& stateEvent, CAIState const* state = NULL);
// callerStateInstance could be NULL;
void interpretCode(AIVM::IScriptContext* callerStateInstance, AIVM::CByteCodeEntry const& codeScriptEntry);
void interpretCode(AIVM::IScriptContext* callerStateInstance, NLMISC::CSmartPtr const& codeScript);
/// @name IScriptContext implementation
//@{
virtual std::string getContextName();
virtual void interpretCodeOnChildren(AIVM::CByteCodeEntry const& codeScriptEntry);
float getLogicVar(NLMISC::TStringId varId);
void setLogicVar(NLMISC::TStringId varId, float value);
std::string getStrLogicVar(NLMISC::TStringId varId);
void setStrLogicVar(NLMISC::TStringId varId, std::string const& value);
AIVM::IScriptContext* getCtxLogicVar(NLMISC::TStringId varId);
void setCtxLogicVar(NLMISC::TStringId varId, AIVM::IScriptContext* value);
void setFirstBotSpawned();
virtual AIVM::IScriptContext* findContext(NLMISC::TStringId const strId);
virtual void setScriptCallBack(NLMISC::TStringId const& eventName, AIVM::CByteCodeEntry const& codeScriptEntry);
virtual AIVM::CByteCodeEntry const* getScriptCallBackPtr(NLMISC::TStringId const& eventName) const;
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);
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);
void blockUserEvent(uint32 eventId);
void unblockUserEvent(uint32 eventId);
bool isUserEventBlocked(uint32 eventId) const;
//@}
protected:
/// Logic variables
typedef std::map TLogicVarList;
typedef std::map TStrLogicVarList;
typedef std::map TLogicVarIndex;
typedef std::map TCtxLogicVarList;
TLogicVarList _LogicVar;
TStrLogicVarList _StrLogicVar;
TCtxLogicVarList _CtxLogicVar;
// Nel variables
typedef std::map*> TNelVarList;
typedef std::map*> TStrNelVarList;
TNelVarList _NelVar;
TStrNelVarList _StrNelVar;
// Callbacks (?)
typedef std::map TCallBackList;
TCallBackList _CallBacks;
/// Flag for variable modification
bool _LogicVarChanged;
bool _LogicVarChangedList[4];
//TLogicVarIndex _VarIndex;
// update logic timers ---------------------------------------------
/// 4 timers available for user logic
CAITimerExtended _UserTimer[4];
/// timer for timing positional states
CAITimerExtended _StateTimeout;
/// current state (index into manager's state vector)
CAIState* _state;
/// variable set to request a state change (~0 otherwise)
CAIState* _NextState;
/// timer for timing punctual states
CAITimerExtended _PunctualStateTimeout;
CAIState* _PunctualState;
CAIState* _NextPunctualState;
/// Flag for leaving the punctual state, returning to normal behavior
bool _CancelPunctualState;
bool _FirstBotSpawned;
uint32 _UserEventBlocked;
};
//////////////////////////////////////////////////////////////////////////////
// CPersistentStateInstance //
//////////////////////////////////////////////////////////////////////////////
class CPersistentStateInstance
: public NLMISC::CDbgRefCount
, public CKeyWordOwner
, public CStateInstance
{
public:
CPersistentStateInstance(CStateMachine& reactionContainer);
virtual ~CPersistentStateInstance();
typedef std::vector > TChildList;
void setParentStateInstance(CPersistentStateInstance* parentStateInstance);
CPersistentStateInstance* getParentStateInstance() const { return _ParentStateInstance; }
void addChildStateInstance(CPersistentStateInstance* parentStateInstance);
void removeChildStateInstance(CPersistentStateInstance* parentStateInstance);
TChildList& childs() { return _PSIChilds; }
TChildList _PSIChilds;
// Interface to state status variables -----------------------------
CAIState* getStartState() { return _StartState; }
void setStartState(CAIState* state);
CStateMachine& getEventContainer() { return _Container; }
void updateStateInstance();
//////////////////////////////////////////////////////////////////////////
// CStateInstance
CPersistentStateInstance* getPersistentStateInstance() { return this; }
//////////////////////////////////////////////////////////////////////////
private:
/// id of the state to use at startup
NLMISC::CDbgPtr _StartState;
CStateMachine& _Container;
NLMISC::CDbgPtr _ParentStateInstance;
};
/****************************************************************************/
/* Inlined methods */
/****************************************************************************/
//////////////////////////////////////////////////////////////////////////////
// CStateInstance //
//////////////////////////////////////////////////////////////////////////////
inline
CStateInstance::CStateInstance(CAIState* startState)
{
_UserEventBlocked = 0;
_CtxLogicVar[NLMISC::CStringMapper::map("@this")] = this;
init(startState);
}
inline
CPersistentStateInstance* CStateInstance::getPersistentStateInstance()
{
// nlassert(false);
return (CPersistentStateInstance*)NULL;
}
inline
CStateInstance::CStatePersistentObjEntry::CStatePersistentObjEntry()
{
}
inline
CStateInstance::CStatePersistentObjEntry::CStatePersistentObjEntry(CAIState const* state, NLMISC::CSmartPtr obj)
: _State(state)
, _Obj(obj)
{
}
inline
CStateInstance::CStatePersistentObjEntry::~CStatePersistentObjEntry()
{
_Obj = NULL;
_State = NULL;
}
inline
CAITimerExtended& CStateInstance::timerUser(uint idx)
{
nlassert(idx<4); return _UserTimer[idx];
}
inline
void CStateInstance::logicVarsToString(std::string& str) const
{
for (TLogicVarList::const_iterator it=_LogicVar.begin(), itEnd=_LogicVar.end();it!=itEnd;++it)
str+=*(it->first)+"="+NLMISC::toString(it->second)+" ";
}
inline
float CStateInstance::getLogicVar(NLMISC::TStringId varId)
{
TLogicVarList::iterator it=_LogicVar.find(varId);
if (it==_LogicVar.end())
{
_LogicVar[varId]=0.f;
return 0.f;
}
return it->second; //_LogicVar[varId];
}
inline
void CStateInstance::setLogicVar(NLMISC::TStringId varId, float value)
{
_LogicVar[varId] = value;
_LogicVarChanged = true;
if (varId && varId->size() == 2 && (*varId)[0] == 'v')
{
sint32 index = (*varId)[1] - '0';
if (0 <= index && index < 4)
{
//_VarIndex[varId]
_LogicVarChangedList[static_cast(index)] = true;
}
}
}
inline
std::string CStateInstance::getStrLogicVar(NLMISC::TStringId varId)
{
return _StrLogicVar[varId];
}
inline
void CStateInstance::setStrLogicVar(NLMISC::TStringId varId, std::string const& value)
{
_StrLogicVar[varId] = value;
_LogicVarChanged = true;
if (varId && varId->size() == 2 && (*varId)[0] == 'v')
{
sint32 index = (*varId)[1] - '0';
if (0 <= index && index < 4)
{
//_VarIndex[varId]
_LogicVarChangedList[static_cast(index)] = true;
}
}
//_LogicVarChangedList[_VarIndex[varId]] = true;
}
inline
AIVM::IScriptContext* CStateInstance::getCtxLogicVar(NLMISC::TStringId varId)
{
return _CtxLogicVar[varId];
}
inline
void CStateInstance::setCtxLogicVar(NLMISC::TStringId varId, AIVM::IScriptContext* value)
{
_CtxLogicVar[varId] = value;
_LogicVarChanged = true;
}
inline
float CStateInstance::getNelVar(std::string const& varId)
{
TNelVarList::iterator it = _NelVar.find(varId);
if (it==_NelVar.end())
{
if (NLMISC::CVariable::exists(varId))
{
nlwarning("Nel variable \"%s\" exists outside of this state instance", varId.c_str());
return 0.f;
}
_NelVar[varId] = new NLMISC::CVariable("StateInstance", varId.c_str(), "", 0.f);
_NelVar[varId]->get();
}
return it->second->get();
}
inline
void CStateInstance::setNelVar(std::string const& varId, float value)
{
TNelVarList::iterator it = _NelVar.find(varId);
if (it==_NelVar.end())
{
if (NLMISC::CVariable::exists(varId))
{
nlwarning("Nel variable \"%s\" exists outside of this state instance", varId.c_str());
return;
}
_NelVar[varId] = new NLMISC::CVariable("StateInstance", varId.c_str(), "", 0.f);
}
_NelVar[varId]->set(value);
}
inline
void CStateInstance::delNelVar(std::string const& varId)
{
TNelVarList::iterator it = _NelVar.find(varId);
if (it!=_NelVar.end())
{
delete it->second;
_NelVar.erase(it);
}
else
{
nldebug("Trying to delete Nel variable that doesn't exist: \"%s\"", varId.c_str());
}
}
inline
std::string CStateInstance::getStrNelVar(std::string const& varId)
{
TStrNelVarList::iterator it = _StrNelVar.find(varId);
if (it==_StrNelVar.end())
{
if (NLMISC::CVariable::exists(varId))
{
nlwarning("Nel variable \"%s\" exists outside of this state instance", varId.c_str());
return "";
}
_StrNelVar[varId] = new NLMISC::CVariable("StateInstanceVar", varId.c_str(), "", std::string());
_NelVar[varId]->get();
}
return it->second->get();
}
inline
void CStateInstance::setStrNelVar(std::string const& varId, std::string const& value)
{
TStrNelVarList::iterator it = _StrNelVar.find(varId);
if (it==_StrNelVar.end())
{
if (NLMISC::CVariable::exists(varId))
{
nlwarning("Nel variable \"%s\" exists outside of this state instance", varId.c_str());
return;
}
_StrNelVar[varId] = new NLMISC::CVariable("StateInstanceStrVar", varId.c_str(), "", std::string());
}
_StrNelVar[varId]->set(value);
}
inline
void CStateInstance::delStrNelVar(std::string const& varId)
{
TStrNelVarList::iterator it = _StrNelVar.find(varId);
if (it!=_StrNelVar.end())
{
delete it->second;
_StrNelVar.erase(it);
}
else
{
nldebug("Trying to delete Nel variable that doesn't exist: \"%s\"", varId.c_str());
}
}
inline
CAITimerExtended const& CStateInstance::userTimer (uint32 index) const
{
return _UserTimer[index];
}
inline
bool CStateInstance::advanceUserTimer(uint32 nbTicks)
{
for (uint k=0; k<4; ++k)
{
const uint32 t = getUserTimer(k);
setUserTimer(k, (t>nbTicks)?(t-nbTicks):0);
}
return true;
}
inline
void CStateInstance::processStateEvent(CAIEvent const& stateEvent, CAIState const* state)
{
// NOTE: This is a quick and inefficient implementation of event treatment - needs to be re-worked
// note that it is OK for state to be 'NULL'
if (!state)
{
state=getActiveState();
if (!state)
return;
}
bool foundReaction=false;
// nlassert(_mgr);
for (uint i=0;iaddHistory("STATE: '%s' EVENT: '%s' REACTION: '%s'", state->getAliasNode()->fullName().c_str(),
stateEvent.getName().c_str(), reaction.getAliasNode()->fullName().c_str());
foundReaction=true;
if (!reaction.getAction())
{
nlwarning("Failed to find action for event: %s",reaction.getAliasNode()->fullName().c_str());
continue;
}
if (!reaction.getAction()->executeAction(this, NULL))
{
nlwarning("Failed to execute action for event '%s': for stateInstance:'%s' in state:'%s'", stateEvent.getName().c_str(),
aliasTreeOwner()->getAliasNode()->fullName().c_str(), state->getAliasNode()->fullName().c_str());
continue;
}
}
if (!foundReaction)
{
getDebugHistory()->addHistory("STATE: '%s' EVENT: '%s' NO REACTION", state->getAliasNode()->fullName().c_str(),
stateEvent.getName().c_str());
}
}
inline
void CStateInstance::setNextState(CAIState* state)
{
// we're allowed to set state to 'no state'
if (!state)
{
_NextState = state;
return;
}
// make sure the state is positional (not punctual)
if (!state->isPositional())
{
nlwarning("setNextState(): State should not be punctual '%s'%s - setting state to ~0",
state->getAliasNode()->fullName().c_str(),
state->getAliasString().c_str());
_NextState = NULL;
return;
}
// set the next state
_NextState = state;
}
inline
void CStateInstance::setNextPunctualState(CAIState* state)
{
// we're allowed to set state to 'no state'
if (!state)
return;
// make sure the state is not positional (ie punctual)
if (state->isPositional())
{
nlwarning("CStateInstance::setNextPunctualState(): State should be punctual '%s'%s - setting state to ~0",
state->getAliasNode()->fullName().c_str(),
state->getAliasString().c_str());
state = NULL;
return;
}
// set the next state
_NextPunctualState = state;
}
//////////////////////////////////////////////////////////////////////////////
// CPersistentStateInstance //
//////////////////////////////////////////////////////////////////////////////
inline
CPersistentStateInstance::CPersistentStateInstance(CStateMachine& reactionContainer)
: CKeyWordOwner()
, CStateInstance(NULL)
, _StartState()
, _Container(reactionContainer)
{
}
inline
CPersistentStateInstance::~CPersistentStateInstance()
{
#if !FINAL_VERSION
if (_PSIChilds.size()!=0)
nlwarning("a Npc group is dying and still have childs, which is not possible !!");
nlassert(_PSIChilds.size()==0);
#endif
if (!_ParentStateInstance.isNULL())
_ParentStateInstance->removeChildStateInstance(this);
}
inline
void CPersistentStateInstance::setParentStateInstance(CPersistentStateInstance* parentStateInstance)
{
if (!_ParentStateInstance.isNULL())
_ParentStateInstance->removeChildStateInstance(this);
_ParentStateInstance=parentStateInstance;
if (parentStateInstance!=NULL)
parentStateInstance->addChildStateInstance(this);
}
inline
void CPersistentStateInstance::addChildStateInstance(CPersistentStateInstance* parentStateInstance)
{
#if !FINAL_VERSION
nlassert(std::find(_PSIChilds.begin(), _PSIChilds.end(), NLMISC::CDbgPtr(parentStateInstance))==_PSIChilds.end());
#endif
_PSIChilds.push_back(parentStateInstance);
}
inline
void CPersistentStateInstance::removeChildStateInstance(CPersistentStateInstance* parentStateInstance)
{
TChildList::iterator it = std::find(_PSIChilds.begin(), _PSIChilds.end(), NLMISC::CDbgPtr(parentStateInstance));
#if !FINAL_VERSION
nlassert(it!=_PSIChilds.end());
#endif
if (it!=_PSIChilds.end())
_PSIChilds.erase(it);
}
inline
void CPersistentStateInstance::setStartState(CAIState* state)
{
_StartState = state;
CStateInstance::init(_StartState);
}
#endif