Savegame functions (#1217)

* Possibly make loading work

* fix compile error

* Move LevelComplete reset to more appropriate place

* Implement Delete save and move functions to FlowHandler

* Fixes and optimizations

* Rename global

* rename func names to more meaningful ones, correct grammar.

* add a check to see if file actually exists before loading

* correct.

* add LogLevel for Delete() func

* Revert "correct."

This reverts commit 5e332bba95.

* Revert "add a check to see if file actually exists before loading"

This reverts commit ef9fb63aa2.

* Fix attempts to load savegames from incorrect slots

* Simplify savegame delete function

* Update Changes.txt

* Minor changes

* Disable Save/Load quick actions when EnableLoadSave is false

* Formatting

* Implement enable load/save into lua

* Fix merge

* Update Changes.txt

* Update savegame.cpp

* Fix savegame count not properly increasing, don't reload headers every frame in menu

* Update savegame.cpp

* Set TimeInMenu default value to -1.0f

* Minor changes

---------

Co-authored-by: Lwmte <3331699+Lwmte@users.noreply.github.com>
Co-authored-by: Sezz <sezzary@outlook.com>
This commit is contained in:
Jakub 2023-09-25 11:06:40 +01:00 committed by GitHub
parent ae03c3e046
commit bfe545e375
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 229 additions and 71 deletions

View file

@ -12,6 +12,7 @@ Version 1.1.1
* Fix TR2 doberman crashing the game when killed by explosive weapons.
* Fix volume change in settings not affecting voice track.
* Fix several lighting bugs.
* Fix savegame count not properly increasing.
* Overhaul look mode:
- Allow for consistent and wider viewing angles while crawling, crouching, and hanging.
- Improve movement and control.
@ -25,10 +26,12 @@ Version 1.1.1
- A negative value requires a trigger to activate.
Lua API changes:
* Add Flow::EnableLoadSave() function to disable savegames.
* Add Flow::EnablePointFilter() function to disable bilinear filtering.
* Add Lara::GetAmmoType() function to read the ammo that Lara is using.
* Add Lara::GetControlLock() and Lara::SetControlLock() functions to handle controls locking.
* Add Logic.HandleEvent() function to call node events.
* Add functions to load, save, and delete savegames.
* Make Vec2 object float-based instead of integer-based.
* Add Vec2 arithmetic for division with a number and multiplication with another Vec2.
* Add log messages warnings to functions AddCallback and RemoveCallback.

View file

@ -93,7 +93,7 @@ bool ThreadEnded;
int RequiredStartPos;
int CurrentLevel;
int LevelComplete;
int NextLevel;
int SystemNameHash = 0;
@ -614,16 +614,20 @@ GameStatus HandleMenuCalls(bool isTitle)
// Does the player want to enter inventory?
if (IsClicked(In::Save) && LaraItem->HitPoints > 0 &&
g_Gui.GetInventoryMode() != InventoryMode::Save)
g_Gui.GetInventoryMode() != InventoryMode::Save &&
g_GameFlow->IsLoadSaveEnabled())
{
SaveGame::LoadSavegameInfos();
g_Gui.SetInventoryMode(InventoryMode::Save);
if (g_Gui.CallInventory(LaraItem, false))
result = GameStatus::SaveGame;
}
else if (IsClicked(In::Load) &&
g_Gui.GetInventoryMode() != InventoryMode::Load)
g_Gui.GetInventoryMode() != InventoryMode::Load &&
g_GameFlow->IsLoadSaveEnabled())
{
SaveGame::LoadSavegameInfos();
g_Gui.SetInventoryMode(InventoryMode::Load);
if (g_Gui.CallInventory(LaraItem, false))
@ -668,8 +672,16 @@ GameStatus HandleGlobalInputEvents(bool isTitle)
}
// Check if level has been completed.
if (LevelComplete)
// Negative NextLevel indicates that a savegame must be loaded from corresponding slot.
if (NextLevel > 0)
{
return GameStatus::LevelComplete;
}
else if (NextLevel < 0)
{
g_GameFlow->SelectedSaveGame = -(NextLevel + 1);
return GameStatus::LoadGame;
}
return GameStatus::None;
}

View file

@ -57,7 +57,7 @@ extern bool ThreadEnded;
extern int RequiredStartPos;
extern int CurrentLevel;
extern int LevelComplete;
extern int NextLevel;
extern int SystemNameHash;
extern bool InItemControlLoop;

