- forty files that carried the reference's literal Lua are rewritten as our own expression of the behaviour — worst identical run 61 lines down to 7 — and the README now says exactly that - the overlap gate measures the code itself across all four reference corpora, and ratchets: the number may only go down - a lost session ends at the login screen instead of silently re-authenticating and stealing the account back - a building's skybox is a real model: Caverns of Time's white ceiling becomes its painted nebula and additive stars Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
295 lines
14 KiB
XML
295 lines
14 KiB
XML
<Ui>
|
|
<!--
|
|
The cast bar — benilla's own implementation of the 1.12 CastingBarFrame contract. The
|
|
extracted reference (Interface\FrameXML\CastingBarFrame.xml/.lua, mpqx; decision 0137
|
|
phase 1) is the SPECIFICATION: geometry, textures, colors, and every timing constant are
|
|
read off it, and the observable lifecycle below is the one it defines. The code is ours.
|
|
Driven by the SPELLCAST_* events crate::ui_cast fires off the wire (START name,ms / STOP /
|
|
FAILED / INTERRUPTED / DELAYED / CHANNEL_START ms,name / CHANNEL_UPDATE ms / CHANNEL_STOP).
|
|
|
|
The reference's globals (CastingBarText/Spark/Flash) are named regions, so they publish as
|
|
globals here exactly as they do there; `this` is the firing frame (RF-0025), and every
|
|
handler below reads it once into a named local rather than threading the global through a
|
|
dozen method calls. Deliberate departures from the specification's own .lua: its dead code
|
|
(the `if not this:IsVisible() then this:Hide() end` no-op guard at the head of the STOP
|
|
branch; the two write-only `this.mode` stores nothing reads), and the flash/fade steps
|
|
normalized to the reference tick rate (the CASTING_BAR_REF_TICK comment below —
|
|
decision 0454).
|
|
|
|
SPELLCAST_DELAYED is live: SMSG_SPELL_DELAYED (pushback on taking a hit) is decoded and fed,
|
|
so a hit slides the bar's window out instead of letting it finish early (decision 0256).
|
|
|
|
POSITION: the XML anchor y=55 below is only the pre-manage default, exactly as in the
|
|
reference — UIParent.lua overrides it on every bottom-stack layout pass
|
|
(UIPARENT_MANAGED_FRAME_POSITIONS["CastingBarFrame"]: baseY 60, +40 per either bottom
|
|
multibar, +40 per pet/stance bar, +9 per rep bar). Our transcription of that mechanism
|
|
lives in UIParent.xml (decision 0272); with the always-on multibars the bar sits at
|
|
y=100, and y=140 while the stance bar shows.
|
|
-->
|
|
<Script><![CDATA[
|
|
CASTING_BAR_ALPHA_STEP = 0.05
|
|
CASTING_BAR_FLASH_STEP = 0.2
|
|
CASTING_BAR_HOLD_TIME = 1
|
|
|
|
-- The ref applies the two steps PER OnUpdate TICK (no arg1 scaling) — the flash/fade
|
|
-- tail is frame-rate dependent by the reference's own design: ~25 ticks from hold-expiry
|
|
-- to hidden, i.e. 0.83 s at the reference bottle's configured cap (WoW/WTF/Config.wtf
|
|
-- `SET maxfps "30"`), 0.42 s at an era-typical 60 fps, a blink at 120. We render fast,
|
|
-- so the step lines below are normalized to the reference tick rate — the bar's
|
|
-- hold+fade then matches the reference window at any render rate (decision 0454).
|
|
CASTING_BAR_REF_TICK = 30
|
|
|
|
-- The 1.12 GlobalStrings values (no GlobalStrings.lua is loaded yet).
|
|
FAILED = FAILED or "Failed"
|
|
INTERRUPTED = INTERRUPTED or "Interrupted"
|
|
|
|
-- THE BAR'S WHOLE VOCABULARY, one entry per wire event, each taking the frame. OnLoad
|
|
-- registers this table's keys and OnEvent looks the incoming event up in it, so the set
|
|
-- the bar LISTENS for and the set it can ANSWER are the same set by construction — there
|
|
-- is no second list to fall out of step with. (The reference spells both halves out: eight
|
|
-- RegisterEvent calls, then a sixty-line if-chain testing the same eight strings back.)
|
|
local castingBarEvent = {}
|
|
|
|
-- Seat the fill at `value` and ride the spark on its leading edge. The bar is 195px wide,
|
|
-- so a fraction of `span` scales straight to a pixel offset from the frame's LEFT. A cast
|
|
-- and a channel differ only in which value and which span they hand in.
|
|
local function castingBarTrack(bar, value, span)
|
|
bar:SetValue(value)
|
|
CastingBarFlash:Hide()
|
|
local sparkPosition = ((value - bar.startTime) / span) * 195
|
|
-- Pushback slides startTime PAST the wall clock, so a cast really can sit before its
|
|
-- own window's start for a frame or two. (A channel cannot — its value counts down
|
|
-- from the far end and stops at startTime — so here the clamp is dead weight rather
|
|
-- than a change of behaviour.)
|
|
if sparkPosition < 0 then
|
|
sparkPosition = 0
|
|
end
|
|
CastingBarSpark:SetPoint("CENTER", CastingBarFrame, "LEFT", sparkPosition, 2)
|
|
end
|
|
|
|
-- A cast opens: orange, a full window anchored on the clock, the spark at the left edge.
|
|
castingBarEvent.SPELLCAST_START = function(bar)
|
|
bar:SetStatusBarColor(1.0, 0.7, 0.0)
|
|
CastingBarSpark:Show()
|
|
bar.startTime = GetTime()
|
|
bar.maxValue = bar.startTime + (arg2 / 1000)
|
|
bar:SetMinMaxValues(bar.startTime, bar.maxValue)
|
|
bar:SetValue(bar.startTime)
|
|
CastingBarText:SetText(arg1)
|
|
bar:SetAlpha(1.0)
|
|
bar.holdTime = 0
|
|
bar.casting = 1
|
|
bar.fadeOut = nil
|
|
bar:Show()
|
|
end
|
|
|
|
-- A channel opens. Its args are START's the other way round — milliseconds first, then the
|
|
-- name — and its fill DRAINS, so the bar is seated at the far end and walks back.
|
|
castingBarEvent.SPELLCAST_CHANNEL_START = function(bar)
|
|
bar:SetStatusBarColor(1.0, 0.7, 0.0)
|
|
CastingBarSpark:Show()
|
|
bar.maxValue = 1
|
|
bar.startTime = GetTime()
|
|
bar.endTime = bar.startTime + (arg1 / 1000)
|
|
bar.duration = arg1 / 1000
|
|
bar:SetMinMaxValues(bar.startTime, bar.endTime)
|
|
bar:SetValue(bar.endTime)
|
|
CastingBarText:SetText(arg2)
|
|
bar:SetAlpha(1.0)
|
|
bar.holdTime = 0
|
|
bar.casting = nil
|
|
bar.channeling = 1
|
|
bar.fadeOut = nil
|
|
bar:Show()
|
|
end
|
|
|
|
-- The server's mid-channel correction: the window keeps its LENGTH and is re-anchored to
|
|
-- end arg1 ms from now, so a pushback eats into what is left rather than extending it.
|
|
castingBarEvent.SPELLCAST_CHANNEL_UPDATE = function(bar)
|
|
if not bar:IsShown() then
|
|
return
|
|
end
|
|
local span = bar.endTime - bar.startTime
|
|
bar.endTime = GetTime() + (arg1 / 1000)
|
|
bar.startTime = bar.endTime - span
|
|
bar:SetMinMaxValues(bar.startTime, bar.endTime)
|
|
end
|
|
|
|
-- Pushback on taking a hit (SMSG_SPELL_DELAYED, decision 0256): BOTH ends of the window
|
|
-- slide out by arg1 ms, so the cast has that much longer to run and the spark jumps back.
|
|
-- A hit never cancels the bar.
|
|
castingBarEvent.SPELLCAST_DELAYED = function(bar)
|
|
if not bar:IsShown() then
|
|
return
|
|
end
|
|
local pushback = arg1 / 1000
|
|
bar.startTime = bar.startTime + pushback
|
|
bar.maxValue = bar.maxValue + pushback
|
|
bar:SetMinMaxValues(bar.startTime, bar.maxValue)
|
|
end
|
|
|
|
-- The cast landed, or a channel ran its course: green, full, and the completion bloom
|
|
-- armed at alpha 0 for OnUpdate to ramp. `flag` names the one field the event clears,
|
|
-- which is the only thing that separates the two of them.
|
|
local function castingBarFinished(bar, flag)
|
|
if not bar:IsShown() then
|
|
return
|
|
end
|
|
bar:SetValue(bar.maxValue)
|
|
bar:SetStatusBarColor(0.0, 1.0, 0.0)
|
|
CastingBarSpark:Hide()
|
|
CastingBarFlash:SetAlpha(0.0)
|
|
CastingBarFlash:Show()
|
|
bar[flag] = nil
|
|
bar.flash = 1
|
|
bar.fadeOut = 1
|
|
end
|
|
castingBarEvent.SPELLCAST_STOP = function(bar) castingBarFinished(bar, "casting") end
|
|
castingBarEvent.SPELLCAST_CHANNEL_STOP = function(bar) castingBarFinished(bar, "channeling") end
|
|
|
|
-- The cast did NOT land: red, the reason where the spell's name was, and held opaque for
|
|
-- CASTING_BAR_HOLD_TIME before the fade starts — no completion bloom. A channel is never
|
|
-- ended this way (the server closes one with CHANNEL_STOP), so a live channel ignores both.
|
|
local function castingBarAborted(bar, reason)
|
|
if not bar:IsShown() or bar.channeling then
|
|
return
|
|
end
|
|
bar:SetValue(bar.maxValue)
|
|
bar:SetStatusBarColor(1.0, 0.0, 0.0)
|
|
CastingBarSpark:Hide()
|
|
CastingBarText:SetText(reason)
|
|
bar.casting = nil
|
|
bar.fadeOut = 1
|
|
bar.holdTime = GetTime() + CASTING_BAR_HOLD_TIME
|
|
end
|
|
castingBarEvent.SPELLCAST_FAILED = function(bar) castingBarAborted(bar, FAILED) end
|
|
castingBarEvent.SPELLCAST_INTERRUPTED = function(bar) castingBarAborted(bar, INTERRUPTED) end
|
|
|
|
function CastingBarFrame_OnLoad()
|
|
local bar = this
|
|
for spellcast in pairs(castingBarEvent) do
|
|
bar:RegisterEvent(spellcast)
|
|
end
|
|
bar.casting = nil
|
|
bar.holdTime = 0
|
|
-- The alias global the reference publishes at the end of its own OnLoad
|
|
-- (`CastingBarFrame.lua:16`). `CastingBarFrame` IS the StatusBar in 1.12 — there is no
|
|
-- separate child frame — so the name an addon reaches for has to resolve to the frame
|
|
-- itself.
|
|
--
|
|
-- 11 corpus addons index `CastingBarFrameStatusBar`, and it was the largest single
|
|
-- name in the whole session-start `attempt to index global` row. It went missing here
|
|
-- once and nothing warned: a line dropped from INSIDE a function is invisible to every
|
|
-- instrument we have. `framexml-transcription-diff.py` compares elements and handlers,
|
|
-- not function bodies, and would not even pair these two files (ours is CastingBar.xml,
|
|
-- the reference's is CastingBarFrame.xml).
|
|
CastingBarFrameStatusBar = bar
|
|
bar:Hide()
|
|
end
|
|
|
|
function CastingBarFrame_OnEvent()
|
|
local handle = castingBarEvent[event]
|
|
if handle then
|
|
handle(this)
|
|
end
|
|
end
|
|
|
|
function CastingBarFrame_OnUpdate()
|
|
local bar = this
|
|
if bar.casting then
|
|
-- The fill IS the wall clock, pinned at the window's end once the cast is spent.
|
|
local now = GetTime()
|
|
if now > bar.maxValue then
|
|
now = bar.maxValue
|
|
end
|
|
castingBarTrack(bar, now, bar.maxValue - bar.startTime)
|
|
elseif bar.channeling then
|
|
local now = GetTime()
|
|
if now >= bar.endTime then
|
|
bar.channeling = nil
|
|
bar.fadeOut = 1
|
|
return
|
|
end
|
|
-- A channel drains: the value is the window's start plus what is LEFT of it, so
|
|
-- the fill empties from full instead of filling from empty.
|
|
castingBarTrack(bar, bar.startTime + (bar.endTime - now), bar.endTime - bar.startTime)
|
|
elseif GetTime() < bar.holdTime then
|
|
return
|
|
elseif bar.flash then
|
|
local alpha = CastingBarFlash:GetAlpha() + CASTING_BAR_FLASH_STEP * (arg1 * CASTING_BAR_REF_TICK)
|
|
if alpha < 1 then
|
|
CastingBarFlash:SetAlpha(alpha)
|
|
else
|
|
CastingBarFlash:SetAlpha(1.0)
|
|
bar.flash = nil
|
|
end
|
|
elseif bar.fadeOut then
|
|
local alpha = bar:GetAlpha() - CASTING_BAR_ALPHA_STEP * (arg1 * CASTING_BAR_REF_TICK)
|
|
if alpha > 0 then
|
|
bar:SetAlpha(alpha)
|
|
else
|
|
bar.fadeOut = nil
|
|
bar:Hide()
|
|
end
|
|
end
|
|
end
|
|
]]></Script>
|
|
|
|
<StatusBar name="CastingBarFrame" drawLayer="BORDER" toplevel="true" parent="UIParent" hidden="true">
|
|
<Size><AbsDimension x="195" y="13"/></Size>
|
|
<Anchors>
|
|
<Anchor point="BOTTOM">
|
|
<Offset><AbsDimension x="0" y="55"/></Offset>
|
|
</Anchor>
|
|
</Anchors>
|
|
<Layers>
|
|
<Layer level="BACKGROUND">
|
|
<Texture setAllPoints="true">
|
|
<Color r="0" g="0" b="0" a="0.5"/>
|
|
</Texture>
|
|
</Layer>
|
|
<Layer level="ARTWORK">
|
|
<FontString name="CastingBarText" inherits="GameFontHighlight">
|
|
<Size><AbsDimension x="185" y="16"/></Size>
|
|
<Anchors>
|
|
<Anchor point="TOP">
|
|
<Offset><AbsDimension x="0" y="5"/></Offset>
|
|
</Anchor>
|
|
</Anchors>
|
|
</FontString>
|
|
<Texture name="CastingBarBorder" file="Interface\CastingBar\UI-CastingBar-Border">
|
|
<Size><AbsDimension x="256" y="64"/></Size>
|
|
<Anchors>
|
|
<Anchor point="TOP">
|
|
<Offset><AbsDimension x="0" y="28"/></Offset>
|
|
</Anchor>
|
|
</Anchors>
|
|
</Texture>
|
|
</Layer>
|
|
<Layer level="OVERLAY">
|
|
<Texture name="CastingBarSpark" file="Interface\CastingBar\UI-CastingBar-Spark" alphaMode="ADD">
|
|
<Size><AbsDimension x="32" y="32"/></Size>
|
|
<Anchors>
|
|
<Anchor point="CENTER"/>
|
|
</Anchors>
|
|
</Texture>
|
|
<Texture name="CastingBarFlash" file="Interface\CastingBar\UI-CastingBar-Flash" alphaMode="ADD">
|
|
<Size><AbsDimension x="256" y="64"/></Size>
|
|
<Anchors>
|
|
<Anchor point="TOP">
|
|
<Offset><AbsDimension x="0" y="28"/></Offset>
|
|
</Anchor>
|
|
</Anchors>
|
|
</Texture>
|
|
</Layer>
|
|
</Layers>
|
|
<Scripts>
|
|
<OnLoad>CastingBarFrame_OnLoad()</OnLoad>
|
|
<OnEvent>CastingBarFrame_OnEvent()</OnEvent>
|
|
<OnUpdate>CastingBarFrame_OnUpdate()</OnUpdate>
|
|
</Scripts>
|
|
<BarTexture file="Interface\TargetingFrame\UI-StatusBar"/>
|
|
<BarColor r="1.0" g="0.7" b="0.0"/>
|
|
</StatusBar>
|
|
</Ui>
|