mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2025-12-15 05:04:16 +01:00
.
This commit is contained in:
parent
b63b02ae5e
commit
60443f6a53
17 changed files with 970 additions and 225 deletions
|
|
@ -1,6 +1,5 @@
|
|||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Security.AccessControl;
|
||||
using Glamourer.Customization;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Events;
|
||||
|
|
@ -9,8 +8,7 @@ using Glamourer.Interop.Structs;
|
|||
using Glamourer.Services;
|
||||
using Glamourer.State;
|
||||
using Glamourer.Structs;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Glamourer.Unlocks;
|
||||
using Penumbra.GameData.Actors;
|
||||
using Penumbra.GameData.Enums;
|
||||
|
||||
|
|
@ -18,25 +16,29 @@ namespace Glamourer.Automation;
|
|||
|
||||
public class AutoDesignApplier : IDisposable
|
||||
{
|
||||
private readonly Configuration _config;
|
||||
private readonly AutoDesignManager _manager;
|
||||
private readonly PhrasingService _phrasing;
|
||||
private readonly StateManager _state;
|
||||
private readonly JobService _jobs;
|
||||
private readonly ActorService _actors;
|
||||
private readonly CustomizationService _customizations;
|
||||
private readonly Configuration _config;
|
||||
private readonly AutoDesignManager _manager;
|
||||
private readonly CodeService _code;
|
||||
private readonly StateManager _state;
|
||||
private readonly JobService _jobs;
|
||||
private readonly ActorService _actors;
|
||||
private readonly CustomizationService _customizations;
|
||||
private readonly CustomizeUnlockManager _customizeUnlocks;
|
||||
private readonly ItemUnlockManager _itemUnlocks;
|
||||
|
||||
public AutoDesignApplier(Configuration config, AutoDesignManager manager, PhrasingService phrasing, StateManager state, JobService jobs,
|
||||
CustomizationService customizations, ActorService actors)
|
||||
public AutoDesignApplier(Configuration config, AutoDesignManager manager, CodeService code, StateManager state, JobService jobs,
|
||||
CustomizationService customizations, ActorService actors, ItemUnlockManager itemUnlocks, CustomizeUnlockManager customizeUnlocks)
|
||||
{
|
||||
_config = config;
|
||||
_manager = manager;
|
||||
_phrasing = phrasing;
|
||||
_state = state;
|
||||
_jobs = jobs;
|
||||
_customizations = customizations;
|
||||
_actors = actors;
|
||||
_jobs.JobChanged += OnJobChange;
|
||||
_config = config;
|
||||
_manager = manager;
|
||||
_code = code;
|
||||
_state = state;
|
||||
_jobs = jobs;
|
||||
_customizations = customizations;
|
||||
_actors = actors;
|
||||
_itemUnlocks = itemUnlocks;
|
||||
_customizeUnlocks = customizeUnlocks;
|
||||
_jobs.JobChanged += OnJobChange;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
|
@ -79,7 +81,7 @@ public class AutoDesignApplier : IDisposable
|
|||
private unsafe void Reduce(Actor actor, ActorState state, AutoDesignSet set, bool respectManual)
|
||||
{
|
||||
EquipFlag totalEquipFlags = 0;
|
||||
var totalCustomizeFlags = _phrasing.Phrasing2 ? 0 : CustomizeFlagExtensions.RedrawRequired;
|
||||
var totalCustomizeFlags = _code.EnabledMesmer ? 0 : CustomizeFlagExtensions.RedrawRequired;
|
||||
byte totalMetaFlags = 0;
|
||||
foreach (var design in set.Designs)
|
||||
{
|
||||
|
|
@ -118,15 +120,18 @@ public class AutoDesignApplier : IDisposable
|
|||
if (equipFlags == 0)
|
||||
return;
|
||||
|
||||
// TODO add item conditions
|
||||
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
||||
{
|
||||
var flag = slot.ToFlag();
|
||||
if (equipFlags.HasFlag(flag))
|
||||
{
|
||||
if (!respectManual || state[slot, false] is not StateChanged.Source.Manual)
|
||||
_state.ChangeItem(state, slot, design.Item(slot), StateChanged.Source.Fixed);
|
||||
totalEquipFlags |= flag;
|
||||
var item = design.Item(slot);
|
||||
if (_code.EnabledInventory || _itemUnlocks.IsUnlocked(item.Id, out _))
|
||||
{
|
||||
if (!respectManual || state[slot, false] is not StateChanged.Source.Manual)
|
||||
_state.ChangeItem(state, slot, item, StateChanged.Source.Fixed);
|
||||
totalEquipFlags |= flag;
|
||||
}
|
||||
}
|
||||
|
||||
var stainFlag = slot.ToStainFlag();
|
||||
|
|
@ -141,7 +146,8 @@ public class AutoDesignApplier : IDisposable
|
|||
if (equipFlags.HasFlag(EquipFlag.Mainhand))
|
||||
{
|
||||
var item = design.Item(EquipSlot.MainHand);
|
||||
if (state.ModelData.Item(EquipSlot.MainHand).Type == item.Type)
|
||||
if (state.ModelData.Item(EquipSlot.MainHand).Type == item.Type
|
||||
&& (_code.EnabledInventory || _itemUnlocks.IsUnlocked(item.Id, out _)))
|
||||
{
|
||||
if (!respectManual || state[EquipSlot.MainHand, false] is not StateChanged.Source.Manual)
|
||||
_state.ChangeItem(state, EquipSlot.MainHand, item, StateChanged.Source.Fixed);
|
||||
|
|
@ -152,7 +158,8 @@ public class AutoDesignApplier : IDisposable
|
|||
if (equipFlags.HasFlag(EquipFlag.Offhand))
|
||||
{
|
||||
var item = design.Item(EquipSlot.OffHand);
|
||||
if (state.ModelData.Item(EquipSlot.OffHand).Type == item.Type)
|
||||
if (state.ModelData.Item(EquipSlot.OffHand).Type == item.Type
|
||||
&& (_code.EnabledInventory || _itemUnlocks.IsUnlocked(item.Id, out _)))
|
||||
{
|
||||
if (!respectManual || state[EquipSlot.OffHand, false] is not StateChanged.Source.Manual)
|
||||
_state.ChangeItem(state, EquipSlot.OffHand, item, StateChanged.Source.Fixed);
|
||||
|
|
@ -220,7 +227,8 @@ public class AutoDesignApplier : IDisposable
|
|||
continue;
|
||||
|
||||
var value = design.Customize[index];
|
||||
if (CustomizationService.IsCustomizationValid(set, face, index, value))
|
||||
if (CustomizationService.IsCustomizationValid(set, face, index, value, out var data)
|
||||
&& (_code.EnabledInventory || _customizeUnlocks.IsUnlocked(data.Value, out _)))
|
||||
{
|
||||
if (!respectManual || state[index] is not StateChanged.Source.Manual)
|
||||
_state.ChangeCustomize(state, index, value, StateChanged.Source.Fixed);
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ public class Configuration : IPluginConfiguration, ISavable
|
|||
public bool AutoRedrawEquipOnChanges { get; set; } = false;
|
||||
public bool EnableAutoDesigns { get; set; } = true;
|
||||
public bool IncognitoMode { get; set; } = false;
|
||||
public bool UnlockDetailMode { get; set; } = true;
|
||||
public MainWindow.TabType SelectedTab { get; set; } = MainWindow.TabType.Settings;
|
||||
public DoubleModifier DeleteDesignModifier { get; set; } = new(ModifierHotkey.Control, ModifierHotkey.Shift);
|
||||
|
||||
|
|
@ -30,8 +31,7 @@ public class Configuration : IPluginConfiguration, ISavable
|
|||
[JsonProperty(Order = int.MaxValue)]
|
||||
public ISortMode<Design> SortMode { get; set; } = ISortMode<Design>.FoldersFirst;
|
||||
|
||||
public string Phrasing1 { get; set; } = string.Empty;
|
||||
public string Phrasing2 { get; set; } = string.Empty;
|
||||
public List<(string Code, bool Enabled)> Codes { get; set; } = new List<(string Code, bool Enabled)>();
|
||||
|
||||
#if DEBUG
|
||||
public bool DebugMode { get; set; } = true;
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ using Glamourer.Gui.Tabs;
|
|||
using Glamourer.Gui.Tabs.ActorTab;
|
||||
using Glamourer.Gui.Tabs.AutomationTab;
|
||||
using Glamourer.Gui.Tabs.DesignTab;
|
||||
using Glamourer.Gui.Tabs.UnlocksTab;
|
||||
using ImGuiNET;
|
||||
using OtterGui.Custom;
|
||||
using OtterGui.Widgets;
|
||||
|
|
@ -22,6 +23,7 @@ public class MainWindow : Window
|
|||
Actors = 2,
|
||||
Designs = 3,
|
||||
Automation = 4,
|
||||
Unlocks = 5,
|
||||
}
|
||||
|
||||
private readonly Configuration _config;
|
||||
|
|
@ -32,11 +34,12 @@ public class MainWindow : Window
|
|||
public readonly DebugTab Debug;
|
||||
public readonly DesignTab Designs;
|
||||
public readonly AutomationTab Automation;
|
||||
public readonly UnlocksTab Unlocks;
|
||||
|
||||
public TabType SelectTab = TabType.None;
|
||||
|
||||
public MainWindow(DalamudPluginInterface pi, Configuration config, SettingsTab settings, ActorTab actors, DesignTab designs,
|
||||
DebugTab debugTab, AutomationTab automation)
|
||||
DebugTab debugTab, AutomationTab automation, UnlocksTab unlocks)
|
||||
: base(GetLabel())
|
||||
{
|
||||
pi.UiBuilder.DisableGposeUiHide = true;
|
||||
|
|
@ -50,6 +53,7 @@ public class MainWindow : Window
|
|||
Designs = designs;
|
||||
Automation = automation;
|
||||
Debug = debugTab;
|
||||
Unlocks = unlocks;
|
||||
_config = config;
|
||||
_tabs = new ITab[]
|
||||
{
|
||||
|
|
@ -57,6 +61,7 @@ public class MainWindow : Window
|
|||
actors,
|
||||
designs,
|
||||
automation,
|
||||
unlocks,
|
||||
debugTab,
|
||||
};
|
||||
|
||||
|
|
@ -81,6 +86,7 @@ public class MainWindow : Window
|
|||
TabType.Actors => Actors.Label,
|
||||
TabType.Designs => Designs.Label,
|
||||
TabType.Automation => Automation.Label,
|
||||
TabType.Unlocks => Unlocks.Label,
|
||||
_ => ReadOnlySpan<byte>.Empty,
|
||||
};
|
||||
|
||||
|
|
@ -91,6 +97,7 @@ public class MainWindow : Window
|
|||
if (label == Designs.Label) return TabType.Designs;
|
||||
if (label == Settings.Label) return TabType.Settings;
|
||||
if (label == Automation.Label) return TabType.Automation;
|
||||
if (label == Unlocks.Label) return TabType.Unlocks;
|
||||
if (label == Debug.Label) return TabType.Debug;
|
||||
// @formatter:on
|
||||
return TabType.None;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,15 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using Glamourer.Events;
|
||||
using Glamourer.Interop;
|
||||
using Glamourer.Interop.Penumbra;
|
||||
using Glamourer.Services;
|
||||
using Glamourer.State;
|
||||
using Glamourer.Structs;
|
||||
using ImGuiNET;
|
||||
using OtterGui.Raii;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
|
@ -45,10 +48,115 @@ public class PenumbraChangedItemTooltip : IDisposable
|
|||
_penumbra.Click -= OnPenumbraClick;
|
||||
}
|
||||
|
||||
public bool Player()
|
||||
=> _objects.Player.Valid;
|
||||
|
||||
public bool Player([NotNullWhen(true)] out ActorState? player)
|
||||
{
|
||||
var (identifier, data) = _objects.PlayerData;
|
||||
if (!data.Valid || !_stateManager.GetOrCreate(identifier, data.Objects[0], out player))
|
||||
{
|
||||
player = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void CreateTooltip(EquipItem item, string prefix, bool openTooltip)
|
||||
{
|
||||
var slot = item.Type.ToSlot();
|
||||
var last = _lastItems[slot.ToIndex()];
|
||||
switch (slot)
|
||||
{
|
||||
case EquipSlot.MainHand when !CanApplyWeapon(EquipSlot.MainHand, item):
|
||||
case EquipSlot.OffHand when !CanApplyWeapon(EquipSlot.OffHand, item):
|
||||
break;
|
||||
case EquipSlot.RFinger:
|
||||
using (var tt = !openTooltip ? null : ImRaii.Tooltip())
|
||||
{
|
||||
ImGui.TextUnformatted($"{prefix}Right-Click to apply to current actor (Right Finger).");
|
||||
ImGui.TextUnformatted($"{prefix}Shift + Right-Click to apply to current actor (Left Finger).");
|
||||
if (last.Valid)
|
||||
ImGui.TextUnformatted(
|
||||
$"{prefix}Control + Right-Click to re-apply {last.Name} to current actor (Right Finger).");
|
||||
|
||||
var last2 = _lastItems[EquipSlot.LFinger.ToIndex()];
|
||||
if (last2.Valid)
|
||||
ImGui.TextUnformatted(
|
||||
$"{prefix}Shift + Control + Right-Click to re-apply {last.Name} to current actor (Left Finger).");
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
using (var tt = !openTooltip ? null : ImRaii.Tooltip())
|
||||
{
|
||||
ImGui.TextUnformatted($"{prefix}Right-Click to apply to current actor.");
|
||||
if (last.Valid)
|
||||
ImGui.TextUnformatted($"{prefix}Control + Right-Click to re-apply {last.Name} to current actor.");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void ApplyItem(ActorState state, EquipItem item)
|
||||
{
|
||||
var slot = item.Type.ToSlot();
|
||||
var last = _lastItems[slot.ToIndex()];
|
||||
switch (slot)
|
||||
{
|
||||
case EquipSlot.MainHand when !CanApplyWeapon(EquipSlot.MainHand, item):
|
||||
case EquipSlot.OffHand when !CanApplyWeapon(EquipSlot.OffHand, item):
|
||||
break;
|
||||
case EquipSlot.RFinger:
|
||||
switch (ImGui.GetIO().KeyCtrl, ImGui.GetIO().KeyShift)
|
||||
{
|
||||
case (false, false):
|
||||
Glamourer.Log.Information($"Applying {item.Name} to Right Finger.");
|
||||
SetLastItem(EquipSlot.RFinger, item, state);
|
||||
_stateManager.ChangeItem(state, EquipSlot.RFinger, item, StateChanged.Source.Manual);
|
||||
break;
|
||||
case (false, true):
|
||||
Glamourer.Log.Information($"Applying {item.Name} to Left Finger.");
|
||||
SetLastItem(EquipSlot.LFinger, item, state);
|
||||
_stateManager.ChangeItem(state, EquipSlot.LFinger, item, StateChanged.Source.Manual);
|
||||
break;
|
||||
case (true, false) when last.Valid:
|
||||
Glamourer.Log.Information($"Re-Applying {last.Name} to Right Finger.");
|
||||
SetLastItem(EquipSlot.RFinger, default, state);
|
||||
_stateManager.ChangeItem(state, EquipSlot.RFinger, last, StateChanged.Source.Manual);
|
||||
break;
|
||||
case (true, true) when _lastItems[EquipSlot.LFinger.ToIndex()].Valid:
|
||||
Glamourer.Log.Information($"Re-Applying {last.Name} to Left Finger.");
|
||||
SetLastItem(EquipSlot.LFinger, default, state);
|
||||
_stateManager.ChangeItem(state, EquipSlot.LFinger, last, StateChanged.Source.Manual);
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
default:
|
||||
if (ImGui.GetIO().KeyCtrl && last.Valid)
|
||||
{
|
||||
Glamourer.Log.Information($"Re-Applying {last.Name} to {slot.ToName()}.");
|
||||
SetLastItem(slot, default, state);
|
||||
_stateManager.ChangeItem(state, slot, last, StateChanged.Source.Manual);
|
||||
}
|
||||
else
|
||||
{
|
||||
Glamourer.Log.Information($"Applying {item.Name} to {slot.ToName()}.");
|
||||
SetLastItem(slot, item, state);
|
||||
_stateManager.ChangeItem(state, slot, item, StateChanged.Source.Manual);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPenumbraTooltip(ChangedItemType type, uint id)
|
||||
{
|
||||
LastTooltip = DateTime.UtcNow;
|
||||
if (!_objects.Player.Valid)
|
||||
if (!Player())
|
||||
return;
|
||||
|
||||
switch (type)
|
||||
|
|
@ -57,33 +165,7 @@ public class PenumbraChangedItemTooltip : IDisposable
|
|||
if (!_items.ItemService.AwaitedService.TryGetValue(id, out var item))
|
||||
return;
|
||||
|
||||
var slot = item.Type.ToSlot();
|
||||
var last = _lastItems[slot.ToIndex()];
|
||||
switch (slot)
|
||||
{
|
||||
case EquipSlot.MainHand when !CanApplyWeapon(EquipSlot.MainHand, item):
|
||||
case EquipSlot.OffHand when !CanApplyWeapon(EquipSlot.OffHand, item):
|
||||
break;
|
||||
case EquipSlot.RFinger:
|
||||
ImGui.TextUnformatted("[Glamourer] Right-Click to apply to current actor (Right Finger).");
|
||||
ImGui.TextUnformatted("[Glamourer] Shift + Right-Click to apply to current actor (Left Finger).");
|
||||
if (last.Valid)
|
||||
ImGui.TextUnformatted(
|
||||
$"[Glamourer] Control + Right-Click to re-apply {last.Name} to current actor (Right Finger).");
|
||||
|
||||
var last2 = _lastItems[EquipSlot.LFinger.ToIndex()];
|
||||
if (last2.Valid)
|
||||
ImGui.TextUnformatted(
|
||||
$"[Glamourer] Shift + Control + Right-Click to re-apply {last.Name} to current actor (Left Finger).");
|
||||
|
||||
break;
|
||||
default:
|
||||
ImGui.TextUnformatted("[Glamourer] Right-Click to apply to current actor.");
|
||||
if (last.Valid)
|
||||
ImGui.TextUnformatted($"[Glamourer] Control + Right-Click to re-apply {last.Name} to current actor.");
|
||||
break;
|
||||
}
|
||||
|
||||
CreateTooltip(item, "[Glamourer] ", false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -107,60 +189,13 @@ public class PenumbraChangedItemTooltip : IDisposable
|
|||
if (button is not MouseButton.Right)
|
||||
return;
|
||||
|
||||
var (identifier, data) = _objects.PlayerData;
|
||||
if (!data.Valid)
|
||||
return;
|
||||
|
||||
if (!_stateManager.GetOrCreate(identifier, data.Objects[0], out var state))
|
||||
if (!Player(out var state))
|
||||
return;
|
||||
|
||||
if (!_items.ItemService.AwaitedService.TryGetValue(id, out var item))
|
||||
return;
|
||||
|
||||
var slot = item.Type.ToSlot();
|
||||
var last = _lastItems[slot.ToIndex()];
|
||||
switch (slot)
|
||||
{
|
||||
case EquipSlot.MainHand when !CanApplyWeapon(EquipSlot.MainHand, item):
|
||||
case EquipSlot.OffHand when !CanApplyWeapon(EquipSlot.OffHand, item):
|
||||
break;
|
||||
case EquipSlot.RFinger:
|
||||
switch (ImGui.GetIO().KeyCtrl, ImGui.GetIO().KeyShift)
|
||||
{
|
||||
case (false, false):
|
||||
Glamourer.Log.Information($"Applying {item.Name} to Right Finger.");
|
||||
SetLastItem(EquipSlot.RFinger, item, state);
|
||||
break;
|
||||
case (false, true):
|
||||
Glamourer.Log.Information($"Applying {item.Name} to Left Finger.");
|
||||
SetLastItem(EquipSlot.LFinger, item, state);
|
||||
break;
|
||||
case (true, false) when last.Valid:
|
||||
Glamourer.Log.Information($"Re-Applying {last.Name} to Right Finger.");
|
||||
SetLastItem(EquipSlot.RFinger, default, state);
|
||||
break;
|
||||
case (true, true) when _lastItems[EquipSlot.LFinger.ToIndex()].Valid:
|
||||
Glamourer.Log.Information($"Re-Applying {last.Name} to Left Finger.");
|
||||
SetLastItem(EquipSlot.LFinger, default, state);
|
||||
break;
|
||||
}
|
||||
|
||||
return;
|
||||
default:
|
||||
if (ImGui.GetIO().KeyCtrl && last.Valid)
|
||||
{
|
||||
Glamourer.Log.Information($"Re-Applying {last.Name} to {slot.ToName()}.");
|
||||
SetLastItem(slot, default, state);
|
||||
}
|
||||
else
|
||||
{
|
||||
Glamourer.Log.Information($"Applying {item.Name} to {slot.ToName()}.");
|
||||
SetLastItem(slot, item, state);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
ApplyItem(state, item);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ public unsafe class DebugTab : ITab
|
|||
private readonly ObjectTable _objects;
|
||||
private readonly ObjectManager _objectManager;
|
||||
private readonly GlamourerIpc _ipc;
|
||||
private readonly PhrasingService _phrasing;
|
||||
private readonly CodeService _code;
|
||||
|
||||
private readonly ItemManager _items;
|
||||
private readonly ActorService _actors;
|
||||
|
|
@ -69,7 +69,7 @@ public unsafe class DebugTab : ITab
|
|||
ActorService actors, ItemManager items, CustomizationService customization, ObjectManager objectManager,
|
||||
DesignFileSystem designFileSystem, DesignManager designManager, StateManager state, Configuration config,
|
||||
PenumbraChangedItemTooltip penumbraTooltip, MetaService metaService, GlamourerIpc ipc, DalamudPluginInterface pluginInterface,
|
||||
AutoDesignManager autoDesignManager, JobService jobs, PhrasingService phrasing, CustomizeUnlockManager customizeUnlocks,
|
||||
AutoDesignManager autoDesignManager, JobService jobs, CodeService code, CustomizeUnlockManager customizeUnlocks,
|
||||
ItemUnlockManager itemUnlocks)
|
||||
{
|
||||
_changeCustomizeService = changeCustomizeService;
|
||||
|
|
@ -92,7 +92,7 @@ public unsafe class DebugTab : ITab
|
|||
_pluginInterface = pluginInterface;
|
||||
_autoDesignManager = autoDesignManager;
|
||||
_jobs = jobs;
|
||||
_phrasing = phrasing;
|
||||
_code = code;
|
||||
_customizeUnlocks = customizeUnlocks;
|
||||
_itemUnlocks = itemUnlocks;
|
||||
}
|
||||
|
|
@ -1190,8 +1190,6 @@ public unsafe class DebugTab : ITab
|
|||
if (!ImGui.CollapsingHeader("Auto Designs"))
|
||||
return;
|
||||
|
||||
DrawPhrasingService();
|
||||
|
||||
foreach (var (set, idx) in _autoDesignManager.WithIndex())
|
||||
{
|
||||
using var id = ImRaii.PushId(idx);
|
||||
|
|
@ -1223,24 +1221,6 @@ public unsafe class DebugTab : ITab
|
|||
}
|
||||
}
|
||||
|
||||
private void DrawPhrasingService()
|
||||
{
|
||||
using var tree = ImRaii.TreeNode("Phrasing");
|
||||
if (!tree)
|
||||
return;
|
||||
|
||||
using var table = ImRaii.Table("phrasing", 3, ImGuiTableFlags.SizingFixedFit);
|
||||
if (!table)
|
||||
return;
|
||||
|
||||
ImGuiUtil.DrawTableColumn("Phrasing 1");
|
||||
ImGuiUtil.DrawTableColumn(_config.Phrasing1);
|
||||
ImGuiUtil.DrawTableColumn(_phrasing.Phrasing1.ToString());
|
||||
ImGuiUtil.DrawTableColumn("Phrasing 2");
|
||||
ImGuiUtil.DrawTableColumn(_config.Phrasing2);
|
||||
ImGuiUtil.DrawTableColumn(_phrasing.Phrasing2.ToString());
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Unlocks
|
||||
|
|
@ -1279,7 +1259,7 @@ public unsafe class DebugTab : ITab
|
|||
ImGuiUtil.DrawTableColumn(t.Value.Data.ToString());
|
||||
ImGuiUtil.DrawTableColumn(t.Value.Name);
|
||||
ImGuiUtil.DrawTableColumn(_customizeUnlocks.IsUnlocked(t.Key, out var time)
|
||||
? time == DateTimeOffset.MaxValue
|
||||
? time == DateTimeOffset.MinValue
|
||||
? "Always"
|
||||
: time.LocalDateTime.ToString("g")
|
||||
: "Never");
|
||||
|
|
@ -1325,7 +1305,7 @@ public unsafe class DebugTab : ITab
|
|||
}
|
||||
|
||||
ImGuiUtil.DrawTableColumn(_itemUnlocks.IsUnlocked(t.Key, out var time)
|
||||
? time == DateTimeOffset.MaxValue
|
||||
? time == DateTimeOffset.MinValue
|
||||
? "Always"
|
||||
: time.LocalDateTime.ToString("g")
|
||||
: "Never");
|
||||
|
|
@ -1372,7 +1352,7 @@ public unsafe class DebugTab : ITab
|
|||
}
|
||||
|
||||
ImGuiUtil.DrawTableColumn(_itemUnlocks.IsUnlocked(t.Key, out var time)
|
||||
? time == DateTimeOffset.MaxValue
|
||||
? time == DateTimeOffset.MinValue
|
||||
? "Always"
|
||||
: time.LocalDateTime.ToString("g")
|
||||
: "Never");
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Numerics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Dalamud.Interface;
|
||||
using Glamourer.Gui.Tabs.DesignTab;
|
||||
|
|
@ -17,24 +18,24 @@ public class SettingsTab : ITab
|
|||
private readonly Configuration _config;
|
||||
private readonly DesignFileSystemSelector _selector;
|
||||
private readonly StateListener _stateListener;
|
||||
private readonly PhrasingService _phrasingService;
|
||||
private readonly CodeService _codeService;
|
||||
private readonly PenumbraAutoRedraw _autoRedraw;
|
||||
|
||||
public SettingsTab(Configuration config, DesignFileSystemSelector selector, StateListener stateListener,
|
||||
PhrasingService phrasingService, PenumbraAutoRedraw autoRedraw)
|
||||
CodeService codeService, PenumbraAutoRedraw autoRedraw)
|
||||
{
|
||||
_config = config;
|
||||
_selector = selector;
|
||||
_stateListener = stateListener;
|
||||
_phrasingService = phrasingService;
|
||||
_autoRedraw = autoRedraw;
|
||||
_config = config;
|
||||
_selector = selector;
|
||||
_stateListener = stateListener;
|
||||
_codeService = codeService;
|
||||
_autoRedraw = autoRedraw;
|
||||
}
|
||||
|
||||
public ReadOnlySpan<byte> Label
|
||||
=> "Settings"u8;
|
||||
|
||||
private string? _tmpPhrasing1 = null;
|
||||
private string? _tmpPhrasing2 = null;
|
||||
private string _currentCode = string.Empty;
|
||||
|
||||
|
||||
public void DrawContent()
|
||||
{
|
||||
|
|
@ -62,27 +63,46 @@ public class SettingsTab : ITab
|
|||
Checkbox("Debug Mode", "Show the debug tab. Only useful for debugging or advanced use.", _config.DebugMode, v => _config.DebugMode = v);
|
||||
DrawColorSettings();
|
||||
|
||||
_tmpPhrasing1 ??= _config.Phrasing1;
|
||||
ImGui.InputText("Phrasing 1", ref _tmpPhrasing1, 512);
|
||||
|
||||
if (ImGui.IsItemDeactivatedAfterEdit())
|
||||
{
|
||||
_phrasingService.SetPhrasing1(_tmpPhrasing1);
|
||||
_tmpPhrasing1 = null;
|
||||
}
|
||||
|
||||
_tmpPhrasing2 ??= _config.Phrasing2;
|
||||
ImGui.InputText("Phrasing 2", ref _tmpPhrasing2, 512);
|
||||
|
||||
if (ImGui.IsItemDeactivatedAfterEdit())
|
||||
{
|
||||
_phrasingService.SetPhrasing2(_tmpPhrasing2);
|
||||
_tmpPhrasing2 = null;
|
||||
}
|
||||
DrawCodes();
|
||||
|
||||
MainWindow.DrawSupportButtons();
|
||||
}
|
||||
|
||||
private void DrawCodes()
|
||||
{
|
||||
if (!ImGui.CollapsingHeader("Codes"))
|
||||
return;
|
||||
|
||||
using (var style = ImRaii.PushStyle(ImGuiStyleVar.FrameBorderSize, ImGuiHelpers.GlobalScale, _currentCode.Length > 0))
|
||||
{
|
||||
var color = _codeService.CheckCode(_currentCode) != null ? ColorId.ActorAvailable : ColorId.ActorUnavailable;
|
||||
using var c = ImRaii.PushColor(ImGuiCol.Border, color.Value(), _currentCode.Length > 0);
|
||||
if (ImGui.InputTextWithHint("##Code", "Enter Code...", ref _currentCode, 512, ImGuiInputTextFlags.EnterReturnsTrue))
|
||||
{
|
||||
if (_codeService.AddCode(_currentCode))
|
||||
_currentCode = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
if (_config.Codes.Count <= 0)
|
||||
return;
|
||||
|
||||
for (var i = 0; i < _config.Codes.Count; ++i)
|
||||
{
|
||||
var (code, state) = _config.Codes[i];
|
||||
var action = _codeService.CheckCode(code);
|
||||
if (action == null)
|
||||
continue;
|
||||
|
||||
if (ImGui.Checkbox(code, ref state))
|
||||
{
|
||||
action(state);
|
||||
_config.Codes[i] = (code, state);
|
||||
_config.Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Draw the entire Color subsection. </summary>
|
||||
private void DrawColorSettings()
|
||||
{
|
||||
|
|
|
|||
145
Glamourer/Gui/Tabs/UnlocksTab/UnlockOverview.cs
Normal file
145
Glamourer/Gui/Tabs/UnlocksTab/UnlockOverview.cs
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Dalamud.Interface;
|
||||
using Glamourer.Customization;
|
||||
using Glamourer.Services;
|
||||
using Glamourer.Unlocks;
|
||||
using ImGuiNET;
|
||||
using OtterGui.Raii;
|
||||
using Penumbra.GameData.Enums;
|
||||
|
||||
namespace Glamourer.Gui.Tabs.UnlocksTab;
|
||||
|
||||
public class UnlockOverview
|
||||
{
|
||||
private readonly ItemManager _items;
|
||||
private readonly ItemUnlockManager _itemUnlocks;
|
||||
private readonly CustomizationService _customizations;
|
||||
private readonly CustomizeUnlockManager _customizeUnlocks;
|
||||
private readonly PenumbraChangedItemTooltip _tooltip;
|
||||
|
||||
private static readonly Vector4 UnavailableTint = new(0.3f, 0.3f, 0.3f, 1.0f);
|
||||
|
||||
public UnlockOverview(ItemManager items, CustomizationService customizations, ItemUnlockManager itemUnlocks,
|
||||
CustomizeUnlockManager customizeUnlocks, PenumbraChangedItemTooltip tooltip)
|
||||
{
|
||||
_items = items;
|
||||
_customizations = customizations;
|
||||
_itemUnlocks = itemUnlocks;
|
||||
_customizeUnlocks = customizeUnlocks;
|
||||
_tooltip = tooltip;
|
||||
}
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
using var color = ImRaii.PushColor(ImGuiCol.Border, ImGui.GetColorU32(ImGuiCol.TableBorderStrong));
|
||||
using var child = ImRaii.Child("Panel", -Vector2.One, true);
|
||||
if (!child)
|
||||
return;
|
||||
|
||||
var iconSize = ImGuiHelpers.ScaledVector2(32);
|
||||
foreach (var type in Enum.GetValues<FullEquipType>())
|
||||
DrawEquipTypeHeader(iconSize, type);
|
||||
|
||||
iconSize = ImGuiHelpers.ScaledVector2(64);
|
||||
foreach (var gender in _customizations.AwaitedService.Genders)
|
||||
{
|
||||
foreach (var clan in _customizations.AwaitedService.Clans)
|
||||
DrawCustomizationHeader(iconSize, clan, gender);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawCustomizationHeader(Vector2 iconSize, SubRace subRace, Gender gender)
|
||||
{
|
||||
var set = _customizations.AwaitedService.GetList(subRace, gender);
|
||||
if (set.HairStyles.Count == 0 && set.FacePaints.Count == 0)
|
||||
return;
|
||||
|
||||
if (!ImGui.CollapsingHeader($"Unlockable {subRace.ToName()} {gender.ToName()} Customizations"))
|
||||
return;
|
||||
|
||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero);
|
||||
foreach (var customization in set.HairStyles.Concat(set.FacePaints))
|
||||
{
|
||||
if (!_customizeUnlocks.Unlockable.TryGetValue(customization, out var unlockData))
|
||||
continue;
|
||||
|
||||
var unlocked = _customizeUnlocks.IsUnlocked(customization, out var time);
|
||||
var icon = _customizations.AwaitedService.GetIcon(customization.IconId);
|
||||
|
||||
ImGui.Image(icon.ImGuiHandle, iconSize, Vector2.Zero, Vector2.One, unlocked ? Vector4.One : UnavailableTint);
|
||||
if (ImGui.IsItemHovered())
|
||||
{
|
||||
using var tt = ImRaii.Tooltip();
|
||||
var size = new Vector2(icon.Width, icon.Height);
|
||||
if (size.X >= iconSize.X && size.Y >= iconSize.Y)
|
||||
ImGui.Image(icon.ImGuiHandle, size);
|
||||
ImGui.TextUnformatted(unlockData.Name);
|
||||
ImGui.TextUnformatted($"{customization.Index.ToDefaultName()} {customization.Value.Value}");
|
||||
ImGui.TextUnformatted(unlocked ? $"Unlocked on {time:g}" : "Not unlocked.");
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
if (ImGui.GetContentRegionAvail().X < iconSize.X)
|
||||
ImGui.NewLine();
|
||||
}
|
||||
|
||||
if (ImGui.GetCursorPosX() != 0)
|
||||
ImGui.NewLine();
|
||||
}
|
||||
|
||||
private void DrawEquipTypeHeader(Vector2 iconSize, FullEquipType type)
|
||||
{
|
||||
if (type.IsOffhandType() || !_items.ItemService.AwaitedService.TryGetValue(type, out var items) || items.Count == 0)
|
||||
return;
|
||||
|
||||
if (!ImGui.CollapsingHeader($"{type.ToName()}s"))
|
||||
return;
|
||||
|
||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, new Vector2(2 * ImGuiHelpers.GlobalScale));
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (!ImGui.IsItemVisible())
|
||||
{ }
|
||||
|
||||
var unlocked = _itemUnlocks.IsUnlocked(item.Id, out var time);
|
||||
var icon = _customizations.AwaitedService.GetIcon(item.IconId);
|
||||
|
||||
ImGui.Image(icon.ImGuiHandle, iconSize, Vector2.Zero, Vector2.One, unlocked ? Vector4.One : UnavailableTint);
|
||||
if (ImGui.IsItemClicked())
|
||||
{
|
||||
// TODO link
|
||||
}
|
||||
|
||||
if (ImGui.IsItemClicked(ImGuiMouseButton.Right) && _tooltip.Player(out var state))
|
||||
_tooltip.ApplyItem(state, item);
|
||||
|
||||
if (ImGui.IsItemHovered())
|
||||
{
|
||||
using var tt = ImRaii.Tooltip();
|
||||
var size = new Vector2(icon.Width, icon.Height);
|
||||
if (size.X >= iconSize.X && size.Y >= iconSize.Y)
|
||||
ImGui.Image(icon.ImGuiHandle, size);
|
||||
ImGui.TextUnformatted(item.Name);
|
||||
var slot = item.Type.ToSlot();
|
||||
ImGui.TextUnformatted($"{item.Type.ToName()} ({slot.ToName()})");
|
||||
if (item.Type.Offhand().IsOffhandType())
|
||||
ImGui.TextUnformatted(
|
||||
$"{item.Weapon()}{(_items.ItemService.AwaitedService.TryGetValue(item.Id, false, out var offhand) ? $" | {offhand.Weapon()}" : string.Empty)}");
|
||||
else
|
||||
ImGui.TextUnformatted(slot is EquipSlot.MainHand ? $"{item.Weapon()}" : $"{item.Armor()}");
|
||||
ImGui.TextUnformatted(
|
||||
unlocked ? time == DateTimeOffset.MinValue ? "Always Unlocked" : $"Unlocked on {time:g}" : "Not Unlocked.");
|
||||
_tooltip.CreateTooltip(item, string.Empty, false);
|
||||
}
|
||||
|
||||
ImGui.SameLine();
|
||||
if (ImGui.GetContentRegionAvail().X < iconSize.X)
|
||||
ImGui.NewLine();
|
||||
}
|
||||
|
||||
if (ImGui.GetCursorPosX() != 0)
|
||||
ImGui.NewLine();
|
||||
}
|
||||
}
|
||||
276
Glamourer/Gui/Tabs/UnlocksTab/UnlockTable.cs
Normal file
276
Glamourer/Gui/Tabs/UnlocksTab/UnlockTable.cs
Normal file
|
|
@ -0,0 +1,276 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Dalamud.Game.ClientState.Objects.SubKinds;
|
||||
using Dalamud.Interface;
|
||||
using Glamourer.Services;
|
||||
using Glamourer.Structs;
|
||||
using Glamourer.Unlocks;
|
||||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using OtterGui.Raii;
|
||||
using OtterGui.Table;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Glamourer.Gui.Tabs.UnlocksTab;
|
||||
|
||||
public class UnlockTable : Table<EquipItem>
|
||||
{
|
||||
public UnlockTable(ItemManager items, CustomizationService customizations, ItemUnlockManager itemUnlocks,
|
||||
PenumbraChangedItemTooltip tooltip)
|
||||
: base("ItemUnlockTable", new ItemList(items),
|
||||
new NameColumn(customizations, tooltip) { Label = "Item Name..." },
|
||||
new SlotColumn() { Label = "Equip Slot" },
|
||||
new TypeColumn() { Label = "Item Type..." },
|
||||
new UnlockDateColumn(itemUnlocks) { Label = "Unlocked" },
|
||||
new ItemIdColumn() { Label = "Item Id..." },
|
||||
new ModelDataColumn(items) { Label = "Model Data..." })
|
||||
{
|
||||
Sortable = true;
|
||||
Flags |= ImGuiTableFlags.Hideable;
|
||||
}
|
||||
|
||||
private sealed class NameColumn : ColumnString<EquipItem>
|
||||
{
|
||||
private readonly CustomizationService _customizations;
|
||||
private readonly PenumbraChangedItemTooltip _tooltip;
|
||||
|
||||
public override float Width
|
||||
=> 400 * ImGuiHelpers.GlobalScale;
|
||||
|
||||
public NameColumn(CustomizationService customizations, PenumbraChangedItemTooltip tooltip)
|
||||
{
|
||||
_customizations = customizations;
|
||||
_tooltip = tooltip;
|
||||
Flags |= ImGuiTableColumnFlags.NoHide | ImGuiTableColumnFlags.NoReorder;
|
||||
}
|
||||
|
||||
public override string ToName(EquipItem item)
|
||||
=> item.Name;
|
||||
|
||||
public override void DrawColumn(EquipItem item, int _)
|
||||
{
|
||||
var icon = _customizations.AwaitedService.GetIcon(item.IconId);
|
||||
ImGui.Image(icon.ImGuiHandle, new Vector2(ImGui.GetFrameHeight()));
|
||||
ImGui.SameLine();
|
||||
ImGui.AlignTextToFramePadding();
|
||||
if (ImGui.Selectable(item.Name))
|
||||
{
|
||||
// TODO link
|
||||
}
|
||||
|
||||
if (ImGui.IsItemClicked(ImGuiMouseButton.Right) && _tooltip.Player(out var state))
|
||||
_tooltip.ApplyItem(state, item);
|
||||
|
||||
if (ImGui.IsItemHovered() && _tooltip.Player())
|
||||
_tooltip.CreateTooltip(item, string.Empty, true);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class TypeColumn : ColumnString<EquipItem>
|
||||
{
|
||||
public override float Width
|
||||
=> ImGui.CalcTextSize(FullEquipType.CrossPeinHammer.ToName()).X;
|
||||
|
||||
public override string ToName(EquipItem item)
|
||||
=> item.Type.ToName();
|
||||
|
||||
public override void DrawColumn(EquipItem item, int _)
|
||||
{
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.TextUnformatted(item.Type.ToName());
|
||||
}
|
||||
|
||||
public override int Compare(EquipItem lhs, EquipItem rhs)
|
||||
=> lhs.Type.CompareTo(rhs.Type);
|
||||
}
|
||||
|
||||
private sealed class SlotColumn : ColumnFlags<EquipFlag, EquipItem>
|
||||
{
|
||||
public override float Width
|
||||
=> ImGui.CalcTextSize("Equip Slotmm").X;
|
||||
|
||||
private EquipFlag _filterValue;
|
||||
|
||||
public SlotColumn()
|
||||
{
|
||||
AllFlags = Values.Aggregate((a, b) => a | b);
|
||||
_filterValue = AllFlags;
|
||||
}
|
||||
|
||||
public override void DrawColumn(EquipItem item, int idx)
|
||||
{
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.TextUnformatted(ToString(item.Type.ToSlot()));
|
||||
}
|
||||
|
||||
public override EquipFlag FilterValue
|
||||
=> _filterValue;
|
||||
|
||||
protected override IReadOnlyList<EquipFlag> Values
|
||||
=> new[]
|
||||
{
|
||||
EquipFlag.Mainhand,
|
||||
EquipFlag.Offhand,
|
||||
EquipFlag.Head,
|
||||
EquipFlag.Body,
|
||||
EquipFlag.Hands,
|
||||
EquipFlag.Legs,
|
||||
EquipFlag.Feet,
|
||||
EquipFlag.Ears,
|
||||
EquipFlag.Neck,
|
||||
EquipFlag.Wrist,
|
||||
EquipFlag.RFinger,
|
||||
};
|
||||
|
||||
protected override string[] Names
|
||||
=> new[]
|
||||
{
|
||||
ToString(EquipSlot.MainHand),
|
||||
ToString(EquipSlot.OffHand),
|
||||
ToString(EquipSlot.Head),
|
||||
ToString(EquipSlot.Body),
|
||||
ToString(EquipSlot.Hands),
|
||||
ToString(EquipSlot.Legs),
|
||||
ToString(EquipSlot.Feet),
|
||||
ToString(EquipSlot.Ears),
|
||||
ToString(EquipSlot.Neck),
|
||||
ToString(EquipSlot.Wrists),
|
||||
ToString(EquipSlot.RFinger),
|
||||
};
|
||||
|
||||
protected override void SetValue(EquipFlag value, bool enable)
|
||||
=> _filterValue = enable ? _filterValue | value : _filterValue & ~value;
|
||||
|
||||
public override int Compare(EquipItem lhs, EquipItem rhs)
|
||||
=> lhs.Type.ToSlot().CompareTo(rhs.Type.ToSlot());
|
||||
|
||||
public override bool FilterFunc(EquipItem item)
|
||||
=> _filterValue.HasFlag(item.Type.ToSlot().ToFlag());
|
||||
|
||||
private static string ToString(EquipSlot slot)
|
||||
=> slot switch
|
||||
{
|
||||
EquipSlot.MainHand => "Mainhand",
|
||||
EquipSlot.OffHand => "Offhand",
|
||||
EquipSlot.Head => "Head",
|
||||
EquipSlot.Body => "Body",
|
||||
EquipSlot.Hands => "Hands",
|
||||
EquipSlot.Legs => "Legs",
|
||||
EquipSlot.Feet => "Feet",
|
||||
EquipSlot.Ears => "Ears",
|
||||
EquipSlot.Neck => "Neck",
|
||||
EquipSlot.Wrists => "Wrists",
|
||||
EquipSlot.RFinger => "Finger",
|
||||
_ => string.Empty,
|
||||
};
|
||||
}
|
||||
|
||||
private sealed class UnlockDateColumn : Column<EquipItem>
|
||||
{
|
||||
private readonly ItemUnlockManager _unlocks;
|
||||
|
||||
public override float Width
|
||||
=> 110 * ImGuiHelpers.GlobalScale;
|
||||
|
||||
public UnlockDateColumn(ItemUnlockManager unlocks)
|
||||
=> _unlocks = unlocks;
|
||||
|
||||
public override void DrawColumn(EquipItem item, int idx)
|
||||
{
|
||||
if (!_unlocks.IsUnlocked(item.Id, out var time))
|
||||
return;
|
||||
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGui.TextUnformatted(time == DateTimeOffset.MinValue ? "Always" : time.ToString("g"));
|
||||
}
|
||||
|
||||
public override int Compare(EquipItem lhs, EquipItem rhs)
|
||||
{
|
||||
var unlockedLhs = _unlocks.IsUnlocked(lhs.Id, out var timeLhs);
|
||||
var unlockedRhs = _unlocks.IsUnlocked(lhs.Id, out var timeRhs);
|
||||
var c1 = unlockedLhs.CompareTo(unlockedRhs);
|
||||
return c1 != 0 ? c1 : timeLhs.CompareTo(timeRhs);
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class ItemIdColumn : ColumnString<EquipItem>
|
||||
{
|
||||
public override float Width
|
||||
=> 70 * ImGuiHelpers.GlobalScale;
|
||||
|
||||
public override int Compare(EquipItem lhs, EquipItem rhs)
|
||||
=> lhs.Id.CompareTo(rhs.Id);
|
||||
|
||||
public override string ToName(EquipItem item)
|
||||
=> item.Id.ToString();
|
||||
|
||||
public override void DrawColumn(EquipItem item, int _)
|
||||
{
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGuiUtil.RightAlign(item.Id.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class ModelDataColumn : ColumnString<EquipItem>
|
||||
{
|
||||
private readonly ItemManager _items;
|
||||
|
||||
public override float Width
|
||||
=> 100 * ImGuiHelpers.GlobalScale;
|
||||
|
||||
public ModelDataColumn(ItemManager items)
|
||||
=> _items = items;
|
||||
|
||||
public override void DrawColumn(EquipItem item, int _)
|
||||
{
|
||||
ImGui.AlignTextToFramePadding();
|
||||
ImGuiUtil.RightAlign(item.ModelString);
|
||||
if (ImGui.IsItemHovered()
|
||||
&& item.Type.Offhand().IsOffhandType()
|
||||
&& _items.ItemService.AwaitedService.TryGetValue(item.Id, false, out var offhand))
|
||||
{
|
||||
using var tt = ImRaii.Tooltip();
|
||||
ImGui.TextUnformatted("Offhand: " + offhand.ModelString);
|
||||
}
|
||||
}
|
||||
|
||||
public override int Compare(EquipItem lhs, EquipItem rhs)
|
||||
=> lhs.Weapon().Value.CompareTo(rhs.Weapon().Value);
|
||||
|
||||
public override bool FilterFunc(EquipItem item)
|
||||
{
|
||||
if (FilterValue.Length == 0)
|
||||
return true;
|
||||
|
||||
if (FilterRegex?.IsMatch(item.ModelString) ?? item.ModelString.Contains(FilterValue, StringComparison.OrdinalIgnoreCase))
|
||||
return true;
|
||||
|
||||
if (item.Type.Offhand().IsOffhandType() && _items.ItemService.AwaitedService.TryGetValue(item.Id, false, out var offhand))
|
||||
return FilterRegex?.IsMatch(offhand.ModelString)
|
||||
?? offhand.ModelString.Contains(FilterValue, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class ItemList : IReadOnlyCollection<EquipItem>
|
||||
{
|
||||
private readonly ItemManager _items;
|
||||
|
||||
public ItemList(ItemManager items)
|
||||
=> _items = items;
|
||||
|
||||
public IEnumerator<EquipItem> GetEnumerator()
|
||||
=> _items.ItemService.AwaitedService.AllItems(true).Select(i => i.Item2).GetEnumerator();
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
=> GetEnumerator();
|
||||
|
||||
public int Count
|
||||
=> _items.ItemService.AwaitedService.TotalItemCount(true);
|
||||
}
|
||||
}
|
||||
60
Glamourer/Gui/Tabs/UnlocksTab/UnlocksTab.cs
Normal file
60
Glamourer/Gui/Tabs/UnlocksTab/UnlocksTab.cs
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
using System;
|
||||
using System.Numerics;
|
||||
using Dalamud.Interface;
|
||||
using ImGuiNET;
|
||||
using OtterGui.Raii;
|
||||
using OtterGui;
|
||||
using OtterGui.Widgets;
|
||||
|
||||
namespace Glamourer.Gui.Tabs.UnlocksTab;
|
||||
|
||||
public class UnlocksTab : ITab
|
||||
{
|
||||
private readonly Configuration _config;
|
||||
private readonly UnlockOverview _overview;
|
||||
private readonly UnlockTable _table;
|
||||
|
||||
public UnlocksTab(Configuration config, UnlockOverview overview, UnlockTable table)
|
||||
{
|
||||
_config = config;
|
||||
_overview = overview;
|
||||
_table = table;
|
||||
}
|
||||
|
||||
private bool DetailMode
|
||||
{
|
||||
get => _config.UnlockDetailMode;
|
||||
set
|
||||
{
|
||||
_config.UnlockDetailMode = value;
|
||||
_config.Save();
|
||||
}
|
||||
}
|
||||
|
||||
public ReadOnlySpan<byte> Label
|
||||
=> "Unlocks"u8;
|
||||
|
||||
public void DrawContent()
|
||||
{
|
||||
DrawTypeSelection();
|
||||
if (DetailMode)
|
||||
_table.Draw(ImGui.GetFrameHeightWithSpacing());
|
||||
else
|
||||
_overview.Draw();
|
||||
}
|
||||
|
||||
private void DrawTypeSelection()
|
||||
{
|
||||
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
|
||||
.Push(ImGuiStyleVar.FrameRounding, 0);
|
||||
var buttonSize = new Vector2(ImGui.GetContentRegionAvail().X / 2, ImGui.GetFrameHeight());
|
||||
|
||||
if (ImGuiUtil.DrawDisabledButton("Overview Mode", buttonSize, "Show tinted icons of sets of unlocks.", !DetailMode))
|
||||
DetailMode = false;
|
||||
|
||||
ImGui.SameLine();
|
||||
if (ImGuiUtil.DrawDisabledButton("Detailed Mode", buttonSize, "Show all unlockable data as a combined filterable and sortable table.",
|
||||
DetailMode))
|
||||
DetailMode = true;
|
||||
}
|
||||
}
|
||||
108
Glamourer/Services/CodeService.cs
Normal file
108
Glamourer/Services/CodeService.cs
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Penumbra.GameData.Enums;
|
||||
|
||||
namespace Glamourer.Services;
|
||||
|
||||
public class CodeService
|
||||
{
|
||||
private readonly Configuration _config;
|
||||
private readonly SHA256 _hasher = SHA256.Create();
|
||||
|
||||
public enum Sizing
|
||||
{
|
||||
None,
|
||||
Dwarf,
|
||||
Giant,
|
||||
}
|
||||
|
||||
public bool EnabledClown { get; private set; }
|
||||
public bool EnabledEmperor { get; private set; }
|
||||
public bool EnabledIndividual { get; private set; }
|
||||
public Sizing EnabledSizing { get; private set; }
|
||||
public Race EnabledOops { get; private set; }
|
||||
public bool EnabledMesmer { get; private set; }
|
||||
public bool EnabledInventory { get; private set; }
|
||||
|
||||
public CodeService(Configuration config)
|
||||
=> _config = config;
|
||||
|
||||
private void Load()
|
||||
{
|
||||
var changes = false;
|
||||
for (var i = 0; i < _config.Codes.Count; ++i)
|
||||
{
|
||||
var enabled = CheckCode(_config.Codes[i].Code);
|
||||
if (enabled == null)
|
||||
{
|
||||
--i;
|
||||
_config.Codes.RemoveAt(i);
|
||||
changes = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
enabled(_config.Codes[i].Enabled);
|
||||
}
|
||||
}
|
||||
|
||||
if (changes)
|
||||
_config.Save();
|
||||
}
|
||||
|
||||
public bool AddCode(string name)
|
||||
{
|
||||
if (CheckCode(name) == null || _config.Codes.Any(p => p.Code == name))
|
||||
return false;
|
||||
|
||||
_config.Codes.Add((name, false));
|
||||
_config.Save();
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
public Action<bool>? CheckCode(string name)
|
||||
{
|
||||
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(name));
|
||||
var sha = (ReadOnlySpan<byte>)_hasher.ComputeHash(stream);
|
||||
return sha switch
|
||||
{
|
||||
_ when CodeClown.SequenceEqual(sha) => v => EnabledClown = v,
|
||||
_ when CodeEmperor.SequenceEqual(sha) => v => EnabledEmperor = v,
|
||||
_ when CodeIndividual.SequenceEqual(sha) => v => EnabledIndividual = v,
|
||||
_ when CodeDwarf.SequenceEqual(sha) => v => EnabledSizing = v ? Sizing.Dwarf : Sizing.None,
|
||||
_ when CodeGiant.SequenceEqual(sha) => v => EnabledSizing = v ? Sizing.Giant : Sizing.None,
|
||||
_ when CodeMesmer.SequenceEqual(sha) => v => EnabledMesmer = v,
|
||||
_ when CodeOops1.SequenceEqual(sha) => v => EnabledOops = v ? Race.Hyur : Race.Unknown,
|
||||
_ when CodeOops2.SequenceEqual(sha) => v => EnabledOops = v ? Race.Elezen : Race.Unknown,
|
||||
_ when CodeOops3.SequenceEqual(sha) => v => EnabledOops = v ? Race.Lalafell : Race.Unknown,
|
||||
_ when CodeOops4.SequenceEqual(sha) => v => EnabledOops = v ? Race.Miqote : Race.Unknown,
|
||||
_ when CodeOops5.SequenceEqual(sha) => v => EnabledOops = v ? Race.Roegadyn : Race.Unknown,
|
||||
_ when CodeOops6.SequenceEqual(sha) => v => EnabledOops = v ? Race.AuRa : Race.Unknown,
|
||||
_ when CodeOops7.SequenceEqual(sha) => v => EnabledOops = v ? Race.Hrothgar : Race.Unknown,
|
||||
_ when CodeOops8.SequenceEqual(sha) => v => EnabledOops = v ? Race.Viera : Race.Unknown,
|
||||
_ when CodeInventory.SequenceEqual(sha) => v => EnabledInventory = v,
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
// @formatter:off
|
||||
private static ReadOnlySpan<byte> CodeClown => new byte[] { 0xC4, 0xEE, 0x1D, 0x6F, 0xC5, 0x5D, 0x47, 0xBE, 0x78, 0x63, 0x66, 0x86, 0x81, 0x15, 0xEB, 0xFA, 0xF6, 0x4A, 0x90, 0xEA, 0xC0, 0xE4, 0xEE, 0x86, 0x69, 0x01, 0x8E, 0xDB, 0xCC, 0x69, 0xD1, 0xBD };
|
||||
private static ReadOnlySpan<byte> CodeEmperor => new byte[] { 0xE2, 0x2D, 0x3E, 0x57, 0x16, 0x82, 0x65, 0x98, 0x7E, 0xE6, 0x8F, 0x45, 0x14, 0x7D, 0x65, 0x31, 0xE9, 0xD8, 0xDB, 0xEA, 0xDC, 0xBF, 0xEE, 0x2A, 0xBA, 0xD5, 0x69, 0x96, 0x78, 0x34, 0x3B, 0x57 };
|
||||
private static ReadOnlySpan<byte> CodeIndividual => new byte[] { 0x95, 0xA4, 0x71, 0xAC, 0xA3, 0xC2, 0x34, 0x94, 0xC1, 0x65, 0x07, 0xF3, 0x7F, 0x93, 0x57, 0xEE, 0xE3, 0x04, 0xC0, 0xE8, 0x1B, 0xA0, 0xE2, 0x08, 0x68, 0x02, 0x8D, 0xAD, 0x76, 0x03, 0x9B, 0xC5 };
|
||||
private static ReadOnlySpan<byte> CodeDwarf => new byte[] { 0x55, 0x97, 0xFE, 0xE9, 0x78, 0x64, 0xE8, 0x2F, 0xCD, 0x25, 0xD1, 0xAE, 0xDF, 0x35, 0xE6, 0xED, 0x03, 0x78, 0x54, 0x1D, 0x56, 0x22, 0x34, 0x75, 0x4B, 0x96, 0x6F, 0xBA, 0xAC, 0xEC, 0x00, 0x46 };
|
||||
private static ReadOnlySpan<byte> CodeGiant => new byte[] { 0x6E, 0xBB, 0x91, 0x1D, 0x67, 0xE3, 0x00, 0x07, 0xA1, 0x0F, 0x2A, 0xF0, 0x26, 0x91, 0x38, 0x63, 0xD3, 0x52, 0x82, 0xF7, 0x5D, 0x93, 0xE8, 0x83, 0xB1, 0xF6, 0xB9, 0x69, 0x78, 0x20, 0xC4, 0xCE };
|
||||
private static ReadOnlySpan<byte> CodeMesmer => new byte[] { 0x6A, 0x84, 0x12, 0xEA, 0x3B, 0x03, 0x2E, 0xD9, 0xA3, 0x51, 0xB0, 0x4F, 0xE7, 0x4D, 0x59, 0x87, 0xA9, 0xA1, 0x6E, 0x08, 0xC7, 0x3E, 0xD3, 0x15, 0xEE, 0x40, 0x2C, 0xB3, 0x44, 0x78, 0x1F, 0xA0 };
|
||||
private static ReadOnlySpan<byte> CodeOops1 => new byte[] { 0x4C, 0x51, 0xE2, 0x38, 0xEF, 0xAD, 0x84, 0x0E, 0x4E, 0x11, 0x0F, 0x5E, 0xDE, 0x45, 0x41, 0x9F, 0x6A, 0xF6, 0x5F, 0x5B, 0xA8, 0x91, 0x64, 0x22, 0xEE, 0x62, 0x97, 0x3C, 0x78, 0x18, 0xCD, 0xAF };
|
||||
private static ReadOnlySpan<byte> CodeOops2 => new byte[] { 0x3D, 0x5B, 0xA9, 0x62, 0xCE, 0xBE, 0x52, 0xF5, 0x94, 0x2A, 0xF9, 0xB7, 0xCF, 0xD9, 0x24, 0x2B, 0x38, 0xC7, 0x4F, 0x28, 0x97, 0x29, 0x1D, 0x01, 0x13, 0x53, 0x44, 0x11, 0x15, 0x6F, 0x9B, 0x56 };
|
||||
private static ReadOnlySpan<byte> CodeOops3 => new byte[] { 0x85, 0x8D, 0x5B, 0xC2, 0x66, 0x53, 0x2E, 0xB9, 0xE9, 0x85, 0xE5, 0xF8, 0xD3, 0x75, 0x18, 0x7C, 0x58, 0x55, 0xD4, 0x8C, 0x8E, 0x5F, 0x58, 0x2E, 0xF3, 0xF1, 0xAE, 0xA8, 0xA0, 0x81, 0xC6, 0x0E };
|
||||
private static ReadOnlySpan<byte> CodeOops4 => new byte[] { 0x44, 0x73, 0x8C, 0x39, 0x5A, 0xF1, 0xDB, 0x5F, 0x62, 0xA1, 0x6E, 0x5F, 0xE6, 0x97, 0x9E, 0x90, 0xD7, 0x5C, 0x97, 0x67, 0xB6, 0xC7, 0x99, 0x61, 0x36, 0xCA, 0x34, 0x7E, 0xB9, 0xAC, 0xC3, 0x76 };
|
||||
private static ReadOnlySpan<byte> CodeOops5 => new byte[] { 0xB7, 0x25, 0x73, 0xDB, 0xBE, 0xD0, 0x49, 0xFB, 0xFF, 0x9C, 0x32, 0x21, 0xB0, 0x8A, 0x2C, 0x0C, 0x77, 0x46, 0xD5, 0xCF, 0x0E, 0x63, 0x2F, 0x91, 0x85, 0x8B, 0x55, 0x5C, 0x4D, 0xD2, 0xB9, 0xB8 };
|
||||
private static ReadOnlySpan<byte> CodeOops6 => new byte[] { 0x69, 0x93, 0xAF, 0xE4, 0xB8, 0xEC, 0x5F, 0x40, 0xEB, 0x8A, 0x6F, 0xD1, 0x9B, 0xD9, 0x56, 0x0B, 0xEA, 0x64, 0x79, 0x9B, 0x54, 0xA1, 0x41, 0xED, 0xBC, 0x3E, 0x6E, 0x5C, 0xF1, 0x23, 0x60, 0xF8 };
|
||||
private static ReadOnlySpan<byte> CodeOops7 => new byte[] { 0x41, 0xEC, 0x65, 0x05, 0x8D, 0x20, 0x68, 0x5A, 0xB7, 0xEB, 0x92, 0x15, 0x43, 0xCF, 0x15, 0x05, 0x27, 0x51, 0xFE, 0x20, 0xC9, 0xB6, 0x2B, 0x84, 0xD9, 0x6A, 0x49, 0x5A, 0x5B, 0x7F, 0x2E, 0xE7 };
|
||||
private static ReadOnlySpan<byte> CodeOops8 => new byte[] { 0x16, 0xFF, 0x63, 0x85, 0x1C, 0xF5, 0x34, 0x33, 0x67, 0x8C, 0x46, 0x8E, 0x3E, 0xE3, 0xA6, 0x94, 0xF9, 0x74, 0x47, 0xAA, 0xC7, 0x29, 0x59, 0x1F, 0x6C, 0x6E, 0xF2, 0xF5, 0x87, 0x24, 0x9E, 0x2B };
|
||||
private static ReadOnlySpan<byte> CodeInventory => new byte[] { 0xD1, 0x35, 0xD7, 0x18, 0xBE, 0x45, 0x42, 0xBD, 0x88, 0x77, 0x7E, 0xC4, 0x41, 0x06, 0x34, 0x4D, 0x71, 0x3A, 0xC5, 0xCC, 0xA4, 0x1B, 0x7D, 0x3F, 0x3B, 0x86, 0x07, 0xCB, 0x63, 0xD7, 0xF9, 0xDB };
|
||||
// @formatter:on
|
||||
}
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Dalamud.Data;
|
||||
|
|
@ -105,10 +106,16 @@ public sealed class CustomizationService : AsyncServiceWrapper<ICustomizationMan
|
|||
public bool IsGenderValid(Race race, Gender gender)
|
||||
=> race is Race.Hrothgar ? gender == Gender.Male : AwaitedService.Genders.Contains(gender);
|
||||
|
||||
/// <summary> Returns whether a customization value is valid for a given clan/gender set and face. </summary>
|
||||
/// <inheritdoc cref="IsCustomizationValid(CustomizationSet,CustomizeValue,CustomizeIndex,CustomizeValue, out CustomizeData?)"/>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public static bool IsCustomizationValid(CustomizationSet set, CustomizeValue face, CustomizeIndex type, CustomizeValue value)
|
||||
=> set.DataByValue(type, value, out _, face) >= 0;
|
||||
=> IsCustomizationValid(set, face, type, value, out _);
|
||||
|
||||
/// <summary> Returns whether a customization value is valid for a given clan/gender set and face. </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
public static bool IsCustomizationValid(CustomizationSet set, CustomizeValue face, CustomizeIndex type, CustomizeValue value,
|
||||
[NotNullWhen(true)] out CustomizeData? data)
|
||||
=> set.DataByValue(type, value, out data, face) >= 0;
|
||||
|
||||
/// <summary> Returns whether a customization value is valid for a given clan, gender and face. </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||
|
|
|
|||
|
|
@ -1,54 +0,0 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace Glamourer.Services;
|
||||
|
||||
public class PhrasingService
|
||||
{
|
||||
private readonly Configuration _config;
|
||||
private readonly SHA256 _hasher = SHA256.Create();
|
||||
|
||||
public bool Phrasing1 { get; private set; }
|
||||
public bool Phrasing2 { get; private set; }
|
||||
|
||||
public PhrasingService(Configuration config)
|
||||
{
|
||||
_config = config;
|
||||
Phrasing1 = CheckPhrasing(_config.Phrasing1, P1);
|
||||
Phrasing2 = CheckPhrasing(_config.Phrasing2, P2);
|
||||
}
|
||||
|
||||
public void SetPhrasing1(string newPhrasing)
|
||||
{
|
||||
if (_config.Phrasing1 == newPhrasing)
|
||||
return;
|
||||
|
||||
_config.Phrasing1 = newPhrasing;
|
||||
_config.Save();
|
||||
Phrasing1 = CheckPhrasing(newPhrasing, P1);
|
||||
}
|
||||
|
||||
public void SetPhrasing2(string newPhrasing)
|
||||
{
|
||||
if (_config.Phrasing2 == newPhrasing)
|
||||
return;
|
||||
|
||||
_config.Phrasing2 = newPhrasing;
|
||||
_config.Save();
|
||||
Phrasing2 = CheckPhrasing(newPhrasing, P2);
|
||||
}
|
||||
|
||||
private bool CheckPhrasing(string phrasing, ReadOnlySpan<byte> data)
|
||||
{
|
||||
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(phrasing));
|
||||
var sha = _hasher.ComputeHash(stream);
|
||||
return data.SequenceEqual(sha);
|
||||
}
|
||||
|
||||
// @formatter:off
|
||||
private static ReadOnlySpan<byte> P1 => new byte[] { 0xD1, 0x35, 0xD7, 0x18, 0xBE, 0x45, 0x42, 0xBD, 0x88, 0x77, 0x7E, 0xC4, 0x41, 0x06, 0x34, 0x4D, 0x71, 0x3A, 0xC5, 0xCC, 0xA4, 0x1B, 0x7D, 0x3F, 0x3B, 0x86, 0x07, 0xCB, 0x63, 0xD7, 0xF9, 0xDB };
|
||||
private static ReadOnlySpan<byte> P2 => new byte[] { 0x6A, 0x84, 0x12, 0xEA, 0x3B, 0x03, 0x2E, 0xD9, 0xA3, 0x51, 0xB0, 0x4F, 0xE7, 0x4D, 0x59, 0x87, 0xA9, 0xA1, 0x6E, 0x08, 0xC7, 0x3E, 0xD3, 0x15, 0xEE, 0x40, 0x2C, 0xB3, 0x44, 0x78, 0x1F, 0xA0 };
|
||||
// @formatter:on
|
||||
}
|
||||
|
|
@ -10,6 +10,7 @@ using Glamourer.Gui.Tabs;
|
|||
using Glamourer.Gui.Tabs.ActorTab;
|
||||
using Glamourer.Gui.Tabs.AutomationTab;
|
||||
using Glamourer.Gui.Tabs.DesignTab;
|
||||
using Glamourer.Gui.Tabs.UnlocksTab;
|
||||
using Glamourer.Interop;
|
||||
using Glamourer.Interop.Penumbra;
|
||||
using Glamourer.State;
|
||||
|
|
@ -51,7 +52,7 @@ public static class ServiceManager
|
|||
.AddSingleton<BackupService>()
|
||||
.AddSingleton<FrameworkManager>()
|
||||
.AddSingleton<SaveService>()
|
||||
.AddSingleton<PhrasingService>()
|
||||
.AddSingleton<CodeService>()
|
||||
.AddSingleton<ConfigMigrationService>()
|
||||
.AddSingleton<Configuration>();
|
||||
|
||||
|
|
@ -95,7 +96,8 @@ public static class ServiceManager
|
|||
private static IServiceCollection AddState(this IServiceCollection services)
|
||||
=> services.AddSingleton<StateManager>()
|
||||
.AddSingleton<StateEditor>()
|
||||
.AddSingleton<StateListener>();
|
||||
.AddSingleton<StateListener>()
|
||||
.AddSingleton<FunModule>();
|
||||
|
||||
private static IServiceCollection AddUi(this IServiceCollection services)
|
||||
=> services.AddSingleton<DebugTab>()
|
||||
|
|
@ -110,6 +112,9 @@ public static class ServiceManager
|
|||
.AddSingleton<DesignFileSystemSelector>()
|
||||
.AddSingleton<DesignPanel>()
|
||||
.AddSingleton<DesignTab>()
|
||||
.AddSingleton<UnlockTable>()
|
||||
.AddSingleton<UnlockOverview>()
|
||||
.AddSingleton<UnlocksTab>()
|
||||
.AddSingleton<PenumbraChangedItemTooltip>()
|
||||
.AddSingleton<AutomationTab>()
|
||||
.AddSingleton<SetSelector>()
|
||||
|
|
|
|||
137
Glamourer/State/FunModule.cs
Normal file
137
Glamourer/State/FunModule.cs
Normal file
|
|
@ -0,0 +1,137 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using Dalamud.Game.ClientState.Objects.Enums;
|
||||
using Glamourer.Customization;
|
||||
using Glamourer.Interop.Structs;
|
||||
using Glamourer.Services;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
using CustomizeIndex = Glamourer.Customization.CustomizeIndex;
|
||||
|
||||
namespace Glamourer.State;
|
||||
|
||||
public unsafe class FunModule
|
||||
{
|
||||
private readonly ItemManager _items;
|
||||
private readonly CustomizationService _customizations;
|
||||
private readonly CodeService _codes;
|
||||
private readonly Random _rng;
|
||||
private readonly StainId[] _stains;
|
||||
|
||||
public FunModule(CodeService codes, CustomizationService customizations, ItemManager items)
|
||||
{
|
||||
_codes = codes;
|
||||
_customizations = customizations;
|
||||
_items = items;
|
||||
_rng = new Random();
|
||||
_stains = _items.Stains.Keys.Prepend((StainId)0).ToArray();
|
||||
}
|
||||
|
||||
public void ApplyFun(Actor actor, ref CharacterArmor armor, EquipSlot slot)
|
||||
{
|
||||
if (actor.AsObject->ObjectKind is not (byte)ObjectKind.Player || !actor.IsCharacter)
|
||||
return;
|
||||
|
||||
if (actor.AsCharacter->CharacterData.ModelCharaId != 0)
|
||||
return;
|
||||
|
||||
ApplyEmperor(new Span<CharacterArmor>(ref armor));
|
||||
ApplyClown(new Span<CharacterArmor>(ref armor));
|
||||
}
|
||||
|
||||
public void ApplyFun(Actor actor, Span<CharacterArmor> armor, ref Customize customize)
|
||||
{
|
||||
if (actor.AsObject->ObjectKind is not (byte)ObjectKind.Player || !actor.IsCharacter)
|
||||
return;
|
||||
|
||||
if (actor.AsCharacter->CharacterData.ModelCharaId != 0)
|
||||
return;
|
||||
|
||||
ApplyEmperor(armor);
|
||||
ApplyClown(armor);
|
||||
|
||||
ApplyOops(ref customize);
|
||||
ApplyIndividual(ref customize);
|
||||
ApplySizing(actor, ref customize);
|
||||
}
|
||||
|
||||
public void ApplyClown(Span<CharacterArmor> armors)
|
||||
{
|
||||
if (!_codes.EnabledClown)
|
||||
return;
|
||||
|
||||
foreach (ref var armor in armors)
|
||||
{
|
||||
var stainIdx = _rng.Next(0, _stains.Length - 1);
|
||||
armor.Stain = _stains[stainIdx];
|
||||
}
|
||||
}
|
||||
|
||||
public void ApplyEmperor(Span<CharacterArmor> armors, EquipSlot slot = EquipSlot.Unknown)
|
||||
{
|
||||
if (!_codes.EnabledEmperor)
|
||||
return;
|
||||
|
||||
if (armors.Length == 1)
|
||||
{
|
||||
ref var piece = ref armors[0];
|
||||
piece.Variant = 1;
|
||||
piece.Set = (SetId)(slot.IsAccessory() ? 53 : 279);
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0; i < armors.Length; ++i)
|
||||
{
|
||||
ref var piece = ref armors[i];
|
||||
piece.Variant = 1;
|
||||
piece.Set = (SetId)(i < 5 ? 279 : 53);
|
||||
}
|
||||
}
|
||||
|
||||
public void ApplyOops(ref Customize customize)
|
||||
{
|
||||
if (_codes.EnabledOops == Race.Unknown)
|
||||
return;
|
||||
|
||||
var targetClan = (SubRace)((int)_codes.EnabledOops * 2 - (int)customize.Clan % 2);
|
||||
// TODO Female Hrothgar
|
||||
if (_codes.EnabledOops is Race.Hrothgar && customize.Gender is Gender.Female)
|
||||
targetClan = targetClan is SubRace.Lost ? SubRace.Seawolf : SubRace.Hellsguard;
|
||||
_customizations.ChangeClan(ref customize, targetClan);
|
||||
}
|
||||
|
||||
public void ApplyIndividual(ref Customize customize)
|
||||
{
|
||||
if (!_codes.EnabledIndividual)
|
||||
return;
|
||||
|
||||
var set = _customizations.AwaitedService.GetList(customize.Clan, customize.Gender);
|
||||
foreach (var index in Enum.GetValues<CustomizeIndex>())
|
||||
{
|
||||
if (index is CustomizeIndex.Face || !set.IsAvailable(index))
|
||||
continue;
|
||||
|
||||
var valueIdx = _rng.Next(0, set.Count(index) - 1);
|
||||
customize[index] = set.Data(index, valueIdx).Value;
|
||||
}
|
||||
}
|
||||
|
||||
public void ApplySizing(Actor actor, ref Customize customize)
|
||||
{
|
||||
if (_codes.EnabledSizing == CodeService.Sizing.None)
|
||||
return;
|
||||
|
||||
var size = _codes.EnabledSizing switch
|
||||
{
|
||||
CodeService.Sizing.Dwarf when actor.Index == 0 => 0,
|
||||
CodeService.Sizing.Dwarf when actor.Index != 0 => 100,
|
||||
CodeService.Sizing.Giant when actor.Index == 0 => 100,
|
||||
CodeService.Sizing.Giant when actor.Index != 0 => 0,
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
if (customize.Gender is Gender.Female)
|
||||
customize[CustomizeIndex.BustSize] = (CustomizeValue)size;
|
||||
customize[CustomizeIndex.Height] = (CustomizeValue)size;
|
||||
}
|
||||
}
|
||||
|
|
@ -29,6 +29,7 @@ public class StateListener : IDisposable
|
|||
private readonly VisorStateChanged _visorState;
|
||||
private readonly WeaponVisibilityChanged _weaponVisibility;
|
||||
private readonly AutoDesignApplier _autoDesignApplier;
|
||||
private readonly FunModule _funModule;
|
||||
|
||||
public bool Enabled
|
||||
{
|
||||
|
|
@ -38,7 +39,7 @@ public class StateListener : IDisposable
|
|||
|
||||
public StateListener(StateManager manager, ItemManager items, PenumbraService penumbra, ActorService actors, Configuration config,
|
||||
SlotUpdating slotUpdating, WeaponLoading weaponLoading, VisorStateChanged visorState, WeaponVisibilityChanged weaponVisibility,
|
||||
HeadGearVisibilityChanged headGearVisibility, AutoDesignApplier autoDesignApplier)
|
||||
HeadGearVisibilityChanged headGearVisibility, AutoDesignApplier autoDesignApplier, FunModule funModule)
|
||||
{
|
||||
_manager = manager;
|
||||
_items = items;
|
||||
|
|
@ -51,6 +52,7 @@ public class StateListener : IDisposable
|
|||
_weaponVisibility = weaponVisibility;
|
||||
_headGearVisibility = headGearVisibility;
|
||||
_autoDesignApplier = autoDesignApplier;
|
||||
_funModule = funModule;
|
||||
|
||||
if (Enabled)
|
||||
Subscribe();
|
||||
|
|
@ -126,6 +128,7 @@ public class StateListener : IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
_funModule.ApplyFun(actor, new Span<CharacterArmor>((void*) equipDataPtr, 10), ref customize);
|
||||
if (modelId == 0)
|
||||
ProtectRestrictedGear(equipDataPtr, customize.Race, customize.Gender);
|
||||
}
|
||||
|
|
@ -141,6 +144,7 @@ public class StateListener : IDisposable
|
|||
&& _manager.TryGetValue(identifier, out var state))
|
||||
HandleEquipSlot(actor, state, slot, ref armor.Value);
|
||||
|
||||
_funModule.ApplyFun(actor, ref armor.Value, slot);
|
||||
if (!_config.UseRestrictedGearProtection)
|
||||
return;
|
||||
|
||||
|
|
|
|||
|
|
@ -48,9 +48,16 @@ public class CustomizeUnlockManager : IDisposable, ISavable
|
|||
/// <summary> Check if a customization is unlocked for Glamourer. </summary>
|
||||
public bool IsUnlocked(CustomizeData data, out DateTimeOffset time)
|
||||
{
|
||||
// All other customizations are not unlockable.
|
||||
if (data.Index is not CustomizeIndex.Hairstyle and not CustomizeIndex.FacePaint)
|
||||
{
|
||||
time = DateTime.MinValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!Unlockable.TryGetValue(data, out var pair))
|
||||
{
|
||||
time = DateTime.MaxValue;
|
||||
time = DateTime.MinValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -62,7 +69,7 @@ public class CustomizeUnlockManager : IDisposable, ISavable
|
|||
|
||||
if (!IsUnlockedGame(pair.Data))
|
||||
{
|
||||
time = DateTimeOffset.MinValue;
|
||||
time = DateTimeOffset.MaxValue;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -196,6 +196,7 @@ public class ItemUnlockManager : ISavable, IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
changes = false;
|
||||
var inventoryManager = InventoryManager.Instance();
|
||||
if (inventoryManager != null)
|
||||
{
|
||||
|
|
@ -216,7 +217,6 @@ public class ItemUnlockManager : ISavable, IDisposable
|
|||
_currentInventoryIndex = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (changes)
|
||||
Save();
|
||||
}
|
||||
|
|
@ -226,7 +226,7 @@ public class ItemUnlockManager : ISavable, IDisposable
|
|||
// Pseudo items are always unlocked.
|
||||
if (itemId >= _items.ItemSheet.RowCount)
|
||||
{
|
||||
time = DateTimeOffset.MaxValue;
|
||||
time = DateTimeOffset.MinValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -244,7 +244,7 @@ public class ItemUnlockManager : ISavable, IDisposable
|
|||
return true;
|
||||
}
|
||||
|
||||
time = DateTimeOffset.MinValue;
|
||||
time = DateTimeOffset.MaxValue;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -283,7 +283,7 @@ public class ItemUnlockManager : ISavable, IDisposable
|
|||
=> fileNames.UnlockFileItems;
|
||||
|
||||
public void Save()
|
||||
=> _saveService.QueueSave(this);
|
||||
=> _saveService.DelaySave(this, TimeSpan.FromSeconds(10));
|
||||
|
||||
public void Save(StreamWriter writer)
|
||||
{ }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue