Rework codes and fun a bit.

This commit is contained in:
Ottermandias 2023-12-26 17:29:45 +01:00
parent 1fa9afb9c6
commit dd5c56de9d
9 changed files with 269 additions and 197 deletions

View file

@ -113,7 +113,7 @@ public partial class CustomizationDrawer(DalamudPluginInterface pi, CustomizeSer
try
{
if (_codes.EnabledArtisan)
if (_codes.Enabled(CodeService.CodeFlag.Artisan))
return DrawArtisan();
DrawRaceGenderSelector();

View file

@ -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);

View file

@ -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()

View file

@ -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);

View file

@ -18,7 +18,6 @@ public class VisorService : IDisposable
{
Event = visorStateChanged;
_setupVisorHook = interop.HookFromAddress<UpdateVisorDelegateInternal>((nint)Human.MemberFunctionPointers.SetupVisor, SetupVisorDetour);
interop.InitializeFromAttributes(this);
_setupVisorHook.Enable();
}

View file

@ -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<bool>? 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<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 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<CodeFlag>())
{
if (sha.SequenceEqual(GetSha(flag)))
return flag;
}
return 0;
}
public void VerifyState()
/// <summary> Update all enabled states in the config. </summary>
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<byte>)_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<byte> 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<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> 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> 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<byte> 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<byte> 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<byte> 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<byte> 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<byte> 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 ],
_ => [],
};
}

View file

@ -21,7 +21,7 @@ public class ItemManager
public readonly ObjectIdentification ObjectIdentification;
public readonly ExcelSheet<Lumina.Excel.GeneratedSheets.Item> ItemSheet;
public readonly DictStain Stains;
public readonly DictStain Stains;
public readonly ItemData ItemData;
public readonly RestrictedGear RestrictedGear;

View file

@ -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<CharacterArmor>(ref armor), slot);
ApplyClown(new Span<CharacterArmor>(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<CharacterArmor> armor, ref CustomizeArray customize)
public void ApplyFunOnLoad(Actor actor, Span<CharacterArmor> 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<CharacterArmor> 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<CharacterArmor> 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;

View file

@ -133,7 +133,7 @@ public class StateListener : IDisposable
_creatingState.TempUnlock();
}
_funModule.ApplyFun(actor, new Span<CharacterArmor>((void*)equipDataPtr, 10), ref customize);
_funModule.ApplyFunOnLoad(actor, new Span<CharacterArmor>((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);
}
/// <summary> Update base data for a single changed equipment slot. </summary>