mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2025-12-18 14:44:31 +01:00
Add testing support for changing weapon types in gpose only.
This commit is contained in:
parent
89f2e78690
commit
73589adac9
6 changed files with 127 additions and 48 deletions
71
Glamourer/Events/GPoseService.cs
Normal file
71
Glamourer/Events/GPoseService.cs
Normal 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}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using OtterGui.Log;
|
using OtterGui.Log;
|
||||||
|
using Penumbra.GameData.Actors;
|
||||||
|
|
||||||
namespace Glamourer.Interop.Structs;
|
namespace Glamourer.Interop.Structs;
|
||||||
|
|
||||||
|
|
@ -36,4 +37,13 @@ public readonly struct ActorData
|
||||||
? new LazyString(() => string.Join(", ", objects.Select(o => o.ToString())))
|
? new LazyString(() => string.Join(", ", objects.Select(o => o.ToString())))
|
||||||
: new LazyString(() => invalid);
|
: 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);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,8 @@ public static class ServiceManager
|
||||||
.AddSingleton<WeaponVisibilityChanged>()
|
.AddSingleton<WeaponVisibilityChanged>()
|
||||||
.AddSingleton<ObjectUnlocked>()
|
.AddSingleton<ObjectUnlocked>()
|
||||||
.AddSingleton<TabSelected>()
|
.AddSingleton<TabSelected>()
|
||||||
.AddSingleton<MovedEquipment>();
|
.AddSingleton<MovedEquipment>()
|
||||||
|
.AddSingleton<GPoseService>();
|
||||||
|
|
||||||
private static IServiceCollection AddData(this IServiceCollection services)
|
private static IServiceCollection AddData(this IServiceCollection services)
|
||||||
=> services.AddSingleton<IdentifierService>()
|
=> services.AddSingleton<IdentifierService>()
|
||||||
|
|
|
||||||
|
|
@ -167,12 +167,11 @@ public class StateApplier
|
||||||
public ActorData ChangeWeapon(ActorState state, EquipSlot slot, bool apply, bool onlyGPose)
|
public ActorData ChangeWeapon(ActorState state, EquipSlot slot, bool apply, bool onlyGPose)
|
||||||
{
|
{
|
||||||
var data = GetData(state);
|
var data = GetData(state);
|
||||||
if (apply)
|
|
||||||
{
|
|
||||||
if (onlyGPose)
|
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));
|
ChangeWeapon(data, slot, state.ModelData.Item(slot), state.ModelData.Stain(slot));
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
@ -187,20 +186,6 @@ public class StateApplier
|
||||||
_weapon.LoadWeapon(actor, slot, weapon.Weapon().With(stain));
|
_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>
|
/// <summary> Apply a weapon to the offhand. </summary>
|
||||||
public void ChangeOffhand(ActorData data, EquipItem weapon, StainId stain)
|
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));
|
_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>
|
/// <summary> Change the visor state of actors only on the draw object. </summary>
|
||||||
public void ChangeVisor(ActorData data, bool value)
|
public void ChangeVisor(ActorData data, bool value)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,14 @@ public class StateEditor
|
||||||
private readonly ItemManager _items;
|
private readonly ItemManager _items;
|
||||||
private readonly CustomizationService _customizations;
|
private readonly CustomizationService _customizations;
|
||||||
private readonly HumanModelList _humans;
|
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;
|
_customizations = customizations;
|
||||||
_humans = humans;
|
_humans = humans;
|
||||||
_items = items;
|
_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>
|
/// <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.
|
// Can not change weapon type from expected type in state.
|
||||||
if (slot is EquipSlot.MainHand && item.Type != state.BaseData.MainhandType
|
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;
|
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.ModelData.SetItem(slot, item);
|
||||||
state[slot, false] = source;
|
state[slot, false] = source;
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -143,9 +155,20 @@ public class StateEditor
|
||||||
|
|
||||||
// Can not change weapon type from expected type in state.
|
// Can not change weapon type from expected type in state.
|
||||||
if (slot is EquipSlot.MainHand && item.Type != state.BaseData.MainhandType
|
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;
|
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.SetItem(slot, item);
|
||||||
state.ModelData.SetStain(slot, stain);
|
state.ModelData.SetStain(slot, stain);
|
||||||
state[slot, false] = source;
|
state[slot, false] = source;
|
||||||
|
|
@ -170,10 +193,11 @@ public class StateEditor
|
||||||
{
|
{
|
||||||
(var setter, oldValue) = index switch
|
(var setter, oldValue) = index switch
|
||||||
{
|
{
|
||||||
ActorState.MetaIndex.Wetness => ((Func<bool, bool>) (v => state.ModelData.SetIsWet(v)), state.ModelData.IsWet()),
|
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.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.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.WeaponState => ((Func<bool, bool>)(v => state.ModelData.SetWeaponVisible(v)),
|
||||||
|
state.ModelData.IsWeaponVisible()),
|
||||||
_ => throw new Exception("Invalid MetaIndex."),
|
_ => throw new Exception("Invalid MetaIndex."),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ using Penumbra.GameData.Actors;
|
||||||
using Penumbra.GameData.Data;
|
using Penumbra.GameData.Data;
|
||||||
using Penumbra.GameData.Enums;
|
using Penumbra.GameData.Enums;
|
||||||
using Penumbra.GameData.Structs;
|
using Penumbra.GameData.Structs;
|
||||||
using Penumbra.String;
|
|
||||||
|
|
||||||
namespace Glamourer.State;
|
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 type = slot.ToIndex() < 10 ? StateChanged.Type.Equip : StateChanged.Type.Weapon;
|
||||||
var actors = type is StateChanged.Type.Equip
|
var actors = type is StateChanged.Type.Equip
|
||||||
? _applier.ChangeArmor(state, slot, source is StateChanged.Source.Manual or StateChanged.Source.Ipc)
|
? _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(
|
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")}.]");
|
$"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));
|
_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 type = slot.ToIndex() < 10 ? StateChanged.Type.Equip : StateChanged.Type.Weapon;
|
||||||
var actors = type is StateChanged.Type.Equip
|
var actors = type is StateChanged.Type.Equip
|
||||||
? _applier.ChangeArmor(state, slot, source is StateChanged.Source.Manual or StateChanged.Source.Ipc)
|
? _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(
|
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")}.]");
|
$"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));
|
_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);
|
_applier.ChangeCustomize(actors, state.ModelData.Customize);
|
||||||
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
||||||
_applier.ChangeArmor(actors, slot, state.ModelData.Armor(slot), state.ModelData.IsHatVisible());
|
_applier.ChangeArmor(actors, slot, state.ModelData.Armor(slot), state.ModelData.IsHatVisible());
|
||||||
foreach (var slot in EquipSlotExtensions.WeaponSlots)
|
var mainhandActors = state.ModelData.MainhandType != state.BaseData.MainhandType ? actors.OnlyGPose() : actors;
|
||||||
_applier.ChangeWeapon(actors, slot, state.ModelData.Item(slot), state.ModelData.Stain(slot));
|
_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)
|
if (state.ModelData.IsHuman)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue