mirror of
https://port.numenaute.org/aleajactaest/khanat-opennel-code.git
synced 2024-12-23 17:38:44 +00:00
1059 lines
26 KiB
C++
1059 lines
26 KiB
C++
// Ryzom - MMORPG Framework <http://dev.ryzom.com/projects/ryzom/>
|
||
// 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 <http://www.gnu.org/licenses/>.
|
||
|
||
#include "stdpch.h"
|
||
|
||
#include "script_vm.h"
|
||
#include "script_compiler.h"
|
||
|
||
using namespace std;
|
||
using namespace NLMISC;
|
||
|
||
namespace AIVM
|
||
{
|
||
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
// Library management //
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
|
||
CLibrary* CLibrary::_instance = NULL;
|
||
CLibrary::TLibContainer CLibrary::_libs;
|
||
|
||
#if !FINAL_VERSION
|
||
CVariable<bool> AIScriptDisplayPrint("aiscript", "AIScriptDisplayPrint", "Display the script 'print's in the AIS log.", true, 0, true);
|
||
#else
|
||
CVariable<bool> AIScriptDisplayPrint("aiscript", "AIScriptDisplayPrint", "Display the script 'print's in the AIS log.", false, 0, true);
|
||
#endif
|
||
CVariable<bool> AIScriptDisplayLog("aiscript", "AIScriptDisplayLog", "Display the script 'log's in the AIS log.", true, 0, true);
|
||
|
||
void CLibrary::addLib(std::string const& name, std::string const& code)
|
||
{
|
||
vector<string> codeVect;
|
||
size_t start, end;
|
||
start = 0;
|
||
// Cut code in lines
|
||
while (start<code.length())
|
||
{
|
||
end = code.find("\n", start);
|
||
if (end!=std::string::npos)
|
||
{
|
||
codeVect.push_back(code.substr(start, end-start));
|
||
start = end+1;
|
||
}
|
||
else
|
||
{
|
||
codeVect.push_back(code.substr(start));
|
||
break;
|
||
}
|
||
}
|
||
NLMISC::CSmartPtr<CByteCode const> byteCode = AICOMP::CCompiler::getInstance().compileCode(codeVect, name);
|
||
if (byteCode!=NULL)
|
||
addLib(name, byteCode);
|
||
}
|
||
|
||
void CLibrary::addLib(std::string const& name, std::vector<std::string> const& code)
|
||
{
|
||
NLMISC::CSmartPtr<CByteCode const> byteCode=AICOMP::CCompiler::getInstance().compileCode(code, name);
|
||
if (byteCode!=NULL)
|
||
addLib(name, byteCode);
|
||
}
|
||
|
||
void CLibrary::addLib(std::string const& name, NLMISC::CSmartPtr<CByteCode const> const& byteCode)
|
||
{
|
||
if (byteCode==NULL)
|
||
{
|
||
nlwarning("Trying to register an empty library script.");
|
||
return;
|
||
}
|
||
nlinfo("Adding script library %s", name.c_str());
|
||
_libs.insert(std::make_pair(name, byteCode));
|
||
}
|
||
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
// Virtual machine //
|
||
//////////////////////////////////////////////////////////////////////////////
|
||
|
||
CSmartPtr<CByteCode const> CLibrary::getLib(std::string const& name)
|
||
{
|
||
TLibContainer::const_iterator it = _libs.find(name);
|
||
if (it!=_libs.end())
|
||
return it->second;
|
||
else
|
||
return CSmartPtr<CByteCode const>(NULL);
|
||
}
|
||
|
||
CScriptVM* CScriptVM::_Instance = NULL;
|
||
|
||
CScriptVM* CScriptVM::getInstance()
|
||
{
|
||
if (_Instance==NULL)
|
||
_Instance = new CScriptVM;
|
||
return _Instance;
|
||
}
|
||
|
||
void CScriptVM::destroyInstance()
|
||
{
|
||
if (_Instance!=NULL)
|
||
{
|
||
delete _Instance;
|
||
_Instance = NULL;
|
||
}
|
||
}
|
||
|
||
void CScriptVM::interpretCode(
|
||
IScriptContext* thisContext,
|
||
IScriptContext* parentContext,
|
||
IScriptContext* callerContext,
|
||
CByteCodeEntry const& codeScriptEntry)
|
||
{
|
||
NLMISC::CSmartPtr<CByteCode const> const& byteCode = codeScriptEntry.code();
|
||
size_t startIndex = codeScriptEntry.index();
|
||
|
||
if (byteCode.isNull())
|
||
return;
|
||
vector<size_t> const& opcodes = byteCode->_opcodes;
|
||
static TStringId parentStrId = CStringMapper::map("parent");
|
||
static TStringId callerStrId = CStringMapper::map("caller");
|
||
|
||
CScriptStack stack;
|
||
size_t index = startIndex;
|
||
string currentString;
|
||
|
||
while (index < opcodes.size())
|
||
{
|
||
#if !FINAL_VERSION
|
||
EOpcode op = (EOpcode)opcodes[index];
|
||
#endif
|
||
|
||
switch (opcodes[index])
|
||
{
|
||
default:
|
||
case INVALID_OPCODE:
|
||
nlwarning("Invalid Opcode for Group '%s' with code in '%s'", thisContext->getContextName().c_str(), byteCode->_sourceName.c_str());
|
||
nlassert(false);
|
||
break;
|
||
case EOP:
|
||
return; // End Of Program
|
||
|
||
case EQ: // == Need: Value1: Value2 After: Value1==Value2 (Boolean as float)
|
||
{
|
||
const float res=stack.top(1)==stack.top()?1.f:0.f;
|
||
stack.pop();
|
||
stack.top()=res;
|
||
++index;
|
||
}
|
||
continue;
|
||
case NEQ: // != Need: Value1: Value2 After: Value1!=Value2 (Boolean as float)
|
||
{
|
||
const float res=stack.top(1)!=stack.top()?1.f:0.f;
|
||
stack.pop();
|
||
stack.top()=res;
|
||
index++;
|
||
}
|
||
continue;
|
||
case INF: // < Need: Value1: Value2 After: Value1<Value2 (Boolean as float)
|
||
{
|
||
const float res=stack.top(1)<stack.top()?1.f:0.f;
|
||
stack.pop();
|
||
stack.top()=res;
|
||
index++;
|
||
}
|
||
continue;
|
||
case INFEQ: // <= Need: Value1: Value2 After: Value1<=Value2 (Boolean as float)
|
||
{
|
||
const float res=stack.top(1)<=stack.top()?1.f:0.f;
|
||
stack.pop();
|
||
stack.top()=res;
|
||
index++;
|
||
}
|
||
continue;
|
||
case SUP: // > Need: Value1: Value2 After: Value1>Value2 (Boolean as float)
|
||
{
|
||
const float res=stack.top(1)>stack.top()?1.f:0.f;
|
||
stack.pop();
|
||
stack.top()=res;
|
||
index++;
|
||
}
|
||
continue;
|
||
case SUPEQ: // >= Need: Value1: Value2 After: Value1>=Value2 (Boolean as float)
|
||
{
|
||
const float res=stack.top(1)>=stack.top()?1.f:0.f;
|
||
stack.pop();
|
||
stack.top()=res;
|
||
index++;
|
||
}
|
||
continue;
|
||
case ADD: // + Need: Value1: Value2 After: Value1+Value2
|
||
{
|
||
CScriptStack::CStackEntry &entry0=stack.top();
|
||
CScriptStack::CStackEntry &entry1=stack.top(1);
|
||
|
||
nlassert((entry0.type()==CScriptStack::EFloat||entry0.type()==CScriptStack::EString)
|
||
&&(entry1.type()==CScriptStack::EFloat||entry1.type()==CScriptStack::EString));
|
||
|
||
breakable
|
||
{
|
||
if (entry0.type()==entry1.type())
|
||
{
|
||
if (entry0.type()==CScriptStack::EFloat)
|
||
(float&)entry1+=(float&)entry0;
|
||
else
|
||
(string&)entry1+=(string&)entry0;
|
||
}
|
||
else
|
||
{
|
||
if (entry0.type()==CScriptStack::EFloat)
|
||
(string&)entry1+=toString("%g", (float&)entry0);
|
||
else
|
||
{
|
||
const float value=entry1;
|
||
entry1=toString("%g", value);
|
||
(string&)entry1+=(string&)entry0;
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
stack.pop();
|
||
index++;
|
||
}
|
||
continue;
|
||
case SUB: // - Need: Value1: Value2 After: Value1-Value2
|
||
{
|
||
const float val=stack.top();
|
||
stack.pop();
|
||
(float&)stack.top()-=val;
|
||
index++;
|
||
}
|
||
continue;
|
||
case MUL: // * Need: Value1: Value2 After: Value1/Value2
|
||
{
|
||
float &res=stack.top(1);
|
||
res*=(float&)stack.top();
|
||
stack.pop();
|
||
index++;
|
||
}
|
||
continue;
|
||
case DIV: // / Need: Value1: Value2 After: Value1/Value2 !Exception Gestion.
|
||
{
|
||
float &res=stack.top(1);
|
||
const float &divisor=stack.top();
|
||
if (divisor==0)
|
||
res=1;
|
||
else
|
||
res/=divisor;
|
||
stack.pop();
|
||
index++;
|
||
}
|
||
continue;
|
||
case AND: // && Need: Value1: Value2 After: Value1&&Value2
|
||
{
|
||
const bool val1=(float&)stack.top(1)!=0.f;
|
||
const bool val2=(float&)stack.top()!=0.f;
|
||
stack.pop();
|
||
stack.top()=(val1&&val2)?1.f:0.f;
|
||
index++;
|
||
}
|
||
continue;
|
||
case OR: // || Need: Value1: Value2 After: Value1||Value2
|
||
{
|
||
const bool val1=(float&)stack.top(1)!=0.f;
|
||
const bool val2=(float&)stack.top()!=0.f;
|
||
stack.pop();
|
||
stack.top()=(val1||val2)?1.f:0.f;
|
||
index++;
|
||
}
|
||
continue;
|
||
case NOT: // ! Need: Value After: !Value
|
||
{
|
||
float &val=stack.top();
|
||
val=(val==0.f)?1.f:0.f;
|
||
index++;
|
||
}
|
||
continue;
|
||
case PUSH_ON_STACK: // Set a Value (float) Need: - After: Value(float)
|
||
{
|
||
stack.push(*((float*)&opcodes[index+1]));
|
||
index+=2;
|
||
}
|
||
continue;
|
||
case POP: // Pop Need: ValToPop After: -
|
||
{
|
||
stack.pop();
|
||
index++;
|
||
}
|
||
continue;
|
||
case SET_VAR_VAL: // Set a Value to a Var. Need: VarName: VarValue After: -
|
||
{
|
||
float f = 0.0f;
|
||
switch (stack.top().type())
|
||
{
|
||
case CScriptStack::EString:
|
||
{
|
||
string &str=stack.top();
|
||
f=(float)atof(str.c_str());
|
||
}
|
||
break;
|
||
case CScriptStack::EFloat:
|
||
{
|
||
f = (float&)stack.top();
|
||
}
|
||
break;
|
||
default:
|
||
nlwarning("Stack top type invalid, poping top value!");
|
||
}
|
||
stack.pop();
|
||
thisContext->setLogicVar(*((TStringId*)&opcodes[index+1]), f);
|
||
index+=2;
|
||
}
|
||
continue;
|
||
case SET_STR_VAR_VAL: // Set a Value to a Var. Need: VarName: VarValue After: -
|
||
{
|
||
switch (stack.top().type())
|
||
{
|
||
case CScriptStack::EString:
|
||
{
|
||
thisContext->setStrLogicVar(*((TStringId*)&opcodes[index+1]), stack.top());
|
||
}
|
||
break;
|
||
case CScriptStack::EFloat:
|
||
{
|
||
float const& val = stack.top();
|
||
thisContext->setStrLogicVar(*((TStringId*)&opcodes[index+1]),NLMISC::toString("%g", val));
|
||
}
|
||
break;
|
||
default:
|
||
nlwarning("Stack top type invalid, poping top value!");
|
||
thisContext->setStrLogicVar(*((TStringId*)&opcodes[index+1]),std::string());
|
||
}
|
||
stack.pop();
|
||
index+=2;
|
||
}
|
||
continue;
|
||
case SET_CTX_VAR_VAL: // Set a Value to a Var. Need: VarName: VarValue After: -
|
||
{
|
||
switch (stack.top().type())
|
||
{
|
||
case CScriptStack::EContext:
|
||
{
|
||
thisContext->setCtxLogicVar(*((TStringId*)&opcodes[index+1]), stack.top());
|
||
}
|
||
break;
|
||
default:
|
||
nlwarning("Stack top type invalid, poping top value!");
|
||
thisContext->setCtxLogicVar(*((TStringId*)&opcodes[index+1]), (IScriptContext*)0);
|
||
}
|
||
stack.pop();
|
||
index+=2;
|
||
}
|
||
continue;
|
||
case PUSH_VAR_VAL: // Push the Value of a Var. Need: - (VarName on next IP) After: VarValue(float)
|
||
{
|
||
const float f=thisContext->getLogicVar(*((TStringId*)&opcodes[index+1]));
|
||
stack.push(f);
|
||
index+=2;
|
||
}
|
||
continue;
|
||
case PUSH_STR_VAR_VAL: // Push the Value of a Var. Need: - (VarName on next IP) After: VarValue(float)
|
||
{
|
||
std::string str = thisContext->getStrLogicVar(*((TStringId*)&opcodes[index+1]));
|
||
stack.push(str);
|
||
index+=2;
|
||
}
|
||
continue;
|
||
case PUSH_CTX_VAR_VAL: // Push the Value of a Var. Need: - (VarName on next IP) After: VarValue(float)
|
||
{
|
||
IScriptContext* ctx = thisContext->getCtxLogicVar(*((TStringId*)&opcodes[index+1]));
|
||
stack.push(ctx);
|
||
index+=2;
|
||
}
|
||
continue;
|
||
/*
|
||
case SET_OTHER_VAR_VAL:
|
||
{
|
||
const TStringId strId=*((TStringId*)&opcodes[index+1]);
|
||
|
||
IScriptContext* otherContext = NULL;
|
||
if (strId==parentStrId)
|
||
{
|
||
otherContext = parentContext;
|
||
}
|
||
else if (strId==callerStrId)
|
||
{
|
||
otherContext = callerContext;
|
||
}
|
||
else
|
||
{
|
||
otherContext = thisContext->findContext(strId);
|
||
}
|
||
|
||
float f = 0.0f;
|
||
switch (stack.top().type()) //stack.type(0))
|
||
{
|
||
case CScriptStack::EString:
|
||
{
|
||
string& str = stack.top();
|
||
f = (float)atof(str.c_str());
|
||
}
|
||
break;
|
||
case CScriptStack::EFloat:
|
||
{
|
||
f = (float&)stack.top();
|
||
}
|
||
break;
|
||
default:
|
||
nlwarning("Stack top type invalid, poping top value!");
|
||
}
|
||
if (otherContext)
|
||
otherContext->setLogicVar(*((TStringId*)&opcodes[index+2]), f);
|
||
stack.pop();
|
||
index+=3;
|
||
}
|
||
continue;
|
||
case SET_OTHER_STR_VAR_VAL:
|
||
{
|
||
const TStringId strId=*((TStringId*)&opcodes[index+1]);
|
||
|
||
IScriptContext* otherContext = NULL;
|
||
if (strId==parentStrId)
|
||
{
|
||
otherContext = parentContext;
|
||
}
|
||
else if (strId==callerStrId)
|
||
{
|
||
otherContext = callerContext;
|
||
}
|
||
else
|
||
{
|
||
otherContext = thisContext->findContext(strId);
|
||
}
|
||
|
||
std::string str;
|
||
switch (stack.top().type())
|
||
{
|
||
case CScriptStack::EString:
|
||
{
|
||
str = (string&)stack.top();
|
||
}
|
||
break;
|
||
case CScriptStack::EFloat:
|
||
{
|
||
float& val = (float&)stack.top();
|
||
str = NLMISC::toString("%g", val);
|
||
}
|
||
break;
|
||
default:
|
||
nlwarning("Stack top type invalid, poping top value!");
|
||
}
|
||
if (otherContext)
|
||
otherContext->setStrLogicVar(*((TStringId*)&opcodes[index+2]), str);
|
||
stack.pop();
|
||
index += 3;
|
||
}
|
||
continue;
|
||
case SET_OTHER_CTX_VAR_VAL:
|
||
{
|
||
TStringId const strId = *((TStringId*)&opcodes[index+1]);
|
||
|
||
IScriptContext* otherContext = NULL;
|
||
if (strId==parentStrId)
|
||
{
|
||
otherContext = parentContext;
|
||
}
|
||
else if (strId==callerStrId)
|
||
{
|
||
otherContext = callerContext;
|
||
}
|
||
else
|
||
{
|
||
otherContext = thisContext->findContext(strId);
|
||
}
|
||
|
||
IScriptContext* ctx = (IScriptContext*)0;
|
||
switch (stack.top().type())
|
||
{
|
||
case CScriptStack::EContext:
|
||
{
|
||
ctx = (IScriptContext*)stack.top();
|
||
}
|
||
break;
|
||
default:
|
||
nlwarning("Stack top type invalid, poping top value!");
|
||
}
|
||
if (otherContext)
|
||
otherContext->setCtxLogicVar(*((TStringId*)&opcodes[index+2]), ctx);
|
||
stack.pop();
|
||
index += 3;
|
||
}
|
||
continue;
|
||
case PUSH_OTHER_VAR_VAL:
|
||
{
|
||
const TStringId strId=*((TStringId*)&opcodes[index+1]);
|
||
|
||
IScriptContext* otherContext = NULL;
|
||
if (strId==parentStrId)
|
||
{
|
||
otherContext = parentContext;
|
||
}
|
||
else if (strId==callerStrId)
|
||
{
|
||
otherContext = callerContext;
|
||
}
|
||
else
|
||
{
|
||
otherContext = thisContext->findContext(strId);
|
||
}
|
||
|
||
float f;
|
||
if (otherContext)
|
||
f = otherContext->getLogicVar(*((TStringId*)&opcodes[index+2]));
|
||
else
|
||
f = 1.0f;
|
||
|
||
stack.push(f);
|
||
index += 3;
|
||
}
|
||
continue;
|
||
case PUSH_OTHER_STR_VAR_VAL:
|
||
{
|
||
const TStringId strId=*((TStringId*)&opcodes[index+1]);
|
||
|
||
IScriptContext* otherContext = NULL;
|
||
if (strId==parentStrId)
|
||
{
|
||
otherContext = parentContext;
|
||
}
|
||
else if (strId==callerStrId)
|
||
{
|
||
otherContext = callerContext;
|
||
}
|
||
else
|
||
{
|
||
otherContext = thisContext->findContext(strId);
|
||
}
|
||
|
||
if (otherContext)
|
||
stack.push(otherContext->getStrLogicVar(*((TStringId*)&opcodes[index+2])));
|
||
else
|
||
stack.push(std::string()); // accepted coz const &
|
||
|
||
index += 3;
|
||
}
|
||
continue;
|
||
case PUSH_OTHER_CTX_VAR_VAL:
|
||
{
|
||
TStringId const strId = *((TStringId*)&opcodes[index+1]);
|
||
|
||
IScriptContext* otherContext = NULL;
|
||
if (strId==parentStrId)
|
||
{
|
||
otherContext = parentContext;
|
||
}
|
||
else if (strId==callerStrId)
|
||
{
|
||
otherContext = callerContext;
|
||
}
|
||
else
|
||
{
|
||
otherContext = thisContext->findContext(strId);
|
||
}
|
||
|
||
if (otherContext)
|
||
stack.push(otherContext->getCtxLogicVar(*((TStringId*)&opcodes[index+2])));
|
||
else
|
||
stack.push((IScriptContext*)0);
|
||
|
||
index += 3;
|
||
}
|
||
continue;
|
||
*/
|
||
case SET_CONTEXT_VAR_VAL:
|
||
{
|
||
IScriptContext* otherContext = (IScriptContext*)0;
|
||
switch (stack.top().type())
|
||
{
|
||
case CScriptStack::EContext:
|
||
{
|
||
otherContext = (IScriptContext*)stack.top();
|
||
}
|
||
break;
|
||
default:
|
||
nlwarning("Stack top type invalid, poping top value!");
|
||
}
|
||
stack.pop();
|
||
|
||
float f = 0.0f;
|
||
switch (stack.top().type()) //stack.type(0))
|
||
{
|
||
case CScriptStack::EString:
|
||
{
|
||
string& str = stack.top();
|
||
f = (float)atof(str.c_str());
|
||
}
|
||
break;
|
||
case CScriptStack::EFloat:
|
||
{
|
||
f = (float&)stack.top();
|
||
}
|
||
break;
|
||
default:
|
||
nlwarning("Stack top type invalid, poping top value!");
|
||
}
|
||
if (otherContext)
|
||
otherContext->setLogicVar(*((TStringId*)&opcodes[index+1]), f);
|
||
stack.pop();
|
||
index+=2;
|
||
}
|
||
continue;
|
||
case SET_CONTEXT_STR_VAR_VAL:
|
||
{
|
||
IScriptContext* otherContext = (IScriptContext*)0;
|
||
switch (stack.top().type())
|
||
{
|
||
case CScriptStack::EContext:
|
||
{
|
||
otherContext = (IScriptContext*)stack.top();
|
||
}
|
||
break;
|
||
default:
|
||
nlwarning("Stack top type invalid, poping top value!");
|
||
}
|
||
stack.pop();
|
||
|
||
std::string str;
|
||
switch (stack.top().type())
|
||
{
|
||
case CScriptStack::EString:
|
||
{
|
||
str = (string&)stack.top();
|
||
}
|
||
break;
|
||
case CScriptStack::EFloat:
|
||
{
|
||
float& val = (float&)stack.top();
|
||
str = NLMISC::toString("%g", val);
|
||
}
|
||
break;
|
||
default:
|
||
nlwarning("Stack top type invalid, poping top value!");
|
||
}
|
||
if (otherContext)
|
||
otherContext->setStrLogicVar(*((TStringId*)&opcodes[index+1]), str);
|
||
stack.pop();
|
||
index += 2;
|
||
}
|
||
continue;
|
||
case SET_CONTEXT_CTX_VAR_VAL:
|
||
{
|
||
IScriptContext* otherContext = (IScriptContext*)0;
|
||
switch (stack.top().type())
|
||
{
|
||
case CScriptStack::EContext:
|
||
{
|
||
otherContext = (IScriptContext*)stack.top();
|
||
}
|
||
break;
|
||
default:
|
||
nlwarning("Stack top type invalid, poping top value!");
|
||
}
|
||
stack.pop();
|
||
|
||
IScriptContext* ctx = (IScriptContext*)0;
|
||
switch (stack.top().type())
|
||
{
|
||
case CScriptStack::EContext:
|
||
{
|
||
ctx = (IScriptContext*)stack.top();
|
||
}
|
||
break;
|
||
default:
|
||
nlwarning("Stack top type invalid, poping top value!");
|
||
}
|
||
if (otherContext)
|
||
otherContext->setCtxLogicVar(*((TStringId*)&opcodes[index+1]), ctx);
|
||
stack.pop();
|
||
index += 2;
|
||
}
|
||
continue;
|
||
case PUSH_CONTEXT_VAR_VAL:
|
||
{
|
||
IScriptContext* otherContext = (IScriptContext*)0;
|
||
switch (stack.top().type())
|
||
{
|
||
case CScriptStack::EContext:
|
||
{
|
||
otherContext = (IScriptContext*)stack.top();
|
||
}
|
||
break;
|
||
default:
|
||
nlwarning("Stack top type invalid, poping top value!");
|
||
}
|
||
stack.pop();
|
||
|
||
float f;
|
||
if (otherContext)
|
||
f = otherContext->getLogicVar(*((TStringId*)&opcodes[index+1]));
|
||
else
|
||
f = 1.0f;
|
||
|
||
stack.push(f);
|
||
index += 2;
|
||
}
|
||
continue;
|
||
case PUSH_CONTEXT_STR_VAR_VAL:
|
||
{
|
||
IScriptContext* otherContext = (IScriptContext*)0;
|
||
switch (stack.top().type())
|
||
{
|
||
case CScriptStack::EContext:
|
||
{
|
||
otherContext = (IScriptContext*)stack.top();
|
||
}
|
||
break;
|
||
default:
|
||
nlwarning("Stack top type invalid, poping top value!");
|
||
}
|
||
stack.pop();
|
||
|
||
if (otherContext)
|
||
stack.push(otherContext->getStrLogicVar(*((TStringId*)&opcodes[index+1])));
|
||
else
|
||
stack.push(std::string()); // accepted coz const &
|
||
|
||
index += 2;
|
||
}
|
||
continue;
|
||
case PUSH_CONTEXT_CTX_VAR_VAL:
|
||
{
|
||
IScriptContext* otherContext = (IScriptContext*)0;
|
||
switch (stack.top().type())
|
||
{
|
||
case CScriptStack::EContext:
|
||
{
|
||
otherContext = (IScriptContext*)stack.top();
|
||
}
|
||
break;
|
||
default:
|
||
nlwarning("Stack top type invalid, poping top value!");
|
||
}
|
||
stack.pop();
|
||
|
||
if (otherContext)
|
||
stack.push(otherContext->getCtxLogicVar(*((TStringId*)&opcodes[index+1])));
|
||
else
|
||
stack.push((IScriptContext*)0);
|
||
|
||
index += 2;
|
||
}
|
||
continue;
|
||
case JUMP: // Jump + nb size_t to jump (relative). Need: NewJumpOffset After: -
|
||
{
|
||
index+=opcodes[index+1]+1; // AGI .. Not Opt
|
||
}
|
||
continue;
|
||
case JE: // Jump if last stack value is FALSE(==0). Need: BoolValue(float) (NewJumpOffset on Next Ip) After: -
|
||
{
|
||
if ((float&)stack.top()==0.f)
|
||
index+=opcodes[index+1]+1; // AGI .. Not Opt
|
||
else
|
||
index+=2;
|
||
stack.pop();
|
||
}
|
||
continue;
|
||
case JNE: // Jump if last stack value is TRUE(!=0). Need: BoolValue(float) (NewJumpOffset on Next Ip) After: -
|
||
{
|
||
if ((float&)stack.top()!=0.f)
|
||
index+=opcodes[index+1]+1; // AGI .. Not Opt
|
||
else
|
||
index+=2;
|
||
stack.pop();
|
||
}
|
||
continue;
|
||
case PUSH_PRINT_STRING:
|
||
{
|
||
currentString+=CStringMapper::unmap(*((TStringId*)&opcodes[index+1])); // strPt.substr(1,strPt.size()-2);
|
||
index+=2;
|
||
}
|
||
continue;
|
||
case PUSH_PRINT_VAR:
|
||
{
|
||
float const val = thisContext->getLogicVar(*((TStringId*)&opcodes[index+1]));
|
||
currentString += NLMISC::toString("%g", val);
|
||
index += 2;
|
||
}
|
||
continue;
|
||
case PUSH_PRINT_STR_VAR:
|
||
{
|
||
string const str = thisContext->getStrLogicVar(*((TStringId*)&opcodes[index+1]));
|
||
currentString += str;
|
||
index += 2;
|
||
}
|
||
continue;
|
||
case PRINT_STRING:
|
||
{
|
||
if (AIScriptDisplayPrint)
|
||
{
|
||
nlinfo(currentString.c_str());
|
||
}
|
||
currentString.resize(0);
|
||
++index;
|
||
}
|
||
continue;
|
||
case LOG_STRING:
|
||
{
|
||
if (AIScriptDisplayLog)
|
||
{
|
||
nlinfo(currentString.c_str());
|
||
}
|
||
currentString.resize(0);
|
||
++index;
|
||
}
|
||
continue;
|
||
case FUNCTION:
|
||
{
|
||
// on_event
|
||
TStringId const eventName = *((TStringId*)&opcodes[index+1]);
|
||
|
||
IScriptContext* const sc = stack.top();
|
||
stack.pop();
|
||
if (sc)
|
||
sc->setScriptCallBack(eventName, CByteCodeEntry(byteCode, index+4));
|
||
index+=2;
|
||
}
|
||
continue;
|
||
case CALL:
|
||
{
|
||
// set_event
|
||
const TStringId eventName=*((TStringId*)&opcodes[index+1]);
|
||
|
||
IScriptContext* const sc = stack.top();
|
||
stack.pop();
|
||
if (sc)
|
||
sc->callScriptCallBack(thisContext, eventName, 0, "", "", &stack);
|
||
|
||
index+=2;
|
||
}
|
||
continue;
|
||
case PUSH_THIS:
|
||
{
|
||
IScriptContext* const sc=thisContext;
|
||
stack.push(sc);
|
||
index++;
|
||
}
|
||
continue;
|
||
case PUSH_GROUP:
|
||
{
|
||
const TStringId strId=*((TStringId*)&opcodes[index+1]);
|
||
|
||
IScriptContext* otherContext = NULL;
|
||
if (strId==parentStrId)
|
||
{
|
||
otherContext = parentContext;
|
||
}
|
||
else if (strId==callerStrId)
|
||
{
|
||
otherContext = callerContext;
|
||
}
|
||
else
|
||
{
|
||
otherContext = thisContext->findContext(strId);
|
||
}
|
||
if (!otherContext)
|
||
nlinfo("Group %s unknown, pushing a NULL context on the stack, this may lead to bad behaviours", CStringMapper::unmap(strId).c_str());
|
||
|
||
stack.push(otherContext);
|
||
index+=2;
|
||
}
|
||
continue;
|
||
case PUSH_STRING:
|
||
{
|
||
const string &str = CStringMapper::unmap(*((TStringId*)&opcodes[index+1]));
|
||
stack.push(str);
|
||
index+=2;
|
||
}
|
||
continue;
|
||
case ASSIGN_FUNC_FROM:
|
||
{
|
||
const TStringId srcFunc=CStringMapper::map(stack.top());
|
||
stack.pop();
|
||
|
||
IScriptContext* const src=stack.top();
|
||
stack.pop();
|
||
|
||
const TStringId destFunc=CStringMapper::map(stack.top());
|
||
stack.pop();
|
||
|
||
IScriptContext* const dest=stack.top();
|
||
stack.pop();
|
||
|
||
if ( dest
|
||
&& src )
|
||
{
|
||
CByteCodeEntry const* pcode = src->getScriptCallBackPtr(srcFunc);
|
||
if (pcode!=NULL)
|
||
dest->setScriptCallBack(destFunc, *pcode);
|
||
}
|
||
index++;
|
||
}
|
||
continue;
|
||
case NATIVE_CALL:
|
||
{
|
||
IScriptContext* const sc = stack.top();
|
||
stack.pop();
|
||
string const& funcName = CStringMapper::unmap(*((TStringId*)&opcodes[++index]));
|
||
int mode = (int)opcodes[++index];
|
||
string const& inParamsSig = CStringMapper::unmap(*((TStringId*)&opcodes[++index]));
|
||
string const& outParamsSig = CStringMapper::unmap(*((TStringId*)&opcodes[++index]));
|
||
if (sc)
|
||
{
|
||
sc->callNativeCallBack(thisContext, funcName, mode, inParamsSig, outParamsSig, &stack);
|
||
}
|
||
else
|
||
{
|
||
nlwarning("Calling a native function (%s) on a NULL group/context, rebuilding stack (this situation previously led to unknown behaviour, most of the time crashes)", funcName.c_str());
|
||
// If we have parameters we got to rebuild the stack
|
||
if (!inParamsSig.empty())
|
||
{
|
||
for (size_t i=0; i<inParamsSig.length(); ++i)
|
||
stack.pop();
|
||
}
|
||
if (!outParamsSig.empty())
|
||
{
|
||
for (size_t i=0; i<outParamsSig.length(); ++i)
|
||
{
|
||
switch (outParamsSig[i])
|
||
{
|
||
case 'f': stack.push(0.f); break;
|
||
case 's': stack.push(string()); break;
|
||
case 'c': stack.push((IScriptContext*)0); break;
|
||
default: nlassert("Unknown parameter type in native function call while rebuilding stack");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
++index;
|
||
}
|
||
continue;
|
||
case RAND:
|
||
{
|
||
const size_t randIndex=rand32((uint32)opcodes[index+1]); // rand(RANDCOUNT)
|
||
index+=3; // pass RAND + RANDCOUNT + 1
|
||
|
||
stack.push((int)(index+opcodes[index])); // push the absolute address for RET.
|
||
|
||
index+=(randIndex+1)*2;
|
||
index+=opcodes[index]; // we jump at the random sequence.
|
||
}
|
||
continue;
|
||
case RET:
|
||
{
|
||
index=(int&)stack.top();
|
||
stack.pop();
|
||
}
|
||
continue;
|
||
case ONCHILDREN:
|
||
{
|
||
if (thisContext)
|
||
{
|
||
thisContext->interpretCodeOnChildren(CByteCodeEntry(byteCode, index+3));
|
||
}
|
||
index++; // let's go to jump ..
|
||
}
|
||
continue;
|
||
case SWITCH:
|
||
{
|
||
// !!!!!
|
||
size_t compValue=0;
|
||
switch (stack.top().type())
|
||
{
|
||
case CScriptStack::EString:
|
||
{
|
||
TStringId strId = CStringMapper::map(stack.top());
|
||
stack.pop();
|
||
compValue = *((size_t*)&strId);
|
||
}
|
||
break;
|
||
case CScriptStack::EFloat:
|
||
{
|
||
float val = (float&)stack.top();
|
||
stack.pop();
|
||
compValue = *((size_t*)&val);
|
||
}
|
||
break;
|
||
default:
|
||
nlwarning("Stack top type invalid, poping top value");
|
||
stack.pop();
|
||
}
|
||
|
||
size_t nbCase=opcodes[index+1];
|
||
index+=2; // SWITCH + #Case
|
||
stack.push((int)(index+opcodes[index])); // push the absolute address for RET.
|
||
index++;
|
||
size_t offset=index;
|
||
bool found=false;
|
||
while (nbCase>0)
|
||
{
|
||
if (compValue==opcodes[offset]) // we could replace this with binary retrieval.
|
||
{
|
||
index=offset+1;
|
||
index+=opcodes[index]; // we jump at the random sequence.
|
||
found=true;
|
||
break;
|
||
}
|
||
nbCase--;
|
||
offset+=2;
|
||
}
|
||
// if not found, don't do anything.
|
||
if (!found)
|
||
{
|
||
index=(int&)stack.top();
|
||
stack.pop();
|
||
}
|
||
|
||
}
|
||
continue;
|
||
case INCR: // Increment top of stack.
|
||
{
|
||
float &f = stack.top();
|
||
++f;
|
||
++index;
|
||
}
|
||
continue;
|
||
case DECR: // Decrement top of stack.
|
||
{
|
||
float &f = stack.top();
|
||
--f;
|
||
++index;
|
||
}
|
||
continue;
|
||
case CONCAT: // Concatenates 2 strings
|
||
{
|
||
(string&)stack.top(1) += (string&)stack.top();
|
||
stack.pop();
|
||
}
|
||
continue;
|
||
case FTOS: // Convert a float to a string
|
||
{
|
||
stack.top()=NLMISC::toString("%g", (float&)stack.top());
|
||
}
|
||
continue;
|
||
}
|
||
nlassert(false); // must use continue !! Non implement<6E>.
|
||
}
|
||
}
|
||
|
||
}
|