Merge eureka/aetheria-living-world-station-kind-datum: station kinds are one catalog list; stations and fields are placed by seed; bad station packages are excluded by name
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
d233194cb2
24 changed files with 1060 additions and 52 deletions
|
|
@ -7,17 +7,19 @@ using GameCult.Caching.MessagePack;
|
|||
// Aetheria's stores: one home store per type, composed here and nowhere else.
|
||||
public static class AetheriaStores
|
||||
{
|
||||
public static readonly Type[] CatalogTypes = { typeof(ItemData), typeof(ShipAuthoring), typeof(Faction), typeof(FactionProductData), typeof(PersonalityAttribute), typeof(InputLayout), typeof(Loadout), typeof(FieldKindData), typeof(VerseGrammar), typeof(VerseVerb) };
|
||||
public static readonly Type[] CatalogTypes = { typeof(ItemData), typeof(ShipAuthoring), typeof(Faction), typeof(FactionProductData), typeof(PersonalityAttribute), typeof(InputLayout), typeof(Loadout), typeof(FieldKindData), typeof(VerseGrammar), typeof(VerseVerb), typeof(StationKindData) };
|
||||
public static readonly Type[] RunTypes = { typeof(OrbitData), typeof(BodyData), typeof(SavedZone), typeof(SavedGame), typeof(ProvenanceLedger) };
|
||||
public static readonly Type[] PlayerTypes = { typeof(PlayerSettings) };
|
||||
|
||||
// Attaches (hydrates) the catalog read-only unless catalogWritable, then the run and player stores when given.
|
||||
// Throws when a [CultGlobal] type routed to the catalog has no record. The one exemption is a writable catalog with
|
||||
// no records at all, a seed store being authored from nothing; a populated catalog is held to it however it opens.
|
||||
// A catalog's station hull with no Kind is refused, unless the caller gives kindlessStation: the tool that repairs a catalog
|
||||
// is told the hull's name and goes on.
|
||||
// The registry is the process-wide one unless the caller scopes it (the test host does, so its own document types never
|
||||
// become catalog globals); the global check above is judged against whichever registry opened the cache.
|
||||
public static CultCache Open(string catalogPath, string runPath = null, string playerPath = null, bool catalogWritable = false,
|
||||
CultDocumentRegistry registry = null)
|
||||
CultDocumentRegistry registry = null, Action<string> kindlessStation = null)
|
||||
{
|
||||
// A read-only open of an absent catalog would hydrate an empty game; a shipped player missing its catalog stops here.
|
||||
if (!catalogWritable && !File.Exists(catalogPath))
|
||||
|
|
@ -42,6 +44,7 @@ public static class AetheriaStores
|
|||
foreach (var data in cache.GetAll<ConsumableItemData>()) CultRecordRefs.Validate(data);
|
||||
VerseGrammarValidation.ValidateAll(cache.GetAll<VerseVerb>());
|
||||
ValidateBins(cache);
|
||||
StationKinds.Validate(cache, kindlessStation);
|
||||
return cache;
|
||||
}
|
||||
catch
|
||||
|
|
|
|||
|
|
@ -16,7 +16,8 @@ public static class EntitySerializer
|
|||
{
|
||||
Orbit = new CultRecordRef<OrbitData>(orbital.OrbitData),
|
||||
SecurityLevel = orbital.SecurityLevel,
|
||||
SecurityRadius = orbital.SecurityRadius
|
||||
SecurityRadius = orbital.SecurityRadius,
|
||||
Kind = orbital.Kind
|
||||
};
|
||||
else if (entity is Ship ship)
|
||||
pack = new ShipPack
|
||||
|
|
@ -120,6 +121,7 @@ public static class EntitySerializer
|
|||
Restore(itemManager, zone, pack, entity);
|
||||
entity.SecurityLevel = pack.SecurityLevel;
|
||||
entity.SecurityRadius = pack.SecurityRadius;
|
||||
entity.Kind = pack.Kind;
|
||||
if (pack.Story >= 0) entity.Story = zone.GalaxyZone.Locations[pack.Story];
|
||||
return entity;
|
||||
}
|
||||
|
|
@ -214,6 +216,7 @@ public class OrbitalEntityPack : EntityPack
|
|||
[Key(18)] public int Story = -1;
|
||||
[Key(19)] public SecurityLevel SecurityLevel;
|
||||
[Key(20)] public float SecurityRadius;
|
||||
[Key(22)] public CultRecordRef<StationKindData> Kind;
|
||||
}
|
||||
|
||||
[MessagePackObject,
|
||||
|
|
@ -237,7 +240,7 @@ public abstract class EntityPack
|
|||
[Key(14)] public EntitySettings Settings;
|
||||
[Key(15)] public CultRecordRef<Faction> Faction;
|
||||
[Key(16)] public int[][] WeaponGroups;
|
||||
// Keys 17-20 belong to the subclasses; the base's next free key is 21.
|
||||
// Keys 17-20 belong to the subclasses; the base's next free key is 23 (Boss is 21, the orbital pack's Kind is 22).
|
||||
[Key(21)] public bool Boss;
|
||||
|
||||
private int _price;
|
||||
|
|
|
|||
|
|
@ -549,6 +549,12 @@ public class HullData : EquippableItemData
|
|||
[Inspectable, JsonProperty("visual"), Key(32)]
|
||||
public CultRecordRef<ShipAuthoring> Visual;
|
||||
|
||||
// What a station hull is built to be (StationKinds): set on every Station hull and on no other. The hull names its kind
|
||||
// and the kind names no hull, so a package can add a faction's model of a kind without editing any kind record.
|
||||
// Key 33 is the next free key.
|
||||
[Inspectable, JsonProperty("kind"), Key(33)]
|
||||
public CultRecordRef<StationKindData> Kind;
|
||||
|
||||
// The farthest corner of any occupied hull cell from the centre of mass the schematic frame uses, in world
|
||||
// units: a true bound on how far the hull reaches from its position (the half-diagonal of the bounding box is not,
|
||||
// as FireControl.Detonate's comment records). A cell at (x, y) spans x +/- .5 and y +/- .5.
|
||||
|
|
|
|||
|
|
@ -74,9 +74,12 @@ public class LoadoutGenerator
|
|||
return EntitySerializer.Pack(entity) as OrbitalEntityPack;
|
||||
}
|
||||
|
||||
public OrbitalEntityPack GenerateStationLoadout()
|
||||
// A station of one kind: the hull is drawn among the station hulls the kind calls for in this faction's hands
|
||||
// (StationKinds.HullKindFor), so a faction with no model of the kind builds its stand-in's hull and keeps the kind.
|
||||
public OrbitalEntityPack GenerateStationLoadout(CultRecordRef<StationKindData> kind)
|
||||
{
|
||||
var (hullProduct, hullData) = RandomHull(HullType.Station);
|
||||
var hullKind = StationKinds.HullKindFor(ItemManager.ItemData, Faction, kind);
|
||||
var (hullProduct, hullData) = RandomHull(HullType.Station, hull => hull.Kind.Key.Equals(hullKind.Key));
|
||||
if(hullData==null)
|
||||
{
|
||||
ItemManager.Log("Unable to generate station loadout: no compatible hull found!");
|
||||
|
|
@ -85,6 +88,9 @@ public class LoadoutGenerator
|
|||
var hull = ItemManager.CreateInstance(hullProduct) as EquippableItem;
|
||||
var entity = new OrbitalEntity(ItemManager, null, hull, default, ItemManager.GameplaySettings.DefaultEntitySettings);
|
||||
entity.Faction = Faction;
|
||||
entity.Kind = kind;
|
||||
// A stand-in hull keeps its own model but not its own name: the station is what its kind says it is.
|
||||
if (!hullData.Kind.Key.Equals(kind.Key) && ItemManager.ItemData.Get(kind) is { } kindData) entity.Name = kindData.Name;
|
||||
|
||||
// Hardpoint gear goes in first so the docking bay can only take space that gear left free
|
||||
EquipHardpoints(entity);
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ public class OrbitalEntity : Entity
|
|||
public float SecurityRadius;
|
||||
public LocationStory Story;
|
||||
public bool CanTow;
|
||||
// What this station is for (StationKinds): its own fact, which under a stand-in hull differs from the hull's kind.
|
||||
public CultRecordRef<StationKindData> Kind;
|
||||
|
||||
public OrbitalEntity(ItemManager itemManager, Zone zone, EquippableItem hull, CultRecordKey orbit, EntitySettings settings) : base(itemManager, zone, hull, settings)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -108,6 +108,7 @@ public static class ShipModCatalog
|
|||
Exclude(directory, $"hull name '{hull.Name}' collides with an existing catalog item.");
|
||||
if (shipIds.Contains(ship.Id))
|
||||
Exclude(directory, $"ship ID collides with an existing catalog ship authoring record.");
|
||||
if (StationKinds.HullKindFault(cache, hull) is { } kindFault) Exclude(directory, kindFault);
|
||||
foreach (var product in package.Products)
|
||||
{
|
||||
if (!factionKeys.Contains(product.Manufacturer.Key))
|
||||
|
|
|
|||
141
Assets/Scripts/ServerShared/StationKinds.cs
Normal file
141
Assets/Scripts/ServerShared/StationKinds.cs
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using GameCult.Caching;
|
||||
using MessagePack;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
// What a station is for: one authored catalog record per kind (mining, trade-hub, shipyard...). The catalog's list of
|
||||
// these records is the one list of station kinds. A kind names no hull: the hull names its kind (HullData.Kind), and
|
||||
// which hull a faction builds for a kind is derived from the products that faction makes (StationKinds), so a package can
|
||||
// add a faction's model of a kind without editing any kind record. Records are keyed station-kind:<id>.
|
||||
[CultDocument("aetheria.stationkinddata", "1"), MessagePackObject, JsonObject(MemberSerialization.OptIn)]
|
||||
public class StationKindData
|
||||
{
|
||||
[CultName, JsonProperty("name"), Key(0)]
|
||||
public string Name;
|
||||
|
||||
[JsonProperty("description"), Key(1)]
|
||||
public string Description;
|
||||
|
||||
// A faction with no model of this kind builds the stand-in's hull and the station stays this kind. Ignored once the
|
||||
// faction offers a model of this kind.
|
||||
[JsonProperty("standIns"), Key(2)]
|
||||
public List<StationKindStandIn> StandIns = new List<StationKindStandIn>();
|
||||
|
||||
public static CultRecordKey KeyOf(string id) => new CultRecordKey("station-kind:" + id);
|
||||
}
|
||||
|
||||
[MessagePackObject, JsonObject(MemberSerialization.OptIn)]
|
||||
public class StationKindStandIn
|
||||
{
|
||||
[JsonProperty("faction"), Key(0)]
|
||||
public CultRecordRef<Faction> Faction;
|
||||
|
||||
// The kind whose model this faction builds in place of the owning kind's.
|
||||
[JsonProperty("kind"), Key(1)]
|
||||
public CultRecordRef<StationKindData> Kind;
|
||||
}
|
||||
|
||||
// The one owner of which kinds a faction builds, which kind a station gets, and which hull kind stands in.
|
||||
public static class StationKinds
|
||||
{
|
||||
// The kinds this faction can place: those of the station hulls its products offer, and those that stand in for it.
|
||||
// Ordered by key so a choice never depends on store order. Empty for no faction.
|
||||
public static CultRecordRef<StationKindData>[] KindsOf(CultCache catalog, Faction faction)
|
||||
{
|
||||
if (faction == null) return Array.Empty<CultRecordRef<StationKindData>>();
|
||||
var kinds = new HashSet<string>(OfferedStationHulls(catalog, faction).Select(hull => hull.Kind.Key.Value), StringComparer.Ordinal);
|
||||
foreach (var kind in catalog.GetAll<StationKindData>())
|
||||
if (kind.StandIns.Any(standIn => catalog.Get(standIn.Faction) == faction))
|
||||
kinds.Add(catalog.RefOf(kind).Key.Value);
|
||||
return Ordered(kinds);
|
||||
}
|
||||
|
||||
// A station's kind: a stable pick among the faction's kinds by the station's site, which generation names by the zone's
|
||||
// seed and the station's place in its orbit order (never by a key the cache mints). It draws nothing from any random
|
||||
// stream, so adding the kind to generation moves no other roll. A faction with no kinds falls back to any kind some
|
||||
// offered station hull carries, logged, and the hull draw then falls back to any maker. Unset when the catalog has none.
|
||||
public static CultRecordRef<StationKindData> Choose(CultCache catalog, Faction faction, string site, Action<string> log = null)
|
||||
{
|
||||
var pool = KindsOf(catalog, faction);
|
||||
if (pool.Length == 0)
|
||||
{
|
||||
pool = Ordered(new HashSet<string>(
|
||||
catalog.GetAll<FactionProductData>().Where(product => LoadoutGenerator.IsOffered(catalog, product))
|
||||
.Select(product => catalog.Get(product.Design) as HullData)
|
||||
.Where(hull => hull != null && hull.HullType == HullType.Station && hull.Kind.IsSet())
|
||||
.Select(hull => hull.Kind.Key.Value), StringComparer.Ordinal));
|
||||
if (pool.Length == 0) return default;
|
||||
log?.Invoke($"{faction?.Name ?? "This galaxy"} builds no station kind; placing a station of any kind some maker offers.");
|
||||
}
|
||||
return pool[(int) (site.StableHash() % (uint) pool.Length)];
|
||||
}
|
||||
|
||||
// The kind of hull a faction builds for a station kind: the kind itself when the faction offers a model of it (a model
|
||||
// wins over a stale stand-in), else the kind its stand-in names, else the kind itself (the hull draw then falls back to
|
||||
// any maker).
|
||||
public static CultRecordRef<StationKindData> HullKindFor(CultCache catalog, Faction faction, CultRecordRef<StationKindData> kind)
|
||||
{
|
||||
if (!kind.IsSet()) return kind;
|
||||
if (OfferedStationHulls(catalog, faction).Any(hull => hull.Kind.Key.Equals(kind.Key))) return kind;
|
||||
var standIn = catalog.Get(kind)?.StandIns.FirstOrDefault(entry => catalog.Get(entry.Faction) == faction);
|
||||
return standIn != null && standIn.Kind.IsSet() ? standIn.Kind : kind;
|
||||
}
|
||||
|
||||
// What is wrong with a hull's kind, or null: a Station hull names a kind the catalog holds, and no other hull names one.
|
||||
// The one rule, applied to the catalog on Open and to each mod package on Compose.
|
||||
public static string HullKindFault(CultCache catalog, HullData hull)
|
||||
{
|
||||
if (hull.HullType == HullType.Station)
|
||||
{
|
||||
if (!hull.Kind.IsSet())
|
||||
return $"Station hull '{hull.Name}' has no Kind; a station hull names the station kind it is built to be.";
|
||||
if (catalog.Get(hull.Kind) == null)
|
||||
return $"Station hull '{hull.Name}' Kind names no station kind this catalog holds.";
|
||||
}
|
||||
else if (hull.Kind.IsSet())
|
||||
return $"Hull '{hull.Name}' is a {hull.HullType} and names a Kind; only a station hull has one.";
|
||||
return null;
|
||||
}
|
||||
|
||||
// Open refuses a malformed kind catalog, naming the record and field. Every Station hull names a kind the catalog holds
|
||||
// and no other hull names one; every stand-in names a faction and a kind the catalog holds, is never its own kind, and
|
||||
// does not stand on another stand-in for the same faction (one hop). A caller that gives kindlessStation is the owner
|
||||
// of the catalog being repaired: a Station hull with no Kind is reported to it by name instead of refused.
|
||||
public static void Validate(CultCache catalog, Action<string> kindlessStation = null)
|
||||
{
|
||||
foreach (var hull in catalog.GetAll<HullData>())
|
||||
{
|
||||
var fault = HullKindFault(catalog, hull);
|
||||
if (fault == null) continue;
|
||||
if (kindlessStation != null && hull.HullType == HullType.Station && !hull.Kind.IsSet()) kindlessStation(fault);
|
||||
else throw new InvalidOperationException(fault);
|
||||
}
|
||||
foreach (var kind in catalog.GetAll<StationKindData>())
|
||||
{
|
||||
var self = catalog.RefOf(kind).Key;
|
||||
for (var i = 0; i < kind.StandIns.Count; i++)
|
||||
{
|
||||
var entry = kind.StandIns[i];
|
||||
var at = $"Station kind '{kind.Name}' StandIns[{i}]";
|
||||
var faction = catalog.Get(entry.Faction);
|
||||
if (faction == null) throw new InvalidOperationException($"{at}.Faction names no faction this catalog holds.");
|
||||
var other = catalog.Get(entry.Kind);
|
||||
if (other == null) throw new InvalidOperationException($"{at}.Kind names no station kind this catalog holds.");
|
||||
if (entry.Kind.Key.Equals(self)) throw new InvalidOperationException($"{at}.Kind is the kind itself; a kind cannot stand in for itself.");
|
||||
if (other.StandIns.Any(next => catalog.Get(next.Faction) == faction))
|
||||
throw new InvalidOperationException($"{at}.Kind stands in for the same faction itself; a stand-in is one hop.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<HullData> OfferedStationHulls(CultCache catalog, Faction faction) =>
|
||||
catalog.GetAll<FactionProductData>()
|
||||
.Where(product => LoadoutGenerator.IsOffered(catalog, product) && catalog.Get(product.Manufacturer) == faction)
|
||||
.Select(product => catalog.Get(product.Design) as HullData)
|
||||
.Where(hull => hull != null && hull.HullType == HullType.Station && hull.Kind.IsSet());
|
||||
|
||||
private static CultRecordRef<StationKindData>[] Ordered(IEnumerable<string> keys) =>
|
||||
keys.OrderBy(key => key, StringComparer.Ordinal).Select(key => new CultRecordRef<StationKindData>(new CultRecordKey(key))).ToArray();
|
||||
}
|
||||
11
Assets/Scripts/ServerShared/StationKinds.cs.meta
Normal file
11
Assets/Scripts/ServerShared/StationKinds.cs.meta
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 7b3e1a94c2d84f6e9a05d1c8e4f27b63
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -196,7 +196,7 @@ public class Zone
|
|||
case AsteroidBeltData belt:
|
||||
// Mining Cut 3 (Q16 B): a belt from before field kinds existed gets its kind here, once, by
|
||||
// the rule generation uses, and keeps it.
|
||||
FieldKinds.Ensure(body.Key, belt, cache);
|
||||
FieldKinds.Ensure(body.Key.Value, belt, cache);
|
||||
AsteroidBelts[body.Key] = new AsteroidBelt(belt);
|
||||
break;
|
||||
case SunData sun:
|
||||
|
|
|
|||
|
|
@ -134,10 +134,11 @@ public class FieldKindData
|
|||
// Mining Cut 3 (Q16 B): the one owner of which kind a belt is.
|
||||
public static class FieldKinds
|
||||
{
|
||||
// A weighted pick among the catalog's kinds, by a stable hash of the belt's key: the same belt and the same
|
||||
// catalog always give the same kind. Kinds are ordered by key so the pick does not depend on store order.
|
||||
// A weighted pick among the catalog's kinds, by a stable hash of the belt's site: the same site and the same
|
||||
// catalog always give the same kind. Generation names a belt by the zone's seed and the belt's place in generation
|
||||
// order (never by the key the cache mints, which differs every run); a belt saved without a kind is named by its key. Kinds are ordered by key so the pick does not depend on store order.
|
||||
// Unset when the catalog has no kind with positive weight.
|
||||
public static CultRecordRef<FieldKindData> Assign(CultRecordKey field, CultCache catalog)
|
||||
public static CultRecordRef<FieldKindData> Assign(string site, CultCache catalog)
|
||||
{
|
||||
var kinds = catalog.GetAll<FieldKindData>()
|
||||
.Where(kind => kind.GenerationWeight > 0f)
|
||||
|
|
@ -146,7 +147,7 @@ public static class FieldKinds
|
|||
.ToArray();
|
||||
if (kinds.Length == 0) return default;
|
||||
|
||||
var pick = field.Value.StableHash() / 4294967296.0 * kinds.Sum(kind => (double) kind.Weight);
|
||||
var pick = site.StableHash() / 4294967296.0 * kinds.Sum(kind => (double) kind.Weight);
|
||||
foreach (var kind in kinds)
|
||||
{
|
||||
if (pick < kind.Weight) return new CultRecordRef<FieldKindData>(kind.Key);
|
||||
|
|
@ -157,10 +158,10 @@ public static class FieldKinds
|
|||
|
||||
// Gives a belt with no kind its kind and saves it; a belt that has one keeps it, whatever the catalog now says.
|
||||
// Generation (ZoneGenerator) and first load (Zone) both call this, so there is one assignment rule.
|
||||
public static void Ensure(CultRecordKey field, AsteroidBeltData belt, CultCache cache)
|
||||
public static void Ensure(string site, AsteroidBeltData belt, CultCache cache)
|
||||
{
|
||||
if (belt.Kind.IsSet()) return;
|
||||
belt.Kind = Assign(field, cache);
|
||||
belt.Kind = Assign(site, cache);
|
||||
if (belt.Kind.IsSet()) cache.Upsert(belt);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ public static class ZoneGenerator
|
|||
pack.Orbits = planets.Select(planet => cache.RefOf(orbitMap[planet])).ToList();
|
||||
|
||||
var beltOf = new Dictionary<GeneratorPlanet, AsteroidBeltData>();
|
||||
pack.Planets = planets.Where(p=>!p.Empty).Select(planet =>
|
||||
pack.Planets = planets.Where(p=>!p.Empty).Select((planet, index) =>
|
||||
{
|
||||
BodyType bodyType = planet.Belt ? BodyType.Asteroid :
|
||||
planet.Mass > zoneSettings.SunMass ? BodyType.Sun :
|
||||
|
|
@ -213,7 +213,7 @@ public static class ZoneGenerator
|
|||
var bodyKey = cache.Upsert(planetData).Key;
|
||||
planetData.Name = bodyKey.Value.Substring(0, 8);
|
||||
// Mining Cut 3 (Q16 B): a belt's field kind is chosen here, at generation, and saved with it.
|
||||
if (planetData is AsteroidBeltData generatedBelt) FieldKinds.Ensure(bodyKey, generatedBelt, cache);
|
||||
if (planetData is AsteroidBeltData generatedBelt) FieldKinds.Ensure($"{seed}:belt:{index}", generatedBelt, cache);
|
||||
return cache.Upsert(planetData);
|
||||
}).ToList();
|
||||
|
||||
|
|
@ -239,7 +239,7 @@ public static class ZoneGenerator
|
|||
BodyData swarmBody = swarm;
|
||||
var swarmKey = cache.Upsert(swarmBody).Key;
|
||||
swarm.Name = swarmKey.Value.Substring(0, 8);
|
||||
FieldKinds.Ensure(swarmKey, swarm, cache);
|
||||
FieldKinds.Ensure($"{seed}:swarm:{swarmHosts.Count}", swarm, cache);
|
||||
pack.Planets.Add(cache.Upsert(swarmBody));
|
||||
}
|
||||
|
||||
|
|
@ -268,7 +268,7 @@ public static class ZoneGenerator
|
|||
BodyData lanedBody = laned;
|
||||
var lanedKey = cache.Upsert(lanedBody).Key;
|
||||
laned.Name = lanedKey.Value.Substring(0, 8);
|
||||
FieldKinds.Ensure(lanedKey, laned, cache);
|
||||
FieldKinds.Ensure($"{seed}:lane:{shaped.Count}", laned, cache);
|
||||
pack.Planets.Add(cache.Upsert(lanedBody));
|
||||
}
|
||||
|
||||
|
|
@ -419,7 +419,8 @@ public static class ZoneGenerator
|
|||
if (i >= selectedStationOrbits.Length) { itemManager.Log($"Story station for {story.Faction?.Name} has no rail clear of the bodies; its story has nowhere to live!"); continue; }
|
||||
var orbit = selectedStationOrbits[i];
|
||||
var lagrangeOrbit = CreateLagrangeOrbit(orbit);
|
||||
var station = GetLoadoutGenerator(story.Faction).GenerateStationLoadout();
|
||||
var station = GetLoadoutGenerator(story.Faction).GenerateStationLoadout(
|
||||
StationKinds.Choose(cache, story.Faction, $"{seed}:station:{i}", itemManager.Log));
|
||||
if (station == null)
|
||||
{
|
||||
itemManager.Log($"Story station for {story.Faction?.Name} could not be generated; its story has nowhere to live!");
|
||||
|
|
@ -440,7 +441,8 @@ public static class ZoneGenerator
|
|||
var security = (SecurityLevel) ((int) ((1f - pow(random.NextFloat(), factionPresence / 2f)) * 3f));
|
||||
|
||||
var lagrangeOrbit = CreateLagrangeOrbit(orbit);
|
||||
var station = GetLoadoutGenerator(nearestFaction).GenerateStationLoadout();
|
||||
var station = GetLoadoutGenerator(nearestFaction).GenerateStationLoadout(
|
||||
StationKinds.Choose(cache, nearestFaction, $"{seed}:station:{i}", itemManager.Log));
|
||||
if (station == null)
|
||||
{
|
||||
if (isTutorialEntrance)
|
||||
|
|
|
|||
Loading…
Reference in a new issue