diff --git a/Glamourer.GameData/Customization/CustomizationOptions.cs b/Glamourer.GameData/Customization/CustomizationOptions.cs
index 6ee80d3..0830457 100644
--- a/Glamourer.GameData/Customization/CustomizationOptions.cs
+++ b/Glamourer.GameData/Customization/CustomizationOptions.cs
@@ -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,
diff --git a/Glamourer.GameData/Glamourer.GameData.csproj b/Glamourer.GameData/Glamourer.GameData.csproj
index 560f5bf..c2b60ab 100644
--- a/Glamourer.GameData/Glamourer.GameData.csproj
+++ b/Glamourer.GameData/Glamourer.GameData.csproj
@@ -55,8 +55,9 @@
$(AppData)\XIVLauncher\addon\Hooks\dev\Lumina.Excel.dll
False
-
- ..\..\Penumbra\Penumbra\bin\$(Configuration)\$(TargetFramework)\Penumbra.GameData.dll
-
+
+
+
+
diff --git a/Glamourer.sln b/Glamourer.sln
index 8b1bc26..56bc1ba 100644
--- a/Glamourer.sln
+++ b/Glamourer.sln
@@ -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
diff --git a/Glamourer.sln.DotSettings.user b/Glamourer.sln.DotSettings.user
index e414fe4..12f85e0 100644
--- a/Glamourer.sln.DotSettings.user
+++ b/Glamourer.sln.DotSettings.user
@@ -1,4 +1,6 @@
<AssemblyExplorer>
<Assembly Path="H:\Projects\FFPlugins\Penumbra\Penumbra\bin\Debug\net472\Penumbra.GameData.dll" />
+ <Assembly Path="C:\Users\Ozy\AppData\Roaming\XIVLauncher\addon\Hooks\dev\Dalamud.dll" />
+ <Assembly Path="H:\Projects\FFPlugins\Penumbra\Penumbra\bin\Debug\net5.0-windows\Penumbra.GameData.dll" />
</AssemblyExplorer>
\ No newline at end of file
diff --git a/Glamourer.zip b/Glamourer.zip
index aa4d873..e74dd0d 100644
Binary files a/Glamourer.zip and b/Glamourer.zip differ
diff --git a/Glamourer/CharacterExtensions.cs b/Glamourer/CharacterExtensions.cs
index 4a38366..0d28e13 100644
--- a/Glamourer/CharacterExtensions.cs
+++ b/Glamourer/CharacterExtensions.cs
@@ -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);
}
diff --git a/Glamourer/CharacterSave.cs b/Glamourer/CharacterSave.cs
index eeeca88..c9dd7ea 100644
--- a/Glamourer/CharacterSave.cs
+++ b/Glamourer/CharacterSave.cs
@@ -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)
diff --git a/Glamourer/Designs/FixedDesigns.cs b/Glamourer/Designs/FixedDesigns.cs
index 2c56197..5d0804f 100644
--- a/Glamourer/Designs/FixedDesigns.cs
+++ b/Glamourer/Designs/FixedDesigns.cs
@@ -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)
diff --git a/Glamourer/Glamourer.csproj b/Glamourer/Glamourer.csproj
index 08e8b63..5e183c7 100644
--- a/Glamourer/Glamourer.csproj
+++ b/Glamourer/Glamourer.csproj
@@ -17,6 +17,7 @@
enable
bin\$(Configuration)\
$(MSBuildWarningsAsMessages);MSB3277
+ true
@@ -69,23 +70,35 @@
$(appdata)\XIVLauncher\addon\Hooks\dev\Lumina.Excel.dll
False
-
- ..\..\Penumbra\Penumbra\bin\$(Configuration)\$(TargetFramework)\Penumbra.GameData.dll
- True
-
-
- ..\..\Penumbra\Penumbra\bin\$(Configuration)\$(TargetFramework)\Penumbra.PlayerWatch.dll
- True
-
-
+
+ false
+
+
+
+
+
+
+
+
+ True
+ True
+ Resources.resx
+
+
+
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
diff --git a/Glamourer/Gui/InterfaceActorPanel.cs b/Glamourer/Gui/InterfaceActorPanel.cs
index 49f1914..3ff2f14 100644
--- a/Glamourer/Gui/InterfaceActorPanel.cs
+++ b/Glamourer/Gui/InterfaceActorPanel.cs
@@ -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()
diff --git a/Glamourer/Gui/InterfaceCustomization.cs b/Glamourer/Gui/InterfaceCustomization.cs
index a797bdf..31c500f 100644
--- a/Glamourer/Gui/InterfaceCustomization.cs
+++ b/Glamourer/Gui/InterfaceCustomization.cs
@@ -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);
diff --git a/Glamourer/Gui/InterfaceEquipment.cs b/Glamourer/Gui/InterfaceEquipment.cs
index c7ffc33..019fc64 100644
--- a/Glamourer/Gui/InterfaceEquipment.cs
+++ b/Glamourer/Gui/InterfaceEquipment.cs
@@ -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);
diff --git a/Glamourer/Gui/InterfaceHelpers.cs b/Glamourer/Gui/InterfaceHelpers.cs
index 1a0e8cd..88845ec 100644
--- a/Glamourer/Gui/InterfaceHelpers.cs
+++ b/Glamourer/Gui/InterfaceHelpers.cs
@@ -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}");
}
}
diff --git a/Glamourer/Offsets.cs b/Glamourer/Offsets.cs
new file mode 100644
index 0000000..8c08073
--- /dev/null
+++ b/Glamourer/Offsets.cs
@@ -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;
+ }
+ }
+}
diff --git a/Glamourer/PenumbraAttach.cs b/Glamourer/PenumbraAttach.cs
index 3b75c86..68c8619 100644
--- a/Glamourer/PenumbraAttach.cs
+++ b/Glamourer/PenumbraAttach.cs
@@ -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? _tooltipSubscriber;
+ private ICallGateSubscriber? _clickSubscriber;
+ private ICallGateSubscriber? _redrawSubscriberName;
+ private ICallGateSubscriber? _redrawSubscriberObject;
+
+ private readonly ICallGateSubscriber