Consumable supply: scarce stock at stations and on ships through the one availability rule
LoadoutGenerator.AvailableProducts and RandomProducts now range over CraftedItemData; only equippable gear needs a hull home. StockConsumables is the last step of GenerateShipLoadout and GenerateStationLoadout: it draws nothing when no consumable is on offer. Unbuilt/untested at this commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
d4a27def37
commit
7b73e51346
2 changed files with 264 additions and 4 deletions
|
|
@ -16,6 +16,14 @@ public class LoadoutGenerator
|
|||
public Faction Faction { get; }
|
||||
public float PriceExponent { get; }
|
||||
|
||||
// Consumables are scarce (operator, 2026-10-09): a quarter of stations stock one product, one ship in ten carries
|
||||
// one unit, and a carried consumable becomes loot when its ship dies. The units are the most a roll can stock; a
|
||||
// station draws 1 to the maximum.
|
||||
public const float StationConsumableChance = .25f;
|
||||
public const int StationConsumableUnits = 2;
|
||||
public const float ShipConsumableChance = .1f;
|
||||
public const int ShipConsumableUnits = 1;
|
||||
|
||||
public LoadoutGenerator(
|
||||
ref Random random,
|
||||
ItemManager itemManager,
|
||||
|
|
@ -46,6 +54,7 @@ public class LoadoutGenerator
|
|||
var entity = new Ship(ItemManager, null, hull, ItemManager.GameplaySettings.DefaultEntitySettings);
|
||||
entity.Faction = Faction;
|
||||
OutfitEntity(entity);
|
||||
StockConsumables(entity, ShipConsumableChance, ShipConsumableUnits);
|
||||
return EntitySerializer.Pack(entity);
|
||||
}
|
||||
|
||||
|
|
@ -117,11 +126,32 @@ public class LoadoutGenerator
|
|||
cargo.TryStore(instance);
|
||||
}
|
||||
|
||||
StockConsumables(entity, StationConsumableChance, StationConsumableUnits);
|
||||
|
||||
entity.CanTow = hullData.CanTow;
|
||||
|
||||
return EntitySerializer.Pack(entity) as OrbitalEntityPack;
|
||||
}
|
||||
|
||||
// The last thing a loadout does. With no consumable on offer, or no hold to put one in, nothing is drawn, so a
|
||||
// catalog without consumables generates exactly what it did before them. Otherwise one roll decides whether the
|
||||
// entity has any, and a hit stocks 1 to maxUnits instances of one product, stopping when no hold takes another.
|
||||
private void StockConsumables(Entity entity, float chance, int maxUnits)
|
||||
{
|
||||
var (available, _) = AvailableProducts<ConsumableItemData>(null, false);
|
||||
if (available.Length == 0 || !entity.CargoBays.Any()) return;
|
||||
if (Random.NextFloat() >= chance) return;
|
||||
|
||||
var (product, design) = RandomProducts<ConsumableItemData>(1, 0).FirstOrDefault();
|
||||
if (design == null) return;
|
||||
var units = Random.NextInt(1, maxUnits + 1);
|
||||
for (var i = 0; i < units; i++)
|
||||
{
|
||||
var instance = ItemManager.CreateInstance(product);
|
||||
if (!entity.CargoBays.Any(bay => bay.TryStore(instance))) break;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsHeater(GearData item) =>
|
||||
item.HardpointType == HardpointType.Tool &&
|
||||
item.Behaviors.Any(b => b is ThermotoggleData { HighPass: false }) &&
|
||||
|
|
@ -139,7 +169,7 @@ public class LoadoutGenerator
|
|||
// Products normally come only from manufacturers present in the galaxy and known to the zone's faction; a
|
||||
// required item falls back to any manufacturer when none of those make one, which means the product table
|
||||
// lacks variety, so it is logged rather than hidden.
|
||||
public (FactionProductData product, T design)[] RandomProducts<T>(int count, float sizeExponent, Predicate<T> filter = null, bool required = false) where T : EquippableItemData
|
||||
public (FactionProductData product, T design)[] RandomProducts<T>(int count, float sizeExponent, Predicate<T> filter = null, bool required = false) where T : CraftedItemData
|
||||
{
|
||||
var (available, preferManufacturers) = AvailableProducts(filter, required);
|
||||
return available.WeightedRandomElements(ref Random, entry =>
|
||||
|
|
@ -149,9 +179,9 @@ public class LoadoutGenerator
|
|||
count);
|
||||
}
|
||||
|
||||
// The products this faction can be offered for a design kind, and whether manufacturer preference applies (it does
|
||||
// The products this faction can be offered for a design kind (only gear needs a home), and whether manufacturer preference applies (it does
|
||||
// not once a required item has fallen back to any manufacturer). The one availability rule for every selection.
|
||||
private ((FactionProductData product, T design)[] available, bool preferManufacturers) AvailableProducts<T>(Predicate<T> filter, bool required) where T : EquippableItemData
|
||||
private ((FactionProductData product, T design)[] available, bool preferManufacturers) AvailableProducts<T>(Predicate<T> filter, bool required) where T : CraftedItemData
|
||||
{
|
||||
var hulls = ItemManager.ItemData.GetAll<HullData>().ToArray();
|
||||
var candidates = ItemManager.ItemData.GetAll<FactionProductData>()
|
||||
|
|
@ -160,7 +190,7 @@ public class LoadoutGenerator
|
|||
entry.design != null &&
|
||||
entry.design.Price > 0 &&
|
||||
entry.product.Manufacturer.IsSet() &&
|
||||
HasHome(entry.design, hulls) &&
|
||||
(!(entry.design is EquippableItemData equippable) || HasHome(equippable, hulls)) &&
|
||||
(filter?.Invoke(entry.design) ?? true))
|
||||
.ToArray();
|
||||
var available = candidates.Where(entry => IsAvailable(entry.product)).ToArray();
|
||||
|
|
|
|||
Loading…
Reference in a new issue