mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2025-12-12 18:27:24 +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.Plugin;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Events;
|
||||
using Glamourer.Interop.Structs;
|
||||
using Penumbra.Api.Helpers;
|
||||
using Penumbra.GameData.Actors;
|
||||
|
|
@ -76,7 +77,7 @@ public partial class GlamourerIpc
|
|||
continue;
|
||||
}
|
||||
|
||||
_stateManager.ApplyDesign(design, state);
|
||||
_stateManager.ApplyDesign(design, state, StateChanged.Source.Ipc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -115,6 +115,17 @@ public class AutoDesignApplier : IDisposable
|
|||
_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)
|
||||
{
|
||||
if (!_config.EnableAutoDesigns)
|
||||
|
|
|
|||
|
|
@ -130,8 +130,8 @@ public sealed class Design : DesignBase, ISavable
|
|||
if (design.LastEdit < creationDate)
|
||||
design.LastEdit = creationDate;
|
||||
|
||||
LoadEquip(items, json["Equipment"], design, design.Name);
|
||||
LoadCustomize(customizations, json["Customize"], design, design.Name);
|
||||
LoadEquip(items, json["Equipment"], design, design.Name);
|
||||
LoadMods(json["Mods"], design);
|
||||
return design;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ public class DesignBase
|
|||
DesignData = clone.DesignData;
|
||||
ApplyCustomize = clone.ApplyCustomize & CustomizeFlagExtensions.All;
|
||||
ApplyEquip = clone.ApplyEquip & EquipFlagExtensions.All;
|
||||
_designFlags = clone._designFlags & (DesignFlags) 0x0F;
|
||||
_designFlags = clone._designFlags & (DesignFlags)0x0F;
|
||||
}
|
||||
|
||||
internal DesignData DesignData = new();
|
||||
|
|
@ -177,7 +177,6 @@ public class DesignBase
|
|||
};
|
||||
|
||||
var ret = new JObject();
|
||||
|
||||
foreach (var slot in EquipSlotExtensions.EqdpSlots.Prepend(EquipSlot.OffHand).Prepend(EquipSlot.MainHand))
|
||||
{
|
||||
var item = DesignData.Item(slot);
|
||||
|
|
@ -188,6 +187,7 @@ public class DesignBase
|
|||
ret["Hat"] = new QuadBool(DesignData.IsHatVisible(), DoApplyHatVisible()).ToJObject("Show", "Apply");
|
||||
ret["Visor"] = new QuadBool(DesignData.IsVisorToggled(), DoApplyVisorToggle()).ToJObject("IsToggled", "Apply");
|
||||
ret["Weapon"] = new QuadBool(DesignData.IsWeaponVisible(), DoApplyWeaponVisible()).ToJObject("Show", "Apply");
|
||||
ret["Array"] = DesignData.WriteEquipmentBytesBase64();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -213,6 +213,7 @@ public class DesignBase
|
|||
["Value"] = DesignData.IsWet(),
|
||||
["Apply"] = DoApplyWetness(),
|
||||
};
|
||||
ret["Array"] = DesignData.Customize.WriteBase64();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -234,8 +235,8 @@ public class DesignBase
|
|||
private static DesignBase LoadDesignV1Base(CustomizationService customizations, ItemManager items, JObject json)
|
||||
{
|
||||
var ret = new DesignBase(items);
|
||||
LoadEquip(items, json["Equipment"], ret, "Temporary Design");
|
||||
LoadCustomize(customizations, json["Customize"], ret, "Temporary Design");
|
||||
LoadEquip(items, json["Equipment"], ret, "Temporary Design");
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -249,6 +250,13 @@ public class DesignBase
|
|||
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)
|
||||
{
|
||||
var id = item?["ItemId"]?.ToObject<uint>() ?? ItemManager.NothingId(slot);
|
||||
|
|
@ -314,6 +322,7 @@ public class DesignBase
|
|||
if (json == null)
|
||||
{
|
||||
design.DesignData.ModelId = 0;
|
||||
design.DesignData.IsHuman = true;
|
||||
design.DesignData.Customize = Customize.Default;
|
||||
Glamourer.Chat.NotificationMessage("The loaded design does not contain any customization data, reset to default.", "Warning",
|
||||
NotificationType.Warning);
|
||||
|
|
@ -326,8 +335,18 @@ public class DesignBase
|
|||
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;
|
||||
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 clan = (SubRace)(json[CustomizeIndex.Clan.ToString()]?["Value"]?.ToObject<byte>() ?? 0);
|
||||
|
|
@ -352,10 +371,6 @@ public class DesignBase
|
|||
design.DesignData.Customize[idx] = data;
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Buffers.Text;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Glamourer.Customization;
|
||||
using Glamourer.Services;
|
||||
|
|
@ -33,6 +34,7 @@ public unsafe struct DesignData
|
|||
private FullEquipType _typeMainhand;
|
||||
private FullEquipType _typeOffhand;
|
||||
private byte _states;
|
||||
public bool IsHuman = true;
|
||||
|
||||
public DesignData()
|
||||
{ }
|
||||
|
|
@ -202,14 +204,49 @@ public unsafe struct DesignData
|
|||
SetStain(EquipSlot.OffHand, 0);
|
||||
}
|
||||
|
||||
public void LoadNonHuman(uint modelId, Customize customize, byte* equipData)
|
||||
|
||||
public bool LoadNonHuman(uint modelId, Customize customize, byte* equipData)
|
||||
{
|
||||
ModelId = modelId;
|
||||
IsHuman = false;
|
||||
Customize.Load(customize);
|
||||
fixed (byte* ptr = _equipmentBytes)
|
||||
{
|
||||
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()
|
||||
|
|
@ -234,6 +271,32 @@ public unsafe struct DesignData
|
|||
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)]
|
||||
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
|
||||
{
|
||||
/// <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>
|
||||
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>
|
||||
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>
|
||||
Other,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,24 @@
|
|||
using System;
|
||||
using System.Numerics;
|
||||
using System.Xml.Linq;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Glamourer.Automation;
|
||||
using Glamourer.Customization;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Events;
|
||||
using Glamourer.Gui.Customization;
|
||||
using Glamourer.Gui.Equipment;
|
||||
using Glamourer.Interop;
|
||||
using Glamourer.Interop.Structs;
|
||||
using Glamourer.Services;
|
||||
using Glamourer.State;
|
||||
using Glamourer.Structs;
|
||||
using ImGuiNET;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using OtterGui;
|
||||
using OtterGui.Raii;
|
||||
using Penumbra.GameData;
|
||||
using Penumbra.GameData.Actors;
|
||||
using Penumbra.GameData.Data;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Glamourer.Gui.Tabs.ActorTab;
|
||||
|
||||
|
|
@ -26,8 +28,11 @@ public class ActorPanel
|
|||
private readonly StateManager _stateManager;
|
||||
private readonly CustomizationDrawer _customizationDrawer;
|
||||
private readonly EquipmentDrawer _equipmentDrawer;
|
||||
private readonly HumanModelList _humans;
|
||||
private readonly IdentifierService _identification;
|
||||
private readonly AutoDesignApplier _autoDesignApplier;
|
||||
private readonly Configuration _config;
|
||||
private readonly DesignConverter _converter;
|
||||
private readonly ObjectManager _objects;
|
||||
|
||||
private ActorIdentifier _identifier;
|
||||
private string _actorName = string.Empty;
|
||||
|
|
@ -36,14 +41,18 @@ public class ActorPanel
|
|||
private ActorState? _state;
|
||||
|
||||
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;
|
||||
_stateManager = stateManager;
|
||||
_customizationDrawer = customizationDrawer;
|
||||
_equipmentDrawer = equipmentDrawer;
|
||||
_humans = humans;
|
||||
_identification = identification;
|
||||
_autoDesignApplier = autoDesignApplier;
|
||||
_config = config;
|
||||
_converter = converter;
|
||||
_objects = objects;
|
||||
}
|
||||
|
||||
public void Draw()
|
||||
|
|
@ -91,6 +100,28 @@ public class ActorPanel
|
|||
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()
|
||||
{
|
||||
if (_customizationDrawer.Draw(_state!.ModelData.Customize, false))
|
||||
|
|
@ -191,113 +222,85 @@ public class ActorPanel
|
|||
_stateManager.TurnHuman(_state, StateChanged.Source.Manual);
|
||||
}
|
||||
|
||||
private unsafe void DrawPanel()
|
||||
private void ApplyClipboardButton()
|
||||
{
|
||||
using var child = ImRaii.Child("##Panel", -Vector2.One, true);
|
||||
if (!child || !_selector.HasSelection || !_stateManager.GetOrCreate(_identifier, _actor, out _state))
|
||||
if (!ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Clipboard.ToIconString(), new Vector2(ImGui.GetFrameHeight()),
|
||||
"Try to apply a design from your clipboard.", false, true))
|
||||
return;
|
||||
|
||||
if (_humans.IsHuman(_state.ModelData.ModelId))
|
||||
DrawHumanPanel();
|
||||
else
|
||||
DrawMonsterPanel();
|
||||
try
|
||||
{
|
||||
var text = ImGui.GetClipboardText();
|
||||
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 unsafe void RevertButton()
|
||||
private void CopyToClipboardButton()
|
||||
{
|
||||
//if (ImGui.Button("Revert"))
|
||||
// _activeDesigns.RevertDesign(_currentSave!);
|
||||
//foreach (var actor in _currentData.Objects)
|
||||
// _currentSave!.ApplyToActor(actor);
|
||||
//
|
||||
//if (_currentData.Objects.Count > 0)
|
||||
// _currentSave = _manipulations.GetOrCreateSave(_currentData.Objects[0]);
|
||||
//
|
||||
//_currentSave!.Reset();
|
||||
//if (_currentData.Objects.Count > 0)
|
||||
// ImGui.TextUnformatted(_currentData.Objects[0].Pointer->GameObject.DataID.ToString());
|
||||
//VisorBox();
|
||||
if (!ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Copy.ToIconString(), new Vector2(ImGui.GetFrameHeight()),
|
||||
"Copy the current design to your clipboard.", false, true))
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
var text = _converter.ShareBase64(_state!);
|
||||
ImGui.SetClipboardText(text);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
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()
|
||||
//{
|
||||
// var (flags, mask) = (_currentSave!.Data.Flags & (ApplicationFlags.SetVisor | ApplicationFlags.Visor)) switch
|
||||
// {
|
||||
// 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);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
private void RevertButtons()
|
||||
{
|
||||
if (ImGui.Button("Revert to Game"))
|
||||
_stateManager.ResetState(_state!);
|
||||
|
||||
ImGui.SameLine();
|
||||
if (ImGui.Button("Reapply State"))
|
||||
_stateManager.ReapplyState(_actor);
|
||||
|
||||
//private void DrawActorPanel()
|
||||
//{
|
||||
// using var group = ImRaii.Group();
|
||||
// if (!_data.Identifier.IsValid)
|
||||
// return;
|
||||
//
|
||||
// if (DrawCustomization(_currentSave.Customize, _currentSave.Equipment, !_data.Modifiable))
|
||||
// //Glamourer.RedrawManager.Set(_data.Actor.Address, _character);
|
||||
// Glamourer.Penumbra.RedrawObject(_data.Actor.Character, RedrawType.Redraw, true);
|
||||
//
|
||||
// if (ImGui.Button("Set Machinist Goggles"))
|
||||
// Glamourer.RedrawManager.ChangeEquip(_data.Actor, EquipSlot.Head, new CharacterArmor(265, 1, 0));
|
||||
//
|
||||
// if (ImGui.Button("Set Weapon"))
|
||||
// Glamourer.RedrawManager.LoadWeapon(_data.Actor.Address, new CharacterWeapon(0x00C9, 0x004E, 0x0001, 0x00),
|
||||
// new CharacterWeapon(0x0065, 0x003D, 0x0001, 0x00));
|
||||
//
|
||||
// if (ImGui.Button("Set Customize"))
|
||||
// {
|
||||
// unsafe
|
||||
// {
|
||||
// var data = _data.Actor.Customize.Data->Clone();
|
||||
// Glamourer.RedrawManager.UpdateCustomize(_data.Actor.DrawObject, new Customize(&data)
|
||||
// {
|
||||
// SkinColor = 154,
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//private void DrawMonsterPanel()
|
||||
//{
|
||||
// using var group = ImRaii.Group();
|
||||
// var currentModel = (uint)_data.Actor.ModelId;
|
||||
// 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);
|
||||
// }
|
||||
//}
|
||||
ImGui.SameLine();
|
||||
if (ImGuiUtil.DrawDisabledButton("Reapply Automation", Vector2.Zero, string.Empty, !_config.EnableAutoDesigns))
|
||||
{
|
||||
_autoDesignApplier.ReapplyAutomation(_actor, _identifier, _state!);
|
||||
_stateManager.ReapplyState(_actor);
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawApplyToSelf()
|
||||
{
|
||||
var (id, data) = _objects.PlayerData;
|
||||
if (!ImGuiUtil.DrawDisabledButton("Apply to Yourself", Vector2.Zero, "Apply the current state to your own character.",
|
||||
!data.Valid || id == _identifier))
|
||||
return;
|
||||
|
||||
if (_stateManager.GetOrCreate(id, data.Objects[0], out var state))
|
||||
_stateManager.ApplyDesign(_converter.Convert(_state!, EquipFlagExtensions.All, CustomizeFlagExtensions.AllRelevant), state,
|
||||
StateChanged.Source.Manual);
|
||||
}
|
||||
|
||||
private void DrawApplyToTarget()
|
||||
{
|
||||
var (id, data) = _objects.TargetData;
|
||||
var tt = id.IsValid
|
||||
? 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;
|
||||
|
||||
if (_stateManager.GetOrCreate(id, data.Objects[0], out var state))
|
||||
_stateManager.ApplyDesign(_converter.Convert(_state!, EquipFlagExtensions.All, CustomizeFlagExtensions.AllRelevant), state,
|
||||
StateChanged.Source.Manual);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -253,7 +253,7 @@ public unsafe class DebugTab : ITab
|
|||
using var id = ImRaii.PushId("Visor");
|
||||
ImGuiUtil.DrawTableColumn("Visor State");
|
||||
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();
|
||||
if (!model.IsHuman)
|
||||
return;
|
||||
|
|
@ -265,7 +265,7 @@ public unsafe class DebugTab : ITab
|
|||
_visorService.SetVisorState(model, false);
|
||||
ImGui.SameLine();
|
||||
if (ImGui.SmallButton("Toggle"))
|
||||
_visorService.SetVisorState(model, !_visorService.GetVisorState(model));
|
||||
_visorService.SetVisorState(model, !VisorService.GetVisorState(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]);
|
||||
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]);
|
||||
ImGui.TableNextRow();
|
||||
|
|
@ -1079,7 +1079,7 @@ public unsafe class DebugTab : ITab
|
|||
|
||||
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);
|
||||
if (!table)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ using Dalamud.Interface.Internal.Notifications;
|
|||
using Glamourer.Automation;
|
||||
using Glamourer.Customization;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Events;
|
||||
using Glamourer.Gui.Customization;
|
||||
using Glamourer.Gui.Equipment;
|
||||
using Glamourer.Interop;
|
||||
|
|
@ -176,7 +177,7 @@ public class DesignPanel
|
|||
{
|
||||
var set = _customizationService.AwaitedService.GetList(_selector.Selected!.DesignData.Customize.Clan,
|
||||
_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;
|
||||
if (ImGui.CheckboxFlags("Apply All Customizations", ref flags, 3))
|
||||
{
|
||||
|
|
@ -342,7 +343,7 @@ public class DesignPanel
|
|||
return;
|
||||
|
||||
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()
|
||||
|
|
@ -357,6 +358,6 @@ public class DesignPanel
|
|||
return;
|
||||
|
||||
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();
|
||||
|
||||
/// <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;
|
||||
|
||||
/// <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.Plugin;
|
||||
using Glamourer.Customization;
|
||||
using Penumbra.GameData.Data;
|
||||
using Penumbra.GameData.Enums;
|
||||
|
||||
namespace Glamourer.Services;
|
||||
|
||||
public sealed class CustomizationService : AsyncServiceWrapper<ICustomizationManager>
|
||||
{
|
||||
public CustomizationService(DalamudPluginInterface pi, DataManager gameData)
|
||||
: base(nameof(CustomizationService), () => CustomizationManager.Create(pi, gameData))
|
||||
{ }
|
||||
public readonly HumanModelList HumanModels;
|
||||
|
||||
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 changed = 0;
|
||||
Customize ret = default;
|
||||
ret.Load(oldValues);
|
||||
if (applyWhich.HasFlag(CustomizeFlag.Clan))
|
||||
{
|
||||
ChangeClan(ref ret, newValues.Clan);
|
||||
changed |= ChangeClan(ref ret, newValues.Clan);
|
||||
applied |= CustomizeFlag.Clan;
|
||||
}
|
||||
|
||||
if (applyWhich.HasFlag(CustomizeFlag.Gender))
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -43,12 +48,14 @@ public sealed class CustomizationService : AsyncServiceWrapper<ICustomizationMan
|
|||
var value = newValues[index];
|
||||
if (IsCustomizationValid(set, ret.Face, index, value))
|
||||
{
|
||||
if (ret[index].Value != value.Value)
|
||||
changed |= flag;
|
||||
ret[index] = value;
|
||||
applied |= flag;
|
||||
}
|
||||
}
|
||||
|
||||
return (ret, applied);
|
||||
return (ret, applied, changed);
|
||||
}
|
||||
|
||||
/// <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 return value is an empty string if everything was correct and a warning otherwise.
|
||||
/// </summary>
|
||||
public string ValidateModelId(uint modelId, out uint actualModelId)
|
||||
public string ValidateModelId(uint modelId, out uint actualModelId, out bool isHuman)
|
||||
{
|
||||
actualModelId = 0;
|
||||
return modelId != 0 ? $"Model IDs different from 0 are not currently allowed, reset {modelId} to 0." : string.Empty;
|
||||
if (modelId >= HumanModels.Count)
|
||||
{
|
||||
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>
|
||||
|
|
|
|||
|
|
@ -100,6 +100,7 @@ public static class ServiceManager
|
|||
|
||||
private static IServiceCollection AddState(this IServiceCollection services)
|
||||
=> services.AddSingleton<StateManager>()
|
||||
.AddSingleton<StateApplier>()
|
||||
.AddSingleton<StateEditor>()
|
||||
.AddSingleton<StateListener>()
|
||||
.AddSingleton<FunModule>();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
using System;
|
||||
using Glamourer.Customization;
|
||||
using Glamourer.Customization;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Events;
|
||||
using Glamourer.Structs;
|
||||
|
|
@ -32,6 +31,37 @@ public class ActorState
|
|||
/// <summary> The last seen job. </summary>
|
||||
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>
|
||||
private readonly StateChanged.Source[] _sources = Enumerable
|
||||
.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.Interop;
|
||||
using Glamourer.Interop.Penumbra;
|
||||
using Glamourer.Interop.Structs;
|
||||
using Glamourer.Events;
|
||||
using Glamourer.Services;
|
||||
using Penumbra.Api.Enums;
|
||||
using Penumbra.GameData.Data;
|
||||
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 StateEditor
|
||||
{
|
||||
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 ItemManager _items;
|
||||
private readonly CustomizationService _customizations;
|
||||
private readonly HumanModelList _humans;
|
||||
|
||||
public StateEditor(UpdateSlotService updateSlot, VisorService visor, WeaponService weapon, ChangeCustomizeService changeCustomize,
|
||||
ItemManager items, PenumbraService penumbra, MetaService metaService)
|
||||
public StateEditor(CustomizationService customizations, HumanModelList humans, ItemManager items)
|
||||
{
|
||||
_updateSlot = updateSlot;
|
||||
_visor = visor;
|
||||
_weapon = weapon;
|
||||
_changeCustomize = changeCustomize;
|
||||
_items = items;
|
||||
_penumbra = penumbra;
|
||||
_metaService = metaService;
|
||||
_customizations = customizations;
|
||||
_humans = humans;
|
||||
_items = items;
|
||||
}
|
||||
|
||||
/// <summary> Changing the model ID simply requires guaranteed redrawing. </summary>
|
||||
public void ChangeModelId(ActorData data, uint modelId)
|
||||
/// <summary> Change the model id. If the actor is changed from a human to another human, customize and equipData are unused. </summary>
|
||||
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)
|
||||
_penumbra.RedrawObject(actor, RedrawType.Redraw);
|
||||
}
|
||||
oldModelId = state.ModelData.ModelId;
|
||||
if (!state.CanUnlock(key))
|
||||
return false;
|
||||
|
||||
/// <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, Customize customize)
|
||||
{
|
||||
foreach (var actor in data.Objects)
|
||||
var oldIsHuman = state.ModelData.IsHuman;
|
||||
state.ModelData.IsHuman = _humans.IsHuman(modelId);
|
||||
if (state.ModelData.IsHuman)
|
||||
{
|
||||
var mdl = actor.Model;
|
||||
if (!mdl.IsHuman)
|
||||
continue;
|
||||
if (oldModelId == modelId)
|
||||
return true;
|
||||
|
||||
var flags = Customize.Compare(mdl.GetCustomize(), customize);
|
||||
if (!flags.RequiresRedraw())
|
||||
_changeCustomize.UpdateCustomize(mdl, customize.Data);
|
||||
else
|
||||
_penumbra.RedrawObject(actor, RedrawType.Redraw);
|
||||
state.ModelData.ModelId = modelId;
|
||||
if (oldIsHuman)
|
||||
return true;
|
||||
|
||||
// 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
|
||||
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;
|
||||
if (!mdl.IsHuman)
|
||||
continue;
|
||||
|
||||
_visor.SetVisorState(mdl, value);
|
||||
unsafe
|
||||
{
|
||||
state.ModelData.LoadNonHuman(modelId, customize, (byte*)equipData);
|
||||
state[ActorState.MetaIndex.ModelId] = source;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary> Change the forced wetness state on actors. </summary>
|
||||
public unsafe void ChangeWetness(ActorData data, bool value)
|
||||
/// <summary> Change a customization value. </summary>
|
||||
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))
|
||||
actor.AsCharacter->IsGPoseWet = value;
|
||||
old = state.ModelData.Customize[idx];
|
||||
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>
|
||||
public unsafe void ChangeHatState(ActorData data, bool value)
|
||||
/// <summary> Change an entire customization array according to flags. </summary>
|
||||
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))
|
||||
_metaService.SetHatState(actor, value);
|
||||
old = state.ModelData.Customize;
|
||||
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>
|
||||
public unsafe void ChangeWeaponState(ActorData data, bool value)
|
||||
/// <summary> Change a single piece of equipment without stain. </summary>
|
||||
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))
|
||||
_metaService.SetWeaponState(actor, value);
|
||||
oldItem = state.ModelData.Item(slot);
|
||||
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.Services;
|
||||
using OtterGui.Classes;
|
||||
using Penumbra.GameData.Data;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
|
|
@ -30,6 +31,7 @@ public class StateListener : IDisposable
|
|||
private readonly WeaponVisibilityChanged _weaponVisibility;
|
||||
private readonly AutoDesignApplier _autoDesignApplier;
|
||||
private readonly FunModule _funModule;
|
||||
private readonly HumanModelList _humans;
|
||||
|
||||
public bool Enabled
|
||||
{
|
||||
|
|
@ -39,7 +41,7 @@ public class StateListener : IDisposable
|
|||
|
||||
public StateListener(StateManager manager, ItemManager items, PenumbraService penumbra, ActorService actors, Configuration config,
|
||||
SlotUpdating slotUpdating, WeaponLoading weaponLoading, VisorStateChanged visorState, WeaponVisibilityChanged weaponVisibility,
|
||||
HeadGearVisibilityChanged headGearVisibility, AutoDesignApplier autoDesignApplier, FunModule funModule)
|
||||
HeadGearVisibilityChanged headGearVisibility, AutoDesignApplier autoDesignApplier, FunModule funModule, HumanModelList humans)
|
||||
{
|
||||
_manager = manager;
|
||||
_items = items;
|
||||
|
|
@ -53,6 +55,7 @@ public class StateListener : IDisposable
|
|||
_headGearVisibility = headGearVisibility;
|
||||
_autoDesignApplier = autoDesignApplier;
|
||||
_funModule = funModule;
|
||||
_humans = humans;
|
||||
|
||||
if (Enabled)
|
||||
Subscribe();
|
||||
|
|
@ -320,10 +323,11 @@ public class StateListener : IDisposable
|
|||
|
||||
// Model ID did change, reload entire state accordingly.
|
||||
// Always use the actor for the base data.
|
||||
if (modelId == 0)
|
||||
state.BaseData.LoadNonHuman(modelId, *(Customize*)customizeData, (byte*)equipData);
|
||||
else
|
||||
var isHuman = _humans.IsHuman(modelId);
|
||||
if (isHuman)
|
||||
state.BaseData = _manager.FromActor(actor, false);
|
||||
else
|
||||
state.BaseData.LoadNonHuman(modelId, *(Customize*)customizeData, (byte*)equipData);
|
||||
|
||||
return UpdateState.Change;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ using Glamourer.Interop;
|
|||
using Glamourer.Interop.Structs;
|
||||
using Glamourer.Services;
|
||||
using Penumbra.GameData.Actors;
|
||||
using Penumbra.GameData.Data;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
|
|
@ -17,26 +18,24 @@ namespace Glamourer.State;
|
|||
|
||||
public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
|
||||
{
|
||||
private readonly ActorService _actors;
|
||||
private readonly ItemManager _items;
|
||||
private readonly CustomizationService _customizations;
|
||||
private readonly VisorService _visor;
|
||||
private readonly StateChanged _event;
|
||||
private readonly ObjectManager _objects;
|
||||
private readonly StateEditor _editor;
|
||||
private readonly ActorService _actors;
|
||||
private readonly ItemManager _items;
|
||||
private readonly HumanModelList _humans;
|
||||
private readonly StateChanged _event;
|
||||
private readonly StateApplier _applier;
|
||||
private readonly StateEditor _editor;
|
||||
|
||||
private readonly Dictionary<ActorIdentifier, ActorState> _states = new();
|
||||
|
||||
public StateManager(ActorService actors, ItemManager items, CustomizationService customizations, VisorService visor, StateChanged @event,
|
||||
ObjectManager objects, StateEditor editor)
|
||||
public StateManager(ActorService actors, ItemManager items, StateChanged @event, StateApplier applier, StateEditor editor,
|
||||
HumanModelList humans)
|
||||
{
|
||||
_actors = actors;
|
||||
_items = items;
|
||||
_customizations = customizations;
|
||||
_visor = visor;
|
||||
_event = @event;
|
||||
_objects = objects;
|
||||
_editor = editor;
|
||||
_actors = actors;
|
||||
_items = items;
|
||||
_event = @event;
|
||||
_applier = applier;
|
||||
_editor = editor;
|
||||
_humans = humans;
|
||||
}
|
||||
|
||||
public IEnumerator<KeyValuePair<ActorIdentifier, ActorState>> GetEnumerator()
|
||||
|
|
@ -109,17 +108,18 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
|
|||
return ret;
|
||||
}
|
||||
|
||||
var model = actor.Model;
|
||||
|
||||
// Model ID is only unambiguously contained in the game object.
|
||||
// The draw object only has the object type.
|
||||
// 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,
|
||||
(byte*)&actor.AsCharacter->DrawData.Head);
|
||||
return ret;
|
||||
}
|
||||
|
||||
var model = actor.Model;
|
||||
CharacterWeapon main;
|
||||
CharacterWeapon off;
|
||||
|
||||
|
|
@ -152,7 +152,7 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
|
|||
(_, _, 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.
|
||||
ret.SetVisor(_visor.GetVisorState(model));
|
||||
ret.SetVisor(VisorService.GetVisorState(model));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -192,304 +192,228 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
|
|||
|
||||
#region Change Values
|
||||
|
||||
/// <summary> Turn a non-human actor human. </summary>
|
||||
public void TurnHuman(ActorState state, StateChanged.Source source)
|
||||
/// <summary> Turn an actor human. </summary>
|
||||
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;
|
||||
|
||||
state.ModelData.ModelId = 0;
|
||||
state[ActorState.MetaIndex.ModelId] = source;
|
||||
ChangeCustomize(state, Customize.Default, CustomizeFlagExtensions.All, source);
|
||||
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
||||
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);
|
||||
var actors = _applier.ForceRedraw(state, source is StateChanged.Source.Manual or StateChanged.Source.Ipc);
|
||||
Glamourer.Log.Verbose(
|
||||
$"Set model id in state {state.Identifier.Incognito(null)} from {old} to {modelId}. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||
_event.Invoke(StateChanged.Type.Model, source, state, actors, (old, modelId));
|
||||
}
|
||||
|
||||
/// <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.
|
||||
var old = state.ModelData.Customize[idx];
|
||||
state.ModelData.Customize[idx] = value;
|
||||
state[idx] = source;
|
||||
if (!_editor.ChangeCustomize(state, idx, value, source, out var old, key))
|
||||
return;
|
||||
|
||||
// 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.
|
||||
var actors = _applier.ChangeCustomize(state, source is StateChanged.Source.Manual or StateChanged.Source.Ipc);
|
||||
Glamourer.Log.Verbose(
|
||||
$"Set {idx.ToDefaultName()} customizations in state {state.Identifier} from {old.Value} to {value.Value}. [Affecting {objects.ToLazyString("nothing")}.]");
|
||||
_event.Invoke(StateChanged.Type.Customize, source, state, objects, (old, value, idx));
|
||||
$"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, actors, (old, value, idx));
|
||||
}
|
||||
|
||||
/// <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.
|
||||
var old = state.ModelData.Customize;
|
||||
var (customize, applied) = _customizations.Combine(state.ModelData.Customize, customizeInput, apply);
|
||||
if (applied == 0)
|
||||
if (!_editor.ChangeHumanCustomize(state, customizeInput, apply, source, out var old, out var applied, key))
|
||||
return;
|
||||
|
||||
state.ModelData.Customize = customize;
|
||||
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.
|
||||
var actors = _applier.ChangeCustomize(state, source is StateChanged.Source.Manual or StateChanged.Source.Ipc);
|
||||
Glamourer.Log.Verbose(
|
||||
$"Set {applied} customizations in state {state.Identifier} from {old} to {customize}. [Affecting {objects.ToLazyString("nothing")}.]");
|
||||
_event.Invoke(StateChanged.Type.Customize, source, state, objects, (old, customize, applied));
|
||||
$"Set {applied} customizations in state {state.Identifier.Incognito(null)} from {old} to {customizeInput}. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||
_event.Invoke(StateChanged.Type.EntireCustomize, source, state, actors, (old, applied));
|
||||
}
|
||||
|
||||
/// <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>
|
||||
public void ChangeItem(ActorState state, EquipSlot slot, EquipItem item, StateChanged.Source source)
|
||||
/// <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, uint key = 0)
|
||||
{
|
||||
// Update state data.
|
||||
var old = state.ModelData.Item(slot);
|
||||
state.ModelData.SetItem(slot, item);
|
||||
state[slot, false] = source;
|
||||
var type = slot is EquipSlot.MainHand or EquipSlot.OffHand ? StateChanged.Type.Weapon : StateChanged.Type.Equip;
|
||||
if (!_editor.ChangeItem(state, slot, item, source, out var old, key))
|
||||
return;
|
||||
|
||||
// Update draw objects.
|
||||
_objects.Update();
|
||||
var objects = _objects.TryGetValue(state.Identifier, out var d) ? d : ActorData.Invalid;
|
||||
if (source is StateChanged.Source.Manual)
|
||||
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.
|
||||
var type = slot.IsEquipmentPiece() ? StateChanged.Type.Equip : StateChanged.Type.Weapon;
|
||||
var actors = type is StateChanged.Type.Equip
|
||||
? _applier.ChangeArmor(state, slot, source is StateChanged.Source.Manual or StateChanged.Source.Ipc)
|
||||
: _applier.ChangeWeapon(state, slot, source is StateChanged.Source.Manual or StateChanged.Source.Ipc);
|
||||
Glamourer.Log.Verbose(
|
||||
$"Set {slot.ToName()} in state {state.Identifier} from {old.Name} ({old.Id}) to {item.Name} ({item.Id}). [Affecting {objects.ToLazyString("nothing")}.]");
|
||||
_event.Invoke(type, source, state, objects, (old, item, slot));
|
||||
$"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, actors, (old, item, slot));
|
||||
}
|
||||
|
||||
/// <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.
|
||||
var old = state.ModelData.Item(slot);
|
||||
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;
|
||||
if (!_editor.ChangeEquip(state, slot, item, stain, source, out var old, out var oldStain, key))
|
||||
return;
|
||||
|
||||
// Update draw objects.
|
||||
_objects.Update();
|
||||
var objects = _objects.TryGetValue(state.Identifier, out var d) ? d : ActorData.Invalid;
|
||||
if (source is StateChanged.Source.Manual)
|
||||
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.
|
||||
var type = slot.IsEquipmentPiece() ? StateChanged.Type.Equip : StateChanged.Type.Weapon;
|
||||
var actors = type is StateChanged.Type.Equip
|
||||
? _applier.ChangeArmor(state, slot, source is StateChanged.Source.Manual or StateChanged.Source.Ipc)
|
||||
: _applier.ChangeWeapon(state, slot, source is StateChanged.Source.Manual or StateChanged.Source.Ipc);
|
||||
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")}.]");
|
||||
_event.Invoke(type, source, state, objects, (old, item, slot));
|
||||
_event.Invoke(StateChanged.Type.Stain, source, state, objects, (oldStain, stain, slot));
|
||||
$"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, actors, (old, item, slot));
|
||||
_event.Invoke(StateChanged.Type.Stain, source, state, actors, (oldStain, stain, slot));
|
||||
}
|
||||
|
||||
/// <summary> Change only the stain of an equipment piece. </summary>
|
||||
/// <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)
|
||||
/// <remarks> Do not use this in the same frame as ChangeEquip, use <see cref="ChangeEquip(ActorState,EquipSlot,EquipItem,StainId,StateChanged.Source,uint)"/> instead. </remarks>
|
||||
public void ChangeStain(ActorState state, EquipSlot slot, StainId stain, StateChanged.Source source, uint key = 0)
|
||||
{
|
||||
// Update state data.
|
||||
var old = state.ModelData.Stain(slot);
|
||||
state.ModelData.SetStain(slot, stain);
|
||||
state[slot, true] = source;
|
||||
if (!_editor.ChangeStain(state, slot, stain, source, out var old, key))
|
||||
return;
|
||||
|
||||
// Update draw objects.
|
||||
_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.
|
||||
var actors = _applier.ChangeStain(state, slot, source is StateChanged.Source.Manual or StateChanged.Source.Ipc);
|
||||
Glamourer.Log.Verbose(
|
||||
$"Set {slot.ToName()} stain in state {state.Identifier} from {old.Value} to {stain.Value}. [Affecting {objects.ToLazyString("nothing")}.]");
|
||||
_event.Invoke(StateChanged.Type.Stain, source, state, objects, (old, stain, slot));
|
||||
$"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, actors, (old, stain, slot));
|
||||
}
|
||||
|
||||
/// <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.
|
||||
var old = state.ModelData.IsHatVisible();
|
||||
state.ModelData.SetHatVisible(value);
|
||||
state[ActorState.MetaIndex.HatState] = source;
|
||||
if (!_editor.ChangeMetaState(state, ActorState.MetaIndex.HatState, value, source, out var old, key))
|
||||
return;
|
||||
|
||||
// Update draw objects / game objects.
|
||||
_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.
|
||||
var actors = _applier.ChangeHatState(state, source is StateChanged.Source.Manual or StateChanged.Source.Ipc);
|
||||
Glamourer.Log.Verbose(
|
||||
$"Set Head Gear Visibility in state {state.Identifier} from {old} to {value}. [Affecting {objects.ToLazyString("nothing")}.]");
|
||||
_event.Invoke(StateChanged.Type.Other, source, state, objects, (old, value, ActorState.MetaIndex.HatState));
|
||||
$"Set Head Gear Visibility in state {state.Identifier} from {old} to {value}. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||
_event.Invoke(StateChanged.Type.Other, source, state, actors, (old, value, ActorState.MetaIndex.HatState));
|
||||
}
|
||||
|
||||
/// <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.
|
||||
var old = state.ModelData.IsWeaponVisible();
|
||||
state.ModelData.SetWeaponVisible(value);
|
||||
state[ActorState.MetaIndex.WeaponState] = source;
|
||||
if (!_editor.ChangeMetaState(state, ActorState.MetaIndex.WeaponState, value, source, out var old, key))
|
||||
return;
|
||||
|
||||
// Update draw objects / game objects.
|
||||
_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.
|
||||
var actors = _applier.ChangeWeaponState(state, source is StateChanged.Source.Manual or StateChanged.Source.Ipc);
|
||||
Glamourer.Log.Verbose(
|
||||
$"Set Weapon Visibility in state {state.Identifier} from {old} to {value}. [Affecting {objects.ToLazyString("nothing")}.]");
|
||||
_event.Invoke(StateChanged.Type.Other, source, state, objects, (old, value, ActorState.MetaIndex.WeaponState));
|
||||
$"Set Weapon Visibility in state {state.Identifier} from {old} to {value}. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||
_event.Invoke(StateChanged.Type.Other, source, state, actors, (old, value, ActorState.MetaIndex.WeaponState));
|
||||
}
|
||||
|
||||
/// <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.
|
||||
var old = state.ModelData.IsVisorToggled();
|
||||
state.ModelData.SetVisor(value);
|
||||
state[ActorState.MetaIndex.VisorState] = source;
|
||||
if (!_editor.ChangeMetaState(state, ActorState.MetaIndex.VisorState, value, source, out var old, key))
|
||||
return;
|
||||
|
||||
// Update draw objects.
|
||||
_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.
|
||||
var actors = _applier.ChangeVisor(state, source is StateChanged.Source.Manual or StateChanged.Source.Ipc);
|
||||
Glamourer.Log.Verbose(
|
||||
$"Set Visor State in state {state.Identifier} from {old} to {value}. [Affecting {objects.ToLazyString("nothing")}.]");
|
||||
_event.Invoke(StateChanged.Type.Other, source, state, objects, (old, value, ActorState.MetaIndex.VisorState));
|
||||
$"Set Visor State in state {state.Identifier} from {old} to {value}. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||
_event.Invoke(StateChanged.Type.Other, source, state, actors, (old, value, ActorState.MetaIndex.VisorState));
|
||||
}
|
||||
|
||||
/// <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.
|
||||
var old = state.ModelData.IsWet();
|
||||
state.ModelData.SetIsWet(value);
|
||||
state[ActorState.MetaIndex.Wetness] = source;
|
||||
if (!_editor.ChangeMetaState(state, ActorState.MetaIndex.Wetness, value, source, out var old, key))
|
||||
return;
|
||||
|
||||
// Update draw objects / game objects.
|
||||
_objects.Update();
|
||||
var objects = _objects.TryGetValue(state.Identifier, out var d) ? d : ActorData.Invalid;
|
||||
_editor.ChangeWetness(objects, value);
|
||||
|
||||
// Meta.
|
||||
var actors = _applier.ChangeVisor(state, true);
|
||||
Glamourer.Log.Verbose(
|
||||
$"Set Wetness in state {state.Identifier} from {old} to {value}. [Affecting {objects.ToLazyString("nothing")}.]");
|
||||
_event.Invoke(StateChanged.Type.Other, state[ActorState.MetaIndex.Wetness], state, objects, (old, value, ActorState.MetaIndex.Wetness));
|
||||
$"Set Wetness in state {state.Identifier} from {old} to {value}. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||
_event.Invoke(StateChanged.Type.Other, state[ActorState.MetaIndex.Wetness], state, actors, (old, value, ActorState.MetaIndex.Wetness));
|
||||
}
|
||||
|
||||
#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)
|
||||
{
|
||||
switch (applyPiece, applyStain)
|
||||
var unused = (applyPiece, applyStain) switch
|
||||
{
|
||||
case (false, false): break;
|
||||
case (true, false):
|
||||
ChangeItem(state, slot, design.DesignData.Item(slot), StateChanged.Source.Manual);
|
||||
break;
|
||||
case (false, true):
|
||||
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;
|
||||
}
|
||||
(false, false) => false,
|
||||
(true, false) => _editor.ChangeItem(state, slot, design.DesignData.Item(slot), source, out _, key),
|
||||
(false, true) => _editor.ChangeStain(state, slot, design.DesignData.Stain(slot), source, out _, key),
|
||||
(true, true) => _editor.ChangeEquip(state, slot, design.DesignData.Item(slot), design.DesignData.Stain(slot), source, out _,
|
||||
out _, key),
|
||||
};
|
||||
}
|
||||
|
||||
if (state.ModelData.ModelId != 0 && design.DesignData.ModelId == 0)
|
||||
TurnHuman(state, StateChanged.Source.Manual);
|
||||
if (!_editor.ChangeModelId(state, design.DesignData.ModelId, design.DesignData.Customize, design.DesignData.GetEquipmentPtr(), source,
|
||||
out var oldModelId, key))
|
||||
return;
|
||||
|
||||
if (design.DoApplyHatVisible())
|
||||
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);
|
||||
var redraw = oldModelId != design.DesignData.ModelId || !design.DesignData.IsHuman;
|
||||
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)
|
||||
HandleEquip(slot, design.DoApplyEquip(slot), design.DoApplyStain(slot));
|
||||
_editor.ChangeHumanCustomize(state, design.DesignData.Customize, design.ApplyCustomize, source, out _, out var applied, key);
|
||||
redraw |= applied.RequiresRedraw();
|
||||
|
||||
HandleEquip(EquipSlot.MainHand,
|
||||
design.DoApplyEquip(EquipSlot.MainHand)
|
||||
&& design.DesignData.Item(EquipSlot.MainHand).Type == state.BaseData.Item(EquipSlot.MainHand).Type,
|
||||
design.DoApplyStain(EquipSlot.MainHand));
|
||||
HandleEquip(EquipSlot.OffHand,
|
||||
design.DoApplyEquip(EquipSlot.OffHand)
|
||||
&& design.DesignData.Item(EquipSlot.OffHand).Type == state.BaseData.Item(EquipSlot.OffHand).Type,
|
||||
design.DoApplyStain(EquipSlot.OffHand));
|
||||
foreach (var slot in EquipSlotExtensions.FullSlots)
|
||||
HandleEquip(slot, design.DoApplyEquip(slot), design.DoApplyStain(slot));
|
||||
}
|
||||
|
||||
var actors = ApplyAll(state, redraw);
|
||||
Glamourer.Log.Verbose(
|
||||
$"Applied design to {state.Identifier.Incognito(null)}. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||
_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)
|
||||
{
|
||||
ChangeHatState(state, state.BaseData.IsHatVisible(), StateChanged.Source.Game);
|
||||
ChangeVisorState(state, state.BaseData.IsVisorToggled(), StateChanged.Source.Game);
|
||||
ChangeWeaponState(state, state.BaseData.IsWeaponVisible(), StateChanged.Source.Game);
|
||||
ChangeWetness(state, false, StateChanged.Source.Game);
|
||||
ChangeCustomize(state, state.BaseData.Customize, CustomizeFlagExtensions.All, StateChanged.Source.Game);
|
||||
var redraw = state.ModelData.ModelId != state.BaseData.ModelId || !state.ModelData.IsHuman
|
||||
|| Customize.Compare(state.ModelData.Customize, state.BaseData.Customize).RequiresRedraw();
|
||||
state.ModelData = state.BaseData;
|
||||
foreach (var index in Enum.GetValues<CustomizeIndex>())
|
||||
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)
|
||||
ChangeEquip(state, slot, state.BaseData.Item(slot), state.BaseData.Stain(slot), StateChanged.Source.Game);
|
||||
foreach (var type in Enum.GetValues<ActorState.MetaIndex>())
|
||||
state[type] = StateChanged.Source.Game;
|
||||
|
||||
ChangeEquip(state, EquipSlot.MainHand, state.BaseData.Item(EquipSlot.MainHand), state.BaseData.Stain(EquipSlot.MainHand),
|
||||
StateChanged.Source.Game);
|
||||
ChangeEquip(state, EquipSlot.OffHand, state.BaseData.Item(EquipSlot.OffHand), state.BaseData.Stain(EquipSlot.OffHand),
|
||||
StateChanged.Source.Game);
|
||||
|
||||
_objects.Update();
|
||||
var objects = _objects.TryGetValue(state.Identifier, out var d) ? d : ActorData.Invalid;
|
||||
foreach (var actor in objects.Objects)
|
||||
ReapplyState(actor);
|
||||
var actors = ApplyAll(state, redraw);
|
||||
Glamourer.Log.Verbose(
|
||||
$"Reset entire state of {state.Identifier.Incognito(null)} to game base. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||
_event.Invoke(StateChanged.Type.Design, state[ActorState.MetaIndex.Wetness], state, actors, null);
|
||||
}
|
||||
|
||||
public void ReapplyState(Actor actor)
|
||||
|
|
@ -497,28 +421,7 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
|
|||
if (!GetOrCreate(actor, out var state))
|
||||
return;
|
||||
|
||||
var mdl = actor.Model;
|
||||
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));
|
||||
ApplyAll(state, !actor.Model.IsHuman || Customize.Compare(actor.Model.GetCustomize(), state.ModelData.Customize).RequiresRedraw());
|
||||
}
|
||||
|
||||
public void DeleteState(ActorIdentifier identifier)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue