Persist turret controller state in typed snapshots

This commit is contained in:
Meta Vens 2026-06-14 16:40:08 +01:00
commit 6deea479dd
11 changed files with 91 additions and 8 deletions

Binary file not shown.

View file

@ -355,6 +355,16 @@ await using (var node = await AetheriaStateNode.OpenAsync(statePath, "aetheria-s
StatModifierApplied = true,
StatModifierExecuted = true,
StatModifierTargetStatCount = 2
},
new AetheriaBehaviorStateSnapshot
{
OwnerKind = "equipment",
OwnerIndex = 0,
BehaviorIndex = 10,
BehaviorKind = "TurretController",
TurretControllerWeaponCount = 2,
TurretControllerShotSpeed = 80.0,
TurretControllerPredictShots = true
}
],
StatGrids =
@ -618,7 +628,7 @@ await using (var reopened = await AetheriaStateNode.OpenAsync(statePath, "aether
entitySnapshot.WeaponStates[0].BehaviorKind != "LockWeapon" ||
entitySnapshot.WeaponStates[0].LockProgress != 0.65 ||
entitySnapshot.WeaponStates[0].LockTargetEntityKey != entityKey.ToString() ||
entitySnapshot.BehaviorStates.Length != 10 ||
entitySnapshot.BehaviorStates.Length != 11 ||
entitySnapshot.BehaviorStates[0].BehaviorKind != "AetherDrive" ||
entitySnapshot.BehaviorStates[0].AetherDriveAxisX != 0.5 ||
entitySnapshot.BehaviorStates[0].AetherDriveRpmY != 900.0 ||
@ -651,6 +661,10 @@ await using (var reopened = await AetheriaStateNode.OpenAsync(statePath, "aether
!entitySnapshot.BehaviorStates[9].StatModifierApplied ||
!entitySnapshot.BehaviorStates[9].StatModifierExecuted ||
entitySnapshot.BehaviorStates[9].StatModifierTargetStatCount != 2 ||
entitySnapshot.BehaviorStates[10].BehaviorKind != "TurretController" ||
entitySnapshot.BehaviorStates[10].TurretControllerWeaponCount != 2 ||
entitySnapshot.BehaviorStates[10].TurretControllerShotSpeed != 80.0 ||
!entitySnapshot.BehaviorStates[10].TurretControllerPredictShots ||
entitySnapshot.StatGrids.Length != 1 ||
entitySnapshot.StatGrids[0].Values.Length != 4)
{

View file

@ -584,6 +584,16 @@ try
StatModifierApplied = true,
StatModifierExecuted = true,
StatModifierTargetStatCount = 2
},
new AetheriaRuntimeBehaviorStateCommit
{
OwnerKind = "equipment",
OwnerIndex = 14,
BehaviorIndex = 0,
BehaviorKind = "TurretController",
TurretControllerWeaponCount = 2,
TurretControllerShotSpeed = 80.0,
TurretControllerPredictShots = true
}
},
StatGrids = new[]
@ -704,7 +714,7 @@ try
entity.WeaponStates[3].BehaviorKind == "LockWeapon" &&
entity.WeaponStates[3].LockProgress == 0.65 &&
entity.WeaponStates[3].LockTargetEntityKey == "global:aetheria.run_state.smoke-run.zone.0.entity.0.v1" &&
entity.BehaviorStates.Length == 14 &&
entity.BehaviorStates.Length == 15 &&
entity.BehaviorStates[0].BehaviorKind == "Sensor" &&
entity.BehaviorStates[0].Pinging &&
entity.BehaviorStates[0].PingRadius == 1200.0 &&
@ -749,6 +759,10 @@ try
entity.BehaviorStates[13].StatModifierApplied &&
entity.BehaviorStates[13].StatModifierExecuted &&
entity.BehaviorStates[13].StatModifierTargetStatCount == 2 &&
entity.BehaviorStates[14].BehaviorKind == "TurretController" &&
entity.BehaviorStates[14].TurretControllerWeaponCount == 2 &&
entity.BehaviorStates[14].TurretControllerShotSpeed == 80.0 &&
entity.BehaviorStates[14].TurretControllerPredictShots &&
entity.WeaponGroups.Length == 1 &&
entity.StatGrids.Length == 2 &&
entity.StatGrids.Any(grid => grid.Name == "temperature" && grid.Values.Length == 2 && grid.Values[1] == 281.0) &&
@ -804,7 +818,7 @@ try
packageEntities[0].WeaponStates[2].AmmoIntervalProgress != 0.6 ||
packageEntities[0].WeaponStates[3].LockProgress != 0.65 ||
packageEntities[0].WeaponStates[3].LockTargetEntityKey != "global:aetheria.run_state.smoke-run.zone.0.entity.0.v1" ||
packageEntities[0].BehaviorStates.Count != 14 ||
packageEntities[0].BehaviorStates.Count != 15 ||
packageEntities[0].BehaviorStates[3].CapacitorCharge != 7.5 ||
packageEntities[0].BehaviorStates[4].AetherDriveRpmY != 900.0 ||
packageEntities[0].BehaviorStates[4].AetherDriveMaximumRpm != 2400.0 ||
@ -821,6 +835,9 @@ try
!packageEntities[0].BehaviorStates[12].TriggerPulled ||
!packageEntities[0].BehaviorStates[13].StatModifierApplied ||
packageEntities[0].BehaviorStates[13].StatModifierTargetStatCount != 2 ||
packageEntities[0].BehaviorStates[14].TurretControllerWeaponCount != 2 ||
packageEntities[0].BehaviorStates[14].TurretControllerShotSpeed != 80.0 ||
!packageEntities[0].BehaviorStates[14].TurretControllerPredictShots ||
packageEntities[0].StatGrids.Count != 2 ||
packageEntities[0].StatGrids.All(grid => grid.Name != "temperature" || grid.Values.Count != 2 || grid.Values[1] != 281.0))
{

View file

@ -519,7 +519,10 @@ public static class AetheriaRuntimeCommitLogApplier
TriggerPulled = state.TriggerPulled,
StatModifierApplied = state.StatModifierApplied,
StatModifierExecuted = state.StatModifierExecuted,
StatModifierTargetStatCount = state.StatModifierTargetStatCount
StatModifierTargetStatCount = state.StatModifierTargetStatCount,
TurretControllerWeaponCount = state.TurretControllerWeaponCount,
TurretControllerShotSpeed = state.TurretControllerShotSpeed,
TurretControllerPredictShots = state.TurretControllerPredictShots
})
.ToArray();
}

