mirror of
https://github.com/rwengine/openrw.git
synced 2024-11-23 02:42:39 +01:00
Refactor script system to bind named functions.
Replace ugly macros with named functions and binding to script modules
This commit is contained in:
parent
698f5a9d91
commit
39ff9df337
@ -1,12 +0,0 @@
|
||||
#pragma once
|
||||
#ifndef _OPCODES_3_HPP_
|
||||
#define _OPCODES_3_HPP_
|
||||
#include <script/ScriptTypes.hpp>
|
||||
|
||||
struct Opcodes3 : SCMOpcodes
|
||||
{
|
||||
Opcodes3();
|
||||
};
|
||||
|
||||
|
||||
#endif
|
@ -1,19 +0,0 @@
|
||||
#pragma once
|
||||
#ifndef _OPCODESVM_HPP_
|
||||
#define _OPCODESVM_HPP_
|
||||
#include <script/ScriptTypes.hpp>
|
||||
|
||||
namespace Opcodes {
|
||||
struct VM {
|
||||
static VM& get() {
|
||||
static VM vm;
|
||||
return vm;
|
||||
}
|
||||
|
||||
VM();
|
||||
|
||||
SCMMicrocodeTable codes;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
44
rwengine/include/script/ScriptModule.hpp
Normal file
44
rwengine/include/script/ScriptModule.hpp
Normal file
@ -0,0 +1,44 @@
|
||||
#ifndef _SCRIPTMODULE_HPP_
|
||||
#define _SCRIPTMODULE_HPP_
|
||||
#include <script/ScriptTypes.hpp>
|
||||
#include "ScriptMachine.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
/**
|
||||
* Interface for a collection of functions that can be exported to a game script interface.
|
||||
*
|
||||
* For example a collection of functions that control the time of day, or create objects would
|
||||
* be the collected within one ScriptModule with a sensible name like "Environment" or "Objects"
|
||||
*/
|
||||
class ScriptModule
|
||||
{
|
||||
public:
|
||||
ScriptModule(const std::string& name) : name(name) { }
|
||||
|
||||
const std::string& getName() const { return name; }
|
||||
|
||||
void bind(ScriptFunctionID id,
|
||||
ScriptFunction func,
|
||||
int args,
|
||||
const std::string& name,
|
||||
const std::string& desc
|
||||
);
|
||||
|
||||
bool findOpcode(ScriptFunctionID id, ScriptFunctionMeta** out);
|
||||
|
||||
private:
|
||||
const std::string name;
|
||||
std::map<ScriptFunctionID, ScriptFunctionMeta> functions;
|
||||
};
|
||||
|
||||
template<class Tret> ScriptFunction conditional_facade(Tret(*f)(const ScriptArguments&)) { return f; }
|
||||
template<> ScriptFunction conditional_facade<bool>(bool(*f)(const ScriptArguments&));
|
||||
|
||||
// Macro to automatically use function name.
|
||||
#define bindFunction(id, func, argc, desc) \
|
||||
bind(id, conditional_facade(func), argc, #func, desc)
|
||||
#define bindUnimplemented(id, func, argc, desc) \
|
||||
bind(id, 0, argc, #func, desc)
|
||||
|
||||
#endif
|
@ -8,6 +8,7 @@
|
||||
#include <functional>
|
||||
|
||||
class ScriptMachine;
|
||||
class ScriptModule;
|
||||
struct SCMThread;
|
||||
|
||||
typedef uint16_t SCMOpcode;
|
||||
@ -56,6 +57,41 @@ struct SCMOpcodeParameter {
|
||||
|
||||
typedef std::vector<SCMOpcodeParameter> SCMParams;
|
||||
|
||||
class ScriptArguments
|
||||
{
|
||||
const SCMParams* parameters;
|
||||
SCMThread* thread;
|
||||
ScriptMachine* machine;
|
||||
|
||||
public:
|
||||
ScriptArguments(const SCMParams* p, SCMThread* t, ScriptMachine* m)
|
||||
: parameters(p), thread(t), machine(m) { }
|
||||
|
||||
const SCMParams& getParameters() const { return *parameters; }
|
||||
SCMThread* getThread() const { return thread; }
|
||||
ScriptMachine* getVM() const { return machine; }
|
||||
|
||||
const SCMOpcodeParameter& operator[](unsigned int arg) const
|
||||
{
|
||||
return parameters->at(arg);
|
||||
}
|
||||
};
|
||||
|
||||
typedef std::function<void (const ScriptArguments&)> ScriptFunction;
|
||||
typedef std::function<bool (const ScriptArguments&)> ScriptFunctionBoolean;
|
||||
typedef uint16_t ScriptFunctionID;
|
||||
|
||||
struct ScriptFunctionMeta
|
||||
{
|
||||
ScriptFunction function;
|
||||
int arguments;
|
||||
bool conditional;
|
||||
/** API name for this function */
|
||||
const std::string signature;
|
||||
/** Human friendly description */
|
||||
const std::string description;
|
||||
};
|
||||
|
||||
struct SCMMicrocode {
|
||||
std::string name;
|
||||
int parameters;
|
||||
@ -66,20 +102,12 @@ typedef std::map<SCMOpcode, SCMMicrocode> SCMMicrocodeTable;
|
||||
|
||||
struct SCMOpcodes
|
||||
{
|
||||
typedef std::function<void (ScriptMachine*, SCMThread*, SCMParams*)> SCMFunc;
|
||||
std::map<SCMOpcode, SCMMicrocode> codes;
|
||||
std::vector<ScriptModule*> modules;
|
||||
|
||||
~SCMOpcodes();
|
||||
|
||||
bool findOpcode(ScriptFunctionID id, ScriptFunctionMeta** out);
|
||||
};
|
||||
|
||||
|
||||
#define OPC_UNIMPLEMENTED_MSG(code, name) [=](ScriptMachine* m, SCMThread* t, SCMParams* p) { std::cout << #code << " " << name << " unimplemented" << std::endl; }
|
||||
|
||||
#define VM_OPCODE_DEF(code) void _opcode_##code##_func(ScriptMachine* m, SCMThread* t, SCMParams* p)
|
||||
#define VM_CONDOPCODE_DEF(code) bool _opcode_##code##_func(ScriptMachine* m, SCMThread* t, SCMParams* p)
|
||||
|
||||
#define VM_OPCODE_DEC(code, parameters, name) codes[code] = SCMMicrocode{name, parameters, SCMOpcodes::SCMFunc(_opcode_##code##_func)}
|
||||
#define VM_CONDOPCODE_DEC(code, parameters, cname) codes[code] = SCMMicrocode{cname, parameters, \
|
||||
[=](ScriptMachine* m, SCMThread* t, SCMParams* p) { t->conditionResult = _opcode_##code##_func(m, t, p); }}
|
||||
|
||||
#define VM_OPCODE_DEC_U(code, parameters, name) codes.insert({code, {name, parameters, {}}})
|
||||
|
||||
#endif
|
||||
|
12
rwengine/include/script/modules/GameModule.hpp
Normal file
12
rwengine/include/script/modules/GameModule.hpp
Normal file
@ -0,0 +1,12 @@
|
||||
#ifndef _MODULE_GAME_HPP_
|
||||
#define _MODULE_GAME_HPP_
|
||||
#include <script/ScriptModule.hpp>
|
||||
|
||||
class GameModule : public ScriptModule
|
||||
{
|
||||
public:
|
||||
GameModule();
|
||||
};
|
||||
|
||||
|
||||
#endif
|
11
rwengine/include/script/modules/VMModule.hpp
Normal file
11
rwengine/include/script/modules/VMModule.hpp
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef _VM_MODULE_HPP_
|
||||
#define _VM_MODULE_HPP_
|
||||
#include <script/ScriptModule.hpp>
|
||||
|
||||
class VMModule : public ScriptModule
|
||||
{
|
||||
public:
|
||||
VMModule();
|
||||
};
|
||||
|
||||
#endif
|
@ -7,8 +7,9 @@
|
||||
#include <data/WeaponData.hpp>
|
||||
#include <WorkContext.hpp>
|
||||
|
||||
#include <script/Opcodes3.hpp>
|
||||
#include <script/ScriptMachine.hpp>
|
||||
#include <script/modules/VMModule.hpp>
|
||||
#include <script/modules/GameModule.hpp>
|
||||
|
||||
// 3 isn't enough to cause a factory.
|
||||
#include <objects/CharacterObject.hpp>
|
||||
@ -172,8 +173,12 @@ void GameWorld::runScript(const std::string &name)
|
||||
SCMFile* f = gameData.loadSCM(name);
|
||||
if( f ) {
|
||||
if( script ) delete script;
|
||||
|
||||
SCMOpcodes* opcodes = new SCMOpcodes;
|
||||
opcodes->modules.push_back(new VMModule);
|
||||
opcodes->modules.push_back(new GameModule);
|
||||
|
||||
script = new ScriptMachine(this, f, new Opcodes3);
|
||||
script = new ScriptMachine(this, f, opcodes);
|
||||
}
|
||||
else {
|
||||
logError("Failed to load SCM: " + name);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,276 +0,0 @@
|
||||
#include <script/OpcodesVM.hpp>
|
||||
#include <script/ScriptMachine.hpp>
|
||||
#include <script/SCMFile.hpp>
|
||||
|
||||
#include <engine/GameWorld.hpp>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
SCMThread::pc_t localizeLabel(SCMThread* t, int label)
|
||||
{
|
||||
// Negative jump addresses indicate a jump relative to the start of the thread.
|
||||
return (label < 0) ? (t->baseAddress + (-label)) : label;
|
||||
}
|
||||
|
||||
VM_OPCODE_DEF( 0x0001 )
|
||||
{
|
||||
t->wakeCounter = p->at(0).integer;
|
||||
if( t->wakeCounter == 0 ) {
|
||||
t->wakeCounter = -1;
|
||||
}
|
||||
}
|
||||
|
||||
VM_OPCODE_DEF( 0x0002 )
|
||||
{
|
||||
t->programCounter = localizeLabel(t, p->at(0).integer);
|
||||
}
|
||||
|
||||
VM_OPCODE_DEF( 0x0004 )
|
||||
{
|
||||
*p->at(0).globalInteger = p->at(1).integer;
|
||||
}
|
||||
|
||||
VM_OPCODE_DEF( 0x0005 )
|
||||
{
|
||||
*p->at(0).globalReal = p->at(1).real;
|
||||
}
|
||||
VM_OPCODE_DEF( 0x0006 )
|
||||
{
|
||||
*p->at(0).globalInteger = p->at(1).integer;
|
||||
}
|
||||
|
||||
VM_OPCODE_DEF( 0x0008 )
|
||||
{
|
||||
*p->at(0).globalInteger += p->at(1).integer;
|
||||
}
|
||||
VM_OPCODE_DEF( 0x0009 )
|
||||
{
|
||||
*p->at(0).globalReal += p->at(1).real;
|
||||
}
|
||||
|
||||
VM_OPCODE_DEF( 0x000C )
|
||||
{
|
||||
*p->at(0).globalInteger -= p->at(1).integer;
|
||||
}
|
||||
VM_OPCODE_DEF( 0x000D )
|
||||
{
|
||||
*p->at(0).globalReal -= p->at(1).real;
|
||||
}
|
||||
|
||||
VM_OPCODE_DEF( 0x0015 )
|
||||
{
|
||||
*p->at(0).globalReal /= p->at(1).real;
|
||||
}
|
||||
|
||||
VM_OPCODE_DEF( 0x0018 ) {
|
||||
t->conditionResult = *p->at(0).globalInteger > p->at(1).integer;
|
||||
}
|
||||
VM_OPCODE_DEF( 0x0019 ) {
|
||||
t->conditionResult = *p->at(0).globalInteger > p->at(1).integer;
|
||||
}
|
||||
|
||||
VM_OPCODE_DEF( 0x001A ) {
|
||||
t->conditionResult = p->at(0).integer > *p->at(1).globalInteger;
|
||||
}
|
||||
|
||||
VM_OPCODE_DEF( 0x001B ) {
|
||||
t->conditionResult = p->at(0).integer > *p->at(1).globalInteger;
|
||||
}
|
||||
|
||||
VM_OPCODE_DEF( 0x0020 ) {
|
||||
t->conditionResult = *p->at(0).globalReal > p->at(1).real;
|
||||
}
|
||||
|
||||
VM_OPCODE_DEF( 0x0028 ) {
|
||||
t->conditionResult = *p->at(0).globalInteger >= p->at(1).integer;
|
||||
}
|
||||
VM_OPCODE_DEF( 0x0029 )
|
||||
{
|
||||
t->conditionResult = *p->at(0).globalInteger >= p->at(1).integer;
|
||||
}
|
||||
|
||||
|
||||
VM_OPCODE_DEF( 0x002A ) {
|
||||
t->conditionResult = p->at(0).integer >= *p->at(1).globalInteger;
|
||||
}
|
||||
|
||||
VM_OPCODE_DEF( 0x0038 ) {
|
||||
t->conditionResult = *p->at(0).globalInteger == p->at(1).integer;
|
||||
}
|
||||
|
||||
VM_OPCODE_DEF( 0x0039 ) {
|
||||
t->conditionResult = *p->at(0).globalInteger == p->at(1).integer;
|
||||
}
|
||||
|
||||
VM_OPCODE_DEF( 0x004F )
|
||||
{
|
||||
std::cout << t->name << " spawning thread at " << p->at(0).integer << std::endl;
|
||||
m->startThread(p->at(0).integer);
|
||||
}
|
||||
|
||||
VM_OPCODE_DEF( 0x004D )
|
||||
{
|
||||
if( ! t->conditionResult ) {
|
||||
t->programCounter = localizeLabel(t, p->at(0).integer);
|
||||
}
|
||||
}
|
||||
|
||||
VM_OPCODE_DEF( 0x004E )
|
||||
{
|
||||
// ensure the thread is immediately yeilded
|
||||
t->wakeCounter = -1;
|
||||
t->finished = true;
|
||||
}
|
||||
|
||||
VM_OPCODE_DEF( 0x0050 )
|
||||
{
|
||||
t->calls.push(t->programCounter);
|
||||
t->programCounter = localizeLabel(t, p->at(0).integer);
|
||||
}
|
||||
|
||||
VM_OPCODE_DEF( 0x0051 )
|
||||
{
|
||||
t->programCounter = t->calls.top();
|
||||
t->calls.pop();
|
||||
}
|
||||
|
||||
VM_OPCODE_DEF( 0x0060 )
|
||||
{
|
||||
*p->at(0).globalInteger -= *p->at(1).globalInteger;
|
||||
}
|
||||
|
||||
VM_OPCODE_DEF( 0x0061 )
|
||||
{
|
||||
*p->at(0).globalReal -= *p->at(1).globalReal;
|
||||
}
|
||||
|
||||
VM_OPCODE_DEF( 0x0084 )
|
||||
{
|
||||
*p->at(0).globalInteger = *p->at(1).globalInteger;
|
||||
}
|
||||
|
||||
VM_OPCODE_DEF( 0x0086 )
|
||||
{
|
||||
*p->at(0).globalReal = *p->at(1).globalReal;
|
||||
}
|
||||
|
||||
VM_OPCODE_DEF( 0x00D6 )
|
||||
{
|
||||
auto n = p->at(0).integer;
|
||||
if( n <= 7 ) {
|
||||
t->conditionCount = n+1;
|
||||
t->conditionMask = 0xFF;
|
||||
t->conditionAND = true;
|
||||
}
|
||||
else {
|
||||
t->conditionCount = n-19;
|
||||
t->conditionMask = 0x00;
|
||||
t->conditionAND = false;
|
||||
}
|
||||
}
|
||||
|
||||
VM_OPCODE_DEF( 0x00D7 )
|
||||
{
|
||||
std::cout << "Starting Mission Thread at " << p->at(0).integer << std::endl;
|
||||
m->startThread(p->at(0).integer, true);
|
||||
}
|
||||
|
||||
VM_OPCODE_DEF( 0x00D8 )
|
||||
{
|
||||
std::cout << "Ended: " << t->name << std::endl;
|
||||
|
||||
for( auto& o : m->getWorld()->state.missionObjects )
|
||||
{
|
||||
m->getWorld()->destroyObjectQueued(o);
|
||||
}
|
||||
|
||||
m->getWorld()->state.missionObjects.clear();
|
||||
|
||||
*m->getWorld()->state.scriptOnMissionFlag = 0;
|
||||
}
|
||||
|
||||
VM_OPCODE_DEF( 0x02CD )
|
||||
{
|
||||
t->calls.push(t->programCounter);
|
||||
t->programCounter = p->at(0).integer;
|
||||
}
|
||||
|
||||
VM_OPCODE_DEF( 0x03A4 )
|
||||
{
|
||||
t->name = p->at(0).string;
|
||||
std::cout << "Thread renamed: " << t->name << std::endl;
|
||||
}
|
||||
|
||||
VM_OPCODE_DEF( 0x0417 )
|
||||
{
|
||||
std::cout << "Starting mission no. " << p->at(0).integer << std::endl;
|
||||
auto offset = m->getFile()->getMissionOffsets()[p->at(0).integer];
|
||||
m->startThread(offset, true);
|
||||
}
|
||||
|
||||
namespace Opcodes {
|
||||
|
||||
VM::VM()
|
||||
{
|
||||
VM_OPCODE_DEC( 0x0001, 1, "Wait" );
|
||||
VM_OPCODE_DEC( 0x0002, 1, "Jump" );
|
||||
|
||||
VM_OPCODE_DEC( 0x0004, 2, "Set Global Integer" );
|
||||
VM_OPCODE_DEC( 0x0005, 2, "Set Global Float" );
|
||||
VM_OPCODE_DEC( 0x0006, 2, "Set Local Int" );
|
||||
|
||||
VM_OPCODE_DEC( 0x0008, 2, "Increment Global Int" );
|
||||
VM_OPCODE_DEC( 0x0009, 2, "Increment Global Float" );
|
||||
VM_OPCODE_DEC( 0x000C, 2, "Decrement Global Int" );
|
||||
VM_OPCODE_DEC( 0x000D, 2, "Decrement Global Float" );
|
||||
|
||||
VM_OPCODE_DEC( 0x0015, 2, "Divide Global by Float" );
|
||||
|
||||
VM_OPCODE_DEC( 0x0018, 2, "Global Int Greater than Int" );
|
||||
VM_OPCODE_DEC( 0x0019, 2, "Local Int Greater than Int" );
|
||||
|
||||
VM_OPCODE_DEC( 0x001A, 2, "Int Greater Than Global Int" );
|
||||
VM_OPCODE_DEC( 0x001B, 2, "Int Greater Than Var Int" );
|
||||
|
||||
VM_OPCODE_DEC( 0x0020, 2, "Global Float Greather than Float" );
|
||||
|
||||
VM_OPCODE_DEC( 0x0028, 2, "Global Int >= Int" );
|
||||
VM_OPCODE_DEC( 0x0029, 2, "Local Int >= Int" );
|
||||
|
||||
VM_OPCODE_DEC( 0x002A, 2, "Int >= Global Int" );
|
||||
|
||||
VM_OPCODE_DEC( 0x0038, 2, "Global Int Equal to Int" );
|
||||
|
||||
VM_OPCODE_DEC( 0x0039, 2, "Local Int Equal to Int" );
|
||||
|
||||
VM_OPCODE_DEC( 0x004F, -1, "Start New Thread" );
|
||||
|
||||
VM_OPCODE_DEC( 0x004D, 1, "Jump if false" );
|
||||
|
||||
VM_OPCODE_DEC( 0x004E, 0, "End Thread" );
|
||||
|
||||
VM_OPCODE_DEC( 0x0050, 1, "Gosub" );
|
||||
|
||||
VM_OPCODE_DEC( 0x0051, 0, "Return" );
|
||||
|
||||
VM_OPCODE_DEC( 0x0060, 2, "Decrement Global Integer by Global Integer" );
|
||||
VM_OPCODE_DEC( 0x0061, 2, "Decrement Global Float by Global Float" );
|
||||
|
||||
VM_OPCODE_DEC( 0x0084, 2, "Set Global Int To Global" );
|
||||
|
||||
VM_OPCODE_DEC( 0x0086, 2, "Set Global Float To Global" );
|
||||
|
||||
VM_OPCODE_DEC( 0x00D6, 1, "If" );
|
||||
|
||||
VM_OPCODE_DEC( 0x00D7, 1, "Start Mission Thread" );
|
||||
|
||||
VM_OPCODE_DEC( 0x00D8, 0, "Set Mission Finished" );
|
||||
|
||||
VM_OPCODE_DEC( 0x02CD, 2, "Call" );
|
||||
|
||||
VM_OPCODE_DEC( 0x03A4, 1, "Name Thread" );
|
||||
|
||||
VM_OPCODE_DEC( 0x0417, 1, "Start Mission" );
|
||||
}
|
||||
|
||||
}
|
@ -1,11 +1,32 @@
|
||||
#include <script/ScriptMachine.hpp>
|
||||
#include <script/SCMFile.hpp>
|
||||
#include <boost/iterator/iterator_concepts.hpp>
|
||||
#include <script/ScriptModule.hpp>
|
||||
|
||||
#if SCM_DEBUG_INSTRUCTIONS
|
||||
#include <iostream>
|
||||
#endif
|
||||
|
||||
SCMOpcodes::~SCMOpcodes()
|
||||
{
|
||||
for(auto m : modules)
|
||||
{
|
||||
delete m;
|
||||
}
|
||||
}
|
||||
|
||||
bool SCMOpcodes::findOpcode(ScriptFunctionID id, ScriptFunctionMeta** out)
|
||||
{
|
||||
for(ScriptModule* module : modules)
|
||||
{
|
||||
if( module->findOpcode(id, out) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ScriptMachine::executeThread(SCMThread &t, int msPassed)
|
||||
{
|
||||
if( t.wakeCounter > 0 ) {
|
||||
@ -20,16 +41,19 @@ void ScriptMachine::executeThread(SCMThread &t, int msPassed)
|
||||
bool isNegatedConditional = ((opcode & SCM_NEGATE_CONDITIONAL_MASK) == SCM_NEGATE_CONDITIONAL_MASK);
|
||||
opcode = opcode & ~SCM_NEGATE_CONDITIONAL_MASK;
|
||||
|
||||
auto it = _ops->codes.find(opcode);
|
||||
if( it == _ops->codes.end() ) throw IllegalInstruction(opcode, t.programCounter, t.name);
|
||||
ScriptFunctionMeta* foundcode;
|
||||
if( ! _ops->findOpcode(opcode, &foundcode) )
|
||||
{
|
||||
throw IllegalInstruction(opcode, t.programCounter, t.name);
|
||||
}
|
||||
auto& code = *foundcode;
|
||||
|
||||
t.programCounter += sizeof(SCMOpcode);
|
||||
|
||||
SCMMicrocode& code = it->second;
|
||||
|
||||
SCMParams parameters;
|
||||
|
||||
bool hasExtraParameters = code.parameters < 0;
|
||||
auto requiredParams = std::abs(code.parameters);
|
||||
bool hasExtraParameters = code.arguments < 0;
|
||||
auto requiredParams = std::abs(code.arguments);
|
||||
|
||||
for( int p = 0; p < requiredParams || hasExtraParameters; ++p ) {
|
||||
auto type_r = _file->read<SCMByte>(t.programCounter);
|
||||
@ -87,7 +111,7 @@ void ScriptMachine::executeThread(SCMThread &t, int msPassed)
|
||||
};
|
||||
}
|
||||
|
||||
if(! code.func)
|
||||
if(! code.function)
|
||||
{
|
||||
#if SCM_DEBUG_INSTRUCTIONS
|
||||
std::cout << std::setw(7) << std::setfill(' ') << t.name <<
|
||||
@ -115,7 +139,7 @@ void ScriptMachine::executeThread(SCMThread &t, int msPassed)
|
||||
}
|
||||
std::cout << " ";
|
||||
}
|
||||
std::cout << code.name << " unimplemented"<< std::endl;
|
||||
std::cout << code.signature << " unimplemented"<< std::endl;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
@ -153,10 +177,11 @@ void ScriptMachine::executeThread(SCMThread &t, int msPassed)
|
||||
}
|
||||
std::cout << " ";
|
||||
}
|
||||
std::cout << code.name << std::endl;
|
||||
std::cout << code.signature << std::endl;
|
||||
}
|
||||
#endif
|
||||
code.func(this, &t, ¶meters);
|
||||
ScriptArguments sca(¶meters, &t, this);
|
||||
code.function(sca);
|
||||
}
|
||||
|
||||
if(isNegatedConditional) {
|
||||
@ -193,14 +218,11 @@ void ScriptMachine::executeThread(SCMThread &t, int msPassed)
|
||||
p.globalPtr = (t.locals + 17 * sizeof ( SCMByte ) * 4);
|
||||
*p.globalInteger += msPassed;
|
||||
|
||||
|
||||
|
||||
if( t.wakeCounter == -1 ) {
|
||||
t.wakeCounter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#include <iostream>
|
||||
ScriptMachine::ScriptMachine(GameWorld *world, SCMFile *file, SCMOpcodes *ops)
|
||||
: _file(file), _ops(ops), _world(world)
|
||||
{
|
||||
@ -211,7 +233,6 @@ ScriptMachine::ScriptMachine(GameWorld *world, SCMFile *file, SCMOpcodes *ops)
|
||||
{
|
||||
_globals[i] = 0;
|
||||
}
|
||||
std::cout << globals << " " << SCM_VARIABLE_SIZE << std::endl;
|
||||
}
|
||||
|
||||
ScriptMachine::~ScriptMachine()
|
||||
|
31
rwengine/src/script/ScriptModule.cpp
Normal file
31
rwengine/src/script/ScriptModule.cpp
Normal file
@ -0,0 +1,31 @@
|
||||
#include <script/ScriptModule.hpp>
|
||||
#include <script/ScriptMachine.hpp>
|
||||
|
||||
void ScriptModule::bind(ScriptFunctionID id, ScriptFunction func, int args, const std::string& name, const std::string& desc)
|
||||
{
|
||||
functions.insert(
|
||||
{ id,
|
||||
{
|
||||
func,
|
||||
args,
|
||||
false,
|
||||
name,
|
||||
desc
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
bool ScriptModule::findOpcode(ScriptFunctionID id, ScriptFunctionMeta** out)
|
||||
{
|
||||
auto it = functions.find(id);
|
||||
if( it == functions.end() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
*out = &functions[id];
|
||||
return true;
|
||||
}
|
||||
|
||||
template<> ScriptFunction conditional_facade<bool>(bool(*f)(const ScriptArguments&))
|
||||
{ return [=](const ScriptArguments& a) { return a.getThread()->conditionResult = f(a); }; }
|
1551
rwengine/src/script/modules/GameModule.cpp
Normal file
1551
rwengine/src/script/modules/GameModule.cpp
Normal file
File diff suppressed because it is too large
Load Diff
245
rwengine/src/script/modules/VMModule.cpp
Normal file
245
rwengine/src/script/modules/VMModule.cpp
Normal file
@ -0,0 +1,245 @@
|
||||
#include <script/modules/VMModule.hpp>
|
||||
#include <script/ScriptMachine.hpp>
|
||||
#include <script/SCMFile.hpp>
|
||||
#include <engine/GameWorld.hpp>
|
||||
|
||||
SCMThread::pc_t localizeLabel(SCMThread* t, int label)
|
||||
{
|
||||
// Negative jump addresses indicate a jump relative to the start of the thread.
|
||||
return (label < 0) ? (t->baseAddress + (-label)) : label;
|
||||
}
|
||||
|
||||
void vm_sleep(const ScriptArguments& args)
|
||||
{
|
||||
args.getThread()->wakeCounter = args[0].integer;
|
||||
if( args.getThread()->wakeCounter == 0 ) {
|
||||
args.getThread()->wakeCounter = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void vm_jump(const ScriptArguments& args)
|
||||
{
|
||||
args.getThread()->programCounter = localizeLabel(args.getThread(), args[0].integer);
|
||||
}
|
||||
|
||||
void vm_global_int(const ScriptArguments& args)
|
||||
{
|
||||
*args[0].globalInteger = args[1].integer;
|
||||
}
|
||||
|
||||
void vm_global_float(const ScriptArguments& args)
|
||||
{
|
||||
*args[0].globalReal = args[1].real;
|
||||
}
|
||||
|
||||
void vm_inc_global_int(const ScriptArguments& args)
|
||||
{
|
||||
*args[0].globalInteger += args[1].integer;
|
||||
}
|
||||
void vm_inc_global_float(const ScriptArguments& args)
|
||||
{
|
||||
*args[0].globalReal += args[1].real;
|
||||
}
|
||||
|
||||
void vm_dec_global_int(const ScriptArguments& args)
|
||||
{
|
||||
*args[0].globalInteger -= args[1].integer;
|
||||
}
|
||||
void vm_dec_global_float(const ScriptArguments& args)
|
||||
{
|
||||
*args[0].globalReal -= args[1].real;
|
||||
}
|
||||
|
||||
void vm_div_global_float(const ScriptArguments& args)
|
||||
{
|
||||
*args[0].globalReal /= args[1].real;
|
||||
}
|
||||
|
||||
void vm_global_int_gt_int(const ScriptArguments& args)
|
||||
{
|
||||
args.getThread()->conditionResult = *args[0].globalInteger > args[1].integer;
|
||||
}
|
||||
|
||||
void vm_int_gt_global_int(const ScriptArguments& args)
|
||||
{
|
||||
args.getThread()->conditionResult = args[0].integer > *args[1].globalInteger;
|
||||
}
|
||||
|
||||
void vm_global_float_gt_float(const ScriptArguments& args)
|
||||
{
|
||||
args.getThread()->conditionResult = *args[0].globalReal > args[1].real;
|
||||
}
|
||||
|
||||
void vm_global_int_ge_int(const ScriptArguments& args)
|
||||
{
|
||||
args.getThread()->conditionResult = *args[0].globalInteger >= args[1].integer;
|
||||
}
|
||||
|
||||
void vm_int_ge_global_int(const ScriptArguments& args)
|
||||
{
|
||||
args.getThread()->conditionResult = args[0].integer >= *args[1].globalInteger;
|
||||
}
|
||||
|
||||
void vm_global_int_eq_int(const ScriptArguments& args)
|
||||
{
|
||||
args.getThread()->conditionResult = *args[0].globalInteger == args[1].integer;
|
||||
}
|
||||
|
||||
void vm_new_thread(const ScriptArguments& args)
|
||||
{
|
||||
args.getVM()->startThread(args[0].integer);
|
||||
}
|
||||
|
||||
void vm_jump_if_false(const ScriptArguments& args)
|
||||
{
|
||||
if( ! args.getThread()->conditionResult ) {
|
||||
args.getThread()->programCounter = localizeLabel(args.getThread(), args[0].integer);
|
||||
}
|
||||
}
|
||||
|
||||
void vm_halt_thread(const ScriptArguments& args)
|
||||
{
|
||||
// ensure the thread is immediately yeilded
|
||||
args.getThread()->wakeCounter = -1;
|
||||
args.getThread()->finished = true;
|
||||
}
|
||||
|
||||
void vm_call(const ScriptArguments& args)
|
||||
{
|
||||
args.getThread()->calls.push(args.getThread()->programCounter);
|
||||
args.getThread()->programCounter = localizeLabel(args.getThread(), args[0].integer);
|
||||
}
|
||||
|
||||
void vm_return(const ScriptArguments& args)
|
||||
{
|
||||
args.getThread()->programCounter = args.getThread()->calls.top();
|
||||
args.getThread()->calls.pop();
|
||||
}
|
||||
|
||||
void vm_dec_global_int_by_global(const ScriptArguments& args)
|
||||
{
|
||||
*args[0].globalInteger -= *args[1].globalInteger;
|
||||
}
|
||||
|
||||
void vm_dec_global_float_by_global(const ScriptArguments& args)
|
||||
{
|
||||
*args[0].globalReal -= *args[1].globalReal;
|
||||
}
|
||||
|
||||
void vm_global_int_to_global(const ScriptArguments& args)
|
||||
{
|
||||
*args[0].globalInteger = *args[1].globalInteger;
|
||||
}
|
||||
|
||||
void vm_global_float_to_global(const ScriptArguments& args)
|
||||
{
|
||||
*args[0].globalReal = *args[1].globalReal;
|
||||
}
|
||||
|
||||
void vm_if(const ScriptArguments& args)
|
||||
{
|
||||
auto n = args[0].integer;
|
||||
if( n <= 7 ) {
|
||||
args.getThread()->conditionCount = n+1;
|
||||
args.getThread()->conditionMask = 0xFF;
|
||||
args.getThread()->conditionAND = true;
|
||||
}
|
||||
else {
|
||||
args.getThread()->conditionCount = n-19;
|
||||
args.getThread()->conditionMask = 0x00;
|
||||
args.getThread()->conditionAND = false;
|
||||
}
|
||||
}
|
||||
|
||||
void vm_new_mission_thread(const ScriptArguments& args)
|
||||
{
|
||||
args.getVM()->startThread(args[0].integer, true);
|
||||
}
|
||||
|
||||
void vm_mission_over(const ScriptArguments& args)
|
||||
{
|
||||
for( auto& o : args.getVM()->getWorld()->state.missionObjects )
|
||||
{
|
||||
args.getVM()->getWorld()->destroyObjectQueued(o);
|
||||
}
|
||||
|
||||
args.getVM()->getWorld()->state.missionObjects.clear();
|
||||
|
||||
*args.getVM()->getWorld()->state.scriptOnMissionFlag = 0;
|
||||
}
|
||||
|
||||
void vm_name_thread(const ScriptArguments& args)
|
||||
{
|
||||
args.getThread()->name = args[0].string;
|
||||
}
|
||||
|
||||
void vm_start_mission(const ScriptArguments& args)
|
||||
{
|
||||
auto offset = args.getVM()->getFile()->getMissionOffsets()[args[0].integer];
|
||||
args.getVM()->startThread(offset, true);
|
||||
}
|
||||
|
||||
|
||||
VMModule::VMModule()
|
||||
: ScriptModule("VM")
|
||||
{
|
||||
bindFunction(0x001, vm_sleep, 1, "Sleep thread");
|
||||
bindFunction(0x002, vm_jump, 1, "Jump");
|
||||
|
||||
bindFunction(0x004, vm_global_int, 2, "Set Global Integer");
|
||||
bindFunction(0x005, vm_global_float, 2, "Set Global Float");
|
||||
// Local Integers are handled by the VM itself, function can be re-used.
|
||||
bindFunction(0x006, vm_global_int, 2, "Set Local Int");
|
||||
|
||||
bindFunction(0x008, vm_inc_global_int, 2, "Increment Global Int");
|
||||
bindFunction(0x009, vm_inc_global_float, 2, "Increment Global Float");
|
||||
bindFunction(0x00C, vm_dec_global_int, 2, "Decrement Global Int");
|
||||
bindFunction(0x00D, vm_dec_global_float, 2, "Decrement Global Float");
|
||||
|
||||
bindFunction(0x015, vm_div_global_float, 2, "Divide Global by Float");
|
||||
|
||||
bindFunction(0x018, vm_global_int_gt_int, 2, "Global Int Greater than Int");
|
||||
bindFunction(0x019, vm_global_int_gt_int, 2, "Local Int Greater than Int");
|
||||
|
||||
bindFunction(0x01A, vm_int_gt_global_int, 2, "Int Greater Than Global Int");
|
||||
bindFunction(0x01B, vm_int_gt_global_int, 2, "Int Greater Than Var Int");
|
||||
|
||||
bindFunction(0x020, vm_global_float_gt_float, 2, "Global Float Greather than Float");
|
||||
|
||||
bindFunction(0x028, vm_global_int_ge_int, 2, "Global Int >= Int");
|
||||
bindFunction(0x029, vm_global_int_ge_int, 2, "Local Int >= Int");
|
||||
|
||||
bindFunction(0x02A, vm_int_ge_global_int, 2, "Int >= Global Int");
|
||||
|
||||
bindFunction(0x038, vm_global_int_eq_int, 2, "Global Int Equal to Int");
|
||||
bindFunction(0x039, vm_global_int_eq_int, 2, "Local Int Equal to Int");
|
||||
|
||||
bindFunction(0x04F, vm_new_thread, -1, "Start New Thread");
|
||||
|
||||
bindFunction(0x04D, vm_jump_if_false, 1, "Jump if false");
|
||||
|
||||
bindFunction(0x04E, vm_halt_thread, 0, "End Thread");
|
||||
|
||||
bindFunction(0x050, vm_call, 1, "Gosub");
|
||||
|
||||
bindFunction(0x051, vm_return, 0, "Return");
|
||||
|
||||
bindFunction(0x060, vm_dec_global_int_by_global, 2, "Decrement Global Integer by Global Integer");
|
||||
bindFunction(0x061, vm_dec_global_float_by_global, 2, "Decrement Global Float by Global Float");
|
||||
|
||||
bindFunction(0x084, vm_global_int_to_global, 2, "Set Global Int To Global");
|
||||
|
||||
bindFunction(0x086, vm_global_float_to_global, 2, "Set Global Float To Global");
|
||||
|
||||
bindFunction(0x0D6, vm_if, 1, "If");
|
||||
|
||||
bindFunction(0x0D7, vm_new_mission_thread, 1, "Start Mission Thread");
|
||||
|
||||
bindFunction(0x0D8, vm_mission_over, 0, "Set Mission Finished");
|
||||
|
||||
bindFunction(0x2CD, vm_call, 2, "Call");
|
||||
|
||||
bindFunction(0x3A4, vm_name_thread, 1, "Name Thread");
|
||||
|
||||
bindFunction(0x417, vm_start_mission, 1, "Start Mission");
|
||||
}
|
Loading…
Reference in New Issue
Block a user