Merge branch 'main' into phase/32-weather-warmth

This commit is contained in:
Leonid Pershin
2026-08-20 03:50:04 +03:00
46 changed files with 9020 additions and 1931 deletions
+21 -1
View File
@@ -61,7 +61,27 @@ internal sealed record PersonCardResponse(
IReadOnlyList<NeedStatResponse> Needs,
string? Activity,
string? ActivityLabel,
PersonFamilyResponse Family);
PersonFamilyResponse Family,
IReadOnlyList<WornItemResponse> Worn,
IReadOnlyList<CarriedItemResponse> Carried,
float CarryMass,
float CarryCapacity);
internal sealed record WornItemResponse(
string DefName,
string Label,
string? Color,
string? ColorLabel,
IReadOnlyList<DefLabelResponse> Layers);
internal sealed record CarriedItemResponse(
string DefName,
string Label,
string? Color,
string? ColorLabel,
string? Subject,
string? SubjectLabel,
float Mass);
internal sealed record LabeledStatResponse(string Id, string Label, string Value);
+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));
+7
View File
@@ -924,6 +924,13 @@ internal sealed class SchoolWorker
}
}
if (DressGenerator.NeedsDressing(roster, applicants))
{
roster = DressGenerator.EnsureRoster(catalog, roster, seed, school.Clock.Time);
applicants = DressGenerator.EnsurePool(catalog, applicants, roster, seed, school.Clock.Time);
generated = true;
}
if (!RosterFit.Matches(roster, demand))
{
throw new SchoolContentUnavailableException(
@@ -21,4 +21,11 @@
// beats walking on to the next room, below the lesson so it never pulls
// anybody out of class. Raise it past dutyLessonWeight and they will.
"lunchWeight": 6,
// Bag limit: base kg + per point of Strength, Endurance and Hauling.
"carryMassBase": 5,
"carryMassPerStrength": 0.08,
"carryMassPerEndurance": 0.04,
"carryMassPerHauling": 0.08,
// Sweater, coat, hat, accessory — each rolled separately at generation.
"optionalApparelChance": 0.4,
}
@@ -30,4 +30,10 @@
{ "attribute": "Build", "value": "Athletic", "min": 40 },
],
},
{
"defName": "Hauling",
"always": true,
"range": { "min": 0, "max": 100 },
"distribution": { "mean": 50, "stdDev": 12 },
},
]
@@ -1,6 +1,6 @@
[
{ "defName": "SchoolBag", "portable": true, "mass": 0.8 },
{ "defName": "SchoolBag", "portable": true, "mass": 0.8, "age": { "min": 6, "max": 18 }, "carryChance": 1 },
{ "defName": "Textbook", "portable": true, "mass": 0.4 },
{ "defName": "Phone", "portable": true, "mass": 0.15, "colors": ["Black", "White", "Gray"] },
{ "defName": "Bottle", "portable": true, "mass": 0.3, "colors": ["Blue", "Green", "White"] },
{ "defName": "Phone", "portable": true, "mass": 0.15, "colors": ["Black", "White", "Gray"], "age": { "min": 11 }, "carryChance": 0.45 },
{ "defName": "Bottle", "portable": true, "mass": 0.3, "colors": ["Blue", "Green", "White"], "age": { "min": 6 }, "carryChance": 0.7 },
]
@@ -115,6 +115,7 @@
"Agility": "Agility",
"Strength": "Strength",
"Endurance": "Endurance",
"Hauling": "Hauling",
"Diligent": "Diligent",
"AbsentMinded": "Absent-minded",
"Bully": "Bully",
@@ -115,6 +115,7 @@
"Agility": "Ловкость",
"Strength": "Сила",
"Endurance": "Выносливость",
"Hauling": "Переноска",
"Diligent": "Усидчивый",
"AbsentMinded": "Рассеянный",
"Bully": "Задира",