2023-01-29 20:59:31 +01:00
|
|
|
/*
|
|
|
|
===========================================================================
|
|
|
|
Copyright (C) 2015 the OpenMoHAA team
|
|
|
|
|
|
|
|
This file is part of OpenMoHAA source code.
|
|
|
|
|
|
|
|
OpenMoHAA source code is free software; you can redistribute it
|
|
|
|
and/or modify it under the terms of the GNU General Public License as
|
|
|
|
published by the Free Software Foundation; either version 2 of the License,
|
|
|
|
or (at your option) any later version.
|
|
|
|
|
|
|
|
OpenMoHAA source code 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 General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with OpenMoHAA source code; if not, write to the Free Software
|
|
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
===========================================================================
|
|
|
|
*/
|
|
|
|
|
|
|
|
// scriptvm.cpp : Script virtual machine, interprets and execute scripts
|
|
|
|
|
|
|
|
#include "glb_local.h"
|
|
|
|
#include "scriptmaster.h"
|
2023-01-29 22:57:04 +01:00
|
|
|
#include "scriptthread.h"
|
2023-01-29 20:59:31 +01:00
|
|
|
#include "scriptvm.h"
|
|
|
|
#include "compiler.h"
|
|
|
|
#include "game.h"
|
|
|
|
#include "level.h"
|
|
|
|
#include "parm.h"
|
|
|
|
#include "world.h"
|
|
|
|
|
|
|
|
#ifdef CGAME_DLL
|
|
|
|
|
|
|
|
#define VM_Printf cgi.Printf
|
|
|
|
#define VM_DPrintf cgi.DPrintf
|
|
|
|
|
|
|
|
#elif defined GAME_DLL
|
|
|
|
|
|
|
|
#define VM_Printf gi.Printf
|
|
|
|
#define VM_DPrintf gi.DPrintf2
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
#define VM_Printf printf
|
|
|
|
#define VM_DPrintf printf
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
//====================
|
|
|
|
// ScriptClass
|
|
|
|
//====================
|
|
|
|
|
2023-01-30 17:11:44 +01:00
|
|
|
MEM_BlockAlloc<ScriptClass> ScriptClass_allocator;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
|
|
|
CLASS_DECLARATION(Listener, ScriptClass, NULL)
|
|
|
|
{
|
|
|
|
{ NULL, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
new ScriptClass
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
void* ScriptClass::operator new(size_t size)
|
|
|
|
{
|
|
|
|
return ScriptClass_allocator.Alloc();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
delete ptr
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
void ScriptClass::operator delete(void* ptr)
|
|
|
|
{
|
|
|
|
ScriptClass_allocator.Free(ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
ScriptClass
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
ScriptClass::ScriptClass(GameScript* gameScript, Listener* self)
|
|
|
|
{
|
|
|
|
m_Self = self;
|
|
|
|
m_Script = gameScript;
|
|
|
|
m_Threads = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
ScriptClass
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
ScriptClass::ScriptClass()
|
|
|
|
{
|
|
|
|
m_Self = NULL;
|
|
|
|
m_Script = NULL;
|
|
|
|
m_Threads = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
~ScriptClass
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
ScriptClass::~ScriptClass()
|
|
|
|
{
|
|
|
|
if (m_Script == NULL) {
|
2023-01-30 00:23:47 +01:00
|
|
|
throw ScriptException("Attempting to delete dead class.");
|
2023-01-29 20:59:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
KillThreads();
|
|
|
|
|
|
|
|
if (!m_Script->m_Filename)
|
|
|
|
{
|
|
|
|
// This is a temporary gamescript
|
|
|
|
delete m_Script;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
Archive
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
void ScriptClass::Archive(Archiver& arc)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
ArchiveInternal
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
void ScriptClass::ArchiveInternal(Archiver& arc)
|
|
|
|
{
|
|
|
|
Listener::Archive(arc);
|
|
|
|
|
|
|
|
arc.ArchiveObjectPosition(this);
|
|
|
|
arc.ArchiveSafePointer(&m_Self);
|
|
|
|
GameScript::Archive(arc, m_Script);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
ArchiveScript
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
void ScriptClass::ArchiveScript(Archiver& arc, ScriptClass** obj)
|
|
|
|
{
|
|
|
|
ScriptClass* scr;
|
|
|
|
ScriptVM* m_current;
|
|
|
|
ScriptThread* m_thread;
|
|
|
|
int num;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (arc.Saving())
|
|
|
|
{
|
|
|
|
scr = *obj;
|
|
|
|
scr->ArchiveInternal(arc);
|
|
|
|
|
|
|
|
num = 0;
|
|
|
|
for (m_current = scr->m_Threads; m_current != NULL; m_current = m_current->next)
|
|
|
|
num++;
|
|
|
|
|
|
|
|
arc.ArchiveInteger(&num);
|
|
|
|
|
|
|
|
for (m_current = scr->m_Threads; m_current != NULL; m_current = m_current->next)
|
|
|
|
m_current->m_Thread->ArchiveInternal(arc);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
scr = new ScriptClass();
|
|
|
|
scr->ArchiveInternal(arc);
|
|
|
|
|
|
|
|
arc.ArchiveInteger(&num);
|
|
|
|
|
|
|
|
for (i = 0; i < num; i++)
|
|
|
|
{
|
|
|
|
m_thread = new ScriptThread(scr, NULL);
|
|
|
|
m_thread->ArchiveInternal(arc);
|
|
|
|
}
|
|
|
|
|
|
|
|
*obj = scr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
ArchiveCodePos
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
void ScriptClass::ArchiveCodePos(Archiver& arc, unsigned char** codePos)
|
|
|
|
{
|
|
|
|
m_Script->ArchiveCodePos(arc, codePos);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
CreateThreadInternal
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
ScriptThread* ScriptClass::CreateThreadInternal(const ScriptVariable& label)
|
|
|
|
{
|
|
|
|
GameScript* scr;
|
|
|
|
ScriptThread* thread = NULL;
|
|
|
|
|
|
|
|
if (label.GetType() == VARIABLE_STRING || label.GetType() == VARIABLE_CONSTSTRING)
|
|
|
|
{
|
|
|
|
ScriptClass* scriptClass = Director.CurrentScriptClass();
|
|
|
|
scr = scriptClass->GetScript();
|
|
|
|
|
|
|
|
if (label.GetType() == VARIABLE_CONSTSTRING)
|
|
|
|
thread = Director.CreateScriptThread(scr, m_Self, label.constStringValue());
|
|
|
|
else
|
|
|
|
thread = Director.CreateScriptThread(scr, m_Self, label.stringValue());
|
|
|
|
}
|
|
|
|
else if (label.GetType() == VARIABLE_CONSTARRAY && label.arraysize() > 1)
|
|
|
|
{
|
|
|
|
ScriptVariable* script = label[1];
|
|
|
|
ScriptVariable* labelname = label[2];
|
|
|
|
|
|
|
|
if (script->GetType() == VARIABLE_CONSTSTRING)
|
|
|
|
scr = Director.GetGameScript(script->constStringValue());
|
|
|
|
else
|
|
|
|
scr = Director.GetGameScript(script->stringValue());
|
|
|
|
|
|
|
|
if (labelname->GetType() == VARIABLE_CONSTSTRING)
|
|
|
|
thread = Director.CreateScriptThread(scr, m_Self, labelname->constStringValue());
|
|
|
|
else
|
|
|
|
thread = Director.CreateScriptThread(scr, m_Self, labelname->stringValue());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ScriptError("ScriptClass::CreateThreadInternal: bad argument format");
|
|
|
|
}
|
|
|
|
|
|
|
|
return thread;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
CreateScriptInternal
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
ScriptThread* ScriptClass::CreateScriptInternal(const ScriptVariable& label)
|
|
|
|
{
|
|
|
|
GameScript* scr;
|
|
|
|
ScriptThread* thread = NULL;
|
|
|
|
|
|
|
|
if (label.GetType() == VARIABLE_STRING || label.GetType() == VARIABLE_CONSTSTRING)
|
|
|
|
{
|
|
|
|
if (label.GetType() == VARIABLE_CONSTSTRING)
|
|
|
|
thread = Director.CreateScriptThread(Director.GetGameScript(label.stringValue()), m_Self, "");
|
|
|
|
else
|
|
|
|
thread = Director.CreateScriptThread(Director.GetGameScript(label.constStringValue()), m_Self, "");
|
|
|
|
}
|
|
|
|
else if (label.GetType() == VARIABLE_CONSTARRAY && label.arraysize() > 1)
|
|
|
|
{
|
|
|
|
ScriptVariable* script = label[1];
|
|
|
|
ScriptVariable* labelname = label[2];
|
|
|
|
|
|
|
|
if (script->GetType() == VARIABLE_CONSTSTRING)
|
|
|
|
scr = Director.GetGameScript(script->constStringValue());
|
|
|
|
else
|
|
|
|
scr = Director.GetGameScript(script->stringValue());
|
|
|
|
|
|
|
|
if (labelname->GetType() == VARIABLE_CONSTSTRING)
|
|
|
|
thread = Director.CreateScriptThread(scr, m_Self, labelname->constStringValue());
|
|
|
|
else
|
|
|
|
thread = Director.CreateScriptThread(scr, m_Self, labelname->stringValue());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ScriptError("ScriptClass::CreateScriptInternal: bad label type '%s'", label.GetTypeName());
|
|
|
|
}
|
|
|
|
|
|
|
|
return thread;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
AddThread
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
void ScriptClass::AddThread(ScriptVM* m_ScriptVM)
|
|
|
|
{
|
|
|
|
m_ScriptVM->next = m_Threads;
|
|
|
|
m_Threads = m_ScriptVM;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
KillThreads
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
void ScriptClass::KillThreads()
|
|
|
|
{
|
|
|
|
if (!m_Threads) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ScriptVM* m_current;
|
|
|
|
ScriptVM* m_next;
|
|
|
|
|
|
|
|
m_current = m_Threads;
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
m_current->m_ScriptClass = NULL;
|
|
|
|
|
|
|
|
m_next = m_current->next;
|
|
|
|
delete m_current->m_Thread;
|
|
|
|
|
|
|
|
} while (m_current = m_next);
|
|
|
|
|
|
|
|
m_Threads = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
RemoveThread
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
void ScriptClass::RemoveThread(ScriptVM* m_ScriptVM)
|
|
|
|
{
|
|
|
|
if (m_Threads == m_ScriptVM)
|
|
|
|
{
|
|
|
|
m_Threads = m_ScriptVM->next;
|
|
|
|
|
|
|
|
if (m_Threads == NULL) {
|
|
|
|
delete this;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ScriptVM* m_current = m_Threads;
|
|
|
|
ScriptVM* i;
|
|
|
|
|
|
|
|
for (i = m_Threads->next; i != m_ScriptVM; i = i->next) {
|
|
|
|
m_current = i;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_current->next = i->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
Filename
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
str ScriptClass::Filename()
|
|
|
|
{
|
|
|
|
return m_Script->Filename();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
FindLabel
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
unsigned char* ScriptClass::FindLabel(str label)
|
|
|
|
{
|
|
|
|
return m_Script->m_State.FindLabel(label);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
FindLabel
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
unsigned char* ScriptClass::FindLabel(const_str label)
|
|
|
|
{
|
|
|
|
return m_Script->m_State.FindLabel(label);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
NearestLabel
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
const_str ScriptClass::NearestLabel(unsigned char* pos)
|
|
|
|
{
|
|
|
|
return m_Script->m_State.NearestLabel(pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
GetCatchStateScript
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
StateScript* ScriptClass::GetCatchStateScript(unsigned char* in, unsigned char*& out)
|
|
|
|
{
|
|
|
|
return m_Script->GetCatchStateScript(in, out);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
GetScript
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
GameScript* ScriptClass::GetScript()
|
|
|
|
{
|
|
|
|
return m_Script;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
GetSelf
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
Listener* ScriptClass::GetSelf()
|
|
|
|
{
|
|
|
|
return static_cast<Listener*>(m_Self.Pointer());
|
|
|
|
}
|
|
|
|
|
|
|
|
//====================
|
|
|
|
// ScriptVM
|
|
|
|
//====================
|
|
|
|
|
|
|
|
|
2023-01-30 17:11:44 +01:00
|
|
|
MEM_BlockAlloc<ScriptVM> ScriptVM_allocator;
|
2023-01-29 20:59:31 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
new ScriptVM
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
void* ScriptVM::operator new(size_t size)
|
|
|
|
{
|
|
|
|
return ScriptVM_allocator.Alloc();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
delete ptr
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
void ScriptVM::operator delete(void* ptr)
|
|
|
|
{
|
|
|
|
ScriptVM_allocator.Free(ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
ScriptVM
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
ScriptVM::ScriptVM(ScriptClass* scriptClass, unsigned char* pCodePos, ScriptThread* thread)
|
|
|
|
{
|
|
|
|
next = NULL;
|
|
|
|
|
|
|
|
m_Thread = thread;
|
|
|
|
m_ScriptClass = scriptClass;
|
|
|
|
|
|
|
|
m_Stack = NULL;
|
|
|
|
|
|
|
|
m_PrevCodePos = NULL;
|
|
|
|
m_CodePos = pCodePos;
|
|
|
|
|
|
|
|
state = STATE_RUNNING;
|
|
|
|
m_ThreadState = THREAD_RUNNING;
|
|
|
|
|
|
|
|
m_pOldData = NULL;
|
|
|
|
m_OldDataSize = 0;
|
|
|
|
|
|
|
|
m_bMarkStack = false;
|
|
|
|
m_StackPos = NULL;
|
|
|
|
|
|
|
|
m_bAllowContextSwitch = true;
|
|
|
|
|
|
|
|
localStackSize = m_ScriptClass->GetScript()->GetRequiredStackSize();
|
|
|
|
|
|
|
|
if (localStackSize <= 0) {
|
|
|
|
localStackSize = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
localStack = new ScriptVariable[localStackSize];
|
|
|
|
|
|
|
|
pTop = localStack;
|
|
|
|
|
|
|
|
m_ScriptClass->AddThread(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
~ScriptVM
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
ScriptVM::~ScriptVM()
|
|
|
|
{
|
|
|
|
fastEvent.data = m_pOldData;
|
|
|
|
fastEvent.dataSize = m_OldDataSize;
|
|
|
|
|
|
|
|
// clean-up the call stack
|
|
|
|
while (callStack.NumObjects())
|
|
|
|
{
|
|
|
|
LeaveFunction();
|
|
|
|
}
|
|
|
|
|
|
|
|
delete[] localStack;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
Archive
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
void ScriptVM::Archive(Archiver& arc)
|
|
|
|
{
|
|
|
|
int stack = 0;
|
|
|
|
|
|
|
|
if (arc.Saving())
|
|
|
|
{
|
|
|
|
if (m_Stack)
|
|
|
|
stack = m_Stack->m_Count;
|
|
|
|
|
|
|
|
arc.ArchiveInteger(&stack);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
arc.ArchiveInteger(&stack);
|
|
|
|
|
|
|
|
if (stack)
|
|
|
|
{
|
|
|
|
m_Stack = new ScriptStack;
|
|
|
|
m_Stack->m_Array = new ScriptVariable[stack];
|
|
|
|
m_Stack->m_Count = stack;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 1; i <= stack; i++)
|
|
|
|
{
|
|
|
|
m_Stack->m_Array[i].ArchiveInternal(arc);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_ReturnValue.ArchiveInternal(arc);
|
|
|
|
m_ScriptClass->ArchiveCodePos(arc, &m_PrevCodePos);
|
|
|
|
m_ScriptClass->ArchiveCodePos(arc, &m_CodePos);
|
|
|
|
arc.ArchiveByte(&state);
|
|
|
|
arc.ArchiveByte(&m_ThreadState);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
error
|
|
|
|
|
|
|
|
Triggers an error
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
void ScriptVM::error(const char* format, ...)
|
|
|
|
{
|
|
|
|
char buffer[4000];
|
|
|
|
va_list va;
|
|
|
|
|
|
|
|
va_start(va, format);
|
|
|
|
vsprintf(buffer, format, va);
|
|
|
|
va_end(va);
|
|
|
|
|
|
|
|
glbs.Printf("----------------------------------------------------------\n%s\n", buffer);
|
|
|
|
m_ReturnValue.setStringValue("$.INTERRUPTED");
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
executeCommand
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
void ScriptVM::executeCommand(Listener* listener, int iParamCount, int eventnum, bool bReturn)
|
|
|
|
{
|
|
|
|
Event ev;
|
|
|
|
ScriptVariable* var;
|
|
|
|
|
|
|
|
ev = Event(eventnum);
|
|
|
|
|
|
|
|
if (bReturn)
|
|
|
|
{
|
|
|
|
var = pTop;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
var = pTop + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ev.dataSize = iParamCount;
|
|
|
|
ev.data = new ScriptVariable[ev.dataSize];
|
|
|
|
ev.fromScript = true;
|
|
|
|
|
|
|
|
for (int i = 0; i < iParamCount; i++) {
|
|
|
|
ev.data[i] = var[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
listener->ProcessScriptEvent(ev);
|
|
|
|
|
|
|
|
if (ev.NumArgs() > iParamCount) {
|
|
|
|
*pTop = ev.GetValue(ev.NumArgs());
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
pTop->Clear();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
executeGetter
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
bool ScriptVM::executeGetter(Listener* listener, str& name)
|
|
|
|
{
|
|
|
|
Event ev;
|
|
|
|
int eventnum = Event::FindGetterEventNum(name);
|
|
|
|
|
|
|
|
if (eventnum && listener->classinfo()->GetDef(eventnum))
|
|
|
|
{
|
|
|
|
ev = Event(eventnum);
|
|
|
|
ev.fromScript = true;
|
|
|
|
|
|
|
|
if (listener->ProcessScriptEvent(ev))
|
|
|
|
{
|
|
|
|
|
|
|
|
if (ev.NumArgs() > 0) {
|
|
|
|
*pTop = ev.GetValue(ev.NumArgs());
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
pTop->Clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
eventnum = Event::FindSetterEventNum(name);
|
|
|
|
assert(!eventnum || !listener->classinfo()->GetDef(eventnum));
|
|
|
|
if (eventnum && listener->classinfo()->GetDef(eventnum))
|
|
|
|
{
|
|
|
|
ScriptError("Cannot get a write-only variable");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
executeSetter
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
bool ScriptVM::executeSetter(Listener* listener, str& name)
|
|
|
|
{
|
|
|
|
Event ev;
|
|
|
|
int eventnum = Event::FindSetterEventNum(name);
|
|
|
|
|
|
|
|
if (eventnum && listener->classinfo()->GetDef(eventnum))
|
|
|
|
{
|
|
|
|
ev = Event(eventnum);
|
|
|
|
ev.fromScript = true;
|
|
|
|
|
|
|
|
ev.AddValue(*pTop);
|
|
|
|
|
|
|
|
if (listener->ProcessScriptEvent(ev))
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
eventnum = Event::FindGetterEventNum(name);
|
|
|
|
if (eventnum && listener->classinfo()->GetDef(eventnum))
|
|
|
|
{
|
|
|
|
ScriptError("Cannot set a read-only variable");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
jump
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
void ScriptVM::jump(int offset)
|
|
|
|
{
|
|
|
|
m_CodePos += offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
jumpBool
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
void ScriptVM::jumpBool(int offset, bool booleanValue)
|
|
|
|
{
|
|
|
|
if (booleanValue)
|
|
|
|
{
|
|
|
|
jump(offset);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_CodePos += sizeof(unsigned int);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
loadTop
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
void ScriptVM::loadTop(Listener* listener, bool noTop)
|
|
|
|
{
|
|
|
|
int index;
|
|
|
|
|
|
|
|
index = *reinterpret_cast<int*>(m_CodePos);
|
|
|
|
m_CodePos += sizeof(unsigned int);
|
|
|
|
|
|
|
|
if (index != -1)
|
|
|
|
{
|
|
|
|
str& variable = Director.GetString(index);
|
|
|
|
|
|
|
|
if (!executeSetter(listener, variable))
|
|
|
|
{
|
|
|
|
listener->Vars()->SetVariable(variable, *pTop);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!noTop) {
|
|
|
|
pTop--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
storeTop
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
void ScriptVM::storeTop(Listener* listener, bool noTop)
|
|
|
|
{
|
|
|
|
str variable;
|
|
|
|
int index;
|
|
|
|
|
|
|
|
index = *reinterpret_cast<int*>(m_CodePos);
|
|
|
|
m_CodePos += sizeof(unsigned int);
|
|
|
|
|
|
|
|
if (index != -1)
|
|
|
|
{
|
|
|
|
variable = Director.GetString(index);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!noTop) {
|
|
|
|
pTop++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (index != -1 && !executeGetter(listener, variable))
|
|
|
|
{
|
|
|
|
*pTop = *listener->Vars()->GetOrCreateVariable(index);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
ProgBuffer
|
|
|
|
|
|
|
|
Returns the current program buffer
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
unsigned char* ScriptVM::ProgBuffer(void)
|
|
|
|
{
|
|
|
|
return m_CodePos;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
EnterFunction
|
|
|
|
|
|
|
|
Sets a new instruction pointer
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
void ScriptVM::EnterFunction(Event* ev)
|
|
|
|
{
|
|
|
|
ScriptCallStack* stack;
|
|
|
|
str label = ev->GetString(1);
|
|
|
|
|
|
|
|
SetFastData(ev->data + 1, ev->dataSize - 1);
|
|
|
|
|
|
|
|
unsigned char* codePos = m_ScriptClass->FindLabel(label);
|
|
|
|
|
|
|
|
if (!codePos)
|
|
|
|
{
|
|
|
|
ScriptError("ScriptVM::EnterFunction: label '%s' does not exist in '%s'.", label.c_str(), Filename().c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
stack = new ScriptCallStack;
|
|
|
|
|
|
|
|
stack->codePos = m_CodePos;
|
|
|
|
|
|
|
|
stack->pTop = pTop;
|
|
|
|
stack->returnValue = m_ReturnValue;
|
|
|
|
stack->localStack = localStack;
|
|
|
|
stack->m_Self = m_ScriptClass->GetSelf();
|
|
|
|
|
|
|
|
callStack.AddObject(stack);
|
|
|
|
|
|
|
|
m_CodePos = codePos;
|
|
|
|
|
|
|
|
localStack = new ScriptVariable[localStackSize];
|
|
|
|
|
|
|
|
pTop = localStack;
|
|
|
|
m_ReturnValue.Clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
LeaveFunction
|
|
|
|
|
|
|
|
Returns to the previous function
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
void ScriptVM::LeaveFunction()
|
|
|
|
{
|
|
|
|
int num = callStack.NumObjects();
|
|
|
|
|
|
|
|
if (num)
|
|
|
|
{
|
|
|
|
ScriptCallStack* stack = callStack.ObjectAt(num);
|
|
|
|
|
|
|
|
pTop = stack->pTop;
|
|
|
|
*pTop = m_ReturnValue;
|
|
|
|
|
|
|
|
m_CodePos = stack->codePos;
|
|
|
|
m_ReturnValue = stack->returnValue;
|
|
|
|
m_ScriptClass->m_Self = stack->m_Self;
|
|
|
|
|
|
|
|
delete[] localStack;
|
|
|
|
|
|
|
|
localStack = stack->localStack;
|
|
|
|
|
|
|
|
delete stack;
|
|
|
|
|
|
|
|
callStack.RemoveObjectAt(num);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
delete m_Thread;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
End
|
|
|
|
|
|
|
|
End with a return value
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
void ScriptVM::End(const ScriptVariable& returnValue)
|
|
|
|
{
|
|
|
|
m_ReturnValue.setPointer(returnValue);
|
|
|
|
|
|
|
|
LeaveFunction();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
End
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
void ScriptVM::End()
|
|
|
|
{
|
|
|
|
m_ReturnValue.ClearPointer();
|
|
|
|
|
|
|
|
LeaveFunction();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
Execute
|
|
|
|
|
|
|
|
Executes a program
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
void ScriptVM::Execute(ScriptVariable* data, int dataSize, str label)
|
|
|
|
{
|
|
|
|
unsigned char* opcode;
|
|
|
|
bool doneProcessing = false;
|
|
|
|
bool deleteThread = false;
|
|
|
|
bool eventCalled = false;
|
|
|
|
|
|
|
|
ScriptVariable* a;
|
|
|
|
ScriptVariable* b;
|
|
|
|
ScriptVariable* c;
|
|
|
|
|
|
|
|
int index, iParamCount;
|
|
|
|
|
|
|
|
Listener* listener;
|
|
|
|
|
|
|
|
Event ev;
|
|
|
|
Event* ev2;
|
|
|
|
ScriptVariable* var = NULL;
|
|
|
|
|
|
|
|
static str str_null = "";
|
|
|
|
str& value = str_null;
|
|
|
|
|
|
|
|
ConSimple* targetList;
|
|
|
|
|
|
|
|
if (label != "")
|
|
|
|
{
|
|
|
|
// Throw if label is not found
|
|
|
|
if (!(m_CodePos = m_ScriptClass->FindLabel(label)))
|
|
|
|
{
|
|
|
|
ScriptError("ScriptVM::Execute: label '%s' does not exist in '%s'.", label.c_str(), Filename().c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Director.stackCount >= MAX_STACK_DEPTH)
|
|
|
|
{
|
|
|
|
state = STATE_EXECUTION;
|
|
|
|
|
|
|
|
ScriptException::next_abort = -1;
|
|
|
|
ScriptException exc("stack overflow");
|
|
|
|
|
|
|
|
throw exc;
|
|
|
|
}
|
|
|
|
|
|
|
|
Director.stackCount++;
|
|
|
|
|
|
|
|
if (dataSize)
|
|
|
|
{
|
|
|
|
SetFastData(data, dataSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
state = STATE_RUNNING;
|
|
|
|
|
|
|
|
Director.cmdTime = glbs.Milliseconds();
|
|
|
|
Director.cmdCount = 0;
|
|
|
|
|
|
|
|
while (!doneProcessing && state == STATE_RUNNING)
|
|
|
|
{
|
|
|
|
m_PrevCodePos = m_CodePos;
|
|
|
|
|
|
|
|
Director.cmdCount++;
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
if (Director.cmdCount > 9999 && glbs.Milliseconds() - Director.cmdTime > Director.maxTime)
|
|
|
|
{
|
|
|
|
if (level.m_LoopProtection)
|
|
|
|
{
|
|
|
|
Director.cmdTime = glbs.Milliseconds();
|
|
|
|
|
|
|
|
deleteThread = true;
|
|
|
|
state = STATE_EXECUTION;
|
|
|
|
|
|
|
|
if (level.m_LoopDrop) {
|
|
|
|
ScriptException::next_abort = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
ScriptError("Command overflow. Possible infinite loop in thread.\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
VM_DPrintf("Update of script position - This is not an error.\n");
|
|
|
|
VM_DPrintf("=================================================\n");
|
|
|
|
m_ScriptClass->GetScript()->PrintSourcePos(opcode, true);
|
|
|
|
VM_DPrintf("=================================================\n");
|
|
|
|
|
|
|
|
Director.cmdCount = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!m_bMarkStack)
|
|
|
|
{
|
|
|
|
assert(pTop >= localStack && pTop < localStack + localStackSize);
|
|
|
|
if (pTop < localStack)
|
|
|
|
{
|
|
|
|
deleteThread = true;
|
|
|
|
state = STATE_EXECUTION;
|
|
|
|
|
|
|
|
error("VM stack error. Negative stack value %d.\n", pTop - localStack);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (pTop >= localStack + localStackSize)
|
|
|
|
{
|
|
|
|
deleteThread = true;
|
|
|
|
state = STATE_EXECUTION;
|
|
|
|
|
|
|
|
error("VM stack error. Exceeded the maximum stack size %d.\n", localStackSize);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
index = 0;
|
|
|
|
eventCalled = false;
|
|
|
|
|
|
|
|
opcode = m_CodePos++;
|
|
|
|
switch (*opcode)
|
|
|
|
{
|
|
|
|
case OP_BIN_BITWISE_AND:
|
|
|
|
a = pTop--;
|
|
|
|
b = pTop;
|
|
|
|
|
|
|
|
*b &= *a;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_BIN_BITWISE_OR:
|
|
|
|
a = pTop--;
|
|
|
|
b = pTop;
|
|
|
|
|
|
|
|
*b |= *a;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_BIN_BITWISE_EXCL_OR:
|
|
|
|
a = pTop--;
|
|
|
|
b = pTop;
|
|
|
|
|
|
|
|
*b ^= *a;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_BIN_EQUALITY:
|
|
|
|
a = pTop--;
|
|
|
|
b = pTop;
|
|
|
|
|
|
|
|
b->setIntValue(*b == *a);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_BIN_INEQUALITY:
|
|
|
|
a = pTop--;
|
|
|
|
b = pTop;
|
|
|
|
|
|
|
|
b->setIntValue(*b != *a);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_BIN_GREATER_THAN:
|
|
|
|
a = pTop--;
|
|
|
|
b = pTop;
|
|
|
|
|
|
|
|
b->greaterthan(*a);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_BIN_GREATER_THAN_OR_EQUAL:
|
|
|
|
a = pTop--;
|
|
|
|
b = pTop;
|
|
|
|
|
|
|
|
b->greaterthanorequal(*a);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_BIN_LESS_THAN:
|
|
|
|
a = pTop--;
|
|
|
|
b = pTop;
|
|
|
|
|
|
|
|
b->lessthan(*a);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_BIN_LESS_THAN_OR_EQUAL:
|
|
|
|
a = pTop--;
|
|
|
|
b = pTop;
|
|
|
|
|
|
|
|
b->lessthanorequal(*a);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_BIN_PLUS:
|
|
|
|
a = pTop--;
|
|
|
|
b = pTop;
|
|
|
|
|
|
|
|
*b += *a;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_BIN_MINUS:
|
|
|
|
a = pTop--;
|
|
|
|
b = pTop;
|
|
|
|
|
|
|
|
*b -= *a;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_BIN_MULTIPLY:
|
|
|
|
a = pTop--;
|
|
|
|
b = pTop;
|
|
|
|
|
|
|
|
*b *= *a;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_BIN_DIVIDE:
|
|
|
|
a = pTop--;
|
|
|
|
b = pTop;
|
|
|
|
|
|
|
|
*b /= *a;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_BIN_PERCENTAGE:
|
|
|
|
a = pTop--;
|
|
|
|
b = pTop;
|
|
|
|
|
|
|
|
*b %= *a;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_BIN_SHIFT_LEFT:
|
|
|
|
a = pTop--;
|
|
|
|
b = pTop;
|
|
|
|
|
|
|
|
*b <<= *a;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_BIN_SHIFT_RIGHT:
|
|
|
|
a = pTop--;
|
|
|
|
b = pTop;
|
|
|
|
|
|
|
|
*b >>= *a;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_BOOL_JUMP_FALSE4:
|
|
|
|
jumpBool(*reinterpret_cast<unsigned int*>(m_CodePos) + sizeof(unsigned int), !pTop->m_data.intValue);
|
|
|
|
|
|
|
|
pTop--;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_BOOL_JUMP_TRUE4:
|
|
|
|
jumpBool(*reinterpret_cast<unsigned int*>(m_CodePos) + sizeof(unsigned int), pTop->m_data.intValue ? true : false);
|
|
|
|
|
|
|
|
pTop--;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_VAR_JUMP_FALSE4:
|
|
|
|
jumpBool(*reinterpret_cast<unsigned int*>(m_CodePos) + sizeof(unsigned int), !pTop->booleanValue());
|
|
|
|
|
|
|
|
pTop--;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_VAR_JUMP_TRUE4:
|
|
|
|
jumpBool(*reinterpret_cast<unsigned int*>(m_CodePos) + sizeof(unsigned int), pTop->booleanValue());
|
|
|
|
|
|
|
|
pTop--;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_BOOL_LOGICAL_AND:
|
|
|
|
if (pTop->m_data.intValue)
|
|
|
|
{
|
|
|
|
pTop--;
|
|
|
|
m_CodePos += sizeof(unsigned int);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_CodePos += *reinterpret_cast<unsigned int*>(m_CodePos) + sizeof(unsigned int);
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_BOOL_LOGICAL_OR:
|
|
|
|
if (!pTop->m_data.intValue)
|
|
|
|
{
|
|
|
|
pTop--;
|
|
|
|
m_CodePos += sizeof(unsigned int);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
m_CodePos += *reinterpret_cast<unsigned int*>(m_CodePos) + sizeof(unsigned int);
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_VAR_LOGICAL_AND:
|
|
|
|
if (pTop->booleanValue())
|
|
|
|
{
|
|
|
|
pTop--;
|
|
|
|
m_CodePos += sizeof(unsigned int);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pTop->SetFalse();
|
|
|
|
m_CodePos += *reinterpret_cast<unsigned int*>(m_CodePos) + sizeof(unsigned int);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_VAR_LOGICAL_OR:
|
|
|
|
if (!pTop->booleanValue())
|
|
|
|
{
|
|
|
|
pTop--;
|
|
|
|
m_CodePos += sizeof(unsigned int);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pTop->SetTrue();
|
|
|
|
m_CodePos += *reinterpret_cast<unsigned int*>(m_CodePos) + sizeof(unsigned int);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_BOOL_STORE_FALSE:
|
|
|
|
pTop++;
|
|
|
|
pTop->SetFalse();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_BOOL_STORE_TRUE:
|
|
|
|
pTop++;
|
|
|
|
pTop->SetTrue();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_BOOL_UN_NOT:
|
|
|
|
pTop->m_data.intValue = (pTop->m_data.intValue == 0);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_CALC_VECTOR:
|
|
|
|
c = pTop--;
|
|
|
|
b = pTop--;
|
|
|
|
a = pTop;
|
|
|
|
|
|
|
|
pTop->setVectorValue(Vector(a->floatValue(), b->floatValue(), c->floatValue()));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_EXEC_CMD0:
|
|
|
|
iParamCount = 0;
|
|
|
|
goto __execCmd;
|
|
|
|
|
|
|
|
case OP_EXEC_CMD1:
|
|
|
|
iParamCount = 1;
|
|
|
|
goto __execCmd;
|
|
|
|
|
|
|
|
case OP_EXEC_CMD2:
|
|
|
|
iParamCount = 2;
|
|
|
|
goto __execCmd;
|
|
|
|
|
|
|
|
case OP_EXEC_CMD3:
|
|
|
|
iParamCount = 3;
|
|
|
|
goto __execCmd;
|
|
|
|
|
|
|
|
case OP_EXEC_CMD4:
|
|
|
|
iParamCount = 4;
|
|
|
|
goto __execCmd;
|
|
|
|
|
|
|
|
case OP_EXEC_CMD5:
|
|
|
|
iParamCount = 5;
|
|
|
|
goto __execCmd;
|
|
|
|
|
|
|
|
case OP_EXEC_CMD_COUNT1:
|
|
|
|
iParamCount = *m_CodePos++;
|
|
|
|
|
|
|
|
__execCmd:
|
|
|
|
index = *reinterpret_cast<unsigned int*>(m_CodePos);
|
|
|
|
|
|
|
|
m_CodePos += sizeof(unsigned int);
|
|
|
|
|
|
|
|
pTop -= iParamCount;
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
executeCommand(m_Thread, iParamCount, index);
|
|
|
|
}
|
|
|
|
catch (ScriptException& exc)
|
|
|
|
{
|
|
|
|
throw exc;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_EXEC_CMD_METHOD0:
|
|
|
|
iParamCount = 0;
|
|
|
|
goto __execCmdMethod;
|
|
|
|
|
|
|
|
case OP_EXEC_CMD_METHOD1:
|
|
|
|
iParamCount = 1;
|
|
|
|
goto __execCmdMethod;
|
|
|
|
|
|
|
|
case OP_EXEC_CMD_METHOD2:
|
|
|
|
iParamCount = 2;
|
|
|
|
goto __execCmdMethod;
|
|
|
|
|
|
|
|
case OP_EXEC_CMD_METHOD3:
|
|
|
|
iParamCount = 3;
|
|
|
|
goto __execCmdMethod;
|
|
|
|
|
|
|
|
case OP_EXEC_CMD_METHOD4:
|
|
|
|
iParamCount = 4;
|
|
|
|
goto __execCmdMethod;
|
|
|
|
|
|
|
|
case OP_EXEC_CMD_METHOD5:
|
|
|
|
iParamCount = 5;
|
|
|
|
goto __execCmdMethod;
|
|
|
|
|
|
|
|
__execCmdMethod:
|
|
|
|
m_CodePos--;
|
|
|
|
goto __execCmdMethodInternal;
|
|
|
|
|
|
|
|
case OP_EXEC_CMD_METHOD_COUNT1:
|
|
|
|
iParamCount = *m_CodePos;
|
|
|
|
|
|
|
|
__execCmdMethodInternal:
|
|
|
|
a = pTop--;
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
index = *reinterpret_cast<unsigned int*>(m_CodePos + sizeof(byte));
|
|
|
|
|
|
|
|
pTop -= iParamCount;
|
|
|
|
|
|
|
|
if (a->arraysize() < 0)
|
|
|
|
{
|
|
|
|
ScriptError("command '%s' applied to NIL", Event::GetEventName(index).c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
ScriptVariable array = *a;
|
|
|
|
Listener* listener;
|
|
|
|
|
|
|
|
array.CastConstArrayValue();
|
|
|
|
|
|
|
|
for (int i = array.arraysize(); i > 0; i--)
|
|
|
|
{
|
|
|
|
if (!(listener = array[i]->listenerValue()))
|
|
|
|
{
|
|
|
|
ScriptError("command '%s' applied to NULL listener", Event::GetEventName(index).c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
executeCommand(listener, iParamCount, index);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (ScriptException& exc)
|
|
|
|
{
|
|
|
|
m_CodePos += sizeof(byte) + sizeof(unsigned int);
|
|
|
|
|
|
|
|
throw exc;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_CodePos += sizeof(byte) + sizeof(unsigned int);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_EXEC_METHOD0:
|
|
|
|
iParamCount = 0;
|
|
|
|
goto __execMethod;
|
|
|
|
|
|
|
|
case OP_EXEC_METHOD1:
|
|
|
|
iParamCount = 1;
|
|
|
|
goto __execMethod;
|
|
|
|
|
|
|
|
case OP_EXEC_METHOD2:
|
|
|
|
iParamCount = 2;
|
|
|
|
goto __execMethod;
|
|
|
|
|
|
|
|
case OP_EXEC_METHOD3:
|
|
|
|
iParamCount = 3;
|
|
|
|
goto __execMethod;
|
|
|
|
|
|
|
|
case OP_EXEC_METHOD4:
|
|
|
|
iParamCount = 4;
|
|
|
|
goto __execMethod;
|
|
|
|
|
|
|
|
case OP_EXEC_METHOD5:
|
|
|
|
iParamCount = 5;
|
|
|
|
|
|
|
|
__execMethod:
|
|
|
|
m_CodePos--;
|
|
|
|
goto __execMethodInternal;
|
|
|
|
|
|
|
|
case OP_EXEC_METHOD_COUNT1:
|
|
|
|
iParamCount = *m_CodePos;
|
|
|
|
|
|
|
|
__execMethodInternal:
|
|
|
|
a = pTop--;
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
index = *reinterpret_cast<unsigned int*>(m_CodePos + sizeof(byte));
|
|
|
|
|
|
|
|
pTop -= iParamCount;
|
|
|
|
pTop++; // push the return value
|
|
|
|
|
|
|
|
Listener* listener = a->listenerValue();
|
|
|
|
|
|
|
|
if (!listener)
|
|
|
|
{
|
|
|
|
ScriptError("command '%s' applied to NULL listener", Event::GetEventName(index).c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
executeCommand(listener, iParamCount, index, true);
|
|
|
|
}
|
|
|
|
catch (ScriptException& exc)
|
|
|
|
{
|
|
|
|
m_CodePos += sizeof(byte) + sizeof(unsigned int);
|
|
|
|
|
|
|
|
throw exc;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_CodePos += sizeof(byte) + sizeof(unsigned int);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_FUNC:
|
|
|
|
ev.Clear();
|
|
|
|
|
|
|
|
if (!*m_CodePos++)
|
|
|
|
{
|
|
|
|
str& label = Director.GetString(*reinterpret_cast<unsigned int*>(m_CodePos));
|
|
|
|
|
|
|
|
m_CodePos += sizeof(unsigned int);
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
listener = pTop->listenerValue();
|
|
|
|
|
|
|
|
if (!listener)
|
|
|
|
{
|
|
|
|
ScriptError("function '%s' applied to NULL listener", label.c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (ScriptException& exc)
|
|
|
|
{
|
|
|
|
pTop -= *m_CodePos++;
|
|
|
|
|
|
|
|
throw exc;
|
|
|
|
}
|
|
|
|
|
|
|
|
pTop--;
|
|
|
|
|
|
|
|
ev.AddString(label);
|
|
|
|
|
|
|
|
int params = *m_CodePos++;
|
|
|
|
|
|
|
|
var = pTop;
|
|
|
|
pTop -= params;
|
|
|
|
|
|
|
|
for (int i = 0; i < params; var++, i++) {
|
|
|
|
ev.AddValue(*var);
|
|
|
|
}
|
|
|
|
|
|
|
|
pTop++;
|
|
|
|
EnterFunction(&ev);
|
|
|
|
|
|
|
|
m_ScriptClass->m_Self = listener;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
str filename, label;
|
|
|
|
|
|
|
|
filename = Director.GetString(*reinterpret_cast<unsigned int*>(m_CodePos));
|
|
|
|
m_CodePos += sizeof(unsigned int);
|
|
|
|
label = Director.GetString(*reinterpret_cast<unsigned int*>(m_CodePos));
|
|
|
|
m_CodePos += sizeof(unsigned int);
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
listener = pTop->listenerValue();
|
|
|
|
|
|
|
|
if (!listener)
|
|
|
|
{
|
|
|
|
ScriptError("function '%s' in '%s' applied to NULL listener", label.c_str(), filename.c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (ScriptException& exc)
|
|
|
|
{
|
|
|
|
pTop -= *m_CodePos++;
|
|
|
|
|
|
|
|
throw exc;
|
|
|
|
}
|
|
|
|
|
|
|
|
pTop--;
|
|
|
|
|
|
|
|
ScriptVariable constarray;
|
|
|
|
ScriptVariable* pVar = new ScriptVariable[2];
|
|
|
|
|
|
|
|
pVar[0].setStringValue(filename);
|
|
|
|
pVar[1].setStringValue(label);
|
|
|
|
|
|
|
|
constarray.setConstArrayValue(pVar, 2);
|
|
|
|
|
|
|
|
delete[] pVar;
|
|
|
|
|
|
|
|
ev2 = new Event(EV_Listener_WaitCreateReturnThread);
|
|
|
|
ev2->AddValue(constarray);
|
|
|
|
|
|
|
|
int params = *m_CodePos++;
|
|
|
|
|
|
|
|
var = pTop;
|
|
|
|
pTop -= params;
|
|
|
|
|
|
|
|
for (int i = 0; i < params; var++, i++) {
|
|
|
|
ev2->AddValue(*var);
|
|
|
|
}
|
|
|
|
|
|
|
|
pTop++;
|
|
|
|
*pTop = listener->ProcessEventReturn(ev2);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_JUMP4:
|
|
|
|
m_CodePos += *reinterpret_cast<int*>(m_CodePos) + sizeof(unsigned int);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_JUMP_BACK4:
|
|
|
|
m_CodePos -= *reinterpret_cast<int*>(m_CodePos);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_LOAD_ARRAY_VAR:
|
|
|
|
a = pTop--;
|
|
|
|
b = pTop--;
|
|
|
|
c = pTop--;
|
|
|
|
|
|
|
|
b->setArrayAt(*a, *c);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_LOAD_FIELD_VAR:
|
|
|
|
a = pTop--;
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
listener = a->listenerValue();
|
|
|
|
|
|
|
|
if (listener == NULL)
|
|
|
|
{
|
|
|
|
value = Director.GetString(*reinterpret_cast<int*>(m_CodePos));
|
|
|
|
ScriptError("Field '%s' applied to NULL listener", value.c_str());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
eventCalled = true;
|
|
|
|
loadTop(listener);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (ScriptException& exc)
|
|
|
|
{
|
|
|
|
pTop--;
|
|
|
|
|
|
|
|
if (!eventCalled) {
|
|
|
|
m_CodePos += sizeof(unsigned int);
|
|
|
|
}
|
|
|
|
|
|
|
|
throw exc;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_LOAD_CONST_ARRAY1:
|
|
|
|
index = *reinterpret_cast<short*>(m_CodePos);
|
|
|
|
m_CodePos += sizeof(short);
|
|
|
|
|
|
|
|
pTop -= index - 1;
|
|
|
|
pTop->setConstArrayValue(pTop, index);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_LOAD_GAME_VAR:
|
|
|
|
loadTop(&game);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_LOAD_GROUP_VAR:
|
|
|
|
loadTop(m_ScriptClass);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_LOAD_LEVEL_VAR:
|
|
|
|
loadTop(&level);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_LOAD_LOCAL_VAR:
|
|
|
|
loadTop(m_Thread);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_LOAD_OWNER_VAR:
|
|
|
|
if (!m_ScriptClass->m_Self)
|
|
|
|
{
|
|
|
|
pTop--;
|
|
|
|
m_CodePos += sizeof(unsigned int);
|
|
|
|
ScriptError("self is NULL");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!m_ScriptClass->m_Self->GetScriptOwner())
|
|
|
|
{
|
|
|
|
pTop--;
|
|
|
|
m_CodePos += sizeof(unsigned int);
|
|
|
|
ScriptError("self.owner is NULL");
|
|
|
|
}
|
|
|
|
|
|
|
|
loadTop(m_ScriptClass->m_Self->GetScriptOwner());
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_LOAD_PARM_VAR:
|
|
|
|
loadTop(&parm);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_LOAD_SELF_VAR:
|
|
|
|
if (!m_ScriptClass->m_Self)
|
|
|
|
{
|
|
|
|
pTop--;
|
|
|
|
m_CodePos += sizeof(unsigned int);
|
|
|
|
ScriptError("self is NULL");
|
|
|
|
}
|
|
|
|
|
|
|
|
loadTop(m_ScriptClass->m_Self);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_LOAD_STORE_GAME_VAR:
|
|
|
|
loadTop(&game, true);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_LOAD_STORE_GROUP_VAR:
|
|
|
|
loadTop(m_ScriptClass, true);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_LOAD_STORE_LEVEL_VAR:
|
|
|
|
loadTop(&level, true);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_LOAD_STORE_LOCAL_VAR:
|
|
|
|
loadTop(m_Thread, true);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_LOAD_STORE_OWNER_VAR:
|
|
|
|
if (!m_ScriptClass->m_Self)
|
|
|
|
{
|
|
|
|
m_CodePos += sizeof(unsigned int);
|
|
|
|
ScriptError("self is NULL");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!m_ScriptClass->m_Self->GetScriptOwner())
|
|
|
|
{
|
|
|
|
m_CodePos += sizeof(unsigned int);
|
|
|
|
ScriptError("self.owner is NULL");
|
|
|
|
}
|
|
|
|
|
|
|
|
loadTop(m_ScriptClass->m_Self->GetScriptOwner(), true);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_LOAD_STORE_PARM_VAR:
|
|
|
|
loadTop(&parm, true);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_LOAD_STORE_SELF_VAR:
|
|
|
|
if (!m_ScriptClass->m_Self)
|
|
|
|
{
|
|
|
|
ScriptError("self is NULL");
|
|
|
|
}
|
|
|
|
|
|
|
|
loadTop(m_ScriptClass->m_Self, true);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_MARK_STACK_POS:
|
|
|
|
m_StackPos = pTop;
|
|
|
|
m_bMarkStack = true;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE_PARAM:
|
|
|
|
if (fastEvent.dataSize)
|
|
|
|
{
|
|
|
|
pTop = fastEvent.data++;
|
|
|
|
fastEvent.dataSize--;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pTop = m_StackPos + 1;
|
|
|
|
pTop->Clear();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_RESTORE_STACK_POS:
|
|
|
|
pTop = m_StackPos;
|
|
|
|
m_bMarkStack = false;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE_ARRAY:
|
|
|
|
pTop--;
|
|
|
|
pTop->evalArrayAt(*(pTop + 1));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE_ARRAY_REF:
|
|
|
|
pTop--;
|
|
|
|
pTop->setArrayRefValue(*(pTop + 1));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE_FIELD_REF:
|
|
|
|
case OP_STORE_FIELD:
|
|
|
|
try
|
|
|
|
{
|
|
|
|
value = Director.GetString(*reinterpret_cast<int*>(m_CodePos));
|
|
|
|
|
|
|
|
listener = pTop->listenerValue();
|
|
|
|
|
|
|
|
if (listener == NULL)
|
|
|
|
{
|
|
|
|
ScriptError("Field '%s' applied to NULL listener", value.c_str());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
eventCalled = true;
|
|
|
|
storeTop(listener, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (*opcode == OP_STORE_FIELD_REF)
|
|
|
|
{
|
|
|
|
pTop->setRefValue(listener->vars->GetOrCreateVariable(value));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (ScriptException& exc)
|
|
|
|
{
|
|
|
|
if (*opcode == OP_STORE_FIELD_REF)
|
|
|
|
{
|
|
|
|
pTop->setRefValue(pTop);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!eventCalled) {
|
|
|
|
m_CodePos += sizeof(unsigned int);
|
|
|
|
}
|
|
|
|
|
|
|
|
throw exc;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE_FLOAT:
|
|
|
|
pTop++;
|
|
|
|
pTop->setFloatValue(*reinterpret_cast<float*>(m_CodePos));
|
|
|
|
|
|
|
|
m_CodePos += sizeof(float);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE_INT0:
|
|
|
|
pTop++;
|
|
|
|
pTop->setIntValue(0);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE_INT1:
|
|
|
|
pTop++;
|
|
|
|
pTop->setIntValue(*m_CodePos++);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE_INT2:
|
|
|
|
pTop++;
|
|
|
|
pTop->setIntValue(*reinterpret_cast<short*>(m_CodePos));
|
|
|
|
|
|
|
|
m_CodePos += sizeof(short);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE_INT3:
|
|
|
|
pTop++;
|
|
|
|
pTop->setIntValue(*reinterpret_cast<short3*>(m_CodePos));
|
|
|
|
|
|
|
|
m_CodePos += sizeof(short3);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE_INT4:
|
|
|
|
pTop++;
|
|
|
|
pTop->setIntValue(*reinterpret_cast<int*>(m_CodePos));
|
|
|
|
|
|
|
|
m_CodePos += sizeof(int);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE_GAME_VAR:
|
|
|
|
storeTop(&game);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE_GROUP_VAR:
|
|
|
|
storeTop(m_ScriptClass);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE_LEVEL_VAR:
|
|
|
|
storeTop(&level);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE_LOCAL_VAR:
|
|
|
|
storeTop(m_Thread);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE_OWNER_VAR:
|
|
|
|
if (!m_ScriptClass->m_Self)
|
|
|
|
{
|
|
|
|
pTop++;
|
|
|
|
m_CodePos += sizeof(unsigned int);
|
|
|
|
ScriptError("self is NULL");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!m_ScriptClass->m_Self->GetScriptOwner())
|
|
|
|
{
|
|
|
|
pTop++;
|
|
|
|
m_CodePos += sizeof(unsigned int);
|
|
|
|
ScriptError("self.owner is NULL");
|
|
|
|
}
|
|
|
|
|
|
|
|
storeTop(m_ScriptClass->m_Self->GetScriptOwner());
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE_PARM_VAR:
|
|
|
|
storeTop(&parm);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE_SELF_VAR:
|
|
|
|
if (!m_ScriptClass->m_Self)
|
|
|
|
{
|
|
|
|
pTop++;
|
|
|
|
m_CodePos += sizeof(unsigned int);
|
|
|
|
ScriptError("self is NULL");
|
|
|
|
}
|
|
|
|
|
|
|
|
storeTop(m_ScriptClass->m_Self);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE_GAME:
|
|
|
|
pTop++;
|
|
|
|
pTop->setListenerValue(&game);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE_GROUP:
|
|
|
|
pTop++;
|
|
|
|
pTop->setListenerValue(m_ScriptClass);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE_LEVEL:
|
|
|
|
pTop++;
|
|
|
|
pTop->setListenerValue(&level);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE_LOCAL:
|
|
|
|
pTop++;
|
|
|
|
pTop->setListenerValue(m_Thread);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE_OWNER:
|
|
|
|
pTop++;
|
|
|
|
|
|
|
|
if (!m_ScriptClass->m_Self)
|
|
|
|
{
|
|
|
|
pTop++;
|
|
|
|
ScriptError("self is NULL");
|
|
|
|
}
|
|
|
|
|
|
|
|
pTop->setListenerValue(m_ScriptClass->m_Self->GetScriptOwner());
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE_PARM:
|
|
|
|
pTop++;
|
|
|
|
pTop->setListenerValue(&parm);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE_SELF:
|
|
|
|
pTop++;
|
|
|
|
pTop->setListenerValue(m_ScriptClass->m_Self);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE_NIL:
|
|
|
|
pTop++;
|
|
|
|
pTop->Clear();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE_NULL:
|
|
|
|
pTop++;
|
|
|
|
pTop->setListenerValue(NULL);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE_STRING:
|
|
|
|
pTop++;
|
|
|
|
pTop->setConstStringValue(*reinterpret_cast<unsigned int*>(m_CodePos));
|
|
|
|
|
|
|
|
m_CodePos += sizeof(unsigned int);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_STORE_VECTOR:
|
|
|
|
pTop++;
|
|
|
|
pTop->setVectorValue(*reinterpret_cast<Vector*>(m_CodePos));
|
|
|
|
|
|
|
|
m_CodePos += sizeof(Vector);
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_SWITCH:
|
|
|
|
if (!Switch(*reinterpret_cast<StateScript**>(m_CodePos), *pTop))
|
|
|
|
{
|
|
|
|
m_CodePos += sizeof(StateScript*);
|
|
|
|
}
|
|
|
|
|
|
|
|
pTop--;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_UN_CAST_BOOLEAN:
|
|
|
|
pTop->CastBoolean();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_UN_COMPLEMENT:
|
|
|
|
pTop->complement();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_UN_MINUS:
|
|
|
|
pTop->minus();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_UN_DEC:
|
|
|
|
(*pTop)--;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_UN_INC:
|
|
|
|
(*pTop)++;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_UN_SIZE:
|
|
|
|
pTop->setIntValue((int)pTop->size());
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_UN_TARGETNAME:
|
|
|
|
targetList = world->GetExistingTargetList(pTop->stringValue());
|
|
|
|
|
|
|
|
if (!targetList)
|
|
|
|
{
|
|
|
|
value = pTop->stringValue();
|
|
|
|
|
|
|
|
pTop->setListenerValue(NULL);
|
|
|
|
|
|
|
|
if (*m_CodePos >= OP_BIN_EQUALITY && *m_CodePos <= OP_BIN_GREATER_THAN_OR_EQUAL || *m_CodePos >= OP_BOOL_UN_NOT && *m_CodePos <= OP_UN_CAST_BOOLEAN) {
|
|
|
|
ScriptError("Targetname '%s' does not exist.", value.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (targetList->NumObjects() == 1)
|
|
|
|
{
|
|
|
|
pTop->setListenerValue(targetList->ObjectAt(1));
|
|
|
|
}
|
|
|
|
else if (targetList->NumObjects() > 1)
|
|
|
|
{
|
|
|
|
pTop->setContainerValue((Container< SafePtr< Listener > > *)targetList);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
value = pTop->stringValue();
|
|
|
|
|
|
|
|
pTop->setListenerValue(NULL);
|
|
|
|
|
|
|
|
if (*m_CodePos >= OP_BIN_EQUALITY && *m_CodePos <= OP_BIN_GREATER_THAN_OR_EQUAL || *m_CodePos >= OP_BOOL_UN_NOT && *m_CodePos <= OP_UN_CAST_BOOLEAN) {
|
|
|
|
ScriptError("Targetname '%s' does not exist.", value.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_VAR_UN_NOT:
|
|
|
|
pTop->setIntValue(pTop->booleanValue());
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_DONE:
|
|
|
|
End();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case OP_NOP:
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
if (*opcode < OP_MAX)
|
|
|
|
{
|
|
|
|
glbs.DPrintf("unknown opcode %d ('%s')\n", *opcode, OpcodeName(*opcode));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
glbs.DPrintf("unknown opcode %d\n", *opcode);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (ScriptException& exc)
|
|
|
|
{
|
|
|
|
HandleScriptException(exc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Director.stackCount--;
|
|
|
|
|
|
|
|
if (deleteThread || state == STATE_WAITING)
|
|
|
|
{
|
|
|
|
delete m_Thread;
|
|
|
|
}
|
|
|
|
else if (state == STATE_SUSPENDED)
|
|
|
|
{
|
|
|
|
state = STATE_EXECUTION;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (state == STATE_DESTROYED)
|
|
|
|
{
|
|
|
|
delete this;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
HandleScriptException
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
void ScriptVM::HandleScriptException(ScriptException& exc)
|
|
|
|
{
|
|
|
|
if (m_ScriptClass)
|
|
|
|
{
|
|
|
|
m_ScriptClass->GetScript()->PrintSourcePos(m_PrevCodePos, true);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
glbs.DPrintf("unknown source pos");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (exc.bAbort)
|
|
|
|
{
|
|
|
|
ScriptException e(exc.string);
|
|
|
|
|
|
|
|
e.bAbort = exc.bAbort;
|
|
|
|
e.bIsForAnim = exc.bIsForAnim;
|
|
|
|
|
|
|
|
state = STATE_EXECUTION;
|
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
|
|
|
|
glbs.Printf("^~^~^ Script Error : %s\n\n", exc.string.c_str());
|
|
|
|
|
|
|
|
if (m_ScriptClass->GetScript()->ScriptCheck())
|
|
|
|
{
|
|
|
|
if (g_scriptcheck->integer != 2 || !exc.bIsForAnim)
|
|
|
|
{
|
|
|
|
ScriptException e("Script check failed");
|
|
|
|
|
|
|
|
e.bAbort = exc.bAbort;
|
|
|
|
e.bIsForAnim = exc.bIsForAnim;
|
|
|
|
|
|
|
|
state = STATE_EXECUTION;
|
|
|
|
throw e;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Director.cmdCount += 100;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
SetFastData
|
|
|
|
|
|
|
|
Sets the starting virtual machine parameters
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
void ScriptVM::SetFastData(ScriptVariable* data, int dataSize)
|
|
|
|
{
|
|
|
|
if (fastEvent.data)
|
|
|
|
{
|
|
|
|
fastEvent.data = m_pOldData;
|
|
|
|
fastEvent.dataSize = m_OldDataSize;
|
|
|
|
|
|
|
|
fastEvent.Clear();
|
|
|
|
|
|
|
|
m_pOldData = NULL;
|
|
|
|
m_OldDataSize = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dataSize)
|
|
|
|
{
|
|
|
|
fastEvent.data = new ScriptVariable[dataSize];
|
|
|
|
fastEvent.dataSize = dataSize;
|
|
|
|
|
|
|
|
for (int i = 0; i < dataSize; i++)
|
|
|
|
{
|
|
|
|
fastEvent.data[i] = data[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
m_pOldData = fastEvent.data;
|
|
|
|
m_OldDataSize = fastEvent.dataSize;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
NotifyDelete
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
void ScriptVM::NotifyDelete(void)
|
|
|
|
{
|
|
|
|
switch (state)
|
|
|
|
{
|
|
|
|
case STATE_DESTROYED:
|
|
|
|
ScriptError("Attempting to delete a dead thread.");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case STATE_RUNNING:
|
|
|
|
case STATE_SUSPENDED:
|
|
|
|
case STATE_WAITING:
|
|
|
|
state = STATE_DESTROYED;
|
|
|
|
|
|
|
|
if (m_ScriptClass) {
|
|
|
|
m_ScriptClass->RemoveThread(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case STATE_EXECUTION:
|
|
|
|
state = STATE_DESTROYED;
|
|
|
|
|
|
|
|
if (m_ScriptClass) {
|
|
|
|
m_ScriptClass->RemoveThread(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
delete this;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
Resume
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
void ScriptVM::Resume(qboolean bForce)
|
|
|
|
{
|
|
|
|
if (state == STATE_SUSPENDED || (bForce && state != STATE_DESTROYED)) {
|
|
|
|
state = STATE_RUNNING;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
Suspend
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
void ScriptVM::Suspend()
|
|
|
|
{
|
|
|
|
if (state == STATE_DESTROYED) {
|
|
|
|
ScriptError("Cannot suspend a dead thread.");
|
|
|
|
}
|
|
|
|
else if (!state) {
|
|
|
|
state = STATE_SUSPENDED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
Switch
|
|
|
|
|
|
|
|
Switch statement
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
bool ScriptVM::Switch(StateScript* stateScript, ScriptVariable& var)
|
|
|
|
{
|
|
|
|
unsigned char* pos;
|
|
|
|
|
|
|
|
fastEvent.dataSize = 0;
|
|
|
|
|
|
|
|
pos = stateScript->FindLabel(var.stringValue());
|
|
|
|
|
|
|
|
if (!pos)
|
|
|
|
{
|
|
|
|
pos = stateScript->FindLabel("");
|
|
|
|
|
|
|
|
if (!pos) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m_CodePos = pos;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
Filename
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
str ScriptVM::Filename(void)
|
|
|
|
{
|
|
|
|
return m_ScriptClass->Filename();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
Label
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
str ScriptVM::Label(void)
|
|
|
|
{
|
|
|
|
const_str label = m_ScriptClass->NearestLabel(m_CodePos);
|
|
|
|
|
|
|
|
if (!label)
|
|
|
|
{
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
return Director.GetString(label);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
GetScriptClass
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
ScriptClass* ScriptVM::GetScriptClass(void)
|
|
|
|
{
|
|
|
|
return m_ScriptClass;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
IsSuspended
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
bool ScriptVM::IsSuspended(void)
|
|
|
|
{
|
|
|
|
return state == STATE_SUSPENDED;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
State
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
int ScriptVM::State(void)
|
|
|
|
{
|
|
|
|
return state;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
ThreadState
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
int ScriptVM::ThreadState(void)
|
|
|
|
{
|
|
|
|
return m_ThreadState;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
EventGoto
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
void ScriptVM::EventGoto(Event* ev)
|
|
|
|
{
|
|
|
|
str label = ev->GetString(1);
|
|
|
|
|
|
|
|
SetFastData(ev->data + 1, ev->dataSize - 1);
|
|
|
|
|
|
|
|
unsigned char* codePos = m_ScriptClass->FindLabel(label);
|
|
|
|
|
|
|
|
if (!codePos)
|
|
|
|
{
|
|
|
|
ScriptError("ScriptVM::EventGoto: label '%s' does not exist in '%s'.", label.c_str(), Filename().c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
m_CodePos = codePos;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
====================
|
|
|
|
EventThrow
|
|
|
|
|
|
|
|
Called when throwing an exception
|
|
|
|
====================
|
|
|
|
*/
|
|
|
|
bool ScriptVM::EventThrow(Event* ev)
|
|
|
|
{
|
|
|
|
str label = ev->GetString(1);
|
|
|
|
|
|
|
|
SetFastData(ev->data, ev->dataSize);
|
|
|
|
|
|
|
|
fastEvent.eventnum = ev->eventnum;
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
StateScript* stateScript = m_ScriptClass->GetCatchStateScript(m_PrevCodePos, m_PrevCodePos);
|
|
|
|
|
|
|
|
if (!stateScript) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_CodePos = stateScript->FindLabel(label);
|
|
|
|
|
|
|
|
if (m_CodePos)
|
|
|
|
{
|
|
|
|
fastEvent.data++;
|
|
|
|
fastEvent.dataSize--;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ScriptVM::CanScriptTracePrint
|
|
|
|
(
|
|
|
|
void
|
|
|
|
)
|
|
|
|
{
|
|
|
|
if (g_scripttrace->integer < 1 || g_scripttrace->integer > 4)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (g_scripttrace->integer <= 2)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (!m_ScriptClass)
|
|
|
|
{
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!*g_monitor->string || !m_ScriptClass->m_Self || !m_ScriptClass->m_Self->isInheritedBy(&SimpleEntity::ClassInfo) || ((SimpleEntity*)m_ScriptClass->m_Self.Pointer())->targetname != g_monitor->string)
|
|
|
|
{
|
|
|
|
if (g_monitorNum->integer >= 0)
|
|
|
|
{
|
|
|
|
if (m_ScriptClass->m_Self && m_ScriptClass->m_Self->isInheritedBy(&Entity::ClassInfo) && ((Entity*)m_ScriptClass->m_Self.Pointer())->entnum == g_monitorNum->integer)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|