mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2026-02-18 13:37:44 +01:00
Current state.
This commit is contained in:
parent
7a602d6ec5
commit
81059411e5
42 changed files with 913 additions and 320 deletions
|
|
@ -151,6 +151,18 @@ public class InternalStateEditor(
|
|||
return true;
|
||||
}
|
||||
|
||||
/// <summary> Change a single bonus item. </summary>
|
||||
public bool ChangeBonusItem(ActorState state, BonusItemFlag slot, BonusItem item, StateSource source, out BonusItem oldItem, uint key = 0)
|
||||
{
|
||||
oldItem = state.ModelData.BonusItem(slot);
|
||||
if (!state.CanUnlock(key))
|
||||
return false;
|
||||
|
||||
state.ModelData.SetBonusItem(slot, item);
|
||||
state.Sources[slot] = source;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary> Change a single piece of equipment including stain. </summary>
|
||||
public bool ChangeEquip(ActorState state, EquipSlot slot, EquipItem item, StainIds stains, StateSource source, out EquipItem oldItem,
|
||||
out StainIds oldStains, uint key = 0)
|
||||
|
|
|
|||
|
|
@ -125,6 +125,33 @@ public class StateApplier(
|
|||
return data;
|
||||
}
|
||||
|
||||
public void ChangeBonusItem(ActorData data, BonusItemFlag slot, PrimaryId id, Variant variant)
|
||||
{
|
||||
var item = new CharacterArmor(id, variant, StainIds.None);
|
||||
foreach (var actor in data.Objects.Where(a => a.IsCharacter))
|
||||
{
|
||||
var mdl = actor.Model;
|
||||
if (!mdl.IsHuman)
|
||||
continue;
|
||||
|
||||
_updateSlot.UpdateBonusSlot(actor.Model, slot, item);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="ChangeBonusItem(ActorData,BonusItemFlag,PrimaryId,Variant)"/>
|
||||
public ActorData ChangeBonusItem(ActorState state, BonusItemFlag slot, bool apply)
|
||||
{
|
||||
// If the source is not IPC we do not want to apply restrictions.
|
||||
var data = GetData(state);
|
||||
if (apply)
|
||||
{
|
||||
var item = state.ModelData.BonusItem(slot);
|
||||
ChangeBonusItem(data, slot, item.ModelId, item.Variant);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Change the stain of a single piece of armor or weapon.
|
||||
|
|
|
|||
|
|
@ -89,6 +89,18 @@ public class StateEditor(
|
|||
StateChanged.Invoke(type, settings.Source, state, actors, (old, item, slot));
|
||||
}
|
||||
|
||||
public void ChangeBonusItem(object data, BonusItemFlag slot, BonusItem item, ApplySettings settings = default)
|
||||
{
|
||||
var state = (ActorState)data;
|
||||
if (!Editor.ChangeBonusItem(state, slot, item, settings.Source, out var old, settings.Key))
|
||||
return;
|
||||
|
||||
var actors = Applier.ChangeBonusItem(state, slot, settings.Source.RequiresChange());
|
||||
Glamourer.Log.Verbose(
|
||||
$"Set {slot.ToName()} in state {state.Identifier.Incognito(null)} from {old.Name} ({old.Id}) to {item.Name} ({item.Id}). [Affecting {actors.ToLazyString("nothing")}.]");
|
||||
StateChanged.Invoke(StateChangeType.BonusItem, settings.Source, state, actors, (old, item, slot));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void ChangeEquip(object data, EquipSlot slot, EquipItem? item, StainIds? stains, ApplySettings settings)
|
||||
{
|
||||
|
|
@ -226,7 +238,7 @@ public class StateEditor(
|
|||
out _, settings.Key);
|
||||
}
|
||||
|
||||
var customizeFlags = mergedDesign.Design.ApplyCustomizeRaw;
|
||||
var customizeFlags = mergedDesign.Design.Application.Customize;
|
||||
if (mergedDesign.Design.DoApplyCustomize(CustomizeIndex.Clan))
|
||||
customizeFlags |= CustomizeFlag.Race;
|
||||
|
||||
|
|
@ -245,7 +257,7 @@ public class StateEditor(
|
|||
state.Sources[parameter] = StateSource.Game;
|
||||
}
|
||||
|
||||
foreach (var parameter in mergedDesign.Design.ApplyParameters.Iterate())
|
||||
foreach (var parameter in mergedDesign.Design.Application.Parameters.Iterate())
|
||||
{
|
||||
if (settings.RespectManual && state.Sources[parameter].IsManual())
|
||||
continue;
|
||||
|
|
@ -273,6 +285,13 @@ public class StateEditor(
|
|||
Source(slot.ToState(true)), out _, settings.Key);
|
||||
}
|
||||
|
||||
foreach (var slot in BonusExtensions.AllFlags)
|
||||
{
|
||||
if (mergedDesign.Design.DoApplyBonusItem(slot))
|
||||
if (!settings.RespectManual || !state.Sources[slot].IsManual())
|
||||
Editor.ChangeBonusItem(state, slot, mergedDesign.Design.DesignData.BonusItem(slot), Source(slot), out _, settings.Key);
|
||||
}
|
||||
|
||||
foreach (var weaponSlot in EquipSlotExtensions.WeaponSlots)
|
||||
{
|
||||
if (mergedDesign.Design.DoApplyStain(weaponSlot))
|
||||
|
|
|
|||
|
|
@ -38,6 +38,13 @@ public readonly record struct StateIndex(int Value) : IEqualityOperators<StateIn
|
|||
_ => Invalid,
|
||||
};
|
||||
|
||||
public static implicit operator StateIndex(BonusItemFlag flag)
|
||||
=> flag switch
|
||||
{
|
||||
BonusItemFlag.Glasses => new StateIndex(BonusItemGlasses),
|
||||
_ => Invalid,
|
||||
};
|
||||
|
||||
public static implicit operator StateIndex(CustomizeIndex index)
|
||||
=> index switch
|
||||
{
|
||||
|
|
@ -198,23 +205,13 @@ public readonly record struct StateIndex(int Value) : IEqualityOperators<StateIn
|
|||
public const int ParamFacePaintUvOffset = ParamFacePaintUvMultiplier + 1;
|
||||
public const int ParamDecalColor = ParamFacePaintUvOffset + 1;
|
||||
|
||||
public const int Size = ParamDecalColor + 1;
|
||||
public const int BonusItemGlasses = ParamDecalColor + 1;
|
||||
|
||||
public const int Size = BonusItemGlasses + 1;
|
||||
|
||||
public static IEnumerable<StateIndex> All
|
||||
=> Enumerable.Range(0, Size - 1).Select(i => new StateIndex(i));
|
||||
|
||||
public bool GetApply(DesignBase data)
|
||||
=> GetFlag() switch
|
||||
{
|
||||
EquipFlag e => data.ApplyEquip.HasFlag(e),
|
||||
CustomizeFlag c => data.ApplyCustomize.HasFlag(c),
|
||||
MetaFlag m => data.ApplyMeta.HasFlag(m),
|
||||
CrestFlag c => data.ApplyCrest.HasFlag(c),
|
||||
CustomizeParameterFlag c => data.ApplyParameters.HasFlag(c),
|
||||
bool v => v,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
public string ToName()
|
||||
=> GetFlag() switch
|
||||
{
|
||||
|
|
@ -223,6 +220,7 @@ public readonly record struct StateIndex(int Value) : IEqualityOperators<StateIn
|
|||
MetaFlag m => m.ToIndex().ToName(),
|
||||
CrestFlag c => c.ToLabel(),
|
||||
CustomizeParameterFlag c => c.ToName(),
|
||||
BonusItemFlag b => b.ToName(),
|
||||
bool v => "Model ID",
|
||||
_ => "Unknown",
|
||||
};
|
||||
|
|
@ -317,6 +315,8 @@ public readonly record struct StateIndex(int Value) : IEqualityOperators<StateIn
|
|||
ParamFacePaintUvOffset => CustomizeParameterFlag.FacePaintUvOffset,
|
||||
ParamDecalColor => CustomizeParameterFlag.DecalColor,
|
||||
|
||||
BonusItemGlasses => BonusItemFlag.Glasses,
|
||||
|
||||
_ => -1,
|
||||
};
|
||||
|
||||
|
|
@ -411,6 +411,8 @@ public readonly record struct StateIndex(int Value) : IEqualityOperators<StateIn
|
|||
ParamFacePaintUvOffset => data.Parameters[CustomizeParameterFlag.FacePaintUvOffset],
|
||||
ParamDecalColor => data.Parameters[CustomizeParameterFlag.DecalColor],
|
||||
|
||||
BonusItemGlasses => data.BonusItem(BonusItemFlag.Glasses),
|
||||
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,8 @@ public class StateListener : IDisposable
|
|||
private readonly ItemManager _items;
|
||||
private readonly CustomizeService _customizations;
|
||||
private readonly PenumbraService _penumbra;
|
||||
private readonly EquipSlotUpdating _equipSlotUpdating;
|
||||
private readonly EquipSlotUpdating _equipSlotUpdating;
|
||||
private readonly BonusSlotUpdating _bonusSlotUpdating;
|
||||
private readonly WeaponLoading _weaponLoading;
|
||||
private readonly HeadGearVisibilityChanged _headGearVisibility;
|
||||
private readonly VisorStateChanged _visorState;
|
||||
|
|
@ -52,17 +53,18 @@ public class StateListener : IDisposable
|
|||
private CharacterWeapon _lastFistOffhand = CharacterWeapon.Empty;
|
||||
|
||||
public StateListener(StateManager manager, ItemManager items, PenumbraService penumbra, ActorManager actors, Configuration config,
|
||||
EquipSlotUpdating equipSlotUpdating, WeaponLoading weaponLoading, VisorStateChanged visorState, WeaponVisibilityChanged weaponVisibility,
|
||||
HeadGearVisibilityChanged headGearVisibility, AutoDesignApplier autoDesignApplier, FunModule funModule, HumanModelList humans,
|
||||
StateApplier applier, MovedEquipment movedEquipment, ObjectManager objects, GPoseService gPose,
|
||||
ChangeCustomizeService changeCustomizeService, CustomizeService customizations, ICondition condition, CrestService crestService)
|
||||
EquipSlotUpdating equipSlotUpdating, WeaponLoading weaponLoading, VisorStateChanged visorState,
|
||||
WeaponVisibilityChanged weaponVisibility, HeadGearVisibilityChanged headGearVisibility, AutoDesignApplier autoDesignApplier,
|
||||
FunModule funModule, HumanModelList humans, StateApplier applier, MovedEquipment movedEquipment, ObjectManager objects,
|
||||
GPoseService gPose, ChangeCustomizeService changeCustomizeService, CustomizeService customizations, ICondition condition,
|
||||
CrestService crestService, BonusSlotUpdating bonusSlotUpdating)
|
||||
{
|
||||
_manager = manager;
|
||||
_items = items;
|
||||
_penumbra = penumbra;
|
||||
_actors = actors;
|
||||
_config = config;
|
||||
_equipSlotUpdating = equipSlotUpdating;
|
||||
_equipSlotUpdating = equipSlotUpdating;
|
||||
_weaponLoading = weaponLoading;
|
||||
_visorState = visorState;
|
||||
_weaponVisibility = weaponVisibility;
|
||||
|
|
@ -78,6 +80,7 @@ public class StateListener : IDisposable
|
|||
_customizations = customizations;
|
||||
_condition = condition;
|
||||
_crestService = crestService;
|
||||
_bonusSlotUpdating = bonusSlotUpdating;
|
||||
Subscribe();
|
||||
}
|
||||
|
||||
|
|
@ -227,6 +230,35 @@ public class StateListener : IDisposable
|
|||
(_, armor) = _items.RestrictedGear.ResolveRestricted(armor, slot, customize.Race, customize.Gender);
|
||||
}
|
||||
|
||||
private void OnBonusSlotUpdating(Model model, BonusItemFlag slot, ref CharacterArmor item, ref ulong returnValue)
|
||||
{
|
||||
var actor = _penumbra.GameObjectFromDrawObject(model);
|
||||
if (_condition[ConditionFlag.CreatingCharacter] && actor.Index >= ObjectIndex.CutsceneStart)
|
||||
return;
|
||||
|
||||
if (actor.Identifier(_actors, out var identifier)
|
||||
&& _manager.TryGetValue(identifier, out var state))
|
||||
switch (UpdateBaseData(actor, state, slot, item))
|
||||
{
|
||||
// Base data changed equipment while actors were not there.
|
||||
// Update model state if not on fixed design.
|
||||
case UpdateState.Change:
|
||||
var apply = false;
|
||||
if (!state.Sources[slot].IsFixed())
|
||||
_manager.ChangeBonusItem(state, slot, state.BaseData.BonusItem(slot), ApplySettings.Game);
|
||||
else
|
||||
apply = true;
|
||||
if (apply)
|
||||
item = state.ModelData.BonusItem(slot).ToArmor();
|
||||
break;
|
||||
// Use current model data.
|
||||
case UpdateState.NoChange:
|
||||
item = state.ModelData.BonusItem(slot).ToArmor();
|
||||
break;
|
||||
case UpdateState.Transformed: break;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMovedEquipment((EquipSlot, uint, StainIds)[] items)
|
||||
{
|
||||
_objects.Update();
|
||||
|
|
@ -403,6 +435,28 @@ public class StateListener : IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
private UpdateState UpdateBaseData(Actor actor, ActorState state, BonusItemFlag slot, CharacterArmor item)
|
||||
{
|
||||
var actorItemId = actor.GetBonusItem(slot);
|
||||
if (!_items.IsBonusItemValid(slot, actorItemId, out var actorItem))
|
||||
return UpdateState.NoChange;
|
||||
|
||||
// The actor item does not correspond to the model item, thus the actor is transformed.
|
||||
if (actorItem.ModelId != item.Set || actorItem.Variant != item.Variant)
|
||||
return UpdateState.Transformed;
|
||||
|
||||
var baseData = state.BaseData.BonusItem(slot);
|
||||
var change = UpdateState.NoChange;
|
||||
if (baseData.Id != actorItem.Id || baseData.ModelId != item.Set || baseData.Variant != item.Variant)
|
||||
{
|
||||
var identified = _items.Identify(slot, item.Set, item.Variant);
|
||||
state.BaseData.SetBonusItem(slot, identified);
|
||||
change = UpdateState.Change;
|
||||
}
|
||||
|
||||
return change;
|
||||
}
|
||||
|
||||
/// <summary> Handle a full equip slot update for base data and model data. </summary>
|
||||
private void HandleEquipSlot(Actor actor, ActorState state, EquipSlot slot, ref CharacterArmor armor)
|
||||
{
|
||||
|
|
@ -700,6 +754,7 @@ public class StateListener : IDisposable
|
|||
_penumbra.CreatingCharacterBase += OnCreatingCharacterBase;
|
||||
_penumbra.CreatedCharacterBase += OnCreatedCharacterBase;
|
||||
_equipSlotUpdating.Subscribe(OnEquipSlotUpdating, EquipSlotUpdating.Priority.StateListener);
|
||||
_bonusSlotUpdating.Subscribe(OnBonusSlotUpdating, BonusSlotUpdating.Priority.StateListener);
|
||||
_movedEquipment.Subscribe(OnMovedEquipment, MovedEquipment.Priority.StateListener);
|
||||
_weaponLoading.Subscribe(OnWeaponLoading, WeaponLoading.Priority.StateListener);
|
||||
_visorState.Subscribe(OnVisorChange, VisorStateChanged.Priority.StateListener);
|
||||
|
|
@ -716,6 +771,7 @@ public class StateListener : IDisposable
|
|||
_penumbra.CreatingCharacterBase -= OnCreatingCharacterBase;
|
||||
_penumbra.CreatedCharacterBase -= OnCreatedCharacterBase;
|
||||
_equipSlotUpdating.Unsubscribe(OnEquipSlotUpdating);
|
||||
_bonusSlotUpdating.Unsubscribe(OnBonusSlotUpdating);
|
||||
_movedEquipment.Unsubscribe(OnMovedEquipment);
|
||||
_weaponLoading.Unsubscribe(OnWeaponLoading);
|
||||
_visorState.Unsubscribe(OnVisorChange);
|
||||
|
|
|
|||
|
|
@ -160,6 +160,13 @@ public sealed class StateManager(
|
|||
|
||||
foreach (var slot in CrestExtensions.AllRelevantSet)
|
||||
ret.SetCrest(slot, CrestService.GetModelCrest(actor, slot));
|
||||
|
||||
foreach (var slot in BonusExtensions.AllFlags)
|
||||
{
|
||||
var data = model.GetBonus(slot);
|
||||
var item = Items.Identify(slot, data.Set, data.Variant);
|
||||
ret.SetBonusItem(slot, item);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -181,6 +188,13 @@ public sealed class StateManager(
|
|||
|
||||
foreach (var slot in CrestExtensions.AllRelevantSet)
|
||||
ret.SetCrest(slot, actor.GetCrest(slot));
|
||||
|
||||
foreach (var slot in BonusExtensions.AllFlags)
|
||||
{
|
||||
var id = actor.GetBonusItem(slot);
|
||||
var item = Items.Resolve(slot, id);
|
||||
ret.SetBonusItem(slot, item);
|
||||
}
|
||||
}
|
||||
|
||||
// Set the weapons regardless of source.
|
||||
|
|
@ -241,6 +255,9 @@ public sealed class StateManager(
|
|||
state.Sources[slot, false] = StateSource.Game;
|
||||
}
|
||||
|
||||
foreach (var slot in BonusExtensions.AllFlags)
|
||||
state.Sources[slot] = StateSource.Game;
|
||||
|
||||
foreach (var type in Enum.GetValues<MetaIndex>())
|
||||
state.Sources[type] = StateSource.Game;
|
||||
|
||||
|
|
@ -328,6 +345,12 @@ public sealed class StateManager(
|
|||
state.ModelData.IsHatVisible());
|
||||
}
|
||||
|
||||
foreach (var slot in BonusExtensions.AllFlags)
|
||||
{
|
||||
var item = state.ModelData.BonusItem(slot);
|
||||
Applier.ChangeBonusItem(actors, slot, item.ModelId, item.Variant);
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
@ -364,6 +387,15 @@ public sealed class StateManager(
|
|||
}
|
||||
}
|
||||
|
||||
foreach (var slot in BonusExtensions.AllFlags)
|
||||
{
|
||||
if (state.Sources[slot] is StateSource.Fixed)
|
||||
{
|
||||
state.Sources[slot] = StateSource.Game;
|
||||
state.ModelData.SetBonusItem(slot, state.BaseData.BonusItem(slot));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var slot in CrestExtensions.AllRelevantSet)
|
||||
{
|
||||
if (state.Sources[slot] is StateSource.Fixed)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue