Skip to content
This repository was archived by the owner on Oct 11, 2026. It is now read-only.

Release V2.03

Choose a tag to compare

@gennro gennro released this 07 Sep 20:34
· 74 commits to main since this release

2026-09-07

  • "Ignore Distant XTargets When Pulling" now defaults ON (triune.lua). ctrl.ignore_distant_xtargets defaults to true so the shown toggle state matches the new pull-time behavior out of the box; existing saved loadouts with an explicit value keep their stored setting.

  • Per-character loadout files (triune.lua, triune_map.lua). When multiple MQ clients share one MQ install folder, every client wrote to the same triune_loadout.lua — and since the ignore/pull lists, zone hazards, and zone-waypoint presets live at the top level of that file (under __ignore / __pullList / __zoneHazards / __zoneWaypoints / __zoneWaypointPresets), settings and lists were bleeding between different characters across clients. Triune now writes triune_loadout_<server>_<char>.lua (per-character file), and loadAll prefers that file, re-loading it when the character is first detected in the main loop (migrating from the legacy shared file on first save). The map syncs this character's file first (mq.configDir/triune_loadout_<server>_<char>.lua), then falls back to discovery/legacy names so pre-migration setups keep working; candidates now pair every directory with both the per-character name and the legacy name. On a transient parse failure the map now keeps the previous overlay state instead of blanking the triune overlays for a sync cycle (guards against reading mid-write).

  • Search/Pull radius read live from the triune loadout; slider removed (triune_map.lua, triune.lua). Triune's own control table only contains camp_radius (default 100, the "Camp/Pull Radius"), hunter_radius (default 1500, the "Search Radius" — the one true hunt/pull search range triune uses at lines 13878/19881/19996/20029), and hunter_combat_radius (default 250, the anchor roam radius) — there is no pull_radius or combat_radius field, so the old map "Search / Pull Radius" slider (seeded from those phantom fields, clamped to 25–600) was driving a circle radius that brave triune never used. The slider is gone; the search/pull/roam circle now renders td.hunterRadius straight from triune_loadout.lua and the map defaults now match triune exactly (camp 100, search 1500). The map no longer invents its own radius. On triune's side, the three radius sliders (Camp Radius, Search Radius, Combat Radius, Pull Radius) previously assigned ctrl.* without persisting — the circumference setting only hit the loadout file on some unrelated later save. Each slider now calls runtime.saveLoadout(true) on change (plus updateMapRadiusVisuals for the native-map visuals), so moving the slider in triune is immediately visible on the custom map at next sync.

  • Triune camp / pull / anchor / waypoint map overlays restored & expanded (triune_map.lua). The map already synced camp, pull/search, and patrol-waypoint overlays from triune_loadout.lua (saved by triune.lua into the MQ config dir) — camp anchor pin + camp radius, waypoint nodes/arrival & scan radii with connecting patrol lines, the pull/search roam circle, and hazard hotspots. This change adds the missing Hunter/Puller combat anchor (hunter_combat_loc + hunter_combat_radius, the "Anchor / Roam Point"): syncTriuneLoadout now populates state.triuneData.hunterAnchor / hunterCombatRadius, the canvas draws a magenta anchor pin with its max-roam radius circle and label, and the pull/search roam circle now anchors at anchor > camp > player (previously camp > player). New persistent "Show Anchor / Roam Point" toggle in Settings & Layers, and the Triune status line reports anchor set/none plus the resolved loadout path. To make the overlays resilient across install layouts, triune_loadout.lua discovery now mirrors the multi-path search triune itself uses for triune_data.lua (mq.configDir, mq.luaDir, script-relative TAC/config / config, cwd), and the manual "Sync Triune Data" button prints the exact searched locations and a hint to run /lua run triune when the file cannot be found. The triune overlays now draw from a dedicated drawTriuneOverlays helper invoked under pcall (with a throttled one-per-5s chat error report), so a single malformed overlay datum can never abort the rest of the canvas frame — previously the whole frame after the overlay section (circles and NPC markers) silently vanished whenever anything in that block errored, which read as "circles not maintained while the map updates." The pull/search roam circle disappeared every update because syncTriuneLoadout re-seeded ctrl.customSearchRadius from the loadout on every 2.5s re-sync (clobbering the slider), while the circle itself was anchored at the camp/anchor — so enlarging the radius via the slider would push the circle's edge into view and the next sync would shrink it back off-screen. The circle is now anchored at the player and always visible around the toon, the slider value is respected for the session (state.searchRadiusTouched) and never re-clobbered by periodic re-syncs, and it no longer issues per-frame mq.TLO.Me.X/Y() fetches (it reuses the cached/interpolated player position passed into drawTriuneOverlays). The manual sync also prints a load summary (loadout path, waypoint/camp/anchor/hazard counts). Note: TAC/config/triune_data.lua holds spell/AA data only; camp/pull/anchor/waypoints live in triune_loadout.lua under the MQ config dir. Verification: luac -p clean, main chunk 116 locals (limit 200), all 2,092 unit tests pass, luacheck 0 warnings / 0 errors.

  • Smooth player marker & camera-follow update path (triune_map.lua). Player X/Y/Z/Heading are now re-read from TLO every main-loop pass (~40ms / ~25Hz) instead of on a 100ms gate, so player motion is decoupled from the slower NPC scan cadence (500ms). The render callback additionally linearly interpolates the player position between cache writes (lastPlayer.prevX/Y/Z + interpAt, seeded on first write so the marker never lerps from the world origin), and the interpolated position feeds the player arrow, auto-follow camera center, nav-line origin, and smart-floor bounds — giving fluid map movement/pan even though NPC markers refresh every 500ms. Target ID still rides its 100ms gate.

  • Smooth incremental spawn scanning (triune_map.lua). The periodic scanZoneSpawns() rebuilt the full NPC list (up to 120 NearestSpawn fetches plus up to 16 LineOfSight raycasts) in one synchronous main-loop pass, blocking the ImGui draw callback and making the map visibly chop every scan cycle. The scanner is now an incremental state machine (state.scanChunk): the fetch is drained in slices of 12 spawns per frame and the throttled LoS budget in slices of 4 raycasts per frame, with an atomic finalize (filter/sort/group) that swaps filteredNPCs only when the cycle completes — so the map keeps rendering the previous valid frame throughout. Init and zone changes pass forceComplete for an immediate full pass since they are one-shot events.

  • Fix: Map tool startup lag regression (triune_map.lua). The previously-added color precompute ran ImGui.GetColorU32 once per map segment/label at parse time. A typical map file holds tens of thousands of segment records, so this added tens of thousands of Lua→C++ marshal calls to every zone/map load — delaying window display by 30+ seconds (the draw callback is what actually renders, so the window appeared "missing" until parse finished). Color caching is now computed lazily on a segment's first frame and cached, so parse/load runs zero ImGui calls and renders identically.

  • Map & NPC Tracker Performance Hardening (triune_map.lua).

    • Navmesh batch cost collapse (processNavBatch, hover tooltip): The throttled background path verifier no longer computes a full PathLength for every NPC in the zone every 6 seconds (two synchronous mq2nav queries per mob, ~100 navmesh calls/sec sustained). It now runs PathExists only; the expensive exact path length is resolved on demand for the hovered mob only (throttled to ~1 refresh/sec) and cached so tooltips stay accurate. Cache freshness raised from 6s to 15s (navState.cacheFreshMs).
    • Path verification limited to the visible/needed set (scanZoneSpawns): Navmesh checks are now enqueued only for mobs that actually pass the current filters (and skipped entirely for remote ATLAS zones), instead of every scanned NPC up to the 120 cap. Mobs still awaiting their first path check are enqueued before the Pathable-Only gate so that filter can't starve itself. checkQueue compacts its nil-hole floor.
    • Spawn scan TLO storm cut (scanZoneSpawns, tracker tab): Removed the per-mob LineOfSight() raycast from every scan — LoS is now resolved lazily through a throttled losCache (≤16 raycasts per scan, nearest-first, refreshed on hover) instead of a full raycast per NPC every 300ms. Default scan cadence raised from 300ms to 500ms with a new "Spawn Scan Interval (ms)" slider in Settings & Layers (250–3000ms). Removed six redundant on-keystroke/on-filter scanZoneSpawns() calls in the NPC Tracker filter bar (the periodic scan now recomputes filters).
    • Per-frame TLO churn removed from render callback (DrawMapCanvas, DrawTriuneMapUI): Player X/Y/Z/Heading and target ID are refreshed once per ~100ms from the main loop into state.lastPlayer/state.lastTargetId; the render callback reads the cache (with a one-time pcall fallback) instead of issuing ~10 cross-language marshals every frame. Navigation.Active() reads the scan-populated navState.navActive.
    • Big-zone color precompute (DrawMapCanvas): Segment and label base colors are computed lazily on first render and cached (seg.colBase, lb.colBase90), then reused every frame when the Z-filter alpha is 1.0, eliminating a per-segment ImGui.GetColorU32(float) conversion every frame on large map files (fallback preserved when alpha is non-opaque). Computed at draw time rather than parse time so map/zone loading performs zero ImGui marshal calls and tool startup stays instant even on atlas meshes with tens of thousands of segments.
    • Cleanups: Removed the unused spawns.allPCs field; getConStyle returns a hoisted fallback constant instead of allocating a new table per miss.
    • Verification: luac -p clean, main chunk at 103 locals (limit 200), all 2,092 unit tests pass, luacheck 0 warnings / 0 errors.
  • Stuck Recovery & Auto-Avoid Stuck Hotspot Hardening (triune.lua, tests/test_pure_logic.lua).

    • Hazard Aging & Forgetting (runtime.decayZoneHazards, runtime.recordStuckHazard): Learned navigation hazards no longer accumulate forever. Each hotspot loses one hit every nav_hazard_decay_minutes (default 10) it goes without a fresh stuck event, deactivates once below nav_hazard_min_hits, and is removed entirely at zero hits. Added a nav_hazard_max_hits (default 6) accumulation cap so a bad day can't permanently cripple a zone. Decay runs from the main loop once per minute (runtime.nextHazardDecayAt) and persists changes via saveLoadout. New "Hazard Forget Time" slider in the Navigation settings UI.
    • Detour Anti-Oscillation (runtime.moveToward, runtime.moveTowardLoc): Fixed the root cause of detour re-route loops. In-flight detours are no longer hard-killed on a 6s wall-clock expiry while navigation toward the waypoint is still active — they persist on progress and are only abandoned when waypoint navigation actually concludes without arrival (16s backstop). moveToward now stamps the waypoint loc key (instead of nil) so the delegated moveTowardLoc state machine keeps the same detour alive, and a detour with no valid mesh route abandons cleanly to let normal routing proceed. calculateDetourWaypoint now penalizes near-side candidates whose onward segment immediately re-crosses a known hazard, preventing circumferential re-route churn.
    • Dead State Cleanup (runtime.checkCombatStall): Removed the never-assigned combatStallSince and lastCombatStallRecoveryAt fields and their six = nil writes from stuckState/checkCombatStall.
    • Roam Target Hazard Gate Simplification (findRoamTarget): Collapsed the shadowed inHaz/isMelee/pathOk chain into a single pathOk = not (inHaz and isMeleeStyle) guard.
    • Unit Test Coverage (Suite 30b): Added deterministic (fake wall-clock) tests for hazard hit capping, timed decay, deactivation below the active threshold, and complete removal at zero hits.
  • Removal of /alt buy Dispatch & Cap-Protection MQ2AAspend Delegation Fix (triune.lua, tests/test_pure_logic.lua).

    • All /alt buy calls removed (runtime.startAATrainWorkflow, runtime.processAATrainWorkflow): Deleted the fast-path dispatch in startAATrainWorkflow, the select_item listbox fallback, and the click_train belt-and-suspenders call. /alt buy does not function on the project's custom server, so it was issuing dead commands and misleading "Issued /alt buy" logs while the real purchase still depended on the window workflow. The notify-based AA Window sequence is now the sole native purchase path; if an ability can't be located in any window list, a warning is logged and the attempt recorded instead of issuing a no-op command.
    • Cap-threshold delegation no longer skips MQ2AAspend (runtime.checkAutoSpendAA): The cap-protection branch's fallback condition (now - lastAACapDelegatedAt) >= 3.0 was always true on first entry (lastAACapDelegatedAt unset), so it immediately resorted to the native trainer and never issued /aaspend. Added runtime.lastAACapDelegatedTarget tracking (mirroring the prioritized-AA lastAASpendDelegatedTarget pattern) so the first call delegates to MQ2AAspend and the fallback only fires if points are unchanged after 3 seconds. The new state is cleared in the finish step alongside the existing delegation cleanup.
    • Static analysis cleanup (tests/test_pure_logic.lua): Replaced three non-narrowing assert_true(x ~= nil, ...) guards with Lua's native assert(x ~= nil, ...) so the Lua language server no longer reports need-check-nil on the subsequent dereferences, and nil failures now abort with the suite label instead of hard-crashing on the following method call.
  • Special Tab Alternate Advancement & Fireworks Auto-Training Fast Path (triune.lua, tests/test_pure_logic.lua).

    • Fast-Path /alt buy Command Dispatch (runtime.startAATrainWorkflow): When initiating an AA training workflow for any ability with a known ID (such as Alternately Advanced Fireworks with ID 17788), Triune now dispatches /alt buy <id> immediately on the fast path. The window workflow continues in parallel for visual client confirmation and secondary fallback.
    • Unrestricted /alt buy Fallbacks (runtime.processAATrainWorkflow): Removed the obsolete not runtime.isSpecialTabAA guards from both the select_item listbox fallback and the click_train button fallback. Since modern MacroQuest /alt buy <id> works reliably across all AA tabs including Special, Special tab abilities now enjoy full /alt buy fallback redundancy if listbox indexing is delayed.
    • Static Analysis Cleanup (triune.lua): Removed unused canTrain and minLevel local assignments in runtime.checkAutoSpendAA, achieving 0 warnings and 0 errors across all 182 files in luacheck TAC/.
  • Between-Pulling Alternate Advancement Purchasing (triune.lua, tests/test_pure_logic.lua).

    • Between-Pull Auto-Spend Evaluation (runtime.pullerTick, combatTick): Added hooks in pullerTick (Camp mode, IDLE state) and combatTick (Hunt mode, not haveNPC) that check whether any prioritized AA or cap-protection ability is affordable before selecting the next roam target.
    • Affordability-Guarded Clean Movement Cessation (runtime.checkAutoSpendAA(allowStop)): Auto-spend strictly checks if an AA can actually be purchased prior to altering movement. If an ability is affordable, Triune smoothly halts movement (stopMoving(), /nav stop, /stick off), initiates the window workflow, and pauses pulling. If no AA can be purchased, pulling proceeds immediately without any pause or stutter.
    • Pulling Movement Hold During Active Training (runtime.pendingAATrain): Added guards in pullerTick() and combatTick() (for both Camp, Hunt, and Assist modes) that hold character position while runtime.pendingAATrain is active (~0.6s), ensuring client UI interactions complete without movement interruption before immediately resuming pulls.
    • Motion Grace Period (runtime.processAATrainWorkflow): Added an allowStop grace period (0.35s) to the workflow's motion check, ensuring decelerating characters after pulling do not falsely abort the opening sequence.
    • Unit Test Coverage (Suite 73): Added comprehensive unit tests in tests/test_pure_logic.lua covering between-pull evaluation, motion cessation, in-combat rejection, unaffordable skip logic, and source code assertions.
  • Alternate Advancement Training Restoration & Anti-Stutter Elimination (triune.lua, tests/test_pure_logic.lua).

    • Removed checkAutoSpendAA from Active Combat (combatTick): Eliminated auto-spend execution from the combat tick loop. AA spending now runs exclusively during out-of-combat downtime when stationary, completely stopping in-combat window popups and combat stutter.
    • Restored Proven AA Window Training Flow (startAATrainWorkflow, processAATrainWorkflow): Restored the clean, direct 5-step AA window sequence (open -> wait_open -> prepare_tab -> select_item -> click_train -> finish). Removed artificial 5-minute skip timers, speculative verification deadlines, and premature /alt buy calls that interfered with training Fireworks and custom abilities.
    • Reliable wait_open Step Advancement: Ensured that the train sequence advances directly to prepare_tab upon window open detection or after attempt retries, preventing premature aborts and guaranteed window clicks across custom UI layouts.
    • Guarded Inventory Fallback: Strictly verified inventory window state prior to sending fallback button notifications, eliminating red MacroQuest errors (Window 'InventoryWnd' not available).
    • Anti-Pause Movement, Navigation & Casting Protection (checkAutoSpendAA, processAATrainWorkflow): Enforced strict motion gates (not mq.TLO.Me.Moving(), not runtime.isMoveActive(), not mq.TLO.Navigation.Active(), and not isCasting()) both in checkAutoSpendAA and at the main loop call site. Mid-workflow movement or combat immediately aborts and closes the window cleanly without disrupting character motion.
    • Endless Retrain Loop Prevention (runtime.lastAATrainAttempt): Replaced hard lockouts with a lightweight 30-second attempt cooldown that resets automatically whenever unspent AA points change, preventing repeated 2-second window open/close loops on abilities that cannot be trained.
    • Unit Test Coverage (Suite 69, Suite 72): Validated that all 2,073 unit tests pass with zero failures and main chunk locals remain at 182 (limit 200).
  • Lifetap Spell Healing Logic Isolation & Necromancer Touch of Death Fix (triune.lua, triune_data.lua, triune_spellbook.lua, tests/test_pure_logic.lua).

    • Early Detrimental Guard in runtime.isHealAction: Added an explicit if runtime.isDetrimentalAction(name, targetToken, entry) then return false end guard at the top of isHealAction, guaranteeing that offensive lifetap and damage abilities are never mistakenly evaluated by the reactive healing priority engine, gem healing prepends, or healing condition filters.
    • Condition my HP <= Guard Against Hostile Targets (runtime.conditionMet): Updated the my HP <= threshold evaluator so that enemy mob HP is never evaluated when casting detrimental/offensive spells (E: Current Target, isDetrimentalAction, isHostileTarget). When a lifetap is configured with my HP <= (e.g. cast defensively when the player drops below 60%), the condition now strictly evaluates the player's own health rather than firing prematurely on low enemy mob HP.
    • Beneficial Gate in mapTLOCategoryToKind (triune.lua, triune_spellbook.lua): Added a strict bene requirement to category/subcategory heal string matching (bene and (catStr:find('heal') ...)), ensuring detrimental spells with descriptive heal or restore substrings are never classified as friendly heals.
    • Heuristic Lifetap Coverage in isDetrimentalSpell: Added lifedraw, lifespike, and siphon life to the offline fallback detrimental name patterns alongside lifetap.
    • Database Fix for Necromancer Touch of Death (triune_data.lua): Corrected level 64 Necromancer lifetap Touch of Death from {"Touch of Death",64,1,"heal"} to {"Touch of Death",64,0,"dd"} in the era spell database.
    • Unit Test Coverage (tests/test_pure_logic.lua): Added test assertions verifying lifetap rejection from isHealAction, Touch of Death direct damage classification in triune_data.lua, and my HP <= player-only evaluation on enemy targets.
  • Custom Server AA Compatibility & Removal of Speculative Static Pre-Checks (triune.lua, tests/test_pure_logic.lua).

    • Eliminated Speculative Pre-Checks (runtime.checkAutoSpendAA): Removed the static ga.CanTrain() and MinLevel pre-check that previously caused custom/special server abilities (such as Bestial Frenzy) to be falsely marked with Unmet Req and placed on a 5-minute skip. On custom servers that alter level requirements, prerequisite trees, or class access, eligibility is now determined strictly through live client interactions and server purchase verification rather than static client database tables.
    • Unconditional User Priority Authorization (runtime.isAAAllowedForPlayer): Added explicit bypasses in isAAAllowedForPlayer so that any ability checked by the user in ctrl.auto_aa_priorities or discovered with an in-game ID is unconditionally permitted, guaranteeing that custom server abilities are never rejected or pruned from the loadout.
  • Alternate Advancement In-Game Description Tooltips & Text Wrapping (triune.lua, tests/test_pure_logic.lua).

    • AA Description Extraction & Caching (runtime.getAADescription, runtime.recordScannedAA): Integrated in-game ability descriptions from MacroQuest's mq.TLO.Me.AltAbility(name).Description() and mq.TLO.AltAbility(name).Description() (with spell fallback for activated AAs). Descriptions are cached both in memory on the ability entry and persisted in runtime.cachedAAData across sessions.
    • Clean Word-Boundary Text Wrapping (runtime.wrapText): Implemented a formatting helper that formats long descriptions across multiple lines at word boundaries (~55 characters) while preserving existing paragraphs, preventing tooltips from spanning excessively wide across the screen.
    • Hover Tooltips in Auto AA Table (runtime.showAATooltip, UI.drawAutoAATab): Hovering over an ability name or rank in the Auto AA browser displays a comprehensive tooltip showing the ability name, current rank, maximum rank, next rank cost, points spent, followed by the full wrapped description. Implemented using percent-safe string formatting (ImGui.SetTooltip('%s', ...)).
    • Automated Regression Suite (Suite 71): Added unit tests in tests/test_pure_logic.lua covering text wrapping, lazy description extraction and caching, percent format string safety, and UI source code inspection.
  • Player-Specific AA Filtering & Cross-Class Ability Rejection (triune.lua, tests/test_pure_logic.lua).

    • Class & Archetype Ability Maps (runtime.CLASS_ARCHETYPES, runtime.ARCHETYPE_CLASSES, runtime.ARCHETYPE_RESTRICTIONS, runtime.CLASS_SPECIFIC_ABILITIES, runtime.buildAAClassRestrictions): Mapped all 16 EverQuest classes and archetypes to their class-specific and archetype-specific Alternate Advancement abilities.
    • Player Ability Validator (runtime.isAAAllowedForPlayer): Implemented strict validation ensuring that characters only see and auto-train AAs belonging to their own classes (myClasses), their archetype (Melee, Priest, Caster, Pet), universal General AAs, or Special tab abilities. Rejects cross-class abilities (e.g. Warriors seeing Headshot, Mana Burn, Harm Touch, Divine Arbitration, or Clerics seeing Taunt, Area Taunt, Ambidexterity).
    • Permissive Ownership & In-Game UI Bypasses: Grants immediate access if the character has already trained rank > 0 or if the ability was scanned directly from the character's live in-game AAWindow listbox.
    • Cross-Class AA Cache Pruning (runtime.scanPlayerAAs): Updated Step 1.5 in the AA scanner to automatically prune any stale or cross-class abilities from runtime.cachedAAData, immediately cleaning up characters whose caches were populated by earlier scans.
    • Automated Regression Suite (Suite 70): Added comprehensive unit tests in tests/test_pure_logic.lua validating player-specific AA filtering, cross-class rejection for Warrior and Cleric profiles, ownership bypasses, and cache pruning.
  • Alternate Advancement Purchase Verification & Unpurchasable Ability Anti-Stall (triune.lua, tests/test_pure_logic.lua).

    • Purchase Outcome Verification (runtime.processAATrainWorkflow): Added initial point and rank tracking (task.initialPoints, task.initialRank) when initiating an AA training workflow. After clicking the Train button (or issuing fallback /alt buy), the workflow transitions to a new verify step that checks whether unspent AA points decreased (currentPts < initialPts) or ability rank increased (currentRank > initialRank).
    • Unpurchasable Ability Skip & Cooldown (runtime.aaSkipUntil): If an AA purchase cannot be completed (e.g. AAW_TrainButton disabled in UI, level requirement not met, missing prerequisites, or server rejected purchase), Triune records the failure and temporarily skips that ability for 5 minutes (runtime.aaSkipUntil[name] = now + 300). This ensures Auto AA never stalls or loops indefinitely on an unpurchasable ability and seamlessly advances to the next prioritized candidate.
    • UI Train Button Disabled State Detection: In click_train, Triune now checks btn.Enabled() on the client's AAW_TrainButton. If EverQuest has disabled the train button, Triune avoids unnecessary clicks, notes the unmet requirement, and flags the ability for skip cooldown.
    • Level-Up & Manual Spend Skip Reset (runtime.checkAutoSpendAA, runtime.manualSpendAA): Whenever the character levels up (mq.TLO.Me.Level() change), all temporary AA skips are automatically cleared so newly unlocked abilities can be purchased immediately. In addition, manual Train##btn or Spend Now button clicks immediately clear skips for the selected ability.
    • UI Unmet Requirements Status Indicator (UI.drawAutoAATab): In the Auto AA table, abilities currently under temporary skip cooldown display a yellow Unmet Req status with a descriptive hover tooltip explaining that the ability is being skipped due to level/prerequisite requirements.
    • Automated Regression Suite (Suite 69): Added comprehensive unit tests in tests/test_pure_logic.lua validating purchase verification, skip timeout placement, advancement to subsequent priority candidates, level-up reset handling, manual spend overrides, and static pre-checks.
  • Alternate Advancement Auto-Spend MQ2AAspend Fallback & UI Delegation Toggle (triune.lua, tests/test_pure_logic.lua).

    • Automated Native Window Training Fallback (runtime.checkAutoSpendAA): Resolved an issue where auto-spending stalled when delegating to MQ2AAspend on progression/emulated servers where /alt buy or eqlib::AltAdvManager::CanTrainAbility checks fail. If Triune delegates an AA purchase to MQ2AAspend and the ability remains unpurchased with unspent points unchanged after >= 2.5s, Triune automatically falls back to its built-in native window trainer (runtime.startAATrainWorkflow) to train the prioritized ability using in-game window UI clicks.
    • Cap Dump Protection Fallback (runtime.checkAutoSpendAA): Extended the fallback mechanism to cap dump protection (e.g. Fireworks or designated cap dumps) if MQ2AAspend fails to spend points when at or above the threshold.
    • Direct Spend Now Prioritized Fallback (runtime.manualSpendAA): Updated Spend Now button handling so that clicking Spend Now with prioritized abilities immediately purchases the top affordable prioritized ability (or falls back to native training if MQ2AAspend previously stalled).
    • Interactive UI Delegation Toggle (UI.drawAutoAATab): Converted the static MQ2AAspend indicator into an interactive checkbox (MQ2AAspend##aaDelegateMaster) in the AA Progression header toolbar. Users can now directly toggle between delegating to MQ2AAspend or using Triune's native window trainer without needing slash commands.
    • Automated Regression Suite (Suite 64): Enhanced unit tests in tests/test_pure_logic.lua covering stalled MQ2AAspend delegation detection, native fallback triggers, direct native spending with delegation disabled, and UI toggle token verification.
  • Native Alternate Advancement Window Trainer & Window Control Overhaul (triune.lua, tests/test_pure_logic.lua).

    • Window Object Call & Boolean Bugfix (runtime.getAAWindow, runtime.openAAWindow, runtime.closeAAWindow): Resolved a critical issue where Triune logged Initiating AA Window train sequence... but never actually opened the in-game Alternate Advancement window. In MacroQuest Lua, calling a Window TLO as a function (w()) invokes ToString(), which returns boolean false when the window is closed. Previously, statements like if w and w() and w.DoOpen then w.DoOpen() end evaluated to false whenever the window was closed, preventing DoOpen() and DoClose() from ever firing. Created clean dedicated window helpers (runtime.getAAWindow, runtime.getAAWindowName, runtime.isAAWindowOpen, runtime.openAAWindow, and runtime.closeAAWindow) that query .Open() and invoke .DoOpen() without testing w().
    • Native EQ Command Keypress (TOGGLE_ALTADVWIN): Replaced non-functional /keypress V with EverQuest's native internal command /keypress TOGGLE_ALTADVWIN and standard key combos (/keypress v alt, /keypress a alt), as well as /notify InventoryWindow IW_AltAdvBtn leftmouseup (and InventoryWnd), ensuring the AA window opens reliably across client configurations, UI skins, and keybind layouts.
    • Window Name Resolution (AAWindow & AAWnd): Supported both AAWindow and custom UI alias AAWnd across all notifications, list queries, and window operations.
    • Recursive Child Window Iteration Bugfix (runtime.findChildRecursive): Resolved an issue where findChildRecursive prematurely terminated child traversal when encountering an inactive or closed tab page (while curr and curr()). Changed loop check to while curr and safety < 120, allowing seamless navigation across inactive tab pages and deeper UI hierarchies to find listboxes and buttons.
    • Safe Workflow Abort on Failed Open (runtime.processAATrainWorkflow): Replaced premature step advancement with a clean retry threshold (up to 4 attempts) that outputs a clear warning and terminates safely if the client window fails to open, rather than executing phantom list clicks or issuing rejected /alt buy 0 commands.
    • Eliminated Toggle Collisions & Red InventoryWnd Notifications (runtime.openAAWindow): Fixed an issue where all opening methods were fired simultaneously in a single function call. While non-toggling /windowstate AAWindow open successfully opened the window, immediate subsequent keypresses (TOGGLE_ALTADVWIN, v alt, a alt) acted as toggles that immediately shut the window back down, while blind /notify InventoryWnd IW_AltAdvBtn commands triggered red MacroQuest errors (Window 'InventoryWnd' not available). Refactored openAAWindow(attempt) to staged attempts: Attempt 1 executes non-toggling /windowstate %s open and DoOpen(), subsequent retry attempts issue fallback toggle keypresses only if needed, and inventory button notifications are strictly guarded to only fire when the player's inventory window is actually open.
    • Direct Open State Detection (runtime.isAAWindowOpen): Updated isAAWindowOpen() to check mq.TLO.Window('AAWindow').Open() and 'AAWnd' directly, eliminating false-negative window state evaluations.
  • Comprehensive Auto AA Discovery & Unpurchased Ability Retention (triune.lua, tests/test_pure_logic.lua).

    • Removed isAAWindowOpen() Scan Gate: Discovered that scanPlayerAAs() previously wrapped listbox reading (AAW_GeneralList, AAW_ArchList, AAW_ClassList, AAW_SpecialList) inside if runtime.isAAWindowOpen() then. Because players normally play with the in-game AA window closed, Step 1 was skipped 100% of the time during routine gameplay. Removed this gate and the child() boolean check, reading listbox children loaded in EQ memory directly.
    • Removed Non-UI id <= 0 Rejection in recordScannedAA: On EverQuest emulated/progression servers, mq.TLO.Me.AltAbility(name) returns nil for unpurchased (Rank 0) abilities, and unowned passive abilities return ID 0. Previously, if not isFromUI and id <= 0 then return end dropped all unpurchased AAs scanned from catalogs or config when the window was closed. Removed this check while continuing to strictly reject character skills (Mend, Flying Kick, Backstab) via mq.TLO.Skill(name).
    • Reinstated Norrath AA Catalog (NORRATH_AAS): Added a curated reference catalog of General, Archetype, and Class AAs (including Combat Agility, Combat Stability, Physical Enhancement, Fury of Magic, Twinproc, Extended Ingenuity, Fearless, Bloodlust, Gelid Rending, Run Speed, etc.) to ensure that all core progression abilities are immediately listed and prioritizeable even prior to first purchase or before the client UI listboxes populate.
    • Step 1.5 Cache Pruning Bugfix: Updated cached AA verification so that abilities with id == 0 and maxRank == 0 are not purged from runtime.cachedAAData unless they are detected as true character skills.
  • Out-of-Combat AA Spending Enforcement & Deep UI Hierarchy Traversal Fix (triune.lua, tests/test_pure_logic.lua).

    • Strict Out-of-Combat Spending Gate (runtime.checkAutoSpendAA): Enforced a strict out-of-combat gate across all automatic Alternate Advancement spending (mq.TLO.Me.Combat(), mq.TLO.Me.CombatState() == 'COMBAT', AutoFire(), anyXtarAlive(), XTHaterCount() > 0). Triune will now never trigger AA window workflows or point purchases during combat encounters, completely eliminating pauses, stalls, and player interruptions while fighting.
    • Mid-Train Combat Abort Handler (runtime.processAATrainWorkflow): Added an immediate aggro and combat check inside processAATrainWorkflow. If combat initiates while an AA train sequence is active, Triune immediately closes the AA window and aborts the workflow cleanly to prioritize combat.
    • Deep UI Hierarchy Traversal Bugfix (runtime.findChildRecursive): Resolved an issue where findChildRecursive failed to search nested tab pages and subwindows. Previously, recursion was gated behind curr.Children and (curr.Children() == true). Because MacroQuest TLO booleans return DataType objects or string "TRUE", "TRUE" == true evaluated to false in Lua, preventing the traversal from descending down into child tab boxes, subwindows, and pages (AAWindow -> AAW_Subwindows -> AAW_GeneralPage -> AAW_GeneralList). Refactored to inspect curr.FirstChild directly, enabling full recursive discovery of all listboxes and buttons.
    • Enhanced AA Row Selection & Train Button Execution (runtime.findAAInWindowLists, runtime.processAATrainWorkflow): Added direct text matching via child.List('=' .. name) before falling back to row iteration, with safe extraction across string, userdata, and function types. Pre-selected initial tab pages using known AA categories (General, Archetype, Class, Special), added a 0.2s tab activation delay upon switching tabs, and updated button clicks to fire both LeftMouseDown and LeftMouseUp events and notifications so the client engine registers the purchase.
    • Automated Regression Suite (Suite 68): Added unit tests covering in-combat AA spending prevention, 3-level deep nested UI tree navigation, and code token verification.

📦 Installation & Updating Instructions

🆕 New Installation

  1. Go here and grab the latest macroquest release https://github.com/macroquest/macroquest/releases and extract under your user folder.
  2. Download TriuneAutocombat-full.zip below and extract contents of TAC into the root of your macroquest folder.
  3. If running windows add an exception to your MQ folder before running.
  4. Start MacroQuest.exe before launching EverQuest.
  • **Existing install
    • Download TriuneAutocombat-Update.zip from the Assets below.
    • Extract the TAC folder to the root of your macroquest folder.
    • Note: Your personal settings and saved loadouts in triune_loadout.lua are preserved and will never be overwritten.