Initial release: HSCompare v1.0.2 - WoW-style equipment comparison tooltips

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Dawnsorrow
2026-02-15 23:37:05 -06:00
commit c87c3ba8f8
14 changed files with 926 additions and 0 deletions
+118
View File
@@ -0,0 +1,118 @@
# HSCompare Plugin Layout and Implementation Plan
## Overview
HSCompare is a Dalamud plugin for FFXIV that **replaces or augments equipment tooltips** with a comparison view. When the user hovers over an equipment item (armor/weapon) and holds a configurable modifier key (default: Shift), the plugin shows:
1. **Hovered item** the item under the cursor
2. **Currently equipped item** the item in the same equipment slot
3. **Total stat difference** below the tooltip, showing the net stat change if the hovered item were equipped (WoW-style: green for gains, red for losses)
If the hovered item is already equipped, or the item is not equipment, the default game tooltip behavior is unchanged (no replacement).
---
## Architecture
### Components
| Component | Purpose |
|-----------|--------|
| **Plugin.cs** | Entry point. Subscribes to `IGameGui.HoveredItemChanged`, registers `/hscompare` command, injects services, and draws UI each frame. |
| **Configuration.cs** | Persisted settings: modifier key (VirtualKey), tooltip font size, comparison window size (width/scale). |
| **PluginUI.cs** | ImGui logic: draws comparison window when conditions are met, applies font/size from config; config window is in the same class. |
| **ItemBonusType.cs** | Enum of FFXIV base param IDs with display names (Strength, Vitality, etc.). |
### Data Flow
1. **Hover**`IGameGui.HoveredItemChanged` fires with item ID (and HQ flag via offset 1_000_000).
2. **Resolve item**`IDataManager.GetExcelSheet<Item>().GetRow(itemId)``Item` + HQ flag → wrapped as `InvItem`.
3. **Is equipment?**`Item.EquipSlotCategory` non-null and not “none” (e.g. MainHand, Head, Body).
4. **Map to slot**`EquipSlotCategory` → FFXIVClientStructs `InventoryType` (e.g. ArmoryMainHand, ArmoryHead).
5. **Get equipped**`InventoryManager.Instance()->GetInventoryContainer(InventoryType.EquippedItems)`, scan by slot; get `Item` for same slot.
6. **Compare** → Only show comparison if hovered item is **not** the same as equipped (different RowId or slot). Build stat maps for both items (base params + defense/damage/block), then diff.
7. **Key check** → Each frame in Draw: `IKeyState[Configuration.ModifierKey]` (or fallback to Win32 `GetKeyState` for the configured VK). Only show comparison window when key is held.
---
## Implementation Details
### 1. Tooltip “replacement”
- The games own tooltip is **not** suppressed (no public Dalamud API to hide it). The plugin draws an **additional** ImGui window when comparison is active.
- **Layout**: One window or two side-by-side:
- **Left**: Hovered item (name, ilvl, main stats, substats).
- **Right**: “Currently Equipped” (same fields).
- **Below** (or bottom section of same window): “If you replace this item, the following stat changes will occur:” then list of stat deltas (e.g. “+12 Strength”, “-5 Critical Hit”) in green/red.
- Position: near cursor or below default tooltip (e.g. cursor + offset so the comparison appears below the game tooltip).
### 2. Modifier key (configurable)
- **Config**: Store `VirtualKey` (e.g. `VirtualKey.SHIFT`). Default: Shift.
- **Runtime**: In `PluginUI.Draw()`, check `IKeyState[config.ModifierKey]`. If not pressed, do not draw comparison.
- **Config UI**: Dropdown or key selector for modifier (list common: Shift, Ctrl, Alt).
### 3. Font and window size (configurable)
- **Font**: `ImGui.GetIO().Fonts` push a scaled font (e.g. `ImGui.GetFont()->FontSize * config.TooltipFontScale`) before drawing the comparison window; pop after.
- **Window size**: `ImGui.SetWindowSize()` or use `ImGuiWindowFlags.AlwaysAutoResize` and control width via `ImGui.PushItemWidth()` / text wrap width, or store a “max width” in config and use it for wrapping. Alternatively, a simple “scale” factor that scales both font and padding.
### 4. Stat difference (total change when equipping hovered item)
- **Meaning**: “Total stat change” = (hovered item stats) (equipped item stats). Positive = gain if you equip hovered, negative = loss.
- **Sources for stats**:
- **Substats**: `Item.UnkData59` (BaseParam, BaseParamValue). If HQ, add `Item.UnkData73` (BaseParamValueSpecial) for the same BaseParamSpecial.
- **Main stats**: Defense (Phys/Mag), Damage (Phys/Mag), Block, BlockRate from `Item` (mapped into the same “bonus” map by type, as in SimpleCompare).
- **Display**: One line per stat that has a non-zero delta: “+12 Strength”, “-5 Critical Hit”, “-0.7 Damage Per Second” (if we compute DPS or just “Physical Damage”). Use `ImGui.ColoredText` green for positive, red for negative.
- **Materia**: Optional: show materia slot count difference (e.g. “+1 Materia slot”).
### 5. Equipment slot mapping
- Use `Item.EquipSlotCategory.Value` (Lumina): MainHand, OffHand, Head, Body, Hands, Waist, Legs, Feet, Ears, Neck, Wrists, FingerL, FingerR, SoulCrystal.
- Map to `FFXIVClientStructs.FFXIV.Client.Game.InventoryType`: ArmoryMainHand, ArmoryOffHand, ArmoryHead, ArmoryBody, ArmoryHands, ArmoryWaist, ArmoryLegs, ArmoryFeets, ArmoryEar, ArmoryNeck, ArmoryWrist, ArmoryRings (both fingers), ArmorySoulCrystal.
- **Rings**: Two slots; we compare against “the slot this item would go in” e.g. first ring slot that matches. Simple approach: compare hovered to both equipped rings and show the one that matches slot or the first.
### 6. Edge cases
- **Soul crystal / non-equipment**: Ignore (no comparison).
- **Same item equipped**: If hovered item RowId equals equipped item RowId, optionally hide comparison or show “No change”.
- **Empty slot**: If nothing equipped in that slot, show only hovered item and “Currently equipped: (empty)” with stat list = hovered items stats as gains.
---
## File Structure
```
HSCompare/
├── HSCompare.sln
├── HSCompare/
│ ├── HSCompare.csproj
│ ├── HSCompare.json # Manifest template for DalamudPackager
│ ├── Plugin.cs
│ ├── Configuration.cs
│ ├── PluginUI.cs
│ ├── ItemBonusType.cs
└── PLUGIN_LAYOUT.md # This file
```
---
## Dependencies
- **Dalamud** (via `$(DalamudLibPath)`): Dalamud.dll, ImGui.NET.dll, ImGuiScene.dll, Lumina.dll, Lumina.Excel.dll, Newtonsoft.Json.dll.
- **FFXIVClientStructs**: For `InventoryManager`, `InventoryType`, `InventoryItem` (equipped item IDs and HQ flag).
---
## Summary
| Requirement | Implementation |
|-------------|----------------|
| Replace/augment equipment tooltips | Draw ImGui comparison window when modifier held and hovered item is equipment. |
| Compare to equipped when not equipped | Resolve slot from `EquipSlotCategory`, read equipped from `InventoryManager`, show both. |
| Configurable key | Configuration.ModifierKey (VirtualKey), checked via IKeyState. |
| Configurable font and window size | Config: font scale and/or window width; apply in PluginUI before drawing. |
| Total stat difference below tooltip | Section “If you replace this item…” with (hovered equipped) deltas, green/red. |
| WoW-like comparison | Two blocks (hovered | equipped) + stat change list; color-coded. |
This layout keeps the plugin modular, testable, and aligned with existing Dalamud and SimpleCompare patterns.