Welcome! This document outlines the standards, conventions, and preferences for developing the CooldownTracker World of Warcraft Addon. Please adhere to these guidelines when suggesting or writing code.
CooldownTracker is a lightweight, manual tracking tool for Raid Leaders in World of Warcraft (Midnight 12.0+). It displays a customizable grid/column of major healer cooldowns. The raid leader manually clicks abilities to start and reset their timers. It does not automate tracking based on combat log events, keeping the addon fast, simple, and strictly within Blizzard's UI terms of service.
The addon is split into modular components, sharing a single private namespace (CT).
CooldownTracker.toc: The manifest. Controls load order (critical).Data.lua: Defines the base abilities (CT.COOLDOWNS), default durations, icons, and class colors.UI.lua: Handles rendering the main tracker window, row layouts (grid vs vertical), and the per-frameOnUpdatetimer loop.Settings.lua: Implements the in-game options panel (Escape -> Options -> AddOns) using the modernSettingsAPI. Handles SavedVariables overrides.Core.lua: The bootstrap file. HandlesADDON_LOADED, slash commands (/cdt), and initializes the UI and Settings..github/workflows/ci.yml: Luacheck lint workflow — runs on push tomainand all PRs..github/workflows/release.yml: Packaging and GitHub Release workflow — runs onv*tag push..github/pull_request_template.md: PR template with WoW-specific checklist..luacheckrc: Luacheck static analysis configuration..pkgmeta: BigWigs packager metadata for release packaging.
The project uses GitHub Actions for linting and release packaging.
Two separate workflow files handle CI and releases:
.github/workflows/ci.yml — triggers on push to main and on all pull requests:
- Runs
luacheckon all Lua files vianebularg/actions-luacheck@v1.
.github/workflows/release.yml — triggers on v* tag push only:
- Runs luacheck as a gate, then runs
BigWigsMods/packager@v2to package the addon and create a GitHub Release with the zip attached. - Uses
permissions: contents: writeso the built-inGITHUB_TOKENcan publish releases — no additional secrets required.
std = "lua51"covers standard Lua globals.- WoW API globals (
CreateFrame,GetTime,C_Timer,Settings, etc.) are declared inread_globals. - Addon-owned globals (
CooldownTrackerDB, slash command vars) are declared inglobals. - When adding new WoW API calls: if luacheck starts reporting an undefined global, add it to
read_globalsin.luacheckrc.
- Dev-only files (
AGENTS.md,.github/,.luacheckrc,.gitignore) are listed underignoreso they are excluded from the release zip. - When adding new dev-only files (e.g. test scripts, editor config), add them to the
ignorelist in.pkgmeta.
git tag -a v1.2.0 -m "Version 1.2.0"
git push origin v1.2.0Also update ## Version: in CooldownTracker.toc to match the tag before tagging.
- Private Namespace: Always use the addon's private namespace passed by the WoW client on load. Do not pollute the global environment.
local AddonName, CT = ... -- CT is the shared table across all files
- SavedVariables: Use
CooldownTrackerDBfor persistence. Initialize it inADDON_LOADEDinCore.lua. Current keys:Key Type Default Purpose columnsnumber 1Grid column count (1–9) classCountstable {}Per-class player counts (1–5) customDurationstable {}Per-spell duration overrides (seconds) disabledSpellstable {}Set of spell IDs hidden from tracker playSoundOnReadyboolean truePlay alert sound on cooldown expiry frameLockedboolean falseLock window position (disable drag) point,relPoint,x,ymixed nil Saved window position spellOrdertable {}Ordered array of spell idstrings defining tracker display order; empty = default - Slash Commands: Register slash commands via the
SlashCmdListtable. Handle arguments cleanly. - No Third-Party Libraries: The addon intentionally does not use Ace3 or other framework libraries to remain lightweight. Rely on the standard WoW API.
World of Warcraft has a strict "taint" system for UI frames. Violating these rules will cause the addon to break the user's UI during combat.
- Dynamic Frame Creation: Do NOT use
CreateFrame()orSetParent()dynamically in response to user input (e.g., inside anOnTextChangedorOnClickhandler).- Fix: Pre-allocate a pool of frames at
ADDON_LOADEDtime. Show/Hide and reconfigure existing frames.
- Fix: Pre-allocate a pool of frames at
- Rebinding Secure Scripts: Do NOT call
SetScript("OnClick", ...)on protected templates (likeUIPanelButtonTemplate) at runtime after initial creation.- Fix: Set the script once during creation. Have the script read data dynamically from the frame itself (e.g.,
local cd = self:GetParent().cd).
- Fix: Set the script once during creation. Have the script read data dynamically from the frame itself (e.g.,
- Settings API: The
Settings.OpenToCategory()function is fully protected in The War Within/Midnight.- Fix: Do not attempt to programmatically open the Settings menu from slash commands. Instruct the user via chat text to open it manually.
- Grid vs Vertical: The UI supports both a wide-row vertical layout (columns = 1) and a compact square-card grid layout (columns 2-9). Use
CT:LayoutRows()to reflow. - Clickable Rows: Each spell row is a
Buttonframe (not a plainFrame). The entire row is the click target — there is no separate "Used"/"Reset" button.OnClickis set once at creation time and readsrow.cddynamically to avoid taint. - Font: Use
Fonts\\FRIZQT__.TTFwith an"OUTLINE"flag for clear, readable text in the tracker. - Backdrops: Use the
BackdropTemplatemixin for frames requiring backgrounds/borders. Main frame backdrop alpha is0.55(translucent); title bar is0.65. - Title Bar Controls: The title bar contains a close button (
UIPanelCloseButton, 18px) and a lock toggle button (plainButton, 32px) anchored to its left. The lock button usesInterface\\BUTTONS\\LockButton-Locked-Up/LockButton-Unlocked-Uptextures and togglesCooldownTrackerDB.frameLocked. Always guardOnDragStartwith aframeLockedcheck to avoid a Lua error when the frame is not movable.