From 8c71a6379dd55f915b2f4b20c2bbc4c5d3e51b69 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sun, 12 Jul 2026 02:25:40 +0100 Subject: [PATCH] Recover Mine Launcher as daemon deployable --- Aetheria.State.Daemon.Smoke/Program.cs | 76 +++- .../Aetheria.State.Import.csproj | 1 + Aetheria.State.Import/Program.cs | 135 ++++++- Aetheria.State.Verify/Program.cs | 5 +- .../AetheriaLegacyCatalogQuarantine.cs | 3 + Aetheria.Unity/Packages/manifest.json | 2 +- Aetheria.Unity/Packages/packages-lock.json | 4 +- Assets/Prefabs/RPG/Effects/Mine.prefab | 88 +++++ Assets/Prefabs/RPG/Effects/Mine.prefab.meta | 7 + .../Prefabs/Weapons/Mine Launcher.prefab | 336 ------------------ .../Prefabs/Weapons/Mine Launcher.prefab.meta | 7 - .../Weapons/InstantWeaponEffectManager.cs | 8 - .../InstantWeaponEffectManager.cs.meta | 11 - Assets/Scripts/Gameplay/Weapons/Mine.cs | 77 ---- Assets/Scripts/Gameplay/Weapons/Mine.cs.meta | 3 - .../Scripts/Gameplay/Weapons/MineManager.cs | 27 -- .../Gameplay/Weapons/MineManager.cs.meta | 3 - GameData/Legacy/AetherDB.2021-03-05.msgpack | 3 + ...1bc3db30612209f3b763607a4dfd231f4d.msgpack | 3 + .../Runtime/AetheriaRuntimeAssets.cs | 18 + .../Runtime/AetheriaRuntimeCatalogStore.cs | 3 +- ...AetheriaRuntimeDaemonGameSurfaceBuilder.cs | 16 +- .../AetheriaRuntimeDaemonSimulation.cs | 65 +++- .../AetheriaRuntimeSnapshotDocuments.cs | 4 + docs/aetheria-current-codebase-model.md | 13 +- docs/deterministic-combat-resolution.md | 5 + 26 files changed, 431 insertions(+), 492 deletions(-) create mode 100644 Assets/Prefabs/RPG/Effects/Mine.prefab create mode 100644 Assets/Prefabs/RPG/Effects/Mine.prefab.meta delete mode 100644 Assets/Resources/Prefabs/Weapons/Mine Launcher.prefab delete mode 100644 Assets/Resources/Prefabs/Weapons/Mine Launcher.prefab.meta delete mode 100644 Assets/Scripts/Gameplay/Weapons/InstantWeaponEffectManager.cs delete mode 100644 Assets/Scripts/Gameplay/Weapons/InstantWeaponEffectManager.cs.meta delete mode 100644 Assets/Scripts/Gameplay/Weapons/Mine.cs delete mode 100644 Assets/Scripts/Gameplay/Weapons/Mine.cs.meta delete mode 100644 Assets/Scripts/Gameplay/Weapons/MineManager.cs delete mode 100644 Assets/Scripts/Gameplay/Weapons/MineManager.cs.meta create mode 100644 GameData/Legacy/AetherDB.2021-03-05.msgpack create mode 100644 GameData/aetheria-world.cc.records/3b2b177b2026aebdedd310fee6758e1bc3db30612209f3b763607a4dfd231f4d.msgpack 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: