diff --git a/TR5Main/Scripting/GameLogicScript.cpp b/TR5Main/Scripting/GameLogicScript.cpp index f2c608f03..a854833d6 100644 --- a/TR5Main/Scripting/GameLogicScript.cpp +++ b/TR5Main/Scripting/GameLogicScript.cpp @@ -14,6 +14,7 @@ #include "pickup.h" #include "newinv2.h" #include "ObjectIDs.h" +#include "GameScriptDisplayString.h" #include "ReservedScriptNames.h" /*** @@ -152,12 +153,6 @@ static int CalculateHorizontalDistance(GameScriptPosition const & pos1, GameScri return static_cast(round(result)); } -// Misc -static void PrintString(std::string key, GameScriptPosition pos, GameScriptColor color, int lifetime, int flags) -{ - -} - // A "special" table is essentially one that TEN reserves and does things with. template static void MakeSpecialTable(sol::state * state, std::string const & name, funcIndex const & fi, funcNewindex const & fni, obj objPtr) @@ -192,6 +187,8 @@ GameScript::GameScript(sol::state* lua) : LuaHandler{ lua } */ m_lua->set_function("PlayAudioTrack", &PlayAudioTrack); + m_lua->set_function("PrintString", &GameScript::PrintString, this); + /*** Player inventory management @section Inventory */ @@ -229,7 +226,7 @@ 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 @tparam @{InvItem} item the item to be set -@tparam int count the number of items the player will have +@tparam int count the number of items the player will have */ m_lua->set_function("SetInvItemCount", &InventorySetCount); @@ -299,6 +296,26 @@ Calculate the horizontal distance between two positions. */ m_lua->set_function("CalculateHorizontalDistance", &CalculateHorizontalDistance); +/*** +Show some text on-screen. +@function ShowString +@tparam DisplayString str the string object to draw +@tparam float time the time in seconds for which to show the string. +If not given, the string will have an "infinite" life, and will show +until @{HideString} is called or until the level is finished. +Default: nil (i.e. infinite) +*/ + + m_lua->set_function("ShowString", &GameScript::ShowString, this); + +/*** +Hide some on-screen text. +@function HideString +@tparam DisplayString str the string object to hide. Must previously have been shown +with a call to @{ShowString}, or this function will have no effect. +*/ + m_lua->set_function("HideString", [this](GameScriptDisplayString const& s) {ShowString(s, 0.0f); }); + MakeReadOnlyTable("ObjID", kObjIDs); ResetLevelTables(); @@ -341,6 +358,11 @@ Calculate the horizontal distance between two positions. [this](auto && ... param) { return RemoveName(std::forward(param)...); } ); + GameScriptDisplayString::Register(m_lua); + GameScriptDisplayString::SetCallbacks( + [this](auto && ... param) {return AddDisplayString(std::forward(param)...); }, + [this](auto && ... param) {return ScheduleRemoveDisplayString(std::forward(param)...); } + ); GameScriptPosition::Register(m_lua); m_lua->new_enum("Object", { @@ -379,15 +401,25 @@ bool GameScript::SetLevelFunc(sol::table tab, std::string const& luaName, sol::o return true; } +bool GameScript::ScheduleRemoveDisplayString(DisplayStringIDType id) { + auto it = m_userDisplayStrings.find(id); + if (std::cend(m_userDisplayStrings) == it) + return false; + it->second.m_deleteWhenZero = true; + return true; } +bool GameScript::AddDisplayString(DisplayStringIDType id, UserDisplayString const & ds) { + return m_userDisplayStrings.insert(std::make_pair(id, ds)).second; } +void GameScript::SetCallbackDrawString(CallbackDrawString cb) { + m_callbackDrawSring = cb; } void GameScript::FreeLevelScripts() @@ -482,6 +514,7 @@ template void GameScript::GetVariables(std::map void GameScript::SetVariables(std::map& locals, std::map& globals) { + //TODO Look into serialising tables from these maps, too -- squidshire, 24/08/2021 m_locals.variables.clear(); for (const auto& it : locals) { @@ -520,6 +553,45 @@ void GameScript::ResetVariables() { (*m_lua)["Lara"] = NULL; } + +// Misc +void GameScript::PrintString(std::string const & key, int x, int y, GameScriptColor color, int lifetime, int flags) +{ + ten::renderer::g_Renderer.drawString(x, y, key.c_str(), color, flags); +} + +void GameScript::ShowString(GameScriptDisplayString const & str, sol::optional nSeconds) +{ + auto it = m_userDisplayStrings.find(str.GetID()); + it->second.m_timeRemaining = nSeconds.value_or(0.0f); + it->second.m_isInfinite = !nSeconds.has_value(); +} + +void GameScript::ProcessDisplayStrings(float dt) +{ + auto it = std::begin(m_userDisplayStrings); + while (it != std::end(m_userDisplayStrings)) + { + auto& str = it->second; + bool endOfLife = (0.0f >= str.m_timeRemaining); + if (str.m_deleteWhenZero && endOfLife) + { + ScriptAssertF(!str.m_isInfinite, "The infinite string {} (key \"{}\") went out of scope without being hidden.", it->first, str.m_key); + it = m_userDisplayStrings.erase(it); + } + else + { + if (!endOfLife || str.m_isInfinite) + { + m_callbackDrawSring(g_GameFlow->GetString(str.m_key.c_str()), str.m_color, str.m_x, str.m_y, str.m_flags); + if (!endOfLife) + str.m_timeRemaining -= dt; + } + ++it; + } + } +} + sol::object LuaVariables::GetVariable(sol::table tab, std::string key) { if (variables.find(key) == variables.end()) diff --git a/TR5Main/Scripting/GameLogicScript.h b/TR5Main/Scripting/GameLogicScript.h index 25ae1b1ac..d562d759a 100644 --- a/TR5Main/Scripting/GameLogicScript.h +++ b/TR5Main/Scripting/GameLogicScript.h @@ -13,6 +13,7 @@ #include "GameScriptAIObject.h" #include "GameScriptSoundSourceInfo.h" #include "GameScriptCameraInfo.h" +#include "GameScriptDisplayString.h" struct LuaFunction { std::string Name; @@ -47,35 +48,40 @@ struct LuaVariable bool BoolValue; }; +using CallbackDrawString = std::function; +using DisplayStringMap = std::unordered_map; using VarMapVal = std::variant< short, std::reference_wrapper, std::reference_wrapper, std::reference_wrapper, std::reference_wrapper, std::reference_wrapper>; - class GameScript : public LuaHandler { private: - LuaVariables m_globals{}; - LuaVariables m_locals{}; - std::unordered_map m_nameMap{}; - std::unordered_map m_itemsMapName{}; - std::unordered_set m_levelFuncs{}; - sol::protected_function m_onStart{}; - sol::protected_function m_onLoad{}; - sol::protected_function m_onControlPhase{}; - sol::protected_function m_onSave{}; - sol::protected_function m_onEnd{}; + LuaVariables m_globals{}; + LuaVariables m_locals{}; + DisplayStringMap m_userDisplayStrings{}; + std::unordered_map m_nameMap{}; + std::unordered_map m_itemsMapName{}; + std::unordered_set m_levelFuncs{}; + sol::protected_function m_onStart{}; + sol::protected_function m_onLoad{}; + sol::protected_function m_onControlPhase{}; + sol::protected_function m_onSave{}; + sol::protected_function m_onEnd{}; void ResetLevelTables(); + CallbackDrawString m_callbackDrawSring; public: GameScript(sol::state* lua); void FreeLevelScripts(); + bool AddDisplayString(DisplayStringIDType id, UserDisplayString const & ds); + bool ScheduleRemoveDisplayString(DisplayStringIDType id); bool SetLevelFunc(sol::table tab, std::string const& luaName, sol::object obj); sol::protected_function GetLevelFunc(sol::table tab, std::string const& luaName); @@ -112,7 +118,11 @@ public: void SetVariables(std::map& locals, std::map& globals); void ResetVariables(); + void PrintString(std::string const& key, int x, int y, GameScriptColor color, int lifetime, int flags); + void SetCallbackDrawString(CallbackDrawString cb); + void ShowString(GameScriptDisplayString const&, sol::optional nSeconds); + void ProcessDisplayStrings(float dt); void InitCallbacks(); void OnStart(); void OnLoad();