Random fixes and penumbra attach update.

This commit is contained in:
Ottermandias 2022-06-04 20:59:53 +02:00
parent 78fd4ce9b9
commit dbdaaf1ca8
15 changed files with 264 additions and 180 deletions

View file

@ -150,7 +150,7 @@ namespace Glamourer.Customization
var row = _listSheet.GetRow(((uint) race - 1) * 2 - 1 + (uint) gender)!;
var set = new CustomizationSet(race, gender)
{
HairStyles = race.ToRace() == Race.Hrothgar ? HrothgarFaces(row) : GetHairStyles(race, gender),
HairStyles = GetHairStyles(race, gender),
HairColors = hair,
SkinColors = skin,
EyeColors = _eyeColorPicker,

View file

@ -55,8 +55,9 @@
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\dev\Lumina.Excel.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Penumbra.GameData">
<HintPath>..\..\Penumbra\Penumbra\bin\$(Configuration)\$(TargetFramework)\Penumbra.GameData.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Penumbra\Penumbra.GameData\Penumbra.GameData.csproj" />
</ItemGroup>
</Project>

View file

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29613.14
# Visual Studio Version 17
VisualStudioVersion = 17.2.32210.308
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Glamourer", "Glamourer\Glamourer.csproj", "{A5439F6B-83C1-4078-9371-354A147FF554}"
EndProject
@ -13,6 +13,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Glamourer.GameData", "Glamourer.GameData\Glamourer.GameData.csproj", "{51F4DDB0-1FA0-4629-9CFE-C55B6062907B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Penumbra.GameData", "..\Penumbra\Penumbra.GameData\Penumbra.GameData.csproj", "{9BEE2336-AA93-4669-8EEA-4756B3B2D024}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Penumbra.PlayerWatch", "..\Penumbra\Penumbra.PlayerWatch\Penumbra.PlayerWatch.csproj", "{FECEDB39-C103-4333-82A6-A422BDC51EEE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -47,6 +51,30 @@ Global
{51F4DDB0-1FA0-4629-9CFE-C55B6062907B}.Release|x64.Build.0 = Release|Any CPU
{51F4DDB0-1FA0-4629-9CFE-C55B6062907B}.Release|x86.ActiveCfg = Release|Any CPU
{51F4DDB0-1FA0-4629-9CFE-C55B6062907B}.Release|x86.Build.0 = Release|Any CPU
{9BEE2336-AA93-4669-8EEA-4756B3B2D024}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9BEE2336-AA93-4669-8EEA-4756B3B2D024}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9BEE2336-AA93-4669-8EEA-4756B3B2D024}.Debug|x64.ActiveCfg = Debug|Any CPU
{9BEE2336-AA93-4669-8EEA-4756B3B2D024}.Debug|x64.Build.0 = Debug|Any CPU
{9BEE2336-AA93-4669-8EEA-4756B3B2D024}.Debug|x86.ActiveCfg = Debug|Any CPU
{9BEE2336-AA93-4669-8EEA-4756B3B2D024}.Debug|x86.Build.0 = Debug|Any CPU
{9BEE2336-AA93-4669-8EEA-4756B3B2D024}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9BEE2336-AA93-4669-8EEA-4756B3B2D024}.Release|Any CPU.Build.0 = Release|Any CPU
{9BEE2336-AA93-4669-8EEA-4756B3B2D024}.Release|x64.ActiveCfg = Release|Any CPU
{9BEE2336-AA93-4669-8EEA-4756B3B2D024}.Release|x64.Build.0 = Release|Any CPU
{9BEE2336-AA93-4669-8EEA-4756B3B2D024}.Release|x86.ActiveCfg = Release|Any CPU
{9BEE2336-AA93-4669-8EEA-4756B3B2D024}.Release|x86.Build.0 = Release|Any CPU
{FECEDB39-C103-4333-82A6-A422BDC51EEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FECEDB39-C103-4333-82A6-A422BDC51EEE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FECEDB39-C103-4333-82A6-A422BDC51EEE}.Debug|x64.ActiveCfg = Debug|Any CPU
{FECEDB39-C103-4333-82A6-A422BDC51EEE}.Debug|x64.Build.0 = Debug|Any CPU
{FECEDB39-C103-4333-82A6-A422BDC51EEE}.Debug|x86.ActiveCfg = Debug|Any CPU
{FECEDB39-C103-4333-82A6-A422BDC51EEE}.Debug|x86.Build.0 = Debug|Any CPU
{FECEDB39-C103-4333-82A6-A422BDC51EEE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{FECEDB39-C103-4333-82A6-A422BDC51EEE}.Release|Any CPU.Build.0 = Release|Any CPU
{FECEDB39-C103-4333-82A6-A422BDC51EEE}.Release|x64.ActiveCfg = Release|Any CPU
{FECEDB39-C103-4333-82A6-A422BDC51EEE}.Release|x64.Build.0 = Release|Any CPU
{FECEDB39-C103-4333-82A6-A422BDC51EEE}.Release|x86.ActiveCfg = Release|Any CPU
{FECEDB39-C103-4333-82A6-A422BDC51EEE}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View file

@ -1,4 +1,6 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/Environment/AssemblyExplorer/XmlDocument/@EntryValue">&lt;AssemblyExplorer&gt;&#xD;
&lt;Assembly Path="H:\Projects\FFPlugins\Penumbra\Penumbra\bin\Debug\net472\Penumbra.GameData.dll" /&gt;&#xD;
&lt;Assembly Path="C:\Users\Ozy\AppData\Roaming\XIVLauncher\addon\Hooks\dev\Dalamud.dll" /&gt;&#xD;
&lt;Assembly Path="H:\Projects\FFPlugins\Penumbra\Penumbra\bin\Debug\net5.0-windows\Penumbra.GameData.dll" /&gt;&#xD;
&lt;/AssemblyExplorer&gt;</s:String></wpf:ResourceDictionary>

Binary file not shown.

View file

@ -4,20 +4,8 @@ namespace Glamourer;
public static class CharacterExtensions
{
public const int WetnessOffset = 0x1ADA;
public const byte WetnessFlag = 0x80;
public const int HatVisibleOffset = 0x84E;
public const int VisorToggledOffset = 0x84F;
public const byte HatHiddenFlag = 0x01;
public const byte VisorToggledFlag = 0x08;
public const int AlphaOffset = 0x19E0;
public const int WeaponHiddenOffset1 = 0x84F;
public const int WeaponHiddenOffset2 = 0x72C; // maybe
public const byte WeaponHiddenFlag1 = 0x01;
public const byte WeaponHiddenFlag2 = 0x02;
public static unsafe bool IsWet(this Character a)
=> (*((byte*)a.Address + WetnessOffset) & WetnessFlag) != 0;
=> (*((byte*)a.Address + Offsets.Character.Wetness) & Offsets.Character.Flags.IsWet) != 0;
public static unsafe bool SetWetness(this Character a, bool value)
{
@ -26,14 +14,16 @@ public static class CharacterExtensions
return false;
if (value)
*((byte*)a.Address + WetnessOffset) = (byte)(*((byte*)a.Address + WetnessOffset) | WetnessFlag);
*((byte*)a.Address + Offsets.Character.Wetness) =
(byte)(*((byte*)a.Address + Offsets.Character.Wetness) | Offsets.Character.Flags.IsWet);
else
*((byte*)a.Address + WetnessOffset) = (byte)(*((byte*)a.Address + WetnessOffset) & ~WetnessFlag);
*((byte*)a.Address + Offsets.Character.Wetness) =
(byte)(*((byte*)a.Address + Offsets.Character.Wetness) & ~Offsets.Character.Flags.IsWet);
return true;
}
public static unsafe bool IsHatVisible(this Character a)
=> (*((byte*)a.Address + HatVisibleOffset) & HatHiddenFlag) == 0;
=> (*((byte*)a.Address + Offsets.Character.HatVisible) & Offsets.Character.Flags.IsHatHidden) == 0;
public static unsafe bool SetHatVisible(this Character a, bool visible)
{
@ -42,14 +32,17 @@ public static class CharacterExtensions
return false;
if (visible)
*((byte*)a.Address + HatVisibleOffset) = (byte)(*((byte*)a.Address + HatVisibleOffset) & ~HatHiddenFlag);
*((byte*)a.Address + Offsets.Character.HatVisible) =
(byte)(*((byte*)a.Address + Offsets.Character.HatVisible) & ~Offsets.Character.Flags.IsHatHidden);
else
*((byte*)a.Address + HatVisibleOffset) = (byte)(*((byte*)a.Address + HatVisibleOffset) | HatHiddenFlag);
*((byte*)a.Address + Offsets.Character.HatVisible) =
(byte)(*((byte*)a.Address + Offsets.Character.HatVisible) | Offsets.Character.Flags.IsHatHidden);
return true;
}
public static unsafe bool IsVisorToggled(this Character a)
=> (*((byte*)a.Address + VisorToggledOffset) & VisorToggledFlag) == VisorToggledFlag;
=> (*((byte*)a.Address + Offsets.Character.VisorToggled) & Offsets.Character.Flags.IsVisorToggled)
== Offsets.Character.Flags.IsVisorToggled;
public static unsafe bool SetVisorToggled(this Character a, bool toggled)
{
@ -58,15 +51,19 @@ public static class CharacterExtensions
return false;
if (toggled)
*((byte*)a.Address + VisorToggledOffset) = (byte)(*((byte*)a.Address + VisorToggledOffset) | VisorToggledFlag);
*((byte*)a.Address + Offsets.Character.VisorToggled) =
(byte)(*((byte*)a.Address + Offsets.Character.VisorToggled) | Offsets.Character.Flags.IsVisorToggled);
else
*((byte*)a.Address + VisorToggledOffset) = (byte)(*((byte*)a.Address + VisorToggledOffset) & ~VisorToggledFlag);
*((byte*)a.Address + Offsets.Character.VisorToggled) =
(byte)(*((byte*)a.Address + Offsets.Character.VisorToggled) & ~Offsets.Character.Flags.IsVisorToggled);
return true;
}
public static unsafe bool IsWeaponHidden(this Character a)
=> (*((byte*)a.Address + WeaponHiddenOffset1) & WeaponHiddenFlag1) == WeaponHiddenFlag1
&& (*((byte*)a.Address + WeaponHiddenOffset2) & WeaponHiddenFlag2) == WeaponHiddenFlag2;
=> (*((byte*)a.Address + Offsets.Character.WeaponHidden1) & Offsets.Character.Flags.IsWeaponHidden1)
== Offsets.Character.Flags.IsWeaponHidden1
&& (*((byte*)a.Address + Offsets.Character.WeaponHidden2) & Offsets.Character.Flags.IsWeaponHidden2)
== Offsets.Character.Flags.IsWeaponHidden2;
public static unsafe bool SetWeaponHidden(this Character a, bool value)
{
@ -74,21 +71,22 @@ public static class CharacterExtensions
if (hidden == value)
return false;
var val1 = *((byte*)a.Address + WeaponHiddenOffset1);
var val2 = *((byte*)a.Address + WeaponHiddenOffset2);
var val1 = *((byte*)a.Address + Offsets.Character.WeaponHidden1);
var val2 = *((byte*)a.Address + Offsets.Character.WeaponHidden2);
if (value)
{
*((byte*)a.Address + WeaponHiddenOffset1) = (byte)(val1 | WeaponHiddenFlag1);
*((byte*)a.Address + WeaponHiddenOffset2) = (byte)(val2 | WeaponHiddenFlag2);
*((byte*)a.Address + Offsets.Character.WeaponHidden1) = (byte)(val1 | Offsets.Character.Flags.IsWeaponHidden1);
*((byte*)a.Address + Offsets.Character.WeaponHidden2) = (byte)(val2 | Offsets.Character.Flags.IsWeaponHidden2);
}
else
{
*((byte*)a.Address + WeaponHiddenOffset1) = (byte)(val1 & ~WeaponHiddenFlag1);
*((byte*)a.Address + WeaponHiddenOffset2) = (byte)(val2 & ~WeaponHiddenFlag2);
*((byte*)a.Address + Offsets.Character.WeaponHidden1) = (byte)(val1 & ~Offsets.Character.Flags.IsWeaponHidden1);
*((byte*)a.Address + Offsets.Character.WeaponHidden2) = (byte)(val2 & ~Offsets.Character.Flags.IsWeaponHidden2);
}
return true;
}
public static unsafe ref float Alpha(this Character a)
=> ref *(float*)((byte*)a.Address + AlphaOffset);
=> ref *(float*)((byte*)a.Address + Offsets.Character.Alpha);
}

View file

@ -310,11 +310,11 @@ public class CharacterSave
a.SetWetness(IsWet);
a.Alpha() = Alpha;
if (SetHatState)
a.SetHatVisible(!HatState);
a.SetHatVisible(HatState);
if (SetVisorState)
a.SetVisorToggled(VisorState);
if (SetWeaponState)
a.SetWeaponHidden(WeaponState);
a.SetWeaponHidden(!WeaponState);
}
public void ApplyOnlyEquipment(Character a)

