diff --git a/.voidbot/state/aetheria.cc b/.voidbot/state/aetheria.cc index c52f00738..ddac8ac58 100644 Binary files a/.voidbot/state/aetheria.cc and b/.voidbot/state/aetheria.cc differ diff --git a/Aetheria.State.Smoke/Program.cs b/Aetheria.State.Smoke/Program.cs index eb583e3c6..5652cae12 100644 --- a/Aetheria.State.Smoke/Program.cs +++ b/Aetheria.State.Smoke/Program.cs @@ -271,6 +271,19 @@ await using (var node = await AetheriaStateNode.OpenAsync(statePath, "aetheria-s AetherDriveMaximumRpm = 2400.0, AetherDriveThrustDirectionX = 1.5, AetherDriveThrustDirectionY = -0.5 + }, + new AetheriaBehaviorStateSnapshot + { + OwnerKind = "equipment", + OwnerIndex = 0, + BehaviorIndex = 1, + BehaviorKind = "ResourceScanner", + ResourceScannerTargetBodyId = "smoke:body", + ResourceScannerAsteroidIndex = 2, + ResourceScannerScanTime = 1.25, + ResourceScannerRange = 500.0, + ResourceScannerMinimumDensity = 0.2, + ResourceScannerScanDuration = 3.5 } ], StatGrids = @@ -534,11 +547,16 @@ 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 != 1 || + entitySnapshot.BehaviorStates.Length != 2 || entitySnapshot.BehaviorStates[0].BehaviorKind != "AetherDrive" || entitySnapshot.BehaviorStates[0].AetherDriveAxisX != 0.5 || entitySnapshot.BehaviorStates[0].AetherDriveRpmY != 900.0 || entitySnapshot.BehaviorStates[0].AetherDriveMaximumRpm != 2400.0 || + entitySnapshot.BehaviorStates[1].BehaviorKind != "ResourceScanner" || + entitySnapshot.BehaviorStates[1].ResourceScannerTargetBodyId != "smoke:body" || + entitySnapshot.BehaviorStates[1].ResourceScannerAsteroidIndex != 2 || + entitySnapshot.BehaviorStates[1].ResourceScannerScanTime != 1.25 || + entitySnapshot.BehaviorStates[1].ResourceScannerScanDuration != 3.5 || entitySnapshot.StatGrids.Length != 1 || entitySnapshot.StatGrids[0].Values.Length != 4) { diff --git a/Aetheria.State.Unity.Smoke/Program.cs b/Aetheria.State.Unity.Smoke/Program.cs index 9fffce4cb..facd73adb 100644 --- a/Aetheria.State.Unity.Smoke/Program.cs +++ b/Aetheria.State.Unity.Smoke/Program.cs @@ -500,6 +500,19 @@ try AetherDriveMaximumRpm = 2400.0, AetherDriveThrustDirectionX = 1.5, AetherDriveThrustDirectionY = -0.5 + }, + new AetheriaRuntimeBehaviorStateCommit + { + OwnerKind = "equipment", + OwnerIndex = 5, + BehaviorIndex = 0, + BehaviorKind = "ResourceScanner", + ResourceScannerTargetBodyId = "smoke:body", + ResourceScannerAsteroidIndex = 2, + ResourceScannerScanTime = 1.25, + ResourceScannerRange = 500.0, + ResourceScannerMinimumDensity = 0.2, + ResourceScannerScanDuration = 3.5 } }, StatGrids = new[] @@ -620,7 +633,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 == 5 && + entity.BehaviorStates.Length == 6 && entity.BehaviorStates[0].BehaviorKind == "Sensor" && entity.BehaviorStates[0].Pinging && entity.BehaviorStates[0].PingRadius == 1200.0 && @@ -637,6 +650,11 @@ try entity.BehaviorStates[4].AetherDriveRpmY == 900.0 && entity.BehaviorStates[4].AetherDriveMaximumRpm == 2400.0 && entity.BehaviorStates[4].AetherDriveThrustDirectionY == -0.5 && + entity.BehaviorStates[5].BehaviorKind == "ResourceScanner" && + entity.BehaviorStates[5].ResourceScannerTargetBodyId == "smoke:body" && + entity.BehaviorStates[5].ResourceScannerAsteroidIndex == 2 && + entity.BehaviorStates[5].ResourceScannerScanTime == 1.25 && + entity.BehaviorStates[5].ResourceScannerScanDuration == 3.5 && entity.WeaponGroups.Length == 1 && entity.StatGrids.Length == 2 && entity.StatGrids.Any(grid => grid.Name == "temperature" && grid.Values.Length == 2 && grid.Values[1] == 281.0) && @@ -692,10 +710,12 @@ 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 != 5 || + packageEntities[0].BehaviorStates.Count != 6 || packageEntities[0].BehaviorStates[3].CapacitorCharge != 7.5 || packageEntities[0].BehaviorStates[4].AetherDriveRpmY != 900.0 || packageEntities[0].BehaviorStates[4].AetherDriveMaximumRpm != 2400.0 || + packageEntities[0].BehaviorStates[5].ResourceScannerTargetBodyId != "smoke:body" || + packageEntities[0].BehaviorStates[5].ResourceScannerScanDuration != 3.5 || packageEntities[0].StatGrids.Count != 2 || packageEntities[0].StatGrids.All(grid => grid.Name != "temperature" || grid.Values.Count != 2 || grid.Values[1] != 281.0)) { diff --git a/Aetheria.State/AetheriaRuntimeCommitLogApplier.cs b/Aetheria.State/AetheriaRuntimeCommitLogApplier.cs index e9ba509b4..fdf325764 100644 --- a/Aetheria.State/AetheriaRuntimeCommitLogApplier.cs +++ b/Aetheria.State/AetheriaRuntimeCommitLogApplier.cs @@ -498,7 +498,13 @@ public static class AetheriaRuntimeCommitLogApplier AetherDriveRpmZ = state.AetherDriveRpmZ, AetherDriveMaximumRpm = state.AetherDriveMaximumRpm, AetherDriveThrustDirectionX = state.AetherDriveThrustDirectionX, - AetherDriveThrustDirectionY = state.AetherDriveThrustDirectionY + AetherDriveThrustDirectionY = state.AetherDriveThrustDirectionY, + ResourceScannerTargetBodyId = state.ResourceScannerTargetBodyId ?? "", + ResourceScannerAsteroidIndex = state.ResourceScannerAsteroidIndex, + ResourceScannerScanTime = state.ResourceScannerScanTime, + ResourceScannerRange = state.ResourceScannerRange, + ResourceScannerMinimumDensity = state.ResourceScannerMinimumDensity, + ResourceScannerScanDuration = state.ResourceScannerScanDuration }) .ToArray(); } diff --git a/Aetheria.State/Documents/AetheriaRuntimeStateDocuments.cs b/Aetheria.State/Documents/AetheriaRuntimeStateDocuments.cs index da379161e..82da58689 100644 --- a/Aetheria.State/Documents/AetheriaRuntimeStateDocuments.cs +++ b/Aetheria.State/Documents/AetheriaRuntimeStateDocuments.cs @@ -739,6 +739,24 @@ public sealed class AetheriaBehaviorStateSnapshot [Key(30)] public double AetherDriveThrustDirectionY { get; set; } + + [Key(31)] + public string ResourceScannerTargetBodyId { get; set; } = ""; + + [Key(32)] + public int ResourceScannerAsteroidIndex { get; set; } = -1; + + [Key(33)] + public double ResourceScannerScanTime { get; set; } + + [Key(34)] + public double ResourceScannerRange { get; set; } + + [Key(35)] + public double ResourceScannerMinimumDensity { get; set; } + + [Key(36)] + public double ResourceScannerScanDuration { get; set; } } [MessagePackObject] diff --git a/Assets/Scripts/Gameplay/ActionGameManager.cs b/Assets/Scripts/Gameplay/ActionGameManager.cs index dd66e9b9c..0ed741c36 100644 --- a/Assets/Scripts/Gameplay/ActionGameManager.cs +++ b/Assets/Scripts/Gameplay/ActionGameManager.cs @@ -902,6 +902,20 @@ public class ActionGameManager : MonoBehaviour AetherDriveThrustDirectionY = drive.ThrustDirection.y }; } + else if (behavior is ResourceScanner resourceScanner) + { + state = new AetheriaRuntimeBehaviorStateCommit + { + ResourceScannerTargetBodyId = resourceScanner.ScanTarget == Guid.Empty + ? "" + : resourceScanner.ScanTarget.ToString("D"), + ResourceScannerAsteroidIndex = resourceScanner.Asteroid, + ResourceScannerScanTime = resourceScanner.ScanTime, + ResourceScannerRange = resourceScanner.Range, + ResourceScannerMinimumDensity = resourceScanner.MinimumDensity, + ResourceScannerScanDuration = resourceScanner.ScanDuration + }; + } if (state == null) continue; diff --git a/Assets/Scripts/ServerShared/Behaviors/ResourceScanner.cs b/Assets/Scripts/ServerShared/Behaviors/ResourceScanner.cs index f2d657267..3acd6078d 100644 --- a/Assets/Scripts/ServerShared/Behaviors/ResourceScanner.cs +++ b/Assets/Scripts/ServerShared/Behaviors/ResourceScanner.cs @@ -41,6 +41,7 @@ public class ResourceScanner : Behavior, IAlwaysUpdatedBehavior public float Range { get; private set; } public float MinimumDensity { get; private set; } public float ScanDuration { get; private set; } + public float ScanTime => _scanTime; public Guid ScanTarget { diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogSnapshot.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogSnapshot.cs index 8e18c9cb4..e7f2cc0f8 100644 --- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogSnapshot.cs +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogSnapshot.cs @@ -1098,7 +1098,13 @@ namespace GameCult.Aetheria.State.Unity double aetherDriveRpmZ, double aetherDriveMaximumRpm, double aetherDriveThrustDirectionX, - double aetherDriveThrustDirectionY) + double aetherDriveThrustDirectionY, + string resourceScannerTargetBodyId, + int resourceScannerAsteroidIndex, + double resourceScannerScanTime, + double resourceScannerRange, + double resourceScannerMinimumDensity, + double resourceScannerScanDuration) { OwnerKind = ownerKind; OwnerIndex = ownerIndex; @@ -1131,6 +1137,12 @@ namespace GameCult.Aetheria.State.Unity AetherDriveMaximumRpm = aetherDriveMaximumRpm; AetherDriveThrustDirectionX = aetherDriveThrustDirectionX; AetherDriveThrustDirectionY = aetherDriveThrustDirectionY; + ResourceScannerTargetBodyId = resourceScannerTargetBodyId; + ResourceScannerAsteroidIndex = resourceScannerAsteroidIndex; + ResourceScannerScanTime = resourceScannerScanTime; + ResourceScannerRange = resourceScannerRange; + ResourceScannerMinimumDensity = resourceScannerMinimumDensity; + ResourceScannerScanDuration = resourceScannerScanDuration; } public string OwnerKind { get; } @@ -1164,5 +1176,11 @@ namespace GameCult.Aetheria.State.Unity public double AetherDriveMaximumRpm { get; } public double AetherDriveThrustDirectionX { get; } public double AetherDriveThrustDirectionY { get; } + public string ResourceScannerTargetBodyId { get; } + public int ResourceScannerAsteroidIndex { get; } + public double ResourceScannerScanTime { get; } + public double ResourceScannerRange { get; } + public double ResourceScannerMinimumDensity { get; } + public double ResourceScannerScanDuration { get; } } } diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogStore.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogStore.cs index c7c8f29b8..ca4230738 100644 --- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogStore.cs +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogStore.cs @@ -888,7 +888,13 @@ namespace GameCult.Aetheria.State.Unity var aetherDriveMaximumRpm = ReadFieldDouble(ref reader, behaviorFields, 28); var aetherDriveThrustDirectionX = ReadFieldDouble(ref reader, behaviorFields, 29); var aetherDriveThrustDirectionY = ReadFieldDouble(ref reader, behaviorFields, 30); - SkipRemaining(ref reader, behaviorFields, 31); + var resourceScannerTargetBodyId = ReadFieldString(ref reader, behaviorFields, 31); + var resourceScannerAsteroidIndex = ReadFieldInt32(ref reader, behaviorFields, 32); + var resourceScannerScanTime = ReadFieldDouble(ref reader, behaviorFields, 33); + var resourceScannerRange = ReadFieldDouble(ref reader, behaviorFields, 34); + var resourceScannerMinimumDensity = ReadFieldDouble(ref reader, behaviorFields, 35); + var resourceScannerScanDuration = ReadFieldDouble(ref reader, behaviorFields, 36); + SkipRemaining(ref reader, behaviorFields, 37); behaviorStates[behavior] = new AetheriaRuntimeBehaviorStateSnapshot( ownerKind, ownerIndex, @@ -920,7 +926,13 @@ namespace GameCult.Aetheria.State.Unity aetherDriveRpmZ, aetherDriveMaximumRpm, aetherDriveThrustDirectionX, - aetherDriveThrustDirectionY); + aetherDriveThrustDirectionY, + resourceScannerTargetBodyId, + resourceScannerAsteroidIndex, + resourceScannerScanTime, + resourceScannerRange, + resourceScannerMinimumDensity, + resourceScannerScanDuration); } return behaviorStates; diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeStateCommitDocument.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeStateCommitDocument.cs index d326acda9..b2837b3ea 100644 --- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeStateCommitDocument.cs +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeStateCommitDocument.cs @@ -788,5 +788,23 @@ namespace GameCult.Aetheria.State.Unity [Key(30)] public double AetherDriveThrustDirectionY { get; set; } + + [Key(31)] + public string ResourceScannerTargetBodyId { get; set; } = ""; + + [Key(32)] + public int ResourceScannerAsteroidIndex { get; set; } = -1; + + [Key(33)] + public double ResourceScannerScanTime { get; set; } + + [Key(34)] + public double ResourceScannerRange { get; set; } + + [Key(35)] + public double ResourceScannerMinimumDensity { get; set; } + + [Key(36)] + public double ResourceScannerScanDuration { get; set; } } } diff --git a/docs/aetheria-perfect-machine-map.md b/docs/aetheria-perfect-machine-map.md index c8b379c9b..5f2775c9a 100644 --- a/docs/aetheria-perfect-machine-map.md +++ b/docs/aetheria-perfect-machine-map.md @@ -171,7 +171,9 @@ legacy catalog cache, and legacy UI paths should be migration-only or deleted. for ping state, radiator temperature/throughput, reactor draw/load, and capacitor charge/capacity/efficiency. Aether drives publish typed axis, thrust, RPM, maximum RPM, and thrust-direction rows, so rotor simulation - state is no longer trapped inside live behavior memory. + state is no longer trapped inside live behavior memory. Resource scanners + publish target body id, asteroid index, scan timer, range, minimum density, + and scan duration rows. `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. @@ -806,6 +808,9 @@ First Aetheria surfaces to publish: - Done: extend behavior state rows for AetherDrive axis, thrust, RPM, maximum RPM, and thrust direction, so drive rotor state has a typed snapshot/readback path. + - Done: extend behavior state rows for ResourceScanner target body, + asteroid index, scan timer, range, minimum density, and duration, so + scanner progress is typed state instead of live-only behavior memory. - 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.