mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2026-02-10 01:44:36 +01:00
289 lines
10 KiB
C#
289 lines
10 KiB
C#
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)
|
|
{
|
|
state.TempLock();
|
|
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, ActorState? state = null)
|
|
{
|
|
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
|
|
{
|
|
state?.TempLock();
|
|
_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, state);
|
|
|
|
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.ValidOffhand() == 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;
|
|
}
|
|
}
|