ProductBin: a binned product names its maker's line and a floor; the mint draws above that floor
FactionProductData slot 7 Bin (schema id unchanged, additive as slot 6). Mint derives a bin's distributions from its line on every lot, truncated by NextGaussianAbove (64 tries, then the cutoff). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
6d10331c05
commit
49853d3166
4 changed files with 230 additions and 11 deletions
|
|
@ -180,6 +180,21 @@ public static class Extensions
|
|||
return mean + deviation * sqrt(-2 * log(u1)) * cos(2 * PI * u2);
|
||||
}
|
||||
|
||||
// NextGaussian truncated below mean + floorDeviations * deviation: draws until one clears that cutoff, at most 64 times,
|
||||
// then returns the cutoff itself. Exact for any reachable floor and bounded and deterministic per seed for an unreachable
|
||||
// one. A binned product's fills and assembly (ProductBin). Callers clamp, as with NextGaussian.
|
||||
public static float NextGaussianAbove(this ref Random random, float mean, float deviation, float floorDeviations)
|
||||
{
|
||||
var cutoff = mean + floorDeviations * deviation;
|
||||
for (var tries = 0; tries < 64; tries++)
|
||||
{
|
||||
var draw = random.NextGaussian(mean, deviation);
|
||||
if (draw >= cutoff) return draw;
|
||||
}
|
||||
|
||||
return cutoff;
|
||||
}
|
||||
|
||||
public static float NextUnbounded(this ref Random random) => 1 / (1 - random.NextFloat()) - 1;
|
||||
public static float NextUnbounded(this ref Random random, float bias, float power, float ceiling) => 1 / (1 - pow(min(random.NextFloat(), ceiling), 1 - pow(clamp(bias,0,.99f), 1 / power))) - 1;
|
||||
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@ using GameCult.Caching;
|
|||
using MessagePack;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
// A manufacturer's branded variant of a generic design: its name, its flavor text, and how it builds each of
|
||||
// the design's roles. Which products exist is what a faction makes, so market segmentation and regional
|
||||
// progression are authored here rather than as duplicate item entries.
|
||||
// What a manufacturer sells of a generic design: its name, its flavor text, and either a line (how the maker builds each of the
|
||||
// design's roles) or a bin of one of its own lines (maker-design-tiers-allowed). Which products exist is what a faction makes, so
|
||||
// market segmentation and regional progression are authored here rather than as duplicate item entries.
|
||||
[CultDocument("aetheria.factionproductdata", "1"), Inspectable, MessagePackObject, JsonObject(MemberSerialization.OptIn)]
|
||||
public class FactionProductData
|
||||
{
|
||||
|
|
@ -29,16 +29,21 @@ public class FactionProductData
|
|||
[Inspectable, JsonProperty("roles"), Key(5)]
|
||||
public List<ProductRole> Roles = new List<ProductRole>();
|
||||
|
||||
// This line's own final test (maker-design-tiers-allowed): a binned or premium line is a better assembly as well as better
|
||||
// parts. Null assembles as its maker does.
|
||||
// This line's own final test (maker-design-tiers-allowed): a premium line is a better assembly as well as better parts.
|
||||
// Null assembles as its maker does. A binned product leaves it null and reads its line's.
|
||||
[Inspectable, JsonProperty("assembly"), Key(6)]
|
||||
public AssemblyDistribution Assembly;
|
||||
|
||||
// Set on a binned product: the line it bins and the floor above which it takes that line's output. A bin authors no Roles and
|
||||
// no Assembly; the mint derives both from the line.
|
||||
[Inspectable, JsonProperty("bin"), Key(7)]
|
||||
public ProductBin Bin;
|
||||
}
|
||||
|
||||
// How good this product's part is in one of its design's roles: a pseudo-gaussian distribution whose mean is
|
||||
// the technology the manufacturer puts in and whose standard deviation is their quality control. A market
|
||||
// segment is a second product with a role pumped up; what makes that part better is the flavor text's to tell,
|
||||
// because a part here carries quality and nothing else.
|
||||
// segment is a bin of the line (ProductBin), not a second product with a role pumped up; what makes a part better is the
|
||||
// flavor text's to tell, because a part here carries quality and nothing else.
|
||||
[MessagePackObject, JsonObject(MemberSerialization.OptIn)]
|
||||
public class ProductRole
|
||||
{
|
||||
|
|
@ -52,6 +57,19 @@ public class ProductRole
|
|||
public float StandardDeviation = .15f;
|
||||
}
|
||||
|
||||
// A bin of one of the maker's lines (ruling maker-design-tiers-allowed): the line it bins (the same design and maker, itself
|
||||
// unbinned) and the floor, in standard deviations above the mean of each of the line's distributions, below which the bin
|
||||
// takes nothing. 0 is the top half of the line, .67 the top quarter.
|
||||
[MessagePackObject, JsonObject(MemberSerialization.OptIn)]
|
||||
public class ProductBin
|
||||
{
|
||||
[Inspectable, JsonProperty("line"), Key(0)]
|
||||
public CultRecordRef<FactionProductData> Line;
|
||||
|
||||
[Inspectable, JsonProperty("floor"), Key(1)]
|
||||
public float Floor;
|
||||
}
|
||||
|
||||
// How well a maker puts the thing together: the technology and quality control of final assembly. A lot's own quality is drawn
|
||||
// from it, and it is the ceiling on every role the lot fills. Defaults are a middling maker.
|
||||
[MessagePackObject, JsonObject(MemberSerialization.OptIn)]
|
||||
|
|
|
|||
|
|
@ -121,6 +121,9 @@ public class ItemManager
|
|||
// Builds one of a manufacturer's branded products (quality-is-assembly-over-supply-chain): the unit's own quality is its
|
||||
// final assembly, drawn from the product's assembly distribution, else its maker's, else AssemblyDistribution's defaults,
|
||||
// and each of the design's roles is filled with a part whose quality is drawn from the product's distribution for it.
|
||||
// A binned product (maker-design-tiers-allowed) has no distributions of its own: it is its line's, derived here on every
|
||||
// mint, with each draw (the assembly and every fill) truncated below the line's mean + Bin.Floor deviations. The lot is
|
||||
// still the bin's: its Product, its title and so its brand.
|
||||
public int CreateLot(FactionProductData product) => Mint(product, null);
|
||||
|
||||
// The same lot at a stated quality: the assembly and every role fill are that quality and nothing is drawn, so a staged
|
||||
|
|
@ -130,29 +133,37 @@ public class ItemManager
|
|||
private int Mint(FactionProductData product, float? stated)
|
||||
{
|
||||
var design = ItemData.Get(product.Design);
|
||||
var assembly = product.Assembly ?? ItemData.Get(product.Manufacturer)?.Assembly ?? new AssemblyDistribution();
|
||||
var line = product.Bin != null ? ItemData.Get(product.Bin.Line) : null;
|
||||
if (product.Bin != null && line == null)
|
||||
Log("A binned product names a line the catalog does not hold; it mints as a product with no roles of its own.");
|
||||
var source = line ?? product;
|
||||
var floor = line != null ? product.Bin.Floor : (float?) null;
|
||||
var assembly = source.Assembly ?? ItemData.Get(product.Manufacturer)?.Assembly ?? new AssemblyDistribution();
|
||||
var lot = new Lot
|
||||
{
|
||||
Design = ItemData.RefOf<ItemData>(design),
|
||||
Origin = new Attributed { Faction = product.Manufacturer },
|
||||
Product = ItemData.RefOf(product),
|
||||
Quality = stated ?? clamp(Random.NextGaussian(assembly.Mean, assembly.StandardDeviation), .01f, 1),
|
||||
Quality = stated ?? clamp(Draw(assembly.Mean, assembly.StandardDeviation, floor), .01f, 1),
|
||||
Roles = new List<RoleFill>()
|
||||
};
|
||||
if (design.Roles != null)
|
||||
foreach (var role in design.Roles)
|
||||
{
|
||||
var build = product.Roles?.FirstOrDefault(b => b.Role == role.Name) ?? new ProductRole();
|
||||
var build = source.Roles?.FirstOrDefault(b => b.Role == role.Name) ?? new ProductRole();
|
||||
lot.Roles.Add(new RoleFill
|
||||
{
|
||||
Role = role.Name,
|
||||
Quality = stated ?? clamp(Random.NextGaussian(build.Mean, build.StandardDeviation), .01f, 1)
|
||||
Quality = stated ?? clamp(Draw(build.Mean, build.StandardDeviation, floor), .01f, 1)
|
||||
});
|
||||
}
|
||||
|
||||
return Lots.Add(lot);
|
||||
}
|
||||
|
||||
private float Draw(float mean, float deviation, float? floorDeviations) =>
|
||||
floorDeviations is { } floor ? Random.NextGaussianAbove(mean, deviation, floor) : Random.NextGaussian(mean, deviation);
|
||||
|
||||
// No branded product: a lot attributed to maker (which may be unset) with no role fills, e.g. console `give`.
|
||||
public int CreateLot(CraftedItemData design, CultRecordRef<Faction> maker, float quality)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue