This commit is contained in:
Ottermandias 2023-03-12 15:52:38 +01:00
parent 4cf082aa19
commit 1eb16b98a8
17 changed files with 735 additions and 497 deletions

View file

@ -5,317 +5,304 @@ using Dalamud.Logging;
using Dalamud.Plugin;
using Dalamud.Plugin.Ipc;
using System;
using Glamourer.Designs;
using Glamourer.State;
using Penumbra.Api.Enums;
namespace Glamourer.Api;
namespace Glamourer;
public class GlamourerIpc : IDisposable
public partial class Glamourer
{
public const int CurrentApiVersion = 0;
public const string LabelProviderApiVersion = "Glamourer.ApiVersion";
public const string LabelProviderGetAllCustomization = "Glamourer.GetAllCustomization";
public const string LabelProviderGetAllCustomizationFromCharacter = "Glamourer.GetAllCustomizationFromCharacter";
public const string LabelProviderApplyAll = "Glamourer.ApplyAll";
public const string LabelProviderApplyAllToCharacter = "Glamourer.ApplyAllToCharacter";
public const string LabelProviderApplyOnlyEquipment = "Glamourer.ApplyOnlyEquipment";
public const string LabelProviderApplyOnlyEquipmentToCharacter = "Glamourer.ApplyOnlyEquipmentToCharacter";
public const string LabelProviderApplyOnlyCustomization = "Glamourer.ApplyOnlyCustomization";
public const string LabelProviderApplyOnlyCustomizationToCharacter = "Glamourer.ApplyOnlyCustomizationToCharacter";
public const string LabelProviderRevert = "Glamourer.Revert";
public const string LabelProviderRevertCharacter = "Glamourer.RevertCharacter";
private readonly ClientState _clientState;
private readonly ObjectTable _objectTable;
private readonly DalamudPluginInterface _pluginInterface;
internal ICallGateProvider<string, string?>? ProviderGetAllCustomization;
internal ICallGateProvider<Character?, string?>? ProviderGetAllCustomizationFromCharacter;
internal ICallGateProvider<string, string, object>? ProviderApplyAll;
internal ICallGateProvider<string, Character?, object>? ProviderApplyAllToCharacter;
internal ICallGateProvider<string, string, object>? ProviderApplyOnlyCustomization;
internal ICallGateProvider<string, Character?, object>? ProviderApplyOnlyCustomizationToCharacter;
internal ICallGateProvider<string, string, object>? ProviderApplyOnlyEquipment;
internal ICallGateProvider<string, Character?, object>? ProviderApplyOnlyEquipmentToCharacter;
internal ICallGateProvider<string, object>? ProviderRevert;
internal ICallGateProvider<Character?, object>? ProviderRevertCharacter;
internal ICallGateProvider<int>? ProviderGetApiVersion;
public GlamourerIpc(ClientState clientState, ObjectTable objectTable, DalamudPluginInterface pluginInterface)
public class GlamourerIpc : IDisposable
{
_clientState = clientState;
_objectTable = objectTable;
_pluginInterface = pluginInterface;
public const int CurrentApiVersion = 0;
public const string LabelProviderApiVersion = "Glamourer.ApiVersion";
public const string LabelProviderGetAllCustomization = "Glamourer.GetAllCustomization";
public const string LabelProviderGetAllCustomizationFromCharacter = "Glamourer.GetAllCustomizationFromCharacter";
public const string LabelProviderApplyAll = "Glamourer.ApplyAll";
public const string LabelProviderApplyAllToCharacter = "Glamourer.ApplyAllToCharacter";
public const string LabelProviderApplyOnlyEquipment = "Glamourer.ApplyOnlyEquipment";
public const string LabelProviderApplyOnlyEquipmentToCharacter = "Glamourer.ApplyOnlyEquipmentToCharacter";
public const string LabelProviderApplyOnlyCustomization = "Glamourer.ApplyOnlyCustomization";
public const string LabelProviderApplyOnlyCustomizationToCharacter = "Glamourer.ApplyOnlyCustomizationToCharacter";
public const string LabelProviderRevert = "Glamourer.Revert";
public const string LabelProviderRevertCharacter = "Glamourer.RevertCharacter";
InitializeProviders();
}
private readonly ObjectTable _objectTable;
private readonly DalamudPluginInterface _pluginInterface;
private readonly Glamourer _glamourer;
public void Dispose()
=> DisposeProviders();
internal ICallGateProvider<string, string?>? ProviderGetAllCustomization;
internal ICallGateProvider<Character?, string?>? ProviderGetAllCustomizationFromCharacter;
internal ICallGateProvider<string, string, object>? ProviderApplyAll;
internal ICallGateProvider<string, Character?, object>? ProviderApplyAllToCharacter;
internal ICallGateProvider<string, string, object>? ProviderApplyOnlyCustomization;
internal ICallGateProvider<string, Character?, object>? ProviderApplyOnlyCustomizationToCharacter;
internal ICallGateProvider<string, string, object>? ProviderApplyOnlyEquipment;
internal ICallGateProvider<string, Character?, object>? ProviderApplyOnlyEquipmentToCharacter;
internal ICallGateProvider<string, object>? ProviderRevert;
internal ICallGateProvider<Character?, object>? ProviderRevertCharacter;
internal ICallGateProvider<int>? ProviderGetApiVersion;
private void DisposeProviders()
{
ProviderGetAllCustomization?.UnregisterFunc();
ProviderGetAllCustomizationFromCharacter?.UnregisterFunc();
ProviderApplyAll?.UnregisterAction();
ProviderApplyAllToCharacter?.UnregisterAction();
ProviderApplyOnlyCustomization?.UnregisterAction();
ProviderApplyOnlyCustomizationToCharacter?.UnregisterAction();
ProviderApplyOnlyEquipment?.UnregisterAction();
ProviderApplyOnlyEquipmentToCharacter?.UnregisterAction();
ProviderRevert?.UnregisterAction();
ProviderRevertCharacter?.UnregisterAction();
ProviderGetApiVersion?.UnregisterFunc();
}
private void InitializeProviders()
{
try
public GlamourerIpc(Glamourer glamourer, ClientState clientState, ObjectTable objectTable,
DalamudPluginInterface pluginInterface)
{
ProviderGetApiVersion = _pluginInterface.GetIpcProvider<int>(LabelProviderApiVersion);
ProviderGetApiVersion.RegisterFunc(GetApiVersion);
}
catch (Exception ex)
{
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderApiVersion}.");
}
//try
//{
// ProviderGetAllCustomization = _pluginInterface.GetIpcProvider<string, string?>(LabelProviderGetAllCustomization);
// ProviderGetAllCustomization.RegisterFunc(GetAllCustomization);
//}
//catch (Exception ex)
//{
// PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderApplyOnlyEquipment}.");
//}
//
//try
//{
// ProviderGetAllCustomizationFromCharacter = _pluginInterface.GetIpcProvider<Character?, string?>(LabelProviderGetAllCustomizationFromCharacter);
// ProviderGetAllCustomizationFromCharacter.RegisterFunc(GetAllCustomization);
//}
//catch (Exception ex)
//{
// PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderGetAllCustomizationFromCharacter}.");
//}
//
//try
//{
// ProviderApplyAll =
// _pluginInterface.GetIpcProvider<string, string, object>(LabelProviderApplyAll);
// ProviderApplyAll.RegisterAction(ApplyAll);
//}
//catch (Exception ex)
//{
// PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderApplyAll}.");
//}
//
//try
//{
// ProviderApplyAllToCharacter =
// _pluginInterface.GetIpcProvider<string, Character?, object>(LabelProviderApplyAllToCharacter);
// ProviderApplyAllToCharacter.RegisterAction(ApplyAll);
//}
//catch (Exception ex)
//{
// PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderApplyAll}.");
//}
//
//try
//{
// ProviderApplyOnlyCustomization =
// _pluginInterface.GetIpcProvider<string, string, object>(LabelProviderApplyOnlyCustomization);
// ProviderApplyOnlyCustomization.RegisterAction(ApplyOnlyCustomization);
//}
//catch (Exception ex)
//{
// PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderApplyOnlyCustomization}.");
//}
//
//try
//{
// ProviderApplyOnlyCustomizationToCharacter =
// _pluginInterface.GetIpcProvider<string, Character?, object>(LabelProviderApplyOnlyCustomizationToCharacter);
// ProviderApplyOnlyCustomizationToCharacter.RegisterAction(ApplyOnlyCustomization);
//}
//catch (Exception ex)
//{
// PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderApplyOnlyCustomization}.");
//}
//
//try
//{
// ProviderApplyOnlyEquipment =
// _pluginInterface.GetIpcProvider<string, string, object>(LabelProviderApplyOnlyEquipment);
// ProviderApplyOnlyEquipment.RegisterAction(ApplyOnlyEquipment);
//}
//catch (Exception ex)
//{
// PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderApplyOnlyEquipment}.");
//}
//
//try
//{
// ProviderApplyOnlyEquipmentToCharacter =
// _pluginInterface.GetIpcProvider<string, Character?, object>(LabelProviderApplyOnlyEquipmentToCharacter);
// ProviderApplyOnlyEquipmentToCharacter.RegisterAction(ApplyOnlyEquipment);
//}
//catch (Exception ex)
//{
// PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderApplyOnlyEquipment}.");
//}
//
//try
//{
// ProviderRevert =
// _pluginInterface.GetIpcProvider<string, object>(LabelProviderRevert);
// ProviderRevert.RegisterAction(Revert);
//}
//catch (Exception ex)
//{
// PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderRevert}.");
//}
//
//try
//{
// ProviderRevertCharacter =
// _pluginInterface.GetIpcProvider<Character?, object>(LabelProviderRevertCharacter);
// ProviderRevertCharacter.RegisterAction(Revert);
//}
//catch (Exception ex)
//{
// PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderRevert}.");
//}
}
_glamourer = glamourer;
_objectTable = objectTable;
_pluginInterface = pluginInterface;
private static int GetApiVersion()
=> CurrentApiVersion;
//
//private void ApplyAll(string customization, string characterName)
//{
// var save = CharacterSave.FromString(customization);
// foreach (var gameObject in _objectTable)
// {
// if (gameObject.Name.ToString() != characterName)
// continue;
//
// var player = (Character)gameObject;
// Glamourer.RevertableDesigns.Revert(player);
// save.Apply(player);
// Glamourer.Penumbra.UpdateCharacters(player, null);
// break;
// }
//}
//
//private void ApplyAll(string customization, Character? character)
//{
// if (character == null)
// return;
// var save = CharacterSave.FromString(customization);
// Glamourer.RevertableDesigns.Revert(character);
// save.Apply(character);
// Glamourer.Penumbra.UpdateCharacters(character, null);
//}
//
//private void ApplyOnlyCustomization(string customization, string characterName)
//{
// var save = CharacterSave.FromString(customization);
// foreach (var gameObject in _objectTable)
// {
// if (gameObject.Name.ToString() != characterName)
// continue;
//
// var player = (Character)gameObject;
// Glamourer.RevertableDesigns.Revert(player);
// save.ApplyOnlyCustomizations(player);
// Glamourer.Penumbra.UpdateCharacters(player, null);
// break;
// }
//}
//
//private void ApplyOnlyCustomization(string customization, Character? character)
//{
// if (character == null)
// return;
// var save = CharacterSave.FromString(customization);
// Glamourer.RevertableDesigns.Revert(character);
// save.ApplyOnlyCustomizations(character);
// Glamourer.Penumbra.UpdateCharacters(character, null);
//}
//
//private void ApplyOnlyEquipment(string customization, string characterName)
//{
// var save = CharacterSave.FromString(customization);
// foreach (var gameObject in _objectTable)
// {
// if (gameObject.Name.ToString() != characterName)
// continue;
//
// var player = (Character)gameObject;
// Glamourer.RevertableDesigns.Revert(player);
// save.ApplyOnlyEquipment(player);
// Glamourer.Penumbra.UpdateCharacters(player, null);
// break;
// }
//}
//
//private void ApplyOnlyEquipment(string customization, Character? character)
//{
// if (character == null)
// return;
// var save = CharacterSave.FromString(customization);
// Glamourer.RevertableDesigns.Revert(character);
// save.ApplyOnlyEquipment(character);
// Glamourer.Penumbra.UpdateCharacters(character, null);
//}
//
//private void Revert(string characterName)
//{
// foreach (var gameObject in _objectTable)
// {
// if (gameObject.Name.ToString() != characterName)
// continue;
//
// var player = (Character)gameObject;
// Glamourer.RevertableDesigns.Revert(player);
// Glamourer.Penumbra.UpdateCharacters(player, null);
// return;
// }
//
// Glamourer.RevertableDesigns.RevertByNameWithoutApplication(characterName);
//}
//
//private void Revert(Character? character)
//{
// if (character == null)
// return;
// Glamourer.RevertableDesigns.Revert(character);
// Glamourer.Penumbra.UpdateCharacters(character, null);
//}
//
//private string? GetAllCustomization(Character? character)
//{
// if (character == null)
// return null;
//
// CharacterSave save = new CharacterSave();
// save.LoadCharacter(character);
// return save.ToBase64();
//}
//
//private string? GetAllCustomization(string characterName)
//{
// CharacterSave save = null!;
// foreach (var gameObject in _objectTable)
// {
// if (gameObject.Name.ToString() != characterName)
// continue;
//
// var player = (Character)gameObject;
// save = new CharacterSave();
// save.LoadCharacter(player);
// break;
// }
//
// return save?.ToBase64() ?? null;
//}
InitializeProviders();
}
public void Dispose()
=> DisposeProviders();
private void DisposeProviders()
{
ProviderGetAllCustomization?.UnregisterFunc();
ProviderGetAllCustomizationFromCharacter?.UnregisterFunc();
ProviderApplyAll?.UnregisterAction();
ProviderApplyAllToCharacter?.UnregisterAction();
ProviderApplyOnlyCustomization?.UnregisterAction();
ProviderApplyOnlyCustomizationToCharacter?.UnregisterAction();
ProviderApplyOnlyEquipment?.UnregisterAction();
ProviderApplyOnlyEquipmentToCharacter?.UnregisterAction();
ProviderRevert?.UnregisterAction();
ProviderRevertCharacter?.UnregisterAction();
ProviderGetApiVersion?.UnregisterFunc();
}
private void InitializeProviders()
{
try
{
ProviderGetApiVersion = _pluginInterface.GetIpcProvider<int>(LabelProviderApiVersion);
ProviderGetApiVersion.RegisterFunc(GetApiVersion);
}
catch (Exception ex)
{
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderApiVersion}.");
}
try
{
ProviderGetAllCustomization = _pluginInterface.GetIpcProvider<string, string?>(LabelProviderGetAllCustomization);
ProviderGetAllCustomization.RegisterFunc(GetAllCustomization);
}
catch (Exception ex)
{
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderApplyOnlyEquipment}.");
}
try
{
ProviderGetAllCustomizationFromCharacter =
_pluginInterface.GetIpcProvider<Character?, string?>(LabelProviderGetAllCustomizationFromCharacter);
ProviderGetAllCustomizationFromCharacter.RegisterFunc(GetAllCustomization);
}
catch (Exception ex)
{
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderGetAllCustomizationFromCharacter}.");
}
try
{
ProviderApplyAll =
_pluginInterface.GetIpcProvider<string, string, object>(LabelProviderApplyAll);
ProviderApplyAll.RegisterAction(ApplyAll);
}
catch (Exception ex)
{
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderApplyAll}.");
}
try
{
ProviderApplyAllToCharacter =
_pluginInterface.GetIpcProvider<string, Character?, object>(LabelProviderApplyAllToCharacter);
ProviderApplyAllToCharacter.RegisterAction(ApplyAll);
}
catch (Exception ex)
{
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderApplyAll}.");
}
try
{
ProviderApplyOnlyCustomization =
_pluginInterface.GetIpcProvider<string, string, object>(LabelProviderApplyOnlyCustomization);
ProviderApplyOnlyCustomization.RegisterAction(ApplyOnlyCustomization);
}
catch (Exception ex)
{
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderApplyOnlyCustomization}.");
}
try
{
ProviderApplyOnlyCustomizationToCharacter =
_pluginInterface.GetIpcProvider<string, Character?, object>(LabelProviderApplyOnlyCustomizationToCharacter);
ProviderApplyOnlyCustomizationToCharacter.RegisterAction(ApplyOnlyCustomization);
}
catch (Exception ex)
{
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderApplyOnlyCustomization}.");
}
try
{
ProviderApplyOnlyEquipment =
_pluginInterface.GetIpcProvider<string, string, object>(LabelProviderApplyOnlyEquipment);
ProviderApplyOnlyEquipment.RegisterAction(ApplyOnlyEquipment);
}
catch (Exception ex)
{
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderApplyOnlyEquipment}.");
}
try
{
ProviderApplyOnlyEquipmentToCharacter =
_pluginInterface.GetIpcProvider<string, Character?, object>(LabelProviderApplyOnlyEquipmentToCharacter);
ProviderApplyOnlyEquipmentToCharacter.RegisterAction(ApplyOnlyEquipment);
}
catch (Exception ex)
{
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderApplyOnlyEquipment}.");
}
try
{
ProviderRevert =
_pluginInterface.GetIpcProvider<string, object>(LabelProviderRevert);
ProviderRevert.RegisterAction(Revert);
}
catch (Exception ex)
{
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderRevert}.");
}
try
{
ProviderRevertCharacter =
_pluginInterface.GetIpcProvider<Character?, object>(LabelProviderRevertCharacter);
ProviderRevertCharacter.RegisterAction(Revert);
}
catch (Exception ex)
{
PluginLog.Error(ex, $"Error registering IPC provider for {LabelProviderRevert}.");
}
}
private static int GetApiVersion()
=> CurrentApiVersion;
private void ApplyAll(string customization, string characterName)
{
foreach (var gameObject in _objectTable)
{
if (gameObject.Name.ToString() == characterName)
{
ApplyAll(customization, gameObject as Character);
return;
}
}
}
private void ApplyAll(string customization, Character? character)
{
if (character == null)
return;
var design = Design.CreateTemporaryFromBase64(customization, true, true);
var active = _glamourer._stateManager.GetOrCreateSave(character.Address);
_glamourer._stateManager.ApplyDesign(active, design, false);
}
private void ApplyOnlyCustomization(string customization, string characterName)
{
foreach (var gameObject in _objectTable)
{
if (gameObject.Name.ToString() == characterName)
{
ApplyOnlyCustomization(customization, gameObject as Character);
return;
}
}
}
private void ApplyOnlyCustomization(string customization, Character? character)
{
if (character == null)
return;
var design = Design.CreateTemporaryFromBase64(customization, true, false);
var active = _glamourer._stateManager.GetOrCreateSave(character.Address);
_glamourer._stateManager.ApplyDesign(active, design, false);
}
private void ApplyOnlyEquipment(string customization, string characterName)
{
foreach (var gameObject in _objectTable)
{
if (gameObject.Name.ToString() == characterName)
{
ApplyOnlyEquipment(customization, gameObject as Character);
return;
}
}
}
private void ApplyOnlyEquipment(string customization, Character? character)
{
if (character == null)
return;
var design = Design.CreateTemporaryFromBase64(customization, false, true);
var active = _glamourer._stateManager.GetOrCreateSave(character.Address);
_glamourer._stateManager.ApplyDesign(active, design, false);
}
private void Revert(string characterName)
{
foreach (var gameObject in _objectTable)
{
if (gameObject.Name.ToString() != characterName)
continue;
Revert(gameObject as Character);
}
}
private void Revert(Character? character)
{
if (character == null)
return;
var ident = Actors.FromObject(character, true, false, false);
_glamourer._stateManager.DeleteSave(ident);
_glamourer._penumbra.RedrawObject(character.Address, RedrawType.Redraw);
}
private string? GetAllCustomization(Character? character)
{
if (character == null)
return null;
var ident = Actors.FromObject(character, true, false, false);
if (!_glamourer._stateManager.TryGetValue(ident, out var design))
design = new ActiveDesign(ident, character.Address);
return design.CreateOldBase64();
}
private string? GetAllCustomization(string characterName)
{
foreach (var gameObject in _objectTable)
{
if (gameObject.Name.ToString() == characterName)
return GetAllCustomization(gameObject as Character);
}
return null;
}
}
}

