Add Tutorial Text API

This commit is contained in:
2026-03-10 19:12:38 -05:00
parent 5609738464
commit 487a5259fb
4 changed files with 302 additions and 0 deletions

View File

@@ -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

View File

@@ -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<string, TutorialTextView> 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

View File

@@ -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<string, TutorialTextEntry> 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