Revamp, temp state.

This commit is contained in:
Ottermandias 2023-12-02 16:33:01 +01:00
parent 358e33346f
commit cc09cced61
22 changed files with 365 additions and 298 deletions

View file

@ -1,13 +1,12 @@
using System.Linq;
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using Glamourer.Customization;
using Glamourer.Events;
using Glamourer.Interop;
using Glamourer.Interop.Penumbra;
using Glamourer.Interop.Structs;
using Glamourer.Services;
using Glamourer.Structs;
using Penumbra.Api.Enums;
using Penumbra.GameData.Actors;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
@ -17,30 +16,9 @@ namespace Glamourer.State;
/// 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
public class StateApplier(UpdateSlotService _updateSlot, VisorService _visor, WeaponService _weapon, ChangeCustomizeService _changeCustomize,
ItemManager _items, PenumbraService _penumbra, MetaService _metaService, ObjectManager _objects, CrestService _crests)
{
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)
{
@ -279,6 +257,22 @@ public class StateApplier
return data;
}
/// <summary> Change the crest state on actors. </summary>
public void ChangeCrests(ActorData data, CrestFlag flags)
{
foreach (var actor in data.Objects.Where(a => a.IsCharacter))
_crests.UpdateCrests(actor, flags);
}
/// <inheritdoc cref="ChangeCrests(ActorData, CrestFlag)"/>
public ActorData ChangeCrests(ActorState state, bool apply)
{
var data = GetData(state);
if (apply)
ChangeCrests(data, state.ModelData.CrestVisibility);
return data;
}
private ActorData GetData(ActorState state)
{
_objects.Update();

View file

@ -4,6 +4,7 @@ using Dalamud.Plugin.Services;
using Glamourer.Customization;
using Glamourer.Events;
using Glamourer.Services;
using Glamourer.Structs;
using Penumbra.GameData.Data;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
@ -197,6 +198,18 @@ public class StateEditor
return true;
}
/// <summary> Change the crest of an equipment piece. </summary>
public bool ChangeCrest(ActorState state, CrestFlag slot, bool crest, StateChanged.Source source, out bool oldCrest, uint key = 0)
{
oldCrest = state.ModelData.Crest(slot);
if (!state.CanUnlock(key))
return false;
state.ModelData.SetCrest(slot, crest);
state[slot] = source;
return true;
}
public bool ChangeMetaState(ActorState state, ActorState.MetaIndex index, bool value, StateChanged.Source source, out bool oldValue,
uint key = 0)
{

View file

@ -13,6 +13,7 @@ using Penumbra.GameData.Structs;
using System;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Plugin.Services;
using Glamourer.Structs;
namespace Glamourer.State;
@ -42,6 +43,7 @@ public class StateListener : IDisposable
private readonly MovedEquipment _movedEquipment;
private readonly GPoseService _gPose;
private readonly ChangeCustomizeService _changeCustomizeService;
private readonly CrestService _crestService;
private readonly ICondition _condition;
private ActorIdentifier _creatingIdentifier = ActorIdentifier.Invalid;
@ -52,7 +54,7 @@ public class StateListener : IDisposable
SlotUpdating slotUpdating, WeaponLoading weaponLoading, VisorStateChanged visorState, WeaponVisibilityChanged weaponVisibility,
HeadGearVisibilityChanged headGearVisibility, AutoDesignApplier autoDesignApplier, FunModule funModule, HumanModelList humans,
StateApplier applier, MovedEquipment movedEquipment, ObjectManager objects, GPoseService gPose,
ChangeCustomizeService changeCustomizeService, CustomizationService customizations, ICondition condition)
ChangeCustomizeService changeCustomizeService, CustomizationService customizations, ICondition condition, CrestService crestService)
{
_manager = manager;
_items = items;
@ -74,6 +76,7 @@ public class StateListener : IDisposable
_changeCustomizeService = changeCustomizeService;
_customizations = customizations;
_condition = condition;
_crestService = crestService;
Subscribe();
}
@ -405,6 +408,58 @@ public class StateListener : IDisposable
}
}
private void OnCrestChange(Actor actor, CrestFlag slot, Ref<bool> value)
{
if (_condition[ConditionFlag.CreatingCharacter] && actor.Index >= ObjectIndex.CutsceneStart)
return;
if (!actor.Identifier(_actors.AwaitedService, out var identifier)
|| !_manager.TryGetValue(identifier, out var state))
return;
switch (UpdateBaseCrest(actor, state, slot, value.Value))
{
case UpdateState.Change:
if (state[slot] is not StateChanged.Source.Fixed and not StateChanged.Source.Ipc)
_manager.ChangeCrest(state, slot, state.BaseData.Crest(slot), StateChanged.Source.Game);
else
value.Value = state.ModelData.Crest(slot);
break;
case UpdateState.NoChange:
case UpdateState.HatHack:
value.Value = state.ModelData.Crest(slot);
break;
case UpdateState.Transformed: break;
}
}
private void OnModelCrestSetup(Model model, CrestFlag slot, ref bool value)
{
var actor = _penumbra.GameObjectFromDrawObject(model);
if (_condition[ConditionFlag.CreatingCharacter] && actor.Index >= ObjectIndex.CutsceneStart)
return;
if (!actor.Identifier(_actors.AwaitedService, out var identifier)
|| !_manager.TryGetValue(identifier, out var state))
return;
value = state.ModelData.Crest(slot);
}
private static UpdateState UpdateBaseCrest(Actor actor, ActorState state, CrestFlag slot, bool visible)
{
if (actor.IsTransformed)
return UpdateState.Transformed;
if (state.BaseData.Crest(slot) != visible)
{
state.BaseData.SetCrest(slot, visible);
return UpdateState.Change;
}
return UpdateState.NoChange;
}
/// <summary> Update base data for a single changed weapon slot. </summary>
private unsafe UpdateState UpdateBaseData(Actor actor, ActorState state, EquipSlot slot, CharacterWeapon weapon)
{
@ -616,6 +671,8 @@ public class StateListener : IDisposable
_headGearVisibility.Subscribe(OnHeadGearVisibilityChange, HeadGearVisibilityChanged.Priority.StateListener);
_weaponVisibility.Subscribe(OnWeaponVisibilityChange, WeaponVisibilityChanged.Priority.StateListener);
_changeCustomizeService.Subscribe(OnCustomizeChange, ChangeCustomizeService.Priority.StateListener);
_crestService.Subscribe(OnCrestChange, CrestService.Priority.StateListener);
_crestService.ModelCrestSetup += OnModelCrestSetup;
}
private void Unsubscribe()
@ -629,6 +686,8 @@ public class StateListener : IDisposable
_headGearVisibility.Unsubscribe(OnHeadGearVisibilityChange);
_weaponVisibility.Unsubscribe(OnWeaponVisibilityChange);
_changeCustomizeService.Unsubscribe(OnCustomizeChange);
_crestService.Unsubscribe(OnCrestChange);
_crestService.ModelCrestSetup -= OnModelCrestSetup;
}
private void OnCreatedCharacterBase(nint gameObject, string _, nint drawObject)
@ -639,8 +698,9 @@ public class StateListener : IDisposable
if (_creatingState == null)
return;
_applier.ChangeHatState(new ActorData(gameObject, _creatingIdentifier.ToName()), _creatingState.ModelData.IsHatVisible());
_applier.ChangeWeaponState(new ActorData(gameObject, _creatingIdentifier.ToName()), _creatingState.ModelData.IsWeaponVisible());
_applier.ChangeWetness(new ActorData(gameObject, _creatingIdentifier.ToName()), _creatingState.ModelData.IsWet());
var data = new ActorData(gameObject, _creatingIdentifier.ToName());
_applier.ChangeHatState(data, _creatingState.ModelData.IsHatVisible());
_applier.ChangeWeaponState(data, _creatingState.ModelData.IsWeaponVisible());
_applier.ChangeWetness(data, _creatingState.ModelData.IsWet());
}
}

View file

@ -10,6 +10,7 @@ using Glamourer.Events;
using Glamourer.Interop;
using Glamourer.Interop.Structs;
using Glamourer.Services;
using Glamourer.Structs;
using Penumbra.GameData.Actors;
using Penumbra.GameData.Data;
using Penumbra.GameData.Enums;
@ -17,32 +18,12 @@ using Penumbra.GameData.Structs;
namespace Glamourer.State;
public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
public class StateManager(ActorService _actors, ItemManager _items, StateChanged _event, StateApplier _applier, StateEditor _editor,
HumanModelList _humans, ICondition _condition, IClientState _clientState)
: IReadOnlyDictionary<ActorIdentifier, ActorState>
{
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 ICondition _condition;
private readonly IClientState _clientState;
private readonly Dictionary<ActorIdentifier, ActorState> _states = new();
public StateManager(ActorService actors, ItemManager items, StateChanged @event, StateApplier applier, StateEditor editor,
HumanModelList humans, ICondition condition, IClientState clientState)
{
_actors = actors;
_items = items;
_event = @event;
_applier = applier;
_editor = editor;
_humans = humans;
_condition = condition;
_clientState = clientState;
}
public IEnumerator<KeyValuePair<ActorIdentifier, ActorState>> GetEnumerator()
=> _states.GetEnumerator();
@ -83,7 +64,7 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
// and the draw objects data for the model data (where possible).
state = new ActorState(identifier)
{
ModelData = FromActor(actor, true, false),
ModelData = FromActor(actor, true, false),
BaseData = FromActor(actor, false, false),
LastJob = (byte)(actor.IsCharacter ? actor.AsCharacter->CharacterData.ClassJob : 0),
LastTerritory = _clientState.TerritoryType,
@ -162,6 +143,9 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
// Visor state is a flag on the game object, but we can see the actual state on the draw object.
ret.SetVisor(VisorService.GetVisorState(model));
foreach (var slot in CrestExtensions.AllRelevantSet)
ret.SetCrest(slot, CrestService.GetModelCrest(actor, slot));
}
else
{
@ -180,6 +164,9 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
off = actor.GetOffhand();
FistWeaponHack(ref ret, ref main, ref off);
ret.SetVisor(actor.AsCharacter->DrawData.IsVisorToggled);
foreach (var slot in CrestExtensions.AllRelevantSet)
ret.SetCrest(slot, actor.GetCrest(slot));
}
// Set the weapons regardless of source.
@ -206,7 +193,7 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
if (mainhand.Set.Id is < 1601 or >= 1651)
return;
var gauntlets = _items.Identify(EquipSlot.Hands, offhand.Set, (Variant) offhand.Type.Id);
var gauntlets = _items.Identify(EquipSlot.Hands, offhand.Set, (Variant)offhand.Type.Id);
offhand.Set = (SetId)(mainhand.Set.Id + 50);
offhand.Variant = mainhand.Variant;
offhand.Type = mainhand.Type;
@ -304,6 +291,18 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
_event.Invoke(StateChanged.Type.Stain, source, state, actors, (old, stain, slot));
}
/// <summary> Change the crest of an equipment piece. </summary>
public void ChangeCrest(ActorState state, CrestFlag slot, bool crest, StateChanged.Source source, uint key = 0)
{
if (!_editor.ChangeCrest(state, slot, crest, source, out var old, key))
return;
var actors = _applier.ChangeCrests(state, source is StateChanged.Source.Manual or StateChanged.Source.Ipc);
Glamourer.Log.Verbose(
$"Set {slot.ToLabel()} crest in state {state.Identifier.Incognito(null)} from {old} to {crest}. [Affecting {actors.ToLazyString("nothing")}.]");
_event.Invoke(StateChanged.Type.Crest, source, state, actors, (old, crest, slot));
}
/// <summary> Change hat visibility. </summary>
public void ChangeHatState(ActorState state, bool value, StateChanged.Source source, uint key = 0)
{
@ -356,19 +355,8 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
public void ApplyDesign(DesignBase design, ActorState state, StateChanged.Source source, uint key = 0)
{
void HandleEquip(EquipSlot slot, bool applyPiece, bool applyStain)
{
var unused = (applyPiece, applyStain) switch
{
(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 (!_editor.ChangeModelId(state, design.DesignData.ModelId, design.DesignData.Customize, design.GetDesignDataRef().GetEquipmentPtr(), source,
if (!_editor.ChangeModelId(state, design.DesignData.ModelId, design.DesignData.Customize, design.GetDesignDataRef().GetEquipmentPtr(),
source,
out var oldModelId, key))
return;
@ -393,12 +381,28 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
foreach (var slot in EquipSlotExtensions.FullSlots)
HandleEquip(slot, design.DoApplyEquip(slot), design.DoApplyStain(slot));
foreach (var slot in CrestExtensions.AllRelevantSet.Where(design.DoApplyCrest))
_editor.ChangeCrest(state, slot, design.DesignData.Crest(slot), source, out _, key);
}
var actors = ApplyAll(state, redraw, false);
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);
return;
void HandleEquip(EquipSlot slot, bool applyPiece, bool applyStain)
{
var unused = (applyPiece, applyStain) switch
{
(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),
};
}
}
private ActorData ApplyAll(ActorState state, bool redraw, bool withLock)
@ -430,6 +434,7 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
_applier.ChangeHatState(actors, state.ModelData.IsHatVisible());
_applier.ChangeWeaponState(actors, state.ModelData.IsWeaponVisible());
_applier.ChangeVisor(actors, state.ModelData.IsVisorToggled());
_applier.ChangeCrests(actors, state.ModelData.CrestVisibility);
}
return actors;
@ -453,10 +458,13 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
state[slot, true] = StateChanged.Source.Game;
state[slot, false] = StateChanged.Source.Game;
}
foreach (var type in Enum.GetValues<ActorState.MetaIndex>())
state[type] = StateChanged.Source.Game;
foreach (var slot in CrestExtensions.AllRelevantSet)
state[slot] = StateChanged.Source.Game;
var actors = ActorData.Invalid;
if (source is StateChanged.Source.Manual or StateChanged.Source.Ipc)
actors = ApplyAll(state, redraw, true);
@ -491,6 +499,15 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
}
}
foreach (var slot in CrestExtensions.AllRelevantSet)
{
if (state[slot] is StateChanged.Source.Fixed)
{
state[slot] = StateChanged.Source.Game;
state.ModelData.SetCrest(slot, state.BaseData.Crest(slot));
}
}
if (state[ActorState.MetaIndex.HatState] is StateChanged.Source.Fixed)
{
state[ActorState.MetaIndex.HatState] = StateChanged.Source.Game;