View file

@ -332,7 +332,7 @@ void Turn180(ItemInfo* item)
void FinishLevel(ItemInfo* item)
{
LevelComplete = CurrentLevel + 1;
NextLevel = CurrentLevel + 1;
}
void VoidEffect(ItemInfo* item)

View file

@ -751,7 +751,7 @@ void TestTriggers(int x, int y, int z, FloorInfo* floor, VolumeActivator activat
case TO_FINISH:
RequiredStartPos = false;
LevelComplete = CurrentLevel + 1;
NextLevel = CurrentLevel + 1;
break;
case TO_CD:

View file

@ -202,7 +202,7 @@ namespace TEN::Gui
return Rings[(int)ringType];
}
short GuiController::GetSelectedOption()
int GuiController::GetSelectedOption()
{
return SelectedOption;
}
@ -295,7 +295,7 @@ namespace TEN::Gui
switch (MenuToDisplay)
{
case Menu::Title:
OptionCount = numTitleOptions;
OptionCount = g_GameFlow->IsLoadSaveEnabled() ? numTitleOptions : (numTitleOptions - 1);
break;
case Menu::SelectLevel:
@ -378,7 +378,12 @@ namespace TEN::Gui
if (MenuToDisplay == Menu::Title)
{
switch (SelectedOption)
// Skip load game entry if loading and saving is disabled.
int realSelectedOption = SelectedOption;
if (!g_GameFlow->IsLoadSaveEnabled() && SelectedOption > TitleOption::NewGame)
realSelectedOption++;
switch (realSelectedOption)
{
case TitleOption::NewGame:
if (g_GameFlow->IsLevelSelectEnabled())
@ -388,7 +393,9 @@ namespace TEN::Gui
MenuToDisplay = Menu::SelectLevel;
}
else
{
inventoryResult = InventoryResult::NewGame;
}
break;
@ -396,6 +403,7 @@ namespace TEN::Gui
selectedOptionBackup = SelectedOption;
SelectedOption = 0;
MenuToDisplay = Menu::LoadGame;
SaveGame::LoadSavegameInfos();
break;
case TitleOption::Options:
@ -1680,7 +1688,7 @@ namespace TEN::Gui
if (lara->Inventory.Diary.Present)
InsertObjectIntoList(INV_OBJECT_DIARY);
if (g_GameFlow->EnableLoadSave)
if (g_GameFlow->IsLoadSaveEnabled())
{
InsertObjectIntoList(INV_OBJECT_LOAD_FLOPPY);
InsertObjectIntoList(INV_OBJECT_SAVE_FLOPPY);
@ -2532,12 +2540,12 @@ namespace TEN::Gui
break;
case MenuType::Load:
// fill_up_savegames_array // Maybe not?
SaveGame::LoadSavegameInfos();
SetInventoryMode(InventoryMode::Load);
break;
case MenuType::Save:
// fill_up_savegames_array
SaveGame::LoadSavegameInfos();
SetInventoryMode(InventoryMode::Save);
break;
@ -3386,7 +3394,7 @@ namespace TEN::Gui
}
}
short GuiController::GetLoadSaveSelection()
int GuiController::GetLoadSaveSelection()
{
return SelectedSaveSlot;
}
@ -3456,7 +3464,6 @@ namespace TEN::Gui
if (GuiIsSelected())
{
SoundEffect(SFX_TR4_MENU_CHOOSE, nullptr, SoundEnvironment::Always);
g_GameScript->OnSave();
SaveGame::Save(SelectedSaveSlot);
return true;
}

View file

@ -133,7 +133,7 @@ namespace TEN::Gui
int OptionCount;
int SelectedSaveSlot;
float TimeInMenu = 0.0f;
float TimeInMenu = -1.0f;
SettingsData CurrentSettings;
// Inventory variables
@ -183,14 +183,14 @@ namespace TEN::Gui
// Getters
const InventoryRing& GetRing(RingTypes ringType);
short GetSelectedOption();
int GetSelectedOption();
Menu GetMenuToDisplay();
InventoryMode GetInventoryMode();
int GetInventoryItemChosen();
int GetEnterInventory();
int GetLastInventoryItem();
SettingsData& GetCurrentSettings();
short GetLoadSaveSelection();
int GetLoadSaveSelection();
// Setters
void SetSelectedOption(int menu);

View file

@ -45,7 +45,9 @@ using namespace TEN::Entities::TR4;
namespace Save = TEN::Save;
const std::string SAVEGAME_PATH = "Save//";
constexpr auto SAVEGAME_MAX_SLOT = 99;
constexpr auto SAVEGAME_PATH = "Save//";
constexpr auto SAVEGAME_FILE_MASK = "savegame.";
GameStats Statistics;
SaveGameHeader SavegameInfos[SAVEGAME_MAX];
@ -62,21 +64,20 @@ void SaveGame::LoadSavegameInfos()
if (!std::filesystem::is_directory(FullSaveDirectory))
return;
// Reset overall savegame count.
LastSaveGame = 0;
// Try loading savegame.
for (int i = 0; i < SAVEGAME_MAX; i++)
{
auto fileName = FullSaveDirectory + "savegame." + std::to_string(i);
auto savegamePtr = fopen(fileName.c_str(), "rb");
if (savegamePtr == nullptr)
if (!DoesSaveGameExist(i))
continue;
fclose(savegamePtr);
SavegameInfos[i].Present = true;
SaveGame::LoadHeader(i, &SavegameInfos[i]);
fclose(savegamePtr);
if (SavegameInfos[i].Count > LastSaveGame)
LastSaveGame = SavegameInfos[i].Count;
}
}
@ -163,6 +164,33 @@ Vector4 ToVector4(const Save::Vector4* vec)
return Vector4(vec->x(), vec->y(), vec->z(), vec->w());
}
bool SaveGame::IsSaveGameSlotValid(int slot)
{
if (slot < 0 || slot > SAVEGAME_MAX_SLOT)
{
TENLog("Attempted to access invalid savegame slot " + std::to_string(slot), LogLevel::Warning);
return false;
}
return true;
}
bool SaveGame::DoesSaveGameExist(int slot)
{
if (!std::filesystem::is_regular_file(GetSavegameFilename(slot)))
{
TENLog("Attempted to access missing savegame slot " + std::to_string(slot), LogLevel::Warning);
return false;
}
return true;
}
std::string SaveGame::GetSavegameFilename(int slot)
{
return (FullSaveDirectory + SAVEGAME_FILE_MASK + std::to_string(slot));
}
#define SaveVec(Type, Data, TableBuilder, UnionType, SaveType, ConversionFunc) \
auto data = std::get<(int)Type>(Data); \
TableBuilder vtb{ fbb }; \
@ -178,7 +206,15 @@ void SaveGame::Init(const std::string& gameDirectory)
bool SaveGame::Save(int slot)
{
auto fileName = FullSaveDirectory + "savegame." + std::to_string(slot);
if (!IsSaveGameSlotValid(slot))
return false;
g_GameScript->OnSave();
// Savegame infos need to be reloaded so that last savegame counter properly increases.
SaveGame::LoadSavegameInfos();
auto fileName = GetSavegameFilename(slot);
TENLog("Saving to savegame: " + fileName, LogLevel::Info);
ItemInfo itemToSerialize{};
@ -1351,7 +1387,13 @@ bool SaveGame::Save(int slot)
bool SaveGame::Load(int slot)
{
auto fileName = FullSaveDirectory + "savegame." + std::to_string(slot);
if (!IsSaveGameSlotValid(slot))
return false;
if (!DoesSaveGameExist(slot))
return false;
auto fileName = GetSavegameFilename(slot);
TENLog("Loading from savegame: " + fileName, LogLevel::Info);
std::ifstream file;
@ -1366,7 +1408,6 @@ bool SaveGame::Load(int slot)
const Save::SaveGame* s = Save::GetSaveGame(buffer.get());
// Statistics
LastSaveGame = s->header()->count();
GameTimer = s->header()->timer();
Statistics.Game.AmmoHits = s->game()->ammo_hits();
@ -2206,7 +2247,13 @@ bool SaveGame::Load(int slot)
bool SaveGame::LoadHeader(int slot, SaveGameHeader* header)
{
auto fileName = FullSaveDirectory + "savegame." + std::to_string(slot);
if (!IsSaveGameSlotValid(slot))
return false;
if (!DoesSaveGameExist(slot))
return false;
auto fileName = GetSavegameFilename(slot);
std::ifstream file;
file.open(fileName, std::ios_base::app | std::ios_base::binary);
@ -2231,3 +2278,14 @@ bool SaveGame::LoadHeader(int slot, SaveGameHeader* header)
return true;
}
void SaveGame::Delete(int slot)
{
if (!IsSaveGameSlotValid(slot))
return;
if (!DoesSaveGameExist(slot))
return;
std::filesystem::remove(GetSavegameFilename(slot));
}

View file

@ -46,6 +46,9 @@ class SaveGame
private:
static FileStream* StreamPtr;
static std::string FullSaveDirectory;
static std::string SaveGame::GetSavegameFilename(int slot);
static bool IsSaveGameSlotValid(int slot);
public:
static int LastSaveGame;
@ -55,4 +58,7 @@ public:
static bool LoadHeader(int slot, SaveGameHeader* header);
static bool Save(int slot);
static void LoadSavegameInfos();
static void Delete(int slot);
static bool DoesSaveGameExist(int slot);
};

View file

@ -456,30 +456,33 @@ namespace TEN::Renderer
void Renderer11::RenderTitleMenu(Menu menu)
{
int y = MenuVerticalBottomCenter;
auto titleOption = g_Gui.GetSelectedOption();
// HACK: Check if it works properly -- Lwmte, 07.06.22
if (menu == Menu::LoadGame && !g_GameFlow->EnableLoadSave)
menu = Menu::Title;
int titleOption = g_Gui.GetSelectedOption();
int selectedOption = 0;
switch (menu)
{
case Menu::Title:
// New game
AddString(MenuCenterEntry, y, g_GameFlow->GetString(STRING_NEW_GAME), PRINTSTRING_COLOR_WHITE, SF_Center(titleOption == 0));
AddString(MenuCenterEntry, y, g_GameFlow->GetString(STRING_NEW_GAME), PRINTSTRING_COLOR_WHITE, SF_Center(titleOption == selectedOption));
GetNextLinePosition(&y);
selectedOption++;
// Load game
AddString(MenuCenterEntry, y, g_GameFlow->GetString(STRING_LOAD_GAME), PRINTSTRING_COLOR_WHITE, SF_Center(titleOption == 1));
GetNextLinePosition(&y);
if (g_GameFlow->IsLoadSaveEnabled())
{
AddString(MenuCenterEntry, y, g_GameFlow->GetString(STRING_LOAD_GAME), PRINTSTRING_COLOR_WHITE, SF_Center(titleOption == selectedOption));
GetNextLinePosition(&y);
selectedOption++;
}
// Options
AddString(MenuCenterEntry, y, g_GameFlow->GetString(STRING_OPTIONS), PRINTSTRING_COLOR_WHITE, SF_Center(titleOption == 2));
AddString(MenuCenterEntry, y, g_GameFlow->GetString(STRING_OPTIONS), PRINTSTRING_COLOR_WHITE, SF_Center(titleOption == selectedOption));
GetNextLinePosition(&y);
selectedOption++;
// Exit game
AddString(MenuCenterEntry, y, g_GameFlow->GetString(STRING_EXIT_GAME), PRINTSTRING_COLOR_WHITE, SF_Center(titleOption == 3));
AddString(MenuCenterEntry, y, g_GameFlow->GetString(STRING_EXIT_GAME), PRINTSTRING_COLOR_WHITE, SF_Center(titleOption == selectedOption));
break;
case Menu::LoadGame:
@ -496,10 +499,10 @@ namespace TEN::Renderer
GetNextBlockPosition(&y);
// Level listing (starts with 1 because 0 is always title)
for (int i = 1; i < g_GameFlow->GetNumLevels(); i++)
for (int i = 1; i < g_GameFlow->GetNumLevels(); i++, selectedOption++)
{
AddString(MenuCenterEntry, y, g_GameFlow->GetString(g_GameFlow->GetLevel(i)->NameStringKey.c_str()),
PRINTSTRING_COLOR_WHITE, SF_Center(titleOption == i - 1));
PRINTSTRING_COLOR_WHITE, SF_Center(titleOption == selectedOption));
GetNextNarrowLinePosition(&y);
}
break;
@ -565,7 +568,7 @@ namespace TEN::Renderer
void Renderer11::RenderLoadSaveMenu()
{
if (!g_GameFlow->EnableLoadSave)
if (!g_GameFlow->IsLoadSaveEnabled())
{
g_Gui.SetInventoryMode(InventoryMode::InGame);
return;
@ -575,7 +578,6 @@ namespace TEN::Renderer
int y = MenuVerticalLineSpacing;
short selection = g_Gui.GetLoadSaveSelection();
char stringBuffer[255];
SaveGame::LoadSavegameInfos();
// Title
AddString(MenuCenterEntry, MenuVerticalNarrowLineSpacing, Str_LoadSave(g_Gui.GetInventoryMode() == InventoryMode::Save),

View file

@ -1,23 +1,24 @@
#pragma once
enum class TITLE_TYPE
{
FLYBY,
BACKGROUND
};
class ScriptInterfaceLevel;
enum class TitleType
{
Flyby,
Background
};
class ScriptInterfaceFlowHandler
{
public:
std::string IntroImagePath{};
int SelectedLevelForNewGame{ 0 };
int SelectedSaveGame{ 0 };
bool EnableLoadSave{ true };
int TotalNumberOfSecrets{ 0 };
std::string TitleScreenImagePath{};
TITLE_TYPE TitleType{ TITLE_TYPE::FLYBY };
TitleType TitleType = TitleType::Flyby;
std::string IntroImagePath = {};
std::string TitleScreenImagePath = {};
int SelectedLevelForNewGame = 0;
int SelectedSaveGame = 0;
int TotalNumberOfSecrets = 0;
virtual ~ScriptInterfaceFlowHandler() = default;
@ -32,6 +33,7 @@ public:
virtual bool IsMassPickupEnabled() const = 0;
virtual bool IsPointFilterEnabled() const = 0;
virtual bool IsLaraInTitleEnabled() const = 0;
virtual bool IsLoadSaveEnabled() const = 0;
virtual bool HasCrawlExtended() const = 0;
virtual bool HasCrouchRoll() const = 0;
virtual bool HasCrawlspaceDive() const = 0;

View file

@ -173,6 +173,9 @@ static constexpr char ScriptReserved_SetFarView[] = "SetFarView";
static constexpr char ScriptReserved_SetSettings[] = "SetSettings";
static constexpr char ScriptReserved_SetAnimations[] = "SetAnimations";
static constexpr char ScriptReserved_EndLevel[] = "EndLevel";
static constexpr char ScriptReserved_SaveGame[] = "SaveGame";
static constexpr char ScriptReserved_LoadGame[] = "LoadGame";
static constexpr char ScriptReserved_DeleteSaveGame[] = "DeleteSaveGame";
static constexpr char ScriptReserved_GetSecretCount[] = "GetSecretCount";
static constexpr char ScriptReserved_SetSecretCount[] = "SetSecretCount";
static constexpr char ScriptReserved_SetTotalSecretCount[] = "SetTotalSecretCount";
@ -181,6 +184,7 @@ static constexpr char ScriptReserved_EnableFlyCheat[] = "EnableFlyCheat";
static constexpr char ScriptReserved_EnableMassPickup[] = "EnableMassPickup";
static constexpr char ScriptReserved_EnableLaraInTitle[] = "EnableLaraInTitle";
static constexpr char ScriptReserved_EnableLevelSelect[] = "EnableLevelSelect";
static constexpr char ScriptReserved_EnableLoadSave[] = "EnableLoadSave";
static constexpr char ScriptReserved_EnablePointFilter[] = "EnablePointFilter";
// Flow Functions

View file

@ -75,6 +75,12 @@ Must be true or false
*/
tableFlow.set_function(ScriptReserved_EnableLevelSelect, &FlowHandler::EnableLevelSelect, this);
/*** Enable or disable saving and loading of savegames.
@function EnableLoadSave
@tparam bool enabled true or false.
*/
tableFlow.set_function(ScriptReserved_EnableLoadSave, &FlowHandler::EnableLoadSave, this);
/*** gameflow.lua or level scripts.
@section FlowluaOrScripts
*/
@ -124,6 +130,27 @@ level count, jumps to title.
*/
tableFlow.set_function(ScriptReserved_EndLevel, &FlowHandler::EndLevel, this);
/***
Save the game to a savegame slot.
@function SaveGame
@tparam int slotID ID of the savegame slot to save to.
*/
tableFlow.set_function(ScriptReserved_SaveGame, &FlowHandler::SaveGame, this);
/***
Load the game from a savegame slot.
@function LoadGame
@tparam int slotID ID of the savegame slot to load from.
*/
tableFlow.set_function(ScriptReserved_LoadGame, &FlowHandler::LoadGame, this);
/***
Delete a savegame.
@function DeleteSaveGame
@tparam int slotID ID of the savegame slot to clear.
*/
tableFlow.set_function(ScriptReserved_DeleteSaveGame, &FlowHandler::DeleteSaveGame, this);
/***
Returns the player's current per-game secret count.
@function GetSecretCount
@ -382,7 +409,25 @@ int FlowHandler::GetLevelNumber(const std::string& fileName)
void FlowHandler::EndLevel(std::optional<int> nextLevel)
{
int index = (nextLevel.has_value() && nextLevel.value() != 0) ? nextLevel.value() : CurrentLevel + 1;
LevelComplete = index;
NextLevel = index;
}
void FlowHandler::SaveGame(int slot)
{
SaveGame::Save(slot);
}
void FlowHandler::LoadGame(int slot)
{
if (!SaveGame::DoesSaveGameExist(slot))
return;
NextLevel = -(slot + 1);
}
void FlowHandler::DeleteSaveGame(int slot)
{
SaveGame::Delete(slot);
}
int FlowHandler::GetSecretCount() const
@ -467,6 +512,16 @@ void FlowHandler::EnableLevelSelect(bool levelSelect)
LevelSelect = levelSelect;
}
bool FlowHandler::IsLoadSaveEnabled() const
{
return LoadSave;
}
void FlowHandler::EnableLoadSave(bool loadSave)
{
LoadSave = loadSave;
}
bool FlowHandler::DoFlow()
{
// We start with the title level, if no other index is specified
@ -565,21 +620,22 @@ bool FlowHandler::DoFlow()
// Load level
CurrentLevel = header.Level;
NextLevel = 0;
GameTimer = header.Timer;
loadFromSavegame = true;
break;
case GameStatus::LevelComplete:
if (LevelComplete >= Levels.size())
if (NextLevel >= Levels.size())
{
CurrentLevel = 0; // TODO: final credits
}
else
{
CurrentLevel = LevelComplete;
CurrentLevel = NextLevel;
}
LevelComplete = 0;
NextLevel = 0;
break;
}
}

View file

@ -26,17 +26,19 @@ private:
LuaHandler m_handler;
public:
int FogInDistance{ 0 };
int FogOutDistance{ 0 };
bool LevelSelect{ true };
bool FlyCheat{ true };
bool PointFilter{ false };
bool MassPickup{ true };
bool LaraInTitle{ false };
bool DebugMode{ false };
int FogInDistance = 0;
int FogOutDistance = 0;
// New animation flag table
Animations Anims{};
bool LevelSelect = true;
bool LoadSave = true;
bool FlyCheat = true;
bool PointFilter = false;
bool MassPickup = true;
bool LaraInTitle = false;
bool DebugMode = false;
// Table for movesets.
Animations Anims = {};
std::vector<Level*> Levels;
@ -58,6 +60,9 @@ public:
int GetLevelNumber(const std::string& flieName);
int GetNumLevels() const;
void EndLevel(std::optional<int> nextLevel);
void SaveGame(int slot);
void LoadGame(int slot);
void DeleteSaveGame(int slot);
int GetSecretCount() const;
void SetSecretCount(int secretsNum);
void AddSecret(int levelSecretIndex);
@ -74,6 +79,8 @@ public:
void EnableLaraInTitle(bool laraInTitle);
bool IsLevelSelectEnabled() const;
void EnableLevelSelect(bool laraInTitle);
bool IsLoadSaveEnabled() const;
void EnableLoadSave(bool loadSave);
bool HasCrawlExtended() const override { return Anims.HasCrawlExtended; }
bool HasCrouchRoll() const override { return Anims.HasCrouchRoll; }

View file

@ -455,6 +455,7 @@ namespace Misc
tableMisc.set_function(ScriptReserved_FlipMap, &FlipMap);
tableMisc.set_function(ScriptReserved_PlayFlyBy, &PlayFlyBy);
tableMisc.set_function(ScriptReserved_ResetObjCamera, &ResetObjCamera);
tableMisc.set_function(ScriptReserved_PrintLog, &PrintLog);
LuaHandler handler{ state };