Current state.

This commit is contained in:
Ottermandias 2024-07-15 15:19:51 +02:00
parent 7a602d6ec5
commit 81059411e5
42 changed files with 913 additions and 320 deletions

View file

@ -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)

View file

@ -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.

View file

@ -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))

View file

@ -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,
};
}

View file

@ -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);

View file

@ -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)