File Details
CarboniteAllinOneRetailClassic-v12.0.0-00034
- R
- May 26, 2026
- 20.27 MB
- 2
- 12.0.1+7
- Retail + 3
File Name
CarboniteAllinOneRetailClassic-v12.0.0-00034.zip
Supported Versions
- 12.0.1
- 12.0.0
- 11.0.5
- 5.5.3
- 4.4.2
- 3.4.3
- 2.5.5
- 1.15.8
Carbonite All in One (Retail & Classic)
v12.0.0-00034 (2026-05-26)
Full Changelog Previous Releases
- Whatsnew: auto-show on login + "don't show for this update" opt-out
The What's New window now auto-pops on login/reload while there is an
undismissed changelog, and keeps reappearing until the player clicks the
new bottom "Don't show for this update again" button. That pins
lastreadtime to the newest entry's timestamp; a later entry (bigger max
timestamp) re-arms the popup. Closing with the X no longer counts as
read, so it returns next login.
Also fixes the entry list rendering: it iterated pairs() over numeric
timestamp keys (jumbled date order) and over the line array; now sorts
keys newest-first and uses ipairs for the lines.
Adds the 2026-05-26 changelog entry (wrapped to the detail pane width).
Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com - RXP integration: route the RXPGuides arrow through Carbonite's HUD
Mirrors RXPGuides' navigation arrow onto Carbonite's own travel
arrow. A light poller reads RXPG_ARROW.element and pushes the current
step as a Goto waypoint via the TomTom-emulation bridge; the HUD arrow
then points at it. RXP's own arrow is suppressed non-invasively by
swapping its frame OnUpdate for a no-op and zeroing alpha (no writes to
RXP's saved variables), and restored when the feature is off.
The waypoint title is the step's objective text, parsed out of
step.text with accept/turn-in lines filtered and RXP colour tokens
converted to real escapes.
New "Route RXPGuides arrow through Carbonite" toggle in Notes (on by
default), with translations for all 11 Notes locales.
Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com - Map menu: drop the Plugins submenu
The HandyNotes / Questie / RareScanner entries inside the map's
right-click "Plugins..." submenu fired the respective addon's slash
command — which only opens its options panel and isn't a useful
discovery point now that:
* each integration has a dedicated toolbar button (left toggles,
right opens the addon's settings),
* the Notes module's options panel has the same toggles + sizes,
* Carbonite no longer ties any rendering decision to the slash
command of the host addon.
Dropping the submenu cleans up the context menu without losing any
functionality. The "Plugins..." locale string becomes unused; locale
strings aren't pruned automatically but harmless to leave.
Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com - RXP integration: add translations for the 8 remaining Notes locales
Followup to e761e0f — adds the three new RXP option strings
("Display RXPGuides waypoints On Map", description, "RXPGuides Icon
Size") to deDE / esES / esMX / frFR / itIT / koKR / ptBR / zhCN /
zhTW. The previous commit only updated enUS + ruRU.
Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com - RXP integration: options panel + transparent text-only pin
Adds Toggle / Icon Size entries for the RXPGuides integration to the
Notes module's options panel, mirroring the pattern Questie /
HandyNotes / RareScanner already use. Toggle is the existing
Nx.fdb.profile.Notes.RXP flag, size is RXPSize. Both invalidate the
integration cache + redraw immediately on change. Disabled when
_G.RXP isn't loaded.
Visual: pin is now text-only — fully transparent backdrop, just the
class-coloured outlined step number floating over the map. The
white-logo backdrop was visual noise on a 24px frame (and the logo
texture didn't always load), and the colour + outline carries
"RXPGuides" identification well enough on its own. Strips the
trailing "+" RXP appends for stacked pins; the cluster of adjacent
labels speaks to the stack.
Also enable Questie + Questie SE (available quests) by default —
matches the user-asked-for setup for a fresh profile.
Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com - RXP integration: step text overlay in player class colour
Iterate on the RXPGuides pin appearance to match what RXP draws on
Blizzard's world map:
* Texture: keep the RXP logo (rxp_logo-128) as the pin background
so the icon is identifiable as an RXP waypoint at a glance.
* Label: render the live step text from frame.text:GetText() on
top via NxLabel. Falls back to the step index from activeObject
when frame text is empty (e.g. WorldMapFrame hasn't been opened
yet so RXP's pool render() hasn't run on this guide).
* Class colour: pull r/g/b from RAID_CLASS_COLORS[playerClass] so
the label is paladin pink, druid orange, etc — same convention
RXP uses on Blizzard's map.
* Font: explicit STANDARD_TEXT_FONT at 12pt OUTLINE so the step
number stays legible against the busy logo backdrop.
Lazy-creates the NxLabel font-string on the pooled icon frame the
first time the stamp callback runs against it. Nx.Map:PreAllocateIcons
creates pool frames without NxLabel (only the lazy GetIconStatic path
does), so without this lazy-create we'd silently render featureless
squares for the first ~200 pool slots.
onStamp is wired per-pin, not via Pin.SetClassField, because
Pin.Acquire only re-runs Mixin.Apply for newly created pins —
recycled pool entries keep their original class-field snapshot, so
a class-level onStamp set after first pin creation wouldn't reach
them.
Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com - RXPGuides integration: harvest map pins + toolbar button
Adds RXPGuides to the existing adopted-addon set (Questie / HandyNotes /
RareScanner). RXP registers its waypoint pins through HereBeDragons-
Pins-2.0 using its internal addon table (exposed at _G.RXP for debug)
as the registry "ref". The new Carbonite.Notes/Integrations/RXP.lua
reads HBDPins.worldmapPinRegistry[_G.RXP] each frame, copies entries
that match the current uiMapID onto a new "!RXP" Pin/Layer, and gates
the rebuild behind a content hash like the other integrations.
Tooltip text is best-effort: RXP's frame.render() stashes its pin data
table onframe.activeObject, which carries the underlying guide step
plus element so we get "Step N" + the step description without walking
RXP's guide structure directly.
Toolbar button (AddonButtons.lua, "AddonBtn_RXP"):
* Icon: RXP's own rxp_logo-128 texture (matches the addon's branding)
* Left click: toggles Nx.fdb.profile.Notes.RXP and refreshes the
integration (busts the cache then runs the producer for the
current map, or clears the layer when disabling).
* Right click: calls _G.RXP.settings.OpenSettings() — the same
opener RXP's own /rxp command uses.
Profile default: Nx.fdb.profile.Notes.RXP = true, RXPSize = 24.
Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com - AddonButtons: localize Questie / HandyNotes / RareScanner tooltips
The toolbar buttons for the three adopted addons (Questie / HandyNotes /
RareScanner) had English-only left-click / right-click tooltips. Routed
them through AceLocale's "Carbonite" namespace and added translations
for all 10 locale dirs (en/ru/de/fr/es/esMX/it/pt/ko/zh-CN/zh-TW).
Missing translations fall back to enUS automatically, so the strings
stay readable everywhere.
Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com - ClipFrameWChop: route to ClipFrameMF on instance / BG maps
NxMapGuide sets clipKind=chop on the "!POI" iconType (mailbox /
innkeeper / vendor markers). Master's legacy NxMap render loop
ignored the per-class ClipFunc setter and always dispatched via
ClipFrameByMapType, so the chop flag was effectively a no-op —
icons reached ClipFrameMF on instance maps and stayed pinned.
The Pin/Layer Renderer introduced inda45116started actually
honoring cls.clipKind through its clipFn dispatcher, so !POI icons
finally reached ClipFrameWChop. That function projects via world
coords + ScaleDraw + MapPosXDraw, and MapPosXDraw follows the
player via NXPlyrFollow=true — so the icons drifted under a fixed
instance-map texture as the player walked. The drift was hidden by
the oversized icons (ScaleDraw * 0.08 multiplier on tight-scale
instance maps blew them up to fill the canvas); myb9eec72
instance-size clamp shrank them to InstanceScale and the drift
became visible. Visible symptom: mailbox / innkeeper icons in MoP
cities (e.g. Shrine of Seven Stars, uiMapID 393) moving relative to
the city map texture as the character walked.
Fix: gate ClipFrameWChop with the sameIsInstanceMap(RMapId) or IsBattleGroundMap(RMapId) and CurOpts.NXInstanceMapscheck that
ClipFrameByMapType uses, delegating to ClipFrameMF when it fires.
Same benefit for Kill / Death (UEvents) and Questie's !QUE
integration layer — they're also chop-clipped.
Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com - Renderer: route patrol-line hover tooltip through Nx.TooltipText
The Map provider's LINE-drawMode hover ticker wrote directly to
GameTooltip (SetOwner / AddLine / AddDoubleLine / Show / Hide). Per
the project convention (feedback_gametooltip_taint), Carbonite must
never write to Blizzard's GameTooltip — those writes mark the
tooltip's child frame widths as "tainted by Carbonite", and the taint
propagates into Blizzard's own secure code that later reads the same
tooltip. The visible symptom in retail is the FlightMap money tip:
Blizzard_FlightMap/FM_FlightPathDataProvider.lua:252
→ SetTooltipMoney → MoneyFrame_Update:307
attempt to perform arithmetic on a secret number value
(execution tainted by 'Carbonite')
The patrol-line hover (added in the recent MapProvider / LINE drawMode
work) was the missed taint source. Switched to Nx.TooltipText — the
private GameTooltip-cloned frame Carbonite already owns for its own
addon-side tooltips. GameTooltip is still read (IsShown / GetOwner) to
yield correctly when another tooltip is on screen; reads don't taint.
Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com - Instance-map icon-size fix + boss-encounter pins
Two fixes for the Carbonite map on retail instance maps:- Renderer.lua: instance-map icons were rendering at ~800 px (covering
the whole canvas) because Pin.Acquire's Mixin.Apply copies the class's
w/h onto every pin instance — so renderWP's per-pin override branch
(if pin.w then pw = pin.w * scale) always fired and immediately
clobbered the local InstanceScale clamp set just above. Fenced the
per-pin override behindnot isInstso the clamp survives on
instance/BG maps. - MapEngine.lua: dungeon-journal boss pins via
C_EncounterJournal.GetEncountersOnMap, paired with EJ_GetCreatureInfo
(creature name + iconImage) and EJ_GetEncounterInfo (description +
journalInstanceID). Three traps the implementation had to dodge:- Boss entries live in a dedicated ePOIs slot in
ArrayConcatReuse, NOT aPOIs. aPOIs entries get _type=4, hitting
the iteration's stale "_type==4 means Archaeology dig site"
gate (a leftover from the long-removed
C_WorldMap.GetMapLandmarkInfo API; the field name was reused
for source-array index but the filter wasn't updated). - Frame-level bump to self.Level+51 inside the boss branch. The
legacy GetIcon(5) path lands at level ≈ 11, but ClipFrameMF
stamps the instance-map overlay at level 50, hiding the pin. - Zone-coord re-anchor on instance maps. ClipFrameWNoChop
projects via world coords + ScaleDraw, but the instance canvas
displays zone [0..1] stretched to [0..MapW] directly.
Carbonite assigns instances a generic small world-scale
(1002/25600 ≈ 0.039) that's tighter than the actual instance
extent, so world-coord placement compressed positions toward
the canvas centre.
Click handling: NXType=5000 (cat=5) for bosses. Left-click toggles
Blizzard's Dungeon Journal at the encounter (second click on the
same boss closes the panel). Right-click reuses the standard
Carbonite icon menu (Goto / Clear Goto / Paste Link).
Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com
- Boss entries live in a dedicated ePOIs slot in
- Renderer.lua: instance-map icons were rendering at ~800 px (covering
- Fix: bonus task / world quest icons blinking at ~2 Hz on retail
Nx.Quest:UpdateIcons gated everything below the dirty-check fingerprint
behind an early return; the BONUS TASKS / WORLD QUESTS block at the
bottom of the function (line 629+) was part of that gated region. Its
IconWQFrms pool, unlike the Pin/Layer-backed POIs above, gets reset to
Next=1 by MapEngine:ResetIcons every frame -- so on clean frames where
the early return fired we never re-stamped the WQ icons, and
HideExtraIcons hid them. Next dirty frame re-stamped them, and they
flashed back. Player saw every retail world-quest pin blinking at the
dirty-frame cadence (~2 Hz at 30 fps).
Two related changes:
* The dirty-check now guards ONLY the per-quest walk that feeds the
persistent provider; the WQ block below runs every frame so its
legacy direct-stamp pool gets refilled before HideExtraIcons.
* The 1-second taskInfoCache refresh ticker no longer overwrites the
cache with a nil/empty list. C_TaskQuest.GetQuestsOnMap occasionally
returns empty mid-refresh on retail; clobbering the cache that
frame produced the same WQ-icon disappearance for one pass, just
via a different path. mapChange still does an unconditional fetch
so cross-zone transitions don't carry stale entries.
Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com - retail+MoP quest data: rewrite ~13.4k legacy mapIds to modern
Bundled retail and MoP Quest data had pre-Cata legacy zone mapIds
(the 1411-1461 / 1941-1957 range used by TBC and Cata Classic data)
sprinkled into Start, End, and Objectives entries. Both flavors
should be on the modern Legion+ uiMapID scheme (Durotar=1,
Tirisfal=18, Westfall=52, Azuremyst=97, Exodar=103, Bloodmyst=106,
...). Mixed schemes broke GetWorldPos for the legacy rows; tracking
arrows would either snap to world origin or to whatever stale
texture/overlay happened to share the legacy id.
Cross-walked cata+tbc Zones.lua against retail+mop Zones.lua by
zone name to build 64 legacy -> modern mapping pairs. Walked every
Start / End / Objectives POI string and rewrote the mapId field
(second |-segment) when its value was in the table. Coords, NPC
IDs, type fields, w/h preserved verbatim -- only the integer in
position 2 of each "..." literal changed.
Substitution counts:
retail: 6297 across 8 Quests*.lua files
MoP: 7152 across 10 Quests*.lua files
total: 13449
Skipped (intentionally):
[1416] Alterac Mountains -- zone removed in Cataclysm, no modern
counterpart. Any quest referencing 1416 will continue to
fail; needs manual review per affected quest.
[1434] Stranglethorn -- Cata split this into Northern Stranglethorn
(50) and Cape of Stranglethorn (224). Defaulted all 757
occurrences to 50 (most legacy quests sat in the northern
half). Cape-region quests can be manually moved to 224 if
they surface in tracking tests.
Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com - gitignore: stop tracking scripts/
The data backfill helpers (audit_quest_starts.py, backfill_quest_ends.py,
backfill_from_att.py) are one-shot tooling used to clean the bundled
Quest data files, not addon source. They stay in the working tree
locally but don't belong in git.
Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com - quest tracking: flicker / track-to-end / Start data backfill / misc crashes
A multi-layer pass on the retail "map flickers while a quest is tracked"
symptom plus several adjacent bugs that surfaced during repro:
Tracking.lua + DataAndComm.lua: kill the flicker. Five contributing
causes, each fixed:
* IsTargeted compares with a 5-world-unit tolerance instead of exact
float equality. C_QuestLog.GetNextWaypoint returns slightly
different uiMapIDs (97 vs 103, etc.) and sub-yard wobble for the
same NPC at zone boundaries, defeating the skipSame guard;
AddTarget then fired ~5x/sec (every CalcAutoTrack tick via the
Watch:UpdateList 200ms timer), each call reset UpdateTrackingDelay
to 0 and CalcTracking rebuilt the routing path every frame. This
single change is what stopped the flicker for every test quest,
not just the diagnostic one.
* Removed the GetLegacyMapInfo translation in the override block.
Blizzard uiMapID 103 (Exodar) maps to HBD legacy id 471 -- but
Carbonite's MapWorldInfo[471] is the Mogu'shan Vaults raid
instance entry (built by the instance-init loop with the Pandaria
dungeon entrance world coord). GetWorldPos(471,...) returned
Pandaria coords; the arrow snapped to the wrong continent.
Retail's MapWorldInfo is already keyed by Blizzard uiMapIDs, so
no translation is needed.
* GetClosestObjectivePos returns a third value: the zone of the
geometrically winning entry. TrackOnMap overwrites mId with it
so multi-zone Objectives[] lists (10324-style) don't carry a
stale first-entry mapId through to CalcTracking.
* srcMapId in CalcTracking and the tryMap fallback in TrackOnMap
use C_Map.GetBestMapForUnit("player") instead of MapUtil's
GetDisplayableMapForPlayer / Map:GetCurrentMapId. The latter two
follow WorldMapFrame's displayed-map override on retail, so
hovering a neighbor zone repointed the override at THAT zone's
POI and made the target re-attribute mid-tick.
* Sentinel mId=0 + all-zero coord guards in TrackOnMap so bundled
"<npc>|0|32|0|0" rows don't silently SetTarget at world origin.
* CalcAutoTrack early-returns to qObj=0 / useEnd=true when
cur.Complete is set instead of routing at a stale CloseObjI.
BlizzardLog.lua: the RecordQuestsLog fast path (taken when
RealQEntries == qcnt) now syncs cur.Complete = isComplete and
persists per-leaderboard cur[n] / cur[n+100]. The rising-edge
"Quest Complete" block only fired the toast; cur.Complete stayed
nil indefinitely, so CalcAutoTrack's complete-branch never fired
for newly-completed quests and the arrow stuck on the closest
leftover objective POI (quest 9663 "The Kessel Run" surfaced this).
SuperTrack.lua: SetActiveCarboniteQuest short-circuits the
objective-picker when cur.Complete is set. The loop misbehaved on
collect-from-one-of-N quests where bundled Objectives[] has N
alternative pickup locations but Blizzard reports < N leaderboards
-- the trailing cur[n+300] flags are nil, the "not done" branch
fires for them, and the arrow snaps to the highest-indexed
alternate instead of the ender.
NxQuest.lua: the AddQuestWatch / RemoveQuestWatch shims bail on
nil/zero questIndex. Goto curs have QI=0 and the native C_QuestLog
call errored hundreds of times per second on tracking-clear.
CheckMapAndCom.lua: GetAbandonQuestItems shim mirroring the one in
NxQuest.lua. The bare global was removed on retail; quest cancel
was hitting a nil-call.
MapIcons.lua: file-localworldquestdb = Nx.Quest.worldquestdb
alias. The compat shim was scoped to NxQuest.lua and didn't survive
the module extraction; flight-map world-quest icons crashed.
DurabilityWatcher.lua: pcall around GetInventorySlotInfo. RangedSlot
doesn't exist on retail; the unprotected call killed the average /
minimum durability walks.
Five cur.QId assignment sites instrumented with diagnostic prints
gated on Nx._dbgQId for catching a rare "tracking operates on
wrong quest id" report. Sites: RecordQuestsLog full-rebuild (only
on actual ID change), party-quest new cur, Goto new cur,
OnSuperTrackChanged self-heal, broad self-heal in OnQuestUpdate.
retail Quest data: 4,686 Start coord backfills from MoP. Same
shape as last session's 8.4k End backfill -- the extractor
mis-attributed Start NPC/zone for thousands of quests where End is
correct and MoP+retail agree on the questID identity. Canonical
case: 9663 retail Start said NPC 17069 in Eastern Plaguelands;
correct is NPC 17649 in Bloodmyst (same as End). Script:
scripts/audit_quest_starts.py, auto-applies NPC_MISMATCH_SAME_QUEST
+ RETAIL_BROKEN_SAME_QUEST; leaves ZONE_MISMATCH_SAME_QUEST (138)
and COORD_DRIFT (608) alone (multi-starter quests / phasing /
zone refactors).
TBC Quest data: removed bogus Exodar (1947) kill-area row from
quest 10324 Objectives[1] -- moongraze stags don't spawn in the
Exodar city map, the entry was an extraction artifact that made
TrackOnMap route through Exodar even when the player stood 10y
from a legitimate Azuremyst objective.
Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com - quest/map fixes: retail crash + tracking arrow + tooltip lag + skew
A mixed bag of retail-flavor fixes accumulated this session:
- PlayerCharacter/InitGlobal.lua: defensivetr.Versionguards on
five HUDOpts/Travel/Gather/Capture branches; retail was nil-comparing
where the saved-variable shape no longer matched the legacy schema
and crashed at load with "attempt to compare nil with number".
- Quests/Tracking.lua: title-row (qObj==0) override block now rejects
GetWorldPos returning 0,0, so a failed GetQuestsOnMap conversion no
longer clobbers a good bundled End coord for complete quests. The
qObj>0 path already had this guard; mirror it here.
- Quests/QuestWindow.lua: on QUEST_LOG_UPDATE walk every cur and
re-key cur.QId from GetQuestIDForLogIndex(cur.QI), but only when
the recorded title still matches Blizzard's live title for that
log index. Catches Blizzard quest-ID renumbering (e.g. Inoculation
9303 -> 37444) without re-keying to whatever quest now sits at a
shifted log slot. Also forces _iconDirty + invalidates the
Questie integration cache so objective-progress tooltips don't
lag a tick behind the watch list.
- Quests/MapIcons.lua: hoist TrackOnMap above the dirty-check so
the blob anchor refreshes every frame (fixes "blob drifts with
player and snaps back"); switch to MapProvider arrows/POIs;
Util_c2rgba for table-color support.
- Quests/Tooltips.lua, BlizzardLog.lua:hadObjectivesguard on
PatchQuestFromBlizzard / RecordQuestsLog so bundled DB objectives
aren't overwritten by a single synthesized type-32 point.
- Quests/SuperTrack.lua: don't wipe Nx.Quests[mungeId] in
ProcessQuestDB; preserve bundled data and just skip the
chain-walker work for irrelevant quests. Adds a self-heal in
OnSuperTrackChanged for cur.QId drift.
- Quests/WorldQuestWindow.lua: filter C_QuestLine.GetAvailableQuestLines
byisQuestStart and not inProgress, with a nil->true default so
MoP Classic (which lacks isQuestStart) still works. Stops
available-quest icons sticking after accept.
- Map/MapEngine.lua: ClipZoneFrm after Show() in the world-quest
blob path; iconW/iconH at texture-native (no ScaleDraw multiply);
broadcast MapChanged on MapId flip.
- UI/UIEngine.lua: nil-x guard in Window:RecordLayoutData; phantom
children cleanup when reusing a ToolBar frame; debug hooks gated
on Nx._dbgTip in SetTooltipText.
Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com - retail quest data: backfill ~8.4k broken Ends + 1.5k Objectives
The ATT/BtWQuests port left ~13.6k retail quest entries with the
sentinel End "<npcid>|0|32|0.00|0.00" -- NPC id preserved but mapId
and coords zeroed. Without coords the static map question-mark for
turn-ins never renders and Tracking.lua falls through to the runtime
Blizzard-API override only when a live waypoint exists.
Two ordered passes, both with a turn-in NPC equality guard so a
mismatched NPC across expansions never replaces a known-good coord:
1. MoP shard (same uiMapID scheme as retail) -- 8,406 End coords
and 1,572 Objectives blocks spliced into retail entries where
MoP has them and retail doesn't.
2. AllTheThings strict mode -- 546 additional Ends recovered, but
only when ATT'sqgslist includes the same NPC id named by
the broken End. That restricts ATT's coord (which describes the
questgiver, not the turn-in) to short fetch quests where the
start and end NPC are the same person.
Net: 13,644 broken -> 4,692 (~66% reduction). Remaining are mostly
post-MoP quests with distinct start/end NPCs that no in-tree source
can resolve without a Wowhead-class scrape; runtime tracking already
handles those for live quests via GetNextWaypoint/GetQuestsOnMap.
Both backfill scripts are idempotent and committed under scripts/
so the pass can be re-run if upstream MoP/ATT data changes.
Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com - Map provider API + NxQuest migration + addon toolbar buttons
Public Carbonite.Map provider API for third-party icon plug-ins
(Modules/Map/MapProvider.lua + Docs/MapProvider.md). Carbonite.Quests'
icon producer fully migrated off the legacy GetIconStatic pool onto
the new Pin/Layer pipeline via the provider — start/end POIs,
objective points, area spans, distance arrows, and completed-quest
markers all flow through Nx.Quest:AddPOI / Nx.Quest:AddArea now,
with a producer-level dirty-check at the top of UpdateIcons that
skips the whole per-quest walk when nothing relevant changed.
Renderer additions for the migration:- pin.NXType / pin.NXData propagate to the frame so the legacy
t >= 9000 dispatcher routes hover/click into Quest unchanged - pin.onStamp callback for per-pin custom frame setup (glow,
label, vertex colors) - cls.rawSize class flag — per-pin w/h passed raw + center-anchored
cull bypass for area spans whose w/h are world units already - renderWP uses Util_c2rgba (table-aware) instead of Util_c2rgb so
solid-color area pins with {r,g,b,a} table colors render
MapProvider prefixes every provider's pin kind with '!' so the
renderer's computeEnabled honors them regardless of drawNonGuide.
Each pin kind gets its own Layer named the same as the class so
Pin.GetClass(layer.name) resolves.
Toolbar buttons for Questie / HandyNotes / RareScanner: left
click toggles the integration's Carbonite display flag (+ mirrors
to the addon's own enabled state where possible), right click opens
the addon-specific menu (Questie's QuestieMenu at cursor;
HandyNotes/RareScanner Settings panel via Settings.OpenToCategory).
PassRightClick TypeData flag on Nx.Button lets adopters receive
right-click instead of opening the toolbar config menu.
Townsfolk (Questie manualFrames, data.Type == "manual") route to a
smaller !QUE_T layer at ~45% of QuestieSize so reagent vendors and
profession trainers don't crowd the map at full POI size.
Plugin ports broken out into per-concern sibling files via the
validated relocate pattern: NxFav, NxWarehouse, NxQuest entry
files are thin AceAddon shells now (2401 → 82, 4017 → 231,
14803 → 579 lines respectively).
HandyNotes patrol-path rendering + Questie patrol-path rendering
- click delegation for adopted HandyNotes / Questie / RareScanner
pin clicks (routed back to the source addon's OnClick).
Small fixes shipped alongside:
- Nx.Window:RecordLayoutData guards against nil from GetPoint()
on frames mid-drag with no anchor - Nx.ToolBar:Create wipes orphaned NxBut* children from the
reused Frm so subsequent CreateToolBar calls don't leave
phantom click receivers at the same positions - Layer:Clear truncates in place instead of self.pins =
- Renderer hover-ticker early-bails when no LINE pins exist
- Nx.Notes:BustIntegrationCache helper consolidates the
dirty-check cache wipes across Questie / HandyNotes / RareScanner
Docs: - Carbonite/Docs/Architecture.md — codebase overview, plugin
refactor summary, map subsystem notes - Carbonite/Docs/MapProvider.md — public API reference
- README.md points at both
Co-Authored-By: Claude Opus 4.7 noreply@anthropic.com
- pin.NXType / pin.NXData propagate to the frame so the legacy
- Carbonite.lua: extract Nx.Split into Util + consolidate breadcrumb comments
- Nx.Split (the legacy unpack-based string splitter that data files
call at FILE-LOAD time) moved to Util/NxSplit.lua so it loads via
Util.xml -- before Carbonite.lua and before the data XMLs need it. - The forest of "X moved to Modules/Y" stub comments scattered
through the file replaced with a single 'Extracted subsystems' lookup
table at the top, indexing every extracted piece to its new home.
Carbonite.lua: 458 -> 308 lines (down 94% from the 4999-line
pre-experimental baseline).
- Nx.Split (the legacy unpack-based string splitter that data files
- Guide: stop hard-coding Timber in the Gather folder
Timber (the Lumber Mill / Logging gather node) was listed in
NxMapGuide's static Gather folder definition AND added a second
time by PatchFolder when WODMaps is true. On pre-WoD flavors
(Classic Era, TBC Classic, Wrath, Cata, MoP) the static entry
showed even though Timber didn't exist in those expansions; on
retail/WoD+ users saw it twice.
Drop the static entry. PatchFolder already adds it conditionally
on WODMaps -- same pattern used for Artifacts (CataMaps),
Everfrost (WOTLKMaps), and Gas (TBCMaps).
Gas left alone: it's already gated on TBCMaps, which is false on
Classic Era. TBC Classic + later have Outland and Gas extraction
is a real TBC feature there. - Carbonite.lua: extract AceAddon lifecycle + NXOnLoad
Moves Nx:OnInitialize (AceDB:New + RegisterCallbacks + SetupConfig- RegisterComm), Nx:OnProfileChanged (default re-install on missing
MapSettings + reload), the empty OnEnable / OnDisable stubs and
Nx:NXOnLoad (the Carbonite.xml frame OnLoad target: registers /carb
slash, ADDON_LOADED + UNIT_NAME_UPDATE events, stamps the player
class color strs) into Modules/InitFlow/AceAddonLifecycle.lua.
AceAddon's InitializeAddon (runs on ADDON_LOADED('Carbonite')) and
Carbonite.xml's NxFrame OnLoad still resolve these by name on the
Nx/Carbonite table; loading via Modules.xml is fine because
Modules.xml runs before either lifecycle trigger fires.
Carbonite.lua: 534 -> 458 lines (down 91% from baseline).
- RegisterComm), Nx:OnProfileChanged (default re-install on missing
- Carbonite.lua: extract SetupEverything + early event handlers + LocaleInit
- Nx:SetupEverything (the post-PLAYER_LOGIN init pipeline that
walks every subsystem's :Init method, schedules the splash
animation, hooks ZGV waypoints, and stamps Nx.Initialized = true) - Nx:ADDON_LOADED + Nx:UNIT_NAME_UPDATE (the early WoW frame
event handlers registered in NXOnLoad) - Nx:LocaleInit (one-liner)
All move to Modules/InitFlow/SetupPipeline.lua. Carbonite.xml's
NxFrame still registers ADDON_LOADED + UNIT_NAME_UPDATE via
RegisterEvent in NXOnLoad; the handlers stay on the Nx namespace
so AceEvent dispatch resolves.
Carbonite.lua: 651 -> 534 lines (down 89% from the 4999-line
pre-experimental baseline).
- Nx:SetupEverything (the post-PLAYER_LOGIN init pipeline that
- Carbonite.lua: extract NXOnUpdate (the per-frame pump)
The legacy per-frame OnUpdate handler -- post-load Setup kick,
loot auto-click for /carb loot, Nx.Proc tick, the GameTooltip
taint-safe text scanner with the file-scope _tt_lenOf / _tt_neq
helpers, Nx.Com/Nx.Map/Nx.Quest OnUpdate calls, the periodic
character-record stamp, and the one-shot 'Whats New!' menu
append -- moves to Modules/MainUpdater/NXOnUpdate.lua.
Carbonite.xml's NxFrame OnUpdate script still calls
Nx:NXOnUpdate(elapsed) by name.
Carbonite.lua: 783 -> 651 lines (down 87% from the 4999-line
pre-experimental baseline). - Carbonite.lua: extract three more event handlers into EventHandlers
Moves Nx:NXOnEvent (the legacy frame-event router), Nx:OnPlayer_login
(group-cache + Com kick + InitWins + /played hijack) and
Nx:OnUpdate_mouseover_unit (Quest tooltip refresh + GUID debug
line) into Modules/UserEvents/EventHandlers.lua next to the
existing six handlers. Carbonite.xml's NxFrame OnEvent script
still calls Nx:NXOnEvent by name.
Carbonite.lua: 843 -> 783 lines. - Carbonite.lua: extract three tiny Nx helpers (LootIt / Time / UnitIsPlusMob)
- Nx:LootIt() - vendor-test auto-click helper, called by LootHandler.
- Nx:Time() - monotonic event-row timestamp, called by UEventsEngine.
- Nx:UnitIsPlusMob(u) - elite-mob classification, called by Comm and
PlayerCharacter.
All three move into Modules/PlayerCharacter/MiscHelpers.lua.
Carbonite.lua: 895 -> 843 lines.
- Carbonite.lua: extract LDB broker + InitEvents
- Nx.BrokerMenuTemplate + Nx.Broker + ShowBrokerMenu helper ->
Modules/Integrations/LDBEngine.lua (Carbonite.lua: 1027 -> 968).
Plugin OnInitialize hooks (CarboniteQuests / Notes / Warehouse)
still tinsert into Nx.BrokerMenuTemplate after Carbonite's
Modules.xml has run, so the menu extension keeps working. - Nx:InitEvents (AceEvent embed + WoW event registrations for
Nx, Nx.Com, Nx.Map.Guide, Nx.AuctionAssist, Nx.Travel) ->
Modules/InitFlow/EventRegistration.lua. SetupEverything still
calls Nx:InitEvents() at PLAYER_LOGIN.
Carbonite.lua: 968 -> 895 lines (down 82% from the 4999-line
pre-experimental baseline).
- Nx.BrokerMenuTemplate + Nx.Broker + ShowBrokerMenu helper ->
- Carbonite.lua: extract LibDataBroker setup into Integrations/LDBEngine
Moves Nx.BrokerMenuTemplate (base menu entries) and Nx.Broker
(LibDataBroker-1.1 NewDataObject with the click/tooltip handlers)
plus the retail-aware ShowBrokerMenu helper out of Carbonite.lua.
The plugins (CarboniteQuests, CarboniteNotes, CarboniteWarehouse)
extend the template by tinsert from their OnInitialize -- which
fires after their TOC processes, by which time the Carbonite
addon's Modules.xml has populated Nx.BrokerMenuTemplate.
Carbonite.lua: 1027 -> 968 lines (broke under 1000). - Carbonite.lua: extract 468-line AceDB defaults table
Moves the 'local defaults = ' block (lines 250-717: every
saved-variable default for char / global / profile across General,
Map, MiniMap, Track, Quest, QuestWatch, Comm, etc.) out of
Carbonite.lua into Modules/PlayerCharacter/Defaults.lua and
exposes it as Nx.Defaults.
OnInitialize and OnProfileChanged are the only callers; both run
at runtime (AceAddon InitializeAddon + AceDB profile-change
callback), well after Modules.xml has loaded.
Carbonite.lua: 1491 -> 1027 lines (down 79% from the 4999-line
baseline). - Carbonite.lua: extract PackXY / UnpackXY into Modules/Map/CoordPack
The hex coord packers (Nx:PackXY -> 'XXXYYY', Nx:UnpackXY -> x,y)
used by the event log and capture-recording paths. Pure functions,
no state. Methods stay on Nx because the Data/Shared guide files
and the legacy Nx.UEvents log writer still call them by name.
Carbonite.lua: 1509 -> 1491 lines. - Carbonite.lua: extract slashCommand dispatcher + InitGlobal migration
- Nx.slashCommand (the /carb elseif dispatcher: goto / gotoadd /
menu / options / resetwin / rl / track / winpos / winshow /
winsize / gatherd / herb / mine / addopen / cap / com / item /
kill / loot / mapd / questclr / unitc / unitd / vehpos / editmode- the catch-all 'note' WHISPER forwarder) ->
Modules/SlashHandler/SlashCommandEngine.lua. NXOnLoad still does
SlashCmdList['Carbonite'] = Nx.slashCommand, so the global hook
binding keeps resolving.
- the catch-all 'note' WHISPER forwarder) ->
- Nx:InitGlobal (saved-variable migration + default installation
~170 lines) -> Modules/PlayerCharacter/InitGlobal.lua. Called
once at PLAYER_LOGIN from SetupEverything.
Carbonite.lua: 1821 -> 1509 lines (-70% from the 4999-line baseline).
- Nx.slashCommand (the /carb elseif dispatcher: goto / gotoadd /
- Carbonite.lua: extract Nx.UEvents class + Nx.Proc tick scheduler
Two more in-place extractions:- Nx.UEvents (Init / AddInfo / AddDeath / AddKill / AddHonor /
AddHerb / AddMine / AddTimber / AddOpen / GetPlyrPos / UpdateAll
/ SortCmp / Sort / List:Open / List:Update / UpdateMap) ->
Modules/UserEvents/UEventsEngine.lua (~415 lines). NXOnUpdate
still calls Nx.UEvents:Sort + UpdateAll, and EventHandlers.lua
still calls Nx.UEvents:Add{Herb,Mine,Timber,Open}. - Nx.Proc (Init / New / SetFunc / OnUpdate) ->
Modules/MainUpdater/ProcScheduler.lua (~80 lines). Lightweight
tick scheduler used by the title-screen animation and other
multi-frame work; SetupEverything's Init call and NXOnUpdate's
per-tick OnUpdate still call Nx.Proc:X.
Carbonite.lua: 2305 -> 1821 lines (-64% from the 4999-line baseline).
- Nx.UEvents (Init / AddInfo / AddDeath / AddKill / AddHonor /
- Carbonite.lua: extract Nx.Item and Nx.NXMiniMapBut into focused modules
- Nx.Item:Init / Load / EnableLoadFromServer / DisableLoadFromServer
/ AskDeleteVV / ShowTooltip / DrawTimer (~100 lines) ->
Modules/ItemRegistry/ItemEngine.lua. The existing ItemRegistry.lua
documented surface already wraps Nx.Item. - Nx.NXMiniMapBut:Init / Menu_OnOptions / Menu_OnShowMap /
Menu_OnShowEvents / Menu_OnHideWatch / Menu_OnShowAuction /
Menu_OnShowCom / Menu_OnProfiling / ToggleProfiling /
NXOnEnter / NXOnClick / OpenMenu / NXOnUpdate / Move plus the
Nx.ModChatReceive stub (~210 lines) ->
Modules/Map/MinimapButtonEngine.lua. Carbonite.xml's NXMiniMapBut
frame OnClick/OnEnter/OnUpdate handlers still resolve to Nx.NXMiniMapBut:X.
Carbonite.lua: 2609 -> 2305 lines (-54% from the 4999-line baseline).
- Nx.Item:Init / Load / EnableLoadFromServer / DisableLoadFromServer
- Carbonite.lua: extract Gather subsystem into Modules/Gather/GatherStorage
The 1077-line gather block -- Nx.GatherInfo (per-node-type
herb/mine/timber metadata), Nx.GatherRemap (id->id remap),
Nx.GatherCache (spell-name lookup cache), and every Nx:Gather*
function (Init / GetGather / IsGathering / HerbNameToId /
MineNameToId / GatherHerb / Mine / Timber / Gather / GatherUnpack /
GatherDelete{Herb,Mine,Timber,Misc} / GatherConvert /
GatherNodeToCarb / GatherImportCarb / GatherImportBatch) -- moves
into Modules/Gather/GatherStorage.lua.
External callers (NxMapGuide.lua's PatchFolder, the existing
GatherEngine.lua / Gather.lua / GatherEvents.lua surfaces) all
touch Nx.GatherInfo and the Nx:Gather* methods at runtime, so
load-order is safe; Modules.xml loads after Carbonite.lua but
before any of those runtime callsites fire.
Carbonite.lua: 3678 -> 2609 lines (~48% drop from the 4999-line
pre-experimental baseline). - Carbonite.lua: extract TitleScreen + AuctionAssist engines
Two more in-place extractions:- Nx.Title:Init / TickWait / TickWait2 / Tick (the login splash
fade-in/fly-out animation) -> Modules/TitleScreen/TitleScreenEngine.lua.
Methods stay on Nx.Title because Nx.Proc dispatch and SetupEverything's
callsite both expect that table. The existing TitleScreen.lua
documented surface already proxies through it. - Nx.AuctionAssist.OnAuction_house_show / closed / item_list_update,
AuctionFrameBrowse_Update post-hook, and the Create/OnListEvent/Update
stubs -> Modules/AuctionAssist/AuctionAssistEngine.lua. Methods stay
on Nx.AuctionAssist because InitEvents binds them via
AuctionAssist:RegisterEvent('AUCTION_', 'OnAuction_').
Carbonite.lua: 3950 -> 3678 lines.
- Nx.Title:Init / TickWait / TickWait2 / Tick (the login splash
- Modules.xml: fix double-hyphen inside XML comment
The XML comment on line 7-9 contained ' -- ' (in the phrase
'Depends on Nx existing -- loaded after the UI engine'), which
XML 1.0 forbids inside comments. WoW's XML parser appears to have
rejected the whole Modules.xml file as a result, which is why
everything the file loaded was missing at runtime
(Nx.Util_coltrgb2colstr, Nx.SetupConfig, etc. all nil).
Also softened the '<Kind>' angle-bracket inside the EventLog
comment block to be safe (technically valid in a comment, but
not worth the risk).
This was the single cause of both errors in the user's most
recent reload: the OnLoad path crashing because
Util_coltrgb2colstr was nil, and OnInitialize crashing because
SetupConfig was nil. Both functions live in files loaded via
Modules.xml; rejecting the XML threw away every <Script> entry. - Carbonite.lua: extract per-character event log into UserEvents/EventLog
The packed-row event log on Nx.CurCharacter.E -- DeleteOldEvents,
DeleteOldEvent, AddEvent, GetEventMapId, UnpackEvent and the six
Add{Info,Death,Kill,Herb,Mine,Timber}Event wrappers -- moves out
of Carbonite.lua into Modules/UserEvents/EventLog.lua. The
Nx.UEvents:Add* methods in NXOnUpdate still call Nx:Add<Kind>Event
so the methods stay on the Nx namespace.
Carbonite.lua drops from 4089 to 3950 lines (under 4k for the
first time since the relocation push started; total drop from
the pre-experimental 4999-line baseline is ~21%). - Carbonite.lua: extract character-data ops into PlayerCharacter/CharacterData
Saved-variable character record management moves out of
Carbonite.lua into Modules/PlayerCharacter/CharacterData.lua:
Nx:InitCharacter / GetRealmCharName / CalcRealmChars
Nx:FindCharacter / DeleteCharacter / GetUnitClass / RecordCharacter
Nx:GetData / GetDataToolBar / GetHUDOpts / GetCap / CaptureFind
Nx:CopyCharacterData / DeleteCharacterData
Methods stay on the Nx namespace because external callers
(CharacterRoster module, OptionsEngine page handlers, NxQuest,
NxWarehouse) still call them as Nx:... The public-facing accessor
class PlayerCharacter.lua already covers the live-API side
(GetName/GetClass/GetGUID); this file owns the persistent record.
Carbonite.lua drops from 4355 to 4089 lines. - Carbonite.lua: extract six WoW event handlers into UserEvents/EventHandlers
OnPlayer_regen_disabled / OnPlayer_regen_enabled / OnUnit_spellcast_sent
/ OnZone_changed_new_area / OnPlayer_level_up / OnParty_members_changed
/ OnUpdate_battlefield_score all move to
Modules/UserEvents/EventHandlers.lua. Nx:InitEvents still does
'Nx:RegisterEvent(WOW_EVENT, "OnX")' against AceEvent dispatch,
and the methods stay on the Nx namespace, so registration keeps
resolving. Conceptually each handler belongs in a different shell
(CombatLockdown / Gather / ZoneTransition / PartyState / Battleground);
bundled here as an intermediate step so follow-up commits can move
them out one at a time.
Carbonite.lua drops from 4520 to 4355 lines. - Carbonite.lua: extract dialogs + mouseover NPC debug capture
Two focused extractions out of Carbonite.lua:- Nx:ShowMessage / ShowEditBox / ShowMessageTrial /
FindActiveChatFrameEditBox -> Modules/UI/Dialogs.lua (~110 lines).
Methods stay on Nx (legacy callers like Nx:ShowMessage(...) keep
working) and the new Carbonite.UI.Dialogs surface exposes them
as Dialogs:Message / Dialogs:EditBox. - Nx:UnitDGet / UnitDCapture / UnitDTip -> Modules/DebugFlags/UnitDataCapture.lua
(~130 lines). Debug-only capture of mouseover NPC coords and
GameTooltip text, gated on Nx.db.profile.Debug.DebugUnit.
Carbonite.lua's slashCommand 'unitc' and OnUpdate_mouseover_unit
still call Nx:UnitD* directly; nothing else uses these so the
extraction is purely a file move.
Carbonite.lua drops from 4754 to 4520 lines.
- Nx:ShowMessage / ShowEditBox / ShowMessageTrial /
- MapEngine: restore 'local Map = Nx.Map' in Map:Update
I dropped this in commit b41abcb thinking it was unused -- it's
referenced 7 times later in the same function (Map.TaxiOn,
Map.TaxiStartTime, Map.TaxiX, Map.TaxiName) so removing it raised
'attempt to index global Map' the moment the player was on a taxi
flight. - TomTom integration: install Nx.EmulateTomTom alias at file-load
Carbonite.lua's UNIT_NAME_UPDATE handler and MapEngine's Map:Init
both call Nx.EmulateTomTom() before CARBONITE_LOADED fires --
UNIT_NAME_UPDATE is dispatched via AceEvent as soon as the player
unit is named, which can happen before ADDON_LOADED for Carbonite.
The deferred subscription left the alias nil at those callsites,
which (a) erred at line 1122 and (b) aborted the rest of
Map:Init and cascaded into a second crash in Nx.UEvents:GetPlyrPos
because Nx.Map.Maps[1] never got created.
Move the alias to file-load time so it's available from the moment
TomTom.lua finishes loading; the CARBONITE_ENABLE-based full
:Emulate() install is unchanged. - Experimental: extract Nx:GetMaxGatherSkill et al. into Modules/Gather/GatherEngine
The three Nx-namespaced gather helpers GetMaxGatherSkill,
ShouldShowGatherNode, GetGatherNodeName were sitting at the top of
Carbonite.lua. NxMapGuide.lua + Modules/Gather/Gather.lua still
call them as Nx:... methods at runtime, so they survive as a
focused engine file under Modules/Gather/.
The new GetMaxGatherSkill now delegates to Compat/Expansion.lua --
the canonical per-flavor cap source -- with a literal mirror as a
fallback in case Compat isn't loaded yet. Drops ~60 lines from
Carbonite.lua. - Carbonite.lua: drop dead --[<input disabled="disabled" type="checkbox" />]-- WhatsNewUnread minimap-glow block
The block had been commented-out for years; the now-extracted
Whatsnew engine owns the unread-vs-read tracking (HasUnread /
SetLastReadTime) and the glow flag lives in NameplateGlow's
documented API. The stub was just noise inside NXOnUpdate. - Experimental: drop legacy Nx.EmulateTomTom stub
Modules/Integrations/TomTom.lua provides a fuller TomTom emulation
(ClearAllWaypoints, GetCurrentPlayerPosition, GetKey/IsValidWaypoint,
/way /cway /wayb slash commands, etc.) and already aliases
Nx.EmulateTomTom on CARBONITE_LOADED -- runtime callers
(Carbonite.lua's SetupEverything, MapEngine's Init block) route
through that. The 25-line stub in Carbonite.lua that just bound
_G.TomTom = and registered
/way + /cbway has been removed. - Experimental: extract Whatsnew engine from Carbonite.lua
Moves the Nx.Whatsnew data table (categories + the dated 'Maps'
changelog entries) and the legacy window methods (ToggleShow /
Create / Recordtime / Cat_button / OnListEvent / Update) plus
Nx:WhatsNewUnread out of Carbonite.lua and into
Modules/Whatsnew/WhatsnewEngine.lua.
Carbonite.lua's NXOnUpdate still reads Nx.Whatsnew.HasWhatsNew to
add the minimap-button menu entry once; the engine populates the
table at load time (after Carbonite.lua and before runtime) so the
runtime reference resolves. The existing Whatsnew.lua and
WhatsnewWindow.lua surfaces already proxy through Nx.Whatsnew, so
they see no change.
Drops 165 lines from Carbonite.lua (4999 → 4834). - Experimental: relocate NxUI engine into Modules/UI/UIEngine
The 8457-line legacy UI foundation -- every Nx.prt* / Nx.Util_*
helper plus the Nx.Window / Nx.Button / Nx.List / Nx.Item /
Nx.Title / Nx.NXMiniMapBut classes -- moves from Carbonite/NxUI.lua
into Modules/UI/UIEngine.lua unchanged. Methods continue to attach
to the legacy tables Carbonite.lua initialises. The newer UI
primitive tree at Carbonite/UI/* lives in a separate
Carbonite.UI.* namespace and does not collide.
Modules.xml now loads UIEngine.lua FIRST so the Nx.prt / Nx.Util_*
helpers and the Nx.Window family are available before any of the
other legacy/relocated files run.
Fixed two invalid-escape texture paths (Interface\Buttons... )
in the slider builder that strict Lua rejected; WoW's interpreter
accepted the single backslashes but they're now correct as escaped
pairs and the file passes luac -p cleanly.
Removed Carbonite/NxUI.lua and dropped it from all four TOCs. - MapEngine: drop dead code in :Update and the WorldMap attach/detach
Targeted cleanups visible while relocating NxMap into MapEngine:- Update(): the WorldMapFrame visibility 'if' was an empty block
(return statement long-since commented out), and the matching
'if self.NeedWorldUpdate then ... end' wrappers around UpdateWorld()
were also commented to no-ops. Removed both, plus an unused
'local Map = Nx.Map'. - OnUpdate(): replaced an if/else that assigned a boolean to
Nx.Map.MouseOver with a single 'winx ~= nil' assignment, and
dropped a stale block that had only commented-out lines inside. - AttachWorldMap / DetachWorldMap: bodies have been --[<input disabled="disabled" type="checkbox" />]-- for
years with multiple callsites still calling them. Collapsed each
to a one-line named no-op so the file no longer carries ~80 lines
of stale legacy WorldMapButton-reparenting code.
- Update(): the WorldMapFrame visibility 'if' was an empty block
- Experimental: relocate NxMap engine into Modules/Map/MapEngine
The 15600-line world-map / minimap / waypoint / hotspot / icon /
blob / per-flavor switching engine moves from Carbonite/NxMap.lua
into Modules/Map/MapEngine.lua unchanged. Methods continue to
attach to Nx.Map (initialised as by Carbonite.lua), and the 39
small scaffolds already in Modules/Map/* lazy-access Nx.Map via
accessors so they continue to wrap the engine.
Modules.xml now lists MapEngine.lua at the END of the Map block
so the small shells (Pin, Layer, Pathing, Coords, etc.) load
first. Dropped NxMap.lua from all four TOCs. Cleanups to the
engine itself follow in a separate commit. - Experimental: relocate NxOptions engine into Modules/Options/OptionsEngine
The 4137-line options tree, NXCmd* handlers and Nx.Opts lifecycle
move from Carbonite/NxOptions.lua into Modules/Options/OptionsEngine.lua
unchanged. Methods continue to attach to Nx.Opts (initialised as
by Carbonite.lua) and Nx:SetupConfig / Nx:AddToConfig remain
file-scope globals so external callsites keep working.
The new Modules/Options/Options.lua module already owns the
AceConfig wrapper used by post-port modules; the engine now sits
right after it in load order so the giant legacy tree is available
when other modules try to extend it.
Removed Carbonite/NxOptions.lua and dropped it from all four TOCs. - Experimental: relocate NxCom engine into Modules/Comm/CommEngine
The 1842-line protocol/channel/punk engine moves from
Carbonite/NxCom.lua into Modules/Comm/CommEngine.lua unchanged --
methods continue to attach to Nx.Com (initialised as by
Carbonite.lua), and the existing Modules/Comm/* shells already
lazy-access Nx.Com through accessor helpers so they keep working.
This is a structural move; method-by-method extraction into the
matching Comm shells (PositionShare, ChannelManager, WireFormat,
etc.) will follow as separate commits.
Removed Carbonite/NxCom.lua and dropped it from all four flavor
TOCs. Added CommEngine.lua to Modules.xml after Comm.lua. - Experimental: move NxTravel engine into Modules/Travel/TravelEngine
Relocate the 1295-line taxi/flight-path engine from Carbonite/NxTravel.lua
into Modules/Travel/TravelEngine.lua attached to the Travel module
class. Re-anchor _G.Nx.Travel and Carbonite.Travel at the module so
external callsites (NxMap, NxWarehouse, the new Map/* wrappers that
already proxy through _G.Nx.Travel) keep resolving.
Travel.lua had two delegation stubs (Rebuild/RouteTo) that pointed
at non-existent legacy methods -- replaced with a real /carb travel
add command that re-runs the flight-master Add(typ) scan, and an
equivalent options-page button.
Removed Carbonite/NxTravel.lua and dropped it from all four flavor
TOCs. Added TravelEngine.lua to Modules.xml after Travel.lua. - Experimental: move NxHUD engine into Modules/HUD/HUDEngine
The legacy NxHUD.lua already attached its methods to Nx.HUD =
from Carbonite.lua; this commit relocates the engine (Init, Open,
Create, Show, SetFade, UpdateOptions, Update, plus the global
Nx.HUDGetTracking) into Modules/HUD/HUDEngine.lua attached to the
HUD module class, then re-anchors _G.Nx.HUD and Carbonite.HUD at
the module instance so legacy callsites in NxMap (per-frame
:Update), NxOptions (:UpdateOptions, .TexNames) and Carbonite.lua
(:Init) continue to resolve. The HUD lifecycle/options page in
HUD.lua no longer delegates to a legacy global -- the module IS
the engine now.
Removed Carbonite/NxHUD.lua and dropped it from all four flavor
TOCs (retail, classic, tbc, mop). Added HUDEngine.lua to
Modules.xml after HUD.lua so the engine can call
Carbonite:GetModule("HUD") to attach itself. - Quest: don't record history until server confirms turn-in
GetQuestReward's post-hook fires when the CLIENT sends the turn-in
attempt, not when the server accepts it. For an auto-complete quest
with a full inventory the server rejects the turn-in -- but Carbonite
had already called SetQuest(qId, "C", ...). That wrote a bogus
"completed" entry into the History tab and, worse, filtered the
quest out of the watch list (UpdateList only keeps qStatus == "W"),
hiding the "?" autocomplete button so the player couldn't retry
after freeing bag space.
Defer the SetQuest("C") write to QUEST_TURNED_IN, which only fires
on confirmed server-side turn-in. Classic Era doesn't fire that
event, so keep the immediate write there.
Recovery: on every RecordQuestsLog pass, if a quest is in the live
log AND has stale "C" history AND C_QuestLog.IsQuestFlaggedCompleted
says the server has not recorded it as completed, the "C" is a
ghost from a pre-fix failed turn-in -- restore it to "W". - Quest: keep ? autocomplete button visible after a failed turn-in
When ShowQuestComplete is dismissed without finishing the turn-in
(e.g. inventory full), Blizzard's AutoQuestPopup tracker drops the
popup and the next RecordQuestsLog pass nils cur.IsAutoComplete in
Carbonite's cache. The watch list render then stopped stamping
QuestWatchAC and the ? button vanished even though the quest was
still pending on the server.
Mirror the live-API fallback the click handler already does:
when the cached flag is false, re-check C_QuestLog.IsComplete and
GetQuestLogIsAutoComplete so the ? stays visible until the quest
is actually removed from the log. - Quest: drop debug print from autocomplete click handler
User-confirmed the fix is working in-game. Remove the diagnostic
chat print added in 67d403a. - Fix: ShowQuestComplete takes questID on retail, log index on Classic
Carbonite called ShowQuestComplete(qIndex) at every site -- the
QuestWatchAC ("?") button click handler and the AutoTurnInAC path.
Blizzard's API differs between flavors:
Classic (Wrath / MoP / etc): ShowQuestComplete(questLogIndex)
Retail (11.x): ShowQuestComplete(questID)
Verified in Blizzard sources:
blizzard/mop/blizzard_uipanels_game/wrath/watchframe.lua:183
ShowQuestComplete(questIndex)
blizzard/retail/blizzard_objectivetracker/blizzard_autoquestpopuptracker.lua:69
ShowQuestComplete(questID)
On retail, passing the log index made the call silently no-op, so
clicking the "?" did nothing visible. Now Nx.isRetail passes qId,
all other flavors pass qIndex.
Also takes the typ.AutoComplete shortcut from the previous commit:
the click handler trusts the button type rather than re-checking
the (often stale) cur.CompleteMerge / cur.IsAutoComplete flags.
Added a one-time chat print "[Carbonite] AutoComplete ? clicked: ..."
on the click so the user can verify the click is reaching the
handler. - Fix: super-track on left-click swallowed "?" autocomplete button clicks
Follow-up to ec7d9a5 — the previous patch fixed the stale-state check
in the watch-list click handler but missed a higher-up code path:
OnListEvent unconditionally drove C_SuperTrack.SetSuperTrackedQuestID
on any left-click on a button row (anything except WatchError).
That super-track call fires first, toggling the waypoint-arrow on/off
for the clicked quest. Once it has run, the click is effectively
"consumed" as a tracking-toggle — the user sees the quest objective
arrow flip on and off in the world (the "switches to quest objective
back and forth" symptom) and never gets the autocomplete completion.
Fix:
* Add AutoComplete = true marker to Nx.Button.TypeData["QuestWatchAC"].
* Skip the super-track block when typ.AutoComplete is set so the
click falls through to the existing ShowQuestComplete branch.
User-reported on retail. MoP Classic was unaffected because it lacks
C_SuperTrack.SetSuperTrackedQuestID and didn't enter that block. - Experimental: pool POI fallbacks + throttle title SetTitle to 15 hz
Two per-frame allocators in legacy NxMap I can patch without
disturbing render behavior:- POI fetch path - several
or {}fallbacks
On every POI cache miss (~twice per second when the map is
open) the fetch path had patterns like:
local areaPOIIds = C_AreaPoiInfo.GetAreaPOIForMap(rid) or
local delvePOIIds = C_AreaPoiInfo.GetDelvesForMap(rid) or
local hubPOIIds = C_AreaPoiInfo.GetQuestHubsForMap(rid) or
...
When the API returned nil (common on maps without that POI
category), eachor {}allocated a fresh empty table. With 6-7
such fallbacks per refresh, that's ~12-14 empty tables/sec
thrown into the pool.
Replaced with a module-level POI_EMPTY sentinel table protected
by a __newindex metatable so a future bug can't accidentally
mutate it.
Also pooled the vPOIs (vignette) outer array as POI_Pool.vPOIs.
The per-vignette entry tables stay fresh because Blizzard's
C_VignetteInfo returns new info tables per call and reusing
them across refreshes would risk stale data. - NxMap title-bar SetTitle was running at 60 hz
The title-bar XY / speed display ran every frame, allocating
several format() strings and calling map.Win:SetTitle twice
per frame. Player position updates from the server at ~10 hz,
so 60 hz title refresh is wasted work and string allocation.
Now gated to Nx.Tick % 4 == 0 (15 hz, same cadence as the
nearby tip throttle at Nx.Tick % 3 == 0) AND skipped when the
built string matches the previous frame. Force-update on first
call so the title doesn't render blank for the first 3 frames.
- POI fetch path - several
- Experimental: revert tooltip pcall-stripping; hoist closures instead
I previously dropped the pcall(function() ... end) wrappers around
#sands ~= Nx.TooltipLastDiffTextin the NXOnUpdate tooltip-
scan branch, on the assumption that those operations couldn't fail.
The user pointed out the protection was there for secure-tainted
GameTooltip strings: GameTooltipTextLeft1:GetText() can return a
string carrying the secure flag from a spell / aura tooltip path,
and both#secureStringandsecureString ~= otherSecureString
raise from a tainted context with the dreaded "interface action
failed because of an addon" error.
The real fix is to keep the pcall guard but stop allocating the
wrapped function per frame. Hoist two file-scope helpers
(_tt_lenOf, _tt_neq) that pcall calls with explicit args. The
closures are now allocated once at file load instead of 60+
times per second.
Same protection, zero per-frame closure allocation. - Experimental: eliminate per-frame allocations in BuildPath + MainUpdater
User-reported memory growth of 1.2MB/sec until GC sweep. Two
allocators on the per-frame path from my refactor:- Pathing:BuildPath was changed in the API design to take
{mapID=, x=, y=} table args, but the caller (NxMap:CalcTracking)
is a hot path - it runs once every ~45 frames per active
waypoint. Each call wrapped src+dst into two fresh tables.
With 3 tracked waypoints that's 6 tables per pulse * many
pulses per minute.
Fix: scalar-arg signature
Pathing:BuildPath(tr, srcMapID, srcX, srcY, dstMapID, dstX, dstY, dstType)
matching the underlying Travel:MakePath. Added a non-hot
BuildPathFromSpec for one-shot callers that want the
table-arg ergonomics. - MainUpdater:OnTick allocated
local ready = {}every frame
to collect subscribers due to fire that tick. Reused a
module-level readyBuf table and reset count, nil-ing only
the slots we used so dead subscribers can still GC. - The MainUpdater driver frame's OnUpdate kept running even
after the legacy NxOnUpdate bridge took over. Now it
permanently nils its OnUpdate script the first time it
observes _drivenByLegacy == true.
These were the per-frame allocators introduced by the refactor.
Remaining baseline growth comes from the legacy NxOnUpdate body
(tooltip-scan pcall closures, CalcTracking's ownlocal tr = {}
per pulse, etc.) which is pre-existing behavior unchanged by my
refactor.
- Pathing:BuildPath was changed in the API design to take
- Experimental: stop overwriting Nx.Notes / Nx.Warehouse / Nx.Quests
in plugin entry files
Three crash reports from in-game, all the same shape:
Carbonite.Notes/NxFav.lua line 300: attempt to call method 'Init' (nil)
Carbonite.Warehouse/NxWarehouse.lua line 921: attempt to call method 'ConvertData' (nil)
Carbonite.lua line 1812: attempt to call method 'RecordCharacter' (nil)
Root cause: each plugin's Plugin.lua wrote its public-API table to
Carbonite.Notes/Carbonite.Warehouse/Carbonite.Quests.
BecauseCarboniteis aliased asNx, those assignments
replaced the legacy method tables that NxFav.lua / NxWarehouse.lua /
NxQuest.lua populate at file load — Init, ConvertData,
RecordCharacter, Update, Folders, etc. all vanished. The deferred
AceTimer-scheduled OnInitialize then called the missing methods
and crashed.
Fix: each plugin now exposes its public API at:
* NotesAddon.Public / WHAddon.Public / QuestAddon.Public (canonical)
* Carbonite.Plugins.Notes / .Warehouse / .Quests (cross-plugin slot)
Plus for Quests, since several sibling classes
(WatchHider / WatchSurface / Routing) already live under
Carbonite.Quests, we keep it as a table and stash the public API
at Carbonite.Quests.Public instead of replacing the whole bucket.
No legacy namespace is touched, no method tables get clobbered. - Experimental: fix three load-time errors reported from in-game
*** Bug 1: 'CarboniteWarehouse already exists' on load ***
Carbonite.Notes/Plugin.lua and Carbonite.Warehouse/Plugin.lua both
called LibStub("AceAddon-3.0"):NewAddon and ran BEFORE NxFav.lua /
NxWarehouse.lua in the TOC. The legacy files then called NewAddon
again on the same name and crashed.
Fix: swap the TOC order so the legacy file creates the addon and
the Plugin.lua's GetAddon-then-NewAddon-fallback finds it. Done
for Notes and Warehouse.
*** Bug 2: Modules/Comm/Comm.lua line 56 nil RegisterComm ***
Module:New only mixes in AceEvent / AceTimer / AceHook by default,
so the AceComm-3.0 methods (RegisterComm, SendCommMessage) were
absent on the new Comm module.
Fix: declare mixins = { "AceComm-3.0" } in Comm's New spec.
*** Bug 3: profile.General nil after my SavedVariables ***
The new SavedVariables called db:RegisterDefaults(merged) where
merged contained only the new modules' defaults. AceDB's
RegisterDefaults removes the previously-registered defaults from
every scope, which erased the entire legacydefaultstree --
profile.General, profile.Battleground, profile.Guide, profile.Map,
profile.Track, profile.Skin, profile.MiniMap, profile.Whatsnew, etc.
Fix: stop calling RegisterDefaults entirely. Instead poke each
module's defaults directly into db.profile/char/global only when
the destination key is nil. The legacy defaults stay AceDB-managed
(profile reset / copy honors them); module defaults are sticky
through normal SavedVariables persistence.
Also extends Carbonite.Quests per-expansion TOCs to load the new
plugin files (WatchHider / QuestWatchSurface / QuestRouting /
Plugin / Data\Shared\QuestDataLoader) so the architecture works on
every flavor, not just retail.
Verification: luac -p clean on all 152 architecture files; smoke
test loads all 152. - Experimental: InstanceDetector / CurrencyTracker / AchievementHelper / WaypointFormat / PartyState
Final extraction round. 152 architecture files pass luac -p.- InstanceDetector/InstanceDetector.lua: GetType / GetDifficultyID /
GetMaxPlayers / GetInstanceMapID / IsInside / IsRaid / IsParty /
IsBattleground / IsArena / IsScenario around GetInstanceInfo. - CurrencyTracker/CurrencyTracker.lua: GetMoney / GetMoneyString /
GetCurrencyCount / OnMoneyChanged. Listens on PLAYER_MONEY and
fires MONEY_CHANGED with (newCopper, delta). - AchievementHelper/AchievementHelper.lua: GetInfo (returns named
table) / IsCompleted / GetCriteriaCount / GetCriteria around
GetAchievementInfo + GetAchievementCriteriaInfo. - Util/WaypointFormat.lua: Render / RenderFull / Parse for /way
coordinate strings. Detects mapIDs by being a numeric token
larger than 1000. - PartyState/PartyState.lua: IsSolo / IsInParty / IsInRaid /
GetGroupSize / GetGroupType / OnChanged. Fires PARTY_STATE_CHANGED
with (newType, oldType) when transitioning solo<->party<->raid.
- InstanceDetector/InstanceDetector.lua: GetType / GetDifficultyID /
- Experimental: ClassColors / FriendsList / MailboxNotice / PlayerSpec / AutoCompleteRealms
Five more extractions. 147 architecture files pass luac -p.- Util/ClassColors.lua: Get(token) / GetString(token) / Each / UsesCustom
around RAID_CLASS_COLORS / CUSTOM_CLASS_COLORS + the precomputed
Nx.ClassColorStrs table. - FriendsList/FriendsList.lua: GetFriends / GetGuild / IsKnown /
Refresh / Count around Nx.Com.Friends + GuildList + PalNames. - MailboxNotice/MailboxNotice.lua: HasPending / GetCount / OnNewMail.
Listens on UPDATE_PENDING_MAIL and pulses MinimapButton glow on
incoming mail. - PlayerSpec/PlayerSpec.lua: GetActiveSpecID / GetActiveTalentGroup /
GetSpecName / GetSpecRole / OnChanged. Spans the WotLK dual-spec
era through modern specialization. - AutoCompleteRealms/AutoCompleteRealms.lua: Get / Includes /
NormalizeName for connected-realm awareness.
- Util/ClassColors.lua: Get(token) / GetString(token) / Each / UsesCustom
- Experimental: CaptureMode / GatherEvents / MouseoverHandler / DurabilityWatcher / AddonPrefix
Five more extractions. 142 architecture files pass luac -p.- CaptureMode/CaptureMode.lua: IsEnabled / SetEnabled / IsSharing /
CaptureItems around the developer item-capture toggle. Registers
/cb capture [on|off|items]. - Gather/GatherEvents.lua: GetLastGatherTarget / GetLastGatherStartMap /
OnGather(fn) - clean pub/sub on top of GATHER_RECORDED for plugins
that want to observe gathers without parallel WoW listeners. - MouseoverHandler/MouseoverHandler.lua: GetUnitGUID / GetUnitType /
GetUnitNpcID / OnChanged. Fires MOUSEOVER_UNIT_CHANGED on guid
transitions, with legacy GUID parsing preserved. - DurabilityWatcher/DurabilityWatcher.lua: GetMinimumPercent /
GetAveragePercent / GetSlotPercent / OnThreshold(pct, fn). Listens
on UPDATE_INVENTORY_DURABILITY + PLAYER_DEAD; threshold-crossing
callbacks fire when minimum drops past a registered level. - Comm/AddonPrefix.lua: PREFIXES registry (Crb / carbmodule) +
Register / GetKnown / Send. Documents the two prefixes that
Carbonite + sibling plugins use.
- CaptureMode/CaptureMode.lua: IsEnabled / SetEnabled / IsSharing /
- Experimental: ScriptProcessor / GameVersion / DataExportImport / MapDrag / MapView
Five more extractions. 137 architecture files pass luac -p.- ScriptProcessor/ScriptProcessor.lua: thin New / SetFunc / Cancel
wrapper around Nx.Proc. - Util/GameVersion.lua: GetVersionString / GetBuildNumber /
GetBuildDate / GetTocVersion / IsRetail / IsClassic / GetExpansionLevel
around GetBuildInfo + Compat.Expansion. - DataExportImport/DataExportImport.lua: Export / Import /
ExportCurrent / GetSerializer. Uses AceSerializer-3.0 +
LibCompress (Huffman + base64) so the exported payload is
chat-safe to paste between characters. - Map/MapDrag.lua: IsDragging / GetOffset / Begin / End /
EnableDragging around the legacy drag-to-pan state on
Nx.Map.Maps[1]. - Map/MapView.lua: GetPrimary / Get(index) / Each / Count /
GetCurrentMapID / GetPlayerWorldPos around the Nx.Map.Maps
registry.
- ScriptProcessor/ScriptProcessor.lua: thin New / SetFunc / Cancel
- Experimental: WindowEscape / WindowSearch / WindowCombat / QuestRouting / TooltipPostHook
Five more extractions. 132 architecture files pass luac -p.- Window/WindowEscape.lua: Add / Remove / Each / Count around the
UISpecialFrames table that drives ESC-to-close behavior. - Window/WindowSearch.lua: Find / FindNoCase / Each / Count / Exists
around the Nx.Window.Wins registry. - Window/WindowCombat.lua: UpdateAll / SetHideInCombat / IsHiddenInCombat /
IsAnyHidden around Nx.Window:UpdateCombat. - Carbonite.Quests/QuestRouting.lua: RouteToQuest / RouteToObjective /
ClearActive / GetActiveQuestID. Uses C_SuperTrack on modern clients,
falls back to Quest:SetActiveCarboniteQuest on Classic. - TooltipPostHook/TooltipPostHook.lua: Register(typeEnum, fn, name) /
Unregister / IsAvailable / GetTypes around
TooltipDataProcessor.AddTooltipPostCall (the modern taint-safe
tooltip enrichment API). Deduplicates per type+name.
- Window/WindowEscape.lua: Add / Remove / Each / Count around the
- Experimental: QuestWatchSurface / ProfileSwitcher / ChannelSpamFilter / HotspotsRenderer / GuildBank
Five more extractions. 128 architecture files pass luac -p.- Carbonite.Quests/QuestWatchSurface.lua: IsTracked / Track / Untrack /
ToggleTracker / IsTrackerShown / ClearAutoTarget / Refresh.
Uses C_SuperTrack on modern clients, falls back to
Quest:SetActiveCarboniteQuest on Classic. - ProfileSwitcher/ProfileSwitcher.lua: GetCurrent / GetProfiles /
SetProfile / CopyProfile / ResetProfile / DeleteProfile. Fires
PROFILE_* EventBus signals. Registers /cb profile slash that
routes list/set/copy/reset/delete. - Comm/ChannelSpamFilter.lua: Enable / IsEnabled / AddPrefix. Wires
ChatFrame_AddMessageEventFilter on the four CHAT_MSG_CHANNEL_*
events to suppress 'Crb*' addon-channel chatter from the chat
frame. - Map/HotspotsRenderer.lua: Refresh / SetVisible / IsVisible around
Nx.Map:MoveWorldHotspots. - Integrations/GuildBank.lua: LibGuildBankComm-1.0 accessor +
RegisterCallback / UnregisterCallback / IsAvailable.
- Carbonite.Quests/QuestWatchSurface.lua: IsTracked / Track / Untrack /
- Experimental: HUDArrow / GuidePane / CombatLockdown / CommChat / DragonRiding
Five more extractions. 124 architecture files pass luac -p.- HUD/HUDArrow.lua: GetDirectionDegrees / GetDistanceYards /
GetTargetName / GetETA / SetTexture(style) / Show / Hide /
IsShown. Style key matches legacy TexNames vocabulary
('', Chip, Gloss, Glow, Neon). - Map/GuidePane.lua: Toggle / Show / Hide / IsShown / Refresh.
- CombatLockdown/CombatLockdown.lua: IsActive / RunWhenSafe(fn, ...) /
OnEnter(fn) / OnExit(fn). Defers operations until PLAYER_REGEN_ENABLED,
fires COMBAT_ENTERED / COMBAT_LEFT on the EventBus. - Comm/CommChat.lua: OnChannelMessage / OnChannelJoin / OnChannelLeave /
IsCarbChannel / GetCarbChannelType. Bridges from NxCom's
OnChat_msg_channel via post-hook. - Map/DragonRiding.lua: IsUnlocked / GetIntroQuestID (68795) /
GetSpeedMultiplier + /cb skyriding diagnostic slash.
- HUD/HUDArrow.lua: GetDirectionDegrees / GetDistanceYards /
- Experimental: NetSend / TaintlessHelpers / SoundPlayer / IconTooltip / DockOpts
Five more extractions. 119 architecture files pass luac -p.- Comm/NetSend.lua: IsEnabled / Enable / GetInterval / SetInterval /
BroadcastNow / GetLastSendTime around the legacy Nx.NetSendPos +
Nx.NetPlyrSendTime position-broadcast loop. - TaintlessHelpers/TaintlessHelpers.lua: IsAvailable / GetSandbox /
ProxyCall - documents the Taintless.xml sandbox pattern and adds
a deferred-call helper for taint-sensitive secure operations. - SoundPlayer/SoundPlayer.lua: Play / PlayFile / PlayQuestComplete /
PlayLogin / IsEnabled / SetEnabled. Respects the legacy
General.TitleSoundOn toggle. - Map/IconTooltip.lua: SetText / GetText / Show(icon, owner) / Hide.
Splits tip strings on the legacy '~' separator into Carbonite.UI.Tooltip
lines so we never reach for GameTooltip. - Map/DockOpts.lua: Update / IsDocked / GetScale / SetScale /
GetDockAlpha / SetDockAlpha / GetMinScale around Nx.Map.Dock.
- Comm/NetSend.lua: IsEnabled / Enable / GetInterval / SetInterval /
- Experimental: NameplateGlow / CommSend / TooltipScanner / CharacterRoster / InitFlow / DebugFlags
Six more extractions. 114 architecture files pass luac -p.- Map/NameplateGlow: Enable / IsEnabled / Pulse around the minimap-button texture swap.
- Comm/CommSend: Channel / Zone / Guild / Friend / Party / Raid / Whisper named verbs + Typed(distribution, kind, fields) that encodes via WireFormat.
- TooltipScanner: Scan(forceMouseover) / GetLastText / GetLastNumLines / HasTooltipDataProcessor.
- CharacterRoster: GetAll / Each / GetCurrent / GetByName / RecalculateRealmChars / Delete / GetRealmChars / Count + /cb roster slash.
- InitFlow: GetStage (loaded / playerFound / initialized / ready) / IsReady / OnReady(fn) / RequestStartup. Watches Nx.Initialized via MainUpdater and fires CARBONITE_READY exactly once.
- DebugFlags: Get / Set / Toggle / Each / GetKnown around the Nx.db.profile.Debug.* table + /cb flags slash to list / toggle.
- Experimental: WindowFade / WindowAttach / MenuActions / WorldMapIntegration / LootHandler / HotkeyBindings
Six more class extractions. 108 architecture files pass luac -p.- Window/WindowFade.lua: SetCreateFade / SetFadeIn / SetFadeOut / GetFade / SetBordersFade / ResetBackdrops with window-by-name resolution.
- Window/WindowAttach.lua: Attach / Detach / Adjust / GetChildren for the fractional-anchor child-frame system.
- UI/MenuActions.lua: Create / AddItem / AddSubMenu / Open / Close / IsAnyOpen / CloseAll. Fires MENU_OPENED / MENU_CLOSED EventBus signals.
- Map/WorldMapIntegration.lua: Attach / Detach / IsAttached / Refresh / SetIconsScale around Nx.Map's WorldMapFrame moves.
- LootHandler/LootHandler.lua: IsEnabled / Enable / Toggle / LootNow around the legacy auto-click handler. Registers /cb loot.
- HotkeyBindings/HotkeyBindings.lua: documented registry of the 10 built-in BINDING_NAME_* actions + Register/Each/Count + /cb bindings slash.
- Experimental: extract WindowMenu / MenuItem / LDB / GuideLocations / EditModeHooks
Five more class extractions on the experimental branch.
Carbonite/Modules/Window/WindowMenu.lua (new)
Right-click context-menu actions every Carbonite window exposes:
SetHideInCombat / SetLocked / SetFadeIn / SetFadeOut / SetLayer /
SetScale (preserves anchor across scale change) / SetTransparency.
Window argument resolves a name string via legacy Find.
Carbonite/UI/MenuItem.lua (new)
Documented surface around the legacy Nx.MenuI item table.
SetText / GetText / IsChecked / SetCheckedValue / BindCheck /
GetSliderValue / SetSliderValue / BindSlider / SetVisible /
IsVisible. From(legacyItem) adopts an existing item without
breaking Nx.Menu's iteration.
Carbonite/Modules/Integrations/LDB.lua (new)
LibDataBroker-1.1 surface. GetBroker / SetText / SetIcon / GetMenu /
AddMenuItem(text, fn, opts) / ClearPluginItems / ShowMenu. Marks
plugin-added entries with __plugin so ClearPluginItems doesn't
touch the Carbonite default Options / Toggle Map / Toggle Events
trio. Supports both the new MenuUtil.CreateContextMenu API (retail
11.0+) and the legacy EasyMenu path (Classic flavors).
Carbonite/Modules/Map/GuideLocations.lua (new)
NPC location lookup against Nx.NPCData. FindTaxi / FindNPC(predicate)
/ Each / GetCount. Preserves the legacy faction filter (Horde
hides Alliance-only NPCs, Alliance hides Horde-only). Delegates
string parsing to the new LegacyStrings.Split helper.
Carbonite/Modules/EditModeHooks/EditModeHooks.lua (new)
Centralized Blizzard EditMode integration. IsActive / OnEnter(fn) /
OnExit(fn) / OnChange(fn) + EDITMODE_ENTERED / EDITMODE_EXITED
EventBus signals. Wires the EventRegistry callbacks once on enable
so multiple consumers (quest tracker hider, custom window
positioning) don't each install duplicates.
Verification
- luac -p: 102 architecture files clean
- Smoke test: all 102 load
- Module registry stable at 7 - Experimental: extract WindowLayout / WindowConsole / WireFormat / ZoneTransition / PlayerCharacter
Five more class extractions on the experimental branch.
Carbonite/Modules/Window/WindowLayout.lua (new)
Public surface around the layout/anchor/title verbs of Nx.Window.
Find / SetTitle / SetTitleJustify / SetTitleColors / SetTitleLineH /
SetTitleXOff / GetSize / SetSize / SetBGAlpha / Lock / IsLocked /
InitLayoutData / GetLayoutMode / Show / IsShown / GetTitleTextWidth /
SetSizeable + ResetAll for /carb resetwin parity. Window argument
resolution: pass either the live window object OR a name string;
Find handles the lookup. Registers/cb resetwindows.
Carbonite/Modules/Window/WindowConsole.lua (new)
The /carb winpos / winshow / winsize console handlers. Pos /
Show / Size / Parse delegate to the legacy implementations with
a portable Parse fallback. Registers/cb winpos,/cb winshow,
/cb winsizefor parity.
Carbonite/Modules/Comm/WireFormat.lua (new)
Documented encoder/decoder for Carbonite's addon-channel + chat-
channel protocol. Encode / Decode / EscapeChar / EscapeString /
UnescapeString / GetKnownKinds + tilde-separated variants for the
legacy "Map<sub>px</sub>py~mapID" position broadcast. Captures the chat-
string escaping rules (bytes 35, 92, 124, 128+) that NxCom had
scattered through its protocol code.
Carbonite/Modules/ZoneTransition/ZoneTransition.lua (new)
Publish/subscribe surface around zone changes. OnEnterZone /
OnLevelUp / OnGroupChanged / GetCurrentMapID / GetPreviousMapID /
Refresh. Owns its own ZONE_CHANGED_NEW_AREA + PLAYER_LEVEL_UP
listener so new code can subscribe without registering duplicate
WoW-event listeners.
Carbonite/Modules/PlayerCharacter/PlayerCharacter.lua (new)
Identity + classification queries. GetName / GetClass / GetRace /
GetFactionGroup / GetFactionNumber (preserves the legacy
PlFactionNum convention: Horde=0, Alliance=1, neutral=2) /
GetLevel / GetGUID / GetClassColorStr / IsElite / IsPlayerInPVP.
Registers/cb whoamifor a quick identity snapshot.
Verification
- luac -p: 97 architecture files clean
- Smoke test: all 97 load
- Module registry stable at 7 - Experimental: extract ChannelManager / FlightPathFinder / ZoneConnections / Time / MainUpdater
Five more class extractions on the experimental branch.
Carbonite/Modules/Comm/ChannelManager.lua (new)
Public surface around Nx.Com channel join / leave / count.
Join / Leave / Refresh / GetChannelCount / IsInChannel /
GetAddonChannelName / GetMonitoredZones / Each. Registers
/cb channelsfor a channel-membership snapshot.
Carbonite/Modules/Map/FlightPathFinder.lua (new)
Wraps Nx.Travel:FindFlight. Find(src, dst) returns the legacy
(totalDist, path) tuple. CanFly(src, dst) does the cheap
"is there any path?" probe using FindClosest. GetSpeedMultiplier
exposes the active flight-speed factor.
Carbonite/Modules/Map/ZoneConnections.lua (new)
Wraps Nx.Travel:FindConnection / FindCrossContinent. Find /
FindCrossContinent / GetConnectionsFrom / HasDirect / CountDirect.
Resets Travel.VisitedMapIds on every Find to prevent stale
traversal state short-circuiting subsequent searches.
Carbonite/Util/Time.lua (new)
Monotonic event-ordering timestamp + AceTimer left-time helper.
Now / Real / Frame / LeftOnTimer / Format. Preserves the legacy
Nx:Timeseconds * 100 + fracsemantic so saved-variable
ordering stays valid across an upgrade.
Carbonite/Modules/MainUpdater/MainUpdater.lua (new)
Per-frame tick registry. Subscribe(fn, name [, interval]) /
Unsubscribe / RegisterPostHook / GetTick / OnTick. Owns a
stand-alone driver frame for early boot, then bridges into the
legacy Nx:NXOnUpdate via post-hook so we never double-fire ticks.
Verification
- luac -p: 92 architecture files clean
- Smoke test: all 92 load
- Module registry stable at 7 - Experimental: extract DataPersistence / TaxiCapture / CommReceive / WatchHider / MountHelper
Five more class extractions on the experimental branch.
Carbonite/Core/DataPersistence.lua (new)
Saved-variable scope helpers. Get(name [, char]) /
GetToolBarLayout / GetHUDOptions / GetCapture / GetCharacters /
FindCharacter / CopyCharacter / DeleteCharacter. Mirrors the
legacy Nx:GetData scope dispatch (Events / List / Quests / Win /
Herb / Mine / Timber) but reaches into Nx.db.* through documented
helpers instead of an inline if/elseif tree. Registers/cb data
for diagnostic scope dumps.
Carbonite/Modules/Map/TaxiCapture.lua (new)
Flight-master node capture. Capture / IsKnown / GetCurrentNode /
CalcTime / Each / GetKnownNodes / CountKnown. Storage stays on
Nx.db.char.Travel.Taxi.Taxi so existing readers keep working;
this class is the public accessor. Registers/cb taxi.
Carbonite/Modules/Comm/CommReceive.lua (new)
Inbound-message dispatcher. OnReceive(kind, fn, name) /
OffReceive / Dispatch / GetHandlers. InstallsNx.Com._dispatchExt
shim so the legacy NxCom receive path can co-opt extension
handlers without modifying its own dispatcher. Documents the
known wire-protocol kinds (I / P / T / Q / K / L).
Carbonite.Quests/WatchHider.lua (new sibling-addon file)
Class around Nx.Quest:TrackerHider_*. Apply / IsHidden /
ShouldHide / SetEnabled / IsBlizzardLockedDown. Registers
/cb hidewatch on|off|toggleso the user can flip Blizzard
tracker visibility without opening options. Wired into the
Carbonite.Quests TOC.
Carbonite/Modules/Map/MountHelper.lua (new)
Flying-mount + riding-skill detection. GetRidingSkill /
IsFlyableArea / HasFlying / GetSpeed / IsSkyriding / Refresh.
Documents the new Dragonflight skyriding gate (quest 68795 flag).
Registers/cb mountto dump full mount state for path-cost
debugging.
Verification
- luac -p: 87 architecture files + WatchHider clean
- Smoke test: all 87 load
- Module registry stable at 7 - Experimental: extract MapTooltip / ScrollScale / Mouse utils / CommQueue / PositionShare
Five more class extractions on the experimental branch.
Carbonite/Modules/Map/MapTooltip.lua (new)
The little "you are here" tooltip pinned to the map window's
corner. Init / Set / Hide / IsVisible / GetFrame / SetAnchor.
Mirrors back to Nx.Map.LocTipFrm and Nx.Map.LocTipFStrs so any
legacy reader keeps working. Legacy Nx.Map:CreateLocationTip /
SetLocationTip rewired.
Carbonite/Modules/Map/ScrollScale.lua (new)
Pure-function scale-step calculator. Compute(current, ticks) is
side-effect-free for tests; Apply(value) mutates the live primary
map. Documents the legacy magic constants:
MIN_SCALE = 0.015 (world view floor)
STEP = 0.3 (scroll-tick effect)
ZOOM_OUT_DAMPING = 0.76923 (10/13 - zoom-out feels slower)
Legacy Nx.Map:ScrollScale rewired.
Carbonite/Util/Mouse.lua (new)
Frame mouse helpers. IsOver / GetClampedXY / SnapToScreen. Proxies
to Nx.Util_IsMouseOver / Util_GetMouseClampedXY / Util_SnapToScreen
with portable fallbacks for the first two.
Carbonite/Modules/Comm/CommQueue.lua (new)
Public surface around Nx.Com's four parallel send queues
(Chan / Guild / Friend / Zone) + PalsSendQ + SendChanQ. Enqueue /
EnqueuePal / EnqueueChannel / GetDepth / Clear / GetSendRate /
SetSendRate / Snapshot. Registers/cb comqto dump queue depths.
Carbonite/Modules/Comm/PositionShare.lua (new)
Public accessor for the player-position broadcast protocol.
GetPals / GetPal / GetPalNames / IsPal / IsEnabled / SetEnabled /
GetLastSendTime / GetSendIndex / ForceBroadcast. ForceBroadcast
sets PosSendNext = -2 to drive the legacy next-tick send. Registers
/cb palsfor a position-tracking dump.
Verification
- luac -p: 83 architecture files clean
- Smoke test: all 83 load
- Module registry stable at 7 - Experimental: extract SlashHandler / EventDispatcher / MapToolBar / ChatHooks / WhatsnewWindow
Five more class extractions on the experimental branch.
Carbonite/Modules/SlashHandler/SlashHandler.lua (new)
Wrapper around Nx.slashCommand. Dispatch(line) routes plugin-
registered commands first, falls through to the legacy parser
for the documented vocabulary (goto / options / resetwin / rl /
track / winpos / winshow / winsize / events / d / herb / mine /
addopen / com / etc.). Register(cmd, fn, desc) extends the
vocabulary at runtime without touching the global slash table.
Each(fn) + GetCommands() expose the merged list for help / UI.
Carbonite/Modules/EventDispatcher/EventDispatcher.lua (new)
Extension layer on top of the legacy Nx:NXOnEvent router.
Register(event, fn, name) / Unregister / Dispatch / GetHandlers
/ IsBound. Owns its own listener frame so multiple modules can
subscribe to the same event without stomping the legacy router.
Carbonite/Modules/Map/MapToolBar.lua (new)
Map-window toolbar registry. RegisterButton({ id, label, handler,
pressed, order }) / UnregisterButton / Each / Refresh / SetVisible
/ IsVisible. Mirrors the registry into Nx.BarData so the legacy
Nx.Map:CreateToolBar picks up plugin entries automatically.
Seeds the legacy MapZIn / MapZOut / MapGuide / MapCombat / MapEvents
defaults on CARBONITE_LOADED.
Carbonite/Modules/ChatHooks/ChatHooks.lua (new)
Chat-output + popup helpers. Print / Debug / Warn / Error /
DumpVar / DumpStack / Confirm / Prompt. Confirm reuses the legacy
StaticPopupDialogs["NxMsg"] slot so Nx:ShowMessage callers share
the popup queue. SuppressBlizzTimePlayed / RestoreBlizzTimePlayed
centralize the ChatFrame_DisplayTimePlayed override the legacy
OnPlayer_login handler used to inline.
Carbonite/Modules/Whatsnew/WhatsnewWindow.lua (new)
Public face for the changelog window. Toggle / Show / Hide /
IsShown / Refresh / SelectCategory / GetSelectedCategory /
MarkRead / AddCategory. MarkRead also clears the
Map.MinimapButton glow so the "unread" indicator goes away the
moment the user actually looks at the changelog. Registers
/cb changelogslash command.
Verification
- luac -p: 78 architecture files clean
- Smoke test: all 78 load
- Module registry stable at 7 (these are plain classes, not
AceAddon submodules) - Experimental: extract TomTomWaypoints / LegacyStrings / Gather / TravelGraph / List
Five more class extractions on the experimental branch.
Carbonite/Modules/Integrations/TomTomWaypoints.lua (new)
Owns the Nx:TT* waypoint-verb implementations that used to live
in NxMap.lua. AddWaypoint / AddZWaypoint / SetCustomWaypoint /
SetCustomMFWaypoint / SetTarget / RemoveWaypoint / SetCrazyArrow
/ SetClosestWaypoint / DefaultCallbacks / HandleWayCommand. Uses
Carbonite.Modules.Map.Targets and .Coords so the integration
stops reaching into Nx.Map.Maps[1] directly. Distance-callback
selection preserved exactly (shortest radius wins). Legacy
Nx.TT* methods rewired to delegate.
Carbonite/Util/LegacyStrings.lua (new)
Compatibility wrappers around Nx.Split / Nx.Util_str2rgba /
Util_str2rgb / Util_str2a / Util_str2colstr / Util_coltrgb2colstr
/ Util_dec2hex / Util_CapStr / Util_CleanName / Util_GetMoneyStr
/ Util_GetTimeElapsedStr. Module proxies to legacy globals when
present, falls back to a portable implementation otherwise.
Carbonite/Modules/Gather/Gather.lua (new)
Public class around the gathering-node database. Get(typ, id) /
NameToID / HerbNameToID / MineNameToID / IsGathering (with cached
spell-lookup) / RecordHerb / RecordMine / GetMaxSkill /
ShouldShowNode. Mirrors the legacy Ooze-Covered / Thorium-Vein
mining-name normalization verbatim.
Carbonite/Modules/Map/TravelGraph.lua (new)
Higher-level queries against the travel graph. GetRidingSkill /
GetFlightSpeed / HasFlyingMount / RefreshFlying / FindClosestFM.
Registers/cb flyto dump flying-mount status on the current
continent (debug aid for path-cost issues).
Carbonite/UI/List.lua (new)
Modern scrolling list widget built on FauxScrollFrameTemplate so
it works on every Classic flavor. Spec-table API: { parent,
width, height, anchor, rowHeight, drawRow, onSelect, data }.
Methods: SetData / GetData / Count / Refresh / Select /
GetSelected / GetSelectedIndex / SetRowDrawer / ScrollToTop.
Selection highlight on its own backdrop texture; row pool grows
on demand to fit the visible-rows budget.
Verification
- luac -p: 73 architecture files clean
- Smoke test: all 73 load
- Module registry stable at 7 (no new module subclasses; these
are plain classes / wrappers) - Experimental: extract AuctionAssist / UserEvents / ItemRegistry / GroupMembers / MapOpen
Five more class extractions on the experimental branch.
Carbonite/Modules/AuctionAssist/AuctionAssist.lua (new module)
Auction house browse-assistant. Module-class wrapper around the
legacy Nx.AuctionAssist with a clean Enable / IsEnabled / GetLowest
surface. Mirrors Nx.AuctionShowBOPer so the legacy
AuctionFrameBrowse_Update hook keeps reading the right state.
Registers/cb ah on|off|toggleslash command.
Carbonite/Modules/UserEvents/UserEvents.lua (new)
Player-event log (deaths, kills, honor, herb/mine/timber/opens).
AddInfo / AddDeath / AddKill / AddHonor / AddHerb / AddMine /
AddTimber / AddOpen all delegate to the legacy Nx.UEvents
implementations and fire USER_EVENT_ADDED on the EventBus so the
minimap glow / chat alerter / quest tracker can react.
Registers/cb eventsslash command.
Carbonite/Modules/ItemRegistry/ItemRegistry.lua (new)
Asynchronous item-info cache. Get / Request(id, fn) / Cached /
Each / Forget / ShowTooltip. Tooltip rendering routed through
Carbonite.UI.Tooltip (NxTooltipText) - never GameTooltip. Owns a
GET_ITEM_INFO_RECEIVED listener that flushes pending callbacks
once data arrives from the server.
Carbonite/Modules/GroupMembers/GroupMembers.lua (new)
Cached name -> unit-id table for the current party / raid.
Refresh / Lookup / Has / Each / GetType / Count. Listens on
GROUP_ROSTER_UPDATE + PARTY_MEMBERS_CHANGED + PLAYER_ENTERING_WORLD.
Mirrors onto Nx.GroupMembers so the legacy Nx.Punks reader at
NxPunks.lua:643 keeps working.
Carbonite/Modules/Map/MapOpen.lua (new)
Public face for the map window lifecycle. Open / Close / Toggle /
Maximize / Restore / IsShown / IsMaximized / ToggleSize. Adds an
early-call guard the legacy ToggleSize lacked (it errored when
self.Maps was nil during early /carb invocations). Fires
MAP_OPENED / MAP_CLOSED / MAP_MAXIMIZED / MAP_RESTORED on the
EventBus. Registers/cb max+/cb restoreslash commands.
Verification
- luac -p: 68 architecture files clean
- Smoke test: all 68 load
- Module registry now contains 7 modules: Options / Comm / Map /
HUD / Travel / CombatStats / AuctionAssist - Experimental: extract MinimapButton / Versions / TitleScreen / POICache; fix quest "?" autocomplete click
Five more class extractions and a user-reported bug fix.
Carbonite/Modules/Map/MinimapButton.lua (new)
Class wrapper around the Carbonite minimap icon. SetGlow,
ToggleMap, ShowMenu, AddMenuItem, ClearExtraMenu, ShowTooltip.
Plugin menu items registered through AddMenuItem so they survive
a Refresh. Tooltip rendered through Carbonite.UI.Tooltip (never
GameTooltip).
Carbonite/Core/Versions.lua (new)
Canonical registry of every Nx.VERSION* schema-version constant.
Required(scope), NeedsMigration(scope, stored), GetAddonVersion,
GetBuild, Each. Registers/cb versionsslash to dump every
schema cliff at a glance.
Carbonite/Modules/TitleScreen/TitleScreen.lua (new)
Clean front door for the Carbonite splash screen. IsEnabled,
Show, Hide, IsShown. Registers/cb splashslash for manual
testing.
Carbonite/Modules/Map/POICache.lua (new)
Owns the POI cache + table pools the map's per-frame OnUpdate
used to keep as file-local in NxMap.lua. Get(mapID),
Invalidate, GetPool, SetRefreshInterval, GetStats. Pulls fresh
data from C_AreaPoiInfo on cache miss; default 0.5s refresh
interval matches legacy.
Carbonite.Quests/NxQuest.lua (bug fix)
*** User-reported bug ***
Clicking the "?" autocomplete button on a watched quest sometimes
toggled tracking instead of completing the quest. Root cause: the
click handler required both cur.CompleteMerge AND cur.IsAutoComplete
to be true to call ShowQuestComplete. The legacy code had a stale-
state fallback for IsAutoComplete but not for CompleteMerge -- when
the quest just completed and RecordQuestsLog had not yet run,
CompleteMerge was false and the click fell through to the tracking
toggle.
Fix: at click time, when C_QuestLog is available, refresh both
flags from C_QuestLog.IsComplete + GetQuestLogIsAutoComplete. The
"?" button now reliably calls ShowQuestComplete when the API
agrees the quest is auto-completable.
Verification
- luac -p: 63 architecture files + legacy NxQuest clean
- Smoke test: all 63 load - Experimental: extract ContinentLookup / FrameToWorld / Whatsnew / CombatStats / ToolBar
Five more class extractions on the experimental branch.
Carbonite/Modules/Map/ContinentLookup.lua (new)
Cached zone -> continent map traversal. Walks C_Map parent
hierarchy until it hits Enum.UIMapType.Continent, caching each
step. Disambiguates "not found" from "not yet looked up" with a
falsesentinel so a cold zone resolves once. Legacy
Nx.Map:GetContinentMapID rewired.
Carbonite/Modules/Map/FrameToWorld.lua (new)
Frame-pixel <-> map-coordinate conversion. FrameToZone /
FrameToWorld / WorldToFrame / GetCursorWorld. Preserves the legacy
10.02 continent-units-per-zone-percent magic constant verbatim so
data-table arithmetic stays pixel-accurate. Legacy
Nx.Map:FramePosToZonePos / FramePosToWorldPos rewired.
Carbonite/Modules/Whatsnew/Whatsnew.lua (new)
Version-history pane. HasUnread / MarkAllRead / Each / AddCategory
/ AddEntry / Show. Storage still proxies onto Nx.Whatsnew so the
legacy ToggleShow window keeps reading the same data. Registers
the/cb whatsnewslash command.
Carbonite/Modules/CombatStats/CombatStats.lua (new module)
Battleground combat tracking on the AceAddon module pattern.
Refresh / Reset / GetStats / GetKBRank. Resets on BG_STATE_CHANGED
so cumulative counters do not carry over between battlegrounds.
Mirrors writes back to Nx.Combat so legacy readers keep working.
Fires COMBAT_STATS_UPDATED / COMBAT_STATS_RESET on the EventBus.
Carbonite/UI/ToolBar.lua (new)
Horizontal icon-button strip. Spec-table API with Add / Clear /
Count / Get / SetItemEnabled / Each. Auto-reflows on Add. Tooltip
attachments routed through Carbonite.UI.Tooltip (not GameTooltip).
Verification
- luac -p: 59 files clean
- Smoke test: all 59 load; module registry now contains
Options/Comm/Map/HUD/Travel/CombatStats (6 modules)
- Functional: ContinentLookup zone-to-continent + continent-
self-map + world-type-nil, FrameToZone center-of-frame
returns ~50,50, Whatsnew HasUnread/MarkAllRead cycle, CombatStats
snapshot. - Experimental: ZoomController + ZoneIterator + Battleground + GroupTracking + DropDown; fix teleport-to-max-zoom bug
Five more class extractions on the experimental branch and a real
bug fix in the zoom path.
Carbonite/Modules/Map/ZoomController.lua (new)
Owns scale + position animation. Public API: GetScale, SetScale,
ScrollSteps, Move, GotoPlayer, GotoCurrentZone, CenterMap,
CenterMap1To1. Snap thresholds (0.25 world units; 0.01 inverse-
scale diff) preserved from legacy. Legacy Nx.Map:SetScaleOverTime
/ Move / GotoPlayer / GotoCurrentZone / CenterMap / CenterMap1To1
rewired.
*** Bug fix ***
The legacy GotoCurrentZone hard-coded Scale = 20 on the
instance-entry branch. That value is "very zoomed in" for most
modern dungeon / raid / scenario / garrison maps, so every
teleport into an instance wiped whatever the user had selected
(the user-reported "map zooms to max after teleport" bug).
The new controller:
- Captures user-driven scale changes via SetScale + ScrollSteps
(mouse wheel, slider, minimap zoom buttons) into a private
rememberedScale variable.
- On instance teleport, restores rememberedScale if any.
- Falls back to INSTANCE_DEFAULT_SCALE = 5 (was 20) for fresh
installs / first-ever teleport.
Non-instance teleports still go through CenterMap to fit-to-window
for the new zone, which is the expected behavior.
Carbonite/Modules/Map/ZoneIterator.lua (new)
Continent + zone lookup. GetContinent, GetZoneInfo, GetZone (with
parent-map cache preserved), EachContinent, EachZoneInContinent,
GetContinentCount. Legacy Nx.Map:GetWorldContinentInfo /
GetWorldZoneInfo / GetWorldZone rewired.
Carbonite/Modules/Map/Battleground.lua (new)
Instance / BG / micro-dungeon / scenario detection. IsBGMap,
IsMicroDungeon, IsScenario, GetShortName, GetOverrideMapID,
IsInBG. Owns a PLAYER_ENTERING_WORLD + ZONE_CHANGED_NEW_AREA
listener that keeps Nx.InBG in sync and fires BG_STATE_CHANGED
on the EventBus. Legacy Nx.Map:IsBattleGroundMap / IsMicroDungeon
/ IsScenario / GetShortName / GCMI_OVERRIDE rewired.
Carbonite/Modules/Map/GroupTracking.lua (new)
Party + raid member position polling and proximity queries.
GetUnitPosition (with the different-map => 0,0 guard preserved
from legacy), EachMember, GetMemberAtCursor, GetGroupSize.
Legacy Nx.Map:GetGroupMemberAtCursor / GetPlayerMapPosition
rewired.
Carbonite/UI/DropDown.lua (new)
Modern DropDown widget using UIDropDownMenuTemplate. Spec-table
API: { parent, label, width, anchor, items, value, onSelect }.
GetValue / SetValue / SetItems methods on the returned frame.
Verification
- luac -p: 54 files clean
- Smoke test: all 54 load
- Functional tests pass for: SetScale remembering user intent,
teleport preserving remembered scale (3 instead of legacy 20),
default instance scale 5 instead of 20, ScrollSteps remembering
scale, ZoneIterator continent lookup, Battleground IsBGMap +
GetShortName, GroupTracking GetGroupSize. - Experimental: extract MiniMap / ViewState / TomTom + EditBox / Slider / TabBar
Six more class extractions on the experimental branch:
Carbonite/Modules/Map/MiniMap.lua (new)
Public API around minimap integration. Zoom / ZoomIn / ZoomOut,
Ping, mouse routing (OnMouseDown/Up/Enter/Leave), IsOwned,
GetFrame/GetScale/GetZoom, SetButtonVisible+ApplyButtonVisibility
for the Carbonite / calendar / clock / world-map / LFG /
nameplate chrome toggles. Fires MAP_MINIMAP_OWNED /
MAP_MINIMAP_ZOOMED / MAP_MINIMAP_PINGED on the EventBus. Legacy
Nx.Map.Minimap_* statics + Nx.Map:Minimap* methods all rewired.
Carbonite/Modules/Map/ViewState.lua (new)
Named map-view persistence. Save / Restore / Has / Clear / Names.
Preserves the legacy BG-namespace mechanic (saves done in a BG
are scoped under that BG prefix so leaving restores normal-world
view). Legacy Nx.Map:SaveView / RestoreView rewired.
Carbonite/Modules/Integrations/TomTom.lua (new + improved)
Substantially expanded TomTom emulation. New surface modeled on
the real TomTom addon includes: GetCurrentPlayerPosition,
GetCurrentCoords, GetKey/GetKeyArgs, ClearAllWaypoints,
AddWaypointToCurrentZone, WaypointExists, IsValidWaypoint,
UIDIsSaved, GetClosestWaypoint, SendWaypoint, plus a profile
stub so callers checking config flags don't crash. Slash list
now matches real TomTom: /way + /tway + /tomtomway + /cbway
(set), /cway + /closestway (closest), /wayb + /wayback (reverse).
Wraps legacy Nx.TT* functions so behavior is unchanged for
AddWaypoint / RemoveWaypoint / SetCrazyArrow etc. Auto-installs
on CARBONITE_ENABLE and aliases Nx.EmulateTomTom for legacy
call sites.
Carbonite/UI/EditBox.lua (new)
Modern EditBox with the legacy placeholder behavior preserved:
placeholder shown until focused, restored if box empties and
loses focus. Clean onChanged/onSubmit callbacks instead of the
Nx.EditBox SetUser pattern. Uses Blizzard's InputBoxTemplate.
Carbonite/UI/Slider.lua (new)
Modern Slider on OptionsSliderTemplate. Label, low/high text,
value-format suffix, snap-on-drag, onChange callback.
Carbonite/UI/TabBar.lua (new)
Tab strip on PanelTabButtonTemplate. Select / GetSelected /
SetEnabled / Each. Auto-resizes tab text width.
Verification
- luac -p: 49 files clean
- Smoke test: all 49 load
- Functional: MiniMap zoom delta + IsOwned, ViewState
save/restore/names, TomTom emulation surface + GetKeyArgs
format ("100:500000000.0:500000000.0:Test"), EditBox:New - Experimental: extract MapIDs / Hotspots / Targets / IconTypes / Button
Continues the NxMap split with four more well-defined seams:
Carbonite/Modules/Map/MapIDs.lua (new)
Map-ID identification and classification. Single owner of
GetCurrentMapId / GetCurrentMapAreaID / IdToContZone / IsNormalMap
/ IsInstanceMap / IdToName / NameToId / GetZoneDescription.
Cached ContZone lookups (was a file-local in NxMap). Legacy
Nx.Map:* methods rewired on CARBONITE_LOADED.
Carbonite/Modules/Map/Hotspots.lua (new)
Spatial lookup of quest-hub / city / start-zone regions. Owns the
Build pipeline that used to live in Nx.Map:InitHotspots, plus
General / Cities / Each query methods. Mirrors the legacy
WorldHotspots / WorldHotspotsCity arrays onto Nx.Map so existing
renderers keep finding them.
Carbonite/Modules/Map/Targets.lua (new)
Waypoint / target queue. Verbs (Add / Remove / Clear / Reorder /
Reverse / Each / GetFirst / Count) extracted from the scattered
Nx.Map:SetTarget / ClearTarget / ClearTargets / FindTarget /
ChangeTargetOrder / ReverseTargets / SetTargetName / GetTargetInfo
/ GetTargetPos. Fires MAP_TARGET_* events on the EventBus so the
HUD module can react without polling. Legacy methods rewired.
Carbonite/Modules/Map/IconTypes.lua (new)
Icon-type registry: Define / Clear / SetAlpha / SetMinScale /
SetLevel / SetChop / SetNoDockMinimap / AddPoint / AddRect / Count
/ GetPoint / SetIconTip / SetIconUserData. Storage layout
(Nx.Map.Data[<typeName>]) preserved so the existing renderer still
reads from it unchanged. Legacy Nx.Map:InitIconType / SetIconType*
/ AddIconPt / AddIconRect / GetIconCnt / GetIconPt rewired.
Carbonite/UI/Button.lua (new)
Modern Button factory: Carbonite.UI.Button:New { parent, text,
tooltip, anchor, onClick, ... }. Returns a Blizzard-templated
button with Tooltipable mixin attached and a Carbonite-routed
tooltip (never GameTooltip). Legacy Nx.Button:Create remains the
heavy implementation; Button:NewLegacy is the adapter.
Verification
- luac -p syntax check: 43 new architecture files pass
- Smoke test: 43 files load in stubbed env
- Functional tests: MapIDs (current map, IdToContZone, IsNormalMap),
Targets (Add / Reverse / Remove / Clear), IconTypes (Define /
AddPoint / Count), Hotspots (Build city spots from synthetic data) - Experimental: extract Pathing / Coords / MinimapGlow / Font / Skin classes
Continues the NxMap / NxUI / NxOptions split:
Carbonite/Modules/Map/Pathing.lua (new)
Owns route planning previously in NxMap:Route / RouteMerge /
RouteLen / RouteOptimize / RouteSwap. Exposes a single public API:
PlanRoute, OptimizeRoute, MergePoints, RouteLength, ReverseSegment,
BuildPath. The legacy Nx.Map:Route* methods are rewired on
CARBONITE_LOADED to delegate into Pathing. Map module exposes
Map:PlanRoute / Map:BuildPath as the public entry points.
Also rewires the only direct cross-class call site
(NxMap:CalcTracking line 7253) from Travel:MakePath to
Pathing:BuildPath.
Carbonite/Modules/Map/Coords.lua (new)
Coordinate-space conversion. Replaces NxMap:GetWorldZoneScale /
GetWorldPos / GetZonePos / GetWorldRect with a class that reads
Nx.Map.MapWorldInfo as before but presents one stable surface:
Coords:WorldFromZone, ZoneFromWorld, WorldRectFromZone,
GetScale, YardsBetween.
Legacy table methods are rewired on load so existing callers in
NxMap.lua continue to work.
Carbonite/Modules/Map/MinimapGlow.lua (new)
Owns the pulsing-blip animation that used to live in
MinimapNodeGlowInit / OnMinimapNodeGlowTimer / MinimapNodeGlowSet.
Single lifecycle (Start/Stop/Reset).
Carbonite/UI/Font.lua (new)
Class-based replacement for Nx.Font. Owns the named font registry,
LibSharedMedia integration, and per-slot face/size resolution.
Carbonite/UI/Skin.lua (rewrite)
Class-based theme registry (was a placeholder). Ships all 11
legacy themes plus Modern Dark / Light, Glass, Class Color, Faction
with their resolveBorder callbacks for live-state border tinting.
NxOptions.lua
Nx:SetupConfig now feeds every section (General, Battlegrounds,
Fonts, Guide & Gather, Maps, Menus, Privacy, Skin, Tracking HUD,
Main) into the new Carbonite.Modules.Options module via
Options:Register, so all panels appear under a single unified
Carbonite options interface. Falls back to the legacy
AceConfig:RegisterOptionsTable path if the new module is not
loaded (unmigrated TOC order). Plugin Nx:AddToConfig calls forward
to the same place so external addons see no API change.
Modules/Options/Options.lua
buildRootGroup is now lazy / re-evaluated on every panel open,
so providers registered after OnEnable still appear. The hardcoded
About tab is only shown when no other module has registered "Main".
Verification
- luac -p syntax check: 38 new files pass
- Smoke test: all 38 files load in stubbed env
- Functional tests: Pathing PlanRoute/OptimizeRoute/MergePoints/
ReverseSegment, Coords roundtrip + special map IDs - Experimental: introduce modular architecture
Adds a clean architectural skeleton alongside the legacy Nx.* code so
new module work can target Module / EventBus / Pin / Layer classes
without rewriting the 50K-line legacy modules in one drop.
New directory layout under Carbonite/:- Core/ Bootstrap, Logger, EventBus, Module, Plugin, SavedVariables, SlashCommands
- Compat/ Expansion (single source of truth for client detection), ApiShims
- Util/ Strings, Tables, Math, Colors, Async
- UI/ Mixin system, Widget factory, Window, Tooltip, Skin, mixin library
- Modules/ New module shells for Map (with Pin/Layer + 5 pin classes), HUD, Travel, Comm, Options
Per-flavor Carbonite TOCs unified: each loads the same Core/Compat/
Util/UI/Modules manifests; only the Data/<flavor>/ path varies.
Sibling plugin addons (Notes, Warehouse, Quests) gain a Plugin.lua
front door that registers options on the new central panel, exposes
a clean public API via Carbonite.<Plugin>., and bridges into the
EventBus so Map can pull their pins on refresh.
Options module replaces the boilerplate around AceConfig+
AceConfigDialog registrations; modules call Options:Register(name, fn).
Lua 5.4 luac syntax check: 38 new files pass. Stubbed-environment
smoke test loads all 38 files and validates Pin pooling, Layer
add/clear, EventBus dispatch, expansion detection across all four
flavors (retail / cata / mists / classic-era).
Legacy NxUI / NxMap / NxOptions / NxCom / NxTravel / NxHUD remain
in place. The new modules wrap them via Carbonite. (the Nx alias)
so user-visible behavior does not regress while migration proceeds.