View file

@ -134,7 +134,7 @@ namespace Glamourer.Designs
design.Jobs.Name);
design.Design.Data.Apply(character);
Glamourer.PlayerWatcher.UpdatePlayerWithoutEvent(character);
Glamourer.Penumbra.RedrawObject(character, RedrawType.WithSettings, false);
Glamourer.Penumbra.RedrawObject(character, RedrawType.Redraw, false);
}
public void Add(string name, Design design, JobGroup group, bool enabled = false)

View file

@ -17,6 +17,7 @@
<Nullable>enable</Nullable>
<OutputPath>bin\$(Configuration)\</OutputPath>
<MSBuildWarningsAsMessages>$(MSBuildWarningsAsMessages);MSB3277</MSBuildWarningsAsMessages>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
@ -69,23 +70,35 @@
<HintPath>$(appdata)\XIVLauncher\addon\Hooks\dev\Lumina.Excel.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Penumbra.GameData">
<HintPath>..\..\Penumbra\Penumbra\bin\$(Configuration)\$(TargetFramework)\Penumbra.GameData.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Penumbra.PlayerWatch">
<HintPath>..\..\Penumbra\Penumbra\bin\$(Configuration)\$(TargetFramework)\Penumbra.PlayerWatch.dll</HintPath>
<Private>True</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3">
<Private>false</Private>
</PackageReference>
<PackageReference Include="System.Memory" Version="4.5.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Glamourer.GameData\Glamourer.GameData.csproj" />
<ProjectReference Include="..\..\Penumbra\Penumbra.GameData\Penumbra.GameData.csproj" />
<ProjectReference Include="..\..\Penumbra\Penumbra.PlayerWatch\Penumbra.PlayerWatch.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>

View file

@ -18,12 +18,14 @@ namespace Glamourer.Gui
{
internal partial class Interface
{
private readonly CharacterSave _currentSave = new();
private string _newDesignName = string.Empty;
private bool _keyboardFocus;
private const string DesignNamePopupLabel = "Save Design As...";
private const uint RedHeaderColor = 0xFF1818C0;
private const uint GreenHeaderColor = 0xFF18C018;
private readonly CharacterSave _currentSave = new();
private string _newDesignName = string.Empty;
private bool _keyboardFocus;
private bool _holdShift;
private bool _holdCtrl;
private const string DesignNamePopupLabel = "Save Design As...";
private const uint RedHeaderColor = 0xFF1818C0;
private const uint GreenHeaderColor = 0xFF18C018;
private void DrawPlayerHeader()
{
@ -58,10 +60,10 @@ namespace Glamourer.Gui
save.Apply(player);
}
private CharacterSave ConditionalCopy(CharacterSave save)
private static CharacterSave ConditionalCopy(CharacterSave save, bool shift, bool ctrl)
{
var copy = save.Copy();
if (ImGui.GetIO().KeyShift)
if (shift)
{
copy.Load(new CharacterEquipment());
copy.SetHatState = false;
@ -69,7 +71,7 @@ namespace Glamourer.Gui
copy.SetWeaponState = false;
copy.WriteEquipment = CharacterEquipMask.None;
}
else if (ImGui.GetIO().KeyCtrl)
else if (ctrl)
{
copy.Load(CharacterCustomization.Default);
copy.SetHatState = false;
@ -77,8 +79,8 @@ namespace Glamourer.Gui
copy.SetWeaponState = false;
copy.WriteCustomizations = false;
}
return save.Copy();
return copy;
}
private bool DrawApplyClipboardButton()

View file

@ -162,7 +162,7 @@ namespace Glamourer.Gui
var count = set.Count(CustomizationId.FacialFeaturesTattoos);
using (var _ = ImGuiRaii.NewGroup())
{
var face = set.Race == Race.Hrothgar ? customization.Hairstyle : customization.Face;
var face = customization.Face;
if (set.Faces.Count < face)
face = 1;
for (var i = 0; i < count; ++i)
@ -288,9 +288,6 @@ namespace Glamourer.Gui
ret = true;
}
if (id == CustomizationId.Hairstyle && customization.Race == Race.Hrothgar)
customization[CustomizationId.Face] = (byte) ((customization[CustomizationId.Hairstyle] + 1) / 2);
ImGui.Text($"{label} ({custom.Value.Value})");
ImGuiCustom.HoverTooltip(tooltip);

View file

@ -57,6 +57,7 @@ namespace Glamourer.Gui
if (!change)
return false;
newItem = new Item(newItem.Base, newItem.Name, slot);
if (_player == null)
return _inDesignMode && (_selection?.Data.WriteItem(newItem) ?? false);

View file

@ -37,10 +37,7 @@ namespace Glamourer.Gui
case CustomizationId.Gender: break;
case CustomizationId.FacialFeaturesTattoos: break;
case CustomizationId.HighlightsOnFlag: break;
case CustomizationId.Face:
if (customization.Race != Race.Hrothgar)
goto default;
break;
case CustomizationId.Face: break;
default:
var count = set.Count(id);
if (set.DataByValue(id, customization[id], out _) < 0)
@ -148,7 +145,7 @@ namespace Glamourer.Gui
switch (use)
{
case DesignNameUse.SaveCurrent:
SaveNewDesign(ConditionalCopy(_currentSave));
SaveNewDesign(ConditionalCopy(_currentSave, _holdShift, _holdCtrl));
break;
case DesignNameUse.NewDesign:
var empty = new CharacterSave();
@ -157,7 +154,7 @@ namespace Glamourer.Gui
SaveNewDesign(empty);
break;
case DesignNameUse.DuplicateDesign:
SaveNewDesign(ConditionalCopy(_selection!.Data));
SaveNewDesign(ConditionalCopy(_selection!.Data, _holdShift, _holdCtrl));
break;
case DesignNameUse.NewFolder:
_designs.FileSystem
@ -196,6 +193,8 @@ namespace Glamourer.Gui
{
_newDesignName = string.Empty;
_keyboardFocus = true;
_holdCtrl = ImGui.GetIO().KeyCtrl;
_holdShift = ImGui.GetIO().KeyShift;
ImGui.OpenPopup($"{DesignNamePopupLabel}{use}");
}
}

23
Glamourer/Offsets.cs Normal file
View file

@ -0,0 +1,23 @@
namespace Glamourer;
public static class Offsets
{
public static class Character
{
public const int Wetness = 0x1ADA;
public const int HatVisible = 0x84E;
public const int VisorToggled = 0x84F;
public const int WeaponHidden1 = 0x84F;
public const int WeaponHidden2 = 0x72C;
public const int Alpha = 0x19E0;
public static class Flags
{
public const byte IsHatHidden = 0x01;
public const byte IsVisorToggled = 0x08;
public const byte IsWet = 0x80;
public const byte IsWeaponHidden1 = 0x01;
public const byte IsWeaponHidden2 = 0x02;
}
}
}

View file

@ -6,135 +6,155 @@ using Glamourer.Gui;
using ImGuiNET;
using Penumbra.GameData.Enums;
namespace Glamourer
namespace Glamourer;
public class PenumbraAttach : IDisposable
{
public class PenumbraAttach : IDisposable
public const int RequiredPenumbraShareVersion = 4;
private ICallGateSubscriber<ChangedItemType, uint, object>? _tooltipSubscriber;
private ICallGateSubscriber<MouseButton, ChangedItemType, uint, object>? _clickSubscriber;
private ICallGateSubscriber<string, int, object>? _redrawSubscriberName;
private ICallGateSubscriber<GameObject, int, object>? _redrawSubscriberObject;
private readonly ICallGateSubscriber<object?> _initializedEvent;
private readonly ICallGateSubscriber<object?> _disposedEvent;
public PenumbraAttach(bool attach)
{
public const int RequiredPenumbraShareVersion = 3;
_initializedEvent = Dalamud.PluginInterface.GetIpcSubscriber<object?>("Penumbra.Initialized");
_disposedEvent = Dalamud.PluginInterface.GetIpcSubscriber<object?>("Penumbra.Disposed");
_initializedEvent.Subscribe(Reattach);
_disposedEvent.Subscribe(Unattach);
Reattach(attach);
}
private ICallGateSubscriber<ChangedItemType, uint, object>? _tooltipSubscriber;
private ICallGateSubscriber<MouseButton, ChangedItemType, uint, object>? _clickSubscriber;
private ICallGateSubscriber<string, int, object>? _redrawSubscriberName;
private ICallGateSubscriber<GameObject, int, object>? _redrawSubscriberObject;
private void Reattach()
=> Reattach(Glamourer.Config.AttachToPenumbra);
public PenumbraAttach(bool attach)
=> Reattach(attach);
public void Reattach(bool attach)
{
try
{
Unattach();
public void Reattach(bool attach)
var versionSubscriber = Dalamud.PluginInterface.GetIpcSubscriber<int>("Penumbra.ApiVersion");
var version = versionSubscriber.InvokeFunc();
if (version != RequiredPenumbraShareVersion)
throw new Exception($"Invalid Version {version}, required Version {RequiredPenumbraShareVersion}.");
_redrawSubscriberName = Dalamud.PluginInterface.GetIpcSubscriber<string, int, object>("Penumbra.RedrawObjectByName");
_redrawSubscriberObject = Dalamud.PluginInterface.GetIpcSubscriber<GameObject, int, object>("Penumbra.RedrawObject");
if (!attach)
return;
_tooltipSubscriber = Dalamud.PluginInterface.GetIpcSubscriber<ChangedItemType, uint, object>("Penumbra.ChangedItemTooltip");
_clickSubscriber =
Dalamud.PluginInterface.GetIpcSubscriber<MouseButton, ChangedItemType, uint, object>("Penumbra.ChangedItemClick");
_tooltipSubscriber.Subscribe(PenumbraTooltip);
_clickSubscriber.Subscribe(PenumbraRightClick);
PluginLog.Debug("Glamourer attached to Penumbra.");
}
catch (Exception e)
{
PluginLog.Debug($"Could not attach to Penumbra:\n{e}");
}
}
public void Unattach()
{
_tooltipSubscriber?.Unsubscribe(PenumbraTooltip);
_clickSubscriber?.Unsubscribe(PenumbraRightClick);
_tooltipSubscriber = null;
_clickSubscriber = null;
_redrawSubscriberName = null;
if (_redrawSubscriberObject != null)
{
PluginLog.Debug("Glamourer detached from Penumbra.");
_redrawSubscriberObject = null;
}
}
public void Dispose()
{
_initializedEvent.Unsubscribe(Reattach);
_disposedEvent.Unsubscribe(Unattach);
Unattach();
}
private static void PenumbraTooltip(ChangedItemType type, uint _)
{
if (type == ChangedItemType.Item)
ImGui.Text("Right click to apply to current Glamourer Set. [Glamourer]");
}
private void PenumbraRightClick(MouseButton button, ChangedItemType type, uint id)
{
if (button != MouseButton.Right || type != ChangedItemType.Item)
return;
var gPose = Dalamud.Objects[Interface.GPoseObjectId] as Character;
var player = Dalamud.Objects[0] as Character;
var item = (Lumina.Excel.GeneratedSheets.Item)type.GetObject(id)!;
var writeItem = new Item(item, string.Empty);
if (gPose != null)
{
writeItem.Write(gPose.Address);
UpdateCharacters(gPose, player);
}
else if (player != null)
{
writeItem.Write(player.Address);
UpdateCharacters(player);
}
}
public void RedrawObject(GameObject actor, RedrawType settings, bool repeat)
{
if (_redrawSubscriberObject != null)
{
try
{
Unattach();
var versionSubscriber = Dalamud.PluginInterface.GetIpcSubscriber<int>("Penumbra.ApiVersion");
var version = versionSubscriber.InvokeFunc();
if (version != RequiredPenumbraShareVersion)
throw new Exception($"Invalid Version {version}, required Version {RequiredPenumbraShareVersion}.");
_redrawSubscriberName = Dalamud.PluginInterface.GetIpcSubscriber<string, int, object>("Penumbra.RedrawObjectByName");
_redrawSubscriberObject = Dalamud.PluginInterface.GetIpcSubscriber<GameObject, int, object>("Penumbra.RedrawObject");
if (!attach)
return;
_tooltipSubscriber = Dalamud.PluginInterface.GetIpcSubscriber<ChangedItemType, uint, object>("Penumbra.ChangedItemTooltip");
_clickSubscriber =
Dalamud.PluginInterface.GetIpcSubscriber<MouseButton, ChangedItemType, uint, object>("Penumbra.ChangedItemClick");
_tooltipSubscriber.Subscribe(PenumbraTooltip);
_clickSubscriber.Subscribe(PenumbraRightClick);
_redrawSubscriberObject.InvokeAction(actor, (int)settings);
}
catch (Exception e)
{
PluginLog.Debug($"Could not attach to Penumbra:\n{e}");
}
}
public void Unattach()
{
_tooltipSubscriber?.Unsubscribe(PenumbraTooltip);
_clickSubscriber?.Unsubscribe(PenumbraRightClick);
_tooltipSubscriber = null;
_clickSubscriber = null;
_redrawSubscriberName = null;
_redrawSubscriberObject = null;
}
public void Dispose()
=> Unattach();
private static void PenumbraTooltip(ChangedItemType type, uint _)
{
if (type == ChangedItemType.Item)
ImGui.Text("Right click to apply to current Glamourer Set. [Glamourer]");
}
private void PenumbraRightClick(MouseButton button, ChangedItemType type, uint id)
{
if (button != MouseButton.Right || type != ChangedItemType.Item)
return;
var gPose = Dalamud.Objects[Interface.GPoseObjectId] as Character;
var player = Dalamud.Objects[0] as Character;
var item = (Lumina.Excel.GeneratedSheets.Item) type.GetObject(id)!;
var writeItem = new Item(item, string.Empty);
if (gPose != null)
{
writeItem.Write(gPose.Address);
UpdateCharacters(gPose, player);
}
else if (player != null)
{
writeItem.Write(player.Address);
UpdateCharacters(player);
}
}
public void RedrawObject(GameObject actor, RedrawType settings, bool repeat)
{
if (_redrawSubscriberObject != null)
{
try
if (repeat)
{
_redrawSubscriberObject.InvokeAction(actor, (int) settings);
Reattach(Glamourer.Config.AttachToPenumbra);
RedrawObject(actor, settings, false);
}
catch (Exception e)
else
{
if (repeat)
{
Reattach(Glamourer.Config.AttachToPenumbra);
RedrawObject(actor, settings, false);
}
else
{
PluginLog.Debug($"Failure redrawing object:\n{e}");
}
PluginLog.Debug($"Failure redrawing object:\n{e}");
}
}
else if (repeat)
{
Reattach(Glamourer.Config.AttachToPenumbra);
RedrawObject(actor, settings, false);
}
else
{
PluginLog.Debug("Trying to redraw object, but not attached to Penumbra.");
}
}
// Update objects without triggering PlayerWatcher Events,
// then manually redraw using Penumbra.
public void UpdateCharacters(Character character, Character? gPoseOriginalCharacter = null)
else if (repeat)
{
var newEquip = Glamourer.PlayerWatcher.UpdatePlayerWithoutEvent(character);
RedrawObject(character, RedrawType.WithSettings, true);
// Special case for carrying over changes to the gPose player to the regular player, too.
if (gPoseOriginalCharacter == null)
return;
newEquip.Write(gPoseOriginalCharacter.Address);
Glamourer.PlayerWatcher.UpdatePlayerWithoutEvent(gPoseOriginalCharacter);
RedrawObject(gPoseOriginalCharacter, RedrawType.AfterGPoseWithSettings, false);
Reattach(Glamourer.Config.AttachToPenumbra);
RedrawObject(actor, settings, false);
}
else
{
PluginLog.Debug("Trying to redraw object, but not attached to Penumbra.");
}
}
// Update objects without triggering PlayerWatcher Events,
// then manually redraw using Penumbra.
public void UpdateCharacters(Character character, Character? gPoseOriginalCharacter = null)
{
var newEquip = Glamourer.PlayerWatcher.UpdatePlayerWithoutEvent(character);
RedrawObject(character, RedrawType.Redraw, true);
// Special case for carrying over changes to the gPose player to the regular player, too.
if (gPoseOriginalCharacter == null)
return;
newEquip.Write(gPoseOriginalCharacter.Address);
Glamourer.PlayerWatcher.UpdatePlayerWithoutEvent(gPoseOriginalCharacter);
RedrawObject(gPoseOriginalCharacter, RedrawType.AfterGPose, false);
}
}