mirror of
https://github.com/TemujinCalidius/SurvivorCore.git
synced 2026-08-14 00:58:01 +00:00
fix(hud): keep a single HUD, preferring the authored one over the fallback
Both the authored template and the built-in fallback name their ScreenGui "SurvivalHud", and the fallback fires on a 1s timer if no bars have bound yet. The dedupe ran only once at startup, so a fallback that raced the template (more likely now startClient does more before the template copies in) left two HUDs on screen — a restart usually shuffled the timing enough to hide it, until it didn't. Tag the fallback, make the dedupe continuous (re-runs whenever another HUD appears) and always prefer the authored HUD, and don't build the fallback when a HUD ScreenGui already exists. Also link the inventory/hotbar demo video in docs/inventory.md. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
5ab855fa1b
commit
1b5ef088b3
@@ -7,6 +7,8 @@ Character Sheet, with Codex / Achievements / Quests scaffolded). Like the surviv
|
||||
(icons, counts, fills) — never layout or colors. The engine ships **zero items**; your game registers
|
||||
them, and their display data replicates to clients automatically.
|
||||
|
||||
> 📹 **See it in action:** [Inventory & hotbar system](https://makertube.net/w/wXRkuJo323AHMpZKVwWxt3)
|
||||
|
||||
- **Data layer (server):** [src/systems/Inventory.luau](../src/systems/Inventory.luau)
|
||||
- **UI (client):** `PanelManager`, `InventoryUi`, `Hotbar`, `CharacterSheet`, `DragDrop`, `SlotGrid`,
|
||||
`UiFallback` under [src/client/](../src/client)
|
||||
|
||||
+34
-8
@@ -105,15 +105,31 @@ end
|
||||
local Hud = {}
|
||||
local started = false
|
||||
|
||||
-- installHud() + the StarterGui->PlayerGui copy can briefly produce two HUDs; keep one.
|
||||
-- Several paths can land more than one HUD in PlayerGui — installHud()'s clone + the
|
||||
-- StarterGui->PlayerGui copy, or a late fallback racing the authored template. Keep exactly one,
|
||||
-- always preferring an AUTHORED HUD over the built-in fallback (and dropping extra copies).
|
||||
local function dedupeHuds(playerGui: Instance)
|
||||
local kept: Instance? = nil
|
||||
local authored: Instance? = nil
|
||||
local fallbacks: { Instance } = {}
|
||||
for _, gui in playerGui:GetChildren() do
|
||||
if gui:IsA("ScreenGui") and gui.Name == HUD_NAME then
|
||||
if kept then
|
||||
gui:Destroy()
|
||||
if gui:GetAttribute(HudFallback.FALLBACK_ATTRIBUTE) then
|
||||
table.insert(fallbacks, gui)
|
||||
elseif authored then
|
||||
gui:Destroy() -- a second authored copy
|
||||
else
|
||||
kept = gui
|
||||
authored = gui
|
||||
end
|
||||
end
|
||||
end
|
||||
if authored then
|
||||
for _, fb in fallbacks do
|
||||
fb:Destroy() -- authored wins; the fallback is only a no-template safety net
|
||||
end
|
||||
else
|
||||
for index, fb in fallbacks do
|
||||
if index > 1 then
|
||||
fb:Destroy()
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -296,6 +312,13 @@ function Hud.start(_options: { [string]: any }?)
|
||||
end
|
||||
|
||||
dedupeHuds(playerGui)
|
||||
-- Run it again whenever another HUD shows up (a fallback racing the template, a late
|
||||
-- StarterGui copy, installHud's clone) so we never end up with two on screen.
|
||||
playerGui.ChildAdded:Connect(function(child)
|
||||
if child:IsA("ScreenGui") and child.Name == HUD_NAME then
|
||||
dedupeHuds(playerGui)
|
||||
end
|
||||
end)
|
||||
|
||||
-- A stat bar is any GuiObject carrying a `Stat` attribute (how the authored
|
||||
-- template marks them) OR tagged `SurvivorStatBar` (for the Builder UI / fallback).
|
||||
@@ -442,11 +465,14 @@ function Hud.start(_options: { [string]: any }?)
|
||||
end
|
||||
end)
|
||||
|
||||
-- Zero-setup safety net: if nothing showed up, build the minimal fallback.
|
||||
-- Zero-setup safety net: if no HUD showed up at all, build the minimal fallback. Guard on the
|
||||
-- ScreenGui's presence (not just whether bars bound yet) so a slow authored HUD never gets a
|
||||
-- fallback built alongside it; if it still arrives late, the ChildAdded dedupe drops the extra.
|
||||
task.delay(FALLBACK_WAIT, function()
|
||||
if next(bound) == nil then
|
||||
HudFallback.build(playerGui, StatConfig.resolve().stats)
|
||||
if next(bound) ~= nil or playerGui:FindFirstChild(HUD_NAME) then
|
||||
return
|
||||
end
|
||||
HudFallback.build(playerGui, StatConfig.resolve().stats)
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
@@ -28,9 +28,14 @@ local FALLBACK_COLORS = {
|
||||
|
||||
local HudFallback = {}
|
||||
|
||||
-- Marks a HUD as the built-in fallback (vs an authored template), so the binder's dedupe can
|
||||
-- always drop the fallback in favour of an authored HUD when both end up present.
|
||||
HudFallback.FALLBACK_ATTRIBUTE = "SurvivorCoreHudFallback"
|
||||
|
||||
function HudFallback.build(playerGui: Instance, stats: { any })
|
||||
local screen = Instance.new("ScreenGui")
|
||||
screen.Name = HUD_NAME
|
||||
screen:SetAttribute(HudFallback.FALLBACK_ATTRIBUTE, true)
|
||||
screen.ResetOnSpawn = false
|
||||
-- Respect the ~36px Roblox topbar inset so bars never render under the CoreGui menu.
|
||||
screen.IgnoreGuiInset = false
|
||||
|
||||
Reference in New Issue
Block a user