View file

@ -21,50 +21,50 @@ public partial class Design : DesignBase
public string[] Tags { get; private set; } = Array.Empty<string>();
public int Index { get; private set; }
private EquipFlag _applyEquip;
private CustomizeFlag _applyCustomize;
public QuadBool Wetness { get; private set; } = QuadBool.NullFalse;
public QuadBool Visor { get; private set; } = QuadBool.NullFalse;
public QuadBool Hat { get; private set; } = QuadBool.NullFalse;
public QuadBool Weapon { get; private set; } = QuadBool.NullFalse;
public bool WriteProtected { get; private set; }
public EquipFlag ApplyEquip { get; private set; }
public CustomizeFlag ApplyCustomize { get; private set; }
public QuadBool Wetness { get; private set; } = QuadBool.NullFalse;
public QuadBool Visor { get; private set; } = QuadBool.NullFalse;
public QuadBool Hat { get; private set; } = QuadBool.NullFalse;
public QuadBool Weapon { get; private set; } = QuadBool.NullFalse;
public bool WriteProtected { get; private set; }
public bool DoApplyEquip(EquipSlot slot)
=> _applyEquip.HasFlag(slot.ToFlag());
=> ApplyEquip.HasFlag(slot.ToFlag());
public bool DoApplyStain(EquipSlot slot)
=> _applyEquip.HasFlag(slot.ToStainFlag());
=> ApplyEquip.HasFlag(slot.ToStainFlag());
public bool DoApplyCustomize(CustomizeIndex idx)
=> _applyCustomize.HasFlag(idx.ToFlag());
=> ApplyCustomize.HasFlag(idx.ToFlag());
private bool SetApplyEquip(EquipSlot slot, bool value)
{
var newValue = value ? _applyEquip | slot.ToFlag() : _applyEquip & ~slot.ToFlag();
if (newValue == _applyEquip)
var newValue = value ? ApplyEquip | slot.ToFlag() : ApplyEquip & ~slot.ToFlag();
if (newValue == ApplyEquip)
return false;
_applyEquip = newValue;
ApplyEquip = newValue;
return true;
}
private bool SetApplyStain(EquipSlot slot, bool value)
{
var newValue = value ? _applyEquip | slot.ToStainFlag() : _applyEquip & ~slot.ToStainFlag();
if (newValue == _applyEquip)
var newValue = value ? ApplyEquip | slot.ToStainFlag() : ApplyEquip & ~slot.ToStainFlag();
if (newValue == ApplyEquip)
return false;
_applyEquip = newValue;
ApplyEquip = newValue;
return true;
}
private bool SetApplyCustomize(CustomizeIndex idx, bool value)
{
var newValue = value ? _applyCustomize | idx.ToFlag() : _applyCustomize & ~idx.ToFlag();
if (newValue == _applyCustomize)
var newValue = value ? ApplyCustomize | idx.ToFlag() : ApplyCustomize & ~idx.ToFlag();
if (newValue == ApplyCustomize)
return false;
_applyCustomize = newValue;
ApplyCustomize = newValue;
return true;
}
@ -104,7 +104,8 @@ public partial class Design : DesignBase
{
[nameof(MainHand)] =
Serialize(MainHand, CharacterData.MainHand.Stain, DoApplyEquip(EquipSlot.MainHand), DoApplyStain(EquipSlot.MainHand)),
[nameof(OffHand)] = Serialize(OffHand, CharacterData.OffHand.Stain, DoApplyEquip(EquipSlot.OffHand), DoApplyStain(EquipSlot.OffHand)),
[nameof(OffHand)] = Serialize(OffHand, CharacterData.OffHand.Stain, DoApplyEquip(EquipSlot.OffHand),
DoApplyStain(EquipSlot.OffHand)),
};
foreach (var slot in EquipSlotExtensions.EqdpSlots)
@ -258,82 +259,41 @@ public partial class Design : DesignBase
return false;
}
public void MigrateBase64(string base64)
{
static void CheckSize(int length, int requiredLength)
{
if (length != requiredLength)
throw new Exception(
$"Can not parse Base64 string into CharacterSave:\n\tInvalid size {length} instead of {requiredLength}.");
}
var bytes = Convert.FromBase64String(base64);
byte applicationFlags;
ushort equipFlags;
switch (bytes[0])
{
case 1:
{
CheckSize(bytes.Length, 86);
applicationFlags = bytes[1];
equipFlags = BitConverter.ToUInt16(bytes, 2);
break;
}
case 2:
{
CheckSize(bytes.Length, 91);
applicationFlags = bytes[1];
equipFlags = BitConverter.ToUInt16(bytes, 2);
Hat = Hat.SetValue((bytes[90] & 0x01) == 0);
Visor = Visor.SetValue((bytes[90] & 0x10) != 0);
Weapon = Weapon.SetValue((bytes[90] & 0x02) == 0);
break;
}
default: throw new Exception($"Can not parse Base64 string into design for migration:\n\tInvalid Version {bytes[0]}.");
}
_applyCustomize = (applicationFlags & 0x01) != 0 ? CustomizeFlagExtensions.All : 0;
Wetness = (applicationFlags & 0x02) != 0 ? QuadBool.True : QuadBool.NullFalse;
Hat = Hat.SetEnabled((applicationFlags & 0x04) != 0);
Weapon = Weapon.SetEnabled((applicationFlags & 0x08) != 0);
Visor = Visor.SetEnabled((applicationFlags & 0x10) != 0);
WriteProtected = (applicationFlags & 0x20) != 0;
CharacterData.ModelId = 0;
SetApplyEquip(EquipSlot.MainHand, (equipFlags & 0x0001) != 0);
SetApplyEquip(EquipSlot.OffHand, (equipFlags & 0x0002) != 0);
SetApplyStain(EquipSlot.MainHand, (equipFlags & 0x0001) != 0);
SetApplyStain(EquipSlot.OffHand, (equipFlags & 0x0002) != 0);
var flag = 0x0002u;
var data = MigrateBase64(base64, out var applyEquip, out var applyCustomize, out var writeProtected, out var wet, out var hat,
out var visor, out var weapon);
UpdateMainhand(data.MainHand);
UpdateMainhand(data.OffHand);
foreach (var slot in EquipSlotExtensions.EqdpSlots)
{
flag <<= 1;
var apply = (equipFlags & flag) != 0;
SetApplyEquip(slot, apply);
SetApplyStain(slot, apply);
}
unsafe
{
fixed (byte* ptr = bytes)
{
CharacterData.CustomizeData.Read(ptr + 4);
var cur = (CharacterWeapon*)(ptr + 30);
UpdateMainhand(cur[0]);
SetStain(EquipSlot.MainHand, cur[0].Stain);
UpdateOffhand(cur[1]);
SetStain(EquipSlot.OffHand, cur[1].Stain);
var eq = (CharacterArmor*)(cur + 2);
foreach (var (slot, idx) in EquipSlotExtensions.EqdpSlots.WithIndex())
{
UpdateArmor(slot, eq[idx], true);
SetStain(slot, eq[idx].Stain);
}
}
}
UpdateArmor(slot, data.Equipment[slot], true);
CharacterData.CustomizeData = data.CustomizeData;
ApplyEquip = applyEquip;
ApplyCustomize = applyCustomize;
WriteProtected = writeProtected;
Wetness = wet;
Hat = hat;
Visor = visor;
Weapon = weapon;
}
public static Design CreateTemporaryFromBase64(string base64, bool customize, bool equip)
{
var ret = new Design();
ret.MigrateBase64(base64);
if (!customize)
ret.ApplyCustomize = 0;
if (!equip)
ret.ApplyEquip = 0;
ret.Wetness = ret.Wetness.SetEnabled(customize);
ret.Visor = ret.Visor.SetEnabled(equip);
ret.Hat = ret.Hat.SetEnabled(equip);
ret.Weapon = ret.Weapon.SetEnabled(equip);
return ret;
}
// Outdated.
public string CreateOldBase64()
=> CreateOldBase64(in CharacterData, ApplyEquip, ApplyCustomize, Wetness == QuadBool.True, Hat.ForcedValue, Hat.Enabled,
Visor.ForcedValue, Visor.Enabled, Weapon.ForcedValue, Weapon.Enabled, WriteProtected, 1f);
}

