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
+98 -1
View File
@@ -81,7 +81,11 @@ internal static class PersonCardReader
Needs(needs, catalog, locale),
activityId,
activityLabel,
Family(roster, person));
Family(roster, person),
Worn(person, catalog, locale),
Carried(person, catalog, locale),
catalog is null ? 0f : CarryMass.Held(catalog, person.Items),
Capacity(person, skills, catalog));
}
private static IReadOnlyDictionary<string, float>? LiveNeeds(World world, string personId)
@@ -260,6 +264,99 @@ internal static class PersonCardReader
return rows;
}
private static IReadOnlyList<WornItemResponse> Worn(Person person, DefCatalog? catalog, string locale)
{
var rows = new List<WornItemResponse>();
foreach (var item in person.Items)
{
if (!item.Location.Equals(ItemLocations.Worn, StringComparison.Ordinal))
{
continue;
}
IReadOnlyList<string> layerIds = [];
if (catalog is not null && catalog.Things.TryGetValue(item.Def, out var def))
{
layerIds = def.Layers;
}
var layers = layerIds
.Select(layer => new DefLabelResponse(layer, catalog?.Text(locale, layer) ?? layer))
.ToArray();
rows.Add(new WornItemResponse(
item.Def,
ThingLabel(catalog, locale, item.Def),
item.Color,
ColorLabel(catalog, locale, item.Color),
layers));
}
return rows;
}
private static IReadOnlyList<CarriedItemResponse> Carried(Person person, DefCatalog? catalog, string locale)
{
var rows = new List<CarriedItemResponse>();
foreach (var item in person.Items)
{
if (!item.Location.Equals(ItemLocations.Bag, StringComparison.Ordinal))
{
continue;
}
var mass = catalog is not null && catalog.Things.TryGetValue(item.Def, out var def) ? def.Mass : 0f;
string? subjectLabel = null;
if (item.Subject is not null)
{
subjectLabel = catalog is not null && catalog.Subjects.TryGetValue(item.Subject, out var subject)
? catalog.Label(locale, subject)
: item.Subject;
}
rows.Add(new CarriedItemResponse(
item.Def,
ThingLabel(catalog, locale, item.Def),
item.Color,
ColorLabel(catalog, locale, item.Color),
item.Subject,
subjectLabel,
mass));
}
return rows;
}
private static string ThingLabel(DefCatalog? catalog, string locale, string defName)
{
if (catalog is not null && catalog.Things.TryGetValue(defName, out var def))
{
return catalog.Label(locale, def);
}
return defName;
}
private static string? ColorLabel(DefCatalog? catalog, string locale, string? color) =>
color is null ? null : catalog?.Text(locale, color) ?? color;
private static float Capacity(
Person person,
IReadOnlyDictionary<string, float>? live,
DefCatalog? catalog)
{
if (catalog is null)
{
return 0f;
}
if (live is not null)
{
return CarryMass.Capacity(catalog, live);
}
return CarryMass.Capacity(catalog, person.Skills);
}
private static PersonFamilyResponse Family(Roster roster, Person person)
{
var family = roster.Families.FirstOrDefault(candidate => candidate.Id.Equals(person.FamilyId, StringComparison.Ordinal));