promotional bannermobile promotional banner

Tag

Throw a Happy Fun Rock or a Heavy Leather Ball at a friend, and they're It! Tag turns the game's two throwable toys into a game!
Back to Files

Tag-v0.0.2

File nameTag-Tag-v0.0.2.zip
Uploader
PmptastyPmptasty
Uploaded
Oct 6, 2026
Downloads
8
Size
408.1 KB
Flavors
RetailMoP ClassicClassic TBCClassicForever
File ID
9074959
Type
R
Release
Supported game versions
  • 12.1.0
  • 12.0.7
  • 12.0.5
  • 5.5.4
  • 4.4.2
  • 3.4.5
  • 2.5.6
  • 1.60.1
  • 1.15.9

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 and IsInRaid(): while true our throws are not counted, sent or played; OnCommReceived drops 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_UPDATE repaints 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), onto Tag.castFrame registered with the client's RegisterUnitEvent(event, "player") (Classic Era SimpleFrameAPIDocumentation.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 models RegisterUnitEvent at Tag's request (inbox 878fc305). A spec pins that party and raid casts never reach Tag's handlers. Tests/support/addon.lua unregisters the previous load's cast frame on each fresh world, because frames.reset() leaves earlier frames listening (in game a /reload clears 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 no Bindings.xml, so the throw key is an override binding.
  • feat: Throw detection reads each item's own use spell through C_Item.GetItemSpell (bare GetItemSpell where the namespaced form is missing), so no spell ID is hard-coded and the same code serves every client. UNIT_SPELLCAST_SENT records the target per cast GUID; UNIT_SPELLCAST_SUCCEEDED turns it into a hit; UNIT_SPELLCAST_FAILED / INTERRUPTED drop 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) on INSTANCE_CHAT, RAID or PARTY, 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, type item, unit target), 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, follows BAG_UPDATE_DELAYED, moves on Shift-drag out of combat and remembers where it was left. /click TagThrowButton works from a macro.
  • feat: The throw key is set in the window and bound with SetOverrideBindingClick on a frame Tag owns. Protected calls made in combat (the key, a button built during combat) wait for PLAYER_REGEN_ENABLED through one shared queue, Tag:AfterCombat, because one AceEvent object holds one handler per event.
  • feat: The Tag window (/tag): a LibAceGUIWidgets ClearFrame with a LAGW-Strip of Throw (drop-down), Throw key, Announce in chat, Show button and Reset Scores (with a confirm), who is It, and a W.RowList scoreboard (Player, Throws, Tagged). Size and position persist through W: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.mp3 from UNIT_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:PlayTaggedSounds plays sounds/dodgeball.mp3 and chains sounds/tag.mp3 on SOUNDKIT_FINISHED carrying the hit's sound handle (in every flavour's SoundDocumentation.lua), not on a timer; a second tag before the first hit ends chains only the newest. If the hit does not start (PlaySoundFile returns 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 fires SOUNDKIT_FINISHED for an addon file played with PlaySoundFile; if it does not, only the dodgeball plays. On by default. .mp3 is one of the two formats PlaySoundFile accepts (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, then W<tab>hits, and calls Tag:Win; receivers call Tag:Win(sender, hits) without recounting, so the thrower's target decides. Tag:Win prints the winner, shows a "You win Tag!" / "<name> wins Tag!" banner, plays sounds/fireworks.mp3 on 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 start call Tag:StartMatch, which sends S<tab>hits with our own "Hits to win", and Tag:StartRound on every client clears the scoreboard, sets roundWinAt (what Tag:WinTarget answers until the round is won), shows a "Tag! First to N hits wins!" banner and plays sounds/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 pause call Tag:TogglePause, which sends P or R to the group; Tag:SetPaused prints who paused, shows a "Timeout! Tag is paused." / "Game on!" banner and plays sounds/looks-like-you-need-a-timeout.mp3 or, 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 taunt call Tag:Taunt, which sends X<tab>target to the group (and, with announce on, a group chat line) and calls Tag:Taunted on every client: a chat line, a "Knocked out!" banner for the player taunted, and sounds/you-got-knocked-tha-fuggg-out.mp3 for 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 through Tag:Banner and every sound through Tag: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:PulseEdge shows the client's own low-health vignette, Interface\FullScreenTextures\LowHealth (loaded by LowHealthFrame.xml in both the classic_era and live FrameXML), tinted red in ADD blend on a FULLSCREEN_DIALOG frame 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:FlashWin glides 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 on OnUpdate, 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_era SecureTemplates.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 through Tag:AfterCombat (guarded by buttonRefreshQueued), 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: registering UNIT_SPELLCAST_STOP to clear Tag.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.lua now fails if .luacheckrc and .luarc.json name 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:RefreshButton now shows the button only when profile.button.shown and Tag:WindowOpen(); Tag:ToggleWindow refreshes 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 clicking TagThrowButton) 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 /reload or 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 sends Q to the group; every groupmate with a game going (somebody It, a round started, or paused) whispers H; the asker whispers G to the first and ignores the rest; only that one whispers A<tab>round target<tab>paused<tab>It<tab>name=thrown,tagged;..., which replaces the asker's scoreboard, It, round target and pause. An A from 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:RefreshWindow rebuilt the scoreboard rows and reset every control on each bag update, hit and catch-up even while hidden; it now returns while Tag: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). .pkgmeta requires ace3, versioncheck and libaceguiwidgets and turns TOC generation off.
  • chore: wow-version-replication.ps1 is event-driven: a FileSystemWatcher drained with Wait-Event in the main runspace, instead of the fleet copy's 2-second poll. Its .pkgmeta globs follow the packager's shell case matching, where * also crosses folders, so *.ps1 skips 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/wowapi submodule): 178 specs over the real installed Ace3, LibAceGUIWidgets and VersionCheck-1.0, and 100% line coverage of TagCore.lua, TagEffects.lua, TagButton.lua and TagWindow.lua. The harness is pinned at WoWAPITesting d640d37, which added GetItemSpell, the override bindings and RaidNotice_AddMessage at Tag's request, and made ace.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.lua now only declares the two items in wow.items and ChatTypeInfo.RAID_WARNING, which the env leaves to consumers. .luals/wow.lua gives the language server CreateFrame's real signature.
  • KNOWN, not verified in game: that UNIT_SPELLCAST_SENT names 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.