Carbonite All in One (Retail & Classic)

great addon now it supports all WoW Retail and Classic versions

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 on frame.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 in da45116 started 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); my b9eec72
    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 same IsInstanceMap(RMapId) or IsBattleGroundMap(RMapId) and CurOpts.NXInstanceMaps check 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 behind not isInst so 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:
      1. 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).
      2. 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.
      3. 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
  • 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-local worldquestdb = 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: defensive tr.Version guards 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: hadObjectives guard 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
    by isQuestStart 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's qgs list 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
  • 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).
  • 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).
  • 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).
  • 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).
  • 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.
    • 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).
  • 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).
  • 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).
  • 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.
  • 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.
  • 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.
  • 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:
    1. 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), each or {} 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.
    2. 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.
  • Experimental: revert tooltip pcall-stripping; hoist closures instead
    I previously dropped the pcall(function() ... end) wrappers around
    #s and s ~= Nx.TooltipLastDiffText in 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 #secureString and secureString ~= 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:
    1. 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.
    2. 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.
    3. 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 own local tr = {}
      per pulse, etc.) which is pre-existing behavior unchanged by my
      refactor.
  • 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.
    Because Carbonite is aliased as Nx, 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 legacy defaults tree --
    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.
  • 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.
  • 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.
  • 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.
  • 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.
  • 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.
  • 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.
  • 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.
  • 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 winsize for 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 whoami for 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 channels for 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:Time seconds * 100 + frac semantic 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. Installs Nx.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|toggle so 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 mount to 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 comq to 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 pals for 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 changelog slash 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 fly to 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|toggle slash 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 events slash 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 restore slash 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 versions slash 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 splash slash 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
    false sentinel 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 whatsnew slash 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.