View file

@ -802,6 +802,15 @@ public sealed class AetheriaBehaviorStateSnapshot
[Key(51)]
public int StatModifierTargetStatCount { get; set; }
[Key(52)]
public int TurretControllerWeaponCount { get; set; }
[Key(53)]
public double TurretControllerShotSpeed { get; set; }
[Key(54)]
public bool TurretControllerPredictShots { get; set; }
}
[MessagePackObject]

View file

@ -981,6 +981,15 @@ public class ActionGameManager : MonoBehaviour
StatModifierTargetStatCount = statModifier.TargetStatCount
};
}
else if (behavior is TurretController turretController)
{
state = new AetheriaRuntimeBehaviorStateCommit
{
TurretControllerWeaponCount = turretController.WeaponCount,
TurretControllerShotSpeed = turretController.ShotSpeed,
TurretControllerPredictShots = turretController.PredictShots
};
}
if (state == null)
continue;

View file

@ -27,6 +27,10 @@ public class TurretController : Behavior, IInitializableBehavior
private float _shotSpeed;
private bool _predictShots;
public int WeaponCount => _weapons.Count;
public float ShotSpeed => _shotSpeed;
public bool PredictShots => _predictShots;
public TurretController(TurretControllerData data, EquippedItem item) : base(data, item)
{
_data = data;

View file

@ -1119,7 +1119,10 @@ namespace GameCult.Aetheria.State.Unity
bool triggerPulled,
bool statModifierApplied,
bool statModifierExecuted,
int statModifierTargetStatCount)
int statModifierTargetStatCount,
int turretControllerWeaponCount,
double turretControllerShotSpeed,
bool turretControllerPredictShots)
{
OwnerKind = ownerKind;
OwnerIndex = ownerIndex;
@ -1173,6 +1176,9 @@ namespace GameCult.Aetheria.State.Unity
StatModifierApplied = statModifierApplied;
StatModifierExecuted = statModifierExecuted;
StatModifierTargetStatCount = statModifierTargetStatCount;
TurretControllerWeaponCount = turretControllerWeaponCount;
TurretControllerShotSpeed = turretControllerShotSpeed;
TurretControllerPredictShots = turretControllerPredictShots;
}
public string OwnerKind { get; }
@ -1227,5 +1233,8 @@ namespace GameCult.Aetheria.State.Unity
public bool StatModifierApplied { get; }
public bool StatModifierExecuted { get; }
public int StatModifierTargetStatCount { get; }
public int TurretControllerWeaponCount { get; }
public double TurretControllerShotSpeed { get; }
public bool TurretControllerPredictShots { get; }
}
}

