This commit is contained in:
Ottermandias 2022-08-03 12:14:19 +02:00
parent 15d93830a3
commit cb2e2f0128
8 changed files with 393 additions and 357 deletions

4
.gitmodules vendored Normal file
View file

@ -0,0 +1,4 @@
[submodule "OtterGui"]
path = OtterGui
url = git@github.com:Ottermandias/OtterGui.git
branch = main

View file

@ -1,68 +1,72 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0-windows</TargetFramework>
<TargetFramework>net6.0-windows</TargetFramework>
<LangVersion>preview</LangVersion>
<PlatformTarget>x64</PlatformTarget>
<RootNamespace>Glamourer</RootNamespace>
<AssemblyName>Glamourer.GameData</AssemblyName>
<FileVersion>1.0.0.0</FileVersion>
<RootNamespace>Glamourer</RootNamespace>
<AssemblyName>Glamourer.GameData</AssemblyName>
<FileVersion>1.0.0.0</FileVersion>
<AssemblyVersion>1.0.0.0</AssemblyVersion>
<Company>SoftOtter</Company>
<Company>SoftOtter</Company>
<Product>Glamourer</Product>
<Copyright>Copyright © 2020</Copyright>
<Deterministic>true</Deterministic>
<OutputType>Library</OutputType>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Nullable>enable</Nullable>
<OutputPath>bin\$(Configuration)\</OutputPath>
<MSBuildWarningsAsMessages>$(MSBuildWarningsAsMessages);MSB3277</MSBuildWarningsAsMessages>
</PropertyGroup>
<Deterministic>true</Deterministic>
<OutputType>Library</OutputType>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Nullable>enable</Nullable>
<OutputPath>bin\$(Configuration)\</OutputPath>
<MSBuildWarningsAsMessages>$(MSBuildWarningsAsMessages);MSB3277</MSBuildWarningsAsMessages>
<ProduceReferenceAssembly>false</ProduceReferenceAssembly>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugType>full</DebugType>
<DefineConstants>DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugType>full</DebugType>
<DefineConstants>DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
</PropertyGroup>
<PropertyGroup>
<MSBuildWarningsAsMessages>$(MSBuildWarningsAsMessages);MSB3277</MSBuildWarningsAsMessages>
</PropertyGroup>
<PropertyGroup>
<MSBuildWarningsAsMessages>$(MSBuildWarningsAsMessages);MSB3277</MSBuildWarningsAsMessages>
</PropertyGroup>
<ItemGroup>
<Reference Include="Dalamud">
<HintPath>$(DALAMUD_ROOT)\Dalamud.dll</HintPath>
<HintPath>..\libs\Dalamud.dll</HintPath>
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\dev\Dalamud.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="FFXIVClientStructs">
<HintPath>$(appdata)\XIVLauncher\addon\Hooks\dev\FFXIVClientStructs.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="ImGuiScene">
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\dev\ImGuiScene.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Lumina">
<HintPath>$(DALAMUD_ROOT)\Lumina.dll</HintPath>
<HintPath>..\libs\Lumina.dll</HintPath>
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\dev\Lumina.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Lumina.Excel">
<HintPath>$(DALAMUD_ROOT)\Lumina.Excel.dll</HintPath>
<HintPath>..\libs\Lumina.Excel.dll</HintPath>
<HintPath>$(AppData)\XIVLauncher\addon\Hooks\dev\Lumina.Excel.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
<PropertyGroup>
<DalamudLibPath>$(AppData)\XIVLauncher\addon\Hooks\dev\</DalamudLibPath>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\Penumbra\OtterGui\OtterGui.csproj" />
<ProjectReference Include="..\..\Penumbra\Penumbra.GameData\Penumbra.GameData.csproj" />
</ItemGroup>
</Project>
<ItemGroup>
<Reference Include="Dalamud">
<HintPath>$(DalamudLibPath)Dalamud.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="FFXIVClientStructs">
<HintPath>$(DalamudLibPath)FFXIVClientStructs.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Lumina">
<HintPath>$(DalamudLibPath)Lumina.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Lumina.Excel">
<HintPath>$(DalamudLibPath)Lumina.Excel.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Newtonsoft.Json">
<HintPath>$(DalamudLibPath)Newtonsoft.Json.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="ImGuiScene">
<HintPath>$(DalamudLibPath)ImGuiScene.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Penumbra\OtterGui\OtterGui.csproj" />
<ProjectReference Include="..\..\Penumbra\Penumbra.GameData\Penumbra.GameData.csproj" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,149 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Glamourer.Customization;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
namespace Glamourer;
public class CurrentManipulations
{
private readonly RestrictedGear _restrictedGear = GameData.RestrictedGear(Dalamud.GameData);
private readonly Dictionary<Actor.IIdentifier, CharacterSave> _characterSaves = new();
public CharacterSave CreateSave(Actor actor)
{
var id = actor.GetIdentifier();
if (_characterSaves.TryGetValue(id, out var save))
return save;
save = new CharacterSave(actor);
_characterSaves.Add(id.CreatePermanent(), save);
return save;
}
public bool TryGetDesign(Actor.IIdentifier identifier, [NotNullWhen(true)] out CharacterSave? save)
=> _characterSaves.TryGetValue(identifier, out save);
public CharacterArmor? ChangeEquip(Actor actor, EquipSlot slot, CharacterArmor data)
{
var save = CreateSave(actor);
(_, data) = _restrictedGear.ResolveRestricted(data, slot, save.Customize.Race, save.Customize.Gender);
if (save.Equipment[slot] == data)
return null;
save.Equipment[slot] = data;
return data;
}
public bool ChangeWeapon(Actor actor, CharacterWeapon main)
{
var save = CreateSave(actor);
if (save.MainHand == main)
return false;
save.MainHand = main;
return true;
}
public bool ChangeWeapon(Actor actor, CharacterWeapon main, CharacterWeapon off)
{
var save = CreateSave(actor);
if (main == save.MainHand && off == save.OffHand)
return false;
save.MainHand = main;
save.OffHand = off;
return true;
}
public void ChangeCustomization(Actor actor, Customize customize)
{
var save = CreateSave(actor);
FixRestrictedGear(save, customize.Gender, customize.Race);
save.Customize.Load(customize);
}
public bool ChangeCustomization(Actor actor, CustomizationId id, byte value)
{
if (id == CustomizationId.Race)
return ChangeRace(actor, (SubRace)value);
if (id == CustomizationId.Gender)
return ChangeGender(actor, (Gender)value);
var save = CreateSave(actor);
var customize = save.Customize;
if (customize[id] != value)
return false;
customize[id] = value;
return true;
}
// Change a gender and fix up all required customizations afterwards.
public bool ChangeGender(Actor actor, Gender gender)
{
var save = CreateSave(actor);
if (save.Customize.Gender == gender)
return false;
var customize = save.Customize;
FixRestrictedGear(save, gender, customize.Race);
FixUpAttributes(customize);
return true;
}
// Change a race and fix up all required customizations afterwards.
public bool ChangeRace(Actor actor, SubRace clan)
{
var save = CreateSave(actor);
if (save.Customize.Clan == clan)
return false;
var customize = save.Customize;
var race = clan.ToRace();
var gender = race == Race.Hrothgar ? Gender.Male : customize.Gender; // TODO Female Hrothgar
FixRestrictedGear(save, gender, race);
customize.Gender = gender;
customize.Race = race;
customize.Clan = clan;
FixUpAttributes(customize);
return true;
}
// Go through a whole customization struct and fix up all settings that need fixing.
private void FixUpAttributes(Customize customize)
{
var set = Glamourer.Customization.GetList(customize.Clan, customize.Gender);
foreach (CustomizationId id in Enum.GetValues(typeof(CustomizationId)))
{
switch (id)
{
case CustomizationId.Race: break;
case CustomizationId.Clan: break;
case CustomizationId.BodyType: break;
case CustomizationId.Gender: break;
case CustomizationId.FacialFeaturesTattoos: break;
case CustomizationId.HighlightsOnFlag: break;
case CustomizationId.Face: break;
default:
var count = set.Count(id);
if (set.DataByValue(id, customize[id], out _) < 0)
customize[id] = count == 0 ? (byte)0 : set.Data(id, 0).Value;
break;
}
}
}
private void FixRestrictedGear(CharacterSave save, Gender gender, Race race)
{
if (race == save.Customize.Race && gender == save.Customize.Gender)
return;
var equip = save.Equipment;
foreach (var slot in EquipSlotExtensions.EqdpSlots)
(_, equip[slot]) = _restrictedGear.ResolveRestricted(equip[slot], slot, race, gender);
}
}