View file

@ -1,6 +1,8 @@
using System;
using Glamourer.Customization;
using Glamourer.Util;
using OtterGui.Classes;
using OtterGui;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
@ -74,11 +76,12 @@ public class DesignBase
};
}
public Weapon WeaponMain
=> new(MainhandName, MainHand, CharacterData.MainHand, MainhandType);
public Weapon WeaponOff
=> Designs.Weapon.Offhand(OffhandName, OffHand, CharacterData.OffHand, MainhandType);
=> Weapon.Offhand(OffhandName, OffHand, CharacterData.OffHand, MainhandType);
public CustomizeValue GetCustomize(CustomizeIndex idx)
=> Customize()[idx];
@ -108,7 +111,6 @@ public class DesignBase
protected bool UpdateArmor(EquipSlot slot, CharacterArmor armor, bool force)
{
if (!force)
{
switch (slot)
{
case EquipSlot.Head when CharacterData.Head.Value == armor.Value: return false;
@ -122,7 +124,6 @@ public class DesignBase
case EquipSlot.RFinger when CharacterData.RFinger.Value == armor.Value: return false;
case EquipSlot.LFinger when CharacterData.LFinger.Value == armor.Value: return false;
}
}
var (valid, id, name) = Glamourer.Items.Identify(slot, armor.Set, armor.Variant);
if (!valid)
@ -329,4 +330,123 @@ public class DesignBase
default: return false;
}
}
protected const int Base64Size = 89;
public static CharacterData MigrateBase64(string base64, out EquipFlag equipFlags, out CustomizeFlag customizeFlags,
out bool writeProtected, out QuadBool wet, out QuadBool hat, out QuadBool visor, out QuadBool weapon)
{
static void CheckSize(int length, int requiredLength)
{
if (length != requiredLength)
throw new Exception(
$"Can not parse Base64 string into CharacterSave:\n\tInvalid size {length} instead of {requiredLength}.");
}
byte applicationFlags;
ushort equipFlagsS;
var bytes = Convert.FromBase64String(base64);
hat = QuadBool.Null;
visor = QuadBool.Null;
weapon = QuadBool.Null;
switch (bytes[0])
{
case 1:
{
CheckSize(bytes.Length, 86);
applicationFlags = bytes[1];
equipFlagsS = BitConverter.ToUInt16(bytes, 2);
break;
}
case 2:
{
CheckSize(bytes.Length, Base64Size);
applicationFlags = bytes[1];
equipFlagsS = BitConverter.ToUInt16(bytes, 2);
hat = hat.SetValue((bytes[90] & 0x01) == 0);
visor = visor.SetValue((bytes[90] & 0x10) != 0);
weapon = weapon.SetValue((bytes[90] & 0x02) == 0);
break;
}
default: throw new Exception($"Can not parse Base64 string into design for migration:\n\tInvalid Version {bytes[0]}.");
}
customizeFlags = (applicationFlags & 0x01) != 0 ? CustomizeFlagExtensions.All : 0;
wet = (applicationFlags & 0x02) != 0 ? QuadBool.True : QuadBool.NullFalse;
hat = hat.SetEnabled((applicationFlags & 0x04) != 0);
weapon = weapon.SetEnabled((applicationFlags & 0x08) != 0);
visor = visor.SetEnabled((applicationFlags & 0x10) != 0);
writeProtected = (applicationFlags & 0x20) != 0;
equipFlags = 0;
equipFlags |= (equipFlagsS & 0x0001) != 0 ? EquipFlag.Mainhand | EquipFlag.MainhandStain : 0;
equipFlags |= (equipFlagsS & 0x0002) != 0 ? EquipFlag.Offhand | EquipFlag.OffhandStain : 0;
var flag = 0x0002u;
foreach (var slot in EquipSlotExtensions.EqdpSlots)
{
flag <<= 1;
equipFlags |= (equipFlagsS & flag) != 0 ? slot.ToFlag() | slot.ToStainFlag() : 0;
}
var data = new CharacterData();
unsafe
{
fixed (byte* ptr = bytes)
{
data.CustomizeData.Read(ptr + 4);
var cur = (CharacterWeapon*)(ptr + 30);
data.MainHand = cur[0];
data.OffHand = cur[1];
var eq = (CharacterArmor*)(cur + 2);
foreach (var (slot, idx) in EquipSlotExtensions.EqdpSlots.WithIndex())
data.Equipment[slot] = eq[idx];
}
}
return data;
}
public static unsafe string CreateOldBase64(in CharacterData save, EquipFlag equipFlags, CustomizeFlag customizeFlags, bool wet, bool hat,
bool setHat, bool visor, bool setVisor, bool weapon, bool setWeapon, bool writeProtected, float alpha)
{
var data = stackalloc byte[Base64Size];
data[0] = 2;
data[1] = (byte)((customizeFlags == CustomizeFlagExtensions.All ? 0x01 : 0)
| (wet ? 0x02 : 0)
| (setHat ? 0x04 : 0)
| (setWeapon ? 0x08 : 0)
| (setVisor ? 0x10 : 0)
| (writeProtected ? 0x20 : 0));
data[2] = (byte)((equipFlags.HasFlag(EquipFlag.Mainhand) ? 0x01 : 0)
| (equipFlags.HasFlag(EquipFlag.Offhand) ? 0x02 : 0)
| (equipFlags.HasFlag(EquipFlag.Head) ? 0x04 : 0)
| (equipFlags.HasFlag(EquipFlag.Body) ? 0x08 : 0)
| (equipFlags.HasFlag(EquipFlag.Hands) ? 0x10 : 0)
| (equipFlags.HasFlag(EquipFlag.Legs) ? 0x20 : 0)
| (equipFlags.HasFlag(EquipFlag.Feet) ? 0x40 : 0)
| (equipFlags.HasFlag(EquipFlag.Ears) ? 0x80 : 0));
data[3] = (byte)((equipFlags.HasFlag(EquipFlag.Neck) ? 0x01 : 0)
| (equipFlags.HasFlag(EquipFlag.Wrist) ? 0x02 : 0)
| (equipFlags.HasFlag(EquipFlag.RFinger) ? 0x04 : 0)
| (equipFlags.HasFlag(EquipFlag.LFinger) ? 0x08 : 0));
save.CustomizeData.Write(data + 4);
((CharacterWeapon*)(data + 30))[0] = save.MainHand;
((CharacterWeapon*)(data + 30))[1] = save.OffHand;
((CharacterArmor*)(data + 44))[0] = save.Head;
((CharacterArmor*)(data + 44))[1] = save.Body;
((CharacterArmor*)(data + 44))[2] = save.Hands;
((CharacterArmor*)(data + 44))[3] = save.Legs;
((CharacterArmor*)(data + 44))[4] = save.Feet;
((CharacterArmor*)(data + 44))[5] = save.Ears;
((CharacterArmor*)(data + 44))[6] = save.Neck;
((CharacterArmor*)(data + 44))[7] = save.Wrists;
((CharacterArmor*)(data + 44))[8] = save.RFinger;
((CharacterArmor*)(data + 44))[9] = save.LFinger;
*(float*)(data + 84) = 1f;
data[88] = (byte)((hat ? 0x01 : 0)
| (visor ? 0x10 : 0)
| (weapon ? 0x02 : 0));
return Convert.ToBase64String(new Span<byte>(data, Base64Size));
}
}

