Aetheria/Assets/Scripts/ServerShared/RunStart.cs
Meta Vens d0782a7d14 Every new run is a scenario: the script harness, the tutorial and main galaxies as scenarios, the New Game submenu and six test arenas
A Scenario is an abstract C# class compiled with the game: Generate picks the galaxy through GalaxyStage (Main or
Prelude, at the scenario's seed, the background noise drawn from the same seed), Stage places the run's first
entities through ScenarioStage (Fit, Bare, Generated, Preset, Player, Place, Cargo), which collects named failures
and admits nothing. RunStart generates the galaxy, the arena under Ambient and admits everything through Zone.Admit,
all or nothing. The aetheria.scenario record, ScenarioShip, ScenarioEntity, RunStart.Check and the null-scenario
branch are cut.

New Game opens a submenu: Tutorial Galaxy and Main Galaxy, then (editor and development builds) Djinni Shakedown,
Starved Reactor, Arcs, Duel, Launcher Angles and Long Haul, each with its brief. MainMenu.Launch is the one way into a
new run; ActionGameManager.PendingScenario carries the scenario to StartGame once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 20:33:55 +02:00

54 lines
2.7 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
// The owner of a new run (docs/scenarios-cut.md, R.4). Every new run is a scenario: RunStart generates its galaxy,
// generates its arena (the galaxy's entrance zone) under the scenario's Ambient rule, and admits what the scenario
// stages through Zone.Admit, all of it or none of it. A scenario only declares; nothing else generates a new run's
// galaxy, generates or admits its player, or chooses its hull or faction. It holds no Unity type.
public static class RunStart
{
public sealed class Staged
{
public Ship Player;
public List<Entity> Entities; // what the scenario placed besides the player, in its order
}
// The scenario's galaxy, at the scenario's seed or, when that is zero, the clock's.
public static Galaxy Generate(Scenario scenario, GalaxyStage stage)
{
stage.SeedFrom(scenario);
return scenario.Generate(stage) ?? throw new InvalidOperationException($"Scenario {scenario.Name} generated no galaxy.");
}
public static ZonePack GenerateArena(ItemManager itemManager, ZoneGenerationSettings zoneSettings, Galaxy galaxy, Scenario scenario) =>
galaxy.Entrance.PackedContents = ZoneGenerator.GenerateZone(
itemManager, zoneSettings, galaxy, galaxy.Entrance, galaxy.IsPrelude, scenario.Ambient);
// Runs the scenario's Stage, then admits its player and everything it placed: each entity's stance set both ways
// with the player, a pilot for each piloted one. Returns null, with every failure listed and the arena unchanged,
// unless the whole scenario staged.
public static Staged Stage(ItemManager itemManager, Zone arena, Scenario scenario, string startingHull,
TutorialGenerationSettings tutorialGenerationSettings, List<string> failures)
{
var stage = new ScenarioStage(itemManager, arena, startingHull, tutorialGenerationSettings);
scenario.Stage(stage);
var reported = failures.Count;
failures.AddRange(stage.Failures);
if (!stage.PlayerPlaced)
failures.Add("player: the scenario places no player");
if (failures.Count > reported) return null;
var player = stage.PlayerShip;
player.IsPlayerShip = true;
arena.Admit(player, piloted: false);
foreach (var placed in stage.Placed)
{
var hostile = placed.Stance == ScenarioStance.Hostile;
placed.Entity.SetIff(player, hostile);
player.SetIff(placed.Entity, hostile);
arena.Admit(placed.Entity, placed.Piloted);
}
return new Staged { Player = player, Entities = stage.Placed.Select(placed => placed.Entity).ToList() };
}
}