Ship S2: an Editor smoke that composes, preloads and instantiates a mod ship the way ZoneRenderer.LoadEntity does
ShipModPlaySmoke.Run asserts thruster and weapon hardpoints, the hull MeshCollider and the map icon, prints SHIP_MOD_PLAY_SMOKE and exits explicitly. The authoring doc gains a Unity batch smokes section. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
4f6bbbb8da
commit
910b51c3f8
2 changed files with 87 additions and 0 deletions
71
Assets/Scripts/Editor/ShipModPlaySmoke.cs
Normal file
71
Assets/Scripts/Editor/ShipModPlaySmoke.cs
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
// Batch proof that a mod ship survives the game's own path into a zone, without a scene: the derived catalog composed
|
||||
// with one package, ShipModShips.Preload over it, then ShipModShips.Instantiate, which is what ZoneRenderer.LoadEntity's
|
||||
// Body() calls for a hull with a Visual. A plain parent transform stands in for ZoneRoot; nothing else in LoadEntity
|
||||
// touches the body before it takes its ShipInstance.
|
||||
//
|
||||
// Unity -batchmode -nographics -projectPath <project> -executeMethod ShipModPlaySmoke.Run
|
||||
// -shipModPath <mods>/<id>/ship.cc [-shippedCatalog GameData/Aetheria.cc] -logFile <log>
|
||||
//
|
||||
// Do not pass -quit: the method is async and exits the Editor itself, 0 on success and 1 on any failure.
|
||||
public static class ShipModPlaySmoke
|
||||
{
|
||||
public static async void Run()
|
||||
{
|
||||
var args = Environment.GetCommandLineArgs();
|
||||
string Option(string name, string fallback = null)
|
||||
{
|
||||
var at = Array.IndexOf(args, name);
|
||||
return at >= 0 && at + 1 < args.Length ? args[at + 1] : fallback;
|
||||
}
|
||||
var packagePath = Option("-shipModPath");
|
||||
var derived = Path.Combine(Path.GetTempPath(), $"ship-mod-play-smoke-{Guid.NewGuid():N}.cc");
|
||||
GameObject zone = null;
|
||||
try
|
||||
{
|
||||
if (packagePath == null) throw new InvalidOperationException("-shipModPath <package/ship.cc> is required");
|
||||
var package = ShipModCatalog.ReadPackage(packagePath);
|
||||
var modsRoot = Path.GetDirectoryName(Path.GetDirectoryName(Path.GetFullPath(packagePath)));
|
||||
var composition = ShipModCatalog.Compose(Path.GetFullPath(Option("-shippedCatalog", "GameData/Aetheria.cc")), derived, modsRoot);
|
||||
if (!composition.Included.Contains(package.Visual.Id))
|
||||
throw new InvalidOperationException($"{package.Visual.Id} was not composed into the catalog: " +
|
||||
string.Join("; ", composition.Excluded.Select(excluded => $"{excluded.Package}: {excluded.Reason}")));
|
||||
|
||||
using (var catalog = AetheriaStores.Open(derived))
|
||||
{
|
||||
await ShipModShips.Preload(catalog, modsRoot);
|
||||
var hull = catalog.GetAll<HullData>().Single(candidate => candidate.Visual.IsSet() && candidate.Name == package.Hull.Name);
|
||||
|
||||
zone = new GameObject("Play Smoke Zone");
|
||||
var ship = ShipModShips.Instantiate(hull, zone.transform).GetComponent<ShipInstance>();
|
||||
if (ship == null) throw new InvalidOperationException("The mod ship body has no ShipInstance.");
|
||||
var thrusters = ship.ThrusterHardpoints?.Length ?? 0;
|
||||
var weapons = ship.WeaponHardpoints?.Length ?? 0;
|
||||
if (thrusters == 0) throw new InvalidOperationException("ThrusterHardpoints is empty.");
|
||||
if (weapons == 0 || ship.WeaponHardpoints.Any(node => node.FiringPoint == null || node.FiringPoint.Length == 0))
|
||||
throw new InvalidOperationException("WeaponHardpoints is empty or a weapon has no muzzle.");
|
||||
if (ship.HullColliders == null || ship.HullColliders.Length != 1)
|
||||
throw new InvalidOperationException("The ship needs exactly one hull collider.");
|
||||
var collider = ship.HullColliders[0].GetComponent<MeshCollider>();
|
||||
if (collider == null || collider.sharedMesh == null || !collider.sharedMesh.isReadable || collider.sharedMesh.vertexCount == 0)
|
||||
throw new InvalidOperationException("The hull collider is not a MeshCollider over a readable mesh.");
|
||||
if (ship.MapIcon == null || ship.MapIcon.sharedMaterial == null || ship.MapIcon.gameObject.layer != LayerMask.NameToLayer("Minimap"))
|
||||
throw new InvalidOperationException("The map icon is missing its renderer, material or Minimap layer.");
|
||||
Debug.Log($"SHIP_MOD_PLAY_SMOKE thrusters={thrusters} weapons={weapons} collider=ok mapicon=ok");
|
||||
}
|
||||
if (zone != null) UnityEngine.Object.DestroyImmediate(zone);
|
||||
EditorApplication.Exit(0);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
Debug.LogException(exception);
|
||||
if (zone != null) UnityEngine.Object.DestroyImmediate(zone);
|
||||
EditorApplication.Exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -194,6 +194,22 @@ catalog. Continue refuses a run naming a design the catalog no longer holds
|
|||
5. Spawn, equip, thrust, fire, shield, damage, save/reload, and schematic UI
|
||||
work for the mod ship. Only then is a shipped hull eligible for migration.
|
||||
|
||||
### Unity batch smokes
|
||||
|
||||
Both run in batchmode against a package the tests generate: set `AETHERIA_SHIP_FIXTURE_DIR` while running
|
||||
`ShipModCatalogTests.TheFixturePackageIsCompleteAndItsGlbCarriesAScene` and it leaves `mod.skiff/ship.cc` and
|
||||
`skiff.glb` there (the GLB carries a tetrahedron mesh on the map, collider and thruster anchors, plus a weapon mount
|
||||
and muzzle). Neither takes `-quit`: both are `async void` and end the Editor themselves.
|
||||
|
||||
- `-executeMethod ShipModPreview.Smoke -shipModPath <dir>/mod.skiff/ship.cc [-assemble]` imports the GLB and checks
|
||||
the anchors; `-assemble` also builds the `ShipInstance` from the template.
|
||||
- `-executeMethod ShipModPlaySmoke.Run -shipModPath <dir>/mod.skiff/ship.cc [-shippedCatalog GameData/Aetheria.cc]`
|
||||
composes a derived catalog with the package, runs `ShipModShips.Preload`, instantiates the ship through
|
||||
`ShipModShips.Instantiate` (the call `ZoneRenderer.LoadEntity` makes) under a bare parent transform, and requires
|
||||
thruster and weapon hardpoints (each weapon with a muzzle), one `MeshCollider` over a readable mesh, and a map icon.
|
||||
It prints `SHIP_MOD_PLAY_SMOKE thrusters=N weapons=N collider=ok mapicon=ok` and exits 0, or logs the exception and
|
||||
exits 1. It uses no scene and no zone.
|
||||
|
||||
## Structural and build budget
|
||||
|
||||
The proof reuses `HullData` simulation semantics, CultCache `.cc` persistence,
|
||||
|
|
|
|||
Loading…
Reference in a new issue