View file

@ -909,7 +909,10 @@ namespace GameCult.Aetheria.State.Unity
var statModifierApplied = ReadFieldBool(ref reader, behaviorFields, 49);
var statModifierExecuted = ReadFieldBool(ref reader, behaviorFields, 50);
var statModifierTargetStatCount = ReadFieldInt32(ref reader, behaviorFields, 51);
SkipRemaining(ref reader, behaviorFields, 52);
var turretControllerWeaponCount = ReadFieldInt32(ref reader, behaviorFields, 52);
var turretControllerShotSpeed = ReadFieldDouble(ref reader, behaviorFields, 53);
var turretControllerPredictShots = ReadFieldBool(ref reader, behaviorFields, 54);
SkipRemaining(ref reader, behaviorFields, 55);
behaviorStates[behavior] = new AetheriaRuntimeBehaviorStateSnapshot(
ownerKind,
ownerIndex,
@ -962,7 +965,10 @@ namespace GameCult.Aetheria.State.Unity
triggerPulled,
statModifierApplied,
statModifierExecuted,
statModifierTargetStatCount);
statModifierTargetStatCount,
turretControllerWeaponCount,
turretControllerShotSpeed,
turretControllerPredictShots);
}
return behaviorStates;

View file

@ -851,5 +851,14 @@ namespace GameCult.Aetheria.State.Unity
[Key(51)]
public int StatModifierTargetStatCount { get; set; }
[Key(52)]
public int TurretControllerWeaponCount { get; set; }
[Key(53)]
public double TurretControllerShotSpeed { get; set; }
[Key(54)]
public bool TurretControllerPredictShots { get; set; }
}
}

View file

@ -179,7 +179,8 @@ legacy catalog cache, and legacy UI paths should be migration-only or deleted.
energy usage, velocity limiters publish evaluated limit, and thermotoggles
publish their active target temperature. Switches publish activated state,
triggers publish pending pulled state, and stat modifiers publish
applied/executed flags plus target-stat count.
applied/executed flags plus target-stat count. Turret controllers publish
initialized weapon count, shot speed, and predictive-aim flag.
`RuntimeEntityBlueprint` still exists for construction/loadout projection,
but those live hull grids and session scalars are not blueprint fields and
cannot be restored by the old blueprint projector path.
@ -828,6 +829,8 @@ First Aetheria surfaces to publish:
pending pull state.
- Done: extend behavior state rows for StatModifier applied/executed flags
and target-stat count.
- Done: extend behavior state rows for TurretController initialized weapon
count, shot speed, and predictive-aim flag.
- Done: cut live simulation grids out of `RuntimeEntityBlueprint`; loadout
and construction templates no longer capture or restore temperature,
armor, max-armor, or hull-conductivity state.