Make loadouts authored catalog presets with an editor capture command

Loadouts are ship presets (variants) for spawners, not a player feature.

- Route Loadout to the catalog; the player store no longer holds it.
- Materialize takes no credits: affordability, the charge and Loadouts.Price
  are deleted, since only the player menu priced loadouts. Availability,
  key-order product choice, all-or-nothing failures and the weapon-group
  rules stay.
- Delete the InventoryPanel Save Loadout and Restore Loadout entries and
  RestoreLoadout, which also removes its docking-bay overwrite.
- The editor opens the catalog writable in the one process cache, and the
  editor-only console command capturepreset "<name>" [replace] captures the
  piloted ship. Loadouts.Commit keys a preset by name, refuses a same-name
  replace unless asked, and commits conditionally on that key. The single-file
  store then lands only that record onto the file as it is on disk. An
  unconditional commit would re-serialize every live catalog instance,
  including any that play has mutated.
- Tests: capture -> commit -> fresh reopen round-trip, explicit replace with
  no duplicate, and a preset commit that does not persist a mutated live
  Faction. The money-only tests are removed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Meta Vens 2026-09-14 23:09:43 +02:00
commit db83cd290d
6 changed files with 124 additions and 166 deletions

View file

@ -167,64 +167,11 @@ public class InventoryPanel : MonoBehaviour, IPointerClickHandler
if(GameManager.DockingBay!=null && _displayedCargo!=GameManager.DockingBay)
ContextMenu.AddOption(GameManager.DockingBay.Name, () => Display(GameManager.DockingBay));
if (_displayedEntity is Ship displayedShip)
{
ContextMenu.AddOption("Save Loadout",
() =>
{
var loadoutName = displayedShip.Name;
Dialog.Clear();
Dialog.Title.text = "Save Loadout";
Dialog.AddField("Name", () => loadoutName, s => loadoutName = s);
Dialog.Show(() => Loadouts.Save(ActionGameManager.CultCache,
Loadouts.Capture(GameManager.ItemManager, displayedShip, loadoutName)));
Dialog.MoveToCursor();
});
}
var loadouts = ActionGameManager.CultCache.GetAll<Loadout>().ToArray();
if (GameManager.DockedEntity != null && loadouts.Any())
{
ContextMenu.AddDropdown("Restore Loadout",
loadouts.Select<Loadout, (string text, Action action, bool enabled)>(loadout =>
{
var price = Loadouts.Price(GameManager.ItemManager, loadout);
return ($"{loadout.Name} - {price:n0}", () => RestoreLoadout(loadout), true);
}));
}
ContextMenu.Show();
});
}
}
// Builds the loadout against the current galaxy at the docked station. On failure nothing changes: no ship, no
// charge, and a dialog lists every slot that could not be built here.
private void RestoreLoadout(Loadout loadout)
{
var station = GameManager.DockedEntity;
var failures = new List<string>();
var availability = new LoadoutGenerator(ref GameManager.ItemManager.Random, GameManager.ItemManager,
ActionGameManager.CurrentGalaxy, GameManager.Zone.GalaxyZone, station.Faction, 0);
var ship = Loadouts.Materialize(GameManager.ItemManager, GameManager.Zone, loadout, availability.IsAvailable,
ref GameManager.Credits, failures);
if (ship == null)
{
Dialog.Clear();
Dialog.Title.text = "Loadout cannot be built here";
foreach (var failure in failures) Dialog.AddProperty(failure);
Dialog.Show();
Dialog.MoveToCursor();
return;
}
ship.SetParent(station);
ship.IsPlayerShip = true;
GameManager.DockingBay.DockedShip = ship;
GameManager.CurrentEntity = ship;
Display(ship);
}
private void Update()
{
if (_displayedEntity != null && TemperatureDisplay)