Dress people at generation from a separate apparel stream.

Wardrobes live on the person in people.json: everyday layers, textbooks per parallel, and chance-based bags from catalog data. Hauling and the carry cap are defs, not constants. The golden roster gains an items column; later family members shift because Hauling consumes extra appearance rolls. Old saves without items are dressed on load.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Leonid Pershin
2026-08-20 03:24:54 +03:00
co-authored by Cursor
parent d8f4958167
commit 37501c506a
43 changed files with 8883 additions and 1305 deletions
+5
View File
@@ -609,6 +609,11 @@ public sealed class CatalogLoader
throw new ContentLoadException($"ThingDef '{thing.DefName}' has unknown skirtLength '{thing.SkirtLength}'.");
}
if (thing.CarryChance is < 0f or > 1f)
{
throw new ContentLoadException($"ThingDef '{thing.DefName}' carryChance must be 01.");
}
var layers = new HashSet<string>(StringComparer.Ordinal);
foreach (var layer in thing.Layers)
{
+6
View File
@@ -104,6 +104,12 @@ public sealed class ThingDef : Def
/// <summary>PE kit. Worn for that lesson; not a second wardrobe of everyday clothes.</summary>
public bool Pe { get; init; }
/// <summary>
/// Chance the dress generator issues this portable non-apparel item. 0 — not rolled (textbooks
/// are granted per subject instead). Apparel ignores this and fills layers.
/// </summary>
public float CarryChance { get; init; }
}
public sealed class PositionDef : Def;
+13
View File
@@ -513,6 +513,19 @@ internal static class PeopleDefValidator
throw new ContentLoadException($"BehaviorDef '{behavior.DefName}' goal weights cannot be negative.");
}
if (behavior.CarryMassBase < 0f
|| behavior.CarryMassPerStrength < 0f
|| behavior.CarryMassPerEndurance < 0f
|| behavior.CarryMassPerHauling < 0f)
{
throw new ContentLoadException($"BehaviorDef '{behavior.DefName}' carry mass cannot be negative.");
}
if (behavior.OptionalApparelChance is < 0f or > 1f)
{
throw new ContentLoadException($"BehaviorDef '{behavior.DefName}' optionalApparelChance must be 01.");
}
// A pack may invert this on purpose — lunch then pulls the class out of the lesson.
// The warning is the catch; refusing to load would make the number unmoddable.
if (behavior.LunchWeight > behavior.DutyLessonWeight)
+12
View File
@@ -250,6 +250,18 @@ public sealed class BehaviorDef : Def
/// pulls anybody out of a lesson.
/// </summary>
public float LunchWeight { get; init; } = 6f;
/// <summary>Kilograms a person can carry at strength/endurance/hauling 0, before the per-skill terms.</summary>
public float CarryMassBase { get; init; } = 5f;
public float CarryMassPerStrength { get; init; } = 0.08f;
public float CarryMassPerEndurance { get; init; } = 0.04f;
public float CarryMassPerHauling { get; init; } = 0.08f;
/// <summary>Chance each optional layer (sweater, coat, hat, accessory) is worn at generation.</summary>
public float OptionalApparelChance { get; init; } = 0.4f;
}
public enum BodyAttributeKind