feat: begin adding addon window positions

This commit is contained in:
Anna 2021-03-17 13:36:40 -04:00
parent 358bbe491c
commit 7e4db37b2a
10 changed files with 742 additions and 472 deletions

View File

@ -6,7 +6,7 @@ using Dalamud.Plugin;
namespace HUD_Manager.Configuration {
[Serializable]
public class Config : IPluginConfiguration {
public const int LatestVersion = 4;
public const int LatestVersion = 5;
public int Version { get; set; } = LatestVersion;
@ -15,7 +15,6 @@ namespace HUD_Manager.Configuration {
public bool FirstRun { get; set; } = true;
public bool UnderstandsRisks { get; set; }
public bool ImportPositions { get; set; }
public bool SwapsEnabled { get; set; }
public HudSlot StagingSlot { get; set; } = HudSlot.Four;

View File

@ -1,5 +1,7 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using Dalamud.Plugin;
@ -25,20 +27,34 @@ namespace HUD_Manager.Configuration {
}
}
var saved = new SavedLayout(entry.Value.Name, layout, entry.Value.Positions);
var positions = entry.Value.Positions.ToDictionary(
pos => pos.Key,
pos => new Window(
WindowComponent.X | WindowComponent.Y,
pos.Value
)
);
var saved = new SavedLayout(entry.Value.Name, layout, positions);
config.Layouts[entry.Key] = saved;
}
return config;
}
private static void WithEachElement(JObject old, Action<JObject> action) {
private static void WithEachLayout(JObject old, Action<JObject> action) {
foreach (var property in old["Layouts"].Children<JProperty>()) {
if (property.Name == "$type") {
continue;
}
var layout = (JObject) property.Value;
action(layout);
}
}
private static void WithEachElement(JObject old, Action<JObject> action) {
WithEachLayout(old, layout => {
var elements = (JObject) layout["Elements"];
foreach (var elementProp in elements.Children<JProperty>()) {
@ -50,7 +66,7 @@ namespace HUD_Manager.Configuration {
action(element);
}
}
});
}
private static void MigrateV2(JObject old) {
@ -84,6 +100,35 @@ namespace HUD_Manager.Configuration {
old["Version"] = 4;
}
private static void MigrateV4(JObject old) {
WithEachLayout(old, layout => {
var oldPositions = (JObject) layout["Positions"];
var windows = new Dictionary<string, Window>();
foreach (var elementProp in oldPositions.Children<JProperty>()) {
if (elementProp.Name == "$type") {
continue;
}
var position = (JObject) elementProp.Value;
windows[elementProp.Name] = new Window(
WindowComponent.X | WindowComponent.Y,
new Vector2<short>(
position["X"].ToObject<short>(),
position["Y"].ToObject<short>()
)
);
}
layout["Windows"] = JObject.FromObject(windows);
layout.Remove("Positions");
});
old.Remove("ImportPositions");
old["Version"] = 5;
}
private static string PluginConfig(string? pluginName = null) {
pluginName ??= Assembly.GetAssembly(typeof(Plugin)).GetName().Name;
return Path.Combine(new[] {
@ -149,6 +194,9 @@ namespace HUD_Manager.Configuration {
case 3:
MigrateV3(config);
break;
case 4:
MigrateV4(config);
break;
default:
PluginLog.Warning($"Tried to migrate from an unknown version: {version}");
goto DefaultConfig;

View File

@ -8,23 +8,30 @@ namespace HUD_Manager.Configuration {
[Serializable]
public class SavedLayout {
public Dictionary<ElementKind, Element> Elements { get; }
public Dictionary<string, Vector2<short>> Positions { get; private set; }
public Dictionary<string, Window> Windows { get; }
// public Dictionary<string, Vector2<short>> Positions { get; private set; }
public Guid Parent { get; set; } = Guid.Empty;
public string Name { get; set; }
[JsonConstructor]
public SavedLayout(string name, Dictionary<ElementKind, Element> elements, Dictionary<string, Vector2<short>> positions, Guid parent) {
public SavedLayout(string name, Dictionary<ElementKind, Element> elements, Dictionary<string, Window> windows, Guid parent) {
this.Name = name;
this.Elements = elements;
this.Positions = positions;
this.Windows = windows;
this.Parent = parent;
}
public SavedLayout(string name, Layout hud, Dictionary<string, Vector2<short>> positions) {
public SavedLayout(string name, Layout hud, Dictionary<string, Window> windows) {
this.Name = name;
this.Elements = hud.ToDictionary();
this.Positions = positions;
this.Windows = windows;
}
public SavedLayout(string name, Layout hud) {
this.Name = name;
this.Elements = hud.ToDictionary();
this.Windows = new Dictionary<string, Window>();
}
public Layout ToLayout() {

View File

@ -0,0 +1,27 @@
using System;
using Newtonsoft.Json;
namespace HUD_Manager.Configuration {
[Serializable]
public class Window {
public WindowComponent Enabled { get; set; } = WindowComponent.X | WindowComponent.Y;
public Vector2<short> Position { get; set; }
[JsonConstructor]
public Window(WindowComponent enabled, Vector2<short> position) {
this.Enabled = enabled;
this.Position = position;
}
public Window(Vector2<short> position) {
this.Position = position;
}
}
[Flags]
public enum WindowComponent {
X,
Y,
}
}

View File

@ -1,60 +1,82 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace HUD_Manager {
public class GameFunctions {
private delegate IntPtr GetUiBaseDelegate();
private delegate IntPtr GetUiWindowDelegate(IntPtr uiBase, string uiName, int index);
private delegate void MoveWindowDelegate(IntPtr windowBase, short x, short y);
// private delegate IntPtr GetBaseUiObjectDelegate();
private readonly GetUiBaseDelegate _getUiBase;
private readonly GetUiWindowDelegate _getUiWindow;
private readonly MoveWindowDelegate _moveWindow;
private delegate void SetPositionDelegate(IntPtr windowBase, short x, short y);
private delegate void SetAlphaDelegate(IntPtr windowBase, byte alpha);
private delegate byte UpdateAddonPositionDelegate(IntPtr raptureAtkUnitManager, IntPtr addon, byte clicked);
// private readonly GetBaseUiObjectDelegate _getBaseUiObject;
private readonly SetPositionDelegate _setPosition;
private readonly SetAlphaDelegate _setAlpha;
private readonly UpdateAddonPositionDelegate _updateAddonPosition;
private Plugin Plugin { get; }
public GameFunctions(Plugin plugin) {
this.Plugin = plugin;
var getUiBasePtr = this.Plugin.Interface.TargetModuleScanner.ScanText("E8 ?? ?? ?? ?? 41 b8 01 00 00 00 48 8d 15 ?? ?? ?? ?? 48 8b 48 20 e8 ?? ?? ?? ?? 48 8b cf");
var getUiWindowPtr = this.Plugin.Interface.TargetModuleScanner.ScanText("e8 ?? ?? ?? ?? 48 8b cf 48 89 87 ?? ?? 00 00 e8 ?? ?? ?? ?? 41 b8 01 00 00 00");
var moveWindowPtr = this.Plugin.Interface.TargetModuleScanner.ScanText("E8 ?? ?? ?? ?? 48 83 BB ?? ?? ?? ?? 00 74 ?? 48 8B 8B ?? ?? ?? ?? 48 85 C9 74 ?? E8 ?? ?? ?? ??");
var setPositionPtr = this.Plugin.Interface.TargetModuleScanner.ScanText("4C 8B 89 ?? ?? ?? ?? 41 0F BF C0");
var setAlphaPtr = this.Plugin.Interface.TargetModuleScanner.ScanText("F6 81 ?? ?? ?? ?? ?? 88 91 ?? ?? ?? ??");
var updatePositionPtr = this.Plugin.Interface.TargetModuleScanner.ScanText("E8 ?? ?? ?? ?? 48 8B 8B ?? ?? ?? ?? 33 D2 48 8B 01 FF 90 ?? ?? ?? ??");
// var baseUiPtr = this.Plugin.Interface.TargetModuleScanner.ScanText("E8 ?? ?? ?? ?? 0F BF D5");
this._getUiBase = Marshal.GetDelegateForFunctionPointer<GetUiBaseDelegate>(getUiBasePtr);
this._getUiWindow = Marshal.GetDelegateForFunctionPointer<GetUiWindowDelegate>(getUiWindowPtr);
this._moveWindow = Marshal.GetDelegateForFunctionPointer<MoveWindowDelegate>(moveWindowPtr);
this._setPosition = Marshal.GetDelegateForFunctionPointer<SetPositionDelegate>(setPositionPtr);
this._setAlpha = Marshal.GetDelegateForFunctionPointer<SetAlphaDelegate>(setAlphaPtr);
this._updateAddonPosition = Marshal.GetDelegateForFunctionPointer<UpdateAddonPositionDelegate>(updatePositionPtr);
}
private IntPtr GetUiBase() {
return this._getUiBase.Invoke();
}
private IntPtr GetUiWindow(string uiName, int index) {
var uiBase = this.GetUiBase();
var offset = Marshal.ReadIntPtr(uiBase, 0x20);
return this._getUiWindow.Invoke(offset, uiName, index);
}
public void MoveWindow(string uiName, short x, short y) {
var windowBase = this.GetUiWindow(uiName, 1);
if (windowBase == IntPtr.Zero) {
public void SetAddonPosition(string uiName, short x, short y) {
var addon = this.Plugin.Interface.Framework.Gui.GetAddonByName(uiName, 1);
if (addon == null) {
return;
}
this._moveWindow.Invoke(windowBase, x, y);
var baseUi = this.Plugin.Interface.Framework.Gui.GetBaseUIObject();
var manager = Marshal.ReadIntPtr(baseUi + 0x20);
this._updateAddonPosition(
manager,
addon.Address,
1
);
this._setPosition(addon.Address, x, y);
this._updateAddonPosition(
manager,
addon.Address,
0
);
}
public Vector2<short>? GetWindowPosition(string uiName) {
var windowBase = this.GetUiWindow(uiName, 1);
if (windowBase == IntPtr.Zero) {
public Vector2<short>? GetAddonPosition(string uiName) {
var addon = this.Plugin.Interface.Framework.Gui.GetAddonByName(uiName, 1);
if (addon == null) {
return null;
}
var x = Marshal.ReadInt16(windowBase + 0x1bc);
var y = Marshal.ReadInt16(windowBase + 0x1bc + 2);
try {
var x = addon.X;
var y = addon.Y;
return new Vector2<short>(x, y);
return new Vector2<short>(x, y);
} catch (KeyNotFoundException) {
return null;
}
}
public void SetAddonAlpha(string name, byte alpha) {
var addon = this.Plugin.Interface.Framework.Gui.GetAddonByName(name, 1);
if (addon == null) {
return;
}
this._setAlpha(addon.Address, alpha);
}
}
}

View File

@ -41,10 +41,15 @@ namespace HUD_Manager {
}
}
private IntPtr GetFilePointer(byte index) {
public IntPtr GetFilePointer(byte index) {
return this._getFilePointer?.Invoke(index) ?? IntPtr.Zero;
}
public void SaveAddonData() {
var saveMarker = this.GetFilePointer(0) + 0x3E;
Marshal.WriteByte(saveMarker, 1);
}
public void SelectSlot(HudSlot slot, bool force = false) {
if (this._setHudLayout == null) {
return;
@ -93,6 +98,10 @@ namespace HUD_Manager {
return Marshal.ReadIntPtr(dataPtr);
}
internal IntPtr GetDefaultLayoutPointer() {
return this.GetDataPointer() + 0x1c4;
}
internal IntPtr GetLayoutPointer(HudSlot slot) {
var slotNum = (int) slot;
return this.GetDataPointer() + 0x2c58 + slotNum * LayoutSize;
@ -196,8 +205,8 @@ namespace HUD_Manager {
}
public class Vector2<T> {
public T X { get; }
public T Y { get; }
public T X { get; set; }
public T Y { get; set; }
public Vector2(T x, T y) {
this.X = x;

File diff suppressed because it is too large Load Diff

View File

@ -89,8 +89,8 @@ namespace HUD_Manager {
this.Plugin.Hud.WriteEffectiveLayout(this.Plugin.Config.StagingSlot, layoutId);
this.Plugin.Hud.SelectSlot(this.Plugin.Config.StagingSlot, true);
foreach (var entry in layout.Positions) {
this.Plugin.GameFunctions.MoveWindow(entry.Key, entry.Value.X, entry.Value.Y);
foreach (var entry in layout.Windows) {
this.Plugin.GameFunctions.SetAddonPosition(entry.Key, entry.Value.Position.X, entry.Value.Position.Y);
}
}
}

View File

@ -1,4 +1,7 @@
using Dalamud.Data;
using System;
using System.Collections.Generic;
using System.Linq;
using Dalamud.Data;
using HUD_Manager.Lumina;
using Lumina.Excel.GeneratedSheets;
@ -79,9 +82,25 @@ namespace HUD_Manager.Structs {
TheFeastAllyInfo = 933766972,
TheFeastScore = 3622852831,
BattleHighGauge = 884971695,
LeftWCrossHotbar = 1717924701,
RightWCrossHotbar = 1893596455,
OceanFishingVoyageMissions = 1917955123,
Timers = 2578885979,
}
public static class ElementKindExt {
public static readonly ElementKind[] Immutable = {
// cannot be moved with the current method the plugin is using
ElementKind.OceanFishingVoyageMissions,
// don't actually know if this is immutable, but idk what it is
ElementKind.Timers,
};
public static IEnumerable<ElementKind> All() => Enum.GetValues(typeof(ElementKind))
.Cast<ElementKind>()
.Where(kind => !Immutable.Contains(kind));
public static string LocalisedName(this ElementKind kind, DataManager data) {
uint? id = kind switch {
ElementKind.Hotbar1 => 0,
@ -117,8 +136,10 @@ namespace HUD_Manager.Structs {
ElementKind.AllianceList1 => 30,
ElementKind.AllianceList2 => 31,
// ElementKind.DutyList => 32, // listed twice?
// 33 is "Timers"
// 34-39 empty
ElementKind.Timers => 33,
// 34-37 empty
ElementKind.LeftWCrossHotbar => 38,
ElementKind.RightWCrossHotbar => 39,
ElementKind.OathGauge => 40,
// 41 is "LightningGauge" - guessing that's for GL
ElementKind.BeastGauge => 42,
@ -164,7 +185,7 @@ namespace HUD_Manager.Structs {
ElementKind.BattleHighGauge => 82,
ElementKind.NewGamePlusGuide => 83,
ElementKind.CompressedAether => 84,
// 84 is "Ocean Fishing: Voyage Missions"
ElementKind.OceanFishingVoyageMissions => 85,
_ => null,
};

View File

@ -0,0 +1,42 @@
namespace HUD_Manager.Structs {
public enum WindowKind : uint {
FreeCompany = 3769291431,
}
public static class WindowKindExt {
public static readonly string[] All = {
"AreaMap",
"ChatLog",
"ChatLogPanel_0",
"ChatLogPanel_1",
"ChatLogPanel_2",
"ChatLogPanel_3",
"InventoryExpansion",
"InventoryLarge",
"Inventory",
"InventoryBuddy", // chocobo saddlebag
"ArmouryBoard", // armoury chest
"FreeCompany",
"Character",
"Currency",
"ContentsInfo", // timers
"ContentsFinder", // duty finder
"RaidFinder", // raid finder
"LookingForGroup", // party finder
"Macro",
"RecipeNote", // crafting log
"GatheringNote", // gathering log
"ActionMenu", // actions & traits
"Achievement",
"MountNoteBook", // mount guide
"MinionNoteBook", // minion guide
"OrnamentNoteBook", // fashion accessories
"AOZNotebook", // blue magic spellbook
"SystemMenu",
"PvpProfile", // PvP Profile
"GoldSaucerInfo",
"Journal",
"Teleport",
};
}
}