From dd5c56de9db82c432afcd374a0664e53fcc6c101 Mon Sep 17 00:00:00 2001 From: Ottermandias Date: Tue, 26 Dec 2023 17:29:45 +0100 Subject: [PATCH] Rework codes and fun a bit. --- .../Gui/Customization/CustomizationDrawer.cs | 2 +- Glamourer/Gui/Equipment/EquipmentDrawer.cs | 4 +- Glamourer/Gui/Tabs/SettingsTab.cs | 17 +- .../Gui/Tabs/UnlocksTab/UnlockOverview.cs | 4 +- Glamourer/Interop/VisorService.cs | 1 - Glamourer/Services/CodeService.cs | 219 +++++++++++------- Glamourer/Services/ItemManager.cs | 2 +- Glamourer/State/FunModule.cs | 211 ++++++++++------- Glamourer/State/StateListener.cs | 6 +- 9 files changed, 269 insertions(+), 197 deletions(-) diff --git a/Glamourer/Gui/Customization/CustomizationDrawer.cs b/Glamourer/Gui/Customization/CustomizationDrawer.cs index a56040d..b741f39 100644 --- a/Glamourer/Gui/Customization/CustomizationDrawer.cs +++ b/Glamourer/Gui/Customization/CustomizationDrawer.cs @@ -113,7 +113,7 @@ public partial class CustomizationDrawer(DalamudPluginInterface pi, CustomizeSer try { - if (_codes.EnabledArtisan) + if (_codes.Enabled(CodeService.CodeFlag.Artisan)) return DrawArtisan(); DrawRaceGenderSelector(); diff --git a/Glamourer/Gui/Equipment/EquipmentDrawer.cs b/Glamourer/Gui/Equipment/EquipmentDrawer.cs index 616e67c..e8840bc 100644 --- a/Glamourer/Gui/Equipment/EquipmentDrawer.cs +++ b/Glamourer/Gui/Equipment/EquipmentDrawer.cs @@ -94,7 +94,7 @@ public class EquipmentDrawer if (_config.SmallEquip) DrawEquipSmall(equipDrawData); - else if (!equipDrawData.Locked && _codes.EnabledArtisan) + else if (!equipDrawData.Locked && _codes.Enabled(CodeService.CodeFlag.Artisan)) DrawEquipArtisan(equipDrawData); else DrawEquipNormal(equipDrawData); @@ -117,7 +117,7 @@ public class EquipmentDrawer if (_config.SmallEquip) DrawWeaponsSmall(mainhand, offhand, allWeapons); - else if (!mainhand.Locked && _codes.EnabledArtisan) + else if (!mainhand.Locked && _codes.Enabled(CodeService.CodeFlag.Artisan)) DrawWeaponsArtisan(mainhand, offhand); else DrawWeaponsNormal(mainhand, offhand, allWeapons); diff --git a/Glamourer/Gui/Tabs/SettingsTab.cs b/Glamourer/Gui/Tabs/SettingsTab.cs index 1b0e27d..340a9fd 100644 --- a/Glamourer/Gui/Tabs/SettingsTab.cs +++ b/Glamourer/Gui/Tabs/SettingsTab.cs @@ -247,22 +247,17 @@ public class SettingsTab : ITab if (ImGui.Checkbox(code, ref state)) { action(state); - _config.Codes[i] = (code, state); - _codeService.VerifyState(); - _config.Save(); + _codeService.SaveState(); } } - if (_codeService.EnabledCaptain) - { - if (ImGui.Button("Who am I?!?")) - _funModule.WhoAmI(); + if (ImGui.Button("Who am I?!?")) + _funModule.WhoAmI(); - ImGui.SameLine(); + ImGui.SameLine(); - if (ImGui.Button("Who is that!?!")) - _funModule.WhoIsThat(); - } + if (ImGui.Button("Who is that!?!")) + _funModule.WhoIsThat(); } private void DrawCodeHints() diff --git a/Glamourer/Gui/Tabs/UnlocksTab/UnlockOverview.cs b/Glamourer/Gui/Tabs/UnlocksTab/UnlockOverview.cs index 10d972f..65203b9 100644 --- a/Glamourer/Gui/Tabs/UnlocksTab/UnlockOverview.cs +++ b/Glamourer/Gui/Tabs/UnlocksTab/UnlockOverview.cs @@ -121,7 +121,7 @@ public class UnlockOverview var icon = _customizations.Manager.GetIcon(customize.IconId); ImGui.Image(icon.ImGuiHandle, iconSize, Vector2.Zero, Vector2.One, - unlocked || _codes.EnabledShirts ? Vector4.One : UnavailableTint); + unlocked || _codes.Enabled(CodeService.CodeFlag.Shirts) ? Vector4.One : UnavailableTint); if (ImGui.IsItemHovered()) { using var tt = ImRaii.Tooltip(); @@ -189,7 +189,7 @@ public class UnlockOverview var (icon, size) = (iconHandle.ImGuiHandle, new Vector2(iconHandle.Width, iconHandle.Height)); - ImGui.Image(icon, iconSize, Vector2.Zero, Vector2.One, unlocked || _codes.EnabledShirts ? Vector4.One : UnavailableTint); + ImGui.Image(icon, iconSize, Vector2.Zero, Vector2.One, unlocked || _codes.Enabled(CodeService.CodeFlag.Shirts) ? Vector4.One : UnavailableTint); if (_favorites.Contains(item)) ImGui.GetWindowDrawList().AddRect(ImGui.GetItemRectMin(), ImGui.GetItemRectMax(), ColorId.FavoriteStarOn.Value(), 2 * ImGuiHelpers.GlobalScale, ImDrawFlags.RoundCornersAll, 4 * ImGuiHelpers.GlobalScale); diff --git a/Glamourer/Interop/VisorService.cs b/Glamourer/Interop/VisorService.cs index 2b49eba..75c5e36 100644 --- a/Glamourer/Interop/VisorService.cs +++ b/Glamourer/Interop/VisorService.cs @@ -18,7 +18,6 @@ public class VisorService : IDisposable { Event = visorStateChanged; _setupVisorHook = interop.HookFromAddress((nint)Human.MemberFunctionPointers.SetupVisor, SetupVisorDetour); - interop.InitializeFromAttributes(this); _setupVisorHook.Enable(); } diff --git a/Glamourer/Services/CodeService.cs b/Glamourer/Services/CodeService.cs index f065e50..82a6477 100644 --- a/Glamourer/Services/CodeService.cs +++ b/Glamourer/Services/CodeService.cs @@ -12,23 +12,66 @@ public class CodeService private readonly Configuration _config; private readonly SHA256 _hasher = SHA256.Create(); - public enum Sizing + [Flags] + public enum CodeFlag : ulong { - None, - Dwarf, - Giant, + Clown = 0x000001, + Emperor = 0x000002, + Individual = 0x000004, + Dwarf = 0x000008, + Giant = 0x000010, + OopsHyur = 0x000020, + OopsElezen = 0x000040, + OopsLalafell = 0x000080, + OopsMiqote = 0x000100, + OopsRoegadyn = 0x000200, + OopsAuRa = 0x000400, + OopsHrothgar = 0x000800, + OopsViera = 0x001000, + Artisan = 0x002000, + SixtyThree = 0x004000, + Shirts = 0x008000, + World = 0x010000, + Elephants = 0x020000, } - 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 EnabledArtisan { get; private set; } - public bool EnabledCaptain { get; private set; } - public bool Enabled63 { get; private set; } - public bool EnabledShirts { get; private set; } - public bool EnabledWorld { get; private set; } + public const CodeFlag DyeCodes = CodeFlag.Clown | CodeFlag.World | CodeFlag.Elephants; + public const CodeFlag GearCodes = CodeFlag.Emperor | CodeFlag.World | CodeFlag.Elephants; + + public const CodeFlag RaceCodes = CodeFlag.OopsHyur + | CodeFlag.OopsElezen + | CodeFlag.OopsLalafell + | CodeFlag.OopsMiqote + | CodeFlag.OopsRoegadyn + | CodeFlag.OopsAuRa + | CodeFlag.OopsHrothgar; + + public const CodeFlag SizeCodes = CodeFlag.Dwarf | CodeFlag.Giant; + + private CodeFlag _enabled; + + public bool Enabled(CodeFlag flag) + => _enabled.HasFlag(flag); + + public bool AnyEnabled(CodeFlag flag) + => (_enabled & flag) != 0; + + public CodeFlag Masked(CodeFlag mask) + => _enabled & mask; + + public Race GetRace() + => (_enabled & RaceCodes) switch + { + CodeFlag.OopsHyur => Race.Hyur, + CodeFlag.OopsElezen => Race.Elezen, + CodeFlag.OopsLalafell => Race.Lalafell, + CodeFlag.OopsMiqote => Race.Miqote, + CodeFlag.OopsRoegadyn => Race.Roegadyn, + CodeFlag.OopsAuRa => Race.AuRa, + CodeFlag.OopsHrothgar => Race.Hrothgar, + CodeFlag.OopsViera => Race.Viera, + _ => Race.Unknown, + }; public CodeService(Configuration config) { @@ -69,92 +112,94 @@ public class CodeService } public Action? CheckCode(string name) + { + var flag = GetCode(name); + if (flag == 0) + return null; + + var badFlags = ~GetMutuallyExclusive(flag); + return v => _enabled = v ? (_enabled | flag) & badFlags : _enabled & ~flag;; + } + + public CodeFlag GetCode(string name) { using var stream = new MemoryStream(Encoding.UTF8.GetBytes(name)); var sha = (ReadOnlySpan)_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 CodeCaptain.SequenceEqual(sha) => v => EnabledCaptain = v, - _ when Code63.SequenceEqual(sha) => v => Enabled63 = v, - _ when CodeDwarf.SequenceEqual(sha) => v => EnabledSizing = v ? Sizing.Dwarf : Sizing.None, - _ when CodeGiant.SequenceEqual(sha) => v => EnabledSizing = v ? Sizing.Giant : Sizing.None, - _ 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 CodeArtisan.SequenceEqual(sha) => v => EnabledArtisan = v, - _ when CodeShirts.SequenceEqual(sha) => v => EnabledShirts = v, - _ when CodeWorld.SequenceEqual(sha) => v => EnabledWorld = v, - _ => null, - }; + foreach (var flag in Enum.GetValues()) + { + if (sha.SequenceEqual(GetSha(flag))) + return flag; + } + + return 0; } - public void VerifyState() + /// Update all enabled states in the config. + public void SaveState() { - if (EnabledSizing == Sizing.None && EnabledOops == Race.Unknown) - return; - for (var i = 0; i < _config.Codes.Count; ++i) { - var (code, enabled) = _config.Codes[i]; - if (!enabled) - continue; - - using var stream = new MemoryStream(Encoding.UTF8.GetBytes(code)); - var sha = (ReadOnlySpan)_hasher.ComputeHash(stream); - var _ = EnabledSizing switch + var name = _config.Codes[i].Code; + var flag = GetCode(name); + if (flag == 0) { - Sizing.Dwarf when sha.SequenceEqual(CodeGiant) => _config.Codes[i] = (code, false), - Sizing.Giant when sha.SequenceEqual(CodeDwarf) => _config.Codes[i] = (code, false), - _ => (string.Empty, false), - }; + _config.Codes.RemoveAt(i--); + continue; + } - var race = OopsRace(sha); - if (race is not Race.Unknown && race != EnabledOops) - _config.Codes[i] = (code, false); + _config.Codes[i] = (name, Enabled(flag)); } + + _config.Save(); } - private static Race OopsRace(ReadOnlySpan sha) - => sha switch - { - _ when CodeOops1.SequenceEqual(sha) => Race.Hyur, - _ when CodeOops2.SequenceEqual(sha) => Race.Elezen, - _ when CodeOops3.SequenceEqual(sha) => Race.Lalafell, - _ when CodeOops4.SequenceEqual(sha) => Race.Miqote, - _ when CodeOops5.SequenceEqual(sha) => Race.Roegadyn, - _ when CodeOops6.SequenceEqual(sha) => Race.AuRa, - _ when CodeOops7.SequenceEqual(sha) => Race.Hrothgar, - _ when CodeOops8.SequenceEqual(sha) => Race.Viera, - _ => Race.Unknown, - }; - // @formatter:off - private static ReadOnlySpan 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 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 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 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 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 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 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 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 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 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 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 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 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 CodeArtisan => new byte[] { 0xDE, 0x01, 0x32, 0x1E, 0x7F, 0x22, 0x80, 0x3D, 0x76, 0xDF, 0x74, 0x0E, 0xEC, 0x33, 0xD3, 0xF4, 0x1A, 0x98, 0x9E, 0x9D, 0x22, 0x5C, 0xAC, 0x3B, 0xFE, 0x0B, 0xC2, 0x13, 0xB9, 0x91, 0x24, 0x61 }; - private static ReadOnlySpan CodeCaptain => new byte[] { 0x5E, 0x0B, 0xDD, 0x86, 0x8F, 0x24, 0xDA, 0x49, 0x1A, 0xD2, 0x59, 0xB9, 0x10, 0x38, 0x29, 0x37, 0x99, 0x9D, 0x53, 0xD9, 0x9B, 0x84, 0x91, 0x5B, 0x6C, 0xCE, 0x3E, 0x2A, 0x38, 0x06, 0x47, 0xE6 }; - private static ReadOnlySpan Code63 => new byte[] { 0xA1, 0x65, 0x60, 0x99, 0xB0, 0x9F, 0xBF, 0xD7, 0x20, 0xC8, 0x29, 0xF6, 0x7B, 0x86, 0x27, 0xF5, 0xBE, 0xA9, 0x5B, 0xB0, 0x20, 0x5E, 0x57, 0x7B, 0xFF, 0xBC, 0x1E, 0x8C, 0x04, 0xF9, 0x35, 0xD3 }; - private static ReadOnlySpan CodeShirts => 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 CodeWorld => new byte[] { 0xFD, 0xA2, 0xD2, 0xBC, 0xD9, 0x8A, 0x7E, 0x2B, 0x52, 0xCB, 0x57, 0x6E, 0x3A, 0x2E, 0x30, 0xBA, 0x4E, 0xAE, 0x42, 0xEA, 0x5C, 0x57, 0xDF, 0x17, 0x37, 0x3C, 0xCE, 0x17, 0x42, 0x43, 0xAE, 0xD0 }; - // @formatter:on + private static CodeFlag GetMutuallyExclusive(CodeFlag flag) + => flag switch + { + CodeFlag.Clown => DyeCodes & ~CodeFlag.Clown, + CodeFlag.Emperor => GearCodes & ~CodeFlag.Emperor, + CodeFlag.Individual => 0, + CodeFlag.Dwarf => SizeCodes & ~CodeFlag.Dwarf, + CodeFlag.Giant => SizeCodes & ~CodeFlag.Giant, + CodeFlag.OopsHyur => RaceCodes & ~CodeFlag.OopsHyur, + CodeFlag.OopsElezen => RaceCodes & ~CodeFlag.OopsElezen, + CodeFlag.OopsLalafell => RaceCodes & ~CodeFlag.OopsLalafell, + CodeFlag.OopsMiqote => RaceCodes & ~CodeFlag.OopsMiqote, + CodeFlag.OopsRoegadyn => RaceCodes & ~CodeFlag.OopsRoegadyn, + CodeFlag.OopsAuRa => RaceCodes & ~CodeFlag.OopsAuRa, + CodeFlag.OopsHrothgar => RaceCodes & ~CodeFlag.OopsHrothgar, + CodeFlag.OopsViera => RaceCodes & ~CodeFlag.OopsViera, + CodeFlag.Artisan => 0, + CodeFlag.SixtyThree => 0, + CodeFlag.Shirts => 0, + CodeFlag.World => (DyeCodes | GearCodes) & ~CodeFlag.World, + CodeFlag.Elephants => (DyeCodes | GearCodes) & ~CodeFlag.Elephants, + _ => 0, + }; + + private static ReadOnlySpan GetSha(CodeFlag flag) + => flag switch + { + CodeFlag.Clown => [ 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 ], + CodeFlag.Emperor => [ 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 ], + CodeFlag.Individual => [ 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 ], + CodeFlag.Dwarf => [ 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 ], + CodeFlag.Giant => [ 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 ], + CodeFlag.OopsHyur => [ 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 ], + CodeFlag.OopsElezen => [ 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 ], + CodeFlag.OopsLalafell => [ 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 ], + CodeFlag.OopsMiqote => [ 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 ], + CodeFlag.OopsRoegadyn => [ 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 ], + CodeFlag.OopsAuRa => [ 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 ], + CodeFlag.OopsHrothgar => [ 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 ], + CodeFlag.OopsViera => [ 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 ], + CodeFlag.Artisan => [ 0xDE, 0x01, 0x32, 0x1E, 0x7F, 0x22, 0x80, 0x3D, 0x76, 0xDF, 0x74, 0x0E, 0xEC, 0x33, 0xD3, 0xF4, 0x1A, 0x98, 0x9E, 0x9D, 0x22, 0x5C, 0xAC, 0x3B, 0xFE, 0x0B, 0xC2, 0x13, 0xB9, 0x91, 0x24, 0x61 ], + CodeFlag.SixtyThree => [ 0xA1, 0x65, 0x60, 0x99, 0xB0, 0x9F, 0xBF, 0xD7, 0x20, 0xC8, 0x29, 0xF6, 0x7B, 0x86, 0x27, 0xF5, 0xBE, 0xA9, 0x5B, 0xB0, 0x20, 0x5E, 0x57, 0x7B, 0xFF, 0xBC, 0x1E, 0x8C, 0x04, 0xF9, 0x35, 0xD3 ], + CodeFlag.Shirts => [ 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 ], + CodeFlag.World => [ 0xFD, 0xA2, 0xD2, 0xBC, 0xD9, 0x8A, 0x7E, 0x2B, 0x52, 0xCB, 0x57, 0x6E, 0x3A, 0x2E, 0x30, 0xBA, 0x4E, 0xAE, 0x42, 0xEA, 0x5C, 0x57, 0xDF, 0x17, 0x37, 0x3C, 0xCE, 0x17, 0x42, 0x43, 0xAE, 0xD0 ], + CodeFlag.Elephants => [ 0x9F, 0x4C, 0xCF, 0x6D, 0xC4, 0x01, 0x31, 0x46, 0x02, 0x05, 0x31, 0xED, 0xED, 0xB2, 0x66, 0x29, 0x31, 0x09, 0x1E, 0xE7, 0x47, 0xDE, 0x7B, 0x03, 0xB0, 0x3C, 0x06, 0x76, 0x26, 0x91, 0xDF, 0xB2 ], + _ => [], + }; } diff --git a/Glamourer/Services/ItemManager.cs b/Glamourer/Services/ItemManager.cs index 52709c5..6e73945 100644 --- a/Glamourer/Services/ItemManager.cs +++ b/Glamourer/Services/ItemManager.cs @@ -21,7 +21,7 @@ public class ItemManager public readonly ObjectIdentification ObjectIdentification; public readonly ExcelSheet ItemSheet; - public readonly DictStain Stains; + public readonly DictStain Stains; public readonly ItemData ItemData; public readonly RestrictedGear RestrictedGear; diff --git a/Glamourer/State/FunModule.cs b/Glamourer/State/FunModule.cs index deb7771..9e0ffa4 100644 --- a/Glamourer/State/FunModule.cs +++ b/Glamourer/State/FunModule.cs @@ -8,6 +8,7 @@ using Glamourer.Interop; using Glamourer.Interop.Structs; using Glamourer.Services; using ImGuiNET; +using OtterGui; using OtterGui.Classes; using Penumbra.GameData.Enums; using Penumbra.GameData.Structs; @@ -25,18 +26,18 @@ public unsafe class FunModule : IDisposable AprilFirst, } - private readonly WorldSets _worldSets = new(); - private readonly ItemManager _items; - private readonly CustomizeService _customizations; - private readonly Configuration _config; - private readonly CodeService _codes; - private readonly Random _rng; - private readonly GenericPopupWindow _popupWindow; - private readonly StateManager _stateManager; - private readonly DesignConverter _designConverter; - private readonly DesignManager _designManager; - private readonly ObjectManager _objects; - private readonly StainId[] _stains; + private readonly WorldSets _worldSets = new(); + private readonly ItemManager _items; + private readonly CustomizeService _customizations; + private readonly Configuration _config; + private readonly CodeService _codes; + private readonly Random _rng; + private readonly GenericPopupWindow _popupWindow; + private readonly StateManager _stateManager; + private readonly DesignConverter _designConverter; + private readonly DesignManager _designManager; + private readonly ObjectManager _objects; + private readonly StainId[] _stains; public FestivalType CurrentFestival { get; private set; } = FestivalType.None; private FunEquipSet? _festivalSet; @@ -89,54 +90,92 @@ public unsafe class FunModule : IDisposable public void Dispose() => DayChangeTracker.DayChanged -= OnDayChange; - public void ApplyFun(Actor actor, ref CharacterArmor armor, EquipSlot slot) + private bool IsInFestival + => _config.DisableFestivals == 0 && _festivalSet != null; + + public void ApplyFunToSlot(Actor actor, ref CharacterArmor armor, EquipSlot slot) { if (!ValidFunTarget(actor)) return; - if (_config.DisableFestivals == 0 && _festivalSet != null - || _codes.EnabledWorld && actor.Index != 0) + if (IsInFestival) { - armor = actor.Model.Valid ? actor.Model.GetArmor(slot) : armor; + KeepOldArmor(actor, slot, ref armor); + return; } - else + + switch (_codes.Masked(CodeService.GearCodes)) { - ApplyEmperor(new Span(ref armor), slot); - ApplyClown(new Span(ref armor)); + case CodeService.CodeFlag.Emperor: + SetRandomItem(slot, ref armor); + break; + case CodeService.CodeFlag.Elephants: + SetElephant(slot, ref armor); + break; + case CodeService.CodeFlag.World when actor.Index != 0: + KeepOldArmor(actor, slot, ref armor); + break; + } + + switch (_codes.Masked(CodeService.DyeCodes)) + { + case CodeService.CodeFlag.Clown: + SetRandomDye(ref armor); + break; } } - public void ApplyFun(Actor actor, Span armor, ref CustomizeArray customize) + public void ApplyFunOnLoad(Actor actor, Span armor, ref CustomizeArray customize) { if (!ValidFunTarget(actor)) return; - if (_config.DisableFestivals == 0 && _festivalSet != null) + // First set the race, if any. + SetRace(ref customize); + // Now apply the gender. + SetGender(ref customize); + // Randomize customizations inside the race and gender combo. + RandomizeCustomize(ref customize); + // Finally, apply forced sizes. + SetSize(actor, ref customize); + + // Apply the festival gear with priority over all gear codes. + if (IsInFestival) { - _festivalSet.Apply(_stains, _rng, armor); - } - else if (_codes.EnabledWorld && actor.Index != 0) - { - _worldSets.Apply(actor, _rng, armor); - } - else - { - ApplyEmperor(armor); - ApplyClown(armor); + _festivalSet!.Apply(_stains, _rng, armor); + return; } - ApplyOops(ref customize); - Apply63(ref customize); - ApplyIndividual(ref customize); - ApplySizing(actor, ref customize); + switch (_codes.Masked(CodeService.GearCodes)) + { + case CodeService.CodeFlag.Emperor: + foreach (var (slot, idx) in EquipSlotExtensions.EqdpSlots.WithIndex()) + SetRandomItem(slot, ref armor[idx]); + break; + case CodeService.CodeFlag.Elephants: + SetElephant(EquipSlot.Body, ref armor[1]); + SetElephant(EquipSlot.Head, ref armor[0]); + break; + case CodeService.CodeFlag.World when actor.Index != 0: + _worldSets.Apply(actor, _rng, armor); + break; + } + + switch (_codes.Masked(CodeService.DyeCodes)) + { + case CodeService.CodeFlag.Clown: + foreach (ref var piece in armor) + SetRandomDye(ref piece); + break; + } } - public void ApplyFun(Actor actor, ref CharacterWeapon weapon, EquipSlot slot) + public void ApplyFunToWeapon(Actor actor, ref CharacterWeapon weapon, EquipSlot slot) { if (!ValidFunTarget(actor)) return; - if (_codes.EnabledWorld) + if (_codes.Enabled(CodeService.CodeFlag.World) && actor.Index != 0) _worldSets.Apply(actor, _rng, ref weapon, slot); } @@ -146,55 +185,58 @@ public unsafe class FunModule : IDisposable && !actor.IsTransformed && actor.AsCharacter->CharacterData.ModelCharaId == 0; - public void ApplyClown(Span armors) - { - if (!_codes.EnabledClown) - return; + private static void KeepOldArmor(Actor actor, EquipSlot slot, ref CharacterArmor armor) + => armor = actor.Model.Valid ? actor.Model.GetArmor(slot) : armor; - foreach (ref var armor in armors) - { - var stainIdx = _rng.Next(0, _stains.Length - 1); - armor.Stain = _stains[stainIdx]; - } + private void SetRandomDye(ref CharacterArmor armor) + { + var stainIdx = _rng.Next(0, _stains.Length - 1); + armor.Stain = _stains[stainIdx]; } - public void ApplyEmperor(Span armors, EquipSlot slot = EquipSlot.Unknown) + private void SetRandomItem(EquipSlot slot, ref CharacterArmor armor) { - if (!_codes.EnabledEmperor) - return; - - if (armors.Length == 1) - SetItem(slot, ref armors[0]); - else - for (var i = 0u; i < armors.Length; ++i) - SetItem(i.ToEquipSlot(), ref armors[(int)i]); - return; - - void SetItem(EquipSlot slot2, ref CharacterArmor armor) - { - var list = _items.ItemData.ByType[slot2.ToEquipType()]; - var rng = _rng.Next(0, list.Count - 1); - var item = list[rng]; - armor.Set = item.PrimaryId; - armor.Variant = item.Variant; - } + var list = _items.ItemData.ByType[slot.ToEquipType()]; + var rng = _rng.Next(0, list.Count - 1); + var item = list[rng]; + armor.Set = item.PrimaryId; + armor.Variant = item.Variant; } - public void ApplyOops(ref CustomizeArray customize) + private void SetElephant(EquipSlot slot, ref CharacterArmor armor) { - if (_codes.EnabledOops == Race.Unknown) + armor = slot switch + { + EquipSlot.Body => new CharacterArmor(6133, 1, 87), + EquipSlot.Head => new CharacterArmor(6133, 1, 87), + _ => armor, + }; + } + + private void SetRace(ref CustomizeArray customize) + { + var race = _codes.GetRace(); + if (race == Race.Unknown) return; - var targetClan = (SubRace)((int)_codes.EnabledOops * 2 - (int)customize.Clan % 2); + var targetClan = (SubRace)((int)race * 2 - (int)customize.Clan % 2); // TODO Female Hrothgar - if (_codes.EnabledOops is Race.Hrothgar && customize.Gender is Gender.Female) + if (race 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 CustomizeArray customize) + private void SetGender(ref CustomizeArray customize) { - if (!_codes.EnabledIndividual) + if (!_codes.Enabled(CodeService.CodeFlag.SixtyThree) || customize.Race is Race.Hrothgar) // TODO Female Hrothgar + return; + + _customizations.ChangeGender(ref customize, customize.Gender is Gender.Male ? Gender.Female : Gender.Male); + } + + private void RandomizeCustomize(ref CustomizeArray customize) + { + if (!_codes.Enabled(CodeService.CodeFlag.Individual)) return; var set = _customizations.Manager.GetSet(customize.Clan, customize.Gender); @@ -208,27 +250,18 @@ public unsafe class FunModule : IDisposable } } - public void Apply63(ref CustomizeArray customize) + private void SetSize(Actor actor, ref CustomizeArray customize) { - if (!_codes.Enabled63 || customize.Race is Race.Hrothgar) // TODO Female Hrothgar - return; - - _customizations.ChangeGender(ref customize, customize.Gender is Gender.Male ? Gender.Female : Gender.Male); - } - - public void ApplySizing(Actor actor, ref CustomizeArray customize) - { - if (_codes.EnabledSizing == CodeService.Sizing.None) - return; - - var size = _codes.EnabledSizing switch + var size = _codes.Masked(CodeService.SizeCodes) 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, + CodeService.CodeFlag.Dwarf when actor.Index == 0 => (byte)0, + CodeService.CodeFlag.Dwarf => (byte)100, + CodeService.CodeFlag.Giant when actor.Index == 0 => (byte)100, + CodeService.CodeFlag.Giant => (byte)0, + _ => byte.MaxValue, }; + if (size == byte.MaxValue) + return; if (customize.Gender is Gender.Female) customize[CustomizeIndex.BustSize] = (CustomizeValue)size; diff --git a/Glamourer/State/StateListener.cs b/Glamourer/State/StateListener.cs index 1f3c36b..c4c5fb1 100644 --- a/Glamourer/State/StateListener.cs +++ b/Glamourer/State/StateListener.cs @@ -133,7 +133,7 @@ public class StateListener : IDisposable _creatingState.TempUnlock(); } - _funModule.ApplyFun(actor, new Span((void*)equipDataPtr, 10), ref customize); + _funModule.ApplyFunOnLoad(actor, new Span((void*)equipDataPtr, 10), ref customize); if (modelId == 0) ProtectRestrictedGear(equipDataPtr, customize.Race, customize.Gender); } @@ -216,7 +216,7 @@ public class StateListener : IDisposable locked = state[slot, false] is StateChanged.Source.Ipc; } - _funModule.ApplyFun(actor, ref armor.Value, slot); + _funModule.ApplyFunToSlot(actor, ref armor.Value, slot); if (!_config.UseRestrictedGearProtection || locked) return; @@ -315,7 +315,7 @@ public class StateListener : IDisposable _lastFistOffhand = new CharacterWeapon((PrimaryId)(weapon.Value.Skeleton.Id + 50), weapon.Value.Weapon, weapon.Value.Variant, weapon.Value.Stain); - _funModule.ApplyFun(actor, ref weapon.Value, slot); + _funModule.ApplyFunToWeapon(actor, ref weapon.Value, slot); } /// Update base data for a single changed equipment slot.