View file

@ -34,6 +34,8 @@ public enum EquipFlag : uint
public static class EquipFlagExtensions
{
public const EquipFlag All = (EquipFlag)(((uint)EquipFlag.OffhandStain << 1) - 1);
public static EquipFlag ToFlag(this EquipSlot slot)
=> slot switch
{

View file

@ -36,7 +36,7 @@ public class DrawObjectManager : IDisposable
private void FixEquipment(DrawObject drawObject, EquipSlot slot, ref CharacterArmor item)
{
var customize = drawObject.Customize;
var (changed, newArmor) = _items.RestrictedGear.ResolveRestricted(item, slot, customize.Race, customize.Gender);
var (changed, newArmor) = _items.ResolveRestrictedGear(item, slot, customize.Race, customize.Gender);
if (!changed)
return;
@ -54,7 +54,7 @@ public class DrawObjectManager : IDisposable
if (gameObject.ModelId != modelId)
return;
var identifier = _actors.FromObject((GameObject*)gameObjectPtr, out _, true, true);
var identifier = _actors.FromObject((GameObject*)gameObjectPtr, out _, true, true, false);
if (!identifier.IsValid || !_manager.TryGetValue(identifier, out var design))
return;
@ -75,7 +75,7 @@ public class DrawObjectManager : IDisposable
foreach (var slot in EquipSlotExtensions.EquipmentSlots)
{
(_, equip[slot]) =
Glamourer.Items.RestrictedGear.ResolveRestricted(saveEquip[slot], slot, customize.Race, customize.Gender);
Glamourer.Items.ResolveRestrictedGear(saveEquip[slot], slot, customize.Race, customize.Gender);
}
}
}

View file

@ -4,7 +4,6 @@ using System.Reflection;
using Dalamud.Game.Command;
using Dalamud.Interface.Windowing;
using Dalamud.Plugin;
using FFXIVClientStructs.FFXIV.Client.Game.Character;
using Glamourer.Api;
using Glamourer.Customization;
using Glamourer.Designs;
@ -19,7 +18,7 @@ using FixedDesigns = Glamourer.State.FixedDesigns;
namespace Glamourer;
public class Glamourer : IDalamudPlugin
public partial class Glamourer : IDalamudPlugin
{
private const string HelpString = "[Copy|Apply|Save],[Name or PlaceHolder],<Name for Save>";
private const string MainCommandString = "/glamourer";
@ -56,15 +55,14 @@ public class Glamourer : IDalamudPlugin
//public readonly DesignManager Designs;
//public static RevertableDesigns RevertableDesigns = new();
//public readonly GlamourerIpc GlamourerIpc;
public readonly GlamourerIpc Ipc;
public Glamourer(DalamudPluginInterface pluginInterface)
{
try
{
Dalamud.Initialize(pluginInterface);
Log = new Logger();
Log = new Logger();
_framework = new FrameworkManager(Dalamud.Framework, Log);
_interop = new Interop.Interop();
_penumbra = new PenumbraAttach();
@ -100,8 +98,8 @@ public class Glamourer : IDalamudPlugin
_interface = new Interface(Dalamud.PluginInterface, Items, _stateManager, _designManager, _fileSystem, _objectManager);
_windowSystem.AddWindow(_interface);
Dalamud.PluginInterface.UiBuilder.Draw += _windowSystem.Draw;
//FixedDesignManager.Flag((Human*)((Actor)Dalamud.ClientState.LocalPlayer?.Address).Pointer->GameObject.DrawObject, 0, &x);
Dalamud.PluginInterface.UiBuilder.Draw += _windowSystem.Draw;
Ipc = new GlamourerIpc(this, Dalamud.ClientState, Dalamud.Objects, Dalamud.PluginInterface);
}
catch
{
@ -113,6 +111,7 @@ public class Glamourer : IDalamudPlugin
public void Dispose()
{
Ipc?.Dispose();
_drawObjectManager?.Dispose();
RedrawManager?.Dispose();
if (_windowSystem != null)

View file

@ -3,8 +3,8 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Dalamud.Interface;
using Dalamud.Utility;
using Glamourer.Designs;
using Glamourer.State;
using Glamourer.Util;
using ImGuiNET;
using OtterGui;
@ -51,7 +51,7 @@ public class EquipmentDrawer
if (slot.IsAccessory())
return gear.Name;
var (changed, _) = _items.RestrictedGear.ResolveRestricted(gear.Model, slot, race, gender);
var (changed, _) = _items.ResolveRestrictedGear(gear.Model, slot, race, gender);
if (changed)
return gear.Name + " (Restricted)";
@ -136,26 +136,51 @@ public class EquipmentDrawer
}
public bool DrawApply(Design design, EquipSlot slot, out bool enabled)
{
enabled = design.DoApplyEquip(slot);
var tmp = enabled;
if (!ImGuiUtil.Checkbox($"##apply{slot}", string.Empty, enabled, v => tmp = v))
return false;
var change = enabled != tmp;
enabled = tmp;
return change;
}
=> DrawCheckbox($"##apply{slot}", design.DoApplyEquip(slot), out enabled);
public bool DrawApplyStain(Design design, EquipSlot slot, out bool enabled)
{
enabled = design.DoApplyStain(slot);
var tmp = enabled;
if (!ImGuiUtil.Checkbox($"##applyStain{slot}", string.Empty, enabled, v => tmp = v))
return false;
=> DrawCheckbox($"##applyStain{slot}", design.DoApplyStain(slot), out enabled);
var change = enabled != tmp;
enabled = tmp;
return change;
private static bool DrawCheckbox(string label, bool value, out bool on)
{
var ret = ImGuiUtil.Checkbox(label, string.Empty, value, v => value = v);
on = value;
return ret;
}
public bool DrawVisor(Design design, out bool on)
=> DrawCheckbox("##visorToggled", design.Visor.ForcedValue, out on);
public bool DrawVisor(ActiveDesign design, out bool on)
=> DrawCheckbox("##visorToggled", design.IsVisorToggled, out on);
public bool DrawHat(Design design, out bool on)
=> DrawCheckbox("##hatVisible", design.Hat.ForcedValue, out on);
public bool DrawHat(ActiveDesign design, out bool on)
=> DrawCheckbox("##hatVisible", design.IsHatVisible, out on);
public bool DrawWeapon(Design design, out bool on)
=> DrawCheckbox("##weaponVisible", design.Weapon.ForcedValue, out on);
public bool DrawWeapon(ActiveDesign design, out bool on)
=> DrawCheckbox("##weaponVisible", design.IsWeaponVisible, out on);
public bool DrawWetness(Design design, out bool on)
=> DrawCheckbox("##wetness", design.Wetness.ForcedValue, out on);
public bool DrawWetness(ActiveDesign design, out bool on)
=> DrawCheckbox("##wetnessVisible", design.IsWet, out on);
public bool DrawApplyVisor(Design design, out bool on)
=> DrawCheckbox("##applyVisor", design.Visor.Enabled, out on);
public bool DrawApplyWetness(Design design, out bool on)
=> DrawCheckbox("##applyWetness", design.Wetness.Enabled, out on);
public bool DrawApplyHatState(Design design, out bool on)
=> DrawCheckbox("##applyHatState", design.Hat.Enabled, out on);
public bool DrawApplyWeaponState(Design design, out bool on)
=> DrawCheckbox("##applyWeaponState", design.Weapon.Enabled, out on);
}

View file

@ -64,7 +64,7 @@ internal partial class Interface
return;
if (_currentData.Valid)
_currentSave.Update(_currentData.Objects[0]);
_currentSave.Initialize(_currentData.Objects[0]);
RevertButton();
if (_main._customizationDrawer.Draw(_currentSave.Customize(), _identifier.Type == IdentifierType.Special))
@ -93,6 +93,11 @@ internal partial class Interface
ImGui.SameLine();
_main._equipmentDrawer.DrawOffhand(currentOff, main.Type, out var off);
}
if (_main._equipmentDrawer.DrawVisor(_currentSave, out var value))
{
_activeDesigns.ChangeVisor(_currentSave, value, false);
}
}
private const uint RedHeaderColor = 0xFF1818C0;

View file

@ -43,19 +43,12 @@ internal partial class Interface
private static void DrawRestorePenumbraButton()
{
const string buttonLabel = "Re-Register Penumbra";
if (!Glamourer.Config.AttachToPenumbra)
{
using var style = ImRaii.PushStyle(ImGuiStyleVar.Alpha, 0.5f);
ImGui.Button(buttonLabel);
return;
}
// TODO
//if (ImGui.Button(buttonLabel))
// Glamourer.Penumbra.Reattach(true);
ImGuiUtil.HoverTooltip(
"If Penumbra did not register the functions for some reason, pressing this button might help restore functionality.");
//ImGuiUtil.HoverTooltip(
// "If Penumbra did not register the functions for some reason, pressing this button might help restore functionality.");
}
private static void DrawSettingsTab()

View file

@ -25,7 +25,7 @@ public unsafe partial struct Actor : IEquatable<Actor>, IDesignable
=> actor.Pointer == null ? IntPtr.Zero : (IntPtr)actor.Pointer;
public ActorIdentifier GetIdentifier()
=> Glamourer.Actors.FromObject((FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)Pointer, out _, true, true);
=> Glamourer.Actors.FromObject((FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)Pointer, out _, true, true, false);
public bool Identifier(out ActorIdentifier ident)
{

View file

@ -10,17 +10,5 @@ public unsafe partial class RedrawManager
{
public static void SetVisor(Human* data, bool on)
{
if (data == null)
return;
var flags = &data->CharacterBase.UnkFlags_01;
var state = (*flags & Offsets.DrawObjectVisorStateFlag) != 0;
if (state == on)
return;
var newFlag = (byte)(on ? *flags | Offsets.DrawObjectVisorStateFlag : *flags & ~Offsets.DrawObjectVisorStateFlag);
*flags = (byte) (newFlag | Offsets.DrawObjectVisorToggleFlag);
}
}

View file

@ -20,11 +20,82 @@ public partial class Interop : IDisposable
{
SignatureHelper.Initialise(this);
_changeJobHook.Enable();
_flagSlotForUpdateHook.Enable();
_setupVisorHook.Enable();
}
public void Dispose()
{
_changeJobHook.Dispose();
_flagSlotForUpdateHook.Dispose();
_setupVisorHook.Dispose();
}
public static unsafe bool GetVisorState(nint humanPtr)
{
if (humanPtr == IntPtr.Zero)
return false;
var data = (Human*)humanPtr;
var flags = &data->CharacterBase.UnkFlags_01;
return (*flags & Offsets.DrawObjectVisorStateFlag) != 0;
}
public static unsafe void SetVisorState(nint humanPtr, bool on)
{
if (humanPtr == IntPtr.Zero)
return;
var data = (Human*)humanPtr;
var flags = &data->CharacterBase.UnkFlags_01;
var state = (*flags & Offsets.DrawObjectVisorStateFlag) != 0;
if (state == on)
return;
var newFlag = (byte)(on ? *flags | Offsets.DrawObjectVisorStateFlag : *flags & ~Offsets.DrawObjectVisorStateFlag);
*flags = (byte)(newFlag | Offsets.DrawObjectVisorToggleFlag);
}
}
public partial class Interop
{
private delegate void UpdateVisorDelegateInternal(nint humanPtr, ushort modelId, bool on);
public delegate void UpdateVisorDelegate(DrawObject human, SetId modelId, ref bool on);
[Signature(Penumbra.GameData.Sigs.SetupVisor, DetourName = nameof(SetupVisorDetour))]
private readonly Hook<UpdateVisorDelegateInternal> _setupVisorHook = null!;
public event UpdateVisorDelegate? VisorUpdate;
private void SetupVisorDetour(nint humanPtr, ushort modelId, bool on)
{
InvokeVisorEvent(humanPtr, modelId, ref on);
_setupVisorHook.Original(humanPtr, modelId, on);
}
private void InvokeVisorEvent(DrawObject drawObject, SetId modelId, ref bool on)
{
if (VisorUpdate == null)
{
Glamourer.Log.Verbose($"Visor setup on 0x{drawObject.Address:X} with {modelId.Value}, setting to {on}.");
return;
}
var initialValue = on;
foreach (var del in VisorUpdate.GetInvocationList().OfType<UpdateVisorDelegate>())
{
try
{
del(drawObject, modelId, ref on);
}
catch (Exception ex)
{
Glamourer.Log.Error($"Could not invoke {nameof(VisorUpdate)} Subscriber:\n{ex}");
}
}
Glamourer.Log.Verbose(
$"Visor setup on 0x{drawObject.Address:X} with {modelId.Value}, setting to {on}, initial call was {initialValue}.");
}
}
@ -64,8 +135,13 @@ public unsafe partial class Interop
private void InvokeFlagSlotEvent(DrawObject drawObject, EquipSlot slot, ref CharacterArmor armor)
{
if (EquipUpdate == null)
{
Glamourer.Log.Verbose(
$"{slot} updated on 0x{drawObject.Address:X} to {armor.Set.Value}-{armor.Variant} with stain {armor.Stain.Value}.");
return;
}
var iv = armor;
foreach (var del in EquipUpdate.GetInvocationList().OfType<FlagSlotForUpdateDelegate>())
{
try
@ -77,6 +153,9 @@ public unsafe partial class Interop
Glamourer.Log.Error($"Could not invoke {nameof(EquipUpdate)} Subscriber:\n{ex}");
}
}
Glamourer.Log.Verbose(
$"{slot} updated on 0x{drawObject.Address:X} to {armor.Set.Value}-{armor.Variant} with stain {armor.Stain.Value}, initial armor was {iv.Set.Value}-{iv.Variant} with stain {iv.Stain.Value}.");
}
}
@ -122,8 +201,8 @@ public unsafe partial class RedrawManager : IDisposable
public RedrawManager(FixedDesigns fixedDesigns, ActiveDesign.Manager stateManager)
{
SignatureHelper.Initialise(this);
_fixedDesigns = fixedDesigns;
_stateManager = stateManager;
_fixedDesigns = fixedDesigns;
_stateManager = stateManager;
_flagSlotForUpdateHook.Enable();
_loadWeaponHook.Enable();
}
@ -161,7 +240,7 @@ public unsafe partial class RedrawManager : IDisposable
foreach (var slot in EquipSlotExtensions.EqdpSlots)
{
(_, equip[slot]) =
Glamourer.Items.RestrictedGear.ResolveRestricted(saveEquip[slot], slot, customize.Race, customize.Gender);
Glamourer.Items.ResolveRestrictedGear(saveEquip[slot], slot, customize.Race, customize.Gender);
}
}
}

