Add IpcManual state.

This commit is contained in:
Ottermandias 2024-01-30 17:51:52 +01:00
parent 994b7bfb6c
commit 818bf71032
15 changed files with 242 additions and 109 deletions

View file

@ -118,8 +118,7 @@ public class StateApplier(
// If the source is not IPC we do not want to apply restrictions.
var data = GetData(state);
if (apply)
ChangeArmor(data, slot, state.ModelData.Armor(slot), state.Sources[slot, false] is not StateSource.Ipc,
state.ModelData.IsHatVisible());
ChangeArmor(data, slot, state.ModelData.Armor(slot), !state.Sources[slot, false].IsIpc(), state.ModelData.IsHatVisible());
return data;
}
@ -267,7 +266,7 @@ public class StateApplier(
actor.Model.ApplyParameterData(flags, values);
}
/// <inheritdoc cref="ChangeParameters(ActorData,CustomizeParameterFlag,in CustomizeParameterData)"/>
/// <inheritdoc cref="ChangeParameters(ActorData,CustomizeParameterFlag,in CustomizeParameterData,bool)"/>
public ActorData ChangeParameters(ActorState state, CustomizeParameterFlag flags, bool apply)
{
var data = GetData(state);
@ -294,10 +293,7 @@ public class StateApplier(
{
ChangeCustomize(actors, state.ModelData.Customize);
foreach (var slot in EquipSlotExtensions.EqdpSlots)
{
ChangeArmor(actors, slot, state.ModelData.Armor(slot), state.Sources[slot, false] is not StateSource.Ipc,
state.ModelData.IsHatVisible());
}
ChangeArmor(actors, slot, state.ModelData.Armor(slot), !state.Sources[slot, false].IsIpc(), state.ModelData.IsHatVisible());
var mainhandActors = state.ModelData.MainhandType != state.BaseData.MainhandType ? actors.OnlyGPose() : actors;
ChangeMainhand(mainhandActors, state.ModelData.Item(EquipSlot.MainHand), state.ModelData.Stain(EquipSlot.MainHand));

View file

@ -31,7 +31,7 @@ public class StateEditor(
if (!Editor.ChangeModelId(state, modelId, customize, equipData, source, out var old, key))
return;
var actors = Applier.ForceRedraw(state, source is StateSource.Manual or StateSource.Ipc);
var actors = Applier.ForceRedraw(state, source.RequiresChange());
Glamourer.Log.Verbose(
$"Set model id in state {state.Identifier.Incognito(null)} from {old} to {modelId}. [Affecting {actors.ToLazyString("nothing")}.]");
StateChanged.Invoke(StateChanged.Type.Model, source, state, actors, (old, modelId));
@ -44,7 +44,7 @@ public class StateEditor(
if (!Editor.ChangeCustomize(state, idx, value, settings.Source, out var old, settings.Key))
return;
var actors = Applier.ChangeCustomize(state, settings.Source is StateSource.Manual or StateSource.Ipc);
var actors = Applier.ChangeCustomize(state, settings.Source.RequiresChange());
Glamourer.Log.Verbose(
$"Set {idx.ToDefaultName()} customizations in state {state.Identifier.Incognito(null)} from {old.Value} to {value.Value}. [Affecting {actors.ToLazyString("nothing")}.]");
StateChanged.Invoke(StateChanged.Type.Customize, settings.Source, state, actors, (old, value, idx));
@ -57,7 +57,7 @@ public class StateEditor(
if (!Editor.ChangeHumanCustomize(state, customizeInput, apply, _ => settings.Source, out var old, out var applied, settings.Key))
return;
var actors = Applier.ChangeCustomize(state, settings.Source is StateSource.Manual or StateSource.Ipc);
var actors = Applier.ChangeCustomize(state, settings.Source.RequiresChange());
Glamourer.Log.Verbose(
$"Set {applied} customizations in state {state.Identifier.Incognito(null)} from {old} to {customizeInput}. [Affecting {actors.ToLazyString("nothing")}.]");
StateChanged.Invoke(StateChanged.Type.EntireCustomize, settings.Source, state, actors, (old, applied));
@ -72,8 +72,8 @@ public class StateEditor(
var type = slot.ToIndex() < 10 ? StateChanged.Type.Equip : StateChanged.Type.Weapon;
var actors = type is StateChanged.Type.Equip
? Applier.ChangeArmor(state, slot, settings.Source is StateSource.Manual or StateSource.Ipc)
: Applier.ChangeWeapon(state, slot, settings.Source is StateSource.Manual or StateSource.Ipc,
? Applier.ChangeArmor(state, slot, settings.Source.RequiresChange())
: Applier.ChangeWeapon(state, slot, settings.Source.RequiresChange(),
item.Type != (slot is EquipSlot.MainHand ? state.BaseData.MainhandType : state.BaseData.OffhandType));
if (slot is EquipSlot.MainHand)
@ -105,8 +105,8 @@ public class StateEditor(
var type = slot.ToIndex() < 10 ? StateChanged.Type.Equip : StateChanged.Type.Weapon;
var actors = type is StateChanged.Type.Equip
? Applier.ChangeArmor(state, slot, settings.Source is StateSource.Manual or StateSource.Ipc)
: Applier.ChangeWeapon(state, slot, settings.Source is StateSource.Manual or StateSource.Ipc,
? Applier.ChangeArmor(state, slot, settings.Source.RequiresChange())
: Applier.ChangeWeapon(state, slot, settings.Source.RequiresChange(),
item!.Value.Type != (slot is EquipSlot.MainHand ? state.BaseData.MainhandType : state.BaseData.OffhandType));
if (slot is EquipSlot.MainHand)
@ -125,7 +125,7 @@ public class StateEditor(
if (!Editor.ChangeStain(state, slot, stain, settings.Source, out var old, settings.Key))
return;
var actors = Applier.ChangeStain(state, slot, settings.Source is StateSource.Manual or StateSource.Ipc);
var actors = Applier.ChangeStain(state, slot, settings.Source.RequiresChange());
Glamourer.Log.Verbose(
$"Set {slot.ToName()} stain in state {state.Identifier.Incognito(null)} from {old.Id} to {stain.Id}. [Affecting {actors.ToLazyString("nothing")}.]");
StateChanged.Invoke(StateChanged.Type.Stain, settings.Source, state, actors, (old, stain, slot));
@ -138,7 +138,7 @@ public class StateEditor(
if (!Editor.ChangeCrest(state, slot, crest, settings.Source, out var old, settings.Key))
return;
var actors = Applier.ChangeCrests(state, settings.Source is StateSource.Manual or StateSource.Ipc);
var actors = Applier.ChangeCrests(state, settings.Source.RequiresChange());
Glamourer.Log.Verbose(
$"Set {slot.ToLabel()} crest in state {state.Identifier.Incognito(null)} from {old} to {crest}. [Affecting {actors.ToLazyString("nothing")}.]");
StateChanged.Invoke(StateChanged.Type.Crest, settings.Source, state, actors, (old, crest, slot));
@ -158,7 +158,7 @@ public class StateEditor(
return;
var @new = state.ModelData.Parameters[flag];
var actors = Applier.ChangeParameters(state, flag, settings.Source is StateSource.Manual or StateSource.Ipc);
var actors = Applier.ChangeParameters(state, flag, settings.Source.RequiresChange());
Glamourer.Log.Verbose(
$"Set {flag} crest in state {state.Identifier.Incognito(null)} from {old} to {@new}. [Affecting {actors.ToLazyString("nothing")}.]");
StateChanged.Invoke(StateChanged.Type.Parameter, settings.Source, state, actors, (old, @new, flag));
@ -171,7 +171,7 @@ public class StateEditor(
if (!Editor.ChangeMetaState(state, index, value, settings.Source, out var old, settings.Key))
return;
var actors = Applier.ChangeMetaState(state, index, settings.Source is StateSource.Manual or StateSource.Ipc);
var actors = Applier.ChangeMetaState(state, index, settings.Source.RequiresChange());
Glamourer.Log.Verbose(
$"Set Head Gear Visibility in state {state.Identifier.Incognito(null)} from {old} to {value}. [Affecting {actors.ToLazyString("nothing")}.]");
StateChanged.Invoke(StateChanged.Type.Other, settings.Source, state, actors, (old, value, MetaIndex.HatState));
@ -191,7 +191,7 @@ public class StateEditor(
{
foreach (var slot in CrestExtensions.AllRelevantSet.Where(mergedDesign.Design.DoApplyCrest))
{
if (!settings.RespectManual || state.Sources[slot] is not StateSource.Manual)
if (!settings.RespectManual || !state.Sources[slot].IsManual())
Editor.ChangeCrest(state, slot, mergedDesign.Design.DesignData.Crest(slot), Source(slot),
out _, settings.Key);
}
@ -201,7 +201,7 @@ public class StateEditor(
customizeFlags |= CustomizeFlag.Race;
Func<CustomizeIndex, bool> applyWhich = settings.RespectManual
? i => customizeFlags.HasFlag(i.ToFlag()) && state.Sources[i] is not StateSource.Manual
? i => customizeFlags.HasFlag(i.ToFlag()) && !state.Sources[i].IsManual()
: i => customizeFlags.HasFlag(i.ToFlag());
if (Editor.ChangeHumanCustomize(state, mergedDesign.Design.DesignData.Customize, applyWhich, i => Source(i), out _, out var changed,
@ -210,12 +210,10 @@ public class StateEditor(
foreach (var parameter in mergedDesign.Design.ApplyParameters.Iterate())
{
if (settings.RespectManual && state.Sources[parameter] is StateSource.Manual or StateSource.Pending)
if (settings.RespectManual && state.Sources[parameter].IsManual())
continue;
var source = Source(parameter);
if (source is StateSource.Manual)
source = StateSource.Pending;
var source = Source(parameter).SetPending();
Editor.ChangeParameter(state, parameter, mergedDesign.Design.DesignData.Parameters[parameter], source, out _, settings.Key);
}
@ -228,12 +226,12 @@ public class StateEditor(
foreach (var slot in EquipSlotExtensions.EqdpSlots)
{
if (mergedDesign.Design.DoApplyEquip(slot))
if (!settings.RespectManual || state.Sources[slot, false] is not StateSource.Manual)
if (!settings.RespectManual || !state.Sources[slot, false].IsManual())
Editor.ChangeItem(state, slot, mergedDesign.Design.DesignData.Item(slot),
Source(slot.ToState()), out _, settings.Key);
if (mergedDesign.Design.DoApplyStain(slot))
if (!settings.RespectManual || state.Sources[slot, true] is not StateSource.Manual)
if (!settings.RespectManual || !state.Sources[slot, true].IsManual())
Editor.ChangeStain(state, slot, mergedDesign.Design.DesignData.Stain(slot),
Source(slot.ToState(true)), out _, settings.Key);
}
@ -241,14 +239,14 @@ public class StateEditor(
foreach (var weaponSlot in EquipSlotExtensions.WeaponSlots)
{
if (mergedDesign.Design.DoApplyStain(weaponSlot))
if (!settings.RespectManual || state.Sources[weaponSlot, true] is not StateSource.Manual)
if (!settings.RespectManual || !state.Sources[weaponSlot, true].IsManual())
Editor.ChangeStain(state, weaponSlot, mergedDesign.Design.DesignData.Stain(weaponSlot),
Source(weaponSlot.ToState(true)), out _, settings.Key);
if (!mergedDesign.Design.DoApplyEquip(weaponSlot))
continue;
if (settings.RespectManual && state.Sources[weaponSlot, false] is StateSource.Manual)
if (settings.RespectManual && !state.Sources[weaponSlot, false].IsManual())
continue;
var currentType = state.ModelData.Item(weaponSlot).Type;
@ -268,12 +266,12 @@ public class StateEditor(
foreach (var meta in MetaExtensions.AllRelevant)
{
if (!settings.RespectManual || state.Sources[meta] is not StateSource.Manual)
if (!settings.RespectManual || !state.Sources[meta].IsManual())
Editor.ChangeMetaState(state, meta, mergedDesign.Design.DesignData.GetMeta(meta), Source(meta), out _, settings.Key);
}
}
var actors = settings.Source is StateSource.Manual or StateSource.Ipc
var actors = settings.Source.RequiresChange()
? Applier.ApplyAll(state, requiresRedraw, false)
: ActorData.Invalid;
@ -311,7 +309,7 @@ public class StateEditor(
/// <summary> Apply offhand item and potentially gauntlets if configured. </summary>
private void ApplyMainhandPeriphery(ActorState state, EquipItem? newMainhand, ApplySettings settings)
{
if (!Config.ChangeEntireItem || settings.Source is not StateSource.Manual)
if (!Config.ChangeEntireItem || !settings.Source.IsManual())
return;
var mh = newMainhand ?? state.ModelData.Item(EquipSlot.MainHand);

View file

@ -171,7 +171,7 @@ public class StateListener : IDisposable
var set = _customizations.Manager.GetSet(model.Clan, model.Gender);
foreach (var index in CustomizationExtensions.AllBasic)
{
if (state.Sources[index] is not StateSource.Fixed)
if (!state.Sources[index].IsFixed())
{
var newValue = customize[index];
var oldValue = model[index];
@ -214,7 +214,7 @@ public class StateListener : IDisposable
&& _manager.TryGetValue(identifier, out var state))
{
HandleEquipSlot(actor, state, slot, ref armor);
locked = state.Sources[slot, false] is StateSource.Ipc;
locked = state.Sources[slot, false] is StateSource.IpcFixed;
}
_funModule.ApplyFunToSlot(actor, ref armor, slot);
@ -241,7 +241,7 @@ public class StateListener : IDisposable
continue;
var changed = changedItem.Weapon(stain);
if (current.Value == changed.Value && state.Sources[slot, false] is not StateSource.Fixed and not StateSource.Ipc)
if (current.Value == changed.Value && !state.Sources[slot, false].IsFixed())
{
_manager.ChangeItem(state, slot, currentItem, ApplySettings.Game);
_manager.ChangeStain(state, slot, current.Stain, ApplySettings.Game);
@ -252,7 +252,7 @@ public class StateListener : IDisposable
_applier.ChangeWeapon(objects, slot, currentItem, stain);
break;
default:
_applier.ChangeArmor(objects, slot, current.ToArmor(), state.Sources[slot, false] is not StateSource.Ipc,
_applier.ChangeArmor(objects, slot, current.ToArmor(), !state.Sources[slot, false].IsFixed(),
state.ModelData.IsHatVisible());
break;
}
@ -278,20 +278,19 @@ public class StateListener : IDisposable
|| !_manager.TryGetValue(identifier, out var state))
return;
ref var actorWeapon = ref weapon;
var baseType = state.BaseData.Item(slot).Type;
var apply = false;
switch (UpdateBaseData(actor, state, slot, actorWeapon))
var baseType = state.BaseData.Item(slot).Type;
var apply = false;
switch (UpdateBaseData(actor, state, slot, weapon))
{
// Do nothing. But this usually can not happen because the hooked function also writes to game objects later.
case UpdateState.Transformed: break;
case UpdateState.Change:
if (state.Sources[slot, false] is not StateSource.Fixed and not StateSource.Ipc)
if (!state.Sources[slot, false].IsFixed())
_manager.ChangeItem(state, slot, state.BaseData.Item(slot), ApplySettings.Game);
else
apply = true;
if (state.Sources[slot, true] is not StateSource.Fixed and not StateSource.Ipc)
if (!state.Sources[slot, true].IsFixed())
_manager.ChangeStain(state, slot, state.BaseData.Stain(slot), ApplySettings.Game);
else
apply = true;
@ -306,9 +305,9 @@ public class StateListener : IDisposable
// Only allow overwriting identical weapons
var newWeapon = state.ModelData.Weapon(slot);
if (baseType is FullEquipType.Unknown || baseType == state.ModelData.Item(slot).Type || _gPose.InGPose && actor.IsGPoseOrCutscene)
actorWeapon = newWeapon;
else if (actorWeapon.Skeleton.Id != 0)
actorWeapon = actorWeapon.With(newWeapon.Stain);
weapon = newWeapon;
else if (weapon.Skeleton.Id != 0)
weapon = weapon.With(newWeapon.Stain);
}
// Fist Weapon Offhand hack.
@ -385,12 +384,12 @@ public class StateListener : IDisposable
// Update model state if not on fixed design.
case UpdateState.Change:
var apply = false;
if (state.Sources[slot, false] is not StateSource.Fixed and not StateSource.Ipc)
if (!state.Sources[slot, false].IsFixed())
_manager.ChangeItem(state, slot, state.BaseData.Item(slot), ApplySettings.Game);
else
apply = true;
if (state.Sources[slot, true] is not StateSource.Fixed and not StateSource.Ipc)
if (!state.Sources[slot, true].IsFixed())
_manager.ChangeStain(state, slot, state.BaseData.Stain(slot), ApplySettings.Game);
else
apply = true;
@ -419,7 +418,7 @@ public class StateListener : IDisposable
switch (UpdateBaseCrest(actor, state, slot, value))
{
case UpdateState.Change:
if (state.Sources[slot] is not StateSource.Fixed and not StateSource.Ipc)
if (!state.Sources[slot].IsFixed())
_manager.ChangeCrest(state, slot, state.BaseData.Crest(slot), ApplySettings.Game);
else
value = state.ModelData.Crest(slot);
@ -565,7 +564,7 @@ public class StateListener : IDisposable
{
// if base state changed, either overwrite the actual value if we have fixed values,
// or overwrite the stored model state with the new one.
if (state.Sources[MetaIndex.VisorState] is StateSource.Fixed or StateSource.Ipc)
if (!state.Sources[MetaIndex.VisorState].IsFixed())
value = state.ModelData.IsVisorToggled();
else
_manager.ChangeMetaState(state, MetaIndex.VisorState, value, ApplySettings.Game);
@ -598,7 +597,7 @@ public class StateListener : IDisposable
{
// if base state changed, either overwrite the actual value if we have fixed values,
// or overwrite the stored model state with the new one.
if (state.Sources[MetaIndex.HatState] is StateSource.Fixed or StateSource.Ipc)
if (!state.Sources[MetaIndex.HatState].IsFixed())
value = state.ModelData.IsHatVisible();
else
_manager.ChangeMetaState(state, MetaIndex.HatState, value, ApplySettings.Game);
@ -631,7 +630,7 @@ public class StateListener : IDisposable
{
// if base state changed, either overwrite the actual value if we have fixed values,
// or overwrite the stored model state with the new one.
if (state.Sources[MetaIndex.WeaponState] is StateSource.Fixed or StateSource.Ipc)
if (!state.Sources[MetaIndex.WeaponState].IsFixed())
value = state.ModelData.IsWeaponVisible();
else
_manager.ChangeMetaState(state, MetaIndex.WeaponState, value, ApplySettings.Game);
@ -700,8 +699,8 @@ public class StateListener : IDisposable
return;
var data = new ActorData(gameObject, _creatingIdentifier.ToName());
_applier.ChangeMetaState(data, MetaIndex.HatState, _creatingState.ModelData.IsHatVisible());
_applier.ChangeMetaState(data, MetaIndex.Wetness, _creatingState.ModelData.IsWet());
_applier.ChangeMetaState(data, MetaIndex.HatState, _creatingState.ModelData.IsHatVisible());
_applier.ChangeMetaState(data, MetaIndex.Wetness, _creatingState.ModelData.IsWet());
_applier.ChangeMetaState(data, MetaIndex.WeaponState, _creatingState.ModelData.IsWeaponVisible());
ApplyParameters(_creatingState, drawObject);
@ -745,12 +744,18 @@ public class StateListener : IDisposable
else if (_config.UseAdvancedParameters)
model.ApplySingleParameterData(flag, state.ModelData.Parameters);
break;
case StateSource.IpcManual:
if (state.BaseData.Parameters.Set(flag, newValue))
_manager.ChangeCustomizeParameter(state, flag, newValue, ApplySettings.Game);
else
model.ApplySingleParameterData(flag, state.ModelData.Parameters);
break;
case StateSource.Fixed:
state.BaseData.Parameters.Set(flag, newValue);
if (_config.UseAdvancedParameters)
model.ApplySingleParameterData(flag, state.ModelData.Parameters);
break;
case StateSource.Ipc:
case StateSource.IpcFixed:
state.BaseData.Parameters.Set(flag, newValue);
model.ApplySingleParameterData(flag, state.ModelData.Parameters);
break;

View file

@ -243,7 +243,7 @@ public sealed class StateManager(
state.Materials.Clear();
var actors = ActorData.Invalid;
if (source is StateSource.Manual or StateSource.Ipc)
if (source is not StateSource.Game)
actors = Applier.ApplyAll(state, redraw, true);
Glamourer.Log.Verbose(
@ -262,7 +262,7 @@ public sealed class StateManager(
state.Sources[flag] = StateSource.Game;
var actors = ActorData.Invalid;
if (source is StateSource.Manual or StateSource.Ipc)
if (source is not StateSource.Game)
actors = Applier.ChangeParameters(state, CustomizeParameterExtensions.All, true);
Glamourer.Log.Verbose(
$"Reset advanced customization state of {state.Identifier.Incognito(null)} to game base. [Affecting {actors.ToLazyString("nothing")}.]");
@ -316,28 +316,10 @@ public sealed class StateManager(
}
}
if (state.Sources[MetaIndex.HatState] is StateSource.Fixed)
foreach (var meta in MetaExtensions.AllRelevant.Where(f => state.Sources[f] is StateSource.Fixed))
{
state.Sources[MetaIndex.HatState] = StateSource.Game;
state.ModelData.SetHatVisible(state.BaseData.IsHatVisible());
}
if (state.Sources[MetaIndex.VisorState] is StateSource.Fixed)
{
state.Sources[MetaIndex.VisorState] = StateSource.Game;
state.ModelData.SetVisor(state.BaseData.IsVisorToggled());
}
if (state.Sources[MetaIndex.WeaponState] is StateSource.Fixed)
{
state.Sources[MetaIndex.WeaponState] = StateSource.Game;
state.ModelData.SetWeaponVisible(state.BaseData.IsWeaponVisible());
}
if (state.Sources[MetaIndex.Wetness] is StateSource.Fixed)
{
state.Sources[MetaIndex.Wetness] = StateSource.Game;
state.ModelData.SetIsWet(state.BaseData.IsWet());
state.Sources[meta] = StateSource.Game;
state.ModelData.SetMeta(meta, state.BaseData.GetMeta(meta));
}
}

View file

@ -7,12 +7,48 @@ public enum StateSource : byte
Game,
Manual,
Fixed,
Ipc,
IpcFixed,
IpcManual,
// Only used for CustomizeParameters.
Pending,
}
public static class StateSourceExtensions
{
public static StateSource Base(this StateSource source)
=> source switch
{
StateSource.Manual or StateSource.IpcManual or StateSource.Pending => StateSource.Manual,
StateSource.Fixed or StateSource.IpcFixed => StateSource.Fixed,
_ => StateSource.Game,
};
public static bool IsGame(this StateSource source)
=> source.Base() is StateSource.Game;
public static bool IsManual(this StateSource source)
=> source.Base() is StateSource.Manual;
public static bool IsFixed(this StateSource source)
=> source.Base() is StateSource.Fixed;
public static StateSource SetPending(this StateSource source)
=> source is StateSource.Manual ? StateSource.Pending : source;
public static bool RequiresChange(this StateSource source)
=> source switch
{
StateSource.Manual => true,
StateSource.IpcFixed => true,
StateSource.IpcManual => true,
_ => false,
};
public static bool IsIpc(this StateSource source)
=> source is StateSource.IpcManual or StateSource.IpcFixed;
}
public unsafe struct StateSources
{
public const int Size = (StateIndex.Size + 1) / 2;
@ -59,14 +95,16 @@ public unsafe struct StateSources
case (byte)StateSource.Game | ((byte)StateSource.Fixed << 4):
case (byte)StateSource.Manual | ((byte)StateSource.Fixed << 4):
case (byte)StateSource.Ipc | ((byte)StateSource.Fixed << 4):
case (byte)StateSource.IpcFixed | ((byte)StateSource.Fixed << 4):
case (byte)StateSource.Pending | ((byte)StateSource.Fixed << 4):
case (byte)StateSource.IpcManual | ((byte)StateSource.Fixed << 4):
_data[i] = (byte)((value & 0x0F) | ((byte)StateSource.Manual << 4));
break;
case (byte)StateSource.Fixed:
case ((byte)StateSource.Manual << 4) | (byte)StateSource.Fixed:
case ((byte)StateSource.Ipc << 4) | (byte)StateSource.Fixed:
case ((byte)StateSource.IpcFixed << 4) | (byte)StateSource.Fixed:
case ((byte)StateSource.Pending << 4) | (byte)StateSource.Fixed:
case ((byte)StateSource.IpcManual << 4) | (byte)StateSource.Fixed:
_data[i] = (byte)((value & 0xF0) | (byte)StateSource.Manual);
break;
}