diff --git a/Aetheria.State.Daemon.Smoke/Program.cs b/Aetheria.State.Daemon.Smoke/Program.cs
index fe64ceb6c..bfe71ac2b 100644
--- a/Aetheria.State.Daemon.Smoke/Program.cs
+++ b/Aetheria.State.Daemon.Smoke/Program.cs
@@ -15,6 +15,8 @@ internal sealed class AetheriaDaemonYmirSmokeChecks
ChargedWeaponCannotBypassChargeLifecycle();
ChargedWeaponHoldRiskMalfunctionsDeterministically();
DeployableWeaponRunsThroughYmirAndDetonatesOnDaemon();
+ DeployableRangeExpiryDetonatesAfterYmirMovement();
+ CanonicalCatalogPublishesRecoveredMineLauncher();
DaemonSimulationTreatsYmirHitAsPresentationOnly();
ProjectileContactCannotKill();
MissingPhysicsOwnerCannotAdvanceProjectiles();
@@ -240,7 +242,8 @@ internal sealed class AetheriaDaemonYmirSmokeChecks
var payload = new AetheriaRuntimeBehaviorPayload(0, AetheriaRuntimeBehaviorKinds.DeployableWeapon, 0,
[
new AetheriaRuntimeBehaviorField(2, PerformanceStat(35)),
- new AetheriaRuntimeBehaviorField(6, PerformanceStat(10)),
+ new AetheriaRuntimeBehaviorField(6, PerformanceStat(100)),
+ new AetheriaRuntimeBehaviorField(16, PerformanceStat(10)),
new AetheriaRuntimeBehaviorField(17, PerformanceStat(1)),
new AetheriaRuntimeBehaviorField(19, PerformanceStat(1)),
new AetheriaRuntimeBehaviorField(26, Number(0.2)),
@@ -301,6 +304,77 @@ internal sealed class AetheriaDaemonYmirSmokeChecks
"daemon must publish one authoritative detonation event");
}
+ private static void DeployableRangeExpiryDetonatesAfterYmirMovement()
+ {
+ var source = Entity(0, -50, "player");
+ var target = Entity(1, 35, "neutral");
+ var zone = new AetheriaRuntimeZoneSnapshotCommit
+ {
+ ZoneIndex = 0,
+ Entities = [source, target],
+ PhysicalPayloads = [new AetheriaRuntimePhysicalPayloadCommit
+ {
+ PayloadId = "range-expiry-mine",
+ PayloadKind = "mine",
+ WeaponItemKey = "canonical-mine",
+ SourceEntityIndex = source.EntityIndex,
+ PositionX = 5,
+ VelocityX = 20,
+ Radius = 1,
+ ActivationDelaySeconds = 10,
+ LifetimeSeconds = 30,
+ MaximumSourceDistance = 6,
+ BlastRadius = 50,
+ PayloadMagnitude = 15,
+ TriggeredAtSeconds = -1,
+ Active = true
+ }]
+ };
+ var run = new AetheriaRuntimeRunCheckpointCommit
+ {
+ RunId = "range-expiry-smoke", CurrentZoneIndex = 0,
+ CurrentEntityKey = "zone.0.entity.0", Zones = [zone]
+ };
+
+ AetheriaRuntimeDaemonSimulation.Step(run, new AetheriaRuntimeDaemonIntentState(), 0.1,
+ AetheriaRuntimeDaemonSimulationSettings.AetheriaDefault,
+ new AetheriaYmirPhysicalPayloadPhysics(), new AetheriaYmirWorldPhysics(), frameId: 4,
+ simulationTimeSeconds: 0.1);
+
+ RequireEqual(0, zone.PhysicalPayloads.Count,
+ "mine crossing source-relative range after Ymir movement must detonate in the same tick");
+ RequireNear(85, Stat(target, "hull"), 0.000001,
+ "range expiry must preserve the fossil splash consequence");
+ Require(run.GameEvents.Any(value => value.Kind == "deployable.expired" &&
+ value.Reason == "range" && value.ItemKey == "canonical-mine"),
+ "daemon must explain range-triggered expiry with stable item identity");
+ Require(run.GameEvents.Any(value => value.Kind == "deployable.detonated" &&
+ value.Reason == "range" && value.ItemKey == "canonical-mine"),
+ "range expiry must flow through the shared detonation transaction");
+ }
+
+ private static void CanonicalCatalogPublishesRecoveredMineLauncher()
+ {
+ var catalog = AetheriaRuntimeCatalogStore.OpenReadOnly(
+ Path.Combine(Directory.GetCurrentDirectory(), "GameData", "aetheria-world.cc"));
+ var mine = catalog.Items.Single(item =>
+ item.ItemKey == "aetheria.item_definition:legacy:e78d3670-3ac6-4d9b-834c-1da4228ac311");
+ RequireEqual("Mine Launcher", mine.Name, "recovered item must retain fossil identity");
+ RequireEqual("Mine", mine.WeaponType, "recovered item must publish normalized semantic weapon type");
+ var behavior = mine.BehaviorPayloads.Single(value =>
+ value.Kind == AetheriaRuntimeBehaviorKinds.DeployableWeapon);
+ RequireNear(2, behavior.Fields.Single(field => field.Key == 26).Value.NumberValue, 0.000001,
+ "catalog mine must retain two-second activation delay");
+ RequireNear(30, behavior.Fields.Single(field => field.Key == 27).Value.NumberValue, 0.000001,
+ "catalog mine must retain thirty-second lifetime");
+ var manifest = AetheriaRuntimeAssets.ProjectManifest(catalog);
+ var asset = manifest.Assets.Single(value => value.Ref.AssetKey == "prefab.entity.mine");
+ RequireEqual("physical-payload.mine", asset.Ref.Metadata["presentationRole"],
+ "provider must advertise the script-free mine presentation role");
+ RequireEqual("0.25", asset.Ref.Metadata["triggeredPulseSeconds"],
+ "mine presentation must retain the fossil triggered pulse cadence");
+ }
+
private static void ChargedWeaponHoldRiskMalfunctionsDeterministically()
{
var source = Entity(0, 0, "player");
diff --git a/Aetheria.State.Import/Aetheria.State.Import.csproj b/Aetheria.State.Import/Aetheria.State.Import.csproj
index ca788873d..54401dee9 100644
--- a/Aetheria.State.Import/Aetheria.State.Import.csproj
+++ b/Aetheria.State.Import/Aetheria.State.Import.csproj
@@ -9,5 +9,6 @@
+
diff --git a/Aetheria.State.Import/Program.cs b/Aetheria.State.Import/Program.cs
index 13a92c89e..fc77c85c2 100644
--- a/Aetheria.State.Import/Program.cs
+++ b/Aetheria.State.Import/Program.cs
@@ -2,6 +2,7 @@ using System.Security.Cryptography;
using Aetheria.State;
using Aetheria.State.Documents;
using Aetheria.State.Migration;
+using GameCult.Aetheria.State.Verse;
using MessagePack;
var root = args.Length > 0 ? Path.GetFullPath(args[0]) : Directory.GetCurrentDirectory();
@@ -13,17 +14,25 @@ var outputStatePath = Path.GetRelativePath(root, statePath).Replace('\\', '/');
var gameData = Path.Combine(root, "GameData");
var catalogPath = Path.Combine(gameData, "AetherDB.msgpack");
+var supplementalCatalogPath = Path.Combine(gameData, "Legacy", "AetherDB.2021-03-05.msgpack");
var nameFilesRoot = Path.Combine(gameData, "NameFile");
var capturedAtUtc = DateTimeOffset.UtcNow.ToString("O");
var catalog = CaptureFile(root, catalogPath);
+var supplementalCatalog = CaptureFile(root, supplementalCatalogPath);
var nameFiles = Directory.Exists(nameFilesRoot)
? Directory.EnumerateFiles(nameFilesRoot, "*.msgpack", SearchOption.TopDirectoryOnly)
.OrderBy(path => path, StringComparer.OrdinalIgnoreCase)
.Select(path => CaptureFile(root, path))
.ToArray()
: [];
-var entries = LegacyCatalogReader.Read(catalogPath);
+var currentEntries = LegacyCatalogReader.Read(catalogPath);
+var currentLegacyIds = currentEntries.Select(entry => entry.Summary.LegacyId).ToHashSet(StringComparer.Ordinal);
+var supplementalMineEntries = LegacyCatalogReader.Read(supplementalCatalogPath)
+ .Where(entry => entry.ItemDefinition != null && IsMineItem(entry.ItemDefinition))
+ .Where(entry => !currentLegacyIds.Contains(entry.Summary.LegacyId))
+ .ToArray();
+var entries = currentEntries.Concat(supplementalMineEntries).ToArray();
var nameFileEntries = Directory.Exists(nameFilesRoot)
? Directory.EnumerateFiles(nameFilesRoot, "*.msgpack", SearchOption.TopDirectoryOnly)
.OrderBy(path => path, StringComparer.OrdinalIgnoreCase)
@@ -32,7 +41,7 @@ var nameFileEntries = Directory.Exists(nameFilesRoot)
: [];
var itemDefinitions = entries
.Where(entry => entry.ItemDefinition != null)
- .Select(entry => entry.ItemDefinition!)
+ .Select(entry => ProjectDeployableWeapon(entry.ItemDefinition!))
.ToArray();
var corporations = entries
.Where(entry => entry.Corporation != null)
@@ -43,6 +52,97 @@ var parsedNameFiles = entries.Concat(nameFileEntries)
.Select(entry => entry.NameFile!)
.ToArray();
+static AetheriaItemDefinition ProjectDeployableWeapon(AetheriaItemDefinition item)
+{
+ if (!IsMineItem(item))
+ return item;
+
+ item.WeaponType = "Mine";
+ item.BehaviorPayloads = (item.BehaviorPayloads ?? [])
+ .Select(behavior => string.Equals(behavior.Kind, "InstantWeapon", StringComparison.Ordinal)
+ ? new AetheriaBehaviorPayload
+ {
+ UnionKey = behavior.UnionKey,
+ Kind = "DeployableWeapon",
+ Group = behavior.Group,
+ Fields = behavior.Fields
+ .Where(field => field.Key < 26 || field.Key > 30)
+ .Concat([
+ NumberBehaviorField(26, 2),
+ NumberBehaviorField(27, 30),
+ PerformanceBehaviorField(28, 25),
+ NumberBehaviorField(29, 2),
+ PerformanceBehaviorField(30, 25)
+ ])
+ .OrderBy(field => field.Key)
+ .ToArray()
+ }
+ : behavior)
+ .ToArray();
+ item.BehaviorKinds = item.BehaviorPayloads
+ .Select(behavior => behavior.Kind)
+ .Distinct(StringComparer.Ordinal)
+ .OrderBy(kind => kind, StringComparer.Ordinal)
+ .ToArray();
+ item.BehaviorCount = item.BehaviorPayloads.Length;
+ item.Tags = (item.Tags ?? [])
+ .Where(tag => !string.Equals(tag, "behavior:InstantWeapon", StringComparison.Ordinal))
+ .Concat(["behavior:DeployableWeapon"])
+ .Distinct(StringComparer.Ordinal)
+ .ToArray();
+ return item;
+}
+
+static bool IsMineItem(AetheriaItemDefinition item)
+{
+ var hasMineEffect = (item.BehaviorPayloads ?? []).Any(behavior =>
+ string.Equals(behavior.Kind, "InstantWeapon", StringComparison.Ordinal) &&
+ behavior.Fields.Any(field => field.Key == 8 &&
+ field.Value.StringValue.EndsWith("/Mine Launcher.prefab", StringComparison.OrdinalIgnoreCase)));
+ return string.Equals(item.WeaponType, "Mine", StringComparison.Ordinal) || hasMineEffect;
+}
+
+static AetheriaBehaviorField NumberBehaviorField(int key, double value) => new()
+{
+ Key = key,
+ Value = new AetheriaBehaviorValue { Kind = "number", NumberValue = value }
+};
+
+static AetheriaBehaviorField PerformanceBehaviorField(int key, double value) => new()
+{
+ Key = key,
+ Value = new AetheriaBehaviorValue
+ {
+ Kind = "performance-stat",
+ Children =
+ [
+ new AetheriaBehaviorValue { Kind = "number", NumberValue = value },
+ new AetheriaBehaviorValue { Kind = "number", NumberValue = value },
+ new AetheriaBehaviorValue { Kind = "number" },
+ new AetheriaBehaviorValue { Kind = "number" },
+ new AetheriaBehaviorValue { Kind = "number" }
+ ]
+ }
+};
+
+if (args.Any(argument => string.Equals(argument, "--merge-mine", StringComparison.Ordinal)))
+{
+ var mine = itemDefinitions.SingleOrDefault(item => string.Equals(item.WeaponType, "Mine", StringComparison.Ordinal))
+ ?? throw new InvalidDataException("Supplemental catalog did not contain the recovered Mine Launcher item.");
+ await using var mergeNode = await AetheriaStateNode.OpenAsync(statePath, "aetheria-mine-catalog-merge");
+ await mergeNode.MutableDocument(AetheriaCatalogKeys.ItemDefinitionFromLegacyId(mine.LegacyId))
+ .ReplaceAsync(mine);
+ await mergeNode.FlushAsync();
+ var merged = await mergeNode.RuntimeCatalog().LatestAsync().ConfigureAwait(false);
+ var mergedMine = merged.FindItem(mine.ItemKey);
+ if (mergedMine == null || !mergedMine.BehaviorPayloads.Any(behavior =>
+ string.Equals(behavior.Kind, "DeployableWeapon", StringComparison.Ordinal)))
+ throw new InvalidDataException("Mine Launcher merge did not become visible through the runtime catalog.");
+ Console.WriteLine($"Merged Mine Launcher into typed state: {statePath}");
+ Console.WriteLine($"Item: {mine.ItemKey}");
+ return;
+}
+
ResetMaterializedStateOutput(statePath);
await using var node = await AetheriaStateNode.OpenAsync(statePath, "aetheria-legacy-catalog-import");
@@ -56,10 +156,12 @@ await node.MutableDocument(AetheriaStateNode.Le
CatalogFingerprint = catalog.Fingerprint,
CatalogBytes = catalog.Bytes,
NameFiles = nameFiles,
+ SupplementalCatalogFiles = supplementalCatalog.Bytes > 0 ? [supplementalCatalog] : [],
Notes =
[
"This document quarantines legacy catalog file facts and records the bounded raw payload mapping pass. It does not grant old MessagePack files runtime state authority.",
- "AetherDB.msgpack and NameFile/*.msgpack remain migration inputs until Unity runtime reads typed Aetheria catalog documents directly."
+ "AetherDB.msgpack and NameFile/*.msgpack remain migration inputs until Unity runtime reads typed Aetheria catalog documents directly.",
+ "The 2021-03-05 supplemental catalog contributes only the missing Mine Launcher item and remains quarantined provenance, not runtime authority."
]
});
@@ -131,6 +233,21 @@ foreach (var nameFile in parsedNameFiles)
await node.FlushAsync();
var runtimeCatalog = await node.RuntimeCatalog().LatestAsync().ConfigureAwait(false);
+var runtimeMines = runtimeCatalog.Items
+ .Where(item => string.Equals(item.WeaponType, "Mine", StringComparison.Ordinal))
+ .ToArray();
+if (runtimeMines.Any(item => !item.BehaviorPayloads.Any(behavior =>
+ string.Equals(behavior.Kind, "DeployableWeapon", StringComparison.Ordinal) &&
+ HasNumber(behavior, 26, 2) &&
+ HasNumber(behavior, 27, 30) &&
+ HasPerformanceStat(behavior, 28, 25) &&
+ HasNumber(behavior, 29, 2) &&
+ HasPerformanceStat(behavior, 30, 25))))
+{
+ var diagnostic = string.Join("; ", runtimeMines.Select(item =>
+ $"{item.Name} [{string.Join(",", item.BehaviorPayloads.Select(behavior => behavior.Kind + ":" + string.Join("/", behavior.Fields.Select(field => field.Key))))}]"));
+ throw new InvalidDataException($"Mine catalog import did not preserve the canonical deployable lifecycle: {diagnostic}");
+}
await node.MutableDocument(AetheriaStateNode.TradeValuePolicyKey)
.ReplaceAsync(AetheriaRuntimeStateMapper.ToTradeValuePolicy(
runtimeCatalog.TradeValueSettings,
@@ -146,6 +263,18 @@ Console.WriteLine($"Name files: {nameFiles.Length}");
Console.WriteLine($"Mapped items: {itemDefinitions.Length}");
Console.WriteLine($"Mapped factions: {corporations.Length}");
Console.WriteLine($"Mapped name files: {parsedNameFiles.Length}");
+Console.WriteLine($"Mapped mine weapons: {string.Join(", ", runtimeMines.Select(item => item.Name + " (" + item.ItemKey + ")"))}");
+
+static bool HasNumber(AetheriaRuntimeBehaviorPayload behavior, int key, double expected) =>
+ behavior.Fields.Any(field => field.Key == key && Math.Abs(field.Value.NumberValue - expected) < 0.000001);
+
+static bool HasPerformanceStat(AetheriaRuntimeBehaviorPayload behavior, int key, double expected)
+{
+ var field = behavior.Fields.FirstOrDefault(candidate => candidate.Key == key);
+ return field?.Value.Children.Count >= 2 &&
+ Math.Abs(field.Value.Children[0].NumberValue - expected) < 0.000001 &&
+ Math.Abs(field.Value.Children[1].NumberValue - expected) < 0.000001;
+}
static AetheriaLegacyCatalogFile CaptureFile(string root, string path)
{
diff --git a/Aetheria.State.Verify/Program.cs b/Aetheria.State.Verify/Program.cs
index 3d9a8867b..2b78739b0 100644
--- a/Aetheria.State.Verify/Program.cs
+++ b/Aetheria.State.Verify/Program.cs
@@ -17110,7 +17110,10 @@ static void RequireUnityPhysicsIsNotGameplayAuthority(string root)
Path.Combine(root, "Assets", "Scripts", "Gameplay", "Weapons", "ConstantLightningManager.cs"),
Path.Combine(root, "Assets", "Scripts", "Gameplay", "Weapons", "ConstantParticleWeapon.cs"),
Path.Combine(root, "Assets", "Scripts", "Gameplay", "Weapons", "ConstantParticleWeaponManager.cs"),
- Path.Combine(root, "Assets", "Scripts", "Gameplay", "Weapons", "ConstantWeaponEffectManager.cs")
+ Path.Combine(root, "Assets", "Scripts", "Gameplay", "Weapons", "ConstantWeaponEffectManager.cs"),
+ Path.Combine(root, "Assets", "Scripts", "Gameplay", "Weapons", "InstantWeaponEffectManager.cs"),
+ Path.Combine(root, "Assets", "Scripts", "Gameplay", "Weapons", "Mine.cs"),
+ Path.Combine(root, "Assets", "Scripts", "Gameplay", "Weapons", "MineManager.cs")
};
var survivingUnityProjectileAuthority = forbiddenUnityProjectileAuthorityPaths
.Where(File.Exists)
diff --git a/Aetheria.State/Documents/AetheriaLegacyCatalogQuarantine.cs b/Aetheria.State/Documents/AetheriaLegacyCatalogQuarantine.cs
index a81703c65..ccb169444 100644
--- a/Aetheria.State/Documents/AetheriaLegacyCatalogQuarantine.cs
+++ b/Aetheria.State/Documents/AetheriaLegacyCatalogQuarantine.cs
@@ -32,6 +32,9 @@ public sealed class AetheriaLegacyCatalogQuarantine
[Key(7)]
public string[] Notes { get; set; } = [];
+
+ [Key(8)]
+ public AetheriaLegacyCatalogFile[] SupplementalCatalogFiles { get; set; } = [];
}
[MessagePackObject]
diff --git a/Aetheria.Unity/Packages/manifest.json b/Aetheria.Unity/Packages/manifest.json
index 40fe741ae..d84a547f0 100644
--- a/Aetheria.Unity/Packages/manifest.json
+++ b/Aetheria.Unity/Packages/manifest.json
@@ -8,6 +8,6 @@
"org.gamecult.cultlib": "https://github.com/GameCult/CultLib.git?path=/unity/org.gamecult.cultlib#cultlib-unity-source-v1.0.3-r2",
"org.gamecult.eve.plugin-fields": "https://github.com/GameCult/EvePlugins.git?path=/plugins/eve-plugin-fields/unity/org.gamecult.eve.plugin-fields#eve-plugin-fields-unity-v0.1.1",
"org.gamecult.eve.surface": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.surface#eveunity-surface-v0.1.0",
- "org.gamecult.eve.unity-scene": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-scene#d6bdeb8fe9c8d3446b6816ad452a97d035f372c2"
+ "org.gamecult.eve.unity-scene": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-scene#0d97dcf56c8b813de50d319ebce10a93595ad5c4"
}
}
diff --git a/Aetheria.Unity/Packages/packages-lock.json b/Aetheria.Unity/Packages/packages-lock.json
index de1845c6c..4cf69aa56 100644
--- a/Aetheria.Unity/Packages/packages-lock.json
+++ b/Aetheria.Unity/Packages/packages-lock.json
@@ -47,7 +47,7 @@
"hash": "e10821f1cc81f619479c6507438f6a097d1f0ce2"
},
"org.gamecult.eve.unity-scene": {
- "version": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-scene#d6bdeb8fe9c8d3446b6816ad452a97d035f372c2",
+ "version": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-scene#0d97dcf56c8b813de50d319ebce10a93595ad5c4",
"depth": 0,
"source": "git",
"dependencies": {
@@ -56,7 +56,7 @@
"org.gamecult.eve.surface": "0.1.0",
"com.unity.mathematics": "1.3.2"
},
- "hash": "d6bdeb8fe9c8d3446b6816ad452a97d035f372c2"
+ "hash": "0d97dcf56c8b813de50d319ebce10a93595ad5c4"
},
"com.unity.modules.assetbundle": {
"version": "1.0.0",
diff --git a/Assets/Prefabs/RPG/Effects/Mine.prefab b/Assets/Prefabs/RPG/Effects/Mine.prefab
new file mode 100644
index 000000000..266596e6c
--- /dev/null
+++ b/Assets/Prefabs/RPG/Effects/Mine.prefab
@@ -0,0 +1,88 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &100000
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 400000}
+ - component: {fileID: 3300000}
+ - component: {fileID: 2300000}
+ m_Layer: 0
+ m_Name: Mine
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &400000
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 100000}
+ serializedVersion: 2
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_ConstrainProportionsScale: 0
+ m_Children: []
+ m_Father: {fileID: 0}
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &3300000
+MeshFilter:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 100000}
+ m_Mesh: {fileID: -5495902117074765545, guid: dab8e1a18a1b825458e8e6356cf00cd2, type: 3}
+--- !u!23 &2300000
+MeshRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 100000}
+ m_Enabled: 1
+ m_CastShadows: 1
+ m_ReceiveShadows: 1
+ m_DynamicOccludee: 1
+ m_StaticShadowCaster: 0
+ m_MotionVectors: 1
+ m_LightProbeUsage: 1
+ m_ReflectionProbeUsage: 1
+ m_RayTracingMode: 2
+ m_RayTraceProcedural: 0
+ m_RenderingLayerMask: 1
+ m_RendererPriority: 0
+ m_Materials:
+ - {fileID: 3720744386649352057, guid: 504c7bfe0d50ff64a88b8eac2ceb46f8, type: 3}
+ - {fileID: 3720744386649352057, guid: a1d39ea984432bf44b4a0686afe2bf1a, type: 3}
+ - {fileID: 2100000, guid: e879c37f3520bf14a8a6075581faf7b2, type: 2}
+ - {fileID: 9178725544925813344, guid: 58e16b7dbc1c31f4585e11544cdce127, type: 3}
+ m_StaticBatchInfo:
+ firstSubMesh: 0
+ subMeshCount: 0
+ m_StaticBatchRoot: {fileID: 0}
+ m_ProbeAnchor: {fileID: 0}
+ m_LightProbeVolumeOverride: {fileID: 0}
+ m_ScaleInLightmap: 1
+ m_ReceiveGI: 1
+ m_PreserveUVs: 0
+ m_IgnoreNormalsForChartDetection: 0
+ m_ImportantGI: 0
+ m_StitchLightmapSeams: 1
+ m_SelectedEditorRenderState: 3
+ m_MinimumChartSize: 4
+ m_AutoUVMaxDistance: 0.5
+ m_AutoUVMaxAngle: 89
+ m_LightmapParameters: {fileID: 0}
+ m_SortingLayerID: 0
+ m_SortingLayer: 0
+ m_SortingOrder: 0
+ m_AdditionalVertexStreams: {fileID: 0}
diff --git a/Assets/Prefabs/RPG/Effects/Mine.prefab.meta b/Assets/Prefabs/RPG/Effects/Mine.prefab.meta
new file mode 100644
index 000000000..1b6f610d9
--- /dev/null
+++ b/Assets/Prefabs/RPG/Effects/Mine.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 29e3eb2f7c0648ad8a9459a7f82bb1cf
+PrefabImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Resources/Prefabs/Weapons/Mine Launcher.prefab b/Assets/Resources/Prefabs/Weapons/Mine Launcher.prefab
deleted file mode 100644
index 05171242b..000000000
--- a/Assets/Resources/Prefabs/Weapons/Mine Launcher.prefab
+++ /dev/null
@@ -1,336 +0,0 @@
-%YAML 1.1
-%TAG !u! tag:unity3d.com,2011:
---- !u!1 &756302067359749329
-GameObject:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- serializedVersion: 6
- m_Component:
- - component: {fileID: 3605572244125604411}
- - component: {fileID: 5473211974529592485}
- - component: {fileID: 4535912766998605946}
- - component: {fileID: 2069293240452108309}
- - component: {fileID: 951258163572815990}
- - component: {fileID: 6357237398779034136}
- m_Layer: 21
- m_Name: Mine
- m_TagString: Untagged
- m_Icon: {fileID: 0}
- m_NavMeshLayer: 0
- m_StaticEditorFlags: 0
- m_IsActive: 1
---- !u!4 &3605572244125604411
-Transform:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 756302067359749329}
- m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
- m_LocalPosition: {x: -0, y: 0, z: 0}
- m_LocalScale: {x: 1, y: 1, z: 1}
- m_ConstrainProportionsScale: 0
- m_Children: []
- m_Father: {fileID: 466182245789483358}
- m_RootOrder: 1
- m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
---- !u!33 &5473211974529592485
-MeshFilter:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 756302067359749329}
- m_Mesh: {fileID: -5495902117074765545, guid: dab8e1a18a1b825458e8e6356cf00cd2, type: 3}
---- !u!23 &4535912766998605946
-MeshRenderer:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 756302067359749329}
- m_Enabled: 1
- m_CastShadows: 1
- m_ReceiveShadows: 1
- m_DynamicOccludee: 1
- m_StaticShadowCaster: 0
- m_MotionVectors: 1
- m_LightProbeUsage: 1
- m_ReflectionProbeUsage: 1
- m_RayTracingMode: 2
- m_RayTraceProcedural: 0
- m_RenderingLayerMask: 1
- m_RendererPriority: 0
- m_Materials:
- - {fileID: 3720744386649352057, guid: 504c7bfe0d50ff64a88b8eac2ceb46f8, type: 3}
- - {fileID: 3720744386649352057, guid: a1d39ea984432bf44b4a0686afe2bf1a, type: 3}
- - {fileID: 2100000, guid: e879c37f3520bf14a8a6075581faf7b2, type: 2}
- - {fileID: 9178725544925813344, guid: 58e16b7dbc1c31f4585e11544cdce127, type: 3}
- m_StaticBatchInfo:
- firstSubMesh: 0
- subMeshCount: 0
- m_StaticBatchRoot: {fileID: 0}
- m_ProbeAnchor: {fileID: 0}
- m_LightProbeVolumeOverride: {fileID: 0}
- m_ScaleInLightmap: 1
- m_ReceiveGI: 1
- m_PreserveUVs: 0
- m_IgnoreNormalsForChartDetection: 0
- m_ImportantGI: 0
- m_StitchLightmapSeams: 1
- m_SelectedEditorRenderState: 3
- m_MinimumChartSize: 4
- m_AutoUVMaxDistance: 0.5
- m_AutoUVMaxAngle: 89
- m_LightmapParameters: {fileID: 0}
- m_SortingLayerID: 0
- m_SortingLayer: 0
- m_SortingOrder: 0
- m_AdditionalVertexStreams: {fileID: 0}
---- !u!114 &2069293240452108309
-MonoBehaviour:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 756302067359749329}
- m_Enabled: 1
- m_EditorHideFlags: 0
- m_Script: {fileID: 11500000, guid: 1281fe44325f9b548aa42dc331b17266, type: 3}
- m_Name:
- m_EditorClassIdentifier:
---- !u!114 &951258163572815990
-MonoBehaviour:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 756302067359749329}
- m_Enabled: 1
- m_EditorHideFlags: 0
- m_Script: {fileID: 11500000, guid: f190191fe8c8419f85767868c50cde5f, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- GridObject: {fileID: 6357237398779034136}
- MeshRenderer: {fileID: 4535912766998605946}
- EmissionSubmesh: 2
- EmissionCurve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: 1
- inSlope: 0.051179443
- outSlope: 0.051179443
- tangentMode: 0
- weightedMode: 0
- inWeight: 0
- outWeight: 0.24423963
- - serializedVersion: 3
- time: 0.5
- value: 0.1
- inSlope: 0
- outSlope: 0
- tangentMode: 34
- weightedMode: 0
- inWeight: 0.33333334
- outWeight: 0.33333334
- - serializedVersion: 3
- time: 1
- value: 1
- inSlope: -0
- outSlope: -0
- tangentMode: 0
- weightedMode: 0
- inWeight: 0.22580647
- outWeight: 0
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- ActiveCycleDuration: 1
- ArmedCycleDuration: 0.25
- ActiveEmission: 100
- ArmedEmission: 1000
- HitEffect: {fileID: 3349939966709198091}
- Lifetime: 30
- ActivationDelay: 2
- BlastRange: 25
- BlastDelay: 2
---- !u!114 &6357237398779034136
-MonoBehaviour:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 756302067359749329}
- m_Enabled: 1
- m_EditorHideFlags: 0
- m_Script: {fileID: 11500000, guid: 737f47c158214f0995ba2835f040ed96, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- RotationSpeed: 1
- GridOffset: 0
- GridAttraction: 10
- Gravity: 1
- Drag: 0.05
- LaunchDrag: 0.2
---- !u!1 &951357816492268516
-GameObject:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- serializedVersion: 6
- m_Component:
- - component: {fileID: 466182245789483358}
- - component: {fileID: 2749986269966238105}
- m_Layer: 0
- m_Name: Mine Launcher
- m_TagString: Untagged
- m_Icon: {fileID: 0}
- m_NavMeshLayer: 0
- m_StaticEditorFlags: 0
- m_IsActive: 1
---- !u!4 &466182245789483358
-Transform:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 951357816492268516}
- m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
- m_LocalPosition: {x: 0, y: 0, z: 0}
- m_LocalScale: {x: 1, y: 1, z: 1}
- m_ConstrainProportionsScale: 0
- m_Children:
- - {fileID: 3865250807623958572}
- - {fileID: 3605572244125604411}
- m_Father: {fileID: 0}
- m_RootOrder: 0
- m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
---- !u!114 &2749986269966238105
-MonoBehaviour:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 951357816492268516}
- m_Enabled: 1
- m_EditorHideFlags: 0
- m_Script: {fileID: 11500000, guid: 6e048d9c74a84343befd496907ee2126, type: 3}
- m_Name:
- m_EditorClassIdentifier:
- ProjectilePrototype: {fileID: 2069293240452108309}
- InheritVelocity: 1
---- !u!1001 &3867385469207619610
-PrefabInstance:
- m_ObjectHideFlags: 0
- serializedVersion: 2
- m_Modification:
- m_TransformParent: {fileID: 466182245789483358}
- m_Modifications:
- - target: {fileID: 1121192042590650, guid: e1b08032bec16ba4eb1ae23cbd572664, type: 3}
- propertyPath: m_Name
- value: BigExplosion
- objectReference: {fileID: 0}
- - target: {fileID: 4388947112716342, guid: e1b08032bec16ba4eb1ae23cbd572664, type: 3}
- propertyPath: m_RootOrder
- value: 0
- objectReference: {fileID: 0}
- - target: {fileID: 4388947112716342, guid: e1b08032bec16ba4eb1ae23cbd572664, type: 3}
- propertyPath: m_LocalScale.x
- value: 10
- objectReference: {fileID: 0}
- - target: {fileID: 4388947112716342, guid: e1b08032bec16ba4eb1ae23cbd572664, type: 3}
- propertyPath: m_LocalScale.y
- value: 10
- objectReference: {fileID: 0}
- - target: {fileID: 4388947112716342, guid: e1b08032bec16ba4eb1ae23cbd572664, type: 3}
- propertyPath: m_LocalScale.z
- value: 10
- objectReference: {fileID: 0}
- - target: {fileID: 4388947112716342, guid: e1b08032bec16ba4eb1ae23cbd572664, type: 3}
- propertyPath: m_LocalPosition.x
- value: 0
- objectReference: {fileID: 0}
- - target: {fileID: 4388947112716342, guid: e1b08032bec16ba4eb1ae23cbd572664, type: 3}
- propertyPath: m_LocalPosition.y
- value: 0
- objectReference: {fileID: 0}
- - target: {fileID: 4388947112716342, guid: e1b08032bec16ba4eb1ae23cbd572664, type: 3}
- propertyPath: m_LocalPosition.z
- value: 0
- objectReference: {fileID: 0}
- - target: {fileID: 4388947112716342, guid: e1b08032bec16ba4eb1ae23cbd572664, type: 3}
- propertyPath: m_LocalRotation.w
- value: 1
- objectReference: {fileID: 0}
- - target: {fileID: 4388947112716342, guid: e1b08032bec16ba4eb1ae23cbd572664, type: 3}
- propertyPath: m_LocalRotation.x
- value: 0
- objectReference: {fileID: 0}
- - target: {fileID: 4388947112716342, guid: e1b08032bec16ba4eb1ae23cbd572664, type: 3}
- propertyPath: m_LocalRotation.y
- value: 0
- objectReference: {fileID: 0}
- - target: {fileID: 4388947112716342, guid: e1b08032bec16ba4eb1ae23cbd572664, type: 3}
- propertyPath: m_LocalRotation.z
- value: 0
- objectReference: {fileID: 0}
- - target: {fileID: 4388947112716342, guid: e1b08032bec16ba4eb1ae23cbd572664, type: 3}
- propertyPath: m_LocalEulerAnglesHint.x
- value: 0
- objectReference: {fileID: 0}
- - target: {fileID: 4388947112716342, guid: e1b08032bec16ba4eb1ae23cbd572664, type: 3}
- propertyPath: m_LocalEulerAnglesHint.y
- value: 0
- objectReference: {fileID: 0}
- - target: {fileID: 4388947112716342, guid: e1b08032bec16ba4eb1ae23cbd572664, type: 3}
- propertyPath: m_LocalEulerAnglesHint.z
- value: 0
- objectReference: {fileID: 0}
- - target: {fileID: 198917022394846920, guid: e1b08032bec16ba4eb1ae23cbd572664,
- type: 3}
- propertyPath: stopAction
- value: 3
- objectReference: {fileID: 0}
- m_RemovedComponents: []
- m_SourcePrefab: {fileID: 100100000, guid: e1b08032bec16ba4eb1ae23cbd572664, type: 3}
---- !u!4 &3865250807623958572 stripped
-Transform:
- m_CorrespondingSourceObject: {fileID: 4388947112716342, guid: e1b08032bec16ba4eb1ae23cbd572664,
- type: 3}
- m_PrefabInstance: {fileID: 3867385469207619610}
- m_PrefabAsset: {fileID: 0}
---- !u!1 &3866425147859458464 stripped
-GameObject:
- m_CorrespondingSourceObject: {fileID: 1121192042590650, guid: e1b08032bec16ba4eb1ae23cbd572664,
- type: 3}
- m_PrefabInstance: {fileID: 3867385469207619610}
- m_PrefabAsset: {fileID: 0}
---- !u!114 &3349939966709198091
-MonoBehaviour:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 3866425147859458464}
- m_Enabled: 1
- m_EditorHideFlags: 0
- m_Script: {fileID: 11500000, guid: 1281fe44325f9b548aa42dc331b17266, type: 3}
- m_Name:
- m_EditorClassIdentifier:
---- !u!114 &5700147869379115258
-MonoBehaviour:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_GameObject: {fileID: 3866425147859458464}
- m_Enabled: 1
- m_EditorHideFlags: 0
- m_Script: {fileID: 11500000, guid: db021a309de3d324588c5603de40c29f, type: 3}
- m_Name:
- m_EditorClassIdentifier:
diff --git a/Assets/Resources/Prefabs/Weapons/Mine Launcher.prefab.meta b/Assets/Resources/Prefabs/Weapons/Mine Launcher.prefab.meta
deleted file mode 100644
index 2489e77fa..000000000
--- a/Assets/Resources/Prefabs/Weapons/Mine Launcher.prefab.meta
+++ /dev/null
@@ -1,7 +0,0 @@
-fileFormatVersion: 2
-guid: 24eddacc819f20542962f2395930f261
-PrefabImporter:
- externalObjects: {}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Scripts/Gameplay/Weapons/InstantWeaponEffectManager.cs b/Assets/Scripts/Gameplay/Weapons/InstantWeaponEffectManager.cs
deleted file mode 100644
index 13683cc08..000000000
--- a/Assets/Scripts/Gameplay/Weapons/InstantWeaponEffectManager.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-using System.Collections;
-using System.Collections.Generic;
-using UnityEngine;
-
-public abstract class InstantWeaponEffectManager : MonoBehaviour
-{
- public abstract void Fire(InstantWeapon weapon, EquippedItem item, EntityInstance source, EntityInstance target);
-}
\ No newline at end of file
diff --git a/Assets/Scripts/Gameplay/Weapons/InstantWeaponEffectManager.cs.meta b/Assets/Scripts/Gameplay/Weapons/InstantWeaponEffectManager.cs.meta
deleted file mode 100644
index dba49d578..000000000
--- a/Assets/Scripts/Gameplay/Weapons/InstantWeaponEffectManager.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: 947a7d0e9469d724daed03d35a0c1f4e
-MonoImporter:
- externalObjects: {}
- serializedVersion: 2
- defaultReferences: []
- executionOrder: 0
- icon: {instanceID: 0}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/Assets/Scripts/Gameplay/Weapons/Mine.cs b/Assets/Scripts/Gameplay/Weapons/Mine.cs
deleted file mode 100644
index 2394d8eaa..000000000
--- a/Assets/Scripts/Gameplay/Weapons/Mine.cs
+++ /dev/null
@@ -1,77 +0,0 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using UnityEngine;
-using Random = UnityEngine.Random;
-
-public class Mine : MonoBehaviour
-{
- public GridObject GridObject;
- public MeshRenderer MeshRenderer;
- public int EmissionSubmesh;
- public AnimationCurve EmissionCurve;
- public float ActiveCycleDuration;
- public float ArmedCycleDuration;
- public float ActiveEmission;
- public float ArmedEmission;
- public Prototype HitEffect;
- public float Lifetime;
-
- public float ActivationDelay;
- public float BlastRange;
- public float BlastDelay;
-
- private float _startTime;
- private float _blastTime;
- private bool _blastCountdown;
- private bool _active;
- private Material _material;
- private float _pulseLerp;
- private float _emission;
- public float ImpactIntensity { get; set; } = 1;
- public EntityInstance Source { get; set; }
- public float Range { get; set; }
-
- private void Start()
- {
- _material = MeshRenderer.materials[EmissionSubmesh];
- }
-
- private void OnEnable()
- {
- _startTime = Time.time;
- _blastCountdown = false;
- _active = false;
- _emission = 0;
- }
-
- // Update is called once per frame
- void Update()
- {
- if (Time.time - _startTime > ActivationDelay && !_active)
- {
- _active = true;
- }
-
- if(_active)
- {
- _emission = Mathf.Lerp(_emission, _blastCountdown ? ArmedEmission : ActiveEmission, Time.deltaTime * 10);
- _pulseLerp += Time.deltaTime / (_blastCountdown ? ArmedCycleDuration : ActiveCycleDuration);
- _pulseLerp %= 1;
-
- if (Time.time - _startTime > Lifetime)
- {
- Explode();
- }
- }
- _material.SetFloat("_Emission", _emission * EmissionCurve.Evaluate(_pulseLerp));
- }
-
- public void Explode()
- {
- var position = transform.position;
- var ht = HitEffect.Instantiate();
- ht.position = position;
- GetComponent().ReturnToPool();
- }
-}
diff --git a/Assets/Scripts/Gameplay/Weapons/Mine.cs.meta b/Assets/Scripts/Gameplay/Weapons/Mine.cs.meta
deleted file mode 100644
index 656e2023e..000000000
--- a/Assets/Scripts/Gameplay/Weapons/Mine.cs.meta
+++ /dev/null
@@ -1,3 +0,0 @@
-fileFormatVersion: 2
-guid: f190191fe8c8419f85767868c50cde5f
-timeCreated: 1612545564
\ No newline at end of file
diff --git a/Assets/Scripts/Gameplay/Weapons/MineManager.cs b/Assets/Scripts/Gameplay/Weapons/MineManager.cs
deleted file mode 100644
index d8d8204c5..000000000
--- a/Assets/Scripts/Gameplay/Weapons/MineManager.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-using System.Collections;
-using System.Collections.Generic;
-using UnityEngine;
-using Random = UnityEngine.Random;
-
-public class MineManager : InstantWeaponEffectManager
-{
- public Prototype ProjectilePrototype;
-
- public override void Fire(InstantWeapon weapon, EquippedItem item, EntityInstance source, EntityInstance target)
- {
- var p = ProjectilePrototype.Instantiate();
- var hp = source.Entity.Hardpoints[item.Position.x, item.Position.y];
- var barrel = source.GetBarrel(hp);
- var angle = weapon.Spread / 2;
- p.Source = source;
- p.transform.position = barrel.position;
- p.GridObject.Velocity = Quaternion.Euler(
- Random.Range(-angle, angle),
- Random.Range(-angle, angle),
- Random.Range(-angle, angle)) *
- barrel.forward *
- weapon.Velocity;
- p.Range = weapon.Range;
- p.ImpactIntensity = 1;
- }
-}
diff --git a/Assets/Scripts/Gameplay/Weapons/MineManager.cs.meta b/Assets/Scripts/Gameplay/Weapons/MineManager.cs.meta
deleted file mode 100644
index fb1cc2343..000000000
--- a/Assets/Scripts/Gameplay/Weapons/MineManager.cs.meta
+++ /dev/null
@@ -1,3 +0,0 @@
-fileFormatVersion: 2
-guid: 6e048d9c74a84343befd496907ee2126
-timeCreated: 1612545538
\ No newline at end of file
diff --git a/GameData/Legacy/AetherDB.2021-03-05.msgpack b/GameData/Legacy/AetherDB.2021-03-05.msgpack
new file mode 100644
index 000000000..86d53a166
--- /dev/null
+++ b/GameData/Legacy/AetherDB.2021-03-05.msgpack
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:142998fdfb54c613230106b4cef53a1e0688c866399176005645a10dcbe77ca0
+size 58992
diff --git a/GameData/aetheria-world.cc.records/3b2b177b2026aebdedd310fee6758e1bc3db30612209f3b763607a4dfd231f4d.msgpack b/GameData/aetheria-world.cc.records/3b2b177b2026aebdedd310fee6758e1bc3db30612209f3b763607a4dfd231f4d.msgpack
new file mode 100644
index 000000000..d1bf0cf4f
--- /dev/null
+++ b/GameData/aetheria-world.cc.records/3b2b177b2026aebdedd310fee6758e1bc3db30612209f3b763607a4dfd231f4d.msgpack
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:784b46a94de4b1af13f0ca52f72ee3d38e7a15714acb51987aafd170a4711d6b
+size 3529
diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeAssets.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeAssets.cs
index 21a90feb9..1c046794f 100644
--- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeAssets.cs
+++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeAssets.cs
@@ -28,6 +28,7 @@ namespace GameCult.Aetheria.State.Verse
Add(entries, MapPrefab("prefab.entity.orbital", "Orbital", "Prefabs/Stations/Zenith", "orbital"));
Add(entries, MapPrefab("prefab.effect.shot.bolt", "Bolt", "Prefabs/Lightning", "effect.shot.bolt"));
Add(entries, MapProjectPrefab("prefab.entity.pickup", "Pickup", "Assets/Prefabs/RPG/Pickups/Tetrahedron.prefab"));
+ Add(entries, MinePrefab());
foreach (var inventoryAsset in InventoryUiAssets())
Add(entries, inventoryAsset);
@@ -200,6 +201,23 @@ namespace GameCult.Aetheria.State.Verse
};
}
+ private static AetheriaRuntimeAssetManifestEntry MinePrefab()
+ {
+ var entry = MapProjectPrefab(
+ "prefab.entity.mine",
+ "Mine",
+ "Assets/Prefabs/RPG/Effects/Mine.prefab");
+ entry.Ref.Metadata = new Dictionary(entry.Ref.Metadata, StringComparer.Ordinal)
+ {
+ ["presentationRole"] = "physical-payload.mine",
+ ["activePulseSeconds"] = "1",
+ ["triggeredPulseSeconds"] = "0.25",
+ ["activeEmission"] = "100",
+ ["triggeredEmission"] = "1000"
+ };
+ return entry;
+ }
+
private static AetheriaRuntimeAssetRef Sprite(string key, string resourcePath)
{
return CultMeshAssetRef(
diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogStore.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogStore.cs
index 5f2adfe03..c54287e35 100644
--- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogStore.cs
+++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeCatalogStore.cs
@@ -707,7 +707,8 @@ namespace GameCult.Aetheria.State.Verse
var hardpointType = ReadFieldString(ref reader, fields, 12);
var hullType = ReadFieldString(ref reader, fields, 13);
var behaviorKinds = ReadFieldStringArray(ref reader, fields, 14);
- var hullCapacity = ReadFieldBehaviorValue(ref reader, fields, 15);
+ SkipField(ref reader, fields, 15);
+ AetheriaRuntimeBehaviorValue? hullCapacity = null;
var maxStack = ReadFieldInt32(ref reader, fields, 16);
var stackable = ReadFieldBool(ref reader, fields, 17);
var duration = ReadFieldDouble(ref reader, fields, 18);
diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonGameSurfaceBuilder.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonGameSurfaceBuilder.cs
index ad122c7d6..a7dc3fa38 100644
--- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonGameSurfaceBuilder.cs
+++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonGameSurfaceBuilder.cs
@@ -293,7 +293,7 @@ namespace GameCult.Aetheria.State.Verse
("targetEntityIndex", value.TargetEntityIndex.ToString(CultureInfo.InvariantCulture)),
("pickupIndex", value.PickupIndex.ToString(CultureInfo.InvariantCulture)),
("itemKey", value.ItemKey), ("scalarValue", FormatNumber(value.ScalarValue)),
- ("subjectKey", value.SubjectKey),
+ ("subjectKey", value.SubjectKey), ("reason", value.Reason),
("position", string.Join(",", new[] { FormatNumber(value.PositionX), FormatNumber(value.PositionZ) })),
("currentFrameId", frameId.ToString(CultureInfo.InvariantCulture))
}))
@@ -783,7 +783,9 @@ namespace GameCult.Aetheria.State.Verse
["payloadKind"] = projectile.PayloadKind ?? "",
["label"] = projectile.PayloadKind ?? "Projectile",
["faction"] = "",
- ["assetRef"] = "prefab.entity.projectile",
+ ["assetRef"] = string.Equals(projectile.PayloadKind, "mine", StringComparison.Ordinal)
+ ? "prefab.entity.mine"
+ : "prefab.entity.projectile",
["position"] = string.Join(",", new[]
{
projectile.PositionX.ToString("0.###", CultureInfo.InvariantCulture),
@@ -806,6 +808,16 @@ namespace GameCult.Aetheria.State.Verse
["blastRadius"] = FormatNumber(projectile.BlastRadius),
["payloadMagnitude"] = FormatNumber(projectile.PayloadMagnitude),
["stationary"] = projectile.Stationary ? "true" : "false",
+ ["maximumSourceDistance"] = FormatNumber(projectile.MaximumSourceDistance),
+ ["weaponItemKey"] = projectile.WeaponItemKey ?? "",
+ ["triggerReason"] = projectile.TriggerReason ?? "",
+ ["presentationState"] = projectile.TriggeredAtSeconds >= 0
+ ? "triggered"
+ : projectile.AgeSeconds >= projectile.ActivationDelaySeconds ? "active" : "deploying",
+ ["activePulseSeconds"] = "1",
+ ["triggeredPulseSeconds"] = "0.25",
+ ["activeEmission"] = "100",
+ ["triggeredEmission"] = "1000",
["selectable"] = "false",
["controllable"] = "false"
};
diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonSimulation.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonSimulation.cs
index 9843ee3a8..828a32e20 100644
--- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonSimulation.cs
+++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonSimulation.cs
@@ -342,6 +342,7 @@ namespace GameCult.Aetheria.State.Verse
Array.Empty())
: physicalPayloadPhysics.Step(zone, entities, deltaSeconds);
zone.PhysicalPayloads = projectileStep.PhysicalPayloads;
+ TriggerExpiredDeployables(run, zone, byIndex, frameId, simulationTimeSeconds);
foreach (var hit in projectileStep.Hits)
{
if (byIndex.TryGetValue(hit.TargetEntityIndex, out var target))
@@ -352,7 +353,8 @@ namespace GameCult.Aetheria.State.Verse
hit.Payload.TriggeredAtSeconds < 0)
{
hit.Payload.TriggeredAtSeconds = simulationTimeSeconds;
- AetheriaRuntimeGameEvents.Append(run, new AetheriaRuntimeGameEventCommit { EventId = $"physical-payload:{hit.Payload.PayloadId}:triggered", Kind = "deployable.triggered", FrameId = frameId, ZoneIndex = zone.ZoneIndex, SourceEntityIndex = hit.Payload.SourceEntityIndex, TargetEntityIndex = target.EntityIndex, SubjectKey = hit.Payload.PayloadId, ItemKey = hit.Payload.PayloadKind, PositionX = hit.Payload.PositionX, PositionZ = hit.Payload.PositionZ });
+ hit.Payload.TriggerReason = "proximity";
+ AetheriaRuntimeGameEvents.Append(run, new AetheriaRuntimeGameEventCommit { EventId = $"physical-payload:{hit.Payload.PayloadId}:triggered", Kind = "deployable.triggered", FrameId = frameId, ZoneIndex = zone.ZoneIndex, SourceEntityIndex = hit.Payload.SourceEntityIndex, TargetEntityIndex = target.EntityIndex, SubjectKey = hit.Payload.PayloadId, ItemKey = hit.Payload.WeaponItemKey, PositionX = hit.Payload.PositionX, PositionZ = hit.Payload.PositionZ });
}
}
}
@@ -417,7 +419,9 @@ namespace GameCult.Aetheria.State.Verse
TriggerRadius = deployable.TriggerRadius,
DetonationDelaySeconds = deployable.DetonationDelaySeconds,
BlastRadius = deployable.BlastRadius,
- PayloadMagnitude = weapon.Damage
+ PayloadMagnitude = weapon.Damage,
+ MaximumSourceDistance = weapon.Range,
+ WeaponItemKey = weapon.ItemKey
});
zone.PhysicalPayloads = payloads;
weapon.State.Firing = true;
@@ -453,7 +457,7 @@ namespace GameCult.Aetheria.State.Verse
if (dx * dx + dz * dz > payload.BlastRadius * payload.BlastRadius) continue;
Damage(target, payload.PayloadMagnitude);
}
- AetheriaRuntimeGameEvents.Append(run, new AetheriaRuntimeGameEventCommit { EventId = $"physical-payload:{payload.PayloadId}:detonated", Kind = "deployable.detonated", FrameId = frameId, ZoneIndex = zone.ZoneIndex, SourceEntityIndex = payload.SourceEntityIndex, SubjectKey = payload.PayloadId, ItemKey = payload.PayloadKind, ScalarValue = payload.PayloadMagnitude, PositionX = payload.PositionX, PositionZ = payload.PositionZ });
+ AetheriaRuntimeGameEvents.Append(run, new AetheriaRuntimeGameEventCommit { EventId = $"physical-payload:{payload.PayloadId}:detonated", Kind = "deployable.detonated", FrameId = frameId, ZoneIndex = zone.ZoneIndex, SourceEntityIndex = payload.SourceEntityIndex, SubjectKey = payload.PayloadId, ItemKey = payload.WeaponItemKey, Reason = payload.TriggerReason, ScalarValue = payload.PayloadMagnitude, PositionX = payload.PositionX, PositionZ = payload.PositionZ });
}
zone.PhysicalPayloads = survivors;
}
@@ -877,8 +881,11 @@ namespace GameCult.Aetheria.State.Verse
continue;
projectile.AgeSeconds += deltaSeconds;
- if (projectile.AgeSeconds >= projectile.LifetimeSeconds)
+ if (!string.Equals(projectile.PayloadKind, "mine", StringComparison.Ordinal) &&
+ projectile.AgeSeconds >= projectile.LifetimeSeconds)
+ {
continue;
+ }
if (string.Equals(projectile.PayloadKind, "mine", StringComparison.Ordinal) &&
projectile.AgeSeconds >= projectile.ActivationDelaySeconds)
@@ -902,6 +909,56 @@ namespace GameCult.Aetheria.State.Verse
zone.PhysicalPayloads = active;
}
+ private static void TriggerExpiredDeployables(
+ AetheriaRuntimeRunCheckpointCommit run,
+ AetheriaRuntimeZoneSnapshotCommit zone,
+ IReadOnlyDictionary entities,
+ long frameId,
+ double simulationTimeSeconds)
+ {
+ foreach (var payload in zone.PhysicalPayloads ?? Array.Empty())
+ {
+ if (!string.Equals(payload.PayloadKind, "mine", StringComparison.Ordinal) ||
+ payload.TriggeredAtSeconds >= 0)
+ continue;
+
+ var expired = payload.AgeSeconds >= payload.LifetimeSeconds;
+ var outOfRange = payload.MaximumSourceDistance > 0 &&
+ entities.TryGetValue(payload.SourceEntityIndex, out var source) &&
+ PositionDistanceSq(payload.PositionX, payload.PositionZ, source.PositionX, source.PositionZ) >
+ payload.MaximumSourceDistance * payload.MaximumSourceDistance;
+ if (!expired && !outOfRange)
+ continue;
+
+ payload.TriggeredAtSeconds = simulationTimeSeconds;
+ payload.DetonationDelaySeconds = 0;
+ payload.TriggerReason = expired ? "lifetime" : "range";
+ payload.Stationary = true;
+ payload.VelocityX = 0;
+ payload.VelocityY = 0;
+ AetheriaRuntimeGameEvents.Append(run, new AetheriaRuntimeGameEventCommit
+ {
+ EventId = $"physical-payload:{payload.PayloadId}:expired",
+ Kind = "deployable.expired",
+ FrameId = frameId,
+ ZoneIndex = zone.ZoneIndex,
+ SourceEntityIndex = payload.SourceEntityIndex,
+ SubjectKey = payload.PayloadId,
+ ItemKey = payload.WeaponItemKey,
+ Reason = payload.TriggerReason,
+ PositionX = payload.PositionX,
+ PositionZ = payload.PositionZ
+ });
+ }
+ }
+
+ private static double PositionDistanceSq(double leftX, double leftZ, double rightX, double rightZ)
+ {
+ var dx = leftX - rightX;
+ var dz = leftZ - rightZ;
+ return dx * dx + dz * dz;
+ }
+
private static void GuidePhysicalPayload(
AetheriaRuntimePhysicalPayloadCommit projectile,
AetheriaRuntimeEntitySnapshotCommit target)
diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeSnapshotDocuments.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeSnapshotDocuments.cs
index 9b4fee346..2ab30ae02 100644
--- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeSnapshotDocuments.cs
+++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeSnapshotDocuments.cs
@@ -450,6 +450,7 @@ namespace GameCult.Aetheria.State.Verse
[Key(9)] public string SubjectKey { get; set; } = "";
[Key(10)] public double PositionX { get; set; }
[Key(11)] public double PositionZ { get; set; }
+ [Key(12)] public string Reason { get; set; } = "";
}
[MessagePackObject]
@@ -631,6 +632,9 @@ namespace GameCult.Aetheria.State.Verse
[Key(22)] public double BlastRadius { get; set; }
[Key(23)] public double PayloadMagnitude { get; set; }
[Key(24)] public bool Stationary { get; set; }
+ [Key(25)] public double MaximumSourceDistance { get; set; }
+ [Key(26)] public string WeaponItemKey { get; set; } = "";
+ [Key(27)] public string TriggerReason { get; set; } = "";
}
[MessagePackObject]
diff --git a/docs/aetheria-current-codebase-model.md b/docs/aetheria-current-codebase-model.md
index 756d987f4..748d583f3 100644
--- a/docs/aetheria-current-codebase-model.md
+++ b/docs/aetheria-current-codebase-model.md
@@ -181,11 +181,14 @@ Remaining debt:
- Clickable raycasts still construct query bodies from Unity click bounds. That
is presentation picking, not simulation authority, but it should stay clearly
labeled as renderer/UI picking.
-- The fossil `Mine`, `MineManager`, and `InstantWeaponEffectManager` remain as
- one serialized bundle on `Mine Launcher.prefab`. Replace that prefab's
- runtime script component with a provider-owned presentation asset and migrate
- its real catalog behavior before deleting the bundle; deleting the scripts
- alone would merely manufacture a missing Unity component.
+- The original `Mine Launcher` item was recovered from the quarantined
+ 2021-03-05 catalog by stable legacy ID, normalized from its historical effect
+ reference to `DeployableWeapon`, and merged into canonical typed provider
+ state. The supplemental MessagePack file is migration provenance only.
+- `Mine`, `MineManager`, `InstantWeaponEffectManager`, and their serialized
+ launcher prefab are deleted. A script-free provider prefab supplies the
+ original mesh/materials. Eve publishes lifecycle and pulse properties;
+ EveUnity generically lowers those semantic properties to material emission.
## Gravity Terrain
diff --git a/docs/deterministic-combat-resolution.md b/docs/deterministic-combat-resolution.md
index 7cefcb767..29b90dabe 100644
--- a/docs/deterministic-combat-resolution.md
+++ b/docs/deterministic-combat-resolution.md
@@ -163,6 +163,11 @@ owns integration and overlap/contact facts. The production daemon currently
embeds that implementation in-process; moving it across CultNet must not change
either authority or the persisted state contract.
+Mine lifetime and source-relative range expiry are also daemon-owned trigger
+causes. They enter the same detonation transaction as proximity triggers and
+retain their reason in the event chronology. Ymir movement happens before the
+range check, matching the original simulation ordering.
+
## Eve Presentation
Eve receives: