From 487a5259fb74dc6679ede8691cb233eac81f6304 Mon Sep 17 00:00:00 2001 From: Drew C Date: Tue, 10 Mar 2026 19:12:38 -0500 Subject: [PATCH] Add Tutorial Text API --- .luadefs/focapi-functions-emmyluadoc.lua | 2 + .../GameManager/TutorialTextFeed.lua | 72 ++++++++ .../GameManager/TutorialTextManager.lua | 168 ++++++++++++++++++ .../GameManager/TutorialTextView.lua | 60 +++++++ 4 files changed, 302 insertions(+) create mode 100644 Data/Scripts/Miscellaneous/GameManager/TutorialTextFeed.lua create mode 100644 Data/Scripts/Miscellaneous/GameManager/TutorialTextManager.lua create mode 100644 Data/Scripts/Miscellaneous/GameManager/TutorialTextView.lua diff --git a/.luadefs/focapi-functions-emmyluadoc.lua b/.luadefs/focapi-functions-emmyluadoc.lua index a291342..69d5edd 100644 --- a/.luadefs/focapi-functions-emmyluadoc.lua +++ b/.luadefs/focapi-functions-emmyluadoc.lua @@ -24,6 +24,8 @@ function Get_All_Players() end ---@public ---Adds the specified text to the tutorial text box. +---A new text key will be appended as a new line. +---An existing text key will be overwritten. ---@param key string The key to use. ---@param text string The text to add. ---@param duration number The duration to keep text on screen, defaults to -1 (infinite). diff --git a/Data/Scripts/Miscellaneous/GameManager/TutorialTextFeed.lua b/Data/Scripts/Miscellaneous/GameManager/TutorialTextFeed.lua new file mode 100644 index 0000000..6457c60 --- /dev/null +++ b/Data/Scripts/Miscellaneous/GameManager/TutorialTextFeed.lua @@ -0,0 +1,72 @@ +require("GameManager/TutorialTextView") + +---A fixed-capacity feed view. Appending a new line when at capacity evicts the +---oldest (top-most) entry first. Rendering is driven by the IsActive flag set +---by TutorialTextManager; no manager reference is needed. +--- +---@class TutorialTextFeed : TutorialTextView +---@field MaxLines integer Maximum number of lines to display at once. +---@field _KeyOrder string[] Insertion-ordered list of active entry keys (oldest first). +---@field _NextId integer Counter used to generate unique entry keys. +TutorialTextFeed = setmetatable({}, {__index = TutorialTextView}) +TutorialTextFeed.__index = TutorialTextFeed + +---Creates a new Tutorial Text Feed. +---@param name string The view name. +---@param maxLines integer Maximum number of lines to display at once. +---@return TutorialTextFeed +function TutorialTextFeed:New(name, maxLines) + local self = TutorialTextView.New(TutorialTextView, name) + setmetatable(self, TutorialTextFeed) + + self.MaxLines = maxLines + self._KeyOrder = {} + self._NextId = 0 + + return self +end + +---Appends a new line to the bottom of the feed. +---If the feed is at capacity, the oldest line is removed first. +---Only renders to the screen when this view is active. +---@param text string The display text. +---@param duration number|nil Duration in seconds, defaults to -1 (infinite). +---@param color ColorStruct|nil The text color, defaults to white. +function TutorialTextFeed:Append(text, duration, color) + if #self._KeyOrder >= self.MaxLines then + local oldestKey = table.remove(self._KeyOrder, 1) + self.Entries[oldestKey] = nil + if self.IsActive then + Remove_Tutorial_Text(oldestKey) + end + end + + self._NextId = self._NextId + 1 + local key = self.Name .. "_" .. self._NextId + + self:SetEntry(key, text, duration, color) + table.insert(self._KeyOrder, key) + + if self.IsActive then + local entry = self.Entries[key] + Add_Tutorial_Text(entry.Key, entry.Text, entry.Duration, + entry.Color.R, entry.Color.G, entry.Color.B, entry.Color.A) + end +end + +---Removes all entries from the feed. +---Only clears the screen when this view is active. +function TutorialTextFeed:Clear() + if self.IsActive then + for _, key in ipairs(self._KeyOrder) do + Remove_Tutorial_Text(key) + end + end + self.Entries = {} + self._KeyOrder = {} +end + +function TutorialTextFeed:Debug() + return string.format("TutorialTextFeed '%s' (%d/%d lines)", + self.Name, #self._KeyOrder, self.MaxLines) +end diff --git a/Data/Scripts/Miscellaneous/GameManager/TutorialTextManager.lua b/Data/Scripts/Miscellaneous/GameManager/TutorialTextManager.lua new file mode 100644 index 0000000..bad45ef --- /dev/null +++ b/Data/Scripts/Miscellaneous/GameManager/TutorialTextManager.lua @@ -0,0 +1,168 @@ +require("GameManager/TutorialTextView") + +---Manages multiple named tutorial text views, rendering only the active view. +--- +---Views hold their own state independently. Only the current view is rendered +---to the screen. Switching views clears the screen and renders the new view's +---current state. +--- +---@class TutorialTextManager +---@field Views table All registered views by name. +---@field CurrentViewName string|nil The name of the currently active view. +TutorialTextManager = {} +TutorialTextManager.__index = TutorialTextManager + +---Creates a new Tutorial Text Manager. +---@return TutorialTextManager +function TutorialTextManager:New() + local self = setmetatable({}, TutorialTextManager) + + self.Views = {} + self.CurrentViewName = nil + + return self +end + +---Registers a new view. If a view with the given name already exists it is returned as-is. +---@param name string The view name. +---@return TutorialTextView +function TutorialTextManager:AddView(name) + if not self.Views[name] then + self.Views[name] = TutorialTextView:New(name) + end + return self.Views[name] +end + +---Registers an existing view instance (e.g. a TutorialTextFeed). +---If a view with the same name already exists it is returned as-is. +---@param view TutorialTextView The view to register. +---@return TutorialTextView +function TutorialTextManager:RegisterView(view) + if not self.Views[view.Name] then + self.Views[view.Name] = view + end + return self.Views[view.Name] +end + +---Returns a registered view by name, or nil if it does not exist. +---@param name string The view name. +---@return TutorialTextView|nil +function TutorialTextManager:GetView(name) + return self.Views[name] +end + +---Switches the active view. Clears the screen then renders the new view's current state. +---@param name string The view name to activate. +function TutorialTextManager:SetCurrentView(name) + if self.CurrentViewName == name then + return + end + + local oldView = self.Views[self.CurrentViewName] + if oldView then + oldView.IsActive = false + end + + self:_ClearScreen() + self.CurrentViewName = name + + local newView = self.Views[name] + if newView then + newView.IsActive = true + self:_RenderView(newView) + end +end + +---Sets or updates a text entry in a view. +---If the view is currently active the change is immediately rendered to the screen. +---@param viewName string The view to update. +---@param key string The unique key for this entry. +---@param text string The display text. +---@param duration number|nil Duration in seconds, defaults to -1 (infinite). +---@param color ColorStruct|nil The text color, defaults to white. +function TutorialTextManager:UpdateEntry(viewName, key, text, duration, color) + local view = self.Views[viewName] + if not view then + return + end + + view:SetEntry(key, text, duration, color) + + if self.CurrentViewName == viewName then + local entry = view.Entries[key] + Add_Tutorial_Text(entry.Key, entry.Text, entry.Duration, + entry.Color.R, entry.Color.G, entry.Color.B, entry.Color.A) + end +end + +---Removes a text entry from a view. +---If the view is currently active the entry is immediately removed from the screen. +---@param viewName string The view to update. +---@param key string The key to remove. +function TutorialTextManager:RemoveEntry(viewName, key) + local view = self.Views[viewName] + if not view then + return + end + + view:RemoveEntry(key) + + if self.CurrentViewName == viewName then + Remove_Tutorial_Text(key) + end +end + +---Clears all entries from a view. +---If the view is currently active all entries are immediately removed from the screen. +---@param viewName string The view to clear. +function TutorialTextManager:ClearView(viewName) + local view = self.Views[viewName] + if not view then + return + end + + if self.CurrentViewName == viewName then + self:_ClearScreen() + end + + view:Clear() +end + +---Removes all views and clears the screen. +function TutorialTextManager:Reset() + self:_ClearScreen() + self.Views = {} + self.CurrentViewName = nil +end + +---Renders all entries of a view to the screen. +---@param view TutorialTextView +function TutorialTextManager:_RenderView(view) + for _, entry in pairs(view.Entries) do + Add_Tutorial_Text(entry.Key, entry.Text, entry.Duration, + entry.Color.R, entry.Color.G, entry.Color.B, entry.Color.A) + end +end + +---Removes all entries of the current view from the screen. +function TutorialTextManager:_ClearScreen() + if not self.CurrentViewName then + return + end + + local view = self.Views[self.CurrentViewName] + if not view then + return + end + + for key in pairs(view.Entries) do + Remove_Tutorial_Text(key) + end +end + +function TutorialTextManager:Debug() + local count = 0 + for _ in pairs(self.Views) do count = count + 1 end + return string.format("TutorialTextManager (views: %d, current: '%s')", + count, self.CurrentViewName or "none") +end diff --git a/Data/Scripts/Miscellaneous/GameManager/TutorialTextView.lua b/Data/Scripts/Miscellaneous/GameManager/TutorialTextView.lua new file mode 100644 index 0000000..3e9d0fb --- /dev/null +++ b/Data/Scripts/Miscellaneous/GameManager/TutorialTextView.lua @@ -0,0 +1,60 @@ +require("GameManager/ColorStruct") + +---Represents a single text entry in a tutorial text view. +---@class TutorialTextEntry +---@field Key string The unique key for this entry. +---@field Text string The display text. +---@field Duration number Duration in seconds, or -1 for infinite. +---@field Color ColorStruct The text color. + +---Represents a named set of tutorial text entries. +---@class TutorialTextView +---@field Name string The view name. +---@field Entries table Entries keyed by their text key. +---@field IsActive boolean Whether this view is currently being displayed. +TutorialTextView = {} +TutorialTextView.__index = TutorialTextView + +---Creates a new Tutorial Text View. +---@param name string The view name. +---@return TutorialTextView +function TutorialTextView:New(name) + local self = setmetatable({}, TutorialTextView) + + self.Name = name + self.Entries = {} + self.IsActive = false + + return self +end + +---Sets or updates a text entry in this view. +---@param key string The unique key for this entry. +---@param text string The display text. +---@param duration number|nil Duration in seconds, defaults to -1 (infinite). +---@param color ColorStruct|nil The text color, defaults to white. +function TutorialTextView:SetEntry(key, text, duration, color) + self.Entries[key] = { + Key = key, + Text = text, + Duration = duration or -1, + Color = color or ColorStruct:New(1, 1, 1, 1), + } +end + +---Removes a text entry from this view. +---@param key string The key to remove. +function TutorialTextView:RemoveEntry(key) + self.Entries[key] = nil +end + +---Removes all entries from this view. +function TutorialTextView:Clear() + self.Entries = {} +end + +function TutorialTextView:Debug() + local count = 0 + for _ in pairs(self.Entries) do count = count + 1 end + return string.format("TutorialTextView '%s' (%d entries)", self.Name, count) +end