View file

@ -4,7 +4,9 @@ using Glamourer.Interop;
using Penumbra.GameData.Actors;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using FFXIVClientStructs.FFXIV.Client.Game.Object;
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using Glamourer.Api;
using Glamourer.Customization;
using Glamourer.Designs;
@ -65,20 +67,58 @@ public sealed partial class ActiveDesign
public unsafe ActiveDesign GetOrCreateSave(Actor actor)
{
var id = _actors.FromObject((GameObject*)actor.Pointer, out _, false, false);
var id = _actors.FromObject((GameObject*)actor.Pointer, out _, false, false, false);
if (_characterSaves.TryGetValue(id, out var save))
{
save.Update(actor);
save.Initialize(actor);
return save;
}
id = id.CreatePermanent();
save = new ActiveDesign(id, actor);
save.Update(actor);
save.Initialize(actor);
_characterSaves.Add(id, save);
return save;
}
public void SetWetness(ActiveDesign design, bool wet, bool fromFixed)
=> design.IsWet = wet;
public void SetHatVisible(ActiveDesign design, bool visible, bool fromFixed)
=> design.IsHatVisible = visible;
public void SetVisor(ActiveDesign design, bool toggled, bool fromFixed)
=> design.IsVisorToggled = toggled;
public void SetWeaponVisible(ActiveDesign design, bool visible, bool fromFixed)
=> design.IsWeaponVisible = visible;
public unsafe void ApplyDesign(ActiveDesign to, Design from, bool fromFixed)
{
if (to.ModelId != from.ModelId)
return;
if (from.DoApplyEquip(EquipSlot.MainHand))
ChangeMainHand(to, from.MainHand, fromFixed);
if (from.DoApplyEquip(EquipSlot.OffHand))
ChangeOffHand(to, from.OffHand, fromFixed);
foreach (var slot in EquipSlotExtensions.EqdpSlots.Where(from.DoApplyEquip))
ChangeEquipment(to, slot, from.Armor(slot), fromFixed);
ChangeCustomize(to, from.ApplyCustomize, *from.Customize().Data, fromFixed);
if (from.Wetness.Enabled)
SetWetness(to, from.Wetness.ForcedValue, fromFixed);
if (from.Hat.Enabled)
SetHatVisible(to, from.Hat.ForcedValue, fromFixed);
if (from.Visor.Enabled)
SetVisor(to, from.Visor.ForcedValue, fromFixed);
if (from.Weapon.Enabled)
SetWeaponVisible(to, from.Weapon.ForcedValue, fromFixed);
}
public void RevertDesign(ActiveDesign design)
{
RevertCustomize(design, design.ChangedCustomize);
@ -89,6 +129,12 @@ public sealed partial class ActiveDesign
RevertOffHand(design);
}
public void ChangeMainHand(ActiveDesign design, uint itemId, bool fromFixed)
=> design.SetMainhand(itemId);
public void ChangeOffHand(ActiveDesign design, uint itemId, bool fromFixed)
=> design.SetOffhand(itemId);
public void RevertMainHand(ActiveDesign design)
{ }
@ -207,5 +253,20 @@ public sealed partial class ActiveDesign
foreach (var obj in data.Objects)
_interop.UpdateStain(obj.DrawObject, slot, stain);
}
public void ChangeVisor(ActiveDesign design, bool on, bool fromFixed)
{
var current = design.IsVisorToggled;
if (current == on)
return;
design.IsVisorToggled = on;
_objects.Update();
if (!_objects.TryGetValue(design.Identifier, out var data))
return;
foreach (var obj in data.Objects)
Interop.Interop.SetVisorState(obj.DrawObject, on);
}
}
}

