diff --git a/Assets/Editor/EveAssetBundleBuilder.cs b/Assets/Editor/EveAssetBundleBuilder.cs index d14fc765b..96d532aaf 100644 --- a/Assets/Editor/EveAssetBundleBuilder.cs +++ b/Assets/Editor/EveAssetBundleBuilder.cs @@ -104,10 +104,11 @@ namespace Aetheria.Editor try { KeepAdvertisedPresentationVisual(root, entry); - ExtractExternalizedEffects(root, entry.Ref.AssetKey); + ExtractExternalizedEffects(root, entry); StripNonPresentationScripts(root); StripPresentationPhysics(root); NormalizePresentationMaterials(root); + VerifyExternalizedShipEffects(root, entry); VerifyPrefab(root, outputPath); PrefabUtility.SaveAsPrefabAsset(root, outputPath, out var saved); if (!saved) @@ -139,10 +140,20 @@ namespace Aetheria.Editor UnityEngine.Object.DestroyImmediate(child.gameObject, true); } - private static void ExtractExternalizedEffects(GameObject root, string assetKey) + private static void ExtractExternalizedEffects( + GameObject root, + AetheriaRuntimeAssetManifestEntry entry) { - if (!string.Equals(assetKey, "prefab.entity.player", StringComparison.Ordinal) && - !string.Equals(assetKey, "prefab.entity.ship", StringComparison.Ordinal)) + var assetKey = entry?.Ref?.AssetKey ?? ""; + var presentationRole = entry?.Ref?.Metadata != null && + entry.Ref.Metadata.TryGetValue("presentationRole", out var role) + ? role ?? "" + : ""; + var strictLegacyShip = + string.Equals(presentationRole, "player", StringComparison.Ordinal) || + string.Equals(presentationRole, "ship", StringComparison.Ordinal); + var catalogHull = string.Equals(presentationRole, "entity.hull", StringComparison.Ordinal); + if (!strictLegacyShip && !catalogHull) return; var tractorEffects = root.GetComponentsInChildren(true) @@ -151,7 +162,7 @@ namespace Aetheria.Editor .Select(behaviour => behaviour.gameObject) .Distinct() .ToArray(); - if (tractorEffects.Length == 0) + if (tractorEffects.Length == 0 && strictLegacyShip) throw new InvalidOperationException( $"Ship presentation source '{assetKey}' has no embedded tractor effect to externalize."); @@ -167,7 +178,7 @@ namespace Aetheria.Editor .Select(renderer => renderer.gameObject) .Distinct() .ToArray(); - if (embeddedShieldVisuals.Length == 0) + if (embeddedShieldVisuals.Length == 0 && strictLegacyShip) throw new InvalidOperationException( $"Ship presentation source '{assetKey}' has no embedded shield visual to externalize."); @@ -175,6 +186,27 @@ namespace Aetheria.Editor UnityEngine.Object.DestroyImmediate(visual, true); } + private static void VerifyExternalizedShipEffects( + GameObject root, + AetheriaRuntimeAssetManifestEntry entry) + { + var presentationRole = entry?.Ref?.Metadata != null && + entry.Ref.Metadata.TryGetValue("presentationRole", out var role) + ? role ?? "" + : ""; + if (!string.Equals(presentationRole, "player", StringComparison.Ordinal) && + !string.Equals(presentationRole, "ship", StringComparison.Ordinal) && + !string.Equals(presentationRole, "entity.hull", StringComparison.Ordinal)) + return; + + var surviving = root.GetComponentsInChildren(true) + .FirstOrDefault(renderer => renderer != null && + string.Equals(renderer.gameObject.name, "Shield Visual", StringComparison.Ordinal)); + if (surviving != null) + throw new InvalidOperationException( + $"Ship presentation source '{entry.Ref.AssetKey}' retained an always-on shield envelope."); + } + private static void StripNonPresentationScripts(GameObject root) { foreach (var transform in root.GetComponentsInChildren(true)) diff --git a/docs/aetheria-perfect-machine-map.md b/docs/aetheria-perfect-machine-map.md index d403e491c..523f9146f 100644 --- a/docs/aetheria-perfect-machine-map.md +++ b/docs/aetheria-perfect-machine-map.md @@ -1088,14 +1088,15 @@ legacy catalog cache, and legacy UI paths should be migration-only or deleted. skybox/ambient authority or infer shader models from Aetheria asset names. - Cut line: the obsolete provider skybox catalog entry and world binding are deleted, as is the invented `0.4,-1,0.25` warm key light. The embedded - fossil shield renderer is removed from ship presentation prefabs because its + fossil shield renderer is removed from legacy ship roles and catalog-backed + `entity.hull` presentation prefabs because its dithered collision response is already externalized as the provider-owned, receipt-driven shield-impact effect; it cannot remain permanently visible after generic material conversion. - Verification layer: daemon smoke proves the stellar color projection and negatively proves that neither skybox nor key-light fields are published. The released witness records flat ambient mode, the exact lowered color, zero directional intensity, - no player `Shield Visual`, twelve remaining player renderers, and a map-channel + no player `Shield Visual`, seven active player renderers, and a map-channel material using `Universal Render Pipeline/Unlit`; visual inspection shows ship hulls instead of opaque shield ellipsoids and readable map glyphs.