ferris-overhaul: Crossbar, magic burst, EFP, SMN blood pacts, aspirable mob detection, and QoL additions#356
Draft
ferrisaj87 wants to merge 21 commits into
Draft
ferris-overhaul: Crossbar, magic burst, EFP, SMN blood pacts, aspirable mob detection, and QoL additions#356ferrisaj87 wants to merge 21 commits into
ferrisaj87 wants to merge 21 commits into
Conversation
0d922d0 to
58f44fe
Compare
added 21 commits
May 30, 2026 02:44
…etail-only JAs What changed - horizon_abilities.lua: job ability unlock levels from HorizonXI JA Progression (including BST pet commands with level gates); comments reference retail-only omissions moved to the separate file. - ws_weapon_types.lua: weaponskill to weapon category, required skill level, relic-only flags. Used by Show All sort (weapon category A->Z, skill req low->high) and equipment-based availability. - horizon_spell_omissions.lua: spell names excluded from Show All (post-75, retail-only, etc.); kept separate from horizonspells.lua so the core spell DB stays untouched. - horizon_retail_only_job_abilities.lua: named JAs that exist on retail but not on Horizon (Bestial Loyalty, Feral Howl, Killer Instinct, Unleash, Snarl, Spur, Run Wild). HasAbility-driven lists and macro hints treat them as unavailable. Why - Pure data files; no runtime dependencies. Must land first — pr/04, pr/05, pr/06, pr/10, pr/11 all depend on accurate Horizon ability/spell/WS rules. Show All and availability checks use these files to filter retail-only content without editing the main spell DB.
…/crossbar enable What changed - XIUI.lua: Independent crossbarEnabled toggle alongside hotbarEnabled. Packet handlers (0x0068 pet sync, 0x0028 skillchain, 0x00A zone-in, 0x00B zone-out, 0x001B job change) fire when either bar is enabled — not hotbar-only — so crossbar-only setups still get palette/pet/skillchain updates. SaveCurrentProfileFileToDisk() swaps frozen macroDB snapshot, saves profile, persists SharedMacros. Post-load hook trio: ApplyAfterProfileLoad / xiuiInvalidateHotbarDataCaches / xiuiApplyDefaultCrossbarPaletteScopeAfterProfileLoad after every settings load or reset. Palette Manager + FinalizeFrame draw order: configMenu -> paletteManager -> FlushTooltip -> hotbar.FinalizeFrame(). WS cache init: SetKnownWeaponskills() with pcall guard after charSettings load so Show All WS colors are correct on login. imgui.SetMouseCursor(0) in d3d_present for alt-tab hardware cursor recovery. TextureManager.FlushPendingReleases() at top of d3d_present. hideOnMenuFocusKey intentionally absent from hotbar Register block (separate per-bar-type toggle). - config.lua: Crossbar tab registration, Palette Manager routing, smart window sizing, non-blocking profile create/rename/delete modals (BeginPopup instead of BeginPopupModal); hardware cursor stays visible over FFXI while profile prompts are open. - core/settings/user.lua: crossbarEnabled, crossbarLockMovement, 6 macro storage scope keys, petBarResizeAnchor, petTargetSnapTopGap, petTargetSnapCachedHeight. Retains all 1.8.0 additions. - core/settings/migration.lua: 6 Ferris migrations + EnsureMacroDatabaseCoherence + MigrateSlotDualMacroBindings. Runs after 1.8.0's MigrateSlotMacroRefs; idempotent. - core/settings/factories.lua: createCrossbarDefaults() rewrite: universal palettes, segmentOverrides, double-tap preview, paletteJobIconTheme, magicBurst defaults. Re-injected 1.8.0's showStackQuantity lines. mpCostAnchor defaults to topLeft. - handlers/actiontracker.lua, debuffhandler.lua, petbuffhandler.lua: Ferris-only overlays carried forward unchanged (1.8.0 did not touch these files). - handlers/imgui_compat.lua: Ashita 4.3/4.16 compatibility shim; focus-regain hardware cursor recovery; hover detection helpers used by pet target cluster drag. - libs/texturemanager.lua: TextureManager.DeferRelease(value) public API — holds a Lua reference to a D3D texture for one extra d3d_present frame before releasing, preventing EXCEPTION_ACCESS_VIOLATION when palette deletion triggers a cache clear mid-frame. Extends 1.8.0's existing pendingReleases / FlushPendingReleases pattern. Also: custom_icons.maxSize = 0 no-eviction tweak prevents macro picker stutter from LRU evict+reload cycles. - modules/hotbar/init.lua: M.FinalizeFrame() for deferred drag/drop resolution. Separated hotbar vs crossbar menu-hide logic. GetCrossbarDisableXiMacrosEffective(). lastPaletteVisualRefreshSig dedup. ValidatePalettesForJob with applyDefaultCrossbarScope. Universal 2hr reset/sync on zone/job change. 1.8.0's AnyBarIsPetAware() optimization preserved. Why - Foundation layer that must land before all feature slices. Establishes independent hotbar/crossbar enable so crossbar-only setups receive all state updates. DeferRelease prevents reproducible CTD on palette deletion (EXCEPTION_ACCESS_VIOLATION when D3D texture freed while still queued for AddImage). Post-load hook trio ensures macro scope, caches, and crossbar palette scope are coherent after any settings mutation. Non-blocking profile modals keep cursor visible throughout.
…al macro What changed - core/shared_macro_store.lua: Two-mode macro storage — shared (global SharedMacros.lua across all profiles) vs profile (per-profile gConfig.macroDB). Frozen snapshot in shared mode for profile switch. Separate id namespace from profile macros. ApplyAfterProfileLoad integration. - libs/target.lua: subtarget-active detection treats a standalone subtarget as active even without a primary target, so Universal 2 Hour /ja targeting (stpc/stnpc) matches in-game behavior. - modules/hotbar/universal_two_hour.lua: Maps job ID -> 2-hour ability name. Arming window (~7.5s) after execute with late-window opacity ramp for shimmer. Subtarget-glow eligibility cleared on zone/job change. ShouldGlowUniversalTwoHourSlot() for slot renderer gating. - modules/hotbar/macro_global_defaults.lua: Sentinel resolution for Universal 2 Hour Global macro. /ja bind targets: stpc for most jobs, stnpc for RNG. One-time seed migration gated by flag. Locked Global-row macro cannot be deleted or moved through normal palette paths. - modules/hotbar/macro_xiui_defaults.lua: Default /xiui slash macros (Toggle XIUI Menu, Open Macros, Palette Manager, etc.). One-time seed gated by macroXiuiDefaultsSeeded. Why - One global shared macro library vs per-profile gConfig.macroDB; each physical hotbar/crossbar slot holds independent macroBindProfile and macroBindShared arms (active arm follows scope). Deletes, DnD, JSON import, and Edit Full Palette use the same data paths. Players get one pinned Global macro that always reflects the current main job 2-hour name, icon, and correct /ja targeting without hand-maintaining per-job copies. Default /xiui macros seed discoverability.
…sets What changed - horizon_bloodpacts.lua: Synthetic spell-shaped rows (ids starting 10200) with level/MP/element from Horizon progression data. - horizon_bloodpacts_xiui.lua: XIUI overlays on top of blood pact rows: status labels, corner icons, requiresFlow flag for Astral Flow-only pacts. - horizonspells.lua: Extended with blood pact integration hooks and Horizon-specific spell data. - petregistry.lua: Merges retail avatar metadata with Horizon blood pact stats and XIUI overlays. GetBloodPactByName / RebuildBloodPactIndex for consistent blood pact lookup. Astral Flow-gated pacts (requiresFlow): pink asterisk in picker lists with tooltip "only during Astral Flow". GetBloodPactsExpanded (Show All): AF-only pacts sort before Commands, then BP:Rage, then BP:Ward (within each band: availability, level, name). Single resolution point for all BP display/gating. - SMN/pet UI assets: bloodpact.png, ward.png for blood pact / ward type indicators. drg_wyvern.png, jug.png for DRG wyvern and BST jug pet types. AvatarsFavor.png for SMN crossbar corner. Tetsouou/35.png status icon (Frost Armor). Full avatar PNG set (14 avatars) and full spirit PNG set (8 spirits) for pet bar and palette display. Why - Blood pact availability and costs match Horizon progression; data can be regenerated when source tables change. Single registry lookup for BP display and gating across hotbar, macro editor, and pet palette. Full avatar/spirit art assets ship alongside the code that uses them.
… parser What changed - actions.lua: BloodPactRage/BloodPactWard handling. GetBloodPactByName integration for icon resolution. Horizon-aware resolveSpellIndexForMa. spellsByLowerNameLookup lazy multimap for O(1) name lookups. noIconCache extended key for blood pact slots. LoadItemIconByName via actiondb.GetItemId. Effective level reads party:GetMemberMainJobLevel(0) / GetMemberSubJobLevel(0) (post-Level-Sync) instead of raw player:GetMainJobLevel() so synced-down players see correct availability. NotifySlotExecutionEffects skips Universal 2 Hour arming shimmer while the bind is on cooldown. - recast.lua: Blood pact shared timer (173/174) name-based lookup. Macro text sniffers: sniffRecastTargetFromMaMacroText, sniffRecastTargetFromPetMacroText, sniffRecastTargetFromJaMacroText. BuildCommandString uses resolved ability names and per-job target tokens. Lazy recast architecture preserved (no periodic M.Update() reintroduced). - macroparse.lua: New file. Multi-line macro parser. Priority order: /ws,/ma,/pet -> /ja -> /item,/equip -> other. Returns primary action type + JA badge for corner rendering. Drives skillchain/MB routing for macros, recast sniffing, and crossbar SMN blood pact dispatch. Why - Hotbar and macro layers resolve correct icons, names, and cooldowns for blood pact abilities. Macro parser enables skillchain prediction and Magic Burst highlight routing for /pet and /ma primary macros on the crossbar, matching the keyboard hotbar's existing behavior.
…ache, Level Sync invalidation What changed - playerdata.lua: GetAllSpells, GetAllAbilities, GetAllWeaponskills with status tiers and hover reason strings. ABILITY_TYPE enum (24 entries). Spell sort: level then name within magic type; WS sort: weapon category A->Z, skill req low->high (non-relic before relic). JA two-hour sorts first; pink asterisk + tooltip for Universal 2 Hour hint. Trait + CategoryPlaceholder filters. Horizon filtering throughout using horizon_abilities.lua and horizon_spell_omissions.lua. GetExpandedAbilities via actiondb.GetAbilityId; playerHasLearnedNonWsAbilityByName O(1); WS scans via actiondb.GetWeaponSkillAbilityIds(). RefreshCachedLists called at DrawWindow top for crossbar-only setups (fixes crossbar JA/WS slots showing unavailable without hotbar on). - actiondb.lua: GetWeaponSkillAbilityIds() lazy session-cached list. Collapses O(1024) ability scans to O(1) or O(~50-100) per cache miss for WS availability and icon resolution paths. - equipment_ws.lua: New file. WS cache-bust signature: job + levels + level sync + main/sub/range item IDs. Drives WS list refresh on gear swap without full rescan. - handlers/statushandler.lua: Level Sync buff detection (LEVEL_SYNC_BUFF_ID = 269); transition block clears playerdata cache + slotrenderer availability cache. Encoding import updated to libs.encoding (replaces deprecated gdifonts path from 1.7.5). Why - Macro editor and hotbar show consistent availability and readable lists on Horizon without labeling retail-only additions as usable. Crossbar-only setups correctly show JA/WS availability without requiring the keyboard hotbar. Availability caches invalidate on Level Sync toggle so synced-down players see correct spell/ability lists.
… editor clip, availability badges What changed - slotrenderer.lua: Complete GDI-font -> imtext/drawList port. Key additions: DrawSkillchainHighlight: gold dashed marching-ants border + SC-name icon top-right corner. DrawUniversalTwoHourRainbowMarchingBorder + DrawUniversalTwoHourSubtargetGlow: pure ImGui drawList; shimmer suppressed on cooldown; subtarget glow eligibility gated by universalTwoHour module. Unavailable ability feedback: GetFrameAvailability() snapshots (jobId, subjobId, mainLevel, subLevel, partyMain, partySub) once per frame via M.BeginFrame — replaces ~7 FFI getters x 16-32 slots x 60 fps with one compare per frame. displayText pre-parsed at cache-insert as unavailableReason. Allowlist expanded to cover ma, ja, ws, pet, macro action types. Size-bounded caches (8192 availability, 4096 MP cost). labelAboveSlot flag: live HUD respects params.labelAboveSlot; crossbar passes true for top diamond slot. slotOverlayDrawList() returns imgui.GetWindowDrawList() for crossbar window (keeps overlays within window z-order, not above modal dialogs). IsMovementLockedForDropZone routes crossbar_* -> crossbarLockMovement, paled* -> always unlocked. dropPriority support for deferred drop resolution (EFP palette editor uses priority 10). DeferRelease on ClearAllCache, ClearSlotRenderingCache, ClearAvailabilityCache so D3D handles are not freed mid-frame. Double-click tracker (lastClickButtonId, lastClickTime, DOUBLE_CLICK_INTERVAL 0.35s) with 4-branch dispatcher. MakePreviewSettings cached on geometry signature; preview early-exit when baseOp <= 0.02. - display.lua: Keyboard hotbar render path with Ferris additions on 1.8.0 perf base. macro JA badge cache suffix. GetBindIcon cache-miss path. mpCostAnchor topLeft default. Skillchain prediction for BP+macros via macroparse. RefreshCachedLists at DrawWindow top. Deferred icon cache clear via DeferRelease. ClearNoIconCache hook. 1.8.0 reusable slotParams/slotInteraction tables preserved. Why - Central hot path for all slot rendering; must land before any DrawSlot caller (pr/08, pr/09, pr/14, pr/18). Availability feedback ported to imtext path: grayed/Lv65/X labels for unavailable spells, JAs, WS, pet commands, and macros. UTH visual arming cue matches Horizon targeting semantics. Frame snapshot reduces availability cost to constant regardless of slot count.
… and settings UI What changed - skillchain.lua: magicBurstMap state tracked alongside resonationMap (different lifetimes; separate keys). MB_WINDOW_OPEN_DELAY = 0.0s, MB_WINDOW_DURATION = 7.0s after skillchain close. Eligible elements resolved via magicBurstElements map covering all 14 SC tiers (Lv1 single-element through Lv3 Light/Darkness multi-element sets). SMN Magic Blood Pact Rages curated via bloodPactElementMap (12 pacts: Fire II/IV, Stone II/IV, Aero II/IV, Water II/IV, Thunder II/IV, Blizzard II/IV — physical and off-element pacts deliberately excluded). spellElementByLowerName lazy cache resolves element from horizonspells DB. Public API: GetBurstElementForSlot, GetMagicBurstForElement, GetMagicBurstForSlot, GetMagicBurstWindow. MB window clears on zone change (same hook as SC clear). - slotrenderer.lua: DrawMagicBurstHighlight() — animated dashed border drawn via ImGui drawList alongside SC border; configurable color with pulsing opacity. params.magicBurstName branch in DrawSlot. Both SC and MB borders can appear simultaneously (distinct positions: SC top-right, MB bottom-left corner icon placement). - display.lua, crossbar.lua: GetMagicBurstForSlot dispatch wired through all render branches; suppressed in palette editor preview. Crossbar routes actionType=='pet' and /pet-primary macros through skillchain.GetSkillchainForBloodPact for SMN Blood Pact MB eligibility (matches keyboard hotbar behavior; previously crossbar only checked actionType=='ws'). - core/settings/factories.lua: hotbarGlobal.magicBurstHighlightEnabled = false default; magicBurstHighlightColor default (cyan-blue). - config/hotbar.lua: DrawSharedSkillchainHighlightControls includes Magic Burst checkbox + inline color picker nested under the Skillchain section. Shared icon scale/offset help text applies to both SC and MB highlight systems. Why - Gives players an at-a-glance Magic Burst opportunity cue without manually tracking SC results. Parallel to the existing skillchain highlight system; reuses the same imtext drawList path and hotbarGlobal settings. SMN Blood Pact coverage limited to Magic-element pacts to avoid false positives from physical pacts that cannot MB regardless of skillchain.
…binding migration What changed - macro_palette_buckets.lua: Bucket schema: global, items, equipment, xiui, custom:N. Custom buckets support create/rename/delete with slot cleanup via ApplyMacroPaletteBucketRemovedToSlotAction (sweeps profile + shared gConfig, clears all slot arms bound to removed bucket key). - macroparse.lua: Multi-line macro text parser. Priority: /ws,/ma,/pet -> /ja -> /item,/equip -> other. Returns primary action type and command string for JA badge, skillchain/MB routing, and recast sniffing. Pure logic; no render code. - macro_xiui_defaults.lua: Default /xiui slash macros seeded once (Toggle XIUI Menu, Open Macros, Open Palette Manager, etc.); gated by macroXiuiDefaultsSeeded flag in migration. - macro_global_defaults.lua: Universal 2 Hour Global macro sentinel. Job -> 2-hour name map. /ja bind targets with per-job targeting tokens. Migration seed runs after XIUI defaults. - core/settings/migration.lua: MigrateSlotDualMacroBindings upgrades legacy single-arm slot records to macroBindProfile + macroBindShared dual-arm format. MigrateSlotMacroRefs (1.8.0) preserved. Why - Organizes macro library by category; custom types for user-defined groupings with full cleanup on delete. Macro parser drives corner badge, SC/MB routing, and recast sniffing from a single consistent source. Default seeds give new users discoverability and a working 2-hour macro out of the box. Migration idempotently upgrades existing profiles.
…edup, and custom icon resolution What changed - macropalette.lua: Show All toggles and filters (magic type, ability job, WS weapon, pet type). Two-color spell rows (magic-type color + status color). Group headers. Hover reasons on unavailable entries. Ability/BP rows: pink asterisk tooltips for main-job 2-hour (Global macro note) and Astral Flow-only pacts. Copy: duplicate selected macro into editor as a new entry. Locked Global-row handling: Universal 2 Hour macro cannot be deleted or edited through palette duplicate/remove paths. Custom type (+) in popup only; red remove and Rename on the type row; delete confirms macro count; rename modal; delete and rename save paths. Custom grid section header uses Elementals for spirit pets. Move Macro: MoveMacroToPalette API, palette picker popup. RewriteMacroPaletteBindingsInConfig + RewriteMacroPaletteBindingsInDraft update all bindings on move. OpenEditorForSlotData accepts opts.bindTargetSlot for double-click-new-macro EFP flow. - macropalette_macroeditor.lua: New 86 KB closure-factory extension (return function(MP) pattern). SaveMacro syncs dropdown/text buffers into editingMacro before validation so saves match UI state. Main slot icon: implicit refresh on macro text edit no longer forces overwrite; Sync refreshes main icon. JA badge: separate manual vs implicit sync; Sync JA badge clears manual overrides and resolves from /ja line; icon picker marks manual badge on Change. Auto-bind new macro to pendingNewMacroBindTarget slot via data.SetDraftSlotData when set by EFP double-click. Show All combo rows: single InvisibleButton hit target with draw-list text/icons so overlapping widgets do not swallow clicks. - playerdata.lua: Spell dedup on spell id before building display list removes duplicate rows for spells that appear under multiple magic-type categories. GetPlayerSpells reads unlearnable flags from horizonspells.lua; Show-All-off consistently shows only known/available spells without gaps. - customiconresolve.lua: New file. Resolves assets/hotbar/custom/*.png by action name (recursive scan with caching). Allows users to drop custom-named PNGs into the custom/ folder and have them appear on matching macro/action slots automatically. - iconmatch.lua: New file. Fuzzy name-to-icon matcher used by customiconresolve for partial-name and case-insensitive resolution. - textures.lua: Custom texture resolution layer. custom_icons.maxSize = 0 no-eviction tweak (prevents macro picker stutter from LRU evict+reload on large custom icon sets). Why - Large spell/ability lists stay navigable; Copy speeds palette workflows; icon and badge behavior matches user expectations. Custom icon resolution lets users add their own named PNGs to assets/hotbar/custom/ and see them on matching slots without editing Lua. Spell dedup and unlearnable-flag filtering prevent spurious duplicate rows and missing spells in the picker.
…e, and post-import refresh What changed - palette_json.lua: New 40 KB module. Whole-profile JSON export/import (xiuiExportVersion 1, kind xiui_profile). Exports all keyboard palettes, all crossbar palettes, and the full macro library in one annotated JSON file. Merge vs replace import; file list + paste input; exports folder helpers. Post-import hooks call InvalidateCachesAfterExternalSlotMutation and RefreshActivePaletteVisualsAfterExternalEdit so the active bar refreshes immediately. Export now includes enableUniversalCrossbarPalettes and defaultCrossbarPaletteScope in the crossbarPalettes section so the imported profile restores the correct crossbar scope mode. Import applies those fields to gConfig.hotbarCrossbar when present in the payload. Palettes-only import: removed the blocking validatePaletteMacroRefs check that prevented importing palette structure when macros were stored in the frozen profile library (Shared mode). macroExistsInBucket now checks both gConfig.macroDB and the frozen profile DB so dual-bound slots resolve correctly. Slots referencing missing macros import as empty rather than blocking. Bug fix: added ImportProfileFromFile() which reads the export file from disk and calls ImportProfile directly, bypassing the ImGui text buffer (which was silently truncating exports larger than 200 KB — causing all crossbar slot data at the end of the file to be dropped). The import UI now has a "Load & Import" button that uses this path. The old "Load into text box" + "Apply" flow is retained for manual JSON paste. Export filenames now use a human-readable datetime (YYYYMMDD_HHMMSS) instead of Unix epoch. - libs/json.lua: rxi's json.lua (MIT). JSON encode/decode used by palette_json. - palette.lua: InvalidateCachesAfterExternalSlotMutation; RefreshActivePaletteVisualsAfterExternalEdit after external edits (e.g. import). ValidatePalettesForJob merges factory crossbar defaults into gConfig.hotbarCrossbar before reading enableUniversal/defaultCrossbarPaletteScope. SetCrossbarPaletteScope now auto-selects the first available universal palette when switching to universal scope if state.crossbarActiveUniversalPalette is nil — prevents a blank crossbar appearing on the first L1+R1 press after login or profile import. - init.lua: OnPaletteChanged dedupe includes bar/combo id for same-name refreshes so every bar reloads after import. - config.lua: Profiles window Backup/Transfer (export/import modal), larger Profiles window size, merge/replace and import toggles. Why - Before: no structured way to move an entire character's bars and macros between copies or accounts. After: one JSON file per profile for full backup or migration. Import refreshes the visible UI immediately without requiring a manual palette switch or relog. Scope settings export/import ensures a profile transferred to a new device starts in the same crossbar mode (job bars vs universal) without manual reconfiguration. Palettes-only import no longer fails with a spurious macro-reference error when macros exist in the profile library rather than the shared library.
…king popups What changed - config/palettemanager.lua: Floating Palette Manager window expanded from stub to full 146 KB implementation. Override/Pet rows, scroll-safe layout, horizontal resize, job label and warnings when editing a palette that is not active, quick palette switcher, crossbar edit appearance decoupled from in-game crossbar visuals. Active/Inactive column uses centered image icons (checkmark.png / x.png) via TextureManager.getFileTexture instead of text labels. +M button on each palette row pre-populates a new macro with the /xiui cpal switch command for that palette (eliminates manual macro authoring for palette switches). New palette popup converted from blocking BeginPopupModal to BeginPopup (hardware cursor stays visible). Crossbar Copy To adds destination scope Job/Subjob [J] vs Global [G] with destination lists wired for each mode. Scroll-safe clip rect for clipped preview rows. - config/hotbar.lua: Shared palette create/rename modal helpers for hotbar AND crossbar. Persists last-selected job/storage-subjob tier across opens; resets only on character job/subjob change. Auto-names new palettes based on tier selection. DrawSharedDisableXiMacrosControls, DrawSharedSkillchainHighlightControls, DrawLogPaletteNameCheckbox shared helpers. AlwaysClamp on Rows/Columns sliders per 1.8.0 slider policy. - config/components.lua: MANAGER_BUTTON_STYLE table + 4 push/pop helpers + DrawStyledTab variant parameter for consistent styled tabs/buttons in Palette Manager and config UI. - assets/checkmark.png, assets/x.png: New UI assets for palette list Active/Inactive column icons. Why - Central hub for palette CRUD, crossbar scope management, and profile portability. Non-blocking popups keep hardware cursor visible throughout palette creation and copy operations. +M button and named status icons reduce friction in day-to-day palette management.
…e-tap return What changed - modules/hotbar/palette.lua: Cpal anchor API — SetCpalJobAnchorIfUnset/SetCpalUniversalAnchorIfUnset record the active palette before the first /xiui cpal switch; GetCpalAnchor/ClearCpalAnchor/ RestoreCpalAnchor allow controller code to retrieve, clear, or jump back to origin. Separate anchors for job-tier and universal scope. GetCrossbarSjOnlyPaletteNamesOrdered for 6-letter MAINSUB # indexing. - XIUI.lua: Anchor set immediately before SetActivePaletteForCombo/SetActiveUniversalCrossbarPalette in the /xiui cpal handler so origin is always saved on the first switch. Full /xiui cpal / cpalette / xcpal / xcpalette command family: list, toggle, scope (job|universal), cycle on|off, global/g/gname, compact MAIN+SUB, explicit job, bare-job shorthand. /xiui pal, /xiui menuname, /xiui cpaledit. Chat lines follow logPaletteNameCrossbar; optional RB+D-pad cycle hint. - modules/hotbar/controller.lua: R1 double-tap (two presses within 400ms, no L1 held) restores cpal anchor. R1+DPAD palette cycling clears anchor (cycling already serves a similar return function). L1/R1 shoulder-held latch state is set-only from poll, cleared exclusively by HandleXInputButton release events (fixes L1 cycle failing when FFXI consumes the L1 xinput bit before Ashita's snapshot; same fix makes R1 scope toggle and R1 double-tap more reliable). DPad blocking uses two complementary mechanisms: (1) xinput_state zeros DPAD_UP/DOWN bits in e.state_modified (works on standard controllers where Ashita hooks the buffer the game reads); (2) HandleXInputButton sets e.blocked = true for DPad presses when the cycle-shoulder button is held, regardless of whether any trigger combo is active — this is Ashita's first-class event block and works on all hardware including built-in controllers (e.g. ROG Ally) where e.state_modified does not reach the game's input buffer. A shared state.lastDpadCycleTime timestamp (50 ms window) prevents double-cycling when both paths fire for the same press. - modules/hotbar/crossbar.lua: When a cpal anchor is live, draw a pulsing R1 icon above the R2 label with a subtle dark-pill background and gold 'x2' hint; icon scales to ~130% at pulse peak (~2.5 Hz sin wave); disappears when anchor is cleared. Why - /xiui cpal macros can switch the active crossbar palette programmatically; the R1 indicator tells the player an anchor is set, and double-tapping R1 returns to the original palette without another macro. L1 cycle fix resolves users reporting L1 broken while R1 worked. DPad double-block ensures the game does not simultaneously act on a DPad press (e.g. cycling the party target list) when XIUI has consumed it for palette cycling, on all controller hardware.
What changed - core/gamestate.lua: IsMenuOpen() scans memory for the active FFXI menu name; IGNORED_MENUS list passes combat-related menus so the crossbar stays live during battle while inventory/storage menus block it. - config/crossbar.lua: Disable Crossbar While In Menu checkbox under Hide When Menu Open (default on). When unchecked, trigger input passes through so FFXI inventory Quick Jump (trigger + D-pad) still works. Also hosts Lock Crossbar toggle (reads crossbarLockMovement, separate from hotbar lock). - core/settings/factories.lua: crossbarDisableInMenu = true default. - modules/hotbar/controller.lua: Skip XInput/DInput crossbar processing when IsMenuOpen() and crossbarDisableInMenu is enabled. - modules/hotbar/crossbar.lua: visibilityOpacity *= 0.35 when menu is open and disable-in-menu is enabled, giving visual feedback that input is blocked. Why - Prevents accidental crossbar slot execution while FFXI inventory or storage menus have focus. Visual dim confirms input is blocked without requiring the player to open settings. Optional setting preserves trigger navigation for Quick Jump when preferred.
What changed - modules/hotbar/crossbar.lua: Two independent floating ImGui windows (CrossbarPreviewL2x2, CrossbarPreviewR2x2) always display the 8-slot diamond for the corresponding double-tap bar including live cooldowns. Slot backgrounds render at base opacity; icon/text dims with inactiveSideWhileTriggerDim when any trigger is held (both previews dim equally). When a double-tap is live the preview swaps to show the base L2 or R2 bar as reference. Windows omit NoBringToFrontOnFocus so they stay in front of the main crossbar; WindowPadding zeroed to prevent clip-rect edge clipping. Drag anchors sit above the window via anchorSide=top. MakePreviewSettings cached on geometry signature; early-exit when baseOp <= 0.02. Skillchain and Magic Burst borders enabled in preview slot rendering; MP cost suppressed. Item stack quantity is now configurable via doubleTapPreviewShowQty (defaults on). - config/crossbar_settings.lua: Show Double-Tap Crossbars Preview checkbox, Preview Scale slider (0.30-1.0), Preview Opacity slider (0.20-1.0), Lock Preview Positions toggle, and Show Item Quantity checkbox — all nested under Enable Double-Tap. - core/settings/factories.lua: showDoubleTapPreview=false, doubleTapPreviewScale=0.60, doubleTapPreviewOpacity=1.0, doubleTapPreviewLocked=false, doubleTapPreviewShowQty=true defaults. - libs/drawing.lua: DrawMoveAnchor gains anchorSide='top' option (with windowWidth for centering) so the drag handle can be placed above the target window rather than to its left. Why - Players can see their double-tap bar contents and cooldowns at a glance without activating the double-tap. Preview swaps to base bar while double-tap is live so both bars are always visible. SC/MB borders show eligibility in preview; quantity defaults on so item counts are visible, with an opt-out for players who prefer cleaner small-window display.
… and deferred drop UX What changed - data.lua: Draft layer (draftByKey + draftTouchedKeys). Empty-slot sentinel distinguishes cleared palette slots from sparse untouched ones so overlay swap reads do not resurrect live binds. GetCrossbarSlotRawForSwapOverlay for palette row reads. SwapActiveMacroArmsInPlace, FinalizeCrossbarRawSlotForStorage, NormalizeCrossbarSlotRawForSwap. SyncDraftSlotFromLive merges HUD edits into draft only when HUD palette storage key matches draft (liveKey == rk) so hops between active palettes do not overwrite unrelated drafts. 6 EFP public functions. BeginDraftUndoGroup / EndDraftUndoGroup / UndoDraft / CanUndoDraft stack. resolveSlotMacro returns nil for minimal macro binding tables (macroRef + macroPaletteKey only, no displayName/action) when the referenced macro no longer resolves — slot draws empty instead of showing abbreviation '?' (orphan cleanup after macro delete). RewriteMacroPaletteBindingsInConfig + RewriteMacroPaletteBindingsInDraft update all bindings on Move Macro. - crossbar.lua: GetCbInteractionPaletteEditor: getDragData uses GetCrossbarSlotRawForSwapOverlay; onDrop handlers use SwapActiveMacroArmsInPlace + FinalizeCrossbarRawSlotForStorage + proper ClearDraftSlotData; all drop branches pair BeginDraftUndoGroup with EndDraftUndoGroup; all drop and onRightClick handlers call ClearCrossbarIconCacheForSlot (via DeferRelease). Stale slotrenderer.InvalidateSlotByKey calls removed (not applicable to 1.8.0 immediate-mode renderer; their crashes previously prevented EndDrag from firing and left isDragging stuck true). onDoubleClick closure: seeds fresh macro from active palette defaults and passes bindTargetSlot to macropalette.OpenEditorForSlotData. Editor slot params: editorClipRect for clipped preview, dropPriority=10 so palette editor wins over live crossbar zone. - libs/dragdrop.lua: deferredDropCandidates, FlushDeferredDrops() with dropPriority + registration- order tiebreaker. Re-applied 1.8.0 simplification (centralized tooltip in slotrenderer.FlushTooltip). - macropalette.lua: OpenEditorForSlotData accepts opts.bindTargetSlot; stashed as MP.pendingNewMacroBindTarget for SaveMacro auto-bind. - macropalette_macroeditor.lua: SaveMacro auto-binds new macro to pendingNewMacroBindTarget slot via data.SetDraftSlotData when set by EFP double-click flow. - init.lua: M.FinalizeFrame() calls FlushDeferredDrops before drag renderer so palette editor drop zones are resolved in correct priority order. - XIUI.lua: hotbar.FinalizeFrame() called after palette manager draw. - config/palettemanager.lua: Consumes data.pendingPaletteSlotEdit next frame for double-click new-macro flow. Editor clip rect culling. Label rendering (minimal vs full multiline). Scroll-safe layout. Job-shared copy. Override/Pet rows for segment editing. Why - Removes duplicate/wrong swap behavior when draft clears overlapped live-only slots. Drag-drop visual artifact (square stuck on cursor) fixed by removing stale InvalidateSlotByKey calls that crashed the drop handler mid-frame. Undo works correctly after slot swaps. Double-clicking an empty EFP slot creates a new macro and auto-binds it to that slot. Orphan '?' placeholder eliminated after macro delete. Move Macro avoids broken binds after relocating a library row.
…B UI, AlwaysClamp sliders What changed - config/crossbar.lua: New file. Crossbar sidebar entry shell routing to crossbar_settings.lua. Lock Crossbar toggle (independent from hotbar lock). Disable Crossbar While In Menu checkbox. - config/crossbar_settings.lua: New 70 KB file. Full crossbar settings UI extracted from hotbar.lua: controller layout, palette management (Job vs Global scope, palette lists), visuals (inactive dim, trigger icon scale, label above slot), double-tap preview sliders, disable-in-menu, palette scope icon theme (ClassicFFXIV removed; one-shot migration falls back to Classic). Show Double-Tap Crossbars Preview section. Shared skillchain + MB controls via DrawSharedSkillchainHighlightControls. - config/hotbar.lua: Crossbar UI sections removed (~815 lines moved to crossbar.lua + crossbar_settings.lua). Ferris additions retained: DrawSharedDisableXiMacrosControls, DrawSharedSkillchainHighlightControls (SC color picker + MB checkbox + MB color picker), DrawLogPaletteNameCheckbox. AlwaysClamp on Rows/Columns sliders per 1.8.0 slider policy. Layout Mode dropdown removed from Hotbar tab. Edit Full Palette entry moved to Crossbar tab. AlwaysClamp also applied to castbar Fast Cast sliders and notification group sliders. Show Stack Quantity checkbox re-applied from 1.8.0. Slot Y Padding slider retained. - config/efp_pets_tab.lua: New file. EFP pet family tabs (Avatars, Elementals, Beasts, Wyvern, Puppet). Avatar vs spirit elemental distinction. SMN sort and pet-bar omit rules. - config.lua: Crossbar tab registration. Crossbar tab dispatches to config/crossbar.lua. Why - User explicitly separated hotbar and crossbar configuration concerns; Lock Crossbar and Edit Full Palette entry belong in the Crossbar tab. ClassicFFXIV removal prevents user-custom theme from appearing in a public dropdown. AlwaysClamp compliance brings all Ferris-touched config files into line with 1.8.0 slider policy.
…y cost display What changed - pet_palette_allowlist.lua: New file. Type tokens: avatars, elementals, beasts, wyvern, puppet. Legacy "summons" still matches avatars+elementals and upgrades in the editor. Slot Configure and Pet Palette use the same type name constants. - config/efp_pets_tab.lua, palettemanager.lua, crossbar.lua, petregistry.lua: Edit Full Palette pet family tabs (Avatars, Elementals, Beasts, Wyvern, Puppet). Avatar vs spirit elemental distinction. SMN sort and pet-bar omit rules applied consistently. - macropalette.lua: Custom grid section header uses Elementals label for spirit pets. Add custom type (+) only in popup; red remove and Rename on the type row; delete confirms macro count. - core/settings/factories.lua: petPalettePetKeys defaults; comments for new type tokens. - modules/castcost/display.lua: BST Ready jug shows pet charge cost as 'Cost: N' (TP-gold color) instead of mislabeled MP when hovering spell rows fed from petregistry helpers. Why - SMN 'summons' split into avatars vs elementals for configuration and EFP so each family can be separately enabled/positioned. Custom macro categories can be managed from the type row with full cleanup of that palette bucket and revert of affected slots. BST Ready charge cost reads like gameplay (charges, not MP).
… display What changed - display.lua / data.lua: Global gConfig.petBarResizeAnchor (top vs bottom pinned edge when auto-resize changes height). PetBarResizeAnchoredBottom prefers that value and falls back to legacy per-type alignBottom until migration runs. Stable bottom-edge math for align-bottom resize. Preview Mode stripes the anchored edge. Cache lastPetBarTargetWindowHeight for snap placement. petBarTargetHitRect for cluster drag. petBarSyncResizeAnchorNextFrame for cluster drag anchor sync. - display.lua: Pet status effect icons (buffs/debuffs inflicted on the pet by enemies or self) now render correctly. Icons are drawn via drawList:AddImage on the foreground draw list so the pet bar background layer cannot occlude them. Timer text for buff durations drawn alongside. - pettarget.lua: Snap-with-top places Pet Target above the pet bar using last-frame window height plus offsets. Skips ApplyWindowPosition when snap wins. Snap Y/X defaults corrected (no sideways offset for top preset). Input-blocking (NoInputs flag) when snapped so pet bar input events do not pass through. petTargetSnapCachedHeight persisted across sessions. clearPetTargetSpatialState() on hide paths. - config/petbar.lua: Resize anchor combo above Preview Mode. Pet Target anchor help/presets ASCII-only labels. - migration.lua / user.lua: Migrate petBarResizeAnchor from legacy per-type alignBottom. Repair petTargetSnapOffsetX >= 100 when anchor is top. Default petBarResizeAnchor in factory defaults. - handlers/imgui_compat.lua: Hover detection helpers used by pet target cluster drag. - handlers/helpers.lua: ApplyWindowPosition returns true on the frame it applies a saved position so callers can distinguish load/relog from user drag. - petbuffhandler.lua: Expand statusOnMes to match debuffhandler full set (adds TARGET_* family 267-280, monster-specific 319/320, and others) so enemy-cast debuffs on pets are detected via action packets. Expand statusOffMes for correct expiry. Broaden isPetRelevant check. Why - Resize behavior is one global HUD choice regardless of Pet Target snapping or disconnected target window. Top snap aligns vertically above the pet bar instead of drifting sideways from old preset offsets. Cluster drag moves pet bar and pet target together as a single unit. Status effects on pets were silently dropped (missing message IDs) and then hidden behind the background layer even when tracked; both issues are resolved.
…/leave performance What changed - modules/partylist/display.lua: Buff/debuff split: loop changed from i=0 to i=1 through #memInfo.buffs. buffs[0] was always nil, classified as a debuff, and passed nil into native DrawStatusIcons — causing an intermittent crash on login or character swap. Break on nil, -1, and 255 terminators. Local-hoist buff/debuff arrays. (Note: this is also a bug in upstream 1.8.0; consider submitting to tirem/XIUI separately.) Align-bottom: completely rewritten to track anchorBottom explicitly (mirrors petbar/castcost). SetWindowPos is now called inside Begin/End so the correction applies the same frame; previously it fired after End() causing a one-frame lag where the party list visibly grew downward before snapping back. User drag resets the anchorBottom and triggers a deferred settings save; content height changes (member join/leave) do not trigger a disk write. activeMemberList tables are reused (cleared and repopulated) rather than discarded and recreated every frame, eliminating per-frame allocations and GC pressure. Removed per-frame zone-scan loop (party:GetMemberZone / GetMemberServerId per active slot per frame). Status icon pre-warming is driven by the 0x076 packet handler instead. - modules/partylist/data.lua: memberInfoPool: 18 pre-allocated member-info tables reused by GetMemberInformation each frame instead of allocating a new table per member per frame. Eliminates the main per-frame GC pressure source for any party size. - modules/partylist/init.lua: Pre-warm all 22 job icon textures for the active theme at Initialize and UpdateVisuals. Previously the first render frame after a new member joined would fire D3DXCreateTextureFromFile synchronously inside d3d_present if the job icon wasn't cached, causing a one-frame stutter. After pre-warming all job icons are cache hits. - handlers/statushandler.lua: Pre-warm status icon textures for all buff IDs in the 0x076 party buff packet handler. Runs off the render thread so uncached status icon disk reads no longer stall d3d_present when a member joins with buffs the cache hasn't seen yet. render_tooltip rewritten to draw directly on imgui.GetForegroundDrawList() instead of using BeginTooltip/EndTooltip. The foreground draw list composites above all ImGui windows, ensuring tooltips appear on top of the party list even when it uses NoBringToFrontOnFocus. Manual word-wrap via imtext.Measure at 260 px max width prevents description overflow. anchorBottom is now preserved across relogins: when positionJustApplied is true (first frame after appliedPositions reset) and an existing anchorBottom is saved, it is kept rather than re-seeded from the unstable first-frame height. Prevents the party list from drifting upward on relog when align-bottom is enabled. - handlers/helpers.lua: ApplyWindowPosition returns true on the frame it applies a saved position so callers (including partylist align-bottom logic) can distinguish load/relog from user drag. Why - Buff fix: eliminates intermittent crashes on login and character swap. Align-bottom fix: party and alliance lists no longer drift on join/leave; the bottom edge is now a true anchor. anchorBottom relog fix: the party list no longer jumps upward after every login when align-bottom is enabled. Tooltip fix: buff tooltips on the party list are readable at all times regardless of window z-order. Performance fixes reduce the one-frame stutter on member join/leave/zone-change by moving texture I/O off the render thread and eliminating per-frame allocations.
What changed - modules/mobinfo/data.lua: IsAspirable() function. Detects aspirable mobs via two methods: job ID check (WHM/BLM/RDM/PLD/DRK/SMN/SCH/GEO) and curated name-pattern table covering aspirable families by Horizon convention — Beetles, Buffalo, Cockatrice, Colibri, Crabs, Diremites, Elementals, Evil Weapons, Flans, Ahriman, Hecteyes, Hippogryphs, Magic Pots, Rafflesia, Worms and more. Explicit include/exclude tables for NMs and edge cases. False-positive notes inline (e.g. bare "roc" removed due to substring matches in "rock"/"ferocious"/"ferrocrab"; "cutter" removed for Tonberry Cutter; leading space on " beak" to avoid Goblin/Yagudo false matches). - modules/targetbar.lua: When showTargetBarMpStripe is on and target is a monster, calls mobdata.IsAspirable() and draws a 3px gradient stripe at the bottom of the HP bar using the mpBarGradient color. - config/targetbar.lua: Show MP Bar checkbox under target bar settings. Color picker for MP Bar gradient under Color Settings, shown only when stripe is enabled. - core/settings/user.lua: showTargetBarMpStripe = true default. - core/settings/colors.lua: mpBarGradient default (#9abb5a / #bfe07d, matches player MP bar green). Why - At-a-glance indicator on the target bar for whether /aspir is worth casting. Covers both job-based MP mobs and major aspirable creature families on Horizon.
58f44fe to
076fde1
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR consolidates all Ferris additions onto 1.8.0, fully adapted to the new ImGui/imtext rendering architecture, deferred D3D texture release, and immediate-mode state patterns. Changes are split into feature-scoped commits for reviewability.
Commit overview (25 commits)
Architecture notes
Test plan