mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2025-12-12 10:17:23 +01:00
.
This commit is contained in:
parent
8526ce4f33
commit
0b22dd9760
17 changed files with 877 additions and 535 deletions
|
|
@ -2,6 +2,7 @@
|
||||||
using Dalamud.Game.ClientState.Objects.Types;
|
using Dalamud.Game.ClientState.Objects.Types;
|
||||||
using Dalamud.Plugin;
|
using Dalamud.Plugin;
|
||||||
using Glamourer.Designs;
|
using Glamourer.Designs;
|
||||||
|
using Glamourer.Events;
|
||||||
using Glamourer.Interop.Structs;
|
using Glamourer.Interop.Structs;
|
||||||
using Penumbra.Api.Helpers;
|
using Penumbra.Api.Helpers;
|
||||||
using Penumbra.GameData.Actors;
|
using Penumbra.GameData.Actors;
|
||||||
|
|
@ -76,7 +77,7 @@ public partial class GlamourerIpc
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
_stateManager.ApplyDesign(design, state);
|
_stateManager.ApplyDesign(design, state, StateChanged.Source.Ipc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,17 @@ public class AutoDesignApplier : IDisposable
|
||||||
_state.ReapplyState(actor);
|
_state.ReapplyState(actor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ReapplyAutomation(Actor actor, ActorIdentifier identifier, ActorState state)
|
||||||
|
{
|
||||||
|
if (!_config.EnableAutoDesigns)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!GetPlayerSet(identifier, out var set))
|
||||||
|
return;
|
||||||
|
|
||||||
|
Reduce(actor, state, set, false);
|
||||||
|
}
|
||||||
|
|
||||||
public void Reduce(Actor actor, ActorIdentifier identifier, ActorState state)
|
public void Reduce(Actor actor, ActorIdentifier identifier, ActorState state)
|
||||||
{
|
{
|
||||||
if (!_config.EnableAutoDesigns)
|
if (!_config.EnableAutoDesigns)
|
||||||
|
|
|
||||||
|
|
@ -130,8 +130,8 @@ public sealed class Design : DesignBase, ISavable
|
||||||
if (design.LastEdit < creationDate)
|
if (design.LastEdit < creationDate)
|
||||||
design.LastEdit = creationDate;
|
design.LastEdit = creationDate;
|
||||||
|
|
||||||
LoadEquip(items, json["Equipment"], design, design.Name);
|
|
||||||
LoadCustomize(customizations, json["Customize"], design, design.Name);
|
LoadCustomize(customizations, json["Customize"], design, design.Name);
|
||||||
|
LoadEquip(items, json["Equipment"], design, design.Name);
|
||||||
LoadMods(json["Mods"], design);
|
LoadMods(json["Mods"], design);
|
||||||
return design;
|
return design;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ public class DesignBase
|
||||||
DesignData = clone.DesignData;
|
DesignData = clone.DesignData;
|
||||||
ApplyCustomize = clone.ApplyCustomize & CustomizeFlagExtensions.All;
|
ApplyCustomize = clone.ApplyCustomize & CustomizeFlagExtensions.All;
|
||||||
ApplyEquip = clone.ApplyEquip & EquipFlagExtensions.All;
|
ApplyEquip = clone.ApplyEquip & EquipFlagExtensions.All;
|
||||||
_designFlags = clone._designFlags & (DesignFlags) 0x0F;
|
_designFlags = clone._designFlags & (DesignFlags)0x0F;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal DesignData DesignData = new();
|
internal DesignData DesignData = new();
|
||||||
|
|
@ -177,7 +177,6 @@ public class DesignBase
|
||||||
};
|
};
|
||||||
|
|
||||||
var ret = new JObject();
|
var ret = new JObject();
|
||||||
|
|
||||||
foreach (var slot in EquipSlotExtensions.EqdpSlots.Prepend(EquipSlot.OffHand).Prepend(EquipSlot.MainHand))
|
foreach (var slot in EquipSlotExtensions.EqdpSlots.Prepend(EquipSlot.OffHand).Prepend(EquipSlot.MainHand))
|
||||||
{
|
{
|
||||||
var item = DesignData.Item(slot);
|
var item = DesignData.Item(slot);
|
||||||
|
|
@ -188,6 +187,7 @@ public class DesignBase
|
||||||
ret["Hat"] = new QuadBool(DesignData.IsHatVisible(), DoApplyHatVisible()).ToJObject("Show", "Apply");
|
ret["Hat"] = new QuadBool(DesignData.IsHatVisible(), DoApplyHatVisible()).ToJObject("Show", "Apply");
|
||||||
ret["Visor"] = new QuadBool(DesignData.IsVisorToggled(), DoApplyVisorToggle()).ToJObject("IsToggled", "Apply");
|
ret["Visor"] = new QuadBool(DesignData.IsVisorToggled(), DoApplyVisorToggle()).ToJObject("IsToggled", "Apply");
|
||||||
ret["Weapon"] = new QuadBool(DesignData.IsWeaponVisible(), DoApplyWeaponVisible()).ToJObject("Show", "Apply");
|
ret["Weapon"] = new QuadBool(DesignData.IsWeaponVisible(), DoApplyWeaponVisible()).ToJObject("Show", "Apply");
|
||||||
|
ret["Array"] = DesignData.WriteEquipmentBytesBase64();
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
@ -213,6 +213,7 @@ public class DesignBase
|
||||||
["Value"] = DesignData.IsWet(),
|
["Value"] = DesignData.IsWet(),
|
||||||
["Apply"] = DoApplyWetness(),
|
["Apply"] = DoApplyWetness(),
|
||||||
};
|
};
|
||||||
|
ret["Array"] = DesignData.Customize.WriteBase64();
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
@ -234,8 +235,8 @@ public class DesignBase
|
||||||
private static DesignBase LoadDesignV1Base(CustomizationService customizations, ItemManager items, JObject json)
|
private static DesignBase LoadDesignV1Base(CustomizationService customizations, ItemManager items, JObject json)
|
||||||
{
|
{
|
||||||
var ret = new DesignBase(items);
|
var ret = new DesignBase(items);
|
||||||
LoadEquip(items, json["Equipment"], ret, "Temporary Design");
|
|
||||||
LoadCustomize(customizations, json["Customize"], ret, "Temporary Design");
|
LoadCustomize(customizations, json["Customize"], ret, "Temporary Design");
|
||||||
|
LoadEquip(items, json["Equipment"], ret, "Temporary Design");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -249,6 +250,13 @@ public class DesignBase
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!design.DesignData.IsHuman)
|
||||||
|
{
|
||||||
|
var textArray = equip["Array"]?.ToObject<string>() ?? string.Empty;
|
||||||
|
design.DesignData.SetEquipmentBytesFromBase64(textArray);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
static (uint, StainId, bool, bool) ParseItem(EquipSlot slot, JToken? item)
|
static (uint, StainId, bool, bool) ParseItem(EquipSlot slot, JToken? item)
|
||||||
{
|
{
|
||||||
var id = item?["ItemId"]?.ToObject<uint>() ?? ItemManager.NothingId(slot);
|
var id = item?["ItemId"]?.ToObject<uint>() ?? ItemManager.NothingId(slot);
|
||||||
|
|
@ -314,6 +322,7 @@ public class DesignBase
|
||||||
if (json == null)
|
if (json == null)
|
||||||
{
|
{
|
||||||
design.DesignData.ModelId = 0;
|
design.DesignData.ModelId = 0;
|
||||||
|
design.DesignData.IsHuman = true;
|
||||||
design.DesignData.Customize = Customize.Default;
|
design.DesignData.Customize = Customize.Default;
|
||||||
Glamourer.Chat.NotificationMessage("The loaded design does not contain any customization data, reset to default.", "Warning",
|
Glamourer.Chat.NotificationMessage("The loaded design does not contain any customization data, reset to default.", "Warning",
|
||||||
NotificationType.Warning);
|
NotificationType.Warning);
|
||||||
|
|
@ -326,8 +335,18 @@ public class DesignBase
|
||||||
Glamourer.Chat.NotificationMessage($"{msg} ({name})", "Warning", NotificationType.Warning);
|
Glamourer.Chat.NotificationMessage($"{msg} ({name})", "Warning", NotificationType.Warning);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var wetness = QuadBool.FromJObject(json["Wetness"], "Value", "Apply", QuadBool.NullFalse);
|
||||||
|
design.DesignData.SetIsWet(wetness.ForcedValue);
|
||||||
|
design.SetApplyWetness(wetness.Enabled);
|
||||||
|
|
||||||
design.DesignData.ModelId = json["ModelId"]?.ToObject<uint>() ?? 0;
|
design.DesignData.ModelId = json["ModelId"]?.ToObject<uint>() ?? 0;
|
||||||
PrintWarning(customizations.ValidateModelId(design.DesignData.ModelId, out design.DesignData.ModelId));
|
PrintWarning(customizations.ValidateModelId(design.DesignData.ModelId, out design.DesignData.ModelId, out design.DesignData.IsHuman));
|
||||||
|
if (!design.DesignData.IsHuman)
|
||||||
|
{
|
||||||
|
var arrayText = json["Array"]?.ToObject<string>() ?? string.Empty;
|
||||||
|
design.DesignData.Customize.LoadBase64(arrayText);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var race = (Race)(json[CustomizeIndex.Race.ToString()]?["Value"]?.ToObject<byte>() ?? 0);
|
var race = (Race)(json[CustomizeIndex.Race.ToString()]?["Value"]?.ToObject<byte>() ?? 0);
|
||||||
var clan = (SubRace)(json[CustomizeIndex.Clan.ToString()]?["Value"]?.ToObject<byte>() ?? 0);
|
var clan = (SubRace)(json[CustomizeIndex.Clan.ToString()]?["Value"]?.ToObject<byte>() ?? 0);
|
||||||
|
|
@ -352,10 +371,6 @@ public class DesignBase
|
||||||
design.DesignData.Customize[idx] = data;
|
design.DesignData.Customize[idx] = data;
|
||||||
design.SetApplyCustomize(idx, apply);
|
design.SetApplyCustomize(idx, apply);
|
||||||
}
|
}
|
||||||
|
|
||||||
var wetness = QuadBool.FromJObject(json["Wetness"], "Value", "Apply", QuadBool.NullFalse);
|
|
||||||
design.DesignData.SetIsWet(wetness.ForcedValue);
|
|
||||||
design.SetApplyWetness(wetness.Enabled);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void MigrateBase64(ItemManager items, string base64)
|
public void MigrateBase64(ItemManager items, string base64)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Buffers.Text;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using Glamourer.Customization;
|
using Glamourer.Customization;
|
||||||
using Glamourer.Services;
|
using Glamourer.Services;
|
||||||
|
|
@ -33,6 +34,7 @@ public unsafe struct DesignData
|
||||||
private FullEquipType _typeMainhand;
|
private FullEquipType _typeMainhand;
|
||||||
private FullEquipType _typeOffhand;
|
private FullEquipType _typeOffhand;
|
||||||
private byte _states;
|
private byte _states;
|
||||||
|
public bool IsHuman = true;
|
||||||
|
|
||||||
public DesignData()
|
public DesignData()
|
||||||
{ }
|
{ }
|
||||||
|
|
@ -202,14 +204,49 @@ public unsafe struct DesignData
|
||||||
SetStain(EquipSlot.OffHand, 0);
|
SetStain(EquipSlot.OffHand, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void LoadNonHuman(uint modelId, Customize customize, byte* equipData)
|
|
||||||
|
public bool LoadNonHuman(uint modelId, Customize customize, byte* equipData)
|
||||||
{
|
{
|
||||||
ModelId = modelId;
|
ModelId = modelId;
|
||||||
|
IsHuman = false;
|
||||||
Customize.Load(customize);
|
Customize.Load(customize);
|
||||||
fixed (byte* ptr = _equipmentBytes)
|
fixed (byte* ptr = _equipmentBytes)
|
||||||
{
|
{
|
||||||
MemoryUtility.MemCpyUnchecked(ptr, equipData, 40);
|
MemoryUtility.MemCpyUnchecked(ptr, equipData, 40);
|
||||||
|
MemoryUtility.MemSet(ptr + 40, 0, 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SetHatVisible(true);
|
||||||
|
SetWeaponVisible(true);
|
||||||
|
SetVisor(false);
|
||||||
|
fixed (uint* ptr = _itemIds)
|
||||||
|
{
|
||||||
|
MemoryUtility.MemSet(ptr, 0, 12 * 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
fixed (ushort* ptr = _iconIds)
|
||||||
|
{
|
||||||
|
MemoryUtility.MemSet(ptr, 0, 12 * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
_secondaryMainhand = 0;
|
||||||
|
_secondaryOffhand = 0;
|
||||||
|
_typeMainhand = FullEquipType.Unknown;
|
||||||
|
_typeOffhand = FullEquipType.Unknown;
|
||||||
|
|
||||||
|
_nameHead = string.Empty;
|
||||||
|
_nameBody = string.Empty;
|
||||||
|
_nameHands = string.Empty;
|
||||||
|
_nameLegs = string.Empty;
|
||||||
|
_nameFeet = string.Empty;
|
||||||
|
_nameEars = string.Empty;
|
||||||
|
_nameNeck = string.Empty;
|
||||||
|
_nameWrists = string.Empty;
|
||||||
|
_nameRFinger = string.Empty;
|
||||||
|
_nameLFinger = string.Empty;
|
||||||
|
_nameMainhand = string.Empty;
|
||||||
|
_nameOffhand = string.Empty;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public readonly byte[] GetCustomizeBytes()
|
public readonly byte[] GetCustomizeBytes()
|
||||||
|
|
@ -234,6 +271,32 @@ public unsafe struct DesignData
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public nint GetEquipmentPtr()
|
||||||
|
{
|
||||||
|
fixed (byte* ptr = _equipmentBytes)
|
||||||
|
{
|
||||||
|
return (nint)ptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool SetEquipmentBytesFromBase64(string base64)
|
||||||
|
{
|
||||||
|
fixed (byte* dataPtr = _equipmentBytes)
|
||||||
|
{
|
||||||
|
var data = new Span<byte>(dataPtr, 40);
|
||||||
|
return Convert.TryFromBase64String(base64, data, out var written) && written == 40;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string WriteEquipmentBytesBase64()
|
||||||
|
{
|
||||||
|
fixed (byte* dataPtr = _equipmentBytes)
|
||||||
|
{
|
||||||
|
var data = new ReadOnlySpan<byte>(dataPtr, 40);
|
||||||
|
return Convert.ToBase64String(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
|
||||||
private static bool SetIfDifferent<T>(ref T old, T value) where T : IEquatable<T>
|
private static bool SetIfDifferent<T>(ref T old, T value) where T : IEquatable<T>
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,12 @@ public sealed class StateChanged : EventWrapper<Action<StateChanged.Type, StateC
|
||||||
{
|
{
|
||||||
public enum Type
|
public enum Type
|
||||||
{
|
{
|
||||||
|
/// <summary> A characters saved state had the model id changed. This means everything may have changed. Data is the old model id and the new model id. [(uint, uint)] </summary>
|
||||||
|
Model,
|
||||||
|
|
||||||
|
/// <summary> A characters saved state had multiple customization values changed. TData is the old customize array and the applied changes. [(Customize, CustomizeFlag)] </summary>
|
||||||
|
EntireCustomize,
|
||||||
|
|
||||||
/// <summary> A characters saved state had a customization value changed. Data is the old value, the new value and the type. [(CustomizeValue, CustomizeValue, CustomizeIndex)]. </summary>
|
/// <summary> A characters saved state had a customization value changed. Data is the old value, the new value and the type. [(CustomizeValue, CustomizeValue, CustomizeIndex)]. </summary>
|
||||||
Customize,
|
Customize,
|
||||||
|
|
||||||
|
|
@ -31,6 +37,12 @@ public sealed class StateChanged : EventWrapper<Action<StateChanged.Type, StateC
|
||||||
/// <summary> A characters saved state had a stain changed. Data is the old stain id, the new stain id and the slot [(StainId, StainId, EquipSlot)]. </summary>
|
/// <summary> A characters saved state had a stain changed. Data is the old stain id, the new stain id and the slot [(StainId, StainId, EquipSlot)]. </summary>
|
||||||
Stain,
|
Stain,
|
||||||
|
|
||||||
|
/// <summary> A characters saved state had a design applied. This means everything may have changed. Data is the applied design. [DesignBase] </summary>
|
||||||
|
Design,
|
||||||
|
|
||||||
|
/// <summary> A characters saved state had its state reset to its game values. This means everything may have changed. Data is null. </summary>
|
||||||
|
Reset,
|
||||||
|
|
||||||
/// <summary> A characters saved state had a meta toggle changed. Data is the old stain id, the new stain id and the slot [(StainId, StainId, EquipSlot)]. </summary>
|
/// <summary> A characters saved state had a meta toggle changed. Data is the old stain id, the new stain id and the slot [(StainId, StainId, EquipSlot)]. </summary>
|
||||||
Other,
|
Other,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,24 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using System.Xml.Linq;
|
|
||||||
using Dalamud.Interface;
|
using Dalamud.Interface;
|
||||||
|
using Dalamud.Interface.Internal.Notifications;
|
||||||
|
using Glamourer.Automation;
|
||||||
|
using Glamourer.Customization;
|
||||||
|
using Glamourer.Designs;
|
||||||
using Glamourer.Events;
|
using Glamourer.Events;
|
||||||
using Glamourer.Gui.Customization;
|
using Glamourer.Gui.Customization;
|
||||||
using Glamourer.Gui.Equipment;
|
using Glamourer.Gui.Equipment;
|
||||||
|
using Glamourer.Interop;
|
||||||
using Glamourer.Interop.Structs;
|
using Glamourer.Interop.Structs;
|
||||||
using Glamourer.Services;
|
using Glamourer.Services;
|
||||||
using Glamourer.State;
|
using Glamourer.State;
|
||||||
|
using Glamourer.Structs;
|
||||||
using ImGuiNET;
|
using ImGuiNET;
|
||||||
using Lumina.Excel.GeneratedSheets;
|
|
||||||
using OtterGui;
|
using OtterGui;
|
||||||
using OtterGui.Raii;
|
using OtterGui.Raii;
|
||||||
using Penumbra.GameData;
|
|
||||||
using Penumbra.GameData.Actors;
|
using Penumbra.GameData.Actors;
|
||||||
using Penumbra.GameData.Data;
|
using Penumbra.GameData.Data;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
using Penumbra.GameData.Structs;
|
|
||||||
|
|
||||||
namespace Glamourer.Gui.Tabs.ActorTab;
|
namespace Glamourer.Gui.Tabs.ActorTab;
|
||||||
|
|
||||||
|
|
@ -26,8 +28,11 @@ public class ActorPanel
|
||||||
private readonly StateManager _stateManager;
|
private readonly StateManager _stateManager;
|
||||||
private readonly CustomizationDrawer _customizationDrawer;
|
private readonly CustomizationDrawer _customizationDrawer;
|
||||||
private readonly EquipmentDrawer _equipmentDrawer;
|
private readonly EquipmentDrawer _equipmentDrawer;
|
||||||
private readonly HumanModelList _humans;
|
|
||||||
private readonly IdentifierService _identification;
|
private readonly IdentifierService _identification;
|
||||||
|
private readonly AutoDesignApplier _autoDesignApplier;
|
||||||
|
private readonly Configuration _config;
|
||||||
|
private readonly DesignConverter _converter;
|
||||||
|
private readonly ObjectManager _objects;
|
||||||
|
|
||||||
private ActorIdentifier _identifier;
|
private ActorIdentifier _identifier;
|
||||||
private string _actorName = string.Empty;
|
private string _actorName = string.Empty;
|
||||||
|
|
@ -36,14 +41,18 @@ public class ActorPanel
|
||||||
private ActorState? _state;
|
private ActorState? _state;
|
||||||
|
|
||||||
public ActorPanel(ActorSelector selector, StateManager stateManager, CustomizationDrawer customizationDrawer,
|
public ActorPanel(ActorSelector selector, StateManager stateManager, CustomizationDrawer customizationDrawer,
|
||||||
EquipmentDrawer equipmentDrawer, HumanModelList humans, IdentifierService identification)
|
EquipmentDrawer equipmentDrawer, IdentifierService identification, AutoDesignApplier autoDesignApplier,
|
||||||
|
Configuration config, DesignConverter converter, ObjectManager objects)
|
||||||
{
|
{
|
||||||
_selector = selector;
|
_selector = selector;
|
||||||
_stateManager = stateManager;
|
_stateManager = stateManager;
|
||||||
_customizationDrawer = customizationDrawer;
|
_customizationDrawer = customizationDrawer;
|
||||||
_equipmentDrawer = equipmentDrawer;
|
_equipmentDrawer = equipmentDrawer;
|
||||||
_humans = humans;
|
|
||||||
_identification = identification;
|
_identification = identification;
|
||||||
|
_autoDesignApplier = autoDesignApplier;
|
||||||
|
_config = config;
|
||||||
|
_converter = converter;
|
||||||
|
_objects = objects;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Draw()
|
public void Draw()
|
||||||
|
|
@ -91,6 +100,28 @@ public class ActorPanel
|
||||||
return (_selector.IncognitoMode ? _identifier.Incognito(null) : _identifier.ToString(), Actor.Null);
|
return (_selector.IncognitoMode ? _identifier.Incognito(null) : _identifier.ToString(), Actor.Null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private unsafe void DrawPanel()
|
||||||
|
{
|
||||||
|
using var child = ImRaii.Child("##Panel", -Vector2.One, true);
|
||||||
|
if (!child || !_selector.HasSelection || !_stateManager.GetOrCreate(_identifier, _actor, out _state))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ApplyClipboardButton();
|
||||||
|
ImGui.SameLine();
|
||||||
|
CopyToClipboardButton();
|
||||||
|
ImGui.SameLine();
|
||||||
|
DrawApplyToSelf();
|
||||||
|
ImGui.SameLine();
|
||||||
|
DrawApplyToTarget();
|
||||||
|
|
||||||
|
RevertButtons();
|
||||||
|
|
||||||
|
if (_state.ModelData.IsHuman)
|
||||||
|
DrawHumanPanel();
|
||||||
|
else
|
||||||
|
DrawMonsterPanel();
|
||||||
|
}
|
||||||
|
|
||||||
private void DrawHumanPanel()
|
private void DrawHumanPanel()
|
||||||
{
|
{
|
||||||
if (_customizationDrawer.Draw(_state!.ModelData.Customize, false))
|
if (_customizationDrawer.Draw(_state!.ModelData.Customize, false))
|
||||||
|
|
@ -191,113 +222,85 @@ public class ActorPanel
|
||||||
_stateManager.TurnHuman(_state, StateChanged.Source.Manual);
|
_stateManager.TurnHuman(_state, StateChanged.Source.Manual);
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsafe void DrawPanel()
|
private void ApplyClipboardButton()
|
||||||
{
|
{
|
||||||
using var child = ImRaii.Child("##Panel", -Vector2.One, true);
|
if (!ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Clipboard.ToIconString(), new Vector2(ImGui.GetFrameHeight()),
|
||||||
if (!child || !_selector.HasSelection || !_stateManager.GetOrCreate(_identifier, _actor, out _state))
|
"Try to apply a design from your clipboard.", false, true))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (_humans.IsHuman(_state.ModelData.ModelId))
|
try
|
||||||
DrawHumanPanel();
|
{
|
||||||
else
|
var text = ImGui.GetClipboardText();
|
||||||
DrawMonsterPanel();
|
var design = _converter.FromBase64(text, true, true) ?? throw new Exception("The clipboard did not contain valid data.");
|
||||||
|
_stateManager.ApplyDesign(design, _state!, StateChanged.Source.Manual);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Glamourer.Chat.NotificationMessage(ex, $"Could not apply clipboard to {_identifier}.",
|
||||||
|
$"Could not apply clipboard to design {_identifier.Incognito(null)}", "Failure", NotificationType.Error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void CopyToClipboardButton()
|
||||||
private unsafe void RevertButton()
|
|
||||||
{
|
{
|
||||||
//if (ImGui.Button("Revert"))
|
if (!ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Copy.ToIconString(), new Vector2(ImGui.GetFrameHeight()),
|
||||||
// _activeDesigns.RevertDesign(_currentSave!);
|
"Copy the current design to your clipboard.", false, true))
|
||||||
//foreach (var actor in _currentData.Objects)
|
return;
|
||||||
// _currentSave!.ApplyToActor(actor);
|
|
||||||
//
|
try
|
||||||
//if (_currentData.Objects.Count > 0)
|
{
|
||||||
// _currentSave = _manipulations.GetOrCreateSave(_currentData.Objects[0]);
|
var text = _converter.ShareBase64(_state!);
|
||||||
//
|
ImGui.SetClipboardText(text);
|
||||||
//_currentSave!.Reset();
|
}
|
||||||
//if (_currentData.Objects.Count > 0)
|
catch (Exception ex)
|
||||||
// ImGui.TextUnformatted(_currentData.Objects[0].Pointer->GameObject.DataID.ToString());
|
{
|
||||||
//VisorBox();
|
Glamourer.Chat.NotificationMessage(ex, $"Could not copy {_identifier} data to clipboard.",
|
||||||
|
$"Could not copy data from design {_identifier.Incognito(null)} to clipboard", "Failure", NotificationType.Error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//private unsafe void VisorBox()
|
private void RevertButtons()
|
||||||
//{
|
{
|
||||||
// var (flags, mask) = (_currentSave!.Data.Flags & (ApplicationFlags.SetVisor | ApplicationFlags.Visor)) switch
|
if (ImGui.Button("Revert to Game"))
|
||||||
// {
|
_stateManager.ResetState(_state!);
|
||||||
// ApplicationFlags.SetVisor => (0u, 3u),
|
|
||||||
// ApplicationFlags.Visor => (1u, 3u),
|
|
||||||
// ApplicationFlags.SetVisor | ApplicationFlags.Visor => (3u, 3u),
|
|
||||||
// _ => (2u, 3u),
|
|
||||||
// };
|
|
||||||
// var tmp = flags;
|
|
||||||
// if (ImGui.CheckboxFlags("Visor Toggled", ref tmp, mask))
|
|
||||||
// {
|
|
||||||
// _currentSave.Data.Flags = flags switch
|
|
||||||
// {
|
|
||||||
// 0 => (_currentSave.Data.Flags | ApplicationFlags.Visor) & ~ApplicationFlags.SetVisor,
|
|
||||||
// 1 => _currentSave.Data.Flags | ApplicationFlags.SetVisor,
|
|
||||||
// 2 => _currentSave.Data.Flags | ApplicationFlags.SetVisor,
|
|
||||||
// _ => _currentSave.Data.Flags & ~(ApplicationFlags.SetVisor | ApplicationFlags.Visor),
|
|
||||||
// };
|
|
||||||
// if (_currentSave.Data.Flags.HasFlag(ApplicationFlags.SetVisor))
|
|
||||||
// {
|
|
||||||
// var on = _currentSave.Data.Flags.HasFlag(ApplicationFlags.Visor);
|
|
||||||
// foreach (var actor in _currentData.Objects.Where(a => a.IsHuman && a.DrawObject))
|
|
||||||
// RedrawManager.SetVisor(actor.DrawObject.Pointer, on);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
|
ImGui.SameLine();
|
||||||
|
if (ImGui.Button("Reapply State"))
|
||||||
|
_stateManager.ReapplyState(_actor);
|
||||||
|
|
||||||
//private void DrawActorPanel()
|
ImGui.SameLine();
|
||||||
//{
|
if (ImGuiUtil.DrawDisabledButton("Reapply Automation", Vector2.Zero, string.Empty, !_config.EnableAutoDesigns))
|
||||||
// using var group = ImRaii.Group();
|
{
|
||||||
// if (!_data.Identifier.IsValid)
|
_autoDesignApplier.ReapplyAutomation(_actor, _identifier, _state!);
|
||||||
// return;
|
_stateManager.ReapplyState(_actor);
|
||||||
//
|
}
|
||||||
// if (DrawCustomization(_currentSave.Customize, _currentSave.Equipment, !_data.Modifiable))
|
}
|
||||||
// //Glamourer.RedrawManager.Set(_data.Actor.Address, _character);
|
|
||||||
// Glamourer.Penumbra.RedrawObject(_data.Actor.Character, RedrawType.Redraw, true);
|
private void DrawApplyToSelf()
|
||||||
//
|
{
|
||||||
// if (ImGui.Button("Set Machinist Goggles"))
|
var (id, data) = _objects.PlayerData;
|
||||||
// Glamourer.RedrawManager.ChangeEquip(_data.Actor, EquipSlot.Head, new CharacterArmor(265, 1, 0));
|
if (!ImGuiUtil.DrawDisabledButton("Apply to Yourself", Vector2.Zero, "Apply the current state to your own character.",
|
||||||
//
|
!data.Valid || id == _identifier))
|
||||||
// if (ImGui.Button("Set Weapon"))
|
return;
|
||||||
// Glamourer.RedrawManager.LoadWeapon(_data.Actor.Address, new CharacterWeapon(0x00C9, 0x004E, 0x0001, 0x00),
|
|
||||||
// new CharacterWeapon(0x0065, 0x003D, 0x0001, 0x00));
|
if (_stateManager.GetOrCreate(id, data.Objects[0], out var state))
|
||||||
//
|
_stateManager.ApplyDesign(_converter.Convert(_state!, EquipFlagExtensions.All, CustomizeFlagExtensions.AllRelevant), state,
|
||||||
// if (ImGui.Button("Set Customize"))
|
StateChanged.Source.Manual);
|
||||||
// {
|
}
|
||||||
// unsafe
|
|
||||||
// {
|
private void DrawApplyToTarget()
|
||||||
// var data = _data.Actor.Customize.Data->Clone();
|
{
|
||||||
// Glamourer.RedrawManager.UpdateCustomize(_data.Actor.DrawObject, new Customize(&data)
|
var (id, data) = _objects.TargetData;
|
||||||
// {
|
var tt = id.IsValid
|
||||||
// SkinColor = 154,
|
? data.Valid
|
||||||
// });
|
? "Apply the current state to your current target."
|
||||||
// }
|
: "The current target can not be manipulated."
|
||||||
// }
|
: "No valid target selected.";
|
||||||
//}
|
if (!ImGuiUtil.DrawDisabledButton("Apply to Target", Vector2.Zero, tt, !data.Valid || id == _identifier))
|
||||||
//
|
return;
|
||||||
//private void DrawMonsterPanel()
|
|
||||||
//{
|
if (_stateManager.GetOrCreate(id, data.Objects[0], out var state))
|
||||||
// using var group = ImRaii.Group();
|
_stateManager.ApplyDesign(_converter.Convert(_state!, EquipFlagExtensions.All, CustomizeFlagExtensions.AllRelevant), state,
|
||||||
// var currentModel = (uint)_data.Actor.ModelId;
|
StateChanged.Source.Manual);
|
||||||
// var models = GameData.Models(Dalamud.GameData);
|
}
|
||||||
// var currentData = models.Models.TryGetValue(currentModel, out var c) ? c.FirstName : $"#{currentModel}";
|
|
||||||
// using var combo = ImRaii.Combo("Model Id", currentData);
|
|
||||||
// if (!combo)
|
|
||||||
// return;
|
|
||||||
//
|
|
||||||
// foreach (var (id, data) in models.Models)
|
|
||||||
// {
|
|
||||||
// if (ImGui.Selectable(data.FirstName, id == currentModel) && id != currentModel)
|
|
||||||
// {
|
|
||||||
// _data.Actor.SetModelId((int)id);
|
|
||||||
// Glamourer.Penumbra.RedrawObject(_data.Actor.Character, RedrawType.Redraw, true);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// ImGuiUtil.HoverTooltip(data.AllNames);
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -253,7 +253,7 @@ public unsafe class DebugTab : ITab
|
||||||
using var id = ImRaii.PushId("Visor");
|
using var id = ImRaii.PushId("Visor");
|
||||||
ImGuiUtil.DrawTableColumn("Visor State");
|
ImGuiUtil.DrawTableColumn("Visor State");
|
||||||
ImGuiUtil.DrawTableColumn(actor.IsCharacter ? actor.AsCharacter->DrawData.IsVisorToggled.ToString() : "No Character");
|
ImGuiUtil.DrawTableColumn(actor.IsCharacter ? actor.AsCharacter->DrawData.IsVisorToggled.ToString() : "No Character");
|
||||||
ImGuiUtil.DrawTableColumn(model.IsHuman ? _visorService.GetVisorState(model).ToString() : "No Human");
|
ImGuiUtil.DrawTableColumn(model.IsHuman ? VisorService.GetVisorState(model).ToString() : "No Human");
|
||||||
ImGui.TableNextColumn();
|
ImGui.TableNextColumn();
|
||||||
if (!model.IsHuman)
|
if (!model.IsHuman)
|
||||||
return;
|
return;
|
||||||
|
|
@ -265,7 +265,7 @@ public unsafe class DebugTab : ITab
|
||||||
_visorService.SetVisorState(model, false);
|
_visorService.SetVisorState(model, false);
|
||||||
ImGui.SameLine();
|
ImGui.SameLine();
|
||||||
if (ImGui.SmallButton("Toggle"))
|
if (ImGui.SmallButton("Toggle"))
|
||||||
_visorService.SetVisorState(model, !_visorService.GetVisorState(model));
|
_visorService.SetVisorState(model, !VisorService.GetVisorState(model));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawHatState(Actor actor, Model model)
|
private void DrawHatState(Actor actor, Model model)
|
||||||
|
|
@ -1043,7 +1043,7 @@ public unsafe class DebugTab : ITab
|
||||||
PrintRow("Wetness", state.BaseData.IsWet(), state.ModelData.IsWet(), state[ActorState.MetaIndex.Wetness]);
|
PrintRow("Wetness", state.BaseData.IsWet(), state.ModelData.IsWet(), state[ActorState.MetaIndex.Wetness]);
|
||||||
ImGui.TableNextRow();
|
ImGui.TableNextRow();
|
||||||
|
|
||||||
if (state.BaseData.ModelId == 0 && state.ModelData.ModelId == 0)
|
if (state.BaseData.IsHuman && state.ModelData.IsHuman)
|
||||||
{
|
{
|
||||||
PrintRow("Hat Visible", state.BaseData.IsHatVisible(), state.ModelData.IsHatVisible(), state[ActorState.MetaIndex.HatState]);
|
PrintRow("Hat Visible", state.BaseData.IsHatVisible(), state.ModelData.IsHatVisible(), state[ActorState.MetaIndex.HatState]);
|
||||||
ImGui.TableNextRow();
|
ImGui.TableNextRow();
|
||||||
|
|
@ -1079,7 +1079,7 @@ public unsafe class DebugTab : ITab
|
||||||
|
|
||||||
public static void DrawDesignData(in DesignData data)
|
public static void DrawDesignData(in DesignData data)
|
||||||
{
|
{
|
||||||
if (data.ModelId == 0)
|
if (data.IsHuman)
|
||||||
{
|
{
|
||||||
using var table = ImRaii.Table("##equip", 4, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit);
|
using var table = ImRaii.Table("##equip", 4, ImGuiTableFlags.RowBg | ImGuiTableFlags.SizingFixedFit);
|
||||||
if (!table)
|
if (!table)
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ using Dalamud.Interface.Internal.Notifications;
|
||||||
using Glamourer.Automation;
|
using Glamourer.Automation;
|
||||||
using Glamourer.Customization;
|
using Glamourer.Customization;
|
||||||
using Glamourer.Designs;
|
using Glamourer.Designs;
|
||||||
|
using Glamourer.Events;
|
||||||
using Glamourer.Gui.Customization;
|
using Glamourer.Gui.Customization;
|
||||||
using Glamourer.Gui.Equipment;
|
using Glamourer.Gui.Equipment;
|
||||||
using Glamourer.Interop;
|
using Glamourer.Interop;
|
||||||
|
|
@ -176,7 +177,7 @@ public class DesignPanel
|
||||||
{
|
{
|
||||||
var set = _customizationService.AwaitedService.GetList(_selector.Selected!.DesignData.Customize.Clan,
|
var set = _customizationService.AwaitedService.GetList(_selector.Selected!.DesignData.Customize.Clan,
|
||||||
_selector.Selected!.DesignData.Customize.Gender);
|
_selector.Selected!.DesignData.Customize.Gender);
|
||||||
var all = CustomizationExtensions.All.Where(set.IsAvailable).Select(c => c.ToFlag()).Aggregate((a, b) => a | b);
|
var all = CustomizationExtensions.All.Where(set.IsAvailable).Select(c => c.ToFlag()).Aggregate((a, b) => a | b) | CustomizeFlag.Clan | CustomizeFlag.Gender;
|
||||||
var flags = (_selector.Selected!.ApplyCustomize & all) == 0 ? 0 : (_selector.Selected!.ApplyCustomize & all) == all ? 3 : 1;
|
var flags = (_selector.Selected!.ApplyCustomize & all) == 0 ? 0 : (_selector.Selected!.ApplyCustomize & all) == all ? 3 : 1;
|
||||||
if (ImGui.CheckboxFlags("Apply All Customizations", ref flags, 3))
|
if (ImGui.CheckboxFlags("Apply All Customizations", ref flags, 3))
|
||||||
{
|
{
|
||||||
|
|
@ -342,7 +343,7 @@ public class DesignPanel
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (_state.GetOrCreate(id, data.Objects[0], out var state))
|
if (_state.GetOrCreate(id, data.Objects[0], out var state))
|
||||||
_state.ApplyDesign(_selector.Selected!, state);
|
_state.ApplyDesign(_selector.Selected!, state, StateChanged.Source.Manual);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawApplyToTarget()
|
private void DrawApplyToTarget()
|
||||||
|
|
@ -357,6 +358,6 @@ public class DesignPanel
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (_state.GetOrCreate(id, data.Objects[0], out var state))
|
if (_state.GetOrCreate(id, data.Objects[0], out var state))
|
||||||
_state.ApplyDesign(_selector.Selected!, state);
|
_state.ApplyDesign(_selector.Selected!, state, StateChanged.Source.Manual);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ public class VisorService : IDisposable
|
||||||
=> _setupVisorHook.Dispose();
|
=> _setupVisorHook.Dispose();
|
||||||
|
|
||||||
/// <summary> Obtain the current state of the Visor for the given draw object (true: toggled). </summary>
|
/// <summary> Obtain the current state of the Visor for the given draw object (true: toggled). </summary>
|
||||||
public unsafe bool GetVisorState(Model characterBase)
|
public static unsafe bool GetVisorState(Model characterBase)
|
||||||
=> characterBase.IsCharacterBase && characterBase.AsCharacterBase->VisorToggled;
|
=> characterBase.IsCharacterBase && characterBase.AsCharacterBase->VisorToggled;
|
||||||
|
|
||||||
/// <summary> Manually set the state of the Visor for the given draw object. </summary>
|
/// <summary> Manually set the state of the Visor for the given draw object. </summary>
|
||||||
|
|
|
||||||
|
|
@ -5,31 +5,36 @@ using System.Runtime.CompilerServices;
|
||||||
using Dalamud.Data;
|
using Dalamud.Data;
|
||||||
using Dalamud.Plugin;
|
using Dalamud.Plugin;
|
||||||
using Glamourer.Customization;
|
using Glamourer.Customization;
|
||||||
|
using Penumbra.GameData.Data;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
|
|
||||||
namespace Glamourer.Services;
|
namespace Glamourer.Services;
|
||||||
|
|
||||||
public sealed class CustomizationService : AsyncServiceWrapper<ICustomizationManager>
|
public sealed class CustomizationService : AsyncServiceWrapper<ICustomizationManager>
|
||||||
{
|
{
|
||||||
public CustomizationService(DalamudPluginInterface pi, DataManager gameData)
|
public readonly HumanModelList HumanModels;
|
||||||
: base(nameof(CustomizationService), () => CustomizationManager.Create(pi, gameData))
|
|
||||||
{ }
|
|
||||||
|
|
||||||
public (Customize NewValue, CustomizeFlag Applied) Combine(Customize oldValues, Customize newValues, CustomizeFlag applyWhich)
|
public CustomizationService(DalamudPluginInterface pi, DataManager gameData, HumanModelList humanModels)
|
||||||
|
: base(nameof(CustomizationService), () => CustomizationManager.Create(pi, gameData))
|
||||||
|
=> HumanModels = humanModels;
|
||||||
|
|
||||||
|
public (Customize NewValue, CustomizeFlag Applied, CustomizeFlag Changed) Combine(Customize oldValues, Customize newValues,
|
||||||
|
CustomizeFlag applyWhich)
|
||||||
{
|
{
|
||||||
CustomizeFlag applied = 0;
|
CustomizeFlag applied = 0;
|
||||||
|
CustomizeFlag changed = 0;
|
||||||
Customize ret = default;
|
Customize ret = default;
|
||||||
ret.Load(oldValues);
|
ret.Load(oldValues);
|
||||||
if (applyWhich.HasFlag(CustomizeFlag.Clan))
|
if (applyWhich.HasFlag(CustomizeFlag.Clan))
|
||||||
{
|
{
|
||||||
ChangeClan(ref ret, newValues.Clan);
|
changed |= ChangeClan(ref ret, newValues.Clan);
|
||||||
applied |= CustomizeFlag.Clan;
|
applied |= CustomizeFlag.Clan;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (applyWhich.HasFlag(CustomizeFlag.Gender))
|
if (applyWhich.HasFlag(CustomizeFlag.Gender))
|
||||||
if (ret.Race is not Race.Hrothgar || newValues.Gender is not Gender.Female)
|
if (ret.Race is not Race.Hrothgar || newValues.Gender is not Gender.Female)
|
||||||
{
|
{
|
||||||
ChangeGender(ref ret, newValues.Gender);
|
changed |= ChangeGender(ref ret, newValues.Gender);
|
||||||
applied |= CustomizeFlag.Gender;
|
applied |= CustomizeFlag.Gender;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -43,12 +48,14 @@ public sealed class CustomizationService : AsyncServiceWrapper<ICustomizationMan
|
||||||
var value = newValues[index];
|
var value = newValues[index];
|
||||||
if (IsCustomizationValid(set, ret.Face, index, value))
|
if (IsCustomizationValid(set, ret.Face, index, value))
|
||||||
{
|
{
|
||||||
|
if (ret[index].Value != value.Value)
|
||||||
|
changed |= flag;
|
||||||
ret[index] = value;
|
ret[index] = value;
|
||||||
applied |= flag;
|
applied |= flag;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (ret, applied);
|
return (ret, applied, changed);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> In languages other than english the actual clan name may depend on gender. </summary>
|
/// <summary> In languages other than english the actual clan name may depend on gender. </summary>
|
||||||
|
|
@ -190,10 +197,18 @@ public sealed class CustomizationService : AsyncServiceWrapper<ICustomizationMan
|
||||||
/// The returned model id is 0.
|
/// The returned model id is 0.
|
||||||
/// The return value is an empty string if everything was correct and a warning otherwise.
|
/// The return value is an empty string if everything was correct and a warning otherwise.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string ValidateModelId(uint modelId, out uint actualModelId)
|
public string ValidateModelId(uint modelId, out uint actualModelId, out bool isHuman)
|
||||||
{
|
{
|
||||||
actualModelId = 0;
|
if (modelId >= HumanModels.Count)
|
||||||
return modelId != 0 ? $"Model IDs different from 0 are not currently allowed, reset {modelId} to 0." : string.Empty;
|
{
|
||||||
|
actualModelId = 0;
|
||||||
|
isHuman = true;
|
||||||
|
return $"Model ID {modelId} is not an existing model, reset to 0.";
|
||||||
|
}
|
||||||
|
|
||||||
|
actualModelId = modelId;
|
||||||
|
isHuman = HumanModels.IsHuman(modelId);
|
||||||
|
return string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,7 @@ public static class ServiceManager
|
||||||
|
|
||||||
private static IServiceCollection AddState(this IServiceCollection services)
|
private static IServiceCollection AddState(this IServiceCollection services)
|
||||||
=> services.AddSingleton<StateManager>()
|
=> services.AddSingleton<StateManager>()
|
||||||
|
.AddSingleton<StateApplier>()
|
||||||
.AddSingleton<StateEditor>()
|
.AddSingleton<StateEditor>()
|
||||||
.AddSingleton<StateListener>()
|
.AddSingleton<StateListener>()
|
||||||
.AddSingleton<FunModule>();
|
.AddSingleton<FunModule>();
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
using System;
|
using Glamourer.Customization;
|
||||||
using Glamourer.Customization;
|
|
||||||
using Glamourer.Designs;
|
using Glamourer.Designs;
|
||||||
using Glamourer.Events;
|
using Glamourer.Events;
|
||||||
using Glamourer.Structs;
|
using Glamourer.Structs;
|
||||||
|
|
@ -32,6 +31,37 @@ public class ActorState
|
||||||
/// <summary> The last seen job. </summary>
|
/// <summary> The last seen job. </summary>
|
||||||
public byte LastJob;
|
public byte LastJob;
|
||||||
|
|
||||||
|
/// <summary> The Lock-Key locking this state. </summary>
|
||||||
|
public uint Combination;
|
||||||
|
|
||||||
|
/// <summary> Whether the State is locked at all. </summary>
|
||||||
|
public bool IsLocked
|
||||||
|
=> Combination != 0;
|
||||||
|
|
||||||
|
/// <summary> Whether the given key can open the lock. </summary>
|
||||||
|
public bool CanUnlock(uint key)
|
||||||
|
=> !IsLocked || Combination == key;
|
||||||
|
|
||||||
|
/// <summary> Lock the current state for further manipulations. </summary>
|
||||||
|
public bool Lock(uint combination)
|
||||||
|
{
|
||||||
|
if (combination == 0)
|
||||||
|
return false;
|
||||||
|
if (Combination != 0)
|
||||||
|
return Combination == combination;
|
||||||
|
|
||||||
|
Combination = combination;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Unlock the current state. </summary>
|
||||||
|
public bool Unlock(uint key)
|
||||||
|
{
|
||||||
|
if (key == Combination)
|
||||||
|
Combination = 0;
|
||||||
|
return !IsLocked;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary> This contains whether a change to the base data was made by the game, the user via manual input or through automatic application. </summary>
|
/// <summary> This contains whether a change to the base data was made by the game, the user via manual input or through automatic application. </summary>
|
||||||
private readonly StateChanged.Source[] _sources = Enumerable
|
private readonly StateChanged.Source[] _sources = Enumerable
|
||||||
.Repeat(StateChanged.Source.Game, EquipFlagExtensions.NumEquipFlags + CustomizationExtensions.NumIndices + 5).ToArray();
|
.Repeat(StateChanged.Source.Game, EquipFlagExtensions.NumEquipFlags + CustomizationExtensions.NumIndices + 5).ToArray();
|
||||||
|
|
|
||||||
275
Glamourer/State/StateApplier.cs
Normal file
275
Glamourer/State/StateApplier.cs
Normal file
|
|
@ -0,0 +1,275 @@
|
||||||
|
using System.Linq;
|
||||||
|
using Glamourer.Customization;
|
||||||
|
using Glamourer.Interop;
|
||||||
|
using Glamourer.Interop.Penumbra;
|
||||||
|
using Glamourer.Interop.Structs;
|
||||||
|
using Glamourer.Services;
|
||||||
|
using Penumbra.Api.Enums;
|
||||||
|
using Penumbra.GameData.Enums;
|
||||||
|
using Penumbra.GameData.Structs;
|
||||||
|
|
||||||
|
namespace Glamourer.State;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This class applies changes made to state to actual objects in the game.
|
||||||
|
/// It handles applying those changes as well as redrawing the actor if necessary.
|
||||||
|
/// </summary>
|
||||||
|
public class StateApplier
|
||||||
|
{
|
||||||
|
private readonly PenumbraService _penumbra;
|
||||||
|
private readonly UpdateSlotService _updateSlot;
|
||||||
|
private readonly VisorService _visor;
|
||||||
|
private readonly WeaponService _weapon;
|
||||||
|
private readonly MetaService _metaService;
|
||||||
|
private readonly ChangeCustomizeService _changeCustomize;
|
||||||
|
private readonly ItemManager _items;
|
||||||
|
private readonly ObjectManager _objects;
|
||||||
|
|
||||||
|
public StateApplier(UpdateSlotService updateSlot, VisorService visor, WeaponService weapon, ChangeCustomizeService changeCustomize,
|
||||||
|
ItemManager items, PenumbraService penumbra, MetaService metaService, ObjectManager objects)
|
||||||
|
{
|
||||||
|
_updateSlot = updateSlot;
|
||||||
|
_visor = visor;
|
||||||
|
_weapon = weapon;
|
||||||
|
_changeCustomize = changeCustomize;
|
||||||
|
_items = items;
|
||||||
|
_penumbra = penumbra;
|
||||||
|
_metaService = metaService;
|
||||||
|
_objects = objects;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Simply force a redraw regardless of conditions. </summary>
|
||||||
|
public void ForceRedraw(ActorData data)
|
||||||
|
{
|
||||||
|
foreach (var actor in data.Objects)
|
||||||
|
_penumbra.RedrawObject(actor, RedrawType.Redraw);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="ForceRedraw(ActorData)"/>
|
||||||
|
public ActorData ForceRedraw(ActorState state, bool apply)
|
||||||
|
{
|
||||||
|
var data = GetData(state);
|
||||||
|
if (apply)
|
||||||
|
ForceRedraw(data);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Change the customization values of actors either by applying them via update or redrawing,
|
||||||
|
/// this depends on whether the changes include changes to Race, Gender, Body Type or Face.
|
||||||
|
/// </summary>
|
||||||
|
public void ChangeCustomize(ActorData data, in Customize customize)
|
||||||
|
{
|
||||||
|
foreach (var actor in data.Objects)
|
||||||
|
{
|
||||||
|
var mdl = actor.Model;
|
||||||
|
if (!mdl.IsCharacterBase)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var flags = Customize.Compare(mdl.GetCustomize(), customize);
|
||||||
|
if (!flags.RequiresRedraw() || !mdl.IsHuman)
|
||||||
|
_changeCustomize.UpdateCustomize(mdl, customize.Data);
|
||||||
|
else
|
||||||
|
_penumbra.RedrawObject(actor, RedrawType.Redraw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="ChangeCustomize(ActorData, in Customize)"/>
|
||||||
|
public ActorData ChangeCustomize(ActorState state, bool apply)
|
||||||
|
{
|
||||||
|
var data = GetData(state);
|
||||||
|
if (apply)
|
||||||
|
ChangeCustomize(data, state.ModelData.Customize);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Change a single piece of armor and/or stain depending on slot.
|
||||||
|
/// This uses the current customization of the model to potentially prevent restricted gear types from appearing.
|
||||||
|
/// This never requires redrawing.
|
||||||
|
/// </summary>
|
||||||
|
public void ChangeArmor(ActorData data, EquipSlot slot, CharacterArmor armor, bool isHatVisible = true)
|
||||||
|
{
|
||||||
|
if (slot is EquipSlot.Head && !isHatVisible)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var actor in data.Objects.Where(a => a.IsCharacter))
|
||||||
|
{
|
||||||
|
var mdl = actor.Model;
|
||||||
|
if (!mdl.IsHuman)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var customize = mdl.GetCustomize();
|
||||||
|
var (_, resolvedItem) = _items.ResolveRestrictedGear(armor, slot, customize.Race, customize.Gender);
|
||||||
|
_updateSlot.UpdateSlot(actor.Model, slot, resolvedItem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="ChangeArmor(ActorData,EquipSlot,CharacterArmor,bool)"/>
|
||||||
|
public ActorData ChangeArmor(ActorState state, EquipSlot slot, bool apply)
|
||||||
|
{
|
||||||
|
var data = GetData(state);
|
||||||
|
if (apply)
|
||||||
|
ChangeArmor(data, slot, state.ModelData.Armor(slot), state.ModelData.IsHatVisible());
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Change the stain of a single piece of armor or weapon.
|
||||||
|
/// If the offhand is empty, the stain will be fixed to 0 to prevent crashes.
|
||||||
|
/// </summary>
|
||||||
|
public void ChangeStain(ActorData data, EquipSlot slot, StainId stain)
|
||||||
|
{
|
||||||
|
var idx = slot.ToIndex();
|
||||||
|
switch (idx)
|
||||||
|
{
|
||||||
|
case < 10:
|
||||||
|
foreach (var actor in data.Objects.Where(a => a.Model.IsHuman))
|
||||||
|
_updateSlot.UpdateStain(actor.Model, slot, stain);
|
||||||
|
break;
|
||||||
|
case 10:
|
||||||
|
foreach (var actor in data.Objects.Where(a => a.Model.IsHuman))
|
||||||
|
_weapon.LoadStain(actor, EquipSlot.MainHand, stain);
|
||||||
|
break;
|
||||||
|
case 11:
|
||||||
|
foreach (var actor in data.Objects.Where(a => a.Model.IsHuman))
|
||||||
|
_weapon.LoadStain(actor, EquipSlot.OffHand, stain);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="ChangeStain(ActorData,EquipSlot,StainId)"/>
|
||||||
|
public ActorData ChangeStain(ActorState state, EquipSlot slot, bool apply)
|
||||||
|
{
|
||||||
|
var data = GetData(state);
|
||||||
|
if (apply)
|
||||||
|
ChangeStain(data, slot, state.ModelData.Stain(slot));
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary> Apply a weapon to the appropriate slot. </summary>
|
||||||
|
public void ChangeWeapon(ActorData data, EquipSlot slot, EquipItem item, StainId stain)
|
||||||
|
{
|
||||||
|
if (slot is EquipSlot.MainHand)
|
||||||
|
ChangeMainhand(data, item, stain);
|
||||||
|
else
|
||||||
|
ChangeOffhand(data, item, stain);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="ChangeWeapon(ActorData,EquipSlot,EquipItem,StainId)"/>
|
||||||
|
public ActorData ChangeWeapon(ActorState state, EquipSlot slot, bool apply)
|
||||||
|
{
|
||||||
|
var data = GetData(state);
|
||||||
|
if (apply)
|
||||||
|
ChangeWeapon(data, slot, state.ModelData.Item(slot), state.ModelData.Stain(slot));
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Apply a weapon to the mainhand. If the weapon type has no associated offhand type, apply both.
|
||||||
|
/// </summary>
|
||||||
|
public void ChangeMainhand(ActorData data, EquipItem weapon, StainId stain)
|
||||||
|
{
|
||||||
|
var slot = weapon.Type.Offhand() == FullEquipType.Unknown ? EquipSlot.BothHand : EquipSlot.MainHand;
|
||||||
|
foreach (var actor in data.Objects.Where(a => a.Model.IsHuman))
|
||||||
|
_weapon.LoadWeapon(actor, slot, weapon.Weapon().With(stain));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="ChangeMainhand(ActorData,EquipItem,StainId)"/>
|
||||||
|
public ActorData ChangeMainhand(ActorState state, bool apply)
|
||||||
|
{
|
||||||
|
var data = GetData(state);
|
||||||
|
if (apply)
|
||||||
|
ChangeMainhand(data, state.ModelData.Item(EquipSlot.MainHand), state.ModelData.Stain(EquipSlot.MainHand));
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Apply a weapon to the offhand. </summary>
|
||||||
|
public void ChangeOffhand(ActorData data, EquipItem weapon, StainId stain)
|
||||||
|
{
|
||||||
|
stain = weapon.ModelId.Value == 0 ? 0 : stain;
|
||||||
|
foreach (var actor in data.Objects.Where(a => a.Model.IsHuman))
|
||||||
|
_weapon.LoadWeapon(actor, EquipSlot.OffHand, weapon.Weapon().With(stain));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="ChangeOffhand(ActorData,EquipItem,StainId)"/>
|
||||||
|
public ActorData ChangeOffhand(ActorState state, bool apply)
|
||||||
|
{
|
||||||
|
var data = GetData(state);
|
||||||
|
if (apply)
|
||||||
|
ChangeOffhand(data, state.ModelData.Item(EquipSlot.OffHand), state.ModelData.Stain(EquipSlot.OffHand));
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Change the visor state of actors only on the draw object. </summary>
|
||||||
|
public void ChangeVisor(ActorData data, bool value)
|
||||||
|
{
|
||||||
|
foreach (var actor in data.Objects.Where(a => a.Model.IsHuman))
|
||||||
|
_visor.SetVisorState(actor.Model, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="ChangeVisor(ActorData, bool)"/>
|
||||||
|
public ActorData ChangeVisor(ActorState state, bool apply)
|
||||||
|
{
|
||||||
|
var data = GetData(state);
|
||||||
|
if (apply)
|
||||||
|
ChangeVisor(data, state.ModelData.IsVisorToggled());
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Change the forced wetness state on actors. </summary>
|
||||||
|
public unsafe void ChangeWetness(ActorData data, bool value)
|
||||||
|
{
|
||||||
|
foreach (var actor in data.Objects.Where(a => a.IsCharacter))
|
||||||
|
actor.AsCharacter->IsGPoseWet = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="ChangeWetness(ActorData, bool)"/>
|
||||||
|
public ActorData ChangeWetness(ActorState state, bool apply)
|
||||||
|
{
|
||||||
|
var data = GetData(state);
|
||||||
|
if (apply)
|
||||||
|
ChangeWetness(data, state.ModelData.IsWet());
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Change the hat-visibility state on actors. </summary>
|
||||||
|
public void ChangeHatState(ActorData data, bool value)
|
||||||
|
{
|
||||||
|
foreach (var actor in data.Objects.Where(a => a.IsCharacter))
|
||||||
|
_metaService.SetHatState(actor, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="ChangeHatState(ActorData, bool)"/>
|
||||||
|
public ActorData ChangeHatState(ActorState state, bool apply)
|
||||||
|
{
|
||||||
|
var data = GetData(state);
|
||||||
|
if (apply)
|
||||||
|
ChangeHatState(data, state.ModelData.IsHatVisible());
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Change the weapon-visibility state on actors. </summary>
|
||||||
|
public void ChangeWeaponState(ActorData data, bool value)
|
||||||
|
{
|
||||||
|
foreach (var actor in data.Objects.Where(a => a.IsCharacter))
|
||||||
|
_metaService.SetWeaponState(actor, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc cref="ChangeWeaponState(ActorData, bool)"/>
|
||||||
|
public ActorData ChangeWeaponState(ActorState state, bool apply)
|
||||||
|
{
|
||||||
|
var data = GetData(state);
|
||||||
|
if (apply)
|
||||||
|
ChangeWeaponState(data, state.ModelData.IsWeaponVisible());
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private ActorData GetData(ActorState state)
|
||||||
|
{
|
||||||
|
_objects.Update();
|
||||||
|
return _objects.TryGetValue(state.Identifier, out var data) ? data : ActorData.Invalid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,166 +1,174 @@
|
||||||
using System.Linq;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using Glamourer.Customization;
|
using Glamourer.Customization;
|
||||||
using Glamourer.Interop;
|
using Glamourer.Events;
|
||||||
using Glamourer.Interop.Penumbra;
|
|
||||||
using Glamourer.Interop.Structs;
|
|
||||||
using Glamourer.Services;
|
using Glamourer.Services;
|
||||||
using Penumbra.Api.Enums;
|
using Penumbra.GameData.Data;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
using Penumbra.GameData.Structs;
|
using Penumbra.GameData.Structs;
|
||||||
|
|
||||||
namespace Glamourer.State;
|
namespace Glamourer.State;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// This class applies changes made to state to actual objects in the game.
|
|
||||||
/// It handles applying those changes as well as redrawing the actor if necessary.
|
|
||||||
/// </summary>
|
|
||||||
public class StateEditor
|
public class StateEditor
|
||||||
{
|
{
|
||||||
private readonly PenumbraService _penumbra;
|
private readonly ItemManager _items;
|
||||||
private readonly UpdateSlotService _updateSlot;
|
private readonly CustomizationService _customizations;
|
||||||
private readonly VisorService _visor;
|
private readonly HumanModelList _humans;
|
||||||
private readonly WeaponService _weapon;
|
|
||||||
private readonly MetaService _metaService;
|
|
||||||
private readonly ChangeCustomizeService _changeCustomize;
|
|
||||||
private readonly ItemManager _items;
|
|
||||||
|
|
||||||
public StateEditor(UpdateSlotService updateSlot, VisorService visor, WeaponService weapon, ChangeCustomizeService changeCustomize,
|
public StateEditor(CustomizationService customizations, HumanModelList humans, ItemManager items)
|
||||||
ItemManager items, PenumbraService penumbra, MetaService metaService)
|
|
||||||
{
|
{
|
||||||
_updateSlot = updateSlot;
|
_customizations = customizations;
|
||||||
_visor = visor;
|
_humans = humans;
|
||||||
_weapon = weapon;
|
_items = items;
|
||||||
_changeCustomize = changeCustomize;
|
|
||||||
_items = items;
|
|
||||||
_penumbra = penumbra;
|
|
||||||
_metaService = metaService;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Changing the model ID simply requires guaranteed redrawing. </summary>
|
/// <summary> Change the model id. If the actor is changed from a human to another human, customize and equipData are unused. </summary>
|
||||||
public void ChangeModelId(ActorData data, uint modelId)
|
public bool ChangeModelId(ActorState state, uint modelId, in Customize customize, nint equipData, StateChanged.Source source,
|
||||||
|
out uint oldModelId, uint key = 0)
|
||||||
{
|
{
|
||||||
foreach (var actor in data.Objects)
|
oldModelId = state.ModelData.ModelId;
|
||||||
_penumbra.RedrawObject(actor, RedrawType.Redraw);
|
if (!state.CanUnlock(key))
|
||||||
}
|
return false;
|
||||||
|
|
||||||
/// <summary>
|
var oldIsHuman = state.ModelData.IsHuman;
|
||||||
/// Change the customization values of actors either by applying them via update or redrawing,
|
state.ModelData.IsHuman = _humans.IsHuman(modelId);
|
||||||
/// this depends on whether the changes include changes to Race, Gender, Body Type or Face.
|
if (state.ModelData.IsHuman)
|
||||||
/// </summary>
|
|
||||||
public void ChangeCustomize(ActorData data, Customize customize)
|
|
||||||
{
|
|
||||||
foreach (var actor in data.Objects)
|
|
||||||
{
|
{
|
||||||
var mdl = actor.Model;
|
if (oldModelId == modelId)
|
||||||
if (!mdl.IsHuman)
|
return true;
|
||||||
continue;
|
|
||||||
|
|
||||||
var flags = Customize.Compare(mdl.GetCustomize(), customize);
|
state.ModelData.ModelId = modelId;
|
||||||
if (!flags.RequiresRedraw())
|
if (oldIsHuman)
|
||||||
_changeCustomize.UpdateCustomize(mdl, customize.Data);
|
return true;
|
||||||
else
|
|
||||||
_penumbra.RedrawObject(actor, RedrawType.Redraw);
|
// Fix up everything else to make sure the result is a valid human.
|
||||||
|
state.ModelData.Customize = Customize.Default;
|
||||||
|
state.ModelData.SetDefaultEquipment(_items);
|
||||||
|
state.ModelData.SetHatVisible(true);
|
||||||
|
state.ModelData.SetWeaponVisible(true);
|
||||||
|
state.ModelData.SetVisor(false);
|
||||||
|
state[ActorState.MetaIndex.ModelId] = source;
|
||||||
|
state[ActorState.MetaIndex.HatState] = source;
|
||||||
|
state[ActorState.MetaIndex.WeaponState] = source;
|
||||||
|
state[ActorState.MetaIndex.VisorState] = source;
|
||||||
|
foreach (var slot in EquipSlotExtensions.FullSlots)
|
||||||
|
{
|
||||||
|
state[slot, true] = source;
|
||||||
|
state[slot, false] = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
state[CustomizeIndex.Clan] = source;
|
||||||
|
state[CustomizeIndex.Gender] = source;
|
||||||
|
var set = _customizations.AwaitedService.GetList(state.ModelData.Customize.Clan, state.ModelData.Customize.Gender);
|
||||||
|
foreach (var index in Enum.GetValues<CustomizeIndex>().Where(set.IsAvailable))
|
||||||
|
state[index] = source;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Change a single piece of armor and/or stain depending on slot.
|
|
||||||
/// This uses the current customization of the model to potentially prevent restricted gear types from appearing.
|
|
||||||
/// This never requires redrawing.
|
|
||||||
/// </summary>
|
|
||||||
public void ChangeArmor(ActorData data, EquipSlot slot, CharacterArmor armor)
|
|
||||||
{
|
|
||||||
foreach (var actor in data.Objects.Where(a => a.IsCharacter))
|
|
||||||
{
|
|
||||||
var mdl = actor.Model;
|
|
||||||
var customize = mdl.IsHuman ? mdl.GetCustomize() : actor.GetCustomize();
|
|
||||||
var (_, resolvedItem) = _items.ResolveRestrictedGear(armor, slot, customize.Race, customize.Gender);
|
|
||||||
_updateSlot.UpdateSlot(actor.Model, slot, resolvedItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Change the stain of a single piece of armor or weapon.
|
|
||||||
/// If the offhand is empty, the stain will be fixed to 0 to prevent crashes.
|
|
||||||
/// </summary>
|
|
||||||
public void ChangeStain(ActorData data, EquipSlot slot, StainId stain)
|
|
||||||
{
|
|
||||||
var idx = slot.ToIndex();
|
|
||||||
switch (idx)
|
|
||||||
{
|
|
||||||
case < 10:
|
|
||||||
foreach (var actor in data.Objects.Where(a => a.IsCharacter))
|
|
||||||
_updateSlot.UpdateStain(actor.Model, slot, stain);
|
|
||||||
break;
|
|
||||||
case 10:
|
|
||||||
foreach (var actor in data.Objects.Where(a => a.IsCharacter))
|
|
||||||
_weapon.LoadStain(actor, EquipSlot.MainHand, stain);
|
|
||||||
break;
|
|
||||||
case 11:
|
|
||||||
foreach (var actor in data.Objects.Where(a => a.IsCharacter))
|
|
||||||
_weapon.LoadStain(actor, EquipSlot.OffHand, stain);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Apply a weapon to the appropriate slot. </summary>
|
|
||||||
public void ChangeWeapon(ActorData data, EquipSlot slot, EquipItem item, StainId stain)
|
|
||||||
{
|
|
||||||
if (slot is EquipSlot.MainHand)
|
|
||||||
ChangeMainhand(data, item, stain);
|
|
||||||
else
|
else
|
||||||
ChangeOffhand(data, item, stain);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Apply a weapon to the mainhand. If the weapon type has no associated offhand type, apply both.
|
|
||||||
/// </summary>
|
|
||||||
public void ChangeMainhand(ActorData data, EquipItem weapon, StainId stain)
|
|
||||||
{
|
|
||||||
var slot = weapon.Type.Offhand() == FullEquipType.Unknown ? EquipSlot.BothHand : EquipSlot.MainHand;
|
|
||||||
foreach (var actor in data.Objects.Where(a => a.IsCharacter))
|
|
||||||
_weapon.LoadWeapon(actor, slot, weapon.Weapon().With(stain));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Apply a weapon to the offhand. </summary>
|
|
||||||
public void ChangeOffhand(ActorData data, EquipItem weapon, StainId stain)
|
|
||||||
{
|
|
||||||
stain = weapon.ModelId.Value == 0 ? 0 : stain;
|
|
||||||
foreach (var actor in data.Objects.Where(a => a.IsCharacter))
|
|
||||||
_weapon.LoadWeapon(actor, EquipSlot.OffHand, weapon.Weapon().With(stain));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary> Change the visor state of actors only on the draw object. </summary>
|
|
||||||
public void ChangeVisor(ActorData data, bool value)
|
|
||||||
{
|
|
||||||
foreach (var actor in data.Objects.Where(a => a.IsCharacter))
|
|
||||||
{
|
{
|
||||||
var mdl = actor.Model;
|
unsafe
|
||||||
if (!mdl.IsHuman)
|
{
|
||||||
continue;
|
state.ModelData.LoadNonHuman(modelId, customize, (byte*)equipData);
|
||||||
|
state[ActorState.MetaIndex.ModelId] = source;
|
||||||
_visor.SetVisorState(mdl, value);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Change the forced wetness state on actors. </summary>
|
/// <summary> Change a customization value. </summary>
|
||||||
public unsafe void ChangeWetness(ActorData data, bool value)
|
public bool ChangeCustomize(ActorState state, CustomizeIndex idx, CustomizeValue value, StateChanged.Source source,
|
||||||
|
out CustomizeValue old, uint key = 0)
|
||||||
{
|
{
|
||||||
foreach (var actor in data.Objects.Where(a => a.IsCharacter))
|
old = state.ModelData.Customize[idx];
|
||||||
actor.AsCharacter->IsGPoseWet = value;
|
if (!state.CanUnlock(key))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
state.ModelData.Customize[idx] = value;
|
||||||
|
state[idx] = source;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Change the hat-visibility state on actors. </summary>
|
/// <summary> Change an entire customization array according to flags. </summary>
|
||||||
public unsafe void ChangeHatState(ActorData data, bool value)
|
public bool ChangeHumanCustomize(ActorState state, in Customize customizeInput, CustomizeFlag applyWhich, StateChanged.Source source,
|
||||||
|
out Customize old, out CustomizeFlag changed, uint key = 0)
|
||||||
{
|
{
|
||||||
foreach (var actor in data.Objects.Where(a => a.IsCharacter))
|
old = state.ModelData.Customize;
|
||||||
_metaService.SetHatState(actor, value);
|
changed = 0;
|
||||||
|
if (!state.CanUnlock(key))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
(var customize, var applied, changed) = _customizations.Combine(state.ModelData.Customize, customizeInput, applyWhich);
|
||||||
|
if (changed == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
state.ModelData.Customize = customize;
|
||||||
|
applied |= changed;
|
||||||
|
foreach (var type in Enum.GetValues<CustomizeIndex>())
|
||||||
|
{
|
||||||
|
if (applied.HasFlag(type.ToFlag()))
|
||||||
|
state[type] = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Change the weapon-visibility state on actors. </summary>
|
/// <summary> Change a single piece of equipment without stain. </summary>
|
||||||
public unsafe void ChangeWeaponState(ActorData data, bool value)
|
public bool ChangeItem(ActorState state, EquipSlot slot, EquipItem item, StateChanged.Source source, out EquipItem oldItem, uint key = 0)
|
||||||
{
|
{
|
||||||
foreach (var actor in data.Objects.Where(a => a.IsCharacter))
|
oldItem = state.ModelData.Item(slot);
|
||||||
_metaService.SetWeaponState(actor, value);
|
if (!state.CanUnlock(key))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
state.ModelData.SetItem(slot, item);
|
||||||
|
state[slot, false] = source;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Change a single piece of equipment including stain. </summary>
|
||||||
|
public bool ChangeEquip(ActorState state, EquipSlot slot, EquipItem item, StainId stain, StateChanged.Source source, out EquipItem oldItem,
|
||||||
|
out StainId oldStain, uint key = 0)
|
||||||
|
{
|
||||||
|
oldItem = state.ModelData.Item(slot);
|
||||||
|
oldStain = state.ModelData.Stain(slot);
|
||||||
|
if (!state.CanUnlock(key))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
state.ModelData.SetItem(slot, item);
|
||||||
|
state.ModelData.SetStain(slot, stain);
|
||||||
|
state[slot, false] = source;
|
||||||
|
state[slot, true] = source;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary> Change only the stain of an equipment piece. </summary>
|
||||||
|
public bool ChangeStain(ActorState state, EquipSlot slot, StainId stain, StateChanged.Source source, out StainId oldStain, uint key = 0)
|
||||||
|
{
|
||||||
|
oldStain = state.ModelData.Stain(slot);
|
||||||
|
if (!state.CanUnlock(key))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
state.ModelData.SetStain(slot, stain);
|
||||||
|
state[slot, true] = source;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ChangeMetaState(ActorState state, ActorState.MetaIndex index, bool value, StateChanged.Source source, out bool oldValue,
|
||||||
|
uint key = 0)
|
||||||
|
{
|
||||||
|
(var setter, oldValue) = index switch
|
||||||
|
{
|
||||||
|
ActorState.MetaIndex.Wetness => ((Func<bool, bool>)state.ModelData.SetIsWet, state.ModelData.IsWet()),
|
||||||
|
ActorState.MetaIndex.HatState => ((Func<bool, bool>)state.ModelData.SetHatVisible, state.ModelData.IsHatVisible()),
|
||||||
|
ActorState.MetaIndex.VisorState => ((Func<bool, bool>)state.ModelData.SetVisor, state.ModelData.IsVisorToggled()),
|
||||||
|
ActorState.MetaIndex.WeaponState => ((Func<bool, bool>)state.ModelData.SetWeaponVisible, state.ModelData.IsWeaponVisible()),
|
||||||
|
_ => throw new Exception("Invalid MetaIndex."),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!state.CanUnlock(key))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
setter(value);
|
||||||
|
state[index] = source;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ using Glamourer.Interop.Penumbra;
|
||||||
using Glamourer.Interop.Structs;
|
using Glamourer.Interop.Structs;
|
||||||
using Glamourer.Services;
|
using Glamourer.Services;
|
||||||
using OtterGui.Classes;
|
using OtterGui.Classes;
|
||||||
|
using Penumbra.GameData.Data;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
using Penumbra.GameData.Structs;
|
using Penumbra.GameData.Structs;
|
||||||
|
|
||||||
|
|
@ -30,6 +31,7 @@ public class StateListener : IDisposable
|
||||||
private readonly WeaponVisibilityChanged _weaponVisibility;
|
private readonly WeaponVisibilityChanged _weaponVisibility;
|
||||||
private readonly AutoDesignApplier _autoDesignApplier;
|
private readonly AutoDesignApplier _autoDesignApplier;
|
||||||
private readonly FunModule _funModule;
|
private readonly FunModule _funModule;
|
||||||
|
private readonly HumanModelList _humans;
|
||||||
|
|
||||||
public bool Enabled
|
public bool Enabled
|
||||||
{
|
{
|
||||||
|
|
@ -39,7 +41,7 @@ public class StateListener : IDisposable
|
||||||
|
|
||||||
public StateListener(StateManager manager, ItemManager items, PenumbraService penumbra, ActorService actors, Configuration config,
|
public StateListener(StateManager manager, ItemManager items, PenumbraService penumbra, ActorService actors, Configuration config,
|
||||||
SlotUpdating slotUpdating, WeaponLoading weaponLoading, VisorStateChanged visorState, WeaponVisibilityChanged weaponVisibility,
|
SlotUpdating slotUpdating, WeaponLoading weaponLoading, VisorStateChanged visorState, WeaponVisibilityChanged weaponVisibility,
|
||||||
HeadGearVisibilityChanged headGearVisibility, AutoDesignApplier autoDesignApplier, FunModule funModule)
|
HeadGearVisibilityChanged headGearVisibility, AutoDesignApplier autoDesignApplier, FunModule funModule, HumanModelList humans)
|
||||||
{
|
{
|
||||||
_manager = manager;
|
_manager = manager;
|
||||||
_items = items;
|
_items = items;
|
||||||
|
|
@ -53,6 +55,7 @@ public class StateListener : IDisposable
|
||||||
_headGearVisibility = headGearVisibility;
|
_headGearVisibility = headGearVisibility;
|
||||||
_autoDesignApplier = autoDesignApplier;
|
_autoDesignApplier = autoDesignApplier;
|
||||||
_funModule = funModule;
|
_funModule = funModule;
|
||||||
|
_humans = humans;
|
||||||
|
|
||||||
if (Enabled)
|
if (Enabled)
|
||||||
Subscribe();
|
Subscribe();
|
||||||
|
|
@ -320,10 +323,11 @@ public class StateListener : IDisposable
|
||||||
|
|
||||||
// Model ID did change, reload entire state accordingly.
|
// Model ID did change, reload entire state accordingly.
|
||||||
// Always use the actor for the base data.
|
// Always use the actor for the base data.
|
||||||
if (modelId == 0)
|
var isHuman = _humans.IsHuman(modelId);
|
||||||
state.BaseData.LoadNonHuman(modelId, *(Customize*)customizeData, (byte*)equipData);
|
if (isHuman)
|
||||||
else
|
|
||||||
state.BaseData = _manager.FromActor(actor, false);
|
state.BaseData = _manager.FromActor(actor, false);
|
||||||
|
else
|
||||||
|
state.BaseData.LoadNonHuman(modelId, *(Customize*)customizeData, (byte*)equipData);
|
||||||
|
|
||||||
return UpdateState.Change;
|
return UpdateState.Change;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ using Glamourer.Interop;
|
||||||
using Glamourer.Interop.Structs;
|
using Glamourer.Interop.Structs;
|
||||||
using Glamourer.Services;
|
using Glamourer.Services;
|
||||||
using Penumbra.GameData.Actors;
|
using Penumbra.GameData.Actors;
|
||||||
|
using Penumbra.GameData.Data;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
using Penumbra.GameData.Structs;
|
using Penumbra.GameData.Structs;
|
||||||
|
|
||||||
|
|
@ -17,26 +18,24 @@ namespace Glamourer.State;
|
||||||
|
|
||||||
public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
|
public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
|
||||||
{
|
{
|
||||||
private readonly ActorService _actors;
|
private readonly ActorService _actors;
|
||||||
private readonly ItemManager _items;
|
private readonly ItemManager _items;
|
||||||
private readonly CustomizationService _customizations;
|
private readonly HumanModelList _humans;
|
||||||
private readonly VisorService _visor;
|
private readonly StateChanged _event;
|
||||||
private readonly StateChanged _event;
|
private readonly StateApplier _applier;
|
||||||
private readonly ObjectManager _objects;
|
private readonly StateEditor _editor;
|
||||||
private readonly StateEditor _editor;
|
|
||||||
|
|
||||||
private readonly Dictionary<ActorIdentifier, ActorState> _states = new();
|
private readonly Dictionary<ActorIdentifier, ActorState> _states = new();
|
||||||
|
|
||||||
public StateManager(ActorService actors, ItemManager items, CustomizationService customizations, VisorService visor, StateChanged @event,
|
public StateManager(ActorService actors, ItemManager items, StateChanged @event, StateApplier applier, StateEditor editor,
|
||||||
ObjectManager objects, StateEditor editor)
|
HumanModelList humans)
|
||||||
{
|
{
|
||||||
_actors = actors;
|
_actors = actors;
|
||||||
_items = items;
|
_items = items;
|
||||||
_customizations = customizations;
|
_event = @event;
|
||||||
_visor = visor;
|
_applier = applier;
|
||||||
_event = @event;
|
_editor = editor;
|
||||||
_objects = objects;
|
_humans = humans;
|
||||||
_editor = editor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerator<KeyValuePair<ActorIdentifier, ActorState>> GetEnumerator()
|
public IEnumerator<KeyValuePair<ActorIdentifier, ActorState>> GetEnumerator()
|
||||||
|
|
@ -109,17 +108,18 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var model = actor.Model;
|
||||||
|
|
||||||
// Model ID is only unambiguously contained in the game object.
|
// Model ID is only unambiguously contained in the game object.
|
||||||
// The draw object only has the object type.
|
// The draw object only has the object type.
|
||||||
// TODO reverse search model data to get model id from model.
|
// TODO reverse search model data to get model id from model.
|
||||||
if (actor.AsCharacter->CharacterData.ModelCharaId != 0)
|
if (!_humans.IsHuman((uint)actor.AsCharacter->CharacterData.ModelCharaId))
|
||||||
{
|
{
|
||||||
ret.LoadNonHuman((uint)actor.AsCharacter->CharacterData.ModelCharaId, *(Customize*)&actor.AsCharacter->DrawData.CustomizeData,
|
ret.LoadNonHuman((uint)actor.AsCharacter->CharacterData.ModelCharaId, *(Customize*)&actor.AsCharacter->DrawData.CustomizeData,
|
||||||
(byte*)&actor.AsCharacter->DrawData.Head);
|
(byte*)&actor.AsCharacter->DrawData.Head);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
var model = actor.Model;
|
|
||||||
CharacterWeapon main;
|
CharacterWeapon main;
|
||||||
CharacterWeapon off;
|
CharacterWeapon off;
|
||||||
|
|
||||||
|
|
@ -152,7 +152,7 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
|
||||||
(_, _, main, off) = model.GetWeapons(actor);
|
(_, _, main, off) = model.GetWeapons(actor);
|
||||||
|
|
||||||
// Visor state is a flag on the game object, but we can see the actual state on the draw object.
|
// Visor state is a flag on the game object, but we can see the actual state on the draw object.
|
||||||
ret.SetVisor(_visor.GetVisorState(model));
|
ret.SetVisor(VisorService.GetVisorState(model));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -192,304 +192,228 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
|
||||||
|
|
||||||
#region Change Values
|
#region Change Values
|
||||||
|
|
||||||
/// <summary> Turn a non-human actor human. </summary>
|
/// <summary> Turn an actor human. </summary>
|
||||||
public void TurnHuman(ActorState state, StateChanged.Source source)
|
public void TurnHuman(ActorState state, StateChanged.Source source, uint key = 0)
|
||||||
|
=> ChangeModelId(state, 0, Customize.Default, nint.Zero, source, key);
|
||||||
|
|
||||||
|
/// <summary> Turn an actor to. </summary>
|
||||||
|
public void ChangeModelId(ActorState state, uint modelId, Customize customize, nint equipData, StateChanged.Source source,
|
||||||
|
uint key = 0)
|
||||||
{
|
{
|
||||||
if (state.ModelData.ModelId == 0)
|
if (!_editor.ChangeModelId(state, modelId, customize, equipData, source, out var old, key))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
state.ModelData.ModelId = 0;
|
var actors = _applier.ForceRedraw(state, source is StateChanged.Source.Manual or StateChanged.Source.Ipc);
|
||||||
state[ActorState.MetaIndex.ModelId] = source;
|
Glamourer.Log.Verbose(
|
||||||
ChangeCustomize(state, Customize.Default, CustomizeFlagExtensions.All, source);
|
$"Set model id in state {state.Identifier.Incognito(null)} from {old} to {modelId}. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||||
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
_event.Invoke(StateChanged.Type.Model, source, state, actors, (old, modelId));
|
||||||
ChangeEquip(state, slot, ItemManager.NothingItem(slot), 0, source);
|
|
||||||
ChangeEquip(state, EquipSlot.MainHand, _items.DefaultSword, 0, source);
|
|
||||||
ChangeEquip(state, EquipSlot.OffHand, ItemManager.NothingItem(FullEquipType.Shield), 0, source);
|
|
||||||
var objects = _objects.TryGetValue(state.Identifier, out var d) ? d : ActorData.Invalid;
|
|
||||||
_editor.ChangeModelId(objects, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Change a customization value. </summary>
|
/// <summary> Change a customization value. </summary>
|
||||||
public void ChangeCustomize(ActorState state, CustomizeIndex idx, CustomizeValue value, StateChanged.Source source)
|
public void ChangeCustomize(ActorState state, CustomizeIndex idx, CustomizeValue value, StateChanged.Source source, uint key = 0)
|
||||||
{
|
{
|
||||||
// Update state data.
|
if (!_editor.ChangeCustomize(state, idx, value, source, out var old, key))
|
||||||
var old = state.ModelData.Customize[idx];
|
return;
|
||||||
state.ModelData.Customize[idx] = value;
|
|
||||||
state[idx] = source;
|
|
||||||
|
|
||||||
// Update draw objects.
|
var actors = _applier.ChangeCustomize(state, source is StateChanged.Source.Manual or StateChanged.Source.Ipc);
|
||||||
_objects.Update();
|
|
||||||
var objects = _objects.TryGetValue(state.Identifier, out var d) ? d : ActorData.Invalid;
|
|
||||||
if (source is StateChanged.Source.Manual)
|
|
||||||
_editor.ChangeCustomize(objects, state.ModelData.Customize);
|
|
||||||
|
|
||||||
// Meta.
|
|
||||||
Glamourer.Log.Verbose(
|
Glamourer.Log.Verbose(
|
||||||
$"Set {idx.ToDefaultName()} customizations in state {state.Identifier} from {old.Value} to {value.Value}. [Affecting {objects.ToLazyString("nothing")}.]");
|
$"Set {idx.ToDefaultName()} customizations in state {state.Identifier.Incognito(null)} from {old.Value} to {value.Value}. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||||
_event.Invoke(StateChanged.Type.Customize, source, state, objects, (old, value, idx));
|
_event.Invoke(StateChanged.Type.Customize, source, state, actors, (old, value, idx));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Change an entire customization array according to flags. </summary>
|
/// <summary> Change an entire customization array according to flags. </summary>
|
||||||
public void ChangeCustomize(ActorState state, in Customize customizeInput, CustomizeFlag apply, StateChanged.Source source)
|
public void ChangeCustomize(ActorState state, in Customize customizeInput, CustomizeFlag apply, StateChanged.Source source, uint key = 0)
|
||||||
{
|
{
|
||||||
// Update state data.
|
if (!_editor.ChangeHumanCustomize(state, customizeInput, apply, source, out var old, out var applied, key))
|
||||||
var old = state.ModelData.Customize;
|
|
||||||
var (customize, applied) = _customizations.Combine(state.ModelData.Customize, customizeInput, apply);
|
|
||||||
if (applied == 0)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
state.ModelData.Customize = customize;
|
var actors = _applier.ChangeCustomize(state, source is StateChanged.Source.Manual or StateChanged.Source.Ipc);
|
||||||
foreach (var type in Enum.GetValues<CustomizeIndex>())
|
|
||||||
{
|
|
||||||
var flag = type.ToFlag();
|
|
||||||
if (applied.HasFlag(flag))
|
|
||||||
state[type] = source;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update draw objects.
|
|
||||||
_objects.Update();
|
|
||||||
var objects = _objects.TryGetValue(state.Identifier, out var d) ? d : ActorData.Invalid;
|
|
||||||
if (source is StateChanged.Source.Manual)
|
|
||||||
_editor.ChangeCustomize(objects, state.ModelData.Customize);
|
|
||||||
|
|
||||||
// Meta.
|
|
||||||
Glamourer.Log.Verbose(
|
Glamourer.Log.Verbose(
|
||||||
$"Set {applied} customizations in state {state.Identifier} from {old} to {customize}. [Affecting {objects.ToLazyString("nothing")}.]");
|
$"Set {applied} customizations in state {state.Identifier.Incognito(null)} from {old} to {customizeInput}. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||||
_event.Invoke(StateChanged.Type.Customize, source, state, objects, (old, customize, applied));
|
_event.Invoke(StateChanged.Type.EntireCustomize, source, state, actors, (old, applied));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Change a single piece of equipment without stain. </summary>
|
/// <summary> Change a single piece of equipment without stain. </summary>
|
||||||
/// <remarks> Do not use this in the same frame as ChangeStain, use <see cref="ChangeEquip(ActorState,EquipSlot,EquipItem,StainId,StateChanged.Source)"/> instead. </remarks>
|
/// <remarks> Do not use this in the same frame as ChangeStain, use <see cref="ChangeEquip(ActorState,EquipSlot,EquipItem,StainId,StateChanged.Source,uint)"/> instead. </remarks>
|
||||||
public void ChangeItem(ActorState state, EquipSlot slot, EquipItem item, StateChanged.Source source)
|
public void ChangeItem(ActorState state, EquipSlot slot, EquipItem item, StateChanged.Source source, uint key = 0)
|
||||||
{
|
{
|
||||||
// Update state data.
|
if (!_editor.ChangeItem(state, slot, item, source, out var old, key))
|
||||||
var old = state.ModelData.Item(slot);
|
return;
|
||||||
state.ModelData.SetItem(slot, item);
|
|
||||||
state[slot, false] = source;
|
|
||||||
var type = slot is EquipSlot.MainHand or EquipSlot.OffHand ? StateChanged.Type.Weapon : StateChanged.Type.Equip;
|
|
||||||
|
|
||||||
// Update draw objects.
|
var type = slot.IsEquipmentPiece() ? StateChanged.Type.Equip : StateChanged.Type.Weapon;
|
||||||
_objects.Update();
|
var actors = type is StateChanged.Type.Equip
|
||||||
var objects = _objects.TryGetValue(state.Identifier, out var d) ? d : ActorData.Invalid;
|
? _applier.ChangeArmor(state, slot, source is StateChanged.Source.Manual or StateChanged.Source.Ipc)
|
||||||
if (source is StateChanged.Source.Manual)
|
: _applier.ChangeWeapon(state, slot, source is StateChanged.Source.Manual or StateChanged.Source.Ipc);
|
||||||
if (type == StateChanged.Type.Equip)
|
|
||||||
{
|
|
||||||
if (slot is not EquipSlot.Head || state.ModelData.IsHatVisible())
|
|
||||||
_editor.ChangeArmor(objects, slot, state.ModelData.Armor(slot));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_editor.ChangeWeapon(objects, slot, state.ModelData.Item(slot), state.ModelData.Stain(slot));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Meta.
|
|
||||||
Glamourer.Log.Verbose(
|
Glamourer.Log.Verbose(
|
||||||
$"Set {slot.ToName()} in state {state.Identifier} from {old.Name} ({old.Id}) to {item.Name} ({item.Id}). [Affecting {objects.ToLazyString("nothing")}.]");
|
$"Set {slot.ToName()} in state {state.Identifier.Incognito(null)} from {old.Name} ({old.Id}) to {item.Name} ({item.Id}). [Affecting {actors.ToLazyString("nothing")}.]");
|
||||||
_event.Invoke(type, source, state, objects, (old, item, slot));
|
_event.Invoke(type, source, state, actors, (old, item, slot));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Change a single piece of equipment including stain. </summary>
|
/// <summary> Change a single piece of equipment including stain. </summary>
|
||||||
public void ChangeEquip(ActorState state, EquipSlot slot, EquipItem item, StainId stain, StateChanged.Source source)
|
public void ChangeEquip(ActorState state, EquipSlot slot, EquipItem item, StainId stain, StateChanged.Source source, uint key = 0)
|
||||||
{
|
{
|
||||||
// Update state data.
|
if (!_editor.ChangeEquip(state, slot, item, stain, source, out var old, out var oldStain, key))
|
||||||
var old = state.ModelData.Item(slot);
|
return;
|
||||||
var oldStain = state.ModelData.Stain(slot);
|
|
||||||
state.ModelData.SetItem(slot, item);
|
|
||||||
state.ModelData.SetStain(slot, stain);
|
|
||||||
state[slot, false] = source;
|
|
||||||
state[slot, true] = source;
|
|
||||||
var type = slot is EquipSlot.MainHand or EquipSlot.OffHand ? StateChanged.Type.Weapon : StateChanged.Type.Equip;
|
|
||||||
|
|
||||||
// Update draw objects.
|
var type = slot.IsEquipmentPiece() ? StateChanged.Type.Equip : StateChanged.Type.Weapon;
|
||||||
_objects.Update();
|
var actors = type is StateChanged.Type.Equip
|
||||||
var objects = _objects.TryGetValue(state.Identifier, out var d) ? d : ActorData.Invalid;
|
? _applier.ChangeArmor(state, slot, source is StateChanged.Source.Manual or StateChanged.Source.Ipc)
|
||||||
if (source is StateChanged.Source.Manual)
|
: _applier.ChangeWeapon(state, slot, source is StateChanged.Source.Manual or StateChanged.Source.Ipc);
|
||||||
if (type == StateChanged.Type.Equip)
|
|
||||||
{
|
|
||||||
if (slot is not EquipSlot.Head || state.ModelData.IsHatVisible())
|
|
||||||
_editor.ChangeArmor(objects, slot, state.ModelData.Armor(slot));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_editor.ChangeWeapon(objects, slot, state.ModelData.Item(slot), state.ModelData.Stain(slot));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Meta.
|
|
||||||
Glamourer.Log.Verbose(
|
Glamourer.Log.Verbose(
|
||||||
$"Set {slot.ToName()} in state {state.Identifier} from {old.Name} ({old.Id}) to {item.Name} ({item.Id}) and its stain from {oldStain.Value} to {stain.Value}. [Affecting {objects.ToLazyString("nothing")}.]");
|
$"Set {slot.ToName()} in state {state.Identifier.Incognito(null)} from {old.Name} ({old.Id}) to {item.Name} ({item.Id}) and its stain from {oldStain.Value} to {stain.Value}. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||||
_event.Invoke(type, source, state, objects, (old, item, slot));
|
_event.Invoke(type, source, state, actors, (old, item, slot));
|
||||||
_event.Invoke(StateChanged.Type.Stain, source, state, objects, (oldStain, stain, slot));
|
_event.Invoke(StateChanged.Type.Stain, source, state, actors, (oldStain, stain, slot));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Change only the stain of an equipment piece. </summary>
|
/// <summary> Change only the stain of an equipment piece. </summary>
|
||||||
/// <remarks>
|
/// <remarks> Do not use this in the same frame as ChangeEquip, use <see cref="ChangeEquip(ActorState,EquipSlot,EquipItem,StainId,StateChanged.Source,uint)"/> instead. </remarks>
|
||||||
/// Do not use this in the same frame as ChangeEquip, use <see cref="ChangeEquip(ActorState,EquipSlot,EquipItem,StainId,StateChanged.Source)"/> instead. </remarks>
|
public void ChangeStain(ActorState state, EquipSlot slot, StainId stain, StateChanged.Source source, uint key = 0)
|
||||||
public void ChangeStain(ActorState state, EquipSlot slot, StainId stain, StateChanged.Source source)
|
|
||||||
{
|
{
|
||||||
// Update state data.
|
if (!_editor.ChangeStain(state, slot, stain, source, out var old, key))
|
||||||
var old = state.ModelData.Stain(slot);
|
return;
|
||||||
state.ModelData.SetStain(slot, stain);
|
|
||||||
state[slot, true] = source;
|
|
||||||
|
|
||||||
// Update draw objects.
|
var actors = _applier.ChangeStain(state, slot, source is StateChanged.Source.Manual or StateChanged.Source.Ipc);
|
||||||
_objects.Update();
|
|
||||||
var objects = _objects.TryGetValue(state.Identifier, out var d) ? d : ActorData.Invalid;
|
|
||||||
if (source is StateChanged.Source.Manual)
|
|
||||||
_editor.ChangeStain(objects, slot, stain);
|
|
||||||
|
|
||||||
// Meta.
|
|
||||||
Glamourer.Log.Verbose(
|
Glamourer.Log.Verbose(
|
||||||
$"Set {slot.ToName()} stain in state {state.Identifier} from {old.Value} to {stain.Value}. [Affecting {objects.ToLazyString("nothing")}.]");
|
$"Set {slot.ToName()} stain in state {state.Identifier.Incognito(null)} from {old.Value} to {stain.Value}. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||||
_event.Invoke(StateChanged.Type.Stain, source, state, objects, (old, stain, slot));
|
_event.Invoke(StateChanged.Type.Stain, source, state, actors, (old, stain, slot));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Change hat visibility. </summary>
|
/// <summary> Change hat visibility. </summary>
|
||||||
public void ChangeHatState(ActorState state, bool value, StateChanged.Source source)
|
public void ChangeHatState(ActorState state, bool value, StateChanged.Source source, uint key = 0)
|
||||||
{
|
{
|
||||||
// Update state data.
|
if (!_editor.ChangeMetaState(state, ActorState.MetaIndex.HatState, value, source, out var old, key))
|
||||||
var old = state.ModelData.IsHatVisible();
|
return;
|
||||||
state.ModelData.SetHatVisible(value);
|
|
||||||
state[ActorState.MetaIndex.HatState] = source;
|
|
||||||
|
|
||||||
// Update draw objects / game objects.
|
var actors = _applier.ChangeHatState(state, source is StateChanged.Source.Manual or StateChanged.Source.Ipc);
|
||||||
_objects.Update();
|
|
||||||
var objects = _objects.TryGetValue(state.Identifier, out var d) ? d : ActorData.Invalid;
|
|
||||||
if (source is StateChanged.Source.Manual)
|
|
||||||
_editor.ChangeHatState(objects, value);
|
|
||||||
|
|
||||||
// Meta.
|
|
||||||
Glamourer.Log.Verbose(
|
Glamourer.Log.Verbose(
|
||||||
$"Set Head Gear Visibility in state {state.Identifier} from {old} to {value}. [Affecting {objects.ToLazyString("nothing")}.]");
|
$"Set Head Gear Visibility in state {state.Identifier} from {old} to {value}. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||||
_event.Invoke(StateChanged.Type.Other, source, state, objects, (old, value, ActorState.MetaIndex.HatState));
|
_event.Invoke(StateChanged.Type.Other, source, state, actors, (old, value, ActorState.MetaIndex.HatState));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Change weapon visibility. </summary>
|
/// <summary> Change weapon visibility. </summary>
|
||||||
public void ChangeWeaponState(ActorState state, bool value, StateChanged.Source source)
|
public void ChangeWeaponState(ActorState state, bool value, StateChanged.Source source, uint key = 0)
|
||||||
{
|
{
|
||||||
// Update state data.
|
if (!_editor.ChangeMetaState(state, ActorState.MetaIndex.WeaponState, value, source, out var old, key))
|
||||||
var old = state.ModelData.IsWeaponVisible();
|
return;
|
||||||
state.ModelData.SetWeaponVisible(value);
|
|
||||||
state[ActorState.MetaIndex.WeaponState] = source;
|
|
||||||
|
|
||||||
// Update draw objects / game objects.
|
var actors = _applier.ChangeWeaponState(state, source is StateChanged.Source.Manual or StateChanged.Source.Ipc);
|
||||||
_objects.Update();
|
|
||||||
var objects = _objects.TryGetValue(state.Identifier, out var d) ? d : ActorData.Invalid;
|
|
||||||
if (source is StateChanged.Source.Manual)
|
|
||||||
_editor.ChangeWeaponState(objects, value);
|
|
||||||
|
|
||||||
// Meta.
|
|
||||||
Glamourer.Log.Verbose(
|
Glamourer.Log.Verbose(
|
||||||
$"Set Weapon Visibility in state {state.Identifier} from {old} to {value}. [Affecting {objects.ToLazyString("nothing")}.]");
|
$"Set Weapon Visibility in state {state.Identifier} from {old} to {value}. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||||
_event.Invoke(StateChanged.Type.Other, source, state, objects, (old, value, ActorState.MetaIndex.WeaponState));
|
_event.Invoke(StateChanged.Type.Other, source, state, actors, (old, value, ActorState.MetaIndex.WeaponState));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Change visor state. </summary>
|
/// <summary> Change visor state. </summary>
|
||||||
public void ChangeVisorState(ActorState state, bool value, StateChanged.Source source)
|
public void ChangeVisorState(ActorState state, bool value, StateChanged.Source source, uint key = 0)
|
||||||
{
|
{
|
||||||
// Update state data.
|
if (!_editor.ChangeMetaState(state, ActorState.MetaIndex.VisorState, value, source, out var old, key))
|
||||||
var old = state.ModelData.IsVisorToggled();
|
return;
|
||||||
state.ModelData.SetVisor(value);
|
|
||||||
state[ActorState.MetaIndex.VisorState] = source;
|
|
||||||
|
|
||||||
// Update draw objects.
|
var actors = _applier.ChangeVisor(state, source is StateChanged.Source.Manual or StateChanged.Source.Ipc);
|
||||||
_objects.Update();
|
|
||||||
var objects = _objects.TryGetValue(state.Identifier, out var d) ? d : ActorData.Invalid;
|
|
||||||
if (source is StateChanged.Source.Manual)
|
|
||||||
_editor.ChangeVisor(objects, value);
|
|
||||||
|
|
||||||
// Meta.
|
|
||||||
Glamourer.Log.Verbose(
|
Glamourer.Log.Verbose(
|
||||||
$"Set Visor State in state {state.Identifier} from {old} to {value}. [Affecting {objects.ToLazyString("nothing")}.]");
|
$"Set Visor State in state {state.Identifier} from {old} to {value}. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||||
_event.Invoke(StateChanged.Type.Other, source, state, objects, (old, value, ActorState.MetaIndex.VisorState));
|
_event.Invoke(StateChanged.Type.Other, source, state, actors, (old, value, ActorState.MetaIndex.VisorState));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary> Set GPose Wetness. </summary>
|
/// <summary> Set GPose Wetness. </summary>
|
||||||
public void ChangeWetness(ActorState state, bool value, StateChanged.Source source)
|
public void ChangeWetness(ActorState state, bool value, StateChanged.Source source, uint key = 0)
|
||||||
{
|
{
|
||||||
// Update state data.
|
if (!_editor.ChangeMetaState(state, ActorState.MetaIndex.Wetness, value, source, out var old, key))
|
||||||
var old = state.ModelData.IsWet();
|
return;
|
||||||
state.ModelData.SetIsWet(value);
|
|
||||||
state[ActorState.MetaIndex.Wetness] = source;
|
|
||||||
|
|
||||||
// Update draw objects / game objects.
|
var actors = _applier.ChangeVisor(state, true);
|
||||||
_objects.Update();
|
|
||||||
var objects = _objects.TryGetValue(state.Identifier, out var d) ? d : ActorData.Invalid;
|
|
||||||
_editor.ChangeWetness(objects, value);
|
|
||||||
|
|
||||||
// Meta.
|
|
||||||
Glamourer.Log.Verbose(
|
Glamourer.Log.Verbose(
|
||||||
$"Set Wetness in state {state.Identifier} from {old} to {value}. [Affecting {objects.ToLazyString("nothing")}.]");
|
$"Set Wetness in state {state.Identifier} from {old} to {value}. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||||
_event.Invoke(StateChanged.Type.Other, state[ActorState.MetaIndex.Wetness], state, objects, (old, value, ActorState.MetaIndex.Wetness));
|
_event.Invoke(StateChanged.Type.Other, state[ActorState.MetaIndex.Wetness], state, actors, (old, value, ActorState.MetaIndex.Wetness));
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
public void ApplyDesign(DesignBase design, ActorState state)
|
public void ApplyDesign(DesignBase design, ActorState state, StateChanged.Source source, uint key = 0)
|
||||||
{
|
{
|
||||||
void HandleEquip(EquipSlot slot, bool applyPiece, bool applyStain)
|
void HandleEquip(EquipSlot slot, bool applyPiece, bool applyStain)
|
||||||
{
|
{
|
||||||
switch (applyPiece, applyStain)
|
var unused = (applyPiece, applyStain) switch
|
||||||
{
|
{
|
||||||
case (false, false): break;
|
(false, false) => false,
|
||||||
case (true, false):
|
(true, false) => _editor.ChangeItem(state, slot, design.DesignData.Item(slot), source, out _, key),
|
||||||
ChangeItem(state, slot, design.DesignData.Item(slot), StateChanged.Source.Manual);
|
(false, true) => _editor.ChangeStain(state, slot, design.DesignData.Stain(slot), source, out _, key),
|
||||||
break;
|
(true, true) => _editor.ChangeEquip(state, slot, design.DesignData.Item(slot), design.DesignData.Stain(slot), source, out _,
|
||||||
case (false, true):
|
out _, key),
|
||||||
ChangeStain(state, slot, design.DesignData.Stain(slot), StateChanged.Source.Manual);
|
};
|
||||||
break;
|
|
||||||
case (true, true):
|
|
||||||
ChangeEquip(state, slot, design.DesignData.Item(slot), design.DesignData.Stain(slot), StateChanged.Source.Manual);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.ModelData.ModelId != 0 && design.DesignData.ModelId == 0)
|
if (!_editor.ChangeModelId(state, design.DesignData.ModelId, design.DesignData.Customize, design.DesignData.GetEquipmentPtr(), source,
|
||||||
TurnHuman(state, StateChanged.Source.Manual);
|
out var oldModelId, key))
|
||||||
|
return;
|
||||||
|
|
||||||
if (design.DoApplyHatVisible())
|
var redraw = oldModelId != design.DesignData.ModelId || !design.DesignData.IsHuman;
|
||||||
ChangeHatState(state, design.DesignData.IsHatVisible(), StateChanged.Source.Manual);
|
|
||||||
if (design.DoApplyWeaponVisible())
|
|
||||||
ChangeWeaponState(state, design.DesignData.IsWeaponVisible(), StateChanged.Source.Manual);
|
|
||||||
if (design.DoApplyVisorToggle())
|
|
||||||
ChangeVisorState(state, design.DesignData.IsVisorToggled(), StateChanged.Source.Manual);
|
|
||||||
if (design.DoApplyWetness())
|
if (design.DoApplyWetness())
|
||||||
ChangeWetness(state, design.DesignData.IsWet(), StateChanged.Source.Manual);
|
_editor.ChangeMetaState(state, ActorState.MetaIndex.Wetness, design.DesignData.IsWet(), source, out _, key);
|
||||||
|
|
||||||
ChangeCustomize(state, design.DesignData.Customize, design.ApplyCustomize, StateChanged.Source.Manual);
|
if (state.ModelData.IsHuman)
|
||||||
|
{
|
||||||
|
if (design.DoApplyHatVisible())
|
||||||
|
_editor.ChangeMetaState(state, ActorState.MetaIndex.HatState, design.DesignData.IsHatVisible(), source, out _, key);
|
||||||
|
if (design.DoApplyWeaponVisible())
|
||||||
|
_editor.ChangeMetaState(state, ActorState.MetaIndex.WeaponState, design.DesignData.IsWeaponVisible(), source, out _, key);
|
||||||
|
if (design.DoApplyVisorToggle())
|
||||||
|
_editor.ChangeMetaState(state, ActorState.MetaIndex.VisorState, design.DesignData.IsVisorToggled(), source, out _, key);
|
||||||
|
|
||||||
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
_editor.ChangeHumanCustomize(state, design.DesignData.Customize, design.ApplyCustomize, source, out _, out var applied, key);
|
||||||
HandleEquip(slot, design.DoApplyEquip(slot), design.DoApplyStain(slot));
|
redraw |= applied.RequiresRedraw();
|
||||||
|
|
||||||
HandleEquip(EquipSlot.MainHand,
|
foreach (var slot in EquipSlotExtensions.FullSlots)
|
||||||
design.DoApplyEquip(EquipSlot.MainHand)
|
HandleEquip(slot, design.DoApplyEquip(slot), design.DoApplyStain(slot));
|
||||||
&& design.DesignData.Item(EquipSlot.MainHand).Type == state.BaseData.Item(EquipSlot.MainHand).Type,
|
}
|
||||||
design.DoApplyStain(EquipSlot.MainHand));
|
|
||||||
HandleEquip(EquipSlot.OffHand,
|
var actors = ApplyAll(state, redraw);
|
||||||
design.DoApplyEquip(EquipSlot.OffHand)
|
Glamourer.Log.Verbose(
|
||||||
&& design.DesignData.Item(EquipSlot.OffHand).Type == state.BaseData.Item(EquipSlot.OffHand).Type,
|
$"Applied design to {state.Identifier.Incognito(null)}. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||||
design.DoApplyStain(EquipSlot.OffHand));
|
_event.Invoke(StateChanged.Type.Design, state[ActorState.MetaIndex.Wetness], state, actors, design);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ActorData ApplyAll(ActorState state, bool redraw)
|
||||||
|
{
|
||||||
|
var actors = _applier.ChangeWetness(state, true);
|
||||||
|
if (redraw)
|
||||||
|
{
|
||||||
|
_applier.ForceRedraw(actors);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_applier.ChangeCustomize(actors, state.ModelData.Customize);
|
||||||
|
_applier.ChangeHatState(actors, state.ModelData.IsHatVisible());
|
||||||
|
_applier.ChangeWeaponState(actors, state.ModelData.IsWeaponVisible());
|
||||||
|
_applier.ChangeVisor(actors, state.ModelData.IsVisorToggled());
|
||||||
|
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
||||||
|
_applier.ChangeArmor(actors, slot, state.ModelData.Armor(slot), state.ModelData.IsHatVisible());
|
||||||
|
foreach (var slot in EquipSlotExtensions.WeaponSlots)
|
||||||
|
_applier.ChangeWeapon(actors, slot, state.ModelData.Item(slot), state.ModelData.Stain(slot));
|
||||||
|
}
|
||||||
|
|
||||||
|
return actors;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ResetState(ActorState state)
|
public void ResetState(ActorState state)
|
||||||
{
|
{
|
||||||
ChangeHatState(state, state.BaseData.IsHatVisible(), StateChanged.Source.Game);
|
var redraw = state.ModelData.ModelId != state.BaseData.ModelId || !state.ModelData.IsHuman
|
||||||
ChangeVisorState(state, state.BaseData.IsVisorToggled(), StateChanged.Source.Game);
|
|| Customize.Compare(state.ModelData.Customize, state.BaseData.Customize).RequiresRedraw();
|
||||||
ChangeWeaponState(state, state.BaseData.IsWeaponVisible(), StateChanged.Source.Game);
|
state.ModelData = state.BaseData;
|
||||||
ChangeWetness(state, false, StateChanged.Source.Game);
|
foreach (var index in Enum.GetValues<CustomizeIndex>())
|
||||||
ChangeCustomize(state, state.BaseData.Customize, CustomizeFlagExtensions.All, StateChanged.Source.Game);
|
state[index] = StateChanged.Source.Game;
|
||||||
|
foreach (var slot in EquipSlotExtensions.FullSlots)
|
||||||
|
{
|
||||||
|
state[slot, true] = StateChanged.Source.Game;
|
||||||
|
state[slot, false] = StateChanged.Source.Game;
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
foreach (var type in Enum.GetValues<ActorState.MetaIndex>())
|
||||||
ChangeEquip(state, slot, state.BaseData.Item(slot), state.BaseData.Stain(slot), StateChanged.Source.Game);
|
state[type] = StateChanged.Source.Game;
|
||||||
|
|
||||||
ChangeEquip(state, EquipSlot.MainHand, state.BaseData.Item(EquipSlot.MainHand), state.BaseData.Stain(EquipSlot.MainHand),
|
var actors = ApplyAll(state, redraw);
|
||||||
StateChanged.Source.Game);
|
Glamourer.Log.Verbose(
|
||||||
ChangeEquip(state, EquipSlot.OffHand, state.BaseData.Item(EquipSlot.OffHand), state.BaseData.Stain(EquipSlot.OffHand),
|
$"Reset entire state of {state.Identifier.Incognito(null)} to game base. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||||
StateChanged.Source.Game);
|
_event.Invoke(StateChanged.Type.Design, state[ActorState.MetaIndex.Wetness], state, actors, null);
|
||||||
|
|
||||||
_objects.Update();
|
|
||||||
var objects = _objects.TryGetValue(state.Identifier, out var d) ? d : ActorData.Invalid;
|
|
||||||
foreach (var actor in objects.Objects)
|
|
||||||
ReapplyState(actor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ReapplyState(Actor actor)
|
public void ReapplyState(Actor actor)
|
||||||
|
|
@ -497,28 +421,7 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
|
||||||
if (!GetOrCreate(actor, out var state))
|
if (!GetOrCreate(actor, out var state))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var mdl = actor.Model;
|
ApplyAll(state, !actor.Model.IsHuman || Customize.Compare(actor.Model.GetCustomize(), state.ModelData.Customize).RequiresRedraw());
|
||||||
if (!mdl.IsHuman)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var data = new ActorData(actor, string.Empty);
|
|
||||||
var customizeFlags = Customize.Compare(mdl.GetCustomize(), state.ModelData.Customize);
|
|
||||||
|
|
||||||
_editor.ChangeHatState(data, state.ModelData.IsHatVisible());
|
|
||||||
_editor.ChangeWetness(data, false);
|
|
||||||
_editor.ChangeWeaponState(data, state.ModelData.IsWeaponVisible());
|
|
||||||
_editor.ChangeVisor(data, state.ModelData.IsVisorToggled());
|
|
||||||
|
|
||||||
_editor.ChangeCustomize(data, state.ModelData.Customize);
|
|
||||||
if (customizeFlags.RequiresRedraw())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (state.ModelData.IsHatVisible())
|
|
||||||
_editor.ChangeArmor(data, EquipSlot.Head, state.ModelData.Armor(EquipSlot.Head));
|
|
||||||
foreach (var slot in EquipSlotExtensions.EqdpSlots.Skip(1))
|
|
||||||
_editor.ChangeArmor(data, slot, state.ModelData.Armor(slot));
|
|
||||||
_editor.ChangeMainhand(data, state.ModelData.Item(EquipSlot.MainHand), state.ModelData.Stain(EquipSlot.MainHand));
|
|
||||||
_editor.ChangeOffhand(data, state.ModelData.Item(EquipSlot.OffHand), state.ModelData.Stain(EquipSlot.OffHand));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DeleteState(ActorIdentifier identifier)
|
public void DeleteState(ActorIdentifier identifier)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue