// 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 .
#include "stdpch.h"
#include "state_instance.h"
#include "nel/misc/smart_ptr.h"
#include "script_vm.h"
#include "script_compiler.h"
#include "event_reaction_container.h"
#include "ai_grp.h"
#include "ai_outpost.h"
using namespace std;
using namespace NLMISC;
using namespace AICOMP;
using namespace AIVM;
//////////////////////////////////////////////////////////////////////////////
// CStateInstance //
//////////////////////////////////////////////////////////////////////////////
CAIState const* CStateInstance::getActiveState() const
{
#if !FINAL_VERSION
nlassert(this!=NULL);
#else
if (!this)
return NULL; // ugly! -> to remove date = 19/05/2004.
#endif
if (_PunctualState) // the puntual state is active !
return _PunctualState;
if (_state) // the normal state is active !
return _state;
// no state is active !
return NULL;
}
sint32 CStateInstance::getUserTimer(uint timerId)
{
NLMISC::clamp(timerId, 0u, 3u);
return _UserTimer[timerId].timeRemaining();
}
void CStateInstance::setUserTimer(uint timerId, sint32 time)
{
NLMISC::clamp(timerId, 0u, 3u);
if (_UserTimer[timerId].isEnabled())
_UserTimer[timerId].set(time);
}
void CStateInstance::addStatePersistentObj(CAIState const* keyState, CSmartPtr anyObj)
{
_StatePersistentObjList.push_back(CStatePersistentObjEntry(keyState, anyObj));
}
void CStateInstance::setFirstBotSpawned()
{
_FirstBotSpawned = true;
}
struct CDiffStateRemover
{
CDiffStateRemover(CAIState const* const state) : _State(state) { }
bool operator()(CStateInstance::CStatePersistentObjEntry const& entry) const
{
return entry._State!=_State;
}
CAIState const* const _State;
};
void CStateInstance::removeExceptForState(CAIState const* keyState)
{
TStatePersistentObjList::iterator const itLast = remove_if(_StatePersistentObjList.begin(), _StatePersistentObjList.end(), CDiffStateRemover(keyState));
_StatePersistentObjList.erase(itLast, _StatePersistentObjList.end());
}
void CStateInstance::setScriptCallBack(TStringId const& eventName, AIVM::CByteCodeEntry const& codeScriptEntry)
{
_CallBacks[eventName] = codeScriptEntry; // effectively affects this callback to this eventName.
}
AIVM::CByteCodeEntry const* CStateInstance::getScriptCallBackPtr(TStringId const& eventName) const
{
TCallBackList::const_iterator it = _CallBacks.find(eventName);
if (it==_CallBacks.end())
return NULL;
return &(it->second);
}
void CStateInstance::callScriptCallBack(IScriptContext* caller, TStringId const& funcName, int mode, std::string const& inParamsSig, std::string const& outParamsSig, AIVM::CScriptStack* stack)
{
// :FIXME: mode, signatures end stack are unused
AIVM::CByteCodeEntry const* const codeScript = getScriptCallBackPtr(funcName);
if (!codeScript || !codeScript->isValid())
return;
interpretCode(caller, *codeScript); // effectively calls the call back.
}
void CStateInstance::callNativeCallBack(IScriptContext* caller, std::string const& funcName, int mode, std::string const& inParamsSig, std::string const& outParamsSig, AIVM::CScriptStack* stack)
{
// :FIXME: caller is unused
CScriptNativeFuncParams* funcParam = CCompiler::getNativeFunc(funcName, inParamsSig, outParamsSig);
if (funcParam!=NULL)
{
bool varArg = (mode & 1)!=0; // :KLUDGE: Hardcoded 1 :TODO: Replace with a names constant (see script_compiler.cpp)
if (stack!=NULL)
{
if (varArg)
{
stack->push(outParamsSig);
stack->push(inParamsSig);
}
funcParam->_func(this, *stack);
}
else
{
AIVM::CScriptStack dummyStack;
if (varArg)
{
dummyStack.push(outParamsSig);
dummyStack.push(inParamsSig);
}
funcParam->_func(this, dummyStack);
}
}
else
{
nlwarning("Trying to call an unknown native function '%s_%s_%s'", funcName.c_str(), inParamsSig.c_str(), outParamsSig.c_str());
// TODO:kxu: rebuild stack
}
}
void CStateInstance::dumpVarsAndFunctions(CStringWriter& sw) const
{
sw.append("float variables:");
FOREACHC(varIt, TLogicVarList, _LogicVar)
sw.append(" "+CStringMapper::unmap(varIt->first)+" = "+NLMISC::toString(varIt->second));
sw.append("string variables:");
FOREACHC(varIt, TStrLogicVarList, _StrLogicVar)
sw.append(" "+CStringMapper::unmap(varIt->first)+" = "+varIt->second);
sw.append("context variables:");
FOREACHC(varIt, TCtxLogicVarList, _CtxLogicVar)
sw.append(" "+CStringMapper::unmap(varIt->first)+" = "+NLMISC::toStringPtr(varIt->second));
sw.append("callBacks:");
FOREACHC(varIt, TCallBackList, _CallBacks)
sw.append(" "+CStringMapper::unmap(varIt->first));
}
void CStateInstance::interpretCode(IScriptContext* caller, CByteCodeEntry const& codeScriptEntry)
{
AIVM::IScriptContext* parent = getPersistentStateInstance()->getParentStateInstance();
AIVM::CScriptVM::getInstance()->interpretCode(this, parent, caller, codeScriptEntry);
}
void CStateInstance::interpretCode(IScriptContext* caller, CSmartPtr const& codeScript)
{
interpretCode(caller, CByteCodeEntry(codeScript, 0));
}
void CStateInstance::interpretCodeOnChildren(CByteCodeEntry const& codeScriptEntry)
{
vector > tmpList;
FOREACH(childIt, CPersistentStateInstance::TChildList, getPersistentStateInstance()->childs())
tmpList.push_back((*childIt)->getGroup());
FOREACH(childIt, vector >, tmpList)
(*childIt)->getPersistentStateInstance()->interpretCode(this, codeScriptEntry);
}
IScriptContext* CStateInstance::findContext(NLMISC::TStringId const strId)
{
#if !FINAL_VERSION
nlassert(this->getGroup());
#endif
std::vector grps;
this->getGroup()->getAIInstance()->findGroup(grps, CStringMapper::unmap(strId));
if (grps.size()==1)
return grps.back()->getPersistentStateInstance();
else
return NULL;
}
std::string CStateInstance::getContextName()
{
return getGroup()->getFullName();
}
void CStateInstance::init(CAIState* startState)
{
_state=
_PunctualState=
_NextPunctualState=NULL;
_CancelPunctualState = false;
_NextState = startState;
_LogicVarChanged = false;
_FirstBotSpawned = false;
// _VarIndex[CStringMapper::map("v0")]=0;
// _VarIndex[CStringMapper::map("v1")]=1;
// _VarIndex[CStringMapper::map("v2")]=2;
// _VarIndex[CStringMapper::map("v3")]=3;
}
void CStateInstance::setGlobalNelVar(std::string const& varId, float value)
{
if (NLMISC::ICommand::exists(varId))
NLMISC::ICommand::execute(NLMISC::toString("%s %f", varId.c_str(), value), *NLMISC::InfoLog);
else
nlwarning("Nel variable \"%s\" do not exist", varId.c_str());
}
void CStateInstance::setGlobalNelVar(std::string const& varId, std::string value)
{
std::vector args; args.push_back(value);
if (NLMISC::ICommand::exists(varId))
NLMISC::ICommand::execute(NLMISC::toString("%s %s", varId.c_str(), value.c_str()), *NLMISC::InfoLog);
else
nlwarning("Nel variable \"%s\" do not exist", varId.c_str());
}
void CStateInstance::blockUserEvent(uint32 eventId)
{
_UserEventBlocked |= (1 << eventId);
}
void CStateInstance::unblockUserEvent(uint32 eventId)
{
_UserEventBlocked &= ~(1 << eventId);
}
bool CStateInstance::isUserEventBlocked(uint32 eventId) const
{
return ((1 << eventId) & _UserEventBlocked) != 0;
}
//////////////////////////////////////////////////////////////////////////////
// CPersistentStateInstance //
//////////////////////////////////////////////////////////////////////////////
void CPersistentStateInstance::updateStateInstance()
{
// deal with timer events -----------------------------------------
// Event: State Timeout
if (_StateTimeout.testOnce())
processStateEvent(getEventContainer().EventPositionalStateTimeout);
// Event: Punctual State Timeout
if (_PunctualStateTimeout.testOnce())
processStateEvent(getEventContainer().EventPunctualStateTimeout);
// Event: User Timer n Triggered
for (uint i=0;i<4;++i)
{
if (!_UserTimer[i].testOnce())
continue;
processStateEvent(getEventContainer().EventUserTimer[i]);
}
// Event logic var changed
if (_LogicVarChanged)
{
processStateEvent(getEventContainer().EventVariableChanged);
for(uint i =0;i<4;++i)
{
if(_LogicVarChangedList[i]==true)
{
processStateEvent(getEventContainer().EventVariableChangedTab[i]);
_LogicVarChangedList[i]=false;
}
}
_LogicVarChanged = false;
}
// deal with positional state change ------------------------------
// if state change requested (and not in punctual state) then setup new state
if (_NextState)
{
CAIState const* oldState = NULL;
if (!_PunctualState && !_NextPunctualState && _state)
{
// close down current state
oldState = _state;
processStateEvent(getEventContainer().EventEndOfState);
}
// switch state & initalise state status flags
_state = _NextState;
_NextState = NULL;
_StateTimeout.disable();
if (_PunctualState || _NextPunctualState)
return;
removeExceptForState(_state);
processStateEvent(getEventContainer().EventStartOfState);
stateChange(oldState, _state);
}
// deal with start of punctual state ------------------------------
// if state change requested then setup new state
if (_NextPunctualState)
{
CAIState const* oldState = NULL;
breakable
{
if (_PunctualState)
{
oldState = _PunctualState;
break;
}
if (_state)
{
oldState = _state;
break;
}
}
// switch state & initalize state status flags
_PunctualState = _NextPunctualState;
_NextPunctualState = NULL;
_CancelPunctualState = false;
_PunctualStateTimeout.disable();
// initalize new state
CAIState const* const newState = _PunctualState;
removeExceptForState(newState);
processStateEvent(getEventContainer().EventStartOfState, newState);
stateChange(oldState, newState);
}
// must be done after start of state
if (_FirstBotSpawned)
{
_FirstBotSpawned = false;
processStateEvent(getEventContainer().EventFirstBotSpawned);
}
// deal with end of punctual state --------------------------------
if (!_CancelPunctualState)
return;
_CancelPunctualState = false;
// if there was an active punctual state then cancel it
if (!_PunctualState)
return;
// switch state & initalise state status flags
_PunctualStateTimeout.disable();
_StateTimeout.resume(); // this line just in case timeout suspended during punctual state
// close down current state
CAIState const* const punctualState = _PunctualState;
processStateEvent(getEventContainer().EventEndOfState, punctualState);
_PunctualState = NULL;
// this removes obj that depends on state affectation existence.
removeExceptForState(_state);
// specialized virtual state change call.
stateChange(punctualState, _state);
}
#include "event_reaction_include.h"