View file

@ -1,4 +1,6 @@
using Glamourer.Customization;
using System;
using System.Linq;
using Glamourer.Customization;
using Glamourer.Designs;
using Glamourer.Interop;
using Penumbra.Api.Enums;
@ -27,13 +29,12 @@ public sealed partial class ActiveDesign : DesignBase
private ActiveDesign(ActorIdentifier identifier)
=> Identifier = identifier;
private ActiveDesign(ActorIdentifier identifier, Actor actor)
public ActiveDesign(ActorIdentifier identifier, Actor actor)
{
Identifier = identifier;
Update(actor);
Initialize(actor);
}
//public void ApplyToActor(Actor actor)
//{
// if (!actor)
@ -64,7 +65,7 @@ public sealed partial class ActiveDesign : DesignBase
// RedrawManager.SetVisor(actor.DrawObject.Pointer, actor.VisorEnabled);
//}
//
public void Update(Actor actor)
public void Initialize(Actor actor)
{
if (!actor)
return;
@ -101,5 +102,14 @@ public sealed partial class ActiveDesign : DesignBase
UpdateOffhand(actor.OffHand);
SetStain(EquipSlot.OffHand, actor.OffHand.Stain);
}
var visor = Interop.Interop.GetVisorState(actor.DrawObject);
if (IsVisorToggled != visor)
IsVisorToggled = visor;
}
public string CreateOldBase64()
=> CreateOldBase64(in CharacterData, EquipFlagExtensions.All, CustomizeFlagExtensions.All, IsWet, IsHatVisible, true,
IsVisorToggled,
true, IsWeaponVisible, true, false, 1f);
}

