Tag-v0.0.2
What's new
Tag
Tag-v0.0.2 (2026-10-05)
feat: Pause in raids, on by default (operator: "we should have an option that is on by default that tag is suspended in a raid.").
profile.raidSuspend("Pause in raids" in the window,/tag raid).Tag:Suspended()is that setting andIsInRaid(): while true our throws are not counted, sent or played;OnCommReceiveddrops every Tag message; Start Round, Pause and Taunt refuse with a chat line saying why (Tag:RefuseIfSuspended); the catch-up handshake asks nobody; the throw button hides; the window's status says "Paused while you're in a raid."GROUP_ROSTER_UPDATErepaints when the state flips, so leaving the raid wakes Tag by itself. The item can still be thrown (the game's own action), Tag just ignores it.perf: Tag hears only the player's own casts. The four
UNIT_SPELLCAST_*events moved off AceEvent, which has no unit filter (so every cast by every unit in a raid reached Tag's handlers only to be dropped), ontoTag.castFrameregistered with the client'sRegisterUnitEvent(event, "player")(Classic EraSimpleFrameAPIDocumentation.lua:942; Blizzard's cast bar registers these events the same way,CastingBarFrame.lua:231). Raised when the operator asked whether Tag could cause micro-stutter.test: Harness pinned at WoWAPITesting
1575b3d, which modelsRegisterUnitEventat Tag's request (inbox 878fc305). A spec pins that party and raid casts never reach Tag's handlers.Tests/support/addon.luaunregisters the previous load's cast frame on each fresh world, becauseframes.reset()leaves earlier frames listening (in game a/reloadclears them). 187 specs, 100% line coverage.
Tag-v0.0.1 (2026-10-05)
- feat: First version. Tag turns the Happy Fun Rock (item 18640) and the Heavy Leather Ball (item 18662) into a game of tag. Asked for on Discord ("make a tag addon that uses a happy fun rock or the leatherworking one"). Built on the fleet framework: AceAddon-3.0 (
TagCore.lua), AceDB-3.0 (TagDB, per-character profiles), AceEvent-3.0, AceComm-3.0 and AceConsole-3.0, with the UI on AceGUI-3.0 and LibAceGUIWidgets, and VersionCheck-1.0 registered on enable with the raw TOC version. No XML: there is noBindings.xml, so the throw key is an override binding. - feat: Throw detection reads each item's own use spell through
C_Item.GetItemSpell(bareGetItemSpellwhere the namespaced form is missing), so no spell ID is hard-coded and the same code serves every client.UNIT_SPELLCAST_SENTrecords the target per cast GUID;UNIT_SPELLCAST_SUCCEEDEDturns it into a hit;UNIT_SPELLCAST_FAILED/INTERRUPTEDdrop it. When the payload's target is empty the current target's name is used. Secret values (Retail) are never compared or stored. - feat: The group hears every hit over AceComm (prefix
TagGame,T<tab>target<tab>item, the sender is the thrower) onINSTANCE_CHAT,RAIDorPARTY, whichever the player is in; nothing is sent solo. The client's echo of our own send is ignored. Optional party/raid chat announcement for players without the addon (/tag announce, off by default). - feat: The throw button is LibAceGUIWidgets'
CreateSecureActionButton(TagThrowButton, typeitem, unittarget), so its attributes, anchors and visibility queue in combat. It shows the item and count ("Throw Rock (3)"), greys out with the reason when the bags hold neither item, followsBAG_UPDATE_DELAYED, moves on Shift-drag out of combat and remembers where it was left./click TagThrowButtonworks from a macro. - feat: The throw key is set in the window and bound with
SetOverrideBindingClickon a frame Tag owns. Protected calls made in combat (the key, a button built during combat) wait forPLAYER_REGEN_ENABLEDthrough one shared queue,Tag:AfterCombat, because one AceEvent object holds one handler per event. - feat: The Tag window (
/tag): a LibAceGUIWidgetsClearFramewith aLAGW-Stripof Throw (drop-down), Throw key, Announce in chat, Show button and Reset Scores (with a confirm), who is It, and aW.RowListscoreboard (Player, Throws, Tagged). Size and position persist throughW:PersistWindow; Escape closes it. - feat: Being tagged plays a dodgeball hit, then a "Tag, you're it!" clip (operator: "i added a tag you're it clip, tie it to being tagged"; then "i just added a dodgeball one for when you're hit. so it would be the dodgeball sound, then the tag clip"). Then "added a sound for when you throw the ball, throw" and "renamed them to make them easier": our own throw that lands plays
sounds/throw.mp3fromUNIT_SPELLCAST_SUCCEEDED(rock and ball alike; a failed throw plays nothing). One setting,profile.sound("Sounds" in the window,/tag sound), covers all three.Tag:PlayTaggedSoundsplayssounds/dodgeball.mp3and chainssounds/tag.mp3onSOUNDKIT_FINISHEDcarrying the hit's sound handle (in every flavour'sSoundDocumentation.lua), not on a timer; a second tag before the first hit ends chains only the newest. If the hit does not start (PlaySoundFilereturns no handle), the clip plays at once. Both on the Master channel, so they are heard with sound effects off and follow the master volume. NOT VERIFIED: that the client firesSOUNDKIT_FINISHEDfor an addon file played withPlaySoundFile; if it does not, only the dodgeball plays. On by default..mp3is one of the two formatsPlaySoundFileaccepts (warcraft.wiki.gg, API_PlaySoundFile); a spec pins that each of the three files exists at the path the code plays and has one of those extensions. Not yet heard in game. - feat: Rounds with a winner (operator: "do we have it set for points to win? i want to play some audio when someone winds"; then chose each hit = 1 point, a target settable in the window, and everyone in the group with Tag hears it; "i added fireworks, that's the win sound").
profile.winAt(default 10, 1-100) is set by a "Hits to win" slider (rounded to a whole number) and/tag win <n>. When our own hit brings our throw count to the target, our client sends the hit, thenW<tab>hits, and callsTag:Win; receivers callTag:Win(sender, hits)without recounting, so the thrower's target decides.Tag:Winprints the winner, shows a "You win Tag!" / "<name> wins Tag!" banner, playssounds/fireworks.mp3on Master when sounds are on, and resets the scoreboard. With announce on the win is also said in group chat. The window's status bar shows the target. Outside a started round each player's own target decides when their throw wins; a started round (below) gives everyone the starter's. - feat: Start Round (operator: "do we have a start button that starts the match? we need one if we don't, and it should play hte if you can dodge a wernch ..... sound clip"). The window's Start Round button and
/tag startcallTag:StartMatch, which sendsS<tab>hitswith our own "Hits to win", andTag:StartRoundon every client clears the scoreboard, setsroundWinAt(whatTag:WinTargetanswers until the round is won), shows a "Tag! First to N hits wins!" banner and playssounds/start.mp3. A start with a target outside 1-100 is ignored. - feat: Pause (operator: "we need a pause button, use the looks like you need a timeout sound when that is pushed"). The window's Pause button (it reads Resume while paused) and
/tag pausecallTag:TogglePause, which sendsPorRto the group;Tag:SetPausedprints who paused, shows a "Timeout! Tag is paused." / "Game on!" banner and playssounds/looks-like-you-need-a-timeout.mp3or, on resume,sounds/duke_nukem_time_to_kick_ass.mp3. While paused our throws score nothing (with a chat line saying so) and hits heard from the group are not recorded. Session state only: a/reload, a new round or a win ends it. The operator confirmed the Duke Nukem clip as the resume sound ("resume yes, perfect"). - feat: Taunt (operator: "when someone else gets hit we should be able to taunt them, you got knocked tha fugg out is that clip"). The window's Taunt button (enabled only while somebody else is It) and
/tag tauntcallTag:Taunt, which sendsX<tab>targetto the group (and, with announce on, a group chat line) and callsTag:Tauntedon every client: a chat line, a "Knocked out!" banner for the player taunted, andsounds/you-got-knocked-tha-fuggg-out.mp3for everyone.Tag.TAUNT_COOLDOWN(5 s) spaces our own taunts, and receivers play at most one taunt per groupmate per cooldown, so a held key or a spammer cannot loop the clip. - feat: Effects switch (operator: "we also need an option to turn off audio/visual effects").
profile.effects(default on; "Effects" in the window,/tag effects) gates every banner, the red edge and the win flash. Every banner now goes throughTag:Bannerand every sound throughTag:Sound, so the two settings cover all of them from one place; the chat lines always print. - feat: Red pulsing screen edge when we are hit (operator: "i want to add the red pulsing edge thing when you get hit with the ball"), by the rock or the ball. New file
TagEffects.lua:Tag:PulseEdgeshows the client's own low-health vignette,Interface\FullScreenTextures\LowHealth(loaded byLowHealthFrame.xmlin both the classic_era and live FrameXML), tinted red in ADD blend on aFULLSCREEN_DIALOGframe that never takes the mouse, pulsing three times over 1.5 s. - feat: Colour flash on a win (operator: "i want to add the screen flashing all kinds of colors/stuff when someone wins").
Tag:FlashWinglides a translucent wash through seven colours for 4 s with the vignette edge in the opposite colour, then fades out over the last second. It glides rather than cutting between colours, and its brightness swells at 2 Hz, to stay below the 3 Hz flashing range that can trigger photosensitive seizures. Both effects run onOnUpdate, not animation groups, so one code path serves every client. Not yet seen in game. - fix: From Peer Review of the self-audit (thread 95b6e7a3). Shift-click never throws: the button carries
shift-type1 = "none", a type with no secure handler (classic_eraSecureTemplates.lua:730-744), so Shift+left, the move gesture, cannot throw at the target; it also stops a Shift+key throw through the override binding. In combat the button repaints only its count and queues one full refresh throughTag:AfterCombat(guarded bybuttonRefreshQueued), instead of relabelling to an item the locked action is not using and adding three closures to LibAceGUIWidgets' combat queue per bag update. Not taken: registeringUNIT_SPELLCAST_STOPto clearTag.pending-- if the throw has any cast time, STOP may arrive before SUCCEEDED and would erase every throw before it is counted (event order not verified), so leaking one small table per odd cast is the safer side.Tests/globals_spec.luanow fails if.luacheckrcand.luarc.jsonname different globals. - feat: The throw button shows only while the Tag window is open, and has a drag grip (operator, with a screenshot of "Throw Rock (0)": "i need a way to hide this button in settings, and a way to drag move it somewhere else. and it should only be there when the window is open").
Tag:RefreshButtonnow shows the button only whenprofile.button.shownandTag:WindowOpen();Tag:ToggleWindowrefreshes it, and the window's own raw host frame refreshes it from OnShow/OnHide, so Escape (UISpecialFrames) hides the button too, without hooking AceGUI's pooled frame. A 14x26 grip, an ordinary frame on the button's left edge, moves the button on a plain drag. I had disabled the button with no item in the bags, and I believe a disabled button never receives the drag (not verified in game), so with Shift-drag the only gesture, the operator's "(0)" button could not be moved at all. The grip moves it whatever the button's state, and never throws, because the secure button acts on mouse down. A new "Lock button" checkbox (and/tag lock, which now repaints) hides the grip and stops moving; "Show button" stays. Shift-drag on the button itself still works. The throw key (an override binding clickingTagThrowButton) is meant to work with the window closed; NOT VERIFIED in game that a click on the hidden button throws. In combat the button's show/hide is queued by LibAceGUIWidgets, so closing the window mid-fight hides the button when combat ends. Confirmed by the operator in game on Classic Era: the button shows with the window, the grip moves it, it hides when the window closes, and it comes back where it was left. - feat: Catching up with the game in progress (operator asked "how does the score sync across multiple addons?"; each client had only built its own scoreboard from the hits it heard, so a late joiner, a
/reloador a player out of the group diverged until the next round). A handshake with no timer: on joining a group (GROUP_ROSTER_UPDATE, and at login already grouped) Tag sendsQto the group; every groupmate with a game going (somebody It, a round started, or paused) whispersH; the asker whispersGto the first and ignores the rest; only that one whispersA<tab>round target<tab>paused<tab>It<tab>name=thrown,tagged;..., which replaces the asker's scoreboard, It, round target and pause. AnAfrom anyone the asker did not tell to go is ignored. A random answer delay was written first and refused by writ's no-timer law; the handshake replaces it and gives exactly one full answer. Not yet seen in game. - perf: A closed window is not repainted (operator asked whether Tag could cause micro-stutter). After the window had been opened once,
Tag:RefreshWindowrebuilt the scoreboard rows and reset every control on each bag update, hit and catch-up even while hidden; it now returns whileTag:WindowOpen()is false, and opening the window repaints it. - feat: Seven per-flavour TOCs with the same file list: Classic Era (11509), WoW Forever (
_Camelot, 16001), TBC/Anniversary (20506), Wrath (30405), Cata (40402), Mists (50504) and Retail (120005, 120007, 120100)..pkgmetarequiresace3,versioncheckandlibaceguiwidgetsand turns TOC generation off. - chore:
wow-version-replication.ps1is event-driven: aFileSystemWatcherdrained withWait-Eventin the main runspace, instead of the fleet copy's 2-second poll. Its.pkgmetaglobs follow the packager's shellcasematching, where*also crosses folders, so*.ps1skips a script at any depth. Verified with-DryRun: only the TOCs, the four Lua files,sounds/, README, LICENSE and CHANGELOG would be copied, the same set the packager ships. - test: Offline suite on the WoWAPITesting harness (
Tests/wowapisubmodule): 178 specs over the real installed Ace3, LibAceGUIWidgets and VersionCheck-1.0, and 100% line coverage ofTagCore.lua,TagEffects.lua,TagButton.luaandTagWindow.lua. The harness is pinned at WoWAPITestingd640d37, which addedGetItemSpell, the override bindings andRaidNotice_AddMessageat Tag's request, and madeace.reset()silence Ace3's named event frames (they had outlived the reset and answered every later spec's events: two listeners for one throw, measured). Tag's interim stand-ins for all of these are gone;Tests/support/addon.luanow only declares the two items inwow.itemsandChatTypeInfo.RAID_WARNING, which the env leaves to consumers..luals/wow.luagives the language serverCreateFrame's real signature. - KNOWN, not verified in game: that
UNIT_SPELLCAST_SENTnames the target for these two item spells on every client; that the items still drop or sell on every client this ships for; and that a Shift-drag on the button never also throws.
This mod has no additional files

