2020-12-21 13:16:29 -03:00
|
|
|
#include "framework.h"
|
|
|
|
#include "GameLogicScript.h"
|
2021-08-04 16:51:28 +01:00
|
|
|
#include "ScriptAssert.h"
|
2020-12-21 13:16:29 -03:00
|
|
|
#include "items.h"
|
|
|
|
#include "box.h"
|
|
|
|
#include "lara.h"
|
|
|
|
#include "savegame.h"
|
|
|
|
#include "lot.h"
|
|
|
|
#include "sound.h"
|
|
|
|
#include "setup.h"
|
|
|
|
#include "level.h"
|
2021-06-29 05:28:17 +02:00
|
|
|
#include "tomb4fx.h"
|
|
|
|
#include "effect2.h"
|
|
|
|
#include "pickup.h"
|
2021-07-10 14:05:01 +01:00
|
|
|
#include "newinv2.h"
|
2021-07-13 13:21:13 +01:00
|
|
|
#include "InventorySlots.h"
|
2021-07-20 00:16:15 +01:00
|
|
|
#include "ObjectIDs.h"
|
2021-06-29 05:00:15 +02:00
|
|
|
|
2021-07-13 22:29:47 -05:00
|
|
|
#ifndef _DEBUG
|
|
|
|
#include <iostream>
|
|
|
|
#endif
|
2021-06-29 05:00:15 +02:00
|
|
|
|
2021-07-21 18:19:57 +01:00
|
|
|
/***
|
2021-07-28 18:44:24 +01:00
|
|
|
functions and callbacks for game specific scripts
|
|
|
|
@module gamelogic
|
2021-07-21 18:19:57 +01:00
|
|
|
@pragma nostrip
|
|
|
|
*/
|
|
|
|
|
2020-12-21 13:16:29 -03:00
|
|
|
extern GameFlow* g_GameFlow;
|
2021-08-05 21:36:18 +01:00
|
|
|
|
|
|
|
static void PlayAudioTrack(std::string const & trackName, bool looped)
|
|
|
|
{
|
|
|
|
S_CDPlay(trackName, looped);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void PlaySoundEffect(int id, GameScriptPosition p, int flags)
|
|
|
|
{
|
|
|
|
PHD_3DPOS pos;
|
|
|
|
|
|
|
|
pos.xPos = p.x;
|
|
|
|
pos.yPos = p.y;
|
|
|
|
pos.zPos = p.z;
|
|
|
|
pos.xRot = 0;
|
|
|
|
pos.yRot = 0;
|
|
|
|
pos.zRot = 0;
|
|
|
|
|
|
|
|
SoundEffect(id, &pos, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void PlaySoundEffect(int id, int flags)
|
|
|
|
{
|
|
|
|
SoundEffect(id, NULL, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void SetAmbientTrack(std::string const & trackName)
|
|
|
|
{
|
|
|
|
CurrentAtmosphere = trackName;
|
|
|
|
S_CDPlay(CurrentAtmosphere, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int FindRoomNumber(GameScriptPosition pos)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void AddLightningArc(GameScriptPosition src, GameScriptPosition dest, GameScriptColor color, int lifetime, int amplitude, int beamWidth, int segments, int flags)
|
|
|
|
{
|
|
|
|
PHD_VECTOR p1;
|
|
|
|
p1.x = src.x;
|
|
|
|
p1.y = src.y;
|
|
|
|
p1.z = src.z;
|
|
|
|
|
|
|
|
PHD_VECTOR p2;
|
|
|
|
p2.x = dest.x;
|
|
|
|
p2.y = dest.y;
|
|
|
|
p2.z = dest.z;
|
|
|
|
|
|
|
|
TriggerLightning(&p1, &p2, amplitude, color.GetR(), color.GetG(), color.GetB(), lifetime, flags, beamWidth, segments);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void AddShockwave(GameScriptPosition pos, int innerRadius, int outerRadius, GameScriptColor color, int lifetime, int speed, int angle, int flags)
|
|
|
|
{
|
|
|
|
PHD_3DPOS p;
|
|
|
|
p.xPos = pos.x;
|
|
|
|
p.yPos = pos.y;
|
|
|
|
p.zPos = pos.z;
|
|
|
|
|
|
|
|
TriggerShockwave(&p, innerRadius, outerRadius, speed, color.GetR(), color.GetG(), color.GetB(), lifetime, FROM_DEGREES(angle), flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void AddDynamicLight(GameScriptPosition pos, GameScriptColor color, int radius, int lifetime)
|
|
|
|
{
|
|
|
|
TriggerDynamicLight(pos.x, pos.y, pos.z, radius, color.GetR(), color.GetG(), color.GetB());
|
|
|
|
}
|
|
|
|
|
|
|
|
static void AddBlood(GameScriptPosition pos, int num)
|
|
|
|
{
|
|
|
|
TriggerBlood(pos.x, pos.y, pos.z, -1, num);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void AddFireFlame(GameScriptPosition pos, int size)
|
|
|
|
{
|
|
|
|
AddFire(pos.x, pos.y, pos.z, size, FindRoomNumber(pos), true);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void Earthquake(int strength)
|
|
|
|
{
|
|
|
|
Camera.bounce = -strength;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Inventory
|
|
|
|
static void InventoryAdd(GAME_OBJECT_ID slot, sol::optional<int> count)
|
|
|
|
{
|
|
|
|
PickedUpObject(slot, count.value_or(0));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void InventoryRemove(GAME_OBJECT_ID slot, sol::optional<int> count)
|
|
|
|
{
|
|
|
|
RemoveObjectFromInventory(static_cast<GAME_OBJECT_ID>(inventry_objects_list[slot].object_number), count.value_or(0));
|
|
|
|
}
|
|
|
|
|
|
|
|
static int InventoryGetCount(GAME_OBJECT_ID slot)
|
|
|
|
{
|
|
|
|
return GetInventoryCount(slot);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void InventorySetCount(GAME_OBJECT_ID slot, int count)
|
|
|
|
{
|
|
|
|
// add the amount we'd need to add to get to count
|
|
|
|
int currAmt = GetInventoryCount(slot);
|
|
|
|
InventoryAdd(slot, count - currAmt);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void InventoryCombine(int slot1, int slot2)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static void InventorySeparate(int slot)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Misc
|
|
|
|
static void PrintString(std::string key, GameScriptPosition pos, GameScriptColor color, int lifetime, int flags)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
2020-12-21 13:16:29 -03:00
|
|
|
|
2021-08-06 16:47:24 +01:00
|
|
|
GameScript::GameScript(sol::state* lua) : LuaHandler{ lua }
|
2020-12-21 13:16:29 -03:00
|
|
|
{
|
2021-07-23 16:02:30 +01:00
|
|
|
/*** Ambience and music
|
|
|
|
@section Music
|
|
|
|
*/
|
|
|
|
|
2021-07-21 18:19:57 +01:00
|
|
|
/***
|
|
|
|
Set the named track as the ambient track, and start playing it
|
|
|
|
@function SetAmbientTrack
|
|
|
|
@tparam string name of track (without file extension) to play
|
|
|
|
*/
|
2021-08-05 21:36:18 +01:00
|
|
|
m_lua->set_function("SetAmbientTrack", &SetAmbientTrack);
|
2021-07-21 18:19:57 +01:00
|
|
|
|
|
|
|
/***
|
|
|
|
Start playing the named track.
|
|
|
|
@function PlayAudioTrack
|
|
|
|
@tparam string name of track (without file extension) to play
|
|
|
|
@tparam bool loop if true, the track will loop; if false, it won't
|
|
|
|
*/
|
2021-08-05 21:36:18 +01:00
|
|
|
m_lua->set_function("PlayAudioTrack", &PlayAudioTrack);
|
2021-07-04 14:33:57 +01:00
|
|
|
|
2021-07-23 16:02:30 +01:00
|
|
|
/*** Player inventory management
|
|
|
|
@section Inventory
|
|
|
|
*/
|
|
|
|
|
2021-07-21 18:19:57 +01:00
|
|
|
/***
|
|
|
|
Add x of a certain item to the inventory.
|
|
|
|
@function GiveInvItem
|
2021-07-23 16:02:30 +01:00
|
|
|
@tparam @{InvItem} item the item to be added
|
|
|
|
@tparam int count the number of items to add
|
2021-07-21 18:19:57 +01:00
|
|
|
*/
|
2021-08-05 21:36:18 +01:00
|
|
|
m_lua->set_function("GiveInvItem", &InventoryAdd);
|
2021-07-21 18:19:57 +01:00
|
|
|
|
|
|
|
/***
|
|
|
|
Remove x of a certain item from the inventory.
|
|
|
|
@function TakeInvItem
|
2021-07-23 16:02:30 +01:00
|
|
|
@tparam @{InvItem} item the item to be removed
|
|
|
|
@tparam int count the number of items to remove
|
2021-07-21 18:19:57 +01:00
|
|
|
*/
|
2021-08-05 21:36:18 +01:00
|
|
|
m_lua->set_function("TakeInvItem", &InventoryRemove);
|
2021-07-10 14:05:01 +01:00
|
|
|
|
2021-07-21 18:19:57 +01:00
|
|
|
/***
|
|
|
|
Get the amount the player holds of an item.
|
|
|
|
@function GetInvItemCount
|
2021-07-23 16:02:30 +01:00
|
|
|
@tparam @{InvItem} item the item to check
|
2021-07-21 18:19:57 +01:00
|
|
|
@return the amount of the item the player has in the inventory
|
|
|
|
*/
|
2021-08-05 21:36:18 +01:00
|
|
|
m_lua->set_function("GetInvItemCount", &InventoryGetCount);
|
2021-07-21 18:19:57 +01:00
|
|
|
|
|
|
|
/***
|
|
|
|
Set the amount of a certain item the player has in the inventory.
|
|
|
|
Similar to @{GiveInvItem} but replaces with the new amount instead of adding it.
|
|
|
|
@function SetInvItemCount
|
2021-07-23 16:02:30 +01:00
|
|
|
@tparam @{InvItem} item the item to be set
|
|
|
|
@tparam int count the number of items the player will have
|
2021-07-21 18:19:57 +01:00
|
|
|
*/
|
2021-08-05 21:36:18 +01:00
|
|
|
m_lua->set_function("SetInvItemCount", &InventorySetCount);
|
2021-07-05 18:18:20 +01:00
|
|
|
|
2021-07-23 16:02:30 +01:00
|
|
|
/*** Game entity getters.
|
|
|
|
All Lua variables created with these functions will be non-owning.
|
|
|
|
This means that the actual in-game entity (object/camera/sink/whatever)
|
|
|
|
will _not_ be removed from the game if the Lua variable goes out of scope
|
|
|
|
or is destroyed in some other way.
|
|
|
|
@section getters
|
|
|
|
*/
|
|
|
|
|
2021-07-21 18:19:57 +01:00
|
|
|
/***
|
2021-07-23 16:02:30 +01:00
|
|
|
Get an ItemInfo by its name.
|
2021-07-21 18:19:57 +01:00
|
|
|
@function GetItemByName
|
|
|
|
@tparam string name the unique name of the item as set in, or generated by, Tomb Editor
|
|
|
|
@return a non-owning ItemInfo referencing the item.
|
|
|
|
*/
|
2021-07-20 17:58:11 +01:00
|
|
|
m_lua->set_function("GetItemByName", &GameScript::GetItemByName, this);
|
2021-07-21 18:19:57 +01:00
|
|
|
|
|
|
|
/***
|
2021-07-23 16:02:30 +01:00
|
|
|
Get a MeshInfo by its name.
|
2021-07-21 18:19:57 +01:00
|
|
|
@function GetMeshByName
|
|
|
|
@tparam string name the unique name of the mesh as set in, or generated by, Tomb Editor
|
|
|
|
@return a non-owning MeshInfo referencing the mesh.
|
|
|
|
*/
|
|
|
|
m_lua->set_function("GetMeshByName", &GameScript::GetMeshByName, this);
|
2021-07-23 02:09:52 +01:00
|
|
|
|
|
|
|
/***
|
2021-07-23 16:02:30 +01:00
|
|
|
Get a CameraInfo by its name.
|
2021-07-23 02:09:52 +01:00
|
|
|
@function GetCameraByName
|
|
|
|
@tparam string name the unique name of the camera as set in, or generated by, Tomb Editor
|
|
|
|
@return a non-owning CameraInfo referencing the camera.
|
|
|
|
*/
|
|
|
|
m_lua->set_function("GetCameraByName", &GameScript::GetCameraByName, this);
|
|
|
|
|
2021-07-23 16:02:30 +01:00
|
|
|
/***
|
|
|
|
Get a SinkInfo by its name.
|
|
|
|
@function GetSinkByName
|
|
|
|
@tparam string name the unique name of the sink as set in, or generated by, Tomb Editor
|
|
|
|
@return a non-owning CameraInfo referencing the sink.
|
|
|
|
*/
|
|
|
|
m_lua->set_function("GetSinkByName", &GameScript::GetSinkByName, this);
|
|
|
|
|
2021-07-28 18:44:24 +01:00
|
|
|
auto makeReadOnlyTable = [this](std::string const & tableName, auto const& container)
|
2021-07-20 00:16:15 +01:00
|
|
|
{
|
|
|
|
auto mt = tableName + "Meta";
|
|
|
|
// Put all the data in the metatable
|
|
|
|
m_lua->set(mt, sol::as_table(container));
|
|
|
|
|
|
|
|
// Make the metatable's __index refer to itself so that requests
|
|
|
|
// to the main table will go through to the metatable (and thus the
|
|
|
|
// container's members)
|
|
|
|
m_lua->safe_script(mt + ".__index = " + mt);
|
2021-07-13 13:21:13 +01:00
|
|
|
|
2021-07-20 00:16:15 +01:00
|
|
|
// Don't allow the table to have new elements put into it
|
|
|
|
m_lua->safe_script(mt + ".__newindex = function() error('" + tableName + " is read-only') end");
|
2021-07-13 13:21:13 +01:00
|
|
|
|
2021-07-20 00:16:15 +01:00
|
|
|
// Protect the metatable
|
|
|
|
m_lua->safe_script(mt + ".__metatable = 'metatable is protected'");
|
2021-07-13 13:21:13 +01:00
|
|
|
|
2021-07-20 00:16:15 +01:00
|
|
|
auto tab = m_lua->create_named_table(tableName);
|
|
|
|
m_lua->safe_script("setmetatable(" + tableName + ", " + mt + ")");
|
2021-07-13 13:21:13 +01:00
|
|
|
|
2021-07-20 00:16:15 +01:00
|
|
|
// point the initial metatable variable away from its contents. this is just for cleanliness
|
|
|
|
m_lua->safe_script(mt + "= nil");
|
|
|
|
};
|
2021-07-13 13:21:13 +01:00
|
|
|
|
2021-07-20 00:16:15 +01:00
|
|
|
makeReadOnlyTable("InvItem", kInventorySlots);
|
|
|
|
makeReadOnlyTable("ObjID", kObjIDs);
|
2021-07-13 13:21:13 +01:00
|
|
|
|
2021-08-05 21:36:18 +01:00
|
|
|
// LevelFuncs
|
|
|
|
std::string LevelFuncsName{ "LevelFuncs" };
|
|
|
|
std::string LevelFuncsNameMeta{ LevelFuncsName + "Meta" };
|
|
|
|
auto meta = sol::table{ *m_lua, sol::create };
|
|
|
|
m_lua->set(LevelFuncsNameMeta, meta);
|
|
|
|
meta.set_function("__newindex", &GameScript::SetLevelFunc, this);
|
|
|
|
meta.set("__metatable", "\"metatable is protected\"");
|
|
|
|
auto tab = m_lua->create_named_table(LevelFuncsName);
|
|
|
|
tab[sol::metatable_key] = meta;
|
|
|
|
m_lua->set(LevelFuncsNameMeta, sol::nil);
|
|
|
|
|
|
|
|
// Level
|
|
|
|
std::string LevelName{ "Level" };
|
|
|
|
std::string LevelNameMeta{ LevelName + "Meta" };
|
|
|
|
meta = sol::table{ *m_lua, sol::create };
|
|
|
|
m_lua->set(LevelNameMeta, meta);
|
|
|
|
meta.set_function("__index", &LuaVariables::GetVariable, &m_locals);
|
|
|
|
meta.set_function("__newindex", &LuaVariables::SetVariable, &m_locals);
|
|
|
|
meta.set("__metatable", "\"metatable is protected\"");
|
|
|
|
tab = m_lua->create_named_table(LevelName);
|
|
|
|
tab[sol::metatable_key] = meta;
|
|
|
|
m_lua->set(LevelNameMeta, sol::nil);
|
|
|
|
|
|
|
|
// Game
|
|
|
|
std::string GameName{ "Game" };
|
|
|
|
std::string GameNameMeta{ GameName + "Meta" };
|
|
|
|
meta = sol::table{ *m_lua, sol::create };
|
|
|
|
m_lua->set(GameName, meta);
|
|
|
|
meta.set_function("__index", &LuaVariables::GetVariable, &m_globals);
|
|
|
|
meta.set_function("__newindex", &LuaVariables::SetVariable, &m_globals);
|
|
|
|
meta.set("__metatable", "\"metatable is protected\"");
|
|
|
|
tab = m_lua->create_named_table(GameName);
|
|
|
|
tab[sol::metatable_key] = meta;
|
|
|
|
m_lua->set(GameNameMeta, sol::nil);
|
|
|
|
|
2021-06-28 18:35:16 +01:00
|
|
|
GameScriptItemInfo::Register(m_lua);
|
2021-07-21 18:12:17 +01:00
|
|
|
GameScriptItemInfo::SetNameCallbacks(
|
2021-07-23 16:02:30 +01:00
|
|
|
[this](std::string const& str, short num) { return AddLuaNameItem(str, num); },
|
|
|
|
[this](std::string const& str) { return RemoveLuaNameItem(str); }
|
2021-07-21 18:12:17 +01:00
|
|
|
);
|
|
|
|
|
|
|
|
GameScriptMeshInfo::Register(m_lua);
|
|
|
|
GameScriptMeshInfo::SetNameCallbacks(
|
|
|
|
[this](std::string const& str, MESH_INFO & info) { return AddLuaNameMesh(str, info); },
|
|
|
|
[this](std::string const& str) { return RemoveLuaNameMesh(str); }
|
|
|
|
);
|
2021-07-17 22:26:07 +01:00
|
|
|
|
2021-07-23 02:09:52 +01:00
|
|
|
GameScriptCameraInfo::Register(m_lua);
|
|
|
|
GameScriptCameraInfo::SetNameCallbacks(
|
|
|
|
[this](std::string const& str, LEVEL_CAMERA_INFO & info) { return AddLuaNameCamera(str, info); },
|
|
|
|
[this](std::string const& str) { return RemoveLuaNameCamera(str); }
|
|
|
|
);
|
|
|
|
|
2021-07-23 16:02:30 +01:00
|
|
|
GameScriptSinkInfo::Register(m_lua);
|
|
|
|
GameScriptSinkInfo::SetNameCallbacks(
|
|
|
|
[this](std::string const& str, SINK_INFO & info) { return AddLuaNameSink(str, info); },
|
|
|
|
[this](std::string const& str) { return RemoveLuaNameSink(str); }
|
|
|
|
);
|
|
|
|
|
2021-07-24 12:45:33 +01:00
|
|
|
GameScriptAIObject::Register(m_lua);
|
|
|
|
GameScriptAIObject::SetNameCallbacks(
|
|
|
|
[this](std::string const& str, AI_OBJECT & info) { return AddLuaNameAIObject(str, info); },
|
|
|
|
[this](std::string const& str) { return RemoveLuaNameAIObject(str); }
|
|
|
|
);
|
|
|
|
|
|
|
|
GameScriptSoundSourceInfo::Register(m_lua);
|
|
|
|
GameScriptSoundSourceInfo::SetNameCallbacks(
|
|
|
|
[this](std::string const& str, SOUND_SOURCE_INFO & info) { return AddLuaNameSoundSource(str, info); },
|
|
|
|
[this](std::string const& str) { return RemoveLuaNameSoundSource(str); }
|
|
|
|
);
|
|
|
|
|
2021-07-01 19:29:58 +01:00
|
|
|
GameScriptPosition::Register(m_lua);
|
|
|
|
GameScriptRotation::Register(m_lua);
|
2021-07-20 17:58:11 +01:00
|
|
|
GameScriptColor::Register(m_lua);
|
2021-07-01 19:29:58 +01:00
|
|
|
|
2020-12-21 13:16:29 -03:00
|
|
|
m_lua->new_enum<GAME_OBJECT_ID>("Object", {
|
|
|
|
{"LARA", ID_LARA}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
void GameScript::AddTrigger(LuaFunction* function)
|
|
|
|
{
|
|
|
|
m_triggers.push_back(function);
|
|
|
|
(*m_lua).script(function->Code);
|
|
|
|
}
|
|
|
|
|
2021-08-05 21:36:18 +01:00
|
|
|
bool GameScript::SetLevelFunc(sol::table tab, std::string const& luaName, sol::object value)
|
|
|
|
{
|
|
|
|
switch (value.get_type())
|
|
|
|
{
|
|
|
|
case sol::type::lua_nil:
|
|
|
|
m_levelFuncs.erase(luaName);
|
|
|
|
tab.raw_set(luaName, value);
|
|
|
|
break;
|
|
|
|
case sol::type::function:
|
2021-08-06 16:47:24 +01:00
|
|
|
m_levelFuncs.insert(luaName);
|
2021-08-05 21:36:18 +01:00
|
|
|
tab.raw_set(luaName, value);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
std::string error{ "Could not assign LevelFuncs." };
|
|
|
|
error += luaName + "; it must be a function (or nil).";
|
|
|
|
return ScriptAssert(false, error);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-07-23 16:02:30 +01:00
|
|
|
bool GameScript::RemoveLuaNameItem(std::string const & luaName)
|
2020-12-21 13:16:29 -03:00
|
|
|
{
|
2021-07-17 22:26:07 +01:00
|
|
|
return m_itemsMapName.erase(luaName);
|
|
|
|
}
|
|
|
|
|
2021-07-23 16:02:30 +01:00
|
|
|
bool GameScript::AddLuaNameItem(std::string const & luaName, short itemNumber)
|
2021-07-17 22:26:07 +01:00
|
|
|
{
|
|
|
|
return m_itemsMapName.insert(std::pair<std::string, short>(luaName, itemNumber)).second;
|
2020-12-21 13:16:29 -03:00
|
|
|
}
|
|
|
|
|
2021-07-21 18:12:17 +01:00
|
|
|
bool GameScript::RemoveLuaNameMesh(std::string const & luaName)
|
|
|
|
{
|
|
|
|
return m_meshesMapName.erase(luaName);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool GameScript::AddLuaNameMesh(std::string const& luaName, MESH_INFO& infoRef)
|
|
|
|
{
|
|
|
|
return m_meshesMapName.insert(std::pair<std::string, MESH_INFO&>(luaName, infoRef)).second;
|
|
|
|
}
|
|
|
|
|
2021-07-23 02:09:52 +01:00
|
|
|
bool GameScript::RemoveLuaNameCamera(std::string const & luaName)
|
|
|
|
{
|
|
|
|
return m_camerasMapName.erase(luaName);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool GameScript::AddLuaNameCamera(std::string const& luaName, LEVEL_CAMERA_INFO& infoRef)
|
|
|
|
{
|
|
|
|
return m_camerasMapName.insert(std::pair<std::string, LEVEL_CAMERA_INFO&>(luaName, infoRef)).second;
|
|
|
|
}
|
|
|
|
|
2021-07-23 16:02:30 +01:00
|
|
|
bool GameScript::RemoveLuaNameSink(std::string const & luaName)
|
|
|
|
{
|
|
|
|
return m_sinksMapName.erase(luaName);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool GameScript::AddLuaNameSink(std::string const& luaName, SINK_INFO& infoRef)
|
|
|
|
{
|
|
|
|
return m_sinksMapName.insert(std::pair<std::string, SINK_INFO&>(luaName, infoRef)).second;
|
|
|
|
}
|
2021-07-24 12:45:33 +01:00
|
|
|
bool GameScript::RemoveLuaNameSoundSource(std::string const & luaName)
|
|
|
|
{
|
|
|
|
return m_soundSourcesMapName.erase(luaName);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool GameScript::AddLuaNameSoundSource(std::string const& luaName, SOUND_SOURCE_INFO& infoRef)
|
|
|
|
{
|
|
|
|
return m_soundSourcesMapName.insert(std::pair<std::string, SOUND_SOURCE_INFO&>(luaName, infoRef)).second;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool GameScript::RemoveLuaNameAIObject(std::string const & luaName)
|
|
|
|
{
|
|
|
|
return m_aiObjectsMapName.erase(luaName);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool GameScript::AddLuaNameAIObject(std::string const& luaName, AI_OBJECT & ref)
|
|
|
|
{
|
|
|
|
return m_aiObjectsMapName.insert(std::pair<std::string, AI_OBJECT&>(luaName, ref)).second;
|
|
|
|
}
|
2021-07-23 16:02:30 +01:00
|
|
|
|
2020-12-21 13:16:29 -03:00
|
|
|
void GameScript::FreeLevelScripts()
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
// Delete all triggers
|
|
|
|
for (int i = 0; i < m_triggers.size(); i++)
|
|
|
|
{
|
|
|
|
LuaFunction* trigger = m_triggers[i];
|
|
|
|
char* name = (char*)trigger->Name.c_str();
|
|
|
|
(*m_lua)[name] = NULL;
|
|
|
|
delete m_triggers[i];
|
|
|
|
}
|
|
|
|
m_triggers.clear();
|
|
|
|
|
|
|
|
// Clear the items mapping
|
|
|
|
m_itemsMap.clear();
|
|
|
|
|
|
|
|
(*m_lua)["Lara"] = NULL;
|
|
|
|
//delete m_Lara;
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
bool GameScript::ExecuteTrigger(short index)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
/*
|
|
|
|
// Is this a valid trigger?
|
|
|
|
if (index >= m_triggers.size())
|
|
|
|
return true;
|
|
|
|
|
|
|
|
LuaFunction* trigger = m_triggers[index];
|
|
|
|
|
|
|
|
// We want to execute a trigger just one time
|
|
|
|
// TODO: implement in the future continoous trigger?
|
|
|
|
if (trigger->Executed)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// Get the trigger function name
|
|
|
|
char* name = (char*)trigger->Name.c_str();
|
|
|
|
|
|
|
|
// Execute trigger
|
|
|
|
bool result = (*m_lua)[name]();
|
|
|
|
|
|
|
|
// Trigger was executed, don't execute it anymore
|
|
|
|
trigger->Executed = result;
|
|
|
|
|
|
|
|
m_locals.for_each([&](sol::object const& key, sol::object const& value) {
|
|
|
|
if (value.is<bool>())
|
2021-06-29 05:00:15 +02:00
|
|
|
std::cout << key.as<std::string>() << " " << value.as<bool>() << std::endl;
|
|
|
|
else if (value.is<std::string>())
|
|
|
|
std::cout << key.as<std::string>() << " " << value.as<std::string>() << std::endl;
|
2020-12-21 13:16:29 -03:00
|
|
|
else
|
2021-06-29 05:00:15 +02:00
|
|
|
std::cout << key.as<std::string>() << " " << value.as<int>() << std::endl;
|
2020-12-21 13:16:29 -03:00
|
|
|
});
|
|
|
|
|
|
|
|
return result;
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
2021-08-05 21:36:18 +01:00
|
|
|
void JumpToLevel(int levelNum)
|
2020-12-21 13:16:29 -03:00
|
|
|
{
|
|
|
|
if (levelNum >= g_GameFlow->GetNumLevels())
|
|
|
|
return;
|
|
|
|
LevelComplete = levelNum;
|
|
|
|
}
|
|
|
|
|
2021-08-05 21:36:18 +01:00
|
|
|
int GetSecretsCount()
|
2020-12-21 13:16:29 -03:00
|
|
|
{
|
|
|
|
return Savegame.Level.Secrets;
|
|
|
|
}
|
|
|
|
|
2021-08-05 21:36:18 +01:00
|
|
|
void SetSecretsCount(int secretsNum)
|
2020-12-21 13:16:29 -03:00
|
|
|
{
|
|
|
|
if (secretsNum > 255)
|
|
|
|
return;
|
|
|
|
Savegame.Level.Secrets = secretsNum;
|
|
|
|
}
|
|
|
|
|
2021-08-05 21:36:18 +01:00
|
|
|
void AddOneSecret()
|
2020-12-21 13:16:29 -03:00
|
|
|
{
|
|
|
|
if (Savegame.Level.Secrets >= 255)
|
|
|
|
return;
|
|
|
|
Savegame.Level.Secrets++;
|
2021-06-29 05:00:15 +02:00
|
|
|
S_CDPlay(TRACK_FOUND_SECRET, 0);
|
2020-12-21 13:16:29 -03:00
|
|
|
}
|
2021-06-29 05:00:15 +02:00
|
|
|
|
2020-12-21 13:16:29 -03:00
|
|
|
/*
|
|
|
|
void GameScript::MakeItemInvisible(short id)
|
|
|
|
{
|
|
|
|
if (m_itemsMap.find(id) == m_itemsMap.end())
|
|
|
|
return;
|
|
|
|
|
|
|
|
short itemNum = m_itemsMap[id];
|
|
|
|
|
|
|
|
ITEM_INFO* item = &g_Level.Items[itemNum];
|
|
|
|
|
|
|
|
if (item->active)
|
|
|
|
{
|
|
|
|
if (Objects[item->objectNumber].intelligent)
|
|
|
|
{
|
|
|
|
if (item->status == ITEM_ACTIVE)
|
|
|
|
{
|
|
|
|
item->touchBits = 0;
|
|
|
|
item->status = ITEM_INVISIBLE;
|
|
|
|
DisableBaddieAI(itemNum);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
item->touchBits = 0;
|
|
|
|
item->status = ITEM_INVISIBLE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
template <typename T>
|
2021-06-29 05:00:15 +02:00
|
|
|
void GameScript::GetVariables(std::map<std::string, T>& locals, std::map<std::string, T>& globals)
|
2020-12-21 13:16:29 -03:00
|
|
|
{
|
|
|
|
for (const auto& it : m_locals.variables)
|
|
|
|
{
|
|
|
|
if (it.second.is<T>())
|
2021-06-29 05:00:15 +02:00
|
|
|
locals.insert(std::pair<std::string, T>(it.first, it.second.as<T>()));
|
2020-12-21 13:16:29 -03:00
|
|
|
}
|
|
|
|
for (const auto& it : m_globals.variables)
|
|
|
|
{
|
|
|
|
if (it.second.is<T>())
|
2021-06-29 05:00:15 +02:00
|
|
|
globals.insert(std::pair<std::string, T>(it.first, it.second.as<T>()));
|
2020-12-21 13:16:29 -03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-29 05:00:15 +02:00
|
|
|
template void GameScript::GetVariables<bool>(std::map<std::string, bool>& locals, std::map<std::string, bool>& globals);
|
|
|
|
template void GameScript::GetVariables<float>(std::map<std::string, float>& locals, std::map<std::string, float>& globals);
|
|
|
|
template void GameScript::GetVariables<std::string>(std::map<std::string, std::string>& locals, std::map<std::string, std::string>& globals);
|
2020-12-21 13:16:29 -03:00
|
|
|
|
|
|
|
template <typename T>
|
2021-06-29 05:00:15 +02:00
|
|
|
void GameScript::SetVariables(std::map<std::string, T>& locals, std::map<std::string, T>& globals)
|
2020-12-21 13:16:29 -03:00
|
|
|
{
|
|
|
|
m_locals.variables.clear();
|
|
|
|
for (const auto& it : locals)
|
|
|
|
{
|
2021-06-29 05:00:15 +02:00
|
|
|
m_locals.variables.insert(std::pair<std::string, sol::object>(it.first, sol::object(m_lua->lua_state(), sol::in_place, it.second)));
|
2020-12-21 13:16:29 -03:00
|
|
|
}
|
|
|
|
for (const auto& it : globals)
|
|
|
|
{
|
2021-06-29 05:00:15 +02:00
|
|
|
m_globals.variables.insert(std::pair<std::string, sol::object>(it.first, sol::object(m_lua->lua_state(), sol::in_place, it.second)));
|
2020-12-21 13:16:29 -03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-29 05:00:15 +02:00
|
|
|
template void GameScript::SetVariables<bool>(std::map<std::string, bool>& locals, std::map<std::string, bool>& globals);
|
|
|
|
template void GameScript::SetVariables<float>(std::map<std::string, float>& locals, std::map<std::string, float>& globals);
|
|
|
|
template void GameScript::SetVariables<std::string>(std::map<std::string, std::string>& locals, std::map<std::string, std::string>& globals);
|
2020-12-21 13:16:29 -03:00
|
|
|
|
2021-07-23 16:02:30 +01:00
|
|
|
template <typename T, typename Stored>
|
2021-08-06 16:47:24 +01:00
|
|
|
std::unique_ptr<T> GetTByName(std::string const & type, std::string const& name, std::unordered_map<std::string, Stored> const & map)
|
2020-12-21 13:16:29 -03:00
|
|
|
{
|
2021-08-04 16:21:00 +01:00
|
|
|
ScriptAssert(map.find(name) != map.end(), std::string{ type + " name not found: " + name }, ERROR_MODE::TERMINATE);
|
2021-07-23 16:02:30 +01:00
|
|
|
return std::make_unique<T>(map.at(name), false);
|
2020-12-21 13:16:29 -03:00
|
|
|
}
|
|
|
|
|
2021-07-23 16:02:30 +01:00
|
|
|
std::unique_ptr<GameScriptItemInfo> GameScript::GetItemByName(std::string const & name)
|
2021-07-23 02:09:52 +01:00
|
|
|
{
|
2021-07-24 12:29:25 +01:00
|
|
|
return GetTByName<GameScriptItemInfo, short>("ItemInfo", name, m_itemsMapName);
|
2021-07-23 16:02:30 +01:00
|
|
|
}
|
2021-07-23 02:09:52 +01:00
|
|
|
|
2021-07-23 16:02:30 +01:00
|
|
|
std::unique_ptr<GameScriptMeshInfo> GameScript::GetMeshByName(std::string const & name)
|
|
|
|
{
|
2021-07-24 12:29:25 +01:00
|
|
|
return GetTByName<GameScriptMeshInfo, MESH_INFO &>("MeshInfo", name, m_meshesMapName);
|
2021-07-23 02:09:52 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
std::unique_ptr<GameScriptCameraInfo> GameScript::GetCameraByName(std::string const & name)
|
|
|
|
{
|
2021-07-24 12:29:25 +01:00
|
|
|
return GetTByName<GameScriptCameraInfo, LEVEL_CAMERA_INFO &>("CameraInfo", name, m_camerasMapName);
|
2021-07-23 16:02:30 +01:00
|
|
|
}
|
2021-07-23 02:09:52 +01:00
|
|
|
|
2021-07-23 16:02:30 +01:00
|
|
|
std::unique_ptr<GameScriptSinkInfo> GameScript::GetSinkByName(std::string const & name)
|
|
|
|
{
|
2021-07-24 12:29:25 +01:00
|
|
|
return GetTByName<GameScriptSinkInfo, SINK_INFO &>("SinkInfo", name, m_sinksMapName);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::unique_ptr<GameScriptAIObject> GameScript::GetAIObjectByName(std::string const & name)
|
|
|
|
{
|
|
|
|
return GetTByName<GameScriptAIObject, AI_OBJECT &>("AIObject", name, m_aiObjectsMapName);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::unique_ptr<GameScriptSoundSourceInfo> GameScript::GetSoundSourceByName(std::string const & name)
|
|
|
|
{
|
|
|
|
return GetTByName<GameScriptSoundSourceInfo, SOUND_SOURCE_INFO &>("SoundSourceInfo", name, m_soundSourcesMapName);
|
2021-07-23 02:09:52 +01:00
|
|
|
}
|
|
|
|
|
2020-12-21 13:16:29 -03:00
|
|
|
|
2021-06-29 05:00:15 +02:00
|
|
|
void GameScript::AssignItemsAndLara()
|
2020-12-21 13:16:29 -03:00
|
|
|
{
|
2021-07-21 18:09:53 +01:00
|
|
|
m_lua->set("Lara", GameScriptItemInfo(Lara.itemNumber, false)); // do we need GetLara if we have this?
|
2020-12-21 13:16:29 -03:00
|
|
|
}
|
|
|
|
|
2021-06-29 05:00:15 +02:00
|
|
|
void GameScript::ResetVariables()
|
2020-12-21 13:16:29 -03:00
|
|
|
{
|
2021-06-29 05:00:15 +02:00
|
|
|
(*m_lua)["Lara"] = NULL;
|
|
|
|
}
|
2020-12-21 13:16:29 -03:00
|
|
|
|
2021-08-05 21:36:18 +01:00
|
|
|
int CalculateDistance(GameScriptPosition pos1, GameScriptPosition pos2)
|
2021-06-29 05:00:15 +02:00
|
|
|
{
|
2021-07-03 23:18:10 +01:00
|
|
|
return sqrt(SQUARE(pos1.x - pos2.x) + SQUARE(pos1.y - pos2.y) + SQUARE(pos1.z - pos2.z));
|
2020-12-21 13:16:29 -03:00
|
|
|
}
|
|
|
|
|
2021-08-05 21:36:18 +01:00
|
|
|
int CalculateHorizontalDistance(GameScriptPosition pos1, GameScriptPosition pos2)
|
2020-12-21 13:16:29 -03:00
|
|
|
{
|
2021-07-03 23:18:10 +01:00
|
|
|
return sqrt(SQUARE(pos1.x - pos2.x) + SQUARE(pos1.z - pos2.z));
|
2021-06-29 05:00:15 +02:00
|
|
|
}
|
2020-12-21 13:16:29 -03:00
|
|
|
|
2021-08-05 21:36:18 +01:00
|
|
|
sol::object LuaVariables::GetVariable(sol::table tab, std::string key)
|
2020-12-21 13:16:29 -03:00
|
|
|
{
|
|
|
|
if (variables.find(key) == variables.end())
|
|
|
|
return sol::lua_nil;
|
|
|
|
return variables[key];
|
|
|
|
}
|
|
|
|
|
2021-08-05 21:36:18 +01:00
|
|
|
void LuaVariables::SetVariable(sol::table tab, std::string key, sol::object value)
|
2020-12-21 13:16:29 -03:00
|
|
|
{
|
|
|
|
switch (value.get_type())
|
|
|
|
{
|
|
|
|
case sol::type::lua_nil:
|
|
|
|
variables.erase(key);
|
|
|
|
break;
|
|
|
|
case sol::type::boolean:
|
|
|
|
case sol::type::number:
|
|
|
|
case sol::type::string:
|
|
|
|
variables[key] = value;
|
|
|
|
break;
|
|
|
|
default:
|
2021-08-04 16:51:28 +01:00
|
|
|
ScriptAssert(false, "Variable " + key + " has an unsupported type.", ERROR_MODE::TERMINATE);
|
2020-12-21 13:16:29 -03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2021-06-29 05:00:15 +02:00
|
|
|
|
2021-07-17 22:26:07 +01:00
|
|
|
void GameScript::ExecuteFunction(std::string const & name)
|
2021-07-17 05:56:40 +02:00
|
|
|
{
|
2021-08-05 21:36:18 +01:00
|
|
|
sol::protected_function func = (*m_lua)["LevelFuncs"][name.c_str()];
|
2021-07-17 05:56:40 +02:00
|
|
|
auto r = func();
|
2021-08-04 16:51:28 +01:00
|
|
|
if (!r.valid())
|
2021-07-17 05:56:40 +02:00
|
|
|
{
|
|
|
|
sol::error err = r;
|
2021-08-04 16:51:28 +01:00
|
|
|
ScriptAssert(false, err.what(), ERROR_MODE::TERMINATE);
|
2021-07-17 22:26:07 +01:00
|
|
|
}
|
2021-07-17 05:56:40 +02:00
|
|
|
}
|
|
|
|
|
2021-07-03 23:16:28 +01:00
|
|
|
static void doCallback(sol::protected_function const & func) {
|
|
|
|
auto r = func();
|
2021-08-04 16:51:28 +01:00
|
|
|
if (!r.valid())
|
2021-07-03 23:16:28 +01:00
|
|
|
{
|
|
|
|
sol::error err = r;
|
2021-08-04 16:51:28 +01:00
|
|
|
ScriptAssert(false, err.what(), ERROR_MODE::TERMINATE);
|
2021-07-03 23:16:28 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-01 19:33:48 +01:00
|
|
|
void GameScript::OnStart()
|
|
|
|
{
|
2021-07-06 11:54:34 +02:00
|
|
|
if (m_onStart.valid())
|
|
|
|
doCallback(m_onStart);
|
2021-07-01 19:33:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void GameScript::OnLoad()
|
|
|
|
{
|
2021-08-04 16:51:28 +01:00
|
|
|
if(m_onLoad.valid())
|
2021-07-06 11:54:34 +02:00
|
|
|
doCallback(m_onLoad);
|
2021-07-01 19:33:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void GameScript::OnControlPhase()
|
|
|
|
{
|
2021-08-04 16:51:28 +01:00
|
|
|
if(m_onControlPhase.valid())
|
2021-07-06 11:54:34 +02:00
|
|
|
doCallback(m_onControlPhase);
|
2021-07-01 19:33:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void GameScript::OnSave()
|
|
|
|
{
|
2021-08-04 16:51:28 +01:00
|
|
|
if(m_onSave.valid())
|
2021-07-06 11:54:34 +02:00
|
|
|
doCallback(m_onSave);
|
2021-07-01 19:33:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void GameScript::OnEnd()
|
|
|
|
{
|
2021-08-04 16:51:28 +01:00
|
|
|
if(m_onEnd.valid())
|
2021-07-06 11:54:34 +02:00
|
|
|
doCallback(m_onEnd);
|
2021-07-01 19:33:48 +01:00
|
|
|
}
|
|
|
|
|
2021-08-05 21:36:18 +01:00
|
|
|
/*** Special tables
|
|
|
|
|
|
|
|
TombEngine uses the following tables for specific things.
|
|
|
|
|
|
|
|
@section levelandgametables
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*** A table with level-specific data which will be saved and loaded.
|
|
|
|
This is for level-specific information that you want to store in saved games.
|
|
|
|
|
|
|
|
For example, you may have a level with a custom puzzle where Lara has
|
|
|
|
to kill exactly seven enemies to open a door to a secret. You could use
|
|
|
|
the following line each time an enemy is killed:
|
|
|
|
|
|
|
|
Level.enemiesKilled = Level.enemiesKilled + 1
|
|
|
|
|
|
|
|
If the player saves the level after killing three, saves, and then reloads the save
|
|
|
|
some time later, the values `3` will be put back into `Level.enemiesKilled.`
|
|
|
|
|
|
|
|
__This table is emptied when a level is finished.__ If the player needs to be able
|
|
|
|
to return to the level (e.g. like the Karnak level in *The Last Revelation*,
|
|
|
|
you will need to use the @{Game} table, below.
|
|
|
|
@table Level
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*** A table with game data which will be saved and loaded.
|
|
|
|
This is for information not specific to any level, but which concerns your whole
|
|
|
|
levelset or game, that you want to store in saved games.
|
|
|
|
|
|
|
|
For example, you may wish to have a final boss say a specific voice line based on
|
|
|
|
a choice the player made in a previous level. In the level with the choice, you could
|
|
|
|
write:
|
|
|
|
|
|
|
|
Game.playerSnoopedInDraws = true
|
|
|
|
|
|
|
|
And in the script file for the level with the boss, you could write:
|
|
|
|
|
|
|
|
if Game.playerSnoopedInDraws then
|
|
|
|
PlayAudioTrack("how_dare_you.wav")
|
|
|
|
end
|
|
|
|
|
|
|
|
Unlike @{Level}, this table will remain intact for the entirety of the game.
|
|
|
|
@table Game
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*** A table with level-specific functions.
|
|
|
|
|
|
|
|
This serves two purposes: it holds the level callbacks (listed below) as well as
|
|
|
|
any trigger functions you might have specified. For example, if you give a trigger
|
|
|
|
a Lua name of "my_trigger" in Tomb Editor, you will have to implement it as a member
|
|
|
|
of this table:
|
|
|
|
|
|
|
|
LevelFuncs.my_trigger = function()
|
|
|
|
-- implementation goes here
|
|
|
|
end
|
|
|
|
|
|
|
|
The following are the level callbacks. They are optional; if your level has no special
|
|
|
|
behaviour for a particular scenario, you do not need to implement the function. For
|
|
|
|
example, if your level does not need any special initialisation when it is loaded,
|
|
|
|
you can just leave out `LevelFuncs.OnStart`.
|
|
|
|
|
|
|
|
@tfield function OnStart Will be called when a level is loaded
|
|
|
|
@tfield function OnLoad Will be called when a saved game is loaded
|
|
|
|
@tfield function OnControlPhase Will be called once per frame
|
|
|
|
@tfield function OnSave Will be called when the player saves the game
|
|
|
|
@tfield function OnEnd Will be called when leaving a level. This includes finishing it, exiting to the menu, or loading a save in a different level.
|
|
|
|
@table LevelFuncs
|
|
|
|
*/
|
|
|
|
|
2021-07-01 19:33:48 +01:00
|
|
|
void GameScript::InitCallbacks()
|
|
|
|
{
|
2021-08-04 16:51:28 +01:00
|
|
|
auto assignCB = [this](sol::protected_function& func, std::string const & luaFunc) {
|
2021-08-05 21:36:18 +01:00
|
|
|
std::string fullName = "LevelFuncs." + luaFunc;
|
|
|
|
func = (*m_lua)["LevelFuncs"][luaFunc];
|
|
|
|
std::string err{ "Level's script does not define callback " + fullName};
|
2021-08-04 16:51:28 +01:00
|
|
|
if (!ScriptAssert(func.valid(), err)) {
|
2021-08-06 16:48:03 +01:00
|
|
|
ScriptWarn("Defaulting to no " + fullName + " behaviour.");
|
2021-07-01 19:33:48 +01:00
|
|
|
}
|
|
|
|
};
|
2021-08-04 16:51:28 +01:00
|
|
|
|
2021-07-01 19:33:48 +01:00
|
|
|
assignCB(m_onStart, "OnStart");
|
|
|
|
assignCB(m_onLoad, "OnLoad");
|
|
|
|
assignCB(m_onControlPhase, "OnControlPhase");
|
|
|
|
assignCB(m_onSave, "OnSave");
|
|
|
|
assignCB(m_onEnd, "OnEnd");
|
|
|
|
}
|