View file

@ -1,47 +1,47 @@
using System.Collections.Generic;
using Dalamud.Configuration;
namespace Glamourer.State
namespace Glamourer.State;
public class GlamourerConfig : IPluginConfiguration
{
public class GlamourerConfig : IPluginConfiguration
public class FixedDesign
{
public class FixedDesign
{
public string Name = string.Empty;
public string Path = string.Empty;
public uint JobGroups;
public bool Enabled;
}
public string Name = string.Empty;
public string Path = string.Empty;
public uint JobGroups;
public bool Enabled;
}
public int Version { get; set; } = 1;
public int Version { get; set; } = 1;
public const uint DefaultCustomizationColor = 0xFFC000C0;
public const uint DefaultStateColor = 0xFF00C0C0;
public const uint DefaultEquipmentColor = 0xFF00C000;
public const uint DefaultCustomizationColor = 0xFFC000C0;
public const uint DefaultStateColor = 0xFF00C0C0;
public const uint DefaultEquipmentColor = 0xFF00C000;
public bool FoldersFirst { get; set; } = false;
public bool ColorDesigns { get; set; } = true;
public bool ShowLocks { get; set; } = true;
public bool AttachToPenumbra { get; set; } = true;
public bool ApplyFixedDesigns { get; set; } = true;
public bool UseRestrictedGearProtection { get; set; } = true;
public uint CustomizationColor { get; set; } = DefaultCustomizationColor;
public uint StateColor { get; set; } = DefaultStateColor;
public uint EquipmentColor { get; set; } = DefaultEquipmentColor;
public bool FoldersFirst { get; set; } = false;
public bool ColorDesigns { get; set; } = true;
public bool ShowLocks { get; set; } = true;
public bool ApplyFixedDesigns { get; set; } = true;
public List<FixedDesign> FixedDesigns { get; set; } = new();
public uint CustomizationColor { get; set; } = DefaultCustomizationColor;
public uint StateColor { get; set; } = DefaultStateColor;
public uint EquipmentColor { get; set; } = DefaultEquipmentColor;
public void Save()
=> Dalamud.PluginInterface.SavePluginConfig(this);
public List<FixedDesign> FixedDesigns { get; set; } = new();
public static GlamourerConfig Load()
{
if (Dalamud.PluginInterface.GetPluginConfig() is GlamourerConfig config)
return config;
public void Save()
=> Dalamud.PluginInterface.SavePluginConfig(this);
config = new GlamourerConfig();
config.Save();
public static GlamourerConfig Load()
{
if (Dalamud.PluginInterface.GetPluginConfig() is GlamourerConfig config)
return config;
}
config = new GlamourerConfig();
config.Save();
return config;
}
}

View file

@ -121,6 +121,6 @@ public static class CustomizeExtensions
return;
foreach (var slot in EquipSlotExtensions.EqdpSlots)
(_, equip[slot]) = Glamourer.Items.RestrictedGear.ResolveRestricted(equip[slot], slot, race, gender);
(_, equip[slot]) = Glamourer.Items.ResolveRestrictedGear(equip[slot], slot, race, gender);
}
}

View file

@ -10,6 +10,7 @@ using Penumbra.GameData;
using Penumbra.GameData.Data;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using Race = Penumbra.GameData.Enums.Race;
namespace Glamourer.Util;
@ -43,6 +44,14 @@ public class ItemManager : IDisposable
RestrictedGear.Dispose();
}
public (bool, CharacterArmor) ResolveRestrictedGear(CharacterArmor armor, EquipSlot slot, Race race, Gender gender)
{
if (Glamourer.Config.UseRestrictedGearProtection)
return RestrictedGear.ResolveRestricted(armor, slot, race, gender);
return (false, armor);
}
public readonly Item DefaultSword;
public static uint NothingId(EquipSlot slot)