Add testing support for changing weapon types in gpose only.

This commit is contained in:
Ottermandias 2023-07-26 14:59:06 +02:00
parent 89f2e78690
commit 73589adac9
6 changed files with 127 additions and 48 deletions

View file

@ -0,0 +1,71 @@
using System;
using System.Collections.Concurrent;
using Dalamud.Game;
using OtterGui.Classes;
using FFXIVClientStructs.FFXIV.Client.Game;
namespace Glamourer.Events;
public class GPoseService : EventWrapper<Action<bool>, GPoseService.Priority>
{
private readonly Framework _framework;
private readonly ConcurrentQueue<Action> _onLeave = new();
private readonly ConcurrentQueue<Action> _onEnter = new();
public enum Priority
{ }
public bool InGPose { get; private set; } = false;
public GPoseService(Framework framework)
: base(nameof(GPoseService))
{
_framework = framework;
_framework.Update += OnFramework;
}
public new void Dispose()
{
_framework.Update -= OnFramework;
base.Dispose();
}
public void AddActionOnLeave(Action onLeave)
{
if (InGPose)
_onLeave.Enqueue(onLeave);
else
onLeave();
}
public void AddActionOnEnter(Action onEnter)
{
if (InGPose)
onEnter();
else
_onEnter.Enqueue(onEnter);
}
private void OnFramework(Framework _)
{
var inGPose = GameMain.IsInGPose();
if (InGPose == inGPose)
return;
InGPose = inGPose;
Invoke(this, InGPose);
var actions = InGPose ? _onEnter : _onLeave;
foreach (var action in actions)
{
try
{
action();
}
catch (Exception ex)
{
Glamourer.Log.Error($"Error executing GPose action:\n{ex}");
}
}
}
}

View file

@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.Linq;
using OtterGui.Log;
using Penumbra.GameData.Actors;
namespace Glamourer.Interop.Structs;
@ -36,4 +37,13 @@ public readonly struct ActorData
? new LazyString(() => string.Join(", ", objects.Select(o => o.ToString())))
: new LazyString(() => invalid);
}
private ActorData(List<Actor> objects, string label)
{
Objects = objects;
Label = label;
}
public ActorData OnlyGPose()
=> new(Objects.Where(o => o.Index is >= (int)ScreenActor.GPosePlayer and < (int)ScreenActor.CutsceneEnd).ToList(), Label);
}

View file

@ -69,7 +69,8 @@ public static class ServiceManager
.AddSingleton<WeaponVisibilityChanged>()
.AddSingleton<ObjectUnlocked>()
.AddSingleton<TabSelected>()
.AddSingleton<MovedEquipment>();
.AddSingleton<MovedEquipment>()
.AddSingleton<GPoseService>();
private static IServiceCollection AddData(this IServiceCollection services)
=> services.AddSingleton<IdentifierService>()

View file

@ -167,12 +167,11 @@ public class StateApplier
public ActorData ChangeWeapon(ActorState state, EquipSlot slot, bool apply, bool onlyGPose)
{
var data = GetData(state);
if (apply)
{
if (onlyGPose)
data.Objects.RemoveAll(a => a.Index is < (int)ScreenActor.GPosePlayer or >= (int)ScreenActor.CutsceneEnd);
data = data.OnlyGPose();
if (apply)
ChangeWeapon(data, slot, state.ModelData.Item(slot), state.ModelData.Stain(slot));
}
return data;
}
@ -187,20 +186,6 @@ public class StateApplier
_weapon.LoadWeapon(actor, slot, weapon.Weapon().With(stain));
}
/// <inheritdoc cref="ChangeMainhand(ActorData,EquipItem,StainId)"/>
public ActorData ChangeMainhand(ActorState state, bool apply, bool onlyGPose)
{
var data = GetData(state);
if (apply)
{
if (onlyGPose)
data.Objects.RemoveAll(a => a.Index is < (int)ScreenActor.GPosePlayer or >= (int)ScreenActor.CutsceneEnd);
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)
{
@ -209,20 +194,6 @@ public class StateApplier
_weapon.LoadWeapon(actor, EquipSlot.OffHand, weapon.Weapon().With(stain));
}
/// <inheritdoc cref="ChangeOffhand(ActorData,EquipItem,StainId)"/>
public ActorData ChangeOffhand(ActorState state, bool apply, bool onlyGPose)
{
var data = GetData(state);
if (apply)
{
if (onlyGPose)
data.Objects.RemoveAll(a => a.Index is < (int)ScreenActor.GPosePlayer or >= (int)ScreenActor.CutsceneEnd);
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)
{

View file

@ -14,12 +14,14 @@ public class StateEditor
private readonly ItemManager _items;
private readonly CustomizationService _customizations;
private readonly HumanModelList _humans;
private readonly GPoseService _gPose;
public StateEditor(CustomizationService customizations, HumanModelList humans, ItemManager items)
public StateEditor(CustomizationService customizations, HumanModelList humans, ItemManager items, GPoseService gPose)
{
_customizations = customizations;
_humans = humans;
_items = items;
_gPose = gPose;
}
/// <summary> Change the model id. If the actor is changed from a human to another human, customize and equipData are unused. </summary>
@ -124,9 +126,19 @@ public class StateEditor
// Can not change weapon type from expected type in state.
if (slot is EquipSlot.MainHand && item.Type != state.BaseData.MainhandType
|| slot is EquipSlot.OffHand && item.Type != state.BaseData.MainhandType.ValidOffhand())
|| slot is EquipSlot.OffHand && item.Type != state.BaseData.OffhandType)
{
if (!_gPose.InGPose)
return false;
var old = oldItem;
_gPose.AddActionOnLeave(() =>
{
if (old.Type == state.BaseData.Item(slot).Type)
ChangeItem(state, slot, old, state[slot, false], out _, key);
});
}
state.ModelData.SetItem(slot, item);
state[slot, false] = source;
return true;
@ -143,9 +155,20 @@ public class StateEditor
// Can not change weapon type from expected type in state.
if (slot is EquipSlot.MainHand && item.Type != state.BaseData.MainhandType
|| slot is EquipSlot.OffHand && item.Type != state.BaseData.MainhandType.ValidOffhand())
|| slot is EquipSlot.OffHand && item.Type != state.BaseData.OffhandType)
{
if (!_gPose.InGPose)
return false;
var old = oldItem;
var oldS = oldStain;
_gPose.AddActionOnLeave(() =>
{
if (old.Type == state.BaseData.Item(slot).Type)
ChangeEquip(state, slot, old, oldS, state[slot, false], out _, out _, key);
});
}
state.ModelData.SetItem(slot, item);
state.ModelData.SetStain(slot, stain);
state[slot, false] = source;
@ -170,10 +193,11 @@ public class StateEditor
{
(var setter, oldValue) = index switch
{
ActorState.MetaIndex.Wetness => ((Func<bool, bool>) (v => state.ModelData.SetIsWet(v)), state.ModelData.IsWet()),
ActorState.MetaIndex.HatState => ((Func<bool, bool>) (v => state.ModelData.SetHatVisible(v)), state.ModelData.IsHatVisible()),
ActorState.MetaIndex.VisorState => ((Func<bool, bool>) (v => state.ModelData.SetVisor(v)), state.ModelData.IsVisorToggled()),
ActorState.MetaIndex.WeaponState => ((Func<bool, bool>) (v => state.ModelData.SetWeaponVisible(v)), state.ModelData.IsWeaponVisible()),
ActorState.MetaIndex.Wetness => ((Func<bool, bool>)(v => state.ModelData.SetIsWet(v)), state.ModelData.IsWet()),
ActorState.MetaIndex.HatState => ((Func<bool, bool>)(v => state.ModelData.SetHatVisible(v)), state.ModelData.IsHatVisible()),
ActorState.MetaIndex.VisorState => ((Func<bool, bool>)(v => state.ModelData.SetVisor(v)), state.ModelData.IsVisorToggled()),
ActorState.MetaIndex.WeaponState => ((Func<bool, bool>)(v => state.ModelData.SetWeaponVisible(v)),
state.ModelData.IsWeaponVisible()),
_ => throw new Exception("Invalid MetaIndex."),
};

View file

@ -13,7 +13,6 @@ using Penumbra.GameData.Actors;
using Penumbra.GameData.Data;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using Penumbra.String;
namespace Glamourer.State;
@ -258,7 +257,8 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
var type = slot.ToIndex() < 10 ? 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, item.Type != old.Type);
: _applier.ChangeWeapon(state, slot, source is StateChanged.Source.Manual or StateChanged.Source.Ipc,
item.Type != (slot is EquipSlot.MainHand ? state.BaseData.MainhandType : state.BaseData.OffhandType));
Glamourer.Log.Verbose(
$"Set {slot.ToName()} in state {state.Identifier.Incognito(null)} from {old.Name} ({old.ItemId}) to {item.Name} ({item.ItemId}). [Affecting {actors.ToLazyString("nothing")}.]");
_event.Invoke(type, source, state, actors, (old, item, slot));
@ -273,7 +273,7 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
var type = slot.ToIndex() < 10 ? 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, item.Type != old.Type);
: _applier.ChangeWeapon(state, slot, source is StateChanged.Source.Manual or StateChanged.Source.Ipc, item.Type != (slot is EquipSlot.MainHand ? state.BaseData.MainhandType : state.BaseData.OffhandType));
Glamourer.Log.Verbose(
$"Set {slot.ToName()} in state {state.Identifier.Incognito(null)} from {old.Name} ({old.ItemId}) to {item.Name} ({item.ItemId}) and its stain from {oldStain.Value} to {stain.Value}. [Affecting {actors.ToLazyString("nothing")}.]");
_event.Invoke(type, source, state, actors, (old, item, slot));
@ -401,8 +401,10 @@ public class StateManager : IReadOnlyDictionary<ActorIdentifier, ActorState>
_applier.ChangeCustomize(actors, state.ModelData.Customize);
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));
var mainhandActors = state.ModelData.MainhandType != state.BaseData.MainhandType ? actors.OnlyGPose() : actors;
_applier.ChangeMainhand(mainhandActors, state.ModelData.Item(EquipSlot.MainHand), state.ModelData.Stain(EquipSlot.MainHand));
var offhandActors = state.ModelData.OffhandType != state.BaseData.OffhandType ? actors.OnlyGPose() : actors;
_applier.ChangeOffhand(offhandActors, state.ModelData.Item(EquipSlot.OffHand), state.ModelData.Stain(EquipSlot.OffHand));
}
if (state.ModelData.IsHuman)