12
Glamourer/FixedDesigns.cs Normal file
View file

@ -0,0 +1,12 @@
using System.Diagnostics.CodeAnalysis;
namespace Glamourer;
public class FixedDesigns
{
public bool TryGetDesign(Actor.IIdentifier actor, [NotNullWhen(true)] out CharacterSave? save)
{
save = null;
return false;
}
}

View file

@ -1,29 +1,10 @@
using System.Collections;
using System.ComponentModel.Design;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.InteropServices.ComTypes;
using Dalamud.Data;
using Dalamud.Game.ClientState.JobGauge.Enums;
using System.Reflection;
using Dalamud.Game.Command;
using Dalamud.Hooking;
using Dalamud.Interface.Windowing;
using Dalamud.Plugin;
using FFXIVClientStructs.Attributes;
using FFXIVClientStructs.FFXIV.Client.Game.UI;
using FFXIVClientStructs.FFXIV.Component.Excel;
using Glamourer.Api;
using Glamourer.Customization;
using Glamourer.Gui;
using Lumina.Data.Parsing;
using Microsoft.VisualBasic.CompilerServices;
using OtterGui.Table;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using static FFXIVClientStructs.FFXIV.Client.UI.Misc.RaptureMacroModule;
using static System.Collections.Specialized.BitVector32;
using static System.Reflection.Metadata.BlobBuilder;
using Race = Lumina.Excel.GeneratedSheets.Race;
namespace Glamourer;
@ -44,11 +25,12 @@ public class Glamourer : IDalamudPlugin
public static GlamourerConfig Config = null!;
public static PenumbraAttach Penumbra = null!;
public static ICustomizationManager Customization = null!;
public static RedrawManager RedrawManager = null!;
private readonly WindowSystem _windowSystem = new("Glamourer");
public static PenumbraAttach Penumbra = null!;
public static ICustomizationManager Customization = null!;
public static RedrawManager RedrawManager = null!;
private readonly WindowSystem _windowSystem = new("Glamourer");
private readonly FixedDesigns _fixedDesigns;
private readonly CurrentManipulations _currentManipulations;
private readonly Interface _interface;
//public readonly DesignManager Designs;
@ -59,14 +41,15 @@ public class Glamourer : IDalamudPlugin
public unsafe Glamourer(DalamudPluginInterface pluginInterface)
{
Dalamud.Initialize(pluginInterface);
Customization = CustomizationManager.Create(Dalamud.PluginInterface, Dalamud.GameData, Dalamud.ClientState.ClientLanguage);
Config = GlamourerConfig.Load();
Penumbra = new PenumbraAttach(Config.AttachToPenumbra);
Customization = CustomizationManager.Create(Dalamud.PluginInterface, Dalamud.GameData, Dalamud.ClientState.ClientLanguage);
Config = GlamourerConfig.Load();
Penumbra = new PenumbraAttach(Config.AttachToPenumbra);
_fixedDesigns = new FixedDesigns();
//Designs = new DesignManager();
//GlamourerIpc = new GlamourerIpc(Dalamud.ClientState, Dalamud.Objects, Dalamud.PluginInterface);
RedrawManager = new RedrawManager();
RedrawManager = new RedrawManager(_fixedDesigns, _currentManipulations);
Dalamud.Commands.AddHandler(MainCommandString, new CommandInfo(OnGlamourer)
{

View file

@ -1,104 +1,106 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net5.0-windows</TargetFramework>
<LangVersion>preview</LangVersion>
<TargetFramework>net6.0-windows</TargetFramework>
<LangVersion>preview</LangVersion>
<PlatformTarget>x64</PlatformTarget>
<RootNamespace>Glamourer</RootNamespace>
<AssemblyName>Glamourer</AssemblyName>
<FileVersion>0.1.0.5</FileVersion>
<RootNamespace>Glamourer</RootNamespace>
<AssemblyName>Glamourer</AssemblyName>
<FileVersion>0.1.0.5</FileVersion>
<AssemblyVersion>0.1.0.5</AssemblyVersion>
<Company>SoftOtter</Company>
<Company>SoftOtter</Company>
<Product>Glamourer</Product>
<Copyright>Copyright © 2020</Copyright>
<Deterministic>true</Deterministic>
<OutputType>Library</OutputType>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Nullable>enable</Nullable>
<OutputPath>bin\$(Configuration)\</OutputPath>
<MSBuildWarningsAsMessages>$(MSBuildWarningsAsMessages);MSB3277</MSBuildWarningsAsMessages>
<Deterministic>true</Deterministic>
<OutputType>Library</OutputType>
<WarningLevel>4</WarningLevel>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Nullable>enable</Nullable>
<OutputPath>bin\$(Configuration)\</OutputPath>
<MSBuildWarningsAsMessages>$(MSBuildWarningsAsMessages);MSB3277</MSBuildWarningsAsMessages>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup>
<ProduceReferenceAssembly>false</ProduceReferenceAssembly>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<DefineConstants>DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<DefineConstants>DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<DefineConstants>TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<DefineConstants>TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup>
<RunPostBuildEvent>OnOutputUpdated</RunPostBuildEvent>
</PropertyGroup>
<PropertyGroup>
<RunPostBuildEvent>OnOutputUpdated</RunPostBuildEvent>
</PropertyGroup>
<ItemGroup>
<None Remove="LegacyTattoo.raw" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="LegacyTattoo.raw" />
</ItemGroup>
<ItemGroup>
<Reference Include="Dalamud">
<HintPath>$(appdata)\XIVLauncher\addon\Hooks\dev\Dalamud.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="FFXIVClientStructs">
<HintPath>$(appdata)\XIVLauncher\addon\Hooks\dev\FFXIVClientStructs.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="ImGui.NET">
<HintPath>$(appdata)\XIVLauncher\addon\Hooks\dev\ImGui.NET.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="ImGuiScene">
<HintPath>$(appdata)\XIVLauncher\addon\Hooks\dev\ImGuiScene.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="SDL2-CS">
<HintPath>$(appdata)\XIVLauncher\addon\Hooks\dev\SDL2-CS.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Lumina">
<HintPath>$(appdata)\XIVLauncher\addon\Hooks\dev\Lumina.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Lumina.Excel">
<HintPath>$(appdata)\XIVLauncher\addon\Hooks\dev\Lumina.Excel.dll</HintPath>
<Private>False</Private>
</Reference>
<ItemGroup>
<None Remove="LegacyTattoo.raw" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="LegacyTattoo.raw" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Glamourer.GameData\Glamourer.GameData.csproj" />
<ProjectReference Include="..\..\Penumbra\Penumbra.GameData\Penumbra.GameData.csproj" />
</ItemGroup>
<PropertyGroup>
<DalamudLibPath>$(AppData)\XIVLauncher\addon\Hooks\dev\</DalamudLibPath>
</PropertyGroup>
<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<Reference Include="Dalamud">
<HintPath>$(DalamudLibPath)Dalamud.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="FFXIVClientStructs">
<HintPath>$(DalamudLibPath)FFXIVClientStructs.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="ImGui.NET">
<HintPath>$(DalamudLibPath)ImGui.NET.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="ImGuiScene">
<HintPath>$(DalamudLibPath)ImGuiScene.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Lumina">
<HintPath>$(DalamudLibPath)Lumina.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Lumina.Excel">
<HintPath>$(DalamudLibPath)Lumina.Excel.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Newtonsoft.Json">
<HintPath>$(DalamudLibPath)Newtonsoft.Json.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Glamourer.GameData\Glamourer.GameData.csproj" />
<ProjectReference Include="..\..\Penumbra\Penumbra.GameData\Penumbra.GameData.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>
<Target Name="GetGitHash" BeforeTargets="GetAssemblyVersion" Returns="InformationalVersion">
<Exec Command="git rev-parse --short HEAD" ConsoleToMSBuild="true" StandardOutputImportance="low">
@ -111,12 +113,14 @@
</Target>
<ItemGroup>
<None Update="Glamourer.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec Command="if $(Configuration) == Release powershell Compress-Archive -Force $(TargetPath), $(TargetDir)$(SolutionName).json, $(TargetDir)$(SolutionName).GameData.dll, $(TargetDir)Penumbra.GameData.dll $(SolutionDir)$(SolutionName).zip" />
<Exec Command="if $(Configuration) == Release powershell Copy-Item -Force $(TargetDir)$(SolutionName).json -Destination $(SolutionDir)" />
</Target>
<None Update="Glamourer.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent">
<Exec
Command="if $(Configuration) == Release powershell Compress-Archive -Force $(TargetPath), $(TargetDir)$(SolutionName).json, $(TargetDir)$(SolutionName).GameData.dll, $(TargetDir)Penumbra.GameData.dll $(SolutionDir)$(SolutionName).zip" />
<Exec
Command="if $(Configuration) == Release powershell Copy-Item -Force $(TargetDir)$(SolutionName).json -Destination $(SolutionDir)" />
</Target>
</Project>

View file

@ -1,163 +1,15 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Dalamud.Hooking;
using Dalamud.Logging;
using Dalamud.Utility.Signatures;
using FFXIVClientStructs.FFXIV.Client.Game.Character;
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using Glamourer.Customization;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using static Glamourer.Actor;
namespace Glamourer;
public class CurrentManipulations
{
private readonly RestrictedGear _restrictedGear = GameData.RestrictedGear(Dalamud.GameData);
private readonly Dictionary<Actor.IIdentifier, CharacterSave> _characterSaves = new();
public CharacterSave CreateSave(Actor actor)
{
var id = actor.GetIdentifier();
if (_characterSaves.TryGetValue(id, out var save))
return save;
save = new CharacterSave(actor);
_characterSaves.Add(id.CreatePermanent(), save);
return save;
}
public bool GetSave(Actor actor, [NotNullWhen(true)] out CharacterSave? save)
{
save = null;
return actor && _characterSaves.TryGetValue(actor.GetIdentifier(), out save);
}
public bool GetSave(Actor.IIdentifier identifier, [NotNullWhen(true)] out CharacterSave? save)
=> _characterSaves.TryGetValue(identifier, out save);
public CharacterArmor? ChangeEquip(Actor actor, EquipSlot slot, CharacterArmor data)
{
var save = CreateSave(actor);
(_, data) = _restrictedGear.ResolveRestricted(data, slot, save.Customize.Race, save.Customize.Gender);
if (save.Equipment[slot] == data)
return null;
save.Equipment[slot] = data;
return data;
}
public bool ChangeWeapon(Actor actor, CharacterWeapon main)
{
var save = CreateSave(actor);
if (save.MainHand == main)
return false;
save.MainHand = main;
return true;
}
public bool ChangeWeapon(Actor actor, CharacterWeapon main, CharacterWeapon off)
{
var save = CreateSave(actor);
if (main == save.MainHand && off == save.OffHand)
return false;
save.MainHand = main;
save.OffHand = off;
return true;
}
public void ChangeCustomization(Actor actor, Customize customize)
{
var save = CreateSave(actor);
FixRestrictedGear(save, customize.Gender, customize.Race);
save.Customize.Load(customize);
}
public bool ChangeCustomization(Actor actor, CustomizationId id, byte value)
{
if (id == CustomizationId.Race)
return ChangeRace(actor, (SubRace)value);
if (id == CustomizationId.Gender)
return ChangeGender(actor, (Gender)value);
var save = CreateSave(actor);
var customize = save.Customize;
if (customize[id] != value)
return false;
customize[id] = value;
return true;
}
// Change a gender and fix up all required customizations afterwards.
public bool ChangeGender(Actor actor, Gender gender)
{
var save = CreateSave(actor);
if (save.Customize.Gender == gender)
return false;
var customize = save.Customize;
FixRestrictedGear(save, gender, customize.Race);
FixUpAttributes(customize);
return true;
}
// Change a race and fix up all required customizations afterwards.
public bool ChangeRace(Actor actor, SubRace clan)
{
var save = CreateSave(actor);
if (save.Customize.Clan == clan)
return false;
var customize = save.Customize;
var race = clan.ToRace();
var gender = race == Race.Hrothgar ? Gender.Male : customize.Gender; // TODO Female Hrothgar
FixRestrictedGear(save, gender, race);
customize.Gender = gender;
customize.Race = race;
customize.Clan = clan;
FixUpAttributes(customize);
return true;
}
// Go through a whole customization struct and fix up all settings that need fixing.
private void FixUpAttributes(Customize customize)
{
var set = Glamourer.Customization.GetList(customize.Clan, customize.Gender);
foreach (CustomizationId id in Enum.GetValues(typeof(CustomizationId)))
{
switch (id)
{
case CustomizationId.Race: break;
case CustomizationId.Clan: break;
case CustomizationId.BodyType: break;
case CustomizationId.Gender: break;
case CustomizationId.FacialFeaturesTattoos: break;
case CustomizationId.HighlightsOnFlag: break;
case CustomizationId.Face: break;
default:
var count = set.Count(id);
if (set.DataByValue(id, customize[id], out _) < 0)
customize[id] = count == 0 ? (byte)0 : set.Data(id, 0).Value;
break;
}
}
}
private void FixRestrictedGear(CharacterSave save, Gender gender, Race race)
{
if (race == save.Customize.Race && gender == save.Customize.Gender)
return;
var equip = save.Equipment;
foreach (var slot in EquipSlotExtensions.EqdpSlots)
(_, equip[slot]) = _restrictedGear.ResolveRestricted(equip[slot], slot, race, gender);
}
}
public unsafe partial class RedrawManager
{
public delegate ulong FlagSlotForUpdateDelegate(Human* drawObject, uint slot, CharacterArmor* data);
@ -168,25 +20,27 @@ public unsafe partial class RedrawManager
private ulong FlagSlotForUpdateDetour(Human* drawObject, uint slot, CharacterArmor* data)
{
//try
//{
// var actor = Glamourer.Penumbra.GameObjectFromDrawObject((IntPtr)drawObject);
// if (actor && CurrentManipulations.GetSave(actor, out _))
// // TODO fixed design
//
// *data = CurrentManipulations.ChangeEquip(actor, slot.ToEquipSlot(), *data) ?? *data;
//}
//catch
//{
// // ignored
//}
try
{
var actor = Glamourer.Penumbra.GameObjectFromDrawObject((IntPtr)drawObject);
var identifier = actor.GetIdentifier();
if (_fixedDesigns.TryGetDesign(identifier, out var save))
PluginLog.Information($"Loaded {slot.ToEquipSlot()} from fixed design for {identifier}.");
else if (_currentManipulations.TryGetDesign(identifier, out save))
PluginLog.Information($"Updated {slot.ToEquipSlot()} from current designs for {identifier}.");
}
catch (Exception e)
{
PluginLog.Error($"Error on loading new gear:\n{e}");
}
return _flagSlotForUpdateHook!.Original(drawObject, slot, data);
}
public bool ChangeEquip(Actor actor, EquipSlot slot, CharacterArmor data)
{
if (actor && CurrentManipulations.ChangeEquip(actor, slot, data).HasValue && actor.DrawObject != null)
if (actor && actor.DrawObject != null)
return _flagSlotForUpdateHook?.Original(actor.DrawObject, slot.ToIndex(), &data) != 0;
return false;
@ -214,6 +68,20 @@ public unsafe partial class RedrawManager
private void LoadWeaponDetour(IntPtr characterOffset, uint slot, CharacterWeapon weapon, byte unk1, byte unk2, byte unk3, byte unk4)
{
try
{
var character = (Actor)(characterOffset - CharacterWeaponOffset);
var identifier = character.GetIdentifier();
if (_fixedDesigns.TryGetDesign(identifier, out var save))
PluginLog.Information($"Loaded weapon from fixed design for {identifier}.");
else if (unk1 == 1 && _currentManipulations.TryGetDesign(identifier, out save))
PluginLog.Information($"Loaded weapon from current design for {identifier}.");
}
catch (Exception e)
{
PluginLog.Error($"Error on loading new weapon:\n{e}");
}
_loadWeaponHook!.Original(characterOffset, slot, weapon, unk1, unk2, unk3, unk4);
}
@ -223,14 +91,14 @@ public unsafe partial class RedrawManager
switch (slot)
{
case EquipSlot.MainHand:
LoadWeaponDetour(character + CharacterWeaponOffset, 0, weapon, 1, 1, 0, 1);
LoadWeaponDetour(character + CharacterWeaponOffset, 0, weapon, 0, 1, 0, 0);
return;
case EquipSlot.OffHand:
LoadWeaponDetour(character + CharacterWeaponOffset, 1, weapon, 1, 1, 0, 1);
LoadWeaponDetour(character + CharacterWeaponOffset, 1, weapon, 0, 1, 0, 0);
return;
case EquipSlot.BothHand:
LoadWeaponDetour(character + CharacterWeaponOffset, 0, weapon, 1, 1, 0, 1);
LoadWeaponDetour(character + CharacterWeaponOffset, 1, CharacterWeapon.Empty, 1, 1, 0, 1);
LoadWeaponDetour(character + CharacterWeaponOffset, 0, weapon, 0, 1, 0, 0);
LoadWeaponDetour(character + CharacterWeaponOffset, 1, CharacterWeapon.Empty, 0, 1, 0, 0);
return;
// function can also be called with '2', but does not seem to ever be.
}
@ -242,8 +110,8 @@ public unsafe partial class RedrawManager
// Load specific Main- and Offhand weapons.
public void LoadWeapon(IntPtr character, CharacterWeapon main, CharacterWeapon off)
{
LoadWeaponDetour(character + CharacterWeaponOffset, 0, main, 1, 1, 0, 1);
LoadWeaponDetour(character + CharacterWeaponOffset, 1, off, 1, 1, 0, 1);
LoadWeaponDetour(character + CharacterWeaponOffset, 0, main, 0, 1, 0, 0);
LoadWeaponDetour(character + CharacterWeaponOffset, 1, off, 0, 1, 0, 0);
}
public void LoadWeapon(Character* character, CharacterWeapon main, CharacterWeapon off)
@ -254,10 +122,15 @@ public unsafe partial class RedrawManager : IDisposable
{
internal readonly CurrentManipulations CurrentManipulations = new();
public RedrawManager()
private readonly FixedDesigns _fixedDesigns;
private readonly CurrentManipulations _currentManipulations;
public RedrawManager(FixedDesigns fixedDesigns, CurrentManipulations currentManipulations)
{
SignatureHelper.Initialise(this);
Glamourer.Penumbra.CreatingCharacterBase += OnCharacterRedraw;
_fixedDesigns = fixedDesigns;
_currentManipulations = currentManipulations;
//_flagSlotForUpdateHook?.Enable();
//_loadWeaponHook?.Enable();
}
@ -271,13 +144,19 @@ public unsafe partial class RedrawManager : IDisposable
private void OnCharacterRedraw(IntPtr addr, IntPtr modelId, IntPtr customize, IntPtr equipData)
{
//if (CurrentManipulations.GetSave(addr, out var save))
//{
// *(CustomizationData*)customize = *(CustomizationData*)save.Customization.Address;
// var equip = (CharacterEquip)equipData;
// var newEquip = save.Equipment;
// for (var i = 0; i < 10; ++i)
// equip[i] = newEquip[i];
//}
try
{
var actor = (Actor)addr;
var identifier = actor.GetIdentifier();
if (_currentManipulations.TryGetDesign(identifier, out var save))
PluginLog.Information($"Loaded current design for {identifier}.");
else if (_fixedDesigns.TryGetDesign(identifier, out save))
PluginLog.Information($"Loaded fixed design for {identifier}.");
}
catch (Exception e)
{
PluginLog.Error($"Error on new draw object creation:\n{e}");
}
}
}
}

1
OtterGui Submodule

@ -0,0 +1 @@
Subproject commit 4c92479175161617161d8faf844c8f683aa2d5d2