Aetheria/Assets/Scripts/Editor/WireMines.cs
Meta Vens 4d29e0b33e WireMines: Substance materials fall back to the project's plain materials of the same finish
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-08 14:26:26 +02:00

103 lines
5.1 KiB
C#

using System.Linq;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine;
using Object = UnityEngine.Object;
// Builds the mine's prefab from the retired Mine Launcher's mesh, materials and pulse values, and hands it to the
// scene's ZoneRenderer. Run once (menu, or -executeMethod WireMines.Run in batchmode) and commit the diff.
public static class WireMines
{
private const string PrefabPath = "Assets/Content/Prefabs/Mine.prefab";
private const string ScenePath = "Assets/Scenes/ARPG.unity";
// Mesh and materials of the deleted Mine Launcher prefab (git show 35ea0da4), by guid and local file id.
private const string MeshGuid = "dab8e1a18a1b825458e8e6356cf00cd2";
private const long MeshFileId = -5495902117074765545;
private const string BigExplosionGuid = "e1b08032bec16ba4eb1ae23cbd572664";
// The Substance archives (.sbsar) import nothing in this Unity, so their materials fall back to the project's
// plain material of the same finish (red car paint, magenta car paint, and Steel for titanium).
private static readonly (string guid, long fileId, string fallback)[] Materials =
{
("504c7bfe0d50ff64a88b8eac2ceb46f8", 3720744386649352057, "Assets/Materials/Car Paint Red.mat"),
("a1d39ea984432bf44b4a0686afe2bf1a", 3720744386649352057, "Assets/Materials/Car Paint Magenta.mat"),
("e879c37f3520bf14a8a6075581faf7b2", 2100000, null),
("58e16b7dbc1c31f4585e11544cdce127", 9178725544925813344, "Assets/Materials/Steel.mat"),
};
[MenuItem("Aetheria/Wire Mines")]
public static void Run()
{
var prefab = BuildPrefab();
var scene = EditorSceneManager.OpenScene(ScenePath, OpenSceneMode.Single);
var renderer = Object.FindFirstObjectByType<ZoneRenderer>(FindObjectsInactive.Include);
if (renderer == null) throw new System.InvalidOperationException($"No ZoneRenderer in {ScenePath}");
renderer.MinePrefab = prefab.GetComponent<MineInstance>();
EditorUtility.SetDirty(renderer);
EditorSceneManager.MarkSceneDirty(scene);
EditorSceneManager.SaveScene(scene);
Debug.Log($"WireMines: {PrefabPath} built and assigned to ZoneRenderer.MinePrefab in {ScenePath}");
}
private static GameObject BuildPrefab()
{
var root = new GameObject("Mine");
try
{
root.AddComponent<MeshFilter>().sharedMesh = Load<Mesh>(MeshGuid, MeshFileId);
var meshRenderer = root.AddComponent<MeshRenderer>();
meshRenderer.sharedMaterials = Materials.Select(m => MaterialFor(m.guid, m.fileId, m.fallback)).ToArray();
var mine = root.AddComponent<MineInstance>();
mine.MeshRenderer = meshRenderer;
mine.EmissionSubmesh = 2;
mine.ActiveCycleDuration = 1f;
mine.ArmedCycleDuration = .25f;
mine.ActiveEmission = 100f;
mine.ArmedEmission = 1000f;
mine.RotationSpeed = 1f;
mine.GridOffset = 0f;
mine.HitEffectScale = 10f;
mine.HitEffect = AssetDatabase.LoadAssetAtPath<GameObject>(AssetDatabase.GUIDToAssetPath(BigExplosionGuid));
if (mine.HitEffect == null) throw new System.InvalidOperationException("BigExplosion prefab did not load");
// The retired launcher's pulse: bright, dipping to a tenth at mid-cycle, bright again.
mine.EmissionCurve = new AnimationCurve(
new Keyframe(0f, 1f, .051179443f, .051179443f, 0f, .24423963f),
new Keyframe(.5f, .1f, 0f, 0f, .33333334f, .33333334f),
new Keyframe(1f, 1f, 0f, 0f, .22580647f, 0f));
return PrefabUtility.SaveAsPrefabAsset(root, PrefabPath);
}
finally
{
Object.DestroyImmediate(root);
}
}
private static Material MaterialFor(string guid, long fileId, string fallback)
{
var path = AssetDatabase.GUIDToAssetPath(guid);
if (!string.IsNullOrEmpty(path))
{
var loaded = AssetDatabase.LoadAllAssetsAtPath(path).OfType<Material>().FirstOrDefault(m => IsLocal(m, fileId));
if (loaded != null) return loaded;
}
var substitute = fallback == null ? null : AssetDatabase.LoadAssetAtPath<Material>(fallback);
if (substitute == null) throw new System.InvalidOperationException($"No material for {guid} and no fallback loads");
Debug.Log($"WireMines: {guid} did not load; using {fallback}");
return substitute;
}
private static bool IsLocal(Object asset, long fileId) =>
AssetDatabase.TryGetGUIDAndLocalFileIdentifier(asset, out _, out long id) && id == fileId;
private static T Load<T>(string guid, long fileId) where T : Object
{
var path = AssetDatabase.GUIDToAssetPath(guid);
if (string.IsNullOrEmpty(path)) throw new System.InvalidOperationException($"No asset for guid {guid}");
foreach (var asset in AssetDatabase.LoadAllAssetsAtPath(path).OfType<T>())
if (IsLocal(asset, fileId)) return asset;
throw new System.InvalidOperationException($"No {typeof(T).Name} with file id {fileId} in {path}");
}
}