diff --git a/docs/phases/32-weather-warmth.md b/docs/phases/32-weather-warmth.md
index 8334558..955e180 100644
--- a/docs/phases/32-weather-warmth.md
+++ b/docs/phases/32-weather-warmth.md
@@ -11,28 +11,28 @@
## Задачи
-- [ ] `ClimatePresetDef` получает рабочие числа: нормы по месяцам, разброс дня, осадки
-- [ ] Школа считает улицу из пресета + сида + даты/часа; не каждый тик, а когда меняется
+- [x] `ClimatePresetDef` получает рабочие числа: нормы по месяцам, разброс дня, осадки
+- [x] Школа считает улицу из пресета + сида + даты/часа; не каждый тик, а когда меняется
надпись или это сдвинет тепло
-- [ ] В здании теплее улицы за счёт стен, без техника ещё не комфорт (комфорт — фаза 33)
-- [ ] Двор и крыльцо — улица
-- [ ] Нужда `Warmth`: декей от разницы «утепление одежды vs температура места»; вне школы
+- [x] В здании теплее улицы за счёт стен, без техника ещё не комфорт (комфорт — фаза 33)
+- [x] Двор и крыльцо — улица
+- [x] Нужда `Warmth`: декей от разницы «утепление одежды vs температура места»; вне школы
восстанавливается, как сон
-- [ ] Черты теплолюбивый / холодолюбивый: поле смещения комфорта на `TraitDef`, взаимно
+- [x] Черты теплолюбивый / холодолюбивый: поле смещения комфорта на `TraitDef`, взаимно
несовместимы
-- [ ] Кадр часов: температура (десятые доли °C, `i16`) и осадки (`u8`). Версия протокола +1.
+- [x] Кадр часов: температура (десятые доли °C, `i16`) и осадки (`u8`). Версия протокола +1.
`ProtocolCodec.cs`, `protocol.ts`, `docs/protocol.md` в одном коммите
-- [ ] Клиент рисует погоду у даты; не считает её из месяца
-- [ ] Пропуск пустого времени выставляет погоду утра, не вчерашнюю
+- [x] Клиент рисует погоду у даты; не считает её из месяца
+- [x] Пропуск пустого времени выставляет погоду утра, не вчерашнюю
## Тесты, без которых фаза не закрыта
-- [ ] Тот же пресет, сид и timestamp → та же температура и осадки
-- [ ] Январь холоднее июля на ванильном пресете
-- [ ] Ниже нуля осадки — снег, не дождь (если пресет вообще дал осадки)
-- [ ] На морозе без верхней одежды тепло падает; дома за ночь возвращается
-- [ ] Кадр часов круглый трип и байтовая раскладка на обеих сторонах
-- [ ] Клиент с старой версией протокола отваливается Hello, как обычно
+- [x] Тот же пресет, сид и timestamp → та же температура и осадки
+- [x] Январь холоднее июля на ванильном пресете
+- [x] Ниже нуля осадки — снег, не дождь (если пресет вообще дал осадки)
+- [x] На морозе без верхней одежды тепло падает; дома за ночь возвращается
+- [x] Кадр часов круглый трип и байтовая раскладка на обеих сторонах
+- [x] Клиент с старой версией протокола отваливается Hello, как обычно
## Критерий готовности
diff --git a/docs/phases/README.md b/docs/phases/README.md
index 94d4102..41c2660 100644
--- a/docs/phases/README.md
+++ b/docs/phases/README.md
@@ -137,7 +137,7 @@
| Фаза | Статус | Зачем |
| --- | --- | --- |
-| [32. Погода и тепло](32-weather-warmth.md) | 🔄 | Пресет крутит улицу, кадр часов, нужда, две черты |
+| [32. Погода и тепло](32-weather-warmth.md) | ✅ | Пресет крутит улицу, кадр часов, нужда, две черты |
| [33. Техник и шкафчики](33-lockers-technician.md) | ⬜ | Комната техника, две раздевалки, раздача шкафчиков |
32 стоит на 29; 33 — на 31 и 32.
diff --git a/docs/protocol.md b/docs/protocol.md
index 2dc196c..e75e0e4 100644
--- a/docs/protocol.md
+++ b/docs/protocol.md
@@ -1,4 +1,4 @@
-# Wire protocol v7
+# Wire protocol v8
The client talks to the server two ways:
@@ -680,11 +680,13 @@ The first frame the client receives.
| 1 | `i64` | client clock, echoed unchanged |
| 9 | `u32` | server tick when the ping was handled |
-### `0x83` Clock — 24 bytes
+### `0x83` Clock — 27 bytes
Sent every tick to every connection that has a school open, and only to those.
`skipAllowed` is the server's verdict; the client must not recompute it.
`skipTargetUnixMs` is 0 when skip is refused.
+Temperature is outdoor tenths of a °C (`i16`, so −50 is −5.0 °C). Precipitation is `0` none,
+`1` rain, `2` snow. The client must not derive weather from the month.
| Offset | Type | Field |
| --- | --- | --- |
@@ -695,6 +697,8 @@ Sent every tick to every connection that has a school open, and only to those.
| 14 | `u8` | speed index |
| 15 | `u8` | `1` skip allowed, `0` refused |
| 16 | `i64` | skip target, milliseconds since the Unix epoch, UTC; `0` if refused |
+| 24 | `i16` | outdoor temperature, tenths of a °C |
+| 26 | `u8` | precipitation: `0` none, `1` rain, `2` snow |
### `0x84` SchoolGone — 5 bytes
@@ -780,7 +784,7 @@ Each person:
the oldest, because a stale clock is worthless once a newer one exists.
- The map snapshot and presence use a separate reliable queue so ticks cannot crowd them out.
-## Not in v7 yet
+## Not in v8 yet
Authentication, Sit orders, an event log, walk animation, and `OpenLocation` on the server —
the tree and the location panel are filtered on the client from the snapshot plus presence.
diff --git a/src/HSchool.Client/src/format/weather.test.ts b/src/HSchool.Client/src/format/weather.test.ts
new file mode 100644
index 0000000..9dcf3fe
--- /dev/null
+++ b/src/HSchool.Client/src/format/weather.test.ts
@@ -0,0 +1,26 @@
+import { afterEach, describe, expect, it } from 'vitest';
+import { Precipitation } from '../net/protocol.ts';
+import { getLocale, setLocale } from '../i18n/locale.ts';
+import { formatTemperatureC, formatWeather } from './weather.ts';
+
+const initial = getLocale();
+
+afterEach(() => setLocale(initial));
+
+describe('formatWeather', () => {
+ it('draws tenths and snow from the frame, not from the month', () => {
+ setLocale('ru');
+ expect(formatTemperatureC(-50)).toBe('\u22125');
+ expect(formatWeather(-50, Precipitation.Snow)).toBe('\u22125 °C, снег');
+ });
+
+ it('omits precipitation when the street is dry', () => {
+ setLocale('ru');
+ expect(formatWeather(82, Precipitation.None)).toBe('8.2 °C');
+ });
+
+ it('uses English rain labels', () => {
+ setLocale('en');
+ expect(formatWeather(40, Precipitation.Rain)).toBe('4 °C, rain');
+ });
+});
diff --git a/src/HSchool.Client/src/format/weather.ts b/src/HSchool.Client/src/format/weather.ts
new file mode 100644
index 0000000..39884cd
--- /dev/null
+++ b/src/HSchool.Client/src/format/weather.ts
@@ -0,0 +1,23 @@
+import { Precipitation } from '../net/protocol.ts';
+import { t } from '../i18n/strings.ts';
+
+/** Formats tenths of a °C as the clock shows them. Does not invent weather from the date. */
+export function formatTemperatureC(tenths: number): string {
+ const value = tenths / 10;
+ const abs = Math.abs(value);
+ const body = Number.isInteger(value) ? String(abs) : abs.toFixed(1);
+ return `${value < 0 ? '\u2212' : ''}${body}`;
+}
+
+export function formatWeather(tenths: number, precipitation: number): string {
+ const temp = formatTemperatureC(tenths);
+ if (precipitation === Precipitation.Snow) {
+ return t('weatherPrecip', { temp, precip: t('precipSnow') });
+ }
+
+ if (precipitation === Precipitation.Rain) {
+ return t('weatherPrecip', { temp, precip: t('precipRain') });
+ }
+
+ return t('weatherClear', { temp });
+}
diff --git a/src/HSchool.Client/src/i18n/strings.test.ts b/src/HSchool.Client/src/i18n/strings.test.ts
index daff97c..c646cd7 100644
--- a/src/HSchool.Client/src/i18n/strings.test.ts
+++ b/src/HSchool.Client/src/i18n/strings.test.ts
@@ -29,6 +29,7 @@ describe('t', () => {
expect(t('peopleCarryMass', { held: '1.2', cap: '14' })).toBe('1.2 / 14 kg');
expect(t('mapOccupancy', { name: 'Кабинет 204', activity: 'Математика · 5Б' }))
.toBe('Кабинет 204 (Математика · 5Б)');
+ expect(t('weatherPrecip', { temp: '\u22125', precip: t('precipSnow') })).toBe('\u22125 °C, snow');
expect(t('mapHeadcount', { name: 'Коридор', count: 12 })).toBe('Коридор (12)');
expect(t('mapHeadcountActivity', { name: 'Класс 101', count: 18, activity: 'Математика · 5А' }))
.toBe('Класс 101 (18 · Математика · 5А)');
diff --git a/src/HSchool.Client/src/i18n/strings.ts b/src/HSchool.Client/src/i18n/strings.ts
index 3d8cb15..2d5c8a6 100644
--- a/src/HSchool.Client/src/i18n/strings.ts
+++ b/src/HSchool.Client/src/i18n/strings.ts
@@ -205,6 +205,10 @@ const ru = {
mapHeadcount: '{name} ({count})',
mapHeadcountActivity: '{name} ({count} · {activity})',
skipTo: 'Пропустить до {date}',
+ weatherClear: '{temp} °C',
+ weatherPrecip: '{temp} °C, {precip}',
+ precipRain: 'дождь',
+ precipSnow: 'снег',
presenceAt: '{name}',
presenceWalking: 'в пути ({name})',
presenceAway: 'вне школы',
@@ -436,6 +440,10 @@ const en: Messages = {
mapHeadcount: '{name} ({count})',
mapHeadcountActivity: '{name} ({count} · {activity})',
skipTo: 'Skip to {date}',
+ weatherClear: '{temp} °C',
+ weatherPrecip: '{temp} °C, {precip}',
+ precipRain: 'rain',
+ precipSnow: 'snow',
presenceAt: '{name}',
presenceWalking: 'walking ({name})',
presenceAway: 'off campus',
diff --git a/src/HSchool.Client/src/net/protocol.test.ts b/src/HSchool.Client/src/net/protocol.test.ts
index a5971c7..1ecdd56 100644
--- a/src/HSchool.Client/src/net/protocol.test.ts
+++ b/src/HSchool.Client/src/net/protocol.test.ts
@@ -98,7 +98,7 @@ describe('decodeServerMessage', () => {
// 2012-04-03T06:00:00Z → skip to 2012-04-04T06:00:00Z
const gameTimeMs = Date.UTC(2012, 3, 3, 6, 0, 0);
const skipTargetMs = Date.UTC(2012, 3, 4, 6, 0, 0);
- const buffer = new ArrayBuffer(24);
+ const buffer = new ArrayBuffer(27);
const view = new DataView(buffer);
view.setUint8(0, MessageType.ServerClock);
view.setInt32(1, 7, true);
@@ -107,6 +107,8 @@ describe('decodeServerMessage', () => {
view.setUint8(14, 2);
view.setUint8(15, 1);
view.setBigInt64(16, BigInt(skipTargetMs), true);
+ view.setInt16(24, -50, true);
+ view.setUint8(26, 2);
expect(decodeServerMessage(buffer)).toEqual({
type: 'clock',
@@ -116,6 +118,8 @@ describe('decodeServerMessage', () => {
speedIndex: 2,
skipAllowed: true,
skipTarget: new Date(skipTargetMs),
+ temperatureTenths: -50,
+ precipitation: 2,
});
});
diff --git a/src/HSchool.Client/src/net/protocol.ts b/src/HSchool.Client/src/net/protocol.ts
index 607ec6c..8dfbc35 100644
--- a/src/HSchool.Client/src/net/protocol.ts
+++ b/src/HSchool.Client/src/net/protocol.ts
@@ -5,7 +5,7 @@
* changed together and documented in `docs/protocol.md`. All numbers are little-endian.
*/
-export const PROTOCOL_VERSION = 7;
+export const PROTOCOL_VERSION = 8;
export const MessageType = {
ClientHello: 0x01,
@@ -50,6 +50,12 @@ export interface PongMessage {
readonly serverTick: number;
}
+export const Precipitation = {
+ None: 0,
+ Rain: 1,
+ Snow: 2,
+} as const;
+
export interface ClockMessage {
readonly type: 'clock';
readonly schoolId: number;
@@ -61,6 +67,10 @@ export interface ClockMessage {
readonly skipAllowed: boolean;
/** UTC instant the skip would land on; null when skip is refused. */
readonly skipTarget: Date | null;
+ /** Outdoor temperature in tenths of a °C. */
+ readonly temperatureTenths: number;
+ /** `Precipitation.None` / `Rain` / `Snow`. */
+ readonly precipitation: number;
}
export interface SchoolGoneMessage {
@@ -248,7 +258,7 @@ function decodePong(view: DataView): PongMessage {
}
function decodeClock(view: DataView): ClockMessage {
- ensure(view, 24);
+ ensure(view, 27);
const skipTargetMs = Number(view.getBigInt64(16, true));
return {
@@ -259,6 +269,8 @@ function decodeClock(view: DataView): ClockMessage {
speedIndex: view.getUint8(14),
skipAllowed: view.getUint8(15) !== 0,
skipTarget: skipTargetMs === 0 ? null : new Date(skipTargetMs),
+ temperatureTenths: view.getInt16(24, true),
+ precipitation: view.getUint8(26),
};
}
diff --git a/src/HSchool.Client/src/style.css b/src/HSchool.Client/src/style.css
index b01fd75..49c6000 100644
--- a/src/HSchool.Client/src/style.css
+++ b/src/HSchool.Client/src/style.css
@@ -266,6 +266,12 @@ body {
text-transform: capitalize;
}
+.clock__weather {
+ margin: 0;
+ color: var(--text-muted);
+ font-size: 13px;
+}
+
.clock__controls {
display: flex;
align-items: center;
diff --git a/src/HSchool.Client/src/ui/gameScreen.ts b/src/HSchool.Client/src/ui/gameScreen.ts
index 7f726ec..0e8c6a0 100644
--- a/src/HSchool.Client/src/ui/gameScreen.ts
+++ b/src/HSchool.Client/src/ui/gameScreen.ts
@@ -8,6 +8,7 @@ import {
type PresenceNode,
} from '../net/protocol.ts';
import { formatGameDate, formatGameDateTime, formatGameTimeOfDay, formatGameWeekday } from '../format/gameTime.ts';
+import { formatWeather } from '../format/weather.ts';
import { getLocale } from '../i18n/locale.ts';
import { t } from '../i18n/strings.ts';
import { fetchDirectory, type School } from '../net/api.ts';
@@ -38,6 +39,7 @@ export class GameScreen {
private readonly time = el('p', { class: 'clock__time', text: '--:--' });
private readonly date = el('p', { class: 'clock__date' });
private readonly weekday = el('p', { class: 'clock__weekday' });
+ private readonly weather = el('p', { class: 'clock__weather' });
private readonly playPauseButton = el('button', { class: 'button button--icon', type: 'button', text: '▶' });
private readonly skipButton = el('button', { class: 'button button--small', type: 'button' });
private readonly speedButtons: HTMLButtonElement[];
@@ -86,6 +88,8 @@ export class GameScreen {
private lastSpeedIndex = 0;
private skipAllowed = false;
private skipTarget: Date | null = null;
+ private lastTemperatureTenths: number | null = null;
+ private lastPrecipitation: number | null = null;
private inspected: 'location' | 'person' = 'location';
constructor(options: GameScreenOptions) {
@@ -112,7 +116,7 @@ export class GameScreen {
'div',
{ class: 'clockbar__now' },
this.time,
- el('div', { class: 'clockbar__labels' }, this.date, this.weekday),
+ el('div', { class: 'clockbar__labels' }, this.date, this.weekday, this.weather),
),
el('div', { class: 'clock__controls' }, this.playPauseButton, ...this.speedButtons, this.skipButton),
),
@@ -184,7 +188,15 @@ export class GameScreen {
this.paintSelection();
if (this.lastGameTime !== null) {
- this.applyClock(this.lastGameTime, this.running, this.lastSpeedIndex, this.skipAllowed, this.skipTarget);
+ this.applyClock(
+ this.lastGameTime,
+ this.running,
+ this.lastSpeedIndex,
+ this.skipAllowed,
+ this.skipTarget,
+ this.lastTemperatureTenths,
+ this.lastPrecipitation,
+ );
} else {
this.playPauseButton.title = t('resume');
}
@@ -208,7 +220,7 @@ export class GameScreen {
this.skipAllowed = false;
this.skipTarget = null;
this.rebuildTree();
- this.applyClock(new Date(school.gameTime), school.running, school.speedIndex, false, null);
+ this.applyClock(new Date(school.gameTime), school.running, school.speedIndex, false, null, null, null);
this.people.show(school.id);
this.people.setLocate((id) => this.placeOf(id));
this.management.setLocate((id) => this.placeOf(id));
@@ -251,7 +263,15 @@ export class GameScreen {
}
update(clock: ClockMessage): void {
- this.applyClock(clock.gameTime, clock.running, clock.speedIndex, clock.skipAllowed, clock.skipTarget);
+ this.applyClock(
+ clock.gameTime,
+ clock.running,
+ clock.speedIndex,
+ clock.skipAllowed,
+ clock.skipTarget,
+ clock.temperatureTenths,
+ clock.precipitation,
+ );
}
private rebuildTree(): void {
@@ -358,16 +378,24 @@ export class GameScreen {
speedIndex: number,
skipAllowed: boolean,
skipTarget: Date | null,
+ temperatureTenths: number | null,
+ precipitation: number | null,
): void {
this.running = running;
this.lastGameTime = gameTime;
this.lastSpeedIndex = speedIndex;
this.skipAllowed = skipAllowed;
this.skipTarget = skipTarget;
+ this.lastTemperatureTenths = temperatureTenths;
+ this.lastPrecipitation = precipitation;
this.time.textContent = formatGameTimeOfDay(gameTime);
this.date.textContent = formatGameDate(gameTime);
this.weekday.textContent = formatGameWeekday(gameTime);
+ this.weather.textContent =
+ temperatureTenths === null || precipitation === null
+ ? ''
+ : formatWeather(temperatureTenths, precipitation);
this.playPauseButton.textContent = running ? '⏸' : '▶';
this.playPauseButton.title = running ? t('pause') : t('resume');
diff --git a/src/HSchool.Content/Defs.cs b/src/HSchool.Content/Defs.cs
index dfeeb10..7194570 100644
--- a/src/HSchool.Content/Defs.cs
+++ b/src/HSchool.Content/Defs.cs
@@ -148,6 +148,12 @@ public sealed class RoomDef : Def
/// Game minutes spent occupying this room when walking through it.
public float TravelMinutes { get; init; }
+
+ ///
+ /// Outdoor like the yard: porch, crossing between buildings. Indoor rooms stay warmer than
+ /// the street by the climate preset's wall offset.
+ ///
+ public bool Outdoor { get; init; }
}
public sealed class BuildingDef : Def;
diff --git a/src/HSchool.Content/PeopleDefValidator.cs b/src/HSchool.Content/PeopleDefValidator.cs
index ef16ff3..d3e1492 100644
--- a/src/HSchool.Content/PeopleDefValidator.cs
+++ b/src/HSchool.Content/PeopleDefValidator.cs
@@ -30,6 +30,11 @@ internal static class PeopleDefValidator
ValidateCountry(country, catalog);
}
+ foreach (var preset in catalog.ClimatePresets.Values)
+ {
+ ValidateClimatePreset(preset);
+ }
+
foreach (var subject in catalog.Subjects.Values)
{
ValidateSubject(subject, catalog);
@@ -642,6 +647,40 @@ internal static class PeopleDefValidator
ValidateNameSet(country.Names ?? new NameSetDef(), catalog, country.DefName);
}
+ private static void ValidateClimatePreset(ClimatePresetDef preset)
+ {
+ if (preset.Abstract)
+ {
+ return;
+ }
+
+ if (preset.MonthlyNorms.Count != 12)
+ {
+ throw new ContentLoadException(
+ $"ClimatePresetDef '{preset.DefName}' monthlyNorms must list 12 months.");
+ }
+
+ if (preset.DaySpread < 0 || preset.HourSpread < 0)
+ {
+ throw new ContentLoadException($"ClimatePresetDef '{preset.DefName}' spreads cannot be negative.");
+ }
+
+ if (preset.PrecipitationChance < 0 || preset.PrecipitationChance > 1)
+ {
+ throw new ContentLoadException($"ClimatePresetDef '{preset.DefName}' precipitationChance must be 0–1.");
+ }
+
+ if (preset.ComfortHalfWidthC < 0)
+ {
+ throw new ContentLoadException($"ClimatePresetDef '{preset.DefName}' comfortHalfWidthC cannot be negative.");
+ }
+
+ if (preset.InsulationPerC < 0)
+ {
+ throw new ContentLoadException($"ClimatePresetDef '{preset.DefName}' insulationPerC cannot be negative.");
+ }
+ }
+
private static void ValidateNameSet(NameSetDef names, DefCatalog catalog, string countryDefName)
{
if (!NameGrammar.IsKnownPatronymic(names.PatronymicRule))
diff --git a/src/HSchool.Content/PeopleDefs.cs b/src/HSchool.Content/PeopleDefs.cs
index d543751..624e3b8 100644
--- a/src/HSchool.Content/PeopleDefs.cs
+++ b/src/HSchool.Content/PeopleDefs.cs
@@ -184,6 +184,12 @@ public sealed class TraitDef : Def
/// Extra minutes of commute slack. Positive arrives earlier; negative cuts it closer.
///
public int CommuteMinutes { get; init; }
+
+ ///
+ /// Shifts the warmth comfort band, in °C. Heat-loving is positive (suffers cold earlier);
+ /// cold-loving is negative.
+ ///
+ public float ComfortTemperatureOffset { get; init; }
}
public sealed class StaffingDef : Def
@@ -355,6 +361,12 @@ public sealed class NeedDef : Def
/// restores overnight; hunger does not keep falling at home.
///
public bool RestoredOffCampus { get; init; }
+
+ ///
+ /// When true, campus drain is not per hour. Warmth uses it as the
+ /// drop per °C of mismatch against the place temperature.
+ ///
+ public bool Environmental { get; init; }
}
public sealed class CaseTable
@@ -453,7 +465,7 @@ public sealed class NameSetDef
///
/// What the player picks at create: nested names plus climate-preset ids. Weather numbers live
-/// on and stay unused until phase 32.
+/// on .
///
public sealed class CountryDef : Def
{
@@ -463,7 +475,32 @@ public sealed class CountryDef : Def
}
///
-/// Outdoor climate a country may roll. Monthly temperatures land in phase 32; the id is enough
-/// to persist which preset a school was born with.
+/// Outdoor climate a country may roll. Monthly norms, day/hour spread and precipitation chance
+/// are the numbers the school uses to sample the street; indoor offset is walls without a technician.
///
-public sealed class ClimatePresetDef : Def;
+public sealed class ClimatePresetDef : Def
+{
+ /// Mean outdoor °C for months 1–12. Concrete presets must list all twelve.
+ public IReadOnlyList MonthlyNorms { get; init; } = [];
+
+ /// How far a day's mean may wander from the monthly norm, °C.
+ public float DaySpread { get; init; }
+
+ /// How far the hour wanders from that day's mean, °C. Coldest around 03:00, warmest 15:00.
+ public float HourSpread { get; init; }
+
+ /// Chance of precipitation this hour, 0–1. Below 0 °C the same roll is snow.
+ public float PrecipitationChance { get; init; }
+
+ /// Added to indoor temperature vs the street. Walls hold heat; this is not comfort.
+ public float IndoorOffset { get; init; } = 8f;
+
+ /// Centre of the clothing comfort band, °C, before trait offsets.
+ public float ComfortC { get; init; } = 21f;
+
+ /// Half-width of the comfort band, °C. Inside it warmth barely drops.
+ public float ComfortHalfWidthC { get; init; } = 3f;
+
+ /// How many °C of protection one insulation point is worth.
+ public float InsulationPerC { get; init; } = 1f;
+}
diff --git a/src/HSchool.Protocol/Messages.cs b/src/HSchool.Protocol/Messages.cs
index cbc203b..1c92079 100644
--- a/src/HSchool.Protocol/Messages.cs
+++ b/src/HSchool.Protocol/Messages.cs
@@ -33,6 +33,8 @@ public readonly record struct ServerPongMessage(long ClientTimeMs, uint ServerTi
/// interpreted as UTC — the game calendar has no time zone.
/// is the server's verdict; the client must not recompute it.
/// is 0 when skip is refused.
+/// is outdoor °C × 10. is
+/// .
///
public readonly record struct ServerClockMessage(
int SchoolId,
@@ -40,7 +42,17 @@ public readonly record struct ServerClockMessage(
bool Running,
byte SpeedIndex,
bool SkipAllowed = false,
- long SkipTargetUnixMs = 0);
+ long SkipTargetUnixMs = 0,
+ short TemperatureTenths = 0,
+ byte Precipitation = 0);
+
+/// Outdoor precipitation on the clock frame. Below 0 °C the same weather roll is snow.
+public static class PrecipitationKind
+{
+ public const byte None = 0;
+ public const byte Rain = 1;
+ public const byte Snow = 2;
+}
/// The open school no longer exists (deleted from another tab); the client returns to the menu.
public readonly record struct ServerSchoolGoneMessage(int SchoolId);
diff --git a/src/HSchool.Protocol/PacketReader.cs b/src/HSchool.Protocol/PacketReader.cs
index 63b76db..c02fc5a 100644
--- a/src/HSchool.Protocol/PacketReader.cs
+++ b/src/HSchool.Protocol/PacketReader.cs
@@ -49,6 +49,14 @@ public ref struct PacketReader(ReadOnlySpan buffer)
return value;
}
+ public short ReadInt16()
+ {
+ EnsureAvailable(sizeof(short));
+ var value = BinaryPrimitives.ReadInt16LittleEndian(_buffer[_position..]);
+ _position += sizeof(short);
+ return value;
+ }
+
public int ReadInt32()
{
EnsureAvailable(sizeof(int));
diff --git a/src/HSchool.Protocol/PacketWriter.cs b/src/HSchool.Protocol/PacketWriter.cs
index a8395b2..cf58859 100644
--- a/src/HSchool.Protocol/PacketWriter.cs
+++ b/src/HSchool.Protocol/PacketWriter.cs
@@ -52,6 +52,13 @@ public ref struct PacketWriter(Span buffer)
_position += byteCount;
}
+ public void WriteInt16(short value)
+ {
+ EnsureRoom(sizeof(short));
+ BinaryPrimitives.WriteInt16LittleEndian(_buffer[_position..], value);
+ _position += sizeof(short);
+ }
+
public void WriteInt32(int value)
{
EnsureRoom(sizeof(int));
diff --git a/src/HSchool.Protocol/ProtocolCodec.cs b/src/HSchool.Protocol/ProtocolCodec.cs
index fefd8a9..34024fb 100644
--- a/src/HSchool.Protocol/ProtocolCodec.cs
+++ b/src/HSchool.Protocol/ProtocolCodec.cs
@@ -13,7 +13,7 @@ public static class ProtocolCodec
/// Largest fixed-size frame this codec produces. Variable map snapshots and
/// presence frames use instead.
///
- public const int MaxFrameSize = 24;
+ public const int MaxFrameSize = 27;
public static int WriteHello(Span destination, in ClientHelloMessage message)
{
@@ -99,6 +99,8 @@ public static class ProtocolCodec
writer.WriteByte(message.SpeedIndex);
writer.WriteByte(message.SkipAllowed ? (byte)1 : (byte)0);
writer.WriteInt64(message.SkipTargetUnixMs);
+ writer.WriteInt16(message.TemperatureTenths);
+ writer.WriteByte(message.Precipitation);
return writer.Position;
}
@@ -317,7 +319,17 @@ public static class ProtocolCodec
var speedIndex = reader.ReadByte();
var skipAllowed = reader.ReadByte() != 0;
var skipTarget = reader.ReadInt64();
- return new ServerClockMessage(schoolId, gameTime, running, speedIndex, skipAllowed, skipTarget);
+ var temperatureTenths = reader.ReadInt16();
+ var precipitation = reader.ReadByte();
+ return new ServerClockMessage(
+ schoolId,
+ gameTime,
+ running,
+ speedIndex,
+ skipAllowed,
+ skipTarget,
+ temperatureTenths,
+ precipitation);
}
public static ServerSchoolGoneMessage ReadSchoolGone(ReadOnlySpan source)
diff --git a/src/HSchool.Protocol/ProtocolConstants.cs b/src/HSchool.Protocol/ProtocolConstants.cs
index a2f053f..6abec88 100644
--- a/src/HSchool.Protocol/ProtocolConstants.cs
+++ b/src/HSchool.Protocol/ProtocolConstants.cs
@@ -4,7 +4,7 @@ namespace HSchool.Protocol;
public static class ProtocolConstants
{
/// Bumped on every breaking change to the binary layout.
- public const byte Version = 7;
+ public const byte Version = 8;
/// Upper bound for a single WebSocket frame accepted by the server.
public const int MaxMessageSize = 8 * 1024;
diff --git a/src/HSchool.Server/Game/SchoolWorker.cs b/src/HSchool.Server/Game/SchoolWorker.cs
index ab9bfc0..e9d9c45 100644
--- a/src/HSchool.Server/Game/SchoolWorker.cs
+++ b/src/HSchool.Server/Game/SchoolWorker.cs
@@ -1070,7 +1070,9 @@ internal sealed class SchoolWorker
school.Clock.IsRunning,
(byte)school.Clock.SpeedIndex,
skip.Allowed,
- skip.Time is { } target ? new DateTimeOffset(target).ToUnixTimeMilliseconds() : 0));
+ skip.Time is { } target ? new DateTimeOffset(target).ToUnixTimeMilliseconds() : 0,
+ school.Weather.Tenths,
+ (byte)school.Weather.Precipitation));
client.TrySend(frame.AsMemory(0, length));
}
diff --git a/src/HSchool.Server/mods/core/defs/climates/continental-cold.jsonc b/src/HSchool.Server/mods/core/defs/climates/continental-cold.jsonc
index 55ca41c..7626f14 100644
--- a/src/HSchool.Server/mods/core/defs/climates/continental-cold.jsonc
+++ b/src/HSchool.Server/mods/core/defs/climates/continental-cold.jsonc
@@ -1,3 +1,11 @@
{
"defName": "ContinentalCold",
+ "monthlyNorms": [-16.0, -14.0, -7.0, 2.0, 10.0, 16.0, 18.0, 15.0, 9.0, 1.0, -8.0, -14.0],
+ "daySpread": 6,
+ "hourSpread": 5,
+ "precipitationChance": 0.28,
+ "indoorOffset": 8,
+ "comfortC": 21,
+ "comfortHalfWidthC": 3,
+ "insulationPerC": 1,
}
diff --git a/src/HSchool.Server/mods/core/defs/climates/temperate-continental.jsonc b/src/HSchool.Server/mods/core/defs/climates/temperate-continental.jsonc
index f65cc2c..4e543b2 100644
--- a/src/HSchool.Server/mods/core/defs/climates/temperate-continental.jsonc
+++ b/src/HSchool.Server/mods/core/defs/climates/temperate-continental.jsonc
@@ -1,3 +1,12 @@
{
"defName": "TemperateContinental",
+ // Moscow-like monthly means. January mornings sit below zero so the clock can show snow.
+ "monthlyNorms": [-9.3, -8.0, -2.2, 6.7, 13.2, 17.0, 19.2, 17.0, 11.3, 5.3, -0.5, -6.2],
+ "daySpread": 5,
+ "hourSpread": 4,
+ "precipitationChance": 0.32,
+ "indoorOffset": 8,
+ "comfortC": 21,
+ "comfortHalfWidthC": 3,
+ "insulationPerC": 1,
}
diff --git a/src/HSchool.Server/mods/core/defs/needs/needs.jsonc b/src/HSchool.Server/mods/core/defs/needs/needs.jsonc
index 4a35cd6..cef13bf 100644
--- a/src/HSchool.Server/mods/core/defs/needs/needs.jsonc
+++ b/src/HSchool.Server/mods/core/defs/needs/needs.jsonc
@@ -3,4 +3,13 @@
{ "defName": "Hunger", "initial": 1, "decayPerHour": 0.2, "min": 0, "max": 1, "restoredOffCampus": true },
{ "defName": "Toilet", "initial": 1, "decayPerHour": 0.15, "min": 0, "max": 1 },
{ "defName": "Social", "initial": 1, "decayPerHour": 0.08, "min": 0, "max": 1 },
+ {
+ "defName": "Warmth",
+ "initial": 1,
+ "decayPerHour": 0.04,
+ "min": 0,
+ "max": 1,
+ "restoredOffCampus": true,
+ "environmental": true,
+ },
]
diff --git a/src/HSchool.Server/mods/core/defs/rooms/circulation.jsonc b/src/HSchool.Server/mods/core/defs/rooms/circulation.jsonc
index baf3faa..7dbde4a 100644
--- a/src/HSchool.Server/mods/core/defs/rooms/circulation.jsonc
+++ b/src/HSchool.Server/mods/core/defs/rooms/circulation.jsonc
@@ -1,5 +1,5 @@
[
// Walkable rooms that exist to connect the graph: lobby, stairs. Empty on purpose.
- { "defName": "EntranceHall", "travelMinutes": 1 },
+ { "defName": "EntranceHall", "travelMinutes": 1, "outdoor": true },
{ "defName": "Stairwell", "travelMinutes": 1.5 },
]
diff --git a/src/HSchool.Server/mods/core/defs/traits/traits.jsonc b/src/HSchool.Server/mods/core/defs/traits/traits.jsonc
index f737af4..36b1a9a 100644
--- a/src/HSchool.Server/mods/core/defs/traits/traits.jsonc
+++ b/src/HSchool.Server/mods/core/defs/traits/traits.jsonc
@@ -80,4 +80,16 @@
{ "skill": "Chemistry", "offset": 4 },
],
},
+ {
+ "defName": "HeatLoving",
+ "weight": 5,
+ "incompatible": ["ColdLoving"],
+ "comfortTemperatureOffset": 4,
+ },
+ {
+ "defName": "ColdLoving",
+ "weight": 5,
+ "incompatible": ["HeatLoving"],
+ "comfortTemperatureOffset": -4,
+ },
]
diff --git a/src/HSchool.Server/mods/core/localizations/en.jsonc b/src/HSchool.Server/mods/core/localizations/en.jsonc
index e99cde3..07fe741 100644
--- a/src/HSchool.Server/mods/core/localizations/en.jsonc
+++ b/src/HSchool.Server/mods/core/localizations/en.jsonc
@@ -145,6 +145,9 @@
"Hunger": "Hunger",
"Toilet": "Toilet",
"Social": "Social",
+ "Warmth": "Warmth",
+ "HeatLoving": "Heat-loving",
+ "ColdLoving": "Cold-loving",
"Russia": "Russia",
"TemperateContinental": "Temperate continental",
"ContinentalCold": "Cold continental",
diff --git a/src/HSchool.Server/mods/core/localizations/ru.jsonc b/src/HSchool.Server/mods/core/localizations/ru.jsonc
index d47b303..4bc62c4 100644
--- a/src/HSchool.Server/mods/core/localizations/ru.jsonc
+++ b/src/HSchool.Server/mods/core/localizations/ru.jsonc
@@ -145,6 +145,9 @@
"Hunger": "Голод",
"Toilet": "Туалет",
"Social": "Общение",
+ "Warmth": "Тепло",
+ "HeatLoving": "Теплолюбивый",
+ "ColdLoving": "Холодолюбивый",
"Russia": "Россия",
"TemperateContinental": "Умеренно-континентальный",
"ContinentalCold": "Континентальный холодный",
diff --git a/src/HSchool.Simulation/NeedDecay.cs b/src/HSchool.Simulation/NeedDecay.cs
index 0936f27..bccef5d 100644
--- a/src/HSchool.Simulation/NeedDecay.cs
+++ b/src/HSchool.Simulation/NeedDecay.cs
@@ -46,7 +46,7 @@ public static class NeedDecay
foreach (var def in catalog.Needs.Values)
{
- if (def.Abstract || !needs.Values.TryGetValue(def.DefName, out var current))
+ if (def.Abstract || def.Environmental || !needs.Values.TryGetValue(def.DefName, out var current))
{
continue;
}
diff --git a/src/HSchool.Simulation/OutdoorWeather.cs b/src/HSchool.Simulation/OutdoorWeather.cs
new file mode 100644
index 0000000..9ab5215
--- /dev/null
+++ b/src/HSchool.Simulation/OutdoorWeather.cs
@@ -0,0 +1,20 @@
+namespace HSchool.Simulation;
+
+/// Street precipitation. Below 0 °C the same roll is snow, above it is rain.
+public enum Precipitation : byte
+{
+ None = 0,
+ Rain = 1,
+ Snow = 2,
+}
+
+/// Cached outdoor state shown on the clock and used to drain warmth.
+public readonly record struct OutdoorWeather(float TemperatureC, Precipitation Precipitation)
+{
+ public static OutdoorWeather None { get; } = new(0f, Precipitation.None);
+
+ public short Tenths => (short)Math.Clamp(
+ Math.Round(TemperatureC * 10d, MidpointRounding.AwayFromZero),
+ short.MinValue,
+ short.MaxValue);
+}
diff --git a/src/HSchool.Simulation/PersonInsulation.cs b/src/HSchool.Simulation/PersonInsulation.cs
new file mode 100644
index 0000000..4acc2f3
--- /dev/null
+++ b/src/HSchool.Simulation/PersonInsulation.cs
@@ -0,0 +1,48 @@
+using HSchool.Content;
+using HSchool.People;
+
+namespace HSchool.Simulation;
+
+///
+/// Worn insulation in °C-equivalent points, summed from apparel currently on the body.
+/// Tests that need a bare frost set on the entity explicitly.
+///
+public readonly record struct PersonInsulation(float Value)
+{
+ public static PersonInsulation Naked { get; } = new(0f);
+
+ public static PersonInsulation FromWorn(DefCatalog catalog, params string[] defNames)
+ {
+ ArgumentNullException.ThrowIfNull(catalog);
+ var sum = 0f;
+ foreach (var name in defNames)
+ {
+ if (catalog.Things.TryGetValue(name, out var thing) && thing.Layers.Count > 0)
+ {
+ sum += thing.Insulation;
+ }
+ }
+
+ return new PersonInsulation(sum);
+ }
+
+ public static PersonInsulation FromPerson(Person person, DefCatalog? catalog)
+ {
+ ArgumentNullException.ThrowIfNull(person);
+ if (catalog is null)
+ {
+ return Naked;
+ }
+
+ var worn = new List();
+ foreach (var item in person.Items)
+ {
+ if (item.Location.Equals(ItemLocations.Worn, StringComparison.Ordinal))
+ {
+ worn.Add(item.Def);
+ }
+ }
+
+ return worn.Count == 0 ? Naked : FromWorn(catalog, [.. worn]);
+ }
+}
diff --git a/src/HSchool.Simulation/PlaceClimate.cs b/src/HSchool.Simulation/PlaceClimate.cs
new file mode 100644
index 0000000..d2c7ee1
--- /dev/null
+++ b/src/HSchool.Simulation/PlaceClimate.cs
@@ -0,0 +1,57 @@
+using HSchool.Content;
+
+namespace HSchool.Simulation;
+
+///
+/// Street vs indoors. The yard is always outdoor; rooms opt in with
+/// so the porch matches the street. Indoor temperature is the street plus the preset's wall offset
+/// — warmer, not yet comfortable (the technician is phase 33).
+///
+public static class PlaceClimate
+{
+ public static bool IsOutdoor(DefCatalog catalog, MapLayout? map, string? nodeId)
+ {
+ if (nodeId is null || map is null)
+ {
+ return true;
+ }
+
+ if (map.Territory is { } territory && territory.Id.Equals(nodeId, StringComparison.Ordinal))
+ {
+ return true;
+ }
+
+ var defName = map.NodeDef(nodeId);
+ if (defName is null)
+ {
+ return true;
+ }
+
+ if (catalog.Territories.ContainsKey(defName))
+ {
+ return true;
+ }
+
+ return catalog.Rooms.TryGetValue(defName, out var room) && room.Outdoor;
+ }
+
+ public static float TemperatureC(School school, string? nodeId)
+ {
+ var outdoor = school.Weather.TemperatureC;
+ var catalog = school.Catalog;
+ if (catalog is null || IsOutdoor(catalog, school.Map, nodeId))
+ {
+ return outdoor;
+ }
+
+ var offset = 8f;
+ if (school.ClimatePresetId is { } presetId
+ && catalog.ClimatePresets.TryGetValue(presetId, out var preset)
+ && !preset.Abstract)
+ {
+ offset = preset.IndoorOffset;
+ }
+
+ return outdoor + offset;
+ }
+}
diff --git a/src/HSchool.Simulation/RosterSpawner.cs b/src/HSchool.Simulation/RosterSpawner.cs
index 0febeb6..1dc32d1 100644
--- a/src/HSchool.Simulation/RosterSpawner.cs
+++ b/src/HSchool.Simulation/RosterSpawner.cs
@@ -1,6 +1,7 @@
using Arch.Core;
-using HSchool.People;
using HSchool.Ai;
+using HSchool.Content;
+using HSchool.People;
namespace HSchool.Simulation;
@@ -10,7 +11,7 @@ public static class RosterSpawner
private static readonly QueryDescription People = new QueryDescription().WithAll();
private static readonly QueryDescription Classes = new QueryDescription().WithAll();
- public static void Spawn(World world, Roster roster)
+ public static void Spawn(World world, Roster roster, DefCatalog? catalog = null)
{
foreach (var schoolClass in roster.Classes)
{
@@ -30,7 +31,8 @@ public static class RosterSpawner
new PersonBody(person.Numbers, person.Choices),
new PersonSkills(person.Skills.ToDictionary(pair => pair.Key, pair => (float)pair.Value, StringComparer.Ordinal)),
new PersonTraits(person.Traits),
- new PersonNeeds(new Dictionary(person.Needs, StringComparer.Ordinal)),
+ new PersonNeeds(NeedsOf(person, catalog)),
+ PersonInsulation.FromPerson(person, catalog),
new PersonRoles(
person.IsStudent,
person.IsStaff,
@@ -45,11 +47,30 @@ public static class RosterSpawner
}
/// Drops the previous composition and spawns . Called on yearly intake.
- public static void Replace(World world, Roster roster)
+ public static void Replace(World world, Roster roster, DefCatalog? catalog = null)
{
DestroyAll(world, People);
DestroyAll(world, Classes);
- Spawn(world, roster);
+ Spawn(world, roster, catalog);
+ }
+
+ private static Dictionary NeedsOf(Person person, DefCatalog? catalog)
+ {
+ var values = new Dictionary(person.Needs, StringComparer.Ordinal);
+ if (catalog is null)
+ {
+ return values;
+ }
+
+ foreach (var need in catalog.Needs.Values)
+ {
+ if (!need.Abstract && !values.ContainsKey(need.DefName))
+ {
+ values[need.DefName] = need.Initial;
+ }
+ }
+
+ return values;
}
private static void DestroyAll(World world, QueryDescription query)
diff --git a/src/HSchool.Simulation/School.cs b/src/HSchool.Simulation/School.cs
index 316851c..81dcf13 100644
--- a/src/HSchool.Simulation/School.cs
+++ b/src/HSchool.Simulation/School.cs
@@ -79,9 +79,12 @@ public sealed class School : IDisposable
/// Country used to generate this school's people. Needed again on 1 September.
public string? CountryId { get; private set; }
- /// Climate preset rolled at birth. Phase 32 reads it; it cannot change on a live school.
+ /// Climate preset rolled at birth. Weather reads it; it cannot change on a live school.
public string? ClimatePresetId { get; private set; }
+ /// Street temperature and precipitation last committed for the clock and warmth.
+ public OutdoorWeather Weather { get; private set; } = OutdoorWeather.None;
+
/// Skill everyone generated for this school speaks natively.
public string? NativeLanguage { get; private set; }
@@ -142,11 +145,12 @@ public sealed class School : IDisposable
ClimatePresetId = climatePresetId;
NativeLanguage = nativeLanguage;
Applicants = applicants;
- RosterSpawner.Spawn(World, roster);
+ RosterSpawner.Spawn(World, roster, Catalog);
PlanDay = null;
LastDecisionSlot = null;
Plans.Clear();
DecisionQueue.Clear();
+ SyncWeather(force: true);
}
public bool TryStartAction(string personId, string actionId)
@@ -219,6 +223,7 @@ public sealed class School : IDisposable
LastDecisionSlot = null;
NeedDecay.Apply(World, Catalog, (next.Value - before).TotalMinutes);
peopleChanged |= ApparelWear.Apply(this, gameMinutes: 0, before);
+ SyncWeather(force: true);
return new SkipEmptyResult(SkipEmptyError.None, next.Value, peopleChanged);
}
@@ -235,7 +240,7 @@ public sealed class School : IDisposable
Roster = roster;
Applicants = applicants;
var snapshot = PresenceSystem.Capture(this);
- RosterSpawner.Replace(World, roster);
+ RosterSpawner.Replace(World, roster, Catalog);
PresenceSystem.Restore(this, snapshot);
TimetableDirty = true;
}
@@ -285,17 +290,51 @@ public sealed class School : IDisposable
if (Catalog is not null)
{
var below = PresenceSystem.BelowThreshold(this);
+ SyncWeather(force: false);
NeedDecay.Apply(World, Catalog, gameMinutes);
+ WarmthDecay.Apply(this, gameMinutes);
PresenceSystem.EnqueueNewlyUrgent(this, below);
PresenceSystem.DrainDecisions(this);
LessonLearningSystem.Apply(this, gameMinutes);
peopleChanged |= ApparelWear.Apply(this, gameMinutes, before);
}
}
+ else
+ {
+ SyncWeather(force: false);
+ }
return peopleChanged;
}
+ ///
+ /// Recomputes the street from the preset, seed and current time. Commits when the clock
+ /// tenths or precipitation change, so warmth does not jitter every tick. A skip must force
+ /// the morning sample — yesterday's evening must not stick.
+ ///
+ public void SyncWeather(bool force)
+ {
+ ObjectDisposedException.ThrowIf(_disposed, this);
+ var next = EvaluateWeather();
+ if (force || next.Tenths != Weather.Tenths || next.Precipitation != Weather.Precipitation)
+ {
+ Weather = next;
+ }
+ }
+
+ private OutdoorWeather EvaluateWeather()
+ {
+ if (Catalog is null
+ || ClimatePresetId is null
+ || !Catalog.ClimatePresets.TryGetValue(ClimatePresetId, out var preset)
+ || preset.Abstract)
+ {
+ return OutdoorWeather.None;
+ }
+
+ return WeatherSampler.Sample(preset, PeopleSeed, Clock.Time);
+ }
+
private bool TryYearlyIntake(DateTime before, DateTime after)
{
if (Roster is null || Catalog is null || CountryId is null)
@@ -313,7 +352,7 @@ public sealed class School : IDisposable
if (changed)
{
var snapshot = PresenceSystem.Capture(this);
- RosterSpawner.Replace(World, Roster);
+ RosterSpawner.Replace(World, Roster, Catalog);
PresenceSystem.Restore(this, snapshot);
TimetableDirty = true;
}
diff --git a/src/HSchool.Simulation/WarmthDecay.cs b/src/HSchool.Simulation/WarmthDecay.cs
new file mode 100644
index 0000000..ba83837
--- /dev/null
+++ b/src/HSchool.Simulation/WarmthDecay.cs
@@ -0,0 +1,81 @@
+using Arch.Core;
+using HSchool.Ai;
+using HSchool.Content;
+
+namespace HSchool.Simulation;
+
+///
+/// Drains Warmth from the gap between clothing insulation and the temperature of the
+/// place the person is in. Off campus the generic need restore already snaps it to max.
+///
+public static class WarmthDecay
+{
+ private static readonly QueryDescription People =
+ new QueryDescription().WithAll();
+
+ public static void Apply(School school, double gameMinutes)
+ {
+ if (gameMinutes <= 0)
+ {
+ return;
+ }
+
+ var catalog = school.Catalog;
+ if (catalog is null || !catalog.Needs.TryGetValue("Warmth", out var warmth) || warmth.Abstract)
+ {
+ return;
+ }
+
+ var preset = Preset(school, catalog);
+ var hours = gameMinutes / 60d;
+ var world = school.World;
+ world.Query(
+ in People,
+ (ref PersonNeeds needs, ref PersonTraits traits, ref PersonInsulation insulation, ref Presence presence) =>
+ {
+ if (!presence.IsOnCampus || !needs.Values.ContainsKey(warmth.DefName))
+ {
+ return;
+ }
+
+ var place = PlaceClimate.TemperatureC(school, presence.NodeId);
+ var felt = place + insulation.Value * (preset?.InsulationPerC ?? 1f);
+ var center = (preset?.ComfortC ?? 21f) + TraitOffset(catalog, traits);
+ var halfWidth = preset?.ComfortHalfWidthC ?? 3f;
+ var mismatch = Math.Abs(felt - center) - halfWidth;
+ if (mismatch <= 0)
+ {
+ return;
+ }
+
+ var next = needs.Values[warmth.DefName] - (float)(mismatch * warmth.DecayPerHour * hours);
+ needs.Values[warmth.DefName] = Math.Clamp(next, warmth.Min, warmth.Max);
+ });
+ }
+
+ private static ClimatePresetDef? Preset(School school, DefCatalog catalog)
+ {
+ if (school.ClimatePresetId is { } id
+ && catalog.ClimatePresets.TryGetValue(id, out var preset)
+ && !preset.Abstract)
+ {
+ return preset;
+ }
+
+ return null;
+ }
+
+ private static float TraitOffset(DefCatalog catalog, PersonTraits traits)
+ {
+ var offset = 0f;
+ foreach (var id in traits.Ids)
+ {
+ if (catalog.Traits.TryGetValue(id, out var trait))
+ {
+ offset += trait.ComfortTemperatureOffset;
+ }
+ }
+
+ return offset;
+ }
+}
diff --git a/src/HSchool.Simulation/WeatherSampler.cs b/src/HSchool.Simulation/WeatherSampler.cs
new file mode 100644
index 0000000..4756684
--- /dev/null
+++ b/src/HSchool.Simulation/WeatherSampler.cs
@@ -0,0 +1,53 @@
+using HSchool.Content;
+using HSchool.People;
+
+namespace HSchool.Simulation;
+
+///
+/// Outdoor temperature and precipitation from a climate preset, the school seed, and the
+/// calendar. Same inputs always produce the same street. Not a tick accumulator — sample it
+/// when the clock label or warmth would move.
+///
+public static class WeatherSampler
+{
+ private const int DaySalt = 0x57EA11;
+ private const int HourSalt = 0x57EA12;
+
+ public static OutdoorWeather Sample(ClimatePresetDef preset, int schoolSeed, DateTime time)
+ {
+ ArgumentNullException.ThrowIfNull(preset);
+
+ var utc = DateTime.SpecifyKind(time, DateTimeKind.Utc);
+ var month = utc.Month;
+ var norm = MonthNorm(preset, month);
+ var dayNumber = DateOnly.FromDateTime(utc).DayNumber;
+ var dayNoise = SignedUnit(Seed.Mix(schoolSeed, dayNumber, DaySalt)) * preset.DaySpread;
+ var hour = utc.Hour + utc.Minute / 60d + utc.Second / 3600d;
+ var diurnal = -Math.Cos((hour - 3d) / 24d * 2d * Math.PI) * preset.HourSpread;
+ var temperature = (float)(norm + dayNoise + diurnal);
+
+ var wet = Unit(Seed.Mix(schoolSeed, dayNumber * 24 + utc.Hour, HourSalt)) < preset.PrecipitationChance;
+ var precipitation = !wet
+ ? Precipitation.None
+ : temperature < 0f ? Precipitation.Snow : Precipitation.Rain;
+
+ return new OutdoorWeather(temperature, precipitation);
+ }
+
+ private static float MonthNorm(ClimatePresetDef preset, int month)
+ {
+ if (preset.MonthlyNorms.Count != 12)
+ {
+ return 0f;
+ }
+
+ return preset.MonthlyNorms[month - 1];
+ }
+
+ private static double Unit(int mixed)
+ {
+ return (uint)mixed / (double)uint.MaxValue;
+ }
+
+ private static double SignedUnit(int mixed) => Unit(mixed) * 2d - 1d;
+}
diff --git a/tests/HSchool.AppHost.Tests/GameSocketTests.cs b/tests/HSchool.AppHost.Tests/GameSocketTests.cs
index cb498b6..0965a05 100644
--- a/tests/HSchool.AppHost.Tests/GameSocketTests.cs
+++ b/tests/HSchool.AppHost.Tests/GameSocketTests.cs
@@ -428,6 +428,22 @@ public class GameSocketTests(AppHostFixture fixture)
Assert.Equal(ClientTime, pong.ClientTimeMs);
}
+ [Fact]
+ public async Task OldProtocolVersion_IsRejected()
+ {
+ using var socket = await ConnectRawAsync();
+
+ await SendAsync(socket, buffer => ProtocolCodec.WriteHello(
+ buffer,
+ new ClientHelloMessage((byte)(ProtocolConstants.Version - 1), ProtocolConstants.LocaleRussian)));
+
+ var buffer = new byte[ProtocolConstants.MaxMessageSize];
+ var result = await socket.ReceiveAsync(buffer, TestContext.Current.CancellationToken);
+
+ Assert.Equal(WebSocketMessageType.Close, result.MessageType);
+ Assert.Equal(WebSocketCloseStatus.ProtocolError, socket.CloseStatus);
+ }
+
[Fact]
public async Task VersionMismatch_IsRejected()
{
diff --git a/tests/HSchool.AppHost.Tests/golden/README.md b/tests/HSchool.AppHost.Tests/golden/README.md
index 3788c91..d5cfa96 100644
--- a/tests/HSchool.AppHost.Tests/golden/README.md
+++ b/tests/HSchool.AppHost.Tests/golden/README.md
@@ -11,6 +11,9 @@ Phase 31 rewrote `current/1.people.json` because people are born dressed (`items
person, plus `Hauling`). `legacy-no-native/` is left as it was: no inventory, so load still
has to dress them and must not reshuffle names.
+Phase 32 rewrote `current/1.people.json` again: `Warmth` plus `HeatLoving` / `ColdLoving` in
+the trait pool. `legacy-no-native/` stays historic.
+
Regenerate after an intentional roster change (same commit):
```
diff --git a/tests/HSchool.AppHost.Tests/golden/current/1.people.json b/tests/HSchool.AppHost.Tests/golden/current/1.people.json
index ff376d8..c25c3b4 100644
--- a/tests/HSchool.AppHost.Tests/golden/current/1.people.json
+++ b/tests/HSchool.AppHost.Tests/golden/current/1.people.json
@@ -69,7 +69,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -190,7 +191,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -319,7 +321,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -463,7 +466,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -571,21 +575,22 @@
"Hauling": 71,
"Strength": 47,
"RussianLanguage": 45,
- "Chemistry": 60,
+ "Chemistry": 56,
"Geography": 44,
"Medicine": 60,
"BelarusianLanguage": 12,
"UkrainianLanguage": 29
},
"traits": [
- "Leader",
- "Neat"
+ "Lazy",
+ "Quiet"
],
"needs": {
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -695,13 +700,14 @@
"BelarusianLanguage": 20
},
"traits": [
- "Curious"
+ "ColdLoving"
],
"needs": {
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -850,7 +856,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -969,7 +976,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -1074,17 +1082,18 @@
"RussianLanguage": 11,
"PhysicalEducation": 65,
"Biology": 54,
- "Literature": 37,
+ "Literature": 41,
"Mathematics": 43
},
"traits": [
- "Bully"
+ "ColdLoving"
],
"needs": {
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -1221,7 +1230,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -1376,7 +1386,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -1487,7 +1498,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -1596,7 +1608,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -1744,7 +1757,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -1895,7 +1909,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -2031,7 +2046,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -2187,7 +2203,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -2330,13 +2347,14 @@
},
"traits": [
"Diligent",
- "Leader"
+ "Kind"
],
"needs": {
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -2450,7 +2468,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -2559,21 +2578,22 @@
"Hauling": 35,
"Strength": 45,
"RussianLanguage": 46,
- "PhysicalEducation": 60,
+ "PhysicalEducation": 68,
"Biology": 41,
"Literature": 53,
- "Mathematics": 33,
+ "Mathematics": 39,
"UkrainianLanguage": 34
},
"traits": [
- "Kind",
- "Lazy"
+ "HeatLoving",
+ "HotTempered"
],
"needs": {
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -2709,7 +2729,7 @@
"Endurance": 58,
"Hauling": 57,
"Strength": 54,
- "RussianLanguage": 41,
+ "RussianLanguage": 35,
"PhysicalEducation": 56,
"History": 56,
"Cooking": 84,
@@ -2718,13 +2738,14 @@
"UkrainianLanguage": 35
},
"traits": [
- "Diligent"
+ "Curious"
],
"needs": {
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -2844,13 +2865,14 @@
"BelarusianLanguage": 26
},
"traits": [
- "Quiet"
+ "Neat"
],
"needs": {
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -2976,7 +2998,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -3118,7 +3141,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -3233,13 +3257,14 @@
},
"traits": [
"AbsentMinded",
- "Leader"
+ "Lazy"
],
"needs": {
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -3362,13 +3387,14 @@
"UkrainianLanguage": 18
},
"traits": [
- "Kind"
+ "HotTempered"
],
"needs": {
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -3509,13 +3535,14 @@
"UkrainianLanguage": 29
},
"traits": [
- "HotTempered"
+ "HeatLoving"
],
"needs": {
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -3631,7 +3658,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -3761,7 +3789,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -3909,13 +3938,14 @@
"UkrainianLanguage": 7
},
"traits": [
- "Leader"
+ "Lazy"
],
"needs": {
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -4022,7 +4052,7 @@
"Endurance": 40,
"Hauling": 47,
"Strength": 59,
- "RussianLanguage": 33,
+ "RussianLanguage": 39,
"Pedagogy": 50,
"Librarianship": 32,
"English": 59,
@@ -4031,13 +4061,14 @@
"UkrainianLanguage": 13
},
"traits": [
- "HotTempered"
+ "Diligent"
],
"needs": {
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -4159,7 +4190,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -4306,13 +4338,14 @@
"BelarusianLanguage": 24
},
"traits": [
- "Kind"
+ "HotTempered"
],
"needs": {
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -4433,7 +4466,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -4547,7 +4581,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -4687,13 +4722,14 @@
"Chemistry": 37
},
"traits": [
- "Leader"
+ "Lazy"
],
"needs": {
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -4827,7 +4863,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -4966,7 +5003,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -5102,20 +5140,21 @@
"Endurance": 63,
"Hauling": 50,
"Strength": 56,
- "RussianLanguage": 56,
- "Informatics": 51,
+ "RussianLanguage": 50,
+ "Informatics": 47,
"English": 36,
"UkrainianLanguage": 27
},
"traits": [
- "Curious",
- "Diligent"
+ "AbsentMinded",
+ "Curious"
],
"needs": {
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -5245,7 +5284,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -5368,7 +5408,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -5505,21 +5546,22 @@
"Endurance": 50,
"Hauling": 50,
"Strength": 45,
- "RussianLanguage": 43,
+ "RussianLanguage": 37,
"PhysicalEducation": 34,
"Biology": 63,
"Literature": 51,
- "Mathematics": 53,
+ "Mathematics": 45,
"UkrainianLanguage": 11
},
"traits": [
- "Diligent"
+ "Curious"
],
"needs": {
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -5664,7 +5706,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -5816,7 +5859,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -5939,7 +5983,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -6054,21 +6099,22 @@
"Hauling": 50,
"Strength": 45,
"RussianLanguage": 31,
- "PhysicalEducation": 35,
+ "PhysicalEducation": 43,
"Biology": 57,
"Literature": 66,
- "Mathematics": 29,
+ "Mathematics": 35,
"UkrainianLanguage": 20
},
"traits": [
- "Lazy",
+ "Kind",
"Quiet"
],
"needs": {
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -6206,14 +6252,15 @@
"UkrainianLanguage": 16
},
"traits": [
- "Kind",
+ "HeatLoving",
"Quiet"
],
"needs": {
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -6327,22 +6374,23 @@
"Endurance": 51,
"Hauling": 43,
"Strength": 34,
- "RussianLanguage": 53,
+ "RussianLanguage": 47,
"PhysicalEducation": 44,
"Biology": 51,
"Literature": 71,
- "Mathematics": 61,
+ "Mathematics": 53,
"BelarusianLanguage": 11
},
"traits": [
- "Diligent",
+ "Curious",
"Neat"
],
"needs": {
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -6479,21 +6527,22 @@
"Endurance": 49,
"Hauling": 28,
"Strength": 45,
- "RussianLanguage": 49,
+ "RussianLanguage": 43,
"PhysicalEducation": 29,
"Biology": 42,
"Literature": 36,
- "Mathematics": 18,
+ "Mathematics": 10,
"UkrainianLanguage": 33
},
"traits": [
- "Diligent"
+ "Curious"
],
"needs": {
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -6650,7 +6699,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -6789,7 +6839,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -6904,14 +6955,15 @@
"UkrainianLanguage": 22
},
"traits": [
- "Kind",
+ "HotTempered",
"Neat"
],
"needs": {
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -7032,7 +7084,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -7180,7 +7233,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -7301,7 +7355,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -7415,23 +7470,24 @@
"Endurance": 45,
"Hauling": 63,
"Strength": 45,
- "RussianLanguage": 26,
+ "RussianLanguage": 20,
"PhysicalEducation": 53,
"Biology": 68,
"Literature": 75,
- "Mathematics": 59,
+ "Mathematics": 51,
"BelarusianLanguage": 30,
"UkrainianLanguage": 31
},
"traits": [
- "Diligent",
- "Neat"
+ "Curious",
+ "Leader"
],
"needs": {
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -7580,7 +7636,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -7736,7 +7793,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -7843,19 +7901,20 @@
"Endurance": 66,
"Hauling": 58,
"Strength": 44,
- "RussianLanguage": 49,
+ "RussianLanguage": 43,
"Librarianship": 33,
"BelarusianLanguage": 25,
"UkrainianLanguage": 20
},
"traits": [
- "Diligent"
+ "Curious"
],
"needs": {
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -7972,7 +8031,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -8121,7 +8181,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -8233,7 +8294,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -8349,7 +8411,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -8486,22 +8549,23 @@
"Endurance": 56,
"Hauling": 52,
"Strength": 45,
- "RussianLanguage": 40,
+ "RussianLanguage": 34,
"PhysicalEducation": 57,
"Biology": 40,
"Literature": 28,
- "Mathematics": 38,
+ "Mathematics": 30,
"UkrainianLanguage": 22
},
"traits": [
- "Diligent",
+ "Curious",
"Kind"
],
"needs": {
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -8651,7 +8715,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -8772,7 +8837,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -8895,7 +8961,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -9042,7 +9109,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -9176,7 +9244,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -9288,7 +9357,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -9408,7 +9478,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -9919,7 +9990,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -10035,17 +10107,18 @@
"Endurance": 40,
"Hauling": 39,
"Strength": 65,
- "RussianLanguage": 51,
+ "RussianLanguage": 45,
"History": 63
},
"traits": [
- "Diligent"
+ "Curious"
],
"needs": {
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -10105,7 +10178,7 @@
}
]
},
- "hourlyWageAsk": 60.34
+ "hourlyWageAsk": 59.83
},
{
"person": {
@@ -10161,20 +10234,21 @@
"Endurance": 40,
"Hauling": 54,
"Strength": 50,
- "RussianLanguage": 45,
+ "RussianLanguage": 39,
"Administration": 54,
"BelarusianLanguage": 16,
"UkrainianLanguage": 8
},
"traits": [
- "Diligent",
+ "HeatLoving",
"HotTempered"
],
"needs": {
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -10234,7 +10308,7 @@
}
]
},
- "hourlyWageAsk": 61.4
+ "hourlyWageAsk": 61
},
{
"person": {
@@ -10290,20 +10364,21 @@
"Endurance": 63,
"Hauling": 50,
"Strength": 56,
- "RussianLanguage": 56,
- "Informatics": 51,
+ "RussianLanguage": 50,
+ "Informatics": 47,
"English": 36,
"UkrainianLanguage": 27
},
"traits": [
- "Curious",
- "Diligent"
+ "AbsentMinded",
+ "Curious"
],
"needs": {
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -10369,7 +10444,7 @@
}
]
},
- "hourlyWageAsk": 60.8
+ "hourlyWageAsk": 60.13
},
{
"person": {
@@ -10438,7 +10513,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -10544,20 +10620,21 @@
"Strength": 45,
"RussianLanguage": 38,
"Informatics": 69,
- "PhysicalEducation": 71,
+ "PhysicalEducation": 79,
"Librarianship": 42,
"Cooking": 47,
"English": 55,
"BelarusianLanguage": 16
},
"traits": [
- "Lazy"
+ "Kind"
],
"needs": {
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -10617,7 +10694,7 @@
}
]
},
- "hourlyWageAsk": 59.65
+ "hourlyWageAsk": 60.05
},
{
"person": {
@@ -10673,7 +10750,7 @@
"Endurance": 58,
"Hauling": 57,
"Strength": 54,
- "RussianLanguage": 41,
+ "RussianLanguage": 35,
"PhysicalEducation": 56,
"History": 56,
"Cooking": 84,
@@ -10682,13 +10759,14 @@
"UkrainianLanguage": 35
},
"traits": [
- "Diligent"
+ "Curious"
],
"needs": {
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -10748,7 +10826,7 @@
}
]
},
- "hourlyWageAsk": 61.9
+ "hourlyWageAsk": 61.6
},
{
"person": {
@@ -10811,13 +10889,14 @@
"BelarusianLanguage": 26
},
"traits": [
- "Quiet"
+ "Neat"
],
"needs": {
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -10877,7 +10956,7 @@
}
]
},
- "hourlyWageAsk": 51.76
+ "hourlyWageAsk": 59.76
},
{
"person": {
@@ -10948,7 +11027,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -11074,7 +11154,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -11197,21 +11278,22 @@
"Hauling": 71,
"Strength": 47,
"RussianLanguage": 45,
- "Chemistry": 60,
+ "Chemistry": 56,
"Geography": 44,
"Medicine": 60,
"BelarusianLanguage": 12,
"UkrainianLanguage": 29
},
"traits": [
- "Leader",
- "Neat"
+ "Lazy",
+ "Quiet"
],
"needs": {
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -11259,7 +11341,7 @@
}
]
},
- "hourlyWageAsk": 68.85
+ "hourlyWageAsk": 50.64
},
{
"person": {
@@ -11323,7 +11405,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -11445,7 +11528,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -11564,7 +11648,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -11686,7 +11771,7 @@
"Endurance": 40,
"Hauling": 47,
"Strength": 59,
- "RussianLanguage": 33,
+ "RussianLanguage": 39,
"Pedagogy": 50,
"Librarianship": 32,
"English": 59,
@@ -11695,13 +11780,14 @@
"UkrainianLanguage": 13
},
"traits": [
- "HotTempered"
+ "Diligent"
],
"needs": {
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -11755,7 +11841,7 @@
}
]
},
- "hourlyWageAsk": 60.1
+ "hourlyWageAsk": 54.4
},
{
"person": {
@@ -11822,7 +11908,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -11945,13 +12032,14 @@
},
"traits": [
"AbsentMinded",
- "Quiet"
+ "Neat"
],
"needs": {
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -12005,7 +12093,7 @@
}
]
},
- "hourlyWageAsk": 47.93
+ "hourlyWageAsk": 55.93
},
{
"person": {
@@ -12071,7 +12159,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -12201,7 +12290,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -12328,7 +12418,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -12426,19 +12517,20 @@
"Endurance": 35,
"Hauling": 61,
"Strength": 51,
- "RussianLanguage": 47,
+ "RussianLanguage": 53,
"Cooking": 74,
"BelarusianLanguage": 22,
"UkrainianLanguage": 20
},
"traits": [
- "HotTempered"
+ "Diligent"
],
"needs": {
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -12504,7 +12596,7 @@
}
]
},
- "hourlyWageAsk": 62.33
+ "hourlyWageAsk": 56.73
},
{
"person": {
@@ -12570,7 +12662,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -12692,7 +12785,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -12790,23 +12884,24 @@
"Endurance": 39,
"Hauling": 29,
"Strength": 44,
- "RussianLanguage": 75,
+ "RussianLanguage": 69,
"Literature": 20,
- "Mathematics": 73,
+ "Mathematics": 65,
"Administration": 27,
"English": 61,
"BelarusianLanguage": 8,
"UkrainianLanguage": 12
},
"traits": [
- "Diligent",
+ "Curious",
"Quiet"
],
"needs": {
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -12866,7 +12961,7 @@
}
]
},
- "hourlyWageAsk": 46.9
+ "hourlyWageAsk": 46.2
},
{
"person": {
@@ -12934,7 +13029,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -13049,14 +13145,15 @@
"English": 48
},
"traits": [
- "HotTempered",
+ "HeatLoving",
"Leader"
],
"needs": {
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -13122,7 +13219,7 @@
}
]
},
- "hourlyWageAsk": 77.43
+ "hourlyWageAsk": 71.43
},
{
"person": {
@@ -13190,7 +13287,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -13316,7 +13414,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -13426,14 +13525,15 @@
"UkrainianLanguage": 26
},
"traits": [
- "Curious",
- "Kind"
+ "ColdLoving",
+ "HotTempered"
],
"needs": {
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -13475,7 +13575,7 @@
}
]
},
- "hourlyWageAsk": 56.67
+ "hourlyWageAsk": 62.67
},
{
"person": {
@@ -13541,7 +13641,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -13671,7 +13772,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
@@ -13798,7 +13900,8 @@
"Hunger": 1,
"Sleep": 1,
"Social": 1,
- "Toilet": 1
+ "Toilet": 1,
+ "Warmth": 1
},
"subjects": [],
"items": [
diff --git a/tests/HSchool.Content.Tests/ClimatePresetTests.cs b/tests/HSchool.Content.Tests/ClimatePresetTests.cs
new file mode 100644
index 0000000..69d3ecf
--- /dev/null
+++ b/tests/HSchool.Content.Tests/ClimatePresetTests.cs
@@ -0,0 +1,72 @@
+namespace HSchool.Content.Tests;
+
+public class ClimatePresetTests
+{
+ private readonly CatalogLoader _loader = new();
+
+ [Fact]
+ public void VanillaPresets_HaveWorkingWeatherNumbers()
+ {
+ var catalog = Vanilla();
+ var temperate = catalog.ClimatePresets["TemperateContinental"];
+ Assert.Equal(12, temperate.MonthlyNorms.Count);
+ Assert.True(temperate.MonthlyNorms[0] < temperate.MonthlyNorms[6]);
+ Assert.InRange(temperate.PrecipitationChance, 0f, 1f);
+ Assert.True(temperate.IndoorOffset > 0);
+
+ var cold = catalog.ClimatePresets["ContinentalCold"];
+ Assert.True(cold.MonthlyNorms[0] < temperate.MonthlyNorms[0]);
+ }
+
+ [Fact]
+ public void HeatAndColdLoving_AreMutuallyIncompatible_AndOffsetComfort()
+ {
+ var catalog = Vanilla();
+ var heat = catalog.Traits["HeatLoving"];
+ var cold = catalog.Traits["ColdLoving"];
+ Assert.Contains("ColdLoving", heat.Incompatible);
+ Assert.Contains("HeatLoving", cold.Incompatible);
+ Assert.Contains("ColdLoving", catalog.TraitIncompatibilities("HeatLoving"));
+ Assert.Contains("HeatLoving", catalog.TraitIncompatibilities("ColdLoving"));
+ Assert.True(heat.ComfortTemperatureOffset > 0);
+ Assert.True(cold.ComfortTemperatureOffset < 0);
+ }
+
+ [Fact]
+ public void WarmthNeed_RestoresOffCampus_AndIsEnvironmental()
+ {
+ var warmth = Vanilla().Needs["Warmth"];
+ Assert.True(warmth.RestoredOffCampus);
+ Assert.True(warmth.Environmental);
+ Assert.True(warmth.DecayPerHour > 0);
+ }
+
+ [Fact]
+ public void Porch_IsOutdoor()
+ {
+ Assert.True(Vanilla().Rooms["EntranceHall"].Outdoor);
+ Assert.False(Vanilla().Rooms["Classroom"].Outdoor);
+ }
+
+ [Fact]
+ public void ConcretePresetWithoutMonthlyNorms_FailsTheCatalog()
+ {
+ var ex = Assert.Throws(() => _loader.Load(
+ [CatalogLoader.CorePackId],
+ [
+ PackDocuments.Def(
+ CatalogLoader.CorePackId,
+ "climates",
+ "empty",
+ """{ "defName": "EmptyClimate" }"""),
+ ]));
+
+ Assert.Contains("monthlyNorms", ex.Message, StringComparison.Ordinal);
+ }
+
+ private DefCatalog Vanilla()
+ {
+ var root = Path.Combine(AppContext.BaseDirectory, "vanilla");
+ return _loader.Load([CatalogLoader.CorePackId], PackDocuments.FromDirectory(CatalogLoader.CorePackId, root));
+ }
+}
diff --git a/tests/HSchool.Content.Tests/CountryDefTests.cs b/tests/HSchool.Content.Tests/CountryDefTests.cs
index 67ba040..c2296d1 100644
--- a/tests/HSchool.Content.Tests/CountryDefTests.cs
+++ b/tests/HSchool.Content.Tests/CountryDefTests.cs
@@ -36,7 +36,7 @@ public class CountryDefTests
CatalogLoader.CorePackId,
"climates",
"temperate",
- """{ "defName": "TemperateContinental" }"""),
+ """{ "defName": "TemperateContinental", "monthlyNorms": [0,0,0,0,0,0,0,0,0,0,0,0] }"""),
PackDocuments.Def(
CatalogLoader.CorePackId,
"countries",
@@ -58,7 +58,7 @@ public class CountryDefTests
CatalogLoader.CorePackId,
"climates",
"temperate",
- """{ "defName": "TemperateContinental" }"""),
+ """{ "defName": "TemperateContinental", "monthlyNorms": [0,0,0,0,0,0,0,0,0,0,0,0] }"""),
PackDocuments.Def(
CatalogLoader.CorePackId,
"countries",
diff --git a/tests/HSchool.Content.Tests/PeopleDefTests.cs b/tests/HSchool.Content.Tests/PeopleDefTests.cs
index d817004..ad76fe2 100644
--- a/tests/HSchool.Content.Tests/PeopleDefTests.cs
+++ b/tests/HSchool.Content.Tests/PeopleDefTests.cs
@@ -11,13 +11,15 @@ public class PeopleDefTests
var catalog = _loader.Load([CatalogLoader.CorePackId], PackDocuments.FromDirectory(CatalogLoader.CorePackId, root));
Assert.True(catalog.Skills.Count >= 10);
- Assert.Equal(10, catalog.Traits.Count);
- Assert.Equal(4, catalog.Needs.Count);
+ Assert.Equal(12, catalog.Traits.Count);
+ Assert.Equal(5, catalog.Needs.Count);
Assert.True(catalog.BodyAttributes.ContainsKey("Height"));
Assert.Equal(BodyAttributeKind.Number, catalog.BodyAttributes["Height"].Kind);
Assert.Equal(BodyAttributeKind.Choice, catalog.BodyAttributes["HairColor"].Kind);
Assert.All(catalog.Needs.Values, need => Assert.True(need.DecayPerHour > 0));
Assert.True(catalog.Needs["Sleep"].RestoredOffCampus);
+ Assert.True(catalog.Needs["Warmth"].RestoredOffCampus);
+ Assert.True(catalog.Needs["Warmth"].Environmental);
Assert.Equal(0.2f, catalog.Needs["Hunger"].DecayPerHour);
Assert.True(catalog.Skills["Communication"].Always);
Assert.True(catalog.Skills["Agility"].Always);
@@ -224,7 +226,7 @@ public class PeopleDefTests
CatalogLoader.CorePackId,
"climates",
"temperate",
- """{ "defName": "TemperateContinental" }"""),
+ """{ "defName": "TemperateContinental", "monthlyNorms": [0,0,0,0,0,0,0,0,0,0,0,0] }"""),
PackDocuments.Def(
CatalogLoader.CorePackId,
"countries",
diff --git a/tests/HSchool.People.Tests/golden/README.md b/tests/HSchool.People.Tests/golden/README.md
index 98d7ada..1747747 100644
--- a/tests/HSchool.People.Tests/golden/README.md
+++ b/tests/HSchool.People.Tests/golden/README.md
@@ -8,6 +8,9 @@ rolls on the shared family stream, so later members' traits/skills shift) and an
column (wardrobe from a separate apparel stream). Names and birth dates of the first child
in a family stay; clothes are new.
+Phase 32 adds `HeatLoving` / `ColdLoving` to the trait pool, which shifts later trait and
+skill rolls on the same stream. Wardrobe items stay on the apparel stream.
+
Update it **in the same commit that changes generation**, and say in the commit message what
changed (a new skill, a related-language roll, a different birth window). A red golden test is
the question "did you mean to?", not an order to revert.
diff --git a/tests/HSchool.People.Tests/golden/roster.txt b/tests/HSchool.People.Tests/golden/roster.txt
index 494e659..ed54516 100644
--- a/tests/HSchool.People.Tests/golden/roster.txt
+++ b/tests/HSchool.People.Tests/golden/roster.txt
@@ -3,8 +3,8 @@ class class-classroom-00 year=1 letter=А room=classroom-00 capacity=16 pupils=f
class class-classroom-01 year=2 letter=А room=classroom-01 capacity=16 pupils=f0.c1,f12.c0,f12.c1,f12.c2,f13.c1,f13.c2,f14.c2,f20.c0,f23.c1,f26.c0,f27.c0,f29.c0,f31.c0,f4.c0,f7.c0,f9.c0
class class-classroom-02 year=3 letter=А room=classroom-02 capacity=16 pupils=f10.c0,f14.c0,f16.c0,f17.c1,f2.c0,f21.c0,f22.c1,f23.c0,f25.c0,f28.c0,f3.c0,f3.c1,f32.c0,f32.c2,f5.c0,f6.c1
class class-classroom-03 year=4 letter=А room=classroom-03 capacity=16 pupils=f0.c0,f1.c0,f11.c0,f14.c1,f15.c0,f17.c0,f17.c2,f20.c1,f22.c0,f24.c0,f28.c1,f30.c0,f30.c1,f8.c1,f9.c1,f9.c2
-person f0.c0 Андреев Сергей Валериевич female=False roles=student class=class-classroom-03 born=2000-10-23 traits=Lazy skills=Agility=33,Biology=57,Communication=47,Endurance=46,Hauling=56,Literature=22,Mathematics=12,PhysicalEducation=65,RussianLanguage=47,Strength=64,UkrainianLanguage=31 items=Underwear:Black:worn,Socks:Gray:worn,Jeans:Black:worn,TShirt:Black:worn,Shoes:Gray:worn,Sweater:Gray:worn,Scarf:Black:worn,Coat:Black:home,PeShirt:Gray:home,PeShorts:Gray:home,Bottle:Green:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
-person f0.c1 Андреев Александр Валериевич female=False roles=student class=class-classroom-01 born=2002-11-09 traits=Kind,Quiet skills=Agility=13,Biology=46,Communication=20,Endurance=52,Hauling=24,Literature=35,Mathematics=49,PhysicalEducation=57,RussianLanguage=47,Strength=45 items=Underwear:Black:worn,Socks:Gray:worn,Jeans:Blue:worn,TShirt:LightBlue:worn,Shoes:Gray:worn,Sweater:Black:worn,Coat:Beige:worn,Scarf:Gray:worn,PeShirt:Gray:home,PeShorts:Black:home,Bottle:White:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
+person f0.c0 Андреев Сергей Валериевич female=False roles=student class=class-classroom-03 born=2000-10-23 traits=Kind skills=Agility=33,Biology=57,Communication=47,Endurance=46,Hauling=56,Literature=22,Mathematics=18,PhysicalEducation=73,RussianLanguage=47,Strength=64,UkrainianLanguage=31 items=Underwear:Black:worn,Socks:Gray:worn,Jeans:Black:worn,TShirt:Black:worn,Shoes:Gray:worn,Sweater:Gray:worn,Scarf:Black:worn,Coat:Black:home,PeShirt:Gray:home,PeShorts:Gray:home,Bottle:Green:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
+person f0.c1 Андреев Александр Валериевич female=False roles=student class=class-classroom-01 born=2002-11-09 traits=HotTempered,Leader skills=Agility=13,Biology=46,Communication=20,Endurance=52,Hauling=24,Literature=35,Mathematics=49,PhysicalEducation=57,RussianLanguage=47,Strength=45 items=Underwear:Black:worn,Socks:Gray:worn,Jeans:Blue:worn,TShirt:LightBlue:worn,Shoes:Gray:worn,Sweater:Black:worn,Coat:Beige:worn,Scarf:Gray:worn,PeShirt:Gray:home,PeShorts:Black:home,Bottle:White:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
person f0.p0 Андреев Валерий Николаевич female=False roles=parent class=- born=1971-06-03 traits=Diligent,Quiet skills=Agility=43,Communication=58,Cooking=82,Endurance=61,Hauling=61,Pedagogy=50,RussianLanguage=89,Strength=33 items=Underwear:Black:worn,Socks:White:worn,Jeans:Black:worn,TShirt:LightBlue:worn,WinterBoots:Beige:worn,Jacket:Gray:worn,Bottle:Green:bag
person f0.p1 Андреева Марина Артемовна female=True roles=parent class=- born=1971-09-30 traits=Quiet skills=Agility=25,BelarusianLanguage=26,Communication=38,Endurance=35,Hauling=46,History=61,Mathematics=75,RussianLanguage=65,Strength=40,UkrainianLanguage=23 items=Underwear:Gray:worn,Socks:White:worn,Pants:Beige:worn,Shirt:Blue:worn,WinterBoots:Gray:worn,Sweater:Beige:worn,Jacket:Blue:home,Bottle:White:bag,Phone:Black:bag
person f1.c0 Александрова Людмила Борисовна female=True roles=student class=class-classroom-03 born=2001-01-26 traits=AbsentMinded skills=Agility=72,BelarusianLanguage=36,Biology=67,Communication=37,Endurance=39,Hauling=56,Literature=77,Mathematics=29,PhysicalEducation=54,RussianLanguage=51,Strength=45,UkrainianLanguage=28 items=Underwear:Black:worn,Socks:White:worn,Dress:Black:worn,Shoes:White:worn,Belt:Beige:worn,Jacket:Black:home,PeShirt:Blue:home,PeShorts:Blue:home,Bottle:Blue:bag,Phone:Black:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
@@ -13,103 +13,103 @@ person f1.c2 Александров Юрий Борисович female=False rol
person f1.p0 Александров Борис Ильич female=False roles=parent class=- born=1968-10-25 traits= skills=Agility=45,Communication=74,Endurance=34,Hauling=65,RussianLanguage=53,Strength=53,UkrainianLanguage=14 items=Underwear:Black:worn,Socks:White:worn,Trousers:Gray:worn,TShirt:Blue:worn,WinterBoots:Gray:worn,Hat:Beige:worn,Scarf:Gray:worn,FurCoat:Beige:home
person f1.p1 Александрова Елизавета Романовна female=True roles=parent class=- born=1972-01-25 traits= skills=Administration=71,Agility=45,BelarusianLanguage=26,Communication=76,Cooking=59,Endurance=35,English=58,Hauling=48,Mathematics=30,RussianLanguage=42,Strength=66 items=Underwear:Black:worn,Socks:Black:worn,Skirt:Beige:worn,Shirt:Blue:worn,WinterBoots:Beige:worn,Sweater:Beige:worn,Coat:Beige:home,Bottle:White:bag,Phone:White:bag
person f10.c0 Захаров Пётр Виталиевич female=False roles=student class=class-classroom-02 born=2001-11-08 traits=Diligent skills=Agility=31,BelarusianLanguage=26,Biology=62,Communication=54,Endurance=45,Hauling=56,Literature=25,Mathematics=81,PhysicalEducation=63,RussianLanguage=46,Strength=60 items=Underwear:Black:worn,Socks:Black:worn,Pants:Gray:worn,TShirt:Black:worn,WinterBoots:Beige:worn,Jacket:Gray:home,PeShirt:Red:home,PeShorts:Red:home,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
-person f10.p0 Захаров Виталий Борисович female=False roles=parent class=- born=1976-09-07 traits=HotTempered,Leader skills=Agility=44,BelarusianLanguage=22,Communication=66,Endurance=82,English=16,Hauling=40,RussianLanguage=69,Strength=55 items=Underwear:Gray:worn,Socks:Black:worn,Trousers:Black:worn,TShirt:White:worn,WinterBoots:Black:worn,FurCoat:Gray:worn,Hat:Blue:worn,Scarf:Blue:worn,Bottle:White:bag,Phone:Black:bag
-person f10.p1 Захарова Любовь Валериевна female=True roles=parent class=- born=1978-11-03 traits=AbsentMinded,Kind skills=Administration=43,Agility=45,BelarusianLanguage=21,Communication=61,Endurance=53,English=52,Hauling=52,Medicine=23,PhysicalEducation=42,Physics=59,RussianLanguage=66,Strength=28 items=Underwear:Gray:worn,Socks:Gray:worn,Trousers:Blue:worn,Shirt:White:worn,Shoes:Gray:worn,Sweater:Blue:worn,FurCoat:Gray:home,Bottle:White:bag,Phone:Gray:bag
-person f11.c0 Андреева Дарья Ивановна female=True roles=student class=class-classroom-03 born=2001-05-20 traits=Leader skills=Agility=47,BelarusianLanguage=13,Biology=57,Communication=43,Endurance=44,Hauling=50,Literature=59,Mathematics=24,PhysicalEducation=61,RussianLanguage=47,Strength=45,UkrainianLanguage=24 items=Underwear:Gray:worn,Socks:White:worn,Dress:Beige:worn,Shoes:White:worn,Hat:Blue:worn,Coat:Gray:home,PeShirt:Black:home,PeShorts:Black:home,Bottle:Green:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
+person f10.p0 Захаров Виталий Борисович female=False roles=parent class=- born=1976-09-07 traits=HeatLoving,Neat skills=Agility=44,BelarusianLanguage=22,Communication=66,Endurance=82,English=16,Hauling=40,RussianLanguage=69,Strength=55 items=Underwear:Gray:worn,Socks:Black:worn,Trousers:Black:worn,TShirt:White:worn,WinterBoots:Black:worn,FurCoat:Gray:worn,Hat:Blue:worn,Scarf:Blue:worn,Bottle:White:bag,Phone:Black:bag
+person f10.p1 Захарова Любовь Валериевна female=True roles=parent class=- born=1978-11-03 traits=AbsentMinded,HotTempered skills=Administration=43,Agility=45,BelarusianLanguage=21,Communication=61,Endurance=53,English=52,Hauling=52,Medicine=23,PhysicalEducation=42,Physics=59,RussianLanguage=66,Strength=28 items=Underwear:Gray:worn,Socks:Gray:worn,Trousers:Blue:worn,Shirt:White:worn,Shoes:Gray:worn,Sweater:Blue:worn,FurCoat:Gray:home,Bottle:White:bag,Phone:Gray:bag
+person f11.c0 Андреева Дарья Ивановна female=True roles=student class=class-classroom-03 born=2001-05-20 traits=Lazy skills=Agility=47,BelarusianLanguage=13,Biology=57,Communication=43,Endurance=44,Hauling=50,Literature=59,Mathematics=18,PhysicalEducation=53,RussianLanguage=47,Strength=45,UkrainianLanguage=24 items=Underwear:Gray:worn,Socks:White:worn,Dress:Beige:worn,Shoes:White:worn,Hat:Blue:worn,Coat:Gray:home,PeShirt:Black:home,PeShorts:Black:home,Bottle:Green:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
person f11.p0 Андреев Иван Алексеевич female=False roles=parent class=- born=1976-06-01 traits= skills=Agility=38,BelarusianLanguage=16,Communication=52,Endurance=82,Hauling=33,Medicine=48,Pedagogy=61,PhysicalEducation=54,RussianLanguage=82,Strength=67,UkrainianLanguage=22 items=Underwear:White:worn,Socks:White:worn,Jeans:Black:worn,Shirt:Gray:worn,WinterBoots:Gray:worn,Sweater:Beige:worn,FurCoat:Beige:worn,Phone:Gray:bag
-person f11.p1 Андреева София Александровна female=True roles=parent class=- born=1978-04-24 traits=Diligent,HotTempered skills=Agility=61,Communication=74,Endurance=55,English=61,Hauling=31,History=29,RussianLanguage=54,Strength=43 items=Underwear:Gray:worn,Socks:Black:worn,Trousers:Blue:worn,Shirt:Black:worn,WinterBoots:Black:worn,Coat:Gray:worn,Bottle:White:bag
-person f12.c0 Кузьмин Олег Владиславович female=False roles=student class=class-classroom-01 born=2003-03-20 traits=Bully,Lazy skills=Agility=49,Biology=58,Communication=48,Endurance=57,Hauling=54,Literature=50,Mathematics=33,PhysicalEducation=39,RussianLanguage=9,Strength=44,UkrainianLanguage=31 items=Underwear:White:worn,Socks:Gray:worn,Pants:Blue:worn,TShirt:LightBlue:worn,WinterBoots:Black:worn,Hat:Black:worn,Belt:Black:worn,FurCoat:Gray:home,PeShirt:Red:home,PeShorts:Red:home,Bottle:Green:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
+person f11.p1 Андреева София Александровна female=True roles=parent class=- born=1978-04-24 traits=Curious,Diligent skills=Agility=61,Communication=74,Endurance=55,English=61,Hauling=31,History=29,RussianLanguage=54,Strength=43 items=Underwear:Gray:worn,Socks:Black:worn,Trousers:Blue:worn,Shirt:Black:worn,WinterBoots:Black:worn,Coat:Gray:worn,Bottle:White:bag
+person f12.c0 Кузьмин Олег Владиславович female=False roles=student class=class-classroom-01 born=2003-03-20 traits=ColdLoving,Lazy skills=Agility=49,Biology=58,Communication=48,Endurance=57,Hauling=54,Literature=54,Mathematics=33,PhysicalEducation=39,RussianLanguage=9,Strength=36,UkrainianLanguage=31 items=Underwear:White:worn,Socks:Gray:worn,Pants:Blue:worn,TShirt:LightBlue:worn,WinterBoots:Black:worn,Hat:Black:worn,Belt:Black:worn,FurCoat:Gray:home,PeShirt:Red:home,PeShorts:Red:home,Bottle:Green:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
person f12.c1 Кузьмин Егор Владиславович female=False roles=student class=class-classroom-01 born=2003-05-31 traits=Lazy,Leader skills=Agility=45,Biology=45,Communication=30,Endurance=59,Hauling=44,Literature=39,Mathematics=30,PhysicalEducation=59,RussianLanguage=49,Strength=35,UkrainianLanguage=23 items=Underwear:Gray:worn,Socks:Gray:worn,Pants:Black:worn,TShirt:Beige:worn,Shoes:Black:worn,Sweater:Black:worn,Coat:Black:worn,Belt:Beige:worn,PeShirt:Black:home,PeShorts:Black:home,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
person f12.c2 Кузьмина Дарья Владиславовна female=True roles=student class=class-classroom-01 born=2002-10-29 traits=Curious,Leader skills=Agility=42,BelarusianLanguage=20,Biology=56,Communication=39,Endurance=58,Hauling=59,Literature=35,Mathematics=22,PhysicalEducation=81,RussianLanguage=58,Strength=34,UkrainianLanguage=28 items=Underwear:Black:worn,Socks:Gray:worn,Skirt:Blue:worn,Shirt:Black:worn,Shoes:Black:worn,Sweater:Beige:worn,Coat:Beige:worn,PeShirt:Green:home,PeShorts:Black:home,Bottle:White:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
person f12.p0 Кузьмин Владислав Александрович female=False roles=parent class=- born=1975-01-17 traits= skills=Administration=56,Agility=25,BelarusianLanguage=10,Communication=68,Endurance=35,English=36,Hauling=63,Informatics=56,Medicine=59,RussianLanguage=54,Strength=36 items=Underwear:Gray:worn,Socks:Gray:worn,Jeans:Blue:worn,Shirt:Blue:worn,Shoes:Gray:worn,Jacket:Blue:worn,Bottle:Blue:bag
-person f12.p1 Кузьмина Ольга Кирилловна female=True roles=parent class=- born=1979-10-15 traits=AbsentMinded,Kind skills=Administration=38,Agility=39,BelarusianLanguage=25,Chemistry=48,Communication=32,Endurance=41,English=42,Geography=41,Hauling=46,RussianLanguage=32,Strength=34,UkrainianLanguage=40 items=Underwear:Gray:worn,Socks:Gray:worn,Pants:Gray:worn,Shirt:Gray:worn,WinterBoots:Gray:worn,Sweater:Gray:worn,FurCoat:Gray:home
+person f12.p1 Кузьмина Ольга Кирилловна female=True roles=parent class=- born=1979-10-15 traits=AbsentMinded,HotTempered skills=Administration=38,Agility=39,BelarusianLanguage=25,Chemistry=48,Communication=32,Endurance=41,English=42,Geography=41,Hauling=46,RussianLanguage=32,Strength=34,UkrainianLanguage=40 items=Underwear:Gray:worn,Socks:Gray:worn,Pants:Gray:worn,Shirt:Gray:worn,WinterBoots:Gray:worn,Sweater:Gray:worn,FurCoat:Gray:home
person f13.c0 Яковлев Константин Владиславович female=False roles=student class=class-classroom-00 born=2004-06-16 traits= skills=Agility=53,BelarusianLanguage=24,Biology=48,Communication=29,Endurance=37,Hauling=64,Literature=58,Mathematics=22,PhysicalEducation=44,RussianLanguage=31,Strength=37 items=Underwear:Gray:worn,Socks:Black:worn,Trousers:Gray:worn,Shirt:Blue:worn,WinterBoots:Gray:worn,Jacket:Blue:home,PeShirt:Gray:home,PeShorts:Red:home,Bottle:Blue:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
person f13.c1 Яковлев Юрий Владиславович female=False roles=student class=class-classroom-01 born=2003-07-23 traits=Diligent,Quiet skills=Agility=36,BelarusianLanguage=22,Biology=56,Communication=43,Endurance=44,Hauling=57,Literature=56,Mathematics=26,PhysicalEducation=51,RussianLanguage=37,Strength=68,UkrainianLanguage=8 items=Underwear:Black:worn,Socks:Gray:worn,Pants:Blue:worn,Shirt:Black:worn,WinterBoots:Gray:worn,Sweater:Gray:worn,FurCoat:Black:worn,PeShirt:Gray:home,PeShorts:Blue:home,Bottle:Green:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
person f13.c2 Яковлева Алина Владиславовна female=True roles=student class=class-classroom-01 born=2003-02-01 traits=Diligent skills=Agility=58,BelarusianLanguage=20,Biology=60,Communication=45,Endurance=54,Hauling=53,Literature=66,Mathematics=34,PhysicalEducation=47,RussianLanguage=59,Strength=45 items=Underwear:White:worn,Socks:White:worn,Trousers:Black:worn,Shirt:Blue:worn,Shoes:Gray:worn,Hat:Gray:worn,Scarf:Blue:worn,Coat:Beige:home,PeShirt:Red:home,PeShorts:Red:home,Bottle:Green:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
-person f13.p0 Яковлев Владислав Андреевич female=False roles=parent class=- born=1974-09-23 traits=AbsentMinded,Kind skills=Agility=25,BelarusianLanguage=28,Communication=42,Endurance=35,English=26,Hauling=52,RussianLanguage=54,Strength=38 items=Underwear:Black:worn,Socks:White:worn,Trousers:Black:worn,TShirt:White:worn,Shoes:Beige:worn,Hat:Beige:worn,Coat:Beige:home,Bottle:Green:bag,Phone:White:bag
+person f13.p0 Яковлев Владислав Андреевич female=False roles=parent class=- born=1974-09-23 traits=AbsentMinded,HotTempered skills=Agility=25,BelarusianLanguage=28,Communication=42,Endurance=35,English=26,Hauling=52,RussianLanguage=54,Strength=38 items=Underwear:Black:worn,Socks:White:worn,Trousers:Black:worn,TShirt:White:worn,Shoes:Beige:worn,Hat:Beige:worn,Coat:Beige:home,Bottle:Green:bag,Phone:White:bag
person f14.c0 Александров Степан Денисович female=False roles=student class=class-classroom-02 born=2002-01-03 traits= skills=Agility=57,BelarusianLanguage=17,Biology=53,Communication=18,Endurance=60,Hauling=64,Literature=34,Mathematics=33,PhysicalEducation=55,RussianLanguage=32,Strength=41 items=Underwear:Gray:worn,Socks:Gray:worn,Pants:Blue:worn,TShirt:Gray:worn,Shoes:Black:worn,Sweater:Beige:worn,Jacket:Gray:worn,Hat:Blue:worn,Scarf:Blue:worn,PeShirt:Gray:home,PeShorts:Red:home,Bottle:Blue:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
person f14.c1 Александрова Вероника Денисовна female=True roles=student class=class-classroom-03 born=2001-08-11 traits= skills=Agility=45,BelarusianLanguage=15,Biology=52,Communication=38,Endurance=37,Hauling=44,Literature=29,Mathematics=18,PhysicalEducation=65,RussianLanguage=46,Strength=64 items=Underwear:Black:worn,Socks:Gray:worn,Trousers:Gray:worn,TShirt:Black:worn,WinterBoots:Gray:worn,Belt:Gray:worn,FurCoat:Beige:home,PeShirt:Red:home,PeShorts:Gray:home,Bottle:Green:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
person f14.c2 Александров Константин Денисович female=False roles=student class=class-classroom-01 born=2003-08-21 traits= skills=Agility=29,BelarusianLanguage=21,Biology=66,Communication=21,Endurance=51,Hauling=42,Literature=64,Mathematics=27,PhysicalEducation=67,RussianLanguage=20,Strength=45 items=Underwear:White:worn,Socks:Gray:worn,Jeans:Gray:worn,TShirt:Gray:worn,Shoes:Gray:worn,Scarf:Blue:worn,Coat:Black:home,PeShirt:Blue:home,PeShorts:Red:home,Bottle:White:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
person f14.p1 Александрова Виктория Викторовна female=True roles=parent class=- born=1973-07-23 traits= skills=Agility=45,Communication=72,Cooking=68,Endurance=64,Geography=58,Hauling=44,Physics=38,RussianLanguage=61,Strength=49,UkrainianLanguage=18 items=Underwear:Gray:worn,Socks:Gray:worn,Dress:Gray:worn,WinterBoots:Beige:worn,Hat:Black:worn,Coat:Black:home,Phone:Black:bag
person f15.c0 Воробьёв Игорь Сергеевич female=False roles=student class=class-classroom-03 born=2000-09-20 traits= skills=Agility=45,BelarusianLanguage=24,Biology=33,Communication=61,Endurance=58,Hauling=42,Literature=45,Mathematics=32,PhysicalEducation=40,RussianLanguage=55,Strength=38 items=Underwear:Black:worn,Socks:White:worn,Trousers:Blue:worn,TShirt:Black:worn,Shoes:Beige:worn,Sweater:Black:worn,Hat:Gray:worn,FurCoat:Black:home,PeShirt:Green:home,PeShorts:Blue:home,Bottle:White:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
person f15.p0 Воробьёв Сергей Никитич female=False roles=parent class=- born=1974-10-22 traits=Neat,Quiet skills=Agility=45,Communication=54,Endurance=45,Hauling=36,Librarianship=67,Pedagogy=38,RussianLanguage=71,Strength=65,UkrainianLanguage=32 items=Underwear:White:worn,Socks:Black:worn,Pants:Blue:worn,TShirt:Gray:worn,Shoes:Black:worn,Sweater:Black:worn,FurCoat:Beige:home,Bottle:Blue:bag
-person f15.p1 Воробьёва Вера Виталиевна female=True roles=parent class=- born=1972-09-07 traits=Leader,Neat skills=Agility=42,BelarusianLanguage=40,Communication=30,Endurance=52,Hauling=46,Pedagogy=55,RussianLanguage=46,Strength=45,UkrainianLanguage=25 items=Underwear:Black:worn,Socks:Gray:worn,Pants:Black:worn,TShirt:Black:worn,WinterBoots:Black:worn,Coat:Gray:worn,Hat:Gray:worn,Scarf:Black:worn,Bottle:Blue:bag,Phone:White:bag
+person f15.p1 Воробьёва Вера Виталиевна female=True roles=parent class=- born=1972-09-07 traits=Lazy,Neat skills=Agility=42,BelarusianLanguage=40,Communication=30,Endurance=52,Hauling=46,Pedagogy=55,RussianLanguage=46,Strength=45,UkrainianLanguage=25 items=Underwear:Black:worn,Socks:Gray:worn,Pants:Black:worn,TShirt:Black:worn,WinterBoots:Black:worn,Coat:Gray:worn,Hat:Gray:worn,Scarf:Black:worn,Bottle:Blue:bag,Phone:White:bag
person f16.c0 Фролова Елизавета Игоревна female=True roles=student class=class-classroom-02 born=2002-06-29 traits=Quiet skills=Agility=29,BelarusianLanguage=17,Biology=43,Communication=32,Endurance=19,Hauling=36,Literature=51,Mathematics=51,PhysicalEducation=68,RussianLanguage=24,Strength=45 items=Underwear:Gray:worn,Socks:Black:worn,Jeans:Blue:worn,Shirt:Blue:worn,WinterBoots:Beige:worn,Sweater:Beige:worn,FurCoat:Black:home,PeShirt:Red:home,PeShorts:Gray:home,Bottle:White:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
person f16.p0 Фролов Игорь Виталиевич female=False roles=parent class=- born=1968-07-25 traits= skills=Administration=51,Agility=57,BelarusianLanguage=25,Communication=58,Endurance=63,Hauling=49,Medicine=53,RussianLanguage=53,Strength=46 items=Underwear:White:worn,Socks:White:worn,Pants:Gray:worn,Shirt:Gray:worn,Shoes:Gray:worn,Hat:Blue:worn,Jacket:Beige:home
person f16.p1 Фролова Татьяна Олеговна female=True roles=parent class=- born=1970-06-17 traits=Leader,Neat skills=Agility=45,BelarusianLanguage=23,Communication=59,Endurance=59,Hauling=62,Literature=51,RussianLanguage=72,Strength=44 items=Underwear:White:worn,Socks:Black:worn,Skirt:Blue:worn,Shirt:Blue:worn,Shoes:Beige:worn,Sweater:Blue:worn,Coat:Beige:home,Bottle:Blue:bag,Phone:Black:bag
-person f17.c0 Иванов Андрей Александрович female=False roles=student class=class-classroom-03 born=2001-06-27 traits=Leader skills=Agility=60,Biology=53,Communication=54,Endurance=35,Hauling=41,Literature=65,Mathematics=38,PhysicalEducation=55,RussianLanguage=35,Strength=45,UkrainianLanguage=13 items=Underwear:White:worn,Socks:Gray:worn,Jeans:Gray:worn,Shirt:White:worn,WinterBoots:Gray:worn,Coat:Black:home,PeShirt:Green:home,PeShorts:Blue:home,Bottle:Blue:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
-person f17.c1 Иванова Ксения Александровна female=True roles=student class=class-classroom-02 born=2001-09-10 traits=Bully skills=Agility=45,Biology=64,Communication=28,Endurance=64,Hauling=48,Literature=47,Mathematics=37,PhysicalEducation=28,RussianLanguage=43,Strength=45 items=Underwear:White:worn,Socks:Black:worn,Pants:Black:worn,Shirt:Gray:worn,WinterBoots:Gray:worn,FurCoat:Beige:worn,Hat:Black:worn,PeShirt:Blue:home,PeShorts:Green:home,Bottle:White:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
-person f17.c2 Иванова Полина Александровна female=True roles=student class=class-classroom-03 born=2001-05-21 traits=HotTempered,Kind skills=Agility=36,Biology=43,Communication=45,Endurance=45,Hauling=47,Literature=43,Mathematics=33,PhysicalEducation=58,RussianLanguage=36,Strength=45,UkrainianLanguage=15 items=Underwear:Gray:worn,Socks:Black:worn,Pants:Black:worn,TShirt:Beige:worn,WinterBoots:Gray:worn,Sweater:Gray:worn,Coat:Beige:worn,Belt:Beige:worn,PeShirt:Red:home,PeShorts:Gray:home,Bottle:Blue:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
-person f17.p0 Иванов Александр Владимирович female=False roles=parent class=- born=1967-12-24 traits=Diligent,HotTempered skills=Agility=47,Communication=74,Endurance=52,Hauling=55,Physics=39,RussianLanguage=27,Strength=49 items=Underwear:Black:worn,Socks:Gray:worn,Jeans:Blue:worn,TShirt:Blue:worn,WinterBoots:Black:worn,Belt:Beige:worn,FurCoat:Black:home
+person f17.c0 Иванов Андрей Александрович female=False roles=student class=class-classroom-03 born=2001-06-27 traits=Lazy skills=Agility=60,Biology=53,Communication=54,Endurance=35,Hauling=41,Literature=65,Mathematics=32,PhysicalEducation=47,RussianLanguage=35,Strength=45,UkrainianLanguage=13 items=Underwear:White:worn,Socks:Gray:worn,Jeans:Gray:worn,Shirt:White:worn,WinterBoots:Gray:worn,Coat:Black:home,PeShirt:Green:home,PeShorts:Blue:home,Bottle:Blue:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
+person f17.c1 Иванова Ксения Александровна female=True roles=student class=class-classroom-02 born=2001-09-10 traits=ColdLoving skills=Agility=45,Biology=64,Communication=28,Endurance=64,Hauling=48,Literature=51,Mathematics=37,PhysicalEducation=28,RussianLanguage=43,Strength=45 items=Underwear:White:worn,Socks:Black:worn,Pants:Black:worn,Shirt:Gray:worn,WinterBoots:Gray:worn,FurCoat:Beige:worn,Hat:Black:worn,PeShirt:Blue:home,PeShorts:Green:home,Bottle:White:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
+person f17.c2 Иванова Полина Александровна female=True roles=student class=class-classroom-03 born=2001-05-21 traits=Diligent,HotTempered skills=Agility=36,Biology=43,Communication=45,Endurance=45,Hauling=47,Literature=43,Mathematics=41,PhysicalEducation=58,RussianLanguage=42,Strength=45,UkrainianLanguage=15 items=Underwear:Gray:worn,Socks:Black:worn,Pants:Black:worn,TShirt:Beige:worn,WinterBoots:Gray:worn,Sweater:Gray:worn,Coat:Beige:worn,Belt:Beige:worn,PeShirt:Red:home,PeShorts:Gray:home,Bottle:Blue:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
+person f17.p0 Иванов Александр Владимирович female=False roles=parent class=- born=1967-12-24 traits=Diligent,HeatLoving skills=Agility=47,Communication=74,Endurance=52,Hauling=55,Physics=39,RussianLanguage=27,Strength=49 items=Underwear:Black:worn,Socks:Gray:worn,Jeans:Blue:worn,TShirt:Blue:worn,WinterBoots:Black:worn,Belt:Beige:worn,FurCoat:Black:home
person f17.p1 Иванова Александра Ивановна female=True roles=parent class=- born=1967-04-25 traits= skills=Agility=40,Communication=69,Endurance=63,Hauling=35,RussianLanguage=56,Strength=68,UkrainianLanguage=19 items=Underwear:White:worn,Socks:White:worn,Pants:Beige:worn,Shirt:Blue:worn,Shoes:Beige:worn,FurCoat:Gray:home,Phone:White:bag
-person f18.c0 Лебедев Александр Валериевич female=False roles=student class=class-classroom-00 born=2004-02-16 traits=Curious skills=Agility=26,Biology=50,Communication=9,Endurance=53,Hauling=33,Literature=55,Mathematics=20,PhysicalEducation=56,RussianLanguage=23,Strength=35 items=Underwear:Black:worn,Socks:Black:worn,Pants:Black:worn,Shirt:White:worn,WinterBoots:Beige:worn,Sweater:Beige:worn,Jacket:Blue:worn,Belt:Gray:worn,PeShirt:Blue:home,PeShorts:Green:home,Bottle:Blue:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
-person f18.p0 Лебедев Валерий Романович female=False roles=parent class=- born=1985-03-29 traits=Neat,Quiet skills=Agility=25,Chemistry=56,Communication=59,Endurance=35,English=49,Geography=50,Hauling=35,Pedagogy=51,RussianLanguage=67,Strength=62 items=Underwear:Gray:worn,Socks:Black:worn,Pants:Black:worn,Shirt:Gray:worn,Shoes:Beige:worn,FurCoat:Gray:worn,Belt:Beige:worn,Bottle:Green:bag
+person f18.c0 Лебедев Александр Валериевич female=False roles=student class=class-classroom-00 born=2004-02-16 traits=ColdLoving skills=Agility=26,Biology=50,Communication=9,Endurance=53,Hauling=33,Literature=55,Mathematics=20,PhysicalEducation=56,RussianLanguage=23,Strength=35 items=Underwear:Black:worn,Socks:Black:worn,Pants:Black:worn,Shirt:White:worn,WinterBoots:Beige:worn,Sweater:Beige:worn,Jacket:Blue:worn,Belt:Gray:worn,PeShirt:Blue:home,PeShorts:Green:home,Bottle:Blue:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
+person f18.p0 Лебедев Валерий Романович female=False roles=parent class=- born=1985-03-29 traits=Leader,Neat skills=Agility=25,Chemistry=56,Communication=59,Endurance=35,English=49,Geography=50,Hauling=35,Pedagogy=51,RussianLanguage=67,Strength=62 items=Underwear:Gray:worn,Socks:Black:worn,Pants:Black:worn,Shirt:Gray:worn,Shoes:Beige:worn,FurCoat:Gray:worn,Belt:Beige:worn,Bottle:Green:bag
person f18.p1 Лебедева Юлия Михаиловна female=True roles=parent class=- born=1981-01-26 traits= skills=Administration=54,Agility=59,BelarusianLanguage=36,Biology=68,Communication=52,Endurance=44,Hauling=67,Mathematics=47,Medicine=37,RussianLanguage=48,Strength=45,UkrainianLanguage=22 items=Underwear:Gray:worn,Socks:White:worn,Dress:Black:worn,Shoes:Gray:worn,Sweater:Gray:worn,Hat:Black:worn,Belt:Black:worn,Jacket:Gray:home,Phone:Black:bag
person f19.c0 Воробьёв Григорий Михаилович female=False roles=student class=class-classroom-00 born=2004-08-29 traits=Quiet skills=Agility=62,BelarusianLanguage=31,Biology=42,Communication=36,Endurance=50,Hauling=39,Literature=34,Mathematics=37,PhysicalEducation=50,RussianLanguage=33,Strength=45,UkrainianLanguage=23 items=Underwear:Black:worn,Socks:White:worn,Pants:Blue:worn,Shirt:Blue:worn,Shoes:Gray:worn,Sweater:Black:worn,Coat:Gray:worn,PeShirt:Blue:home,PeShorts:Black:home,Bottle:White:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
person f19.p0 Воробьёв Михаил Егорович female=False roles=parent class=- born=1978-11-03 traits=Lazy skills=Administration=53,Agility=40,Communication=56,Endurance=46,Hauling=41,Librarianship=66,Mathematics=36,RussianLanguage=45,Strength=55 items=Underwear:Black:worn,Socks:Black:worn,Pants:Black:worn,Shirt:Black:worn,Shoes:Beige:worn,Hat:Black:worn,Jacket:Green:home,Bottle:Blue:bag
-person f19.p1 Воробьёва Вера Алексеевна female=True roles=parent class=- born=1982-08-29 traits=AbsentMinded,Lazy skills=Administration=50,Agility=58,BelarusianLanguage=20,Communication=46,Endurance=30,Hauling=50,PhysicalEducation=66,RussianLanguage=49,Strength=40,UkrainianLanguage=25 items=Underwear:Gray:worn,Socks:Gray:worn,Dress:Black:worn,WinterBoots:Black:worn,Jacket:Red:home
+person f19.p1 Воробьёва Вера Алексеевна female=True roles=parent class=- born=1982-08-29 traits=AbsentMinded,Kind skills=Administration=50,Agility=58,BelarusianLanguage=20,Communication=46,Endurance=30,Hauling=50,PhysicalEducation=74,RussianLanguage=49,Strength=40,UkrainianLanguage=25 items=Underwear:Gray:worn,Socks:Gray:worn,Dress:Black:worn,WinterBoots:Black:worn,Jacket:Red:home
person f2.c0 Алексеев Григорий Константинович female=False roles=student class=class-classroom-02 born=2002-07-24 traits=Neat skills=Agility=40,BelarusianLanguage=36,Biology=64,Communication=34,Endurance=57,Hauling=38,Literature=58,Mathematics=41,PhysicalEducation=47,RussianLanguage=28,Strength=45,UkrainianLanguage=23 items=Underwear:White:worn,Socks:Gray:worn,Jeans:Blue:worn,TShirt:Black:worn,Shoes:Black:worn,FurCoat:Gray:home,PeShirt:Red:home,PeShorts:Red:home,Bottle:Blue:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
person f2.p0 Алексеев Константин Романович female=False roles=parent class=- born=1974-12-11 traits=Neat skills=Agility=36,BelarusianLanguage=23,Communication=60,Endurance=49,English=34,Hauling=47,Mathematics=63,Medicine=81,Pedagogy=74,RussianLanguage=56,Strength=59,UkrainianLanguage=22 items=Underwear:Gray:worn,Socks:White:worn,Pants:Beige:worn,Shirt:Black:worn,WinterBoots:Beige:worn,Sweater:Black:worn,Hat:Black:worn,FurCoat:Gray:home
person f2.p1 Алексеева Ольга Романовна female=True roles=parent class=- born=1970-07-22 traits=Neat skills=Agility=50,Communication=58,Cooking=51,Endurance=43,English=55,Hauling=54,Literature=35,Medicine=76,PhysicalEducation=32,RussianLanguage=38,Strength=42 items=Underwear:Black:worn,Socks:White:worn,Skirt:Gray:worn,TShirt:Blue:worn,Shoes:Beige:worn,Sweater:Gray:worn,Coat:Beige:home,Bottle:White:bag
person f20.c0 Павлова Елена Игоревна female=True roles=student class=class-classroom-01 born=2002-11-09 traits= skills=Agility=55,Biology=47,Communication=19,Endurance=51,Hauling=38,Literature=41,Mathematics=34,PhysicalEducation=49,RussianLanguage=44,Strength=42,UkrainianLanguage=20 items=Underwear:White:worn,Socks:Gray:worn,Pants:Black:worn,TShirt:Blue:worn,WinterBoots:Beige:worn,Hat:Beige:worn,Belt:Gray:worn,Coat:Gray:home,PeShirt:Red:home,PeShorts:Gray:home,Bottle:White:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
-person f20.c1 Павлова Валентина Игоревна female=True roles=student class=class-classroom-03 born=2001-01-24 traits=Kind skills=Agility=68,BelarusianLanguage=12,Biology=41,Communication=31,Endurance=58,Hauling=46,Literature=42,Mathematics=19,PhysicalEducation=50,RussianLanguage=45,Strength=42,UkrainianLanguage=9 items=Underwear:White:worn,Socks:Black:worn,Trousers:Gray:worn,TShirt:Blue:worn,Shoes:Black:worn,Sweater:Blue:worn,FurCoat:Beige:home,PeShirt:Green:home,PeShorts:Blue:home,Bottle:Green:bag,Phone:Black:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
-person f20.p0 Павлов Игорь Никитич female=False roles=parent class=- born=1968-08-01 traits=HotTempered,Leader skills=Administration=61,Agility=72,Communication=57,Endurance=43,Geography=61,German=73,Hauling=44,Pedagogy=56,RussianLanguage=66,Strength=42,UkrainianLanguage=28 items=Underwear:Black:worn,Socks:White:worn,Trousers:Black:worn,TShirt:Blue:worn,WinterBoots:Gray:worn,FurCoat:Black:home,Bottle:White:bag
-person f20.p1 Павлова Людмила Дмитриевна female=True roles=parent class=- born=1970-01-17 traits=Curious,Quiet skills=Agility=46,BelarusianLanguage=26,Communication=71,Endurance=39,English=31,Hauling=36,Physics=48,RussianLanguage=53,Strength=56 items=Underwear:White:worn,Socks:Gray:worn,Dress:Beige:worn,WinterBoots:Beige:worn,Hat:Black:worn,Belt:Beige:worn,Coat:Gray:home,Bottle:White:bag
+person f20.c1 Павлова Валентина Игоревна female=True roles=student class=class-classroom-03 born=2001-01-24 traits=HotTempered skills=Agility=68,BelarusianLanguage=12,Biology=41,Communication=31,Endurance=58,Hauling=46,Literature=42,Mathematics=19,PhysicalEducation=50,RussianLanguage=45,Strength=42,UkrainianLanguage=9 items=Underwear:White:worn,Socks:Black:worn,Trousers:Gray:worn,TShirt:Blue:worn,Shoes:Black:worn,Sweater:Blue:worn,FurCoat:Beige:home,PeShirt:Green:home,PeShorts:Blue:home,Bottle:Green:bag,Phone:Black:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
+person f20.p0 Павлов Игорь Никитич female=False roles=parent class=- born=1968-08-01 traits=HeatLoving,Leader skills=Administration=61,Agility=72,Communication=57,Endurance=43,Geography=61,German=73,Hauling=44,Pedagogy=56,RussianLanguage=66,Strength=42,UkrainianLanguage=28 items=Underwear:Black:worn,Socks:White:worn,Trousers:Black:worn,TShirt:Blue:worn,WinterBoots:Gray:worn,FurCoat:Black:home,Bottle:White:bag
+person f20.p1 Павлова Людмила Дмитриевна female=True roles=parent class=- born=1970-01-17 traits=ColdLoving,Quiet skills=Agility=46,BelarusianLanguage=26,Communication=71,Endurance=39,English=31,Hauling=36,Physics=42,RussianLanguage=53,Strength=56 items=Underwear:White:worn,Socks:Gray:worn,Dress:Beige:worn,WinterBoots:Beige:worn,Hat:Black:worn,Belt:Beige:worn,Coat:Gray:home,Bottle:White:bag
person f21.c0 Ильин Андрей Константинович female=False roles=student class=class-classroom-02 born=2002-05-12 traits= skills=Agility=54,BelarusianLanguage=15,Biology=72,Communication=22,Endurance=54,Hauling=58,Literature=58,Mathematics=45,PhysicalEducation=52,RussianLanguage=38,Strength=45,UkrainianLanguage=25 items=Underwear:Black:worn,Socks:Black:worn,Jeans:Black:worn,TShirt:White:worn,WinterBoots:Black:worn,Sweater:Black:worn,Jacket:Black:worn,PeShirt:Gray:home,PeShorts:Blue:home,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
person f21.p0 Ильин Константин Никитич female=False roles=parent class=- born=1971-01-21 traits=Diligent,Quiet skills=Agility=45,Biology=50,Communication=53,Endurance=40,English=46,Hauling=45,History=42,Librarianship=57,RussianLanguage=68,Strength=40,UkrainianLanguage=9 items=Underwear:Gray:worn,Socks:Black:worn,Trousers:Black:worn,Shirt:Black:worn,Shoes:White:worn,Scarf:Black:worn,FurCoat:Beige:home,Bottle:Blue:bag
-person f21.p1 Ильина Яна Борисовна female=True roles=parent class=- born=1975-04-20 traits=Lazy skills=Agility=74,BelarusianLanguage=26,Biology=41,Communication=54,Endurance=55,English=77,Hauling=35,Librarianship=44,Medicine=57,RussianLanguage=35,Strength=50,UkrainianLanguage=23 items=Underwear:Black:worn,Socks:White:worn,Jeans:Gray:worn,Shirt:Gray:worn,WinterBoots:Gray:worn,Coat:Gray:worn,Bottle:Blue:bag
+person f21.p1 Ильина Яна Борисовна female=True roles=parent class=- born=1975-04-20 traits=Kind skills=Agility=74,BelarusianLanguage=26,Biology=41,Communication=54,Endurance=55,English=77,Hauling=35,Librarianship=44,Medicine=57,RussianLanguage=35,Strength=50,UkrainianLanguage=23 items=Underwear:Black:worn,Socks:White:worn,Jeans:Gray:worn,Shirt:Gray:worn,WinterBoots:Gray:worn,Coat:Gray:worn,Bottle:Blue:bag
person f22.c0 Захаров Никита Виталиевич female=False roles=student class=class-classroom-03 born=2000-09-14 traits= skills=Agility=62,BelarusianLanguage=19,Biology=50,Communication=42,Endurance=39,Hauling=47,Literature=68,Mathematics=65,PhysicalEducation=60,RussianLanguage=59,Strength=45,UkrainianLanguage=31 items=Underwear:Gray:worn,Socks:White:worn,Jeans:Gray:worn,TShirt:Beige:worn,Shoes:Beige:worn,FurCoat:Gray:home,PeShirt:Black:home,PeShorts:Blue:home,Bottle:White:bag,Phone:White:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
person f22.c1 Захаров Денис Виталиевич female=False roles=student class=class-classroom-02 born=2001-09-11 traits=Kind,Leader skills=Agility=51,Biology=42,Communication=55,Endurance=51,Hauling=69,Literature=53,Mathematics=60,PhysicalEducation=80,RussianLanguage=50,Strength=53,UkrainianLanguage=16 items=Underwear:White:worn,Socks:White:worn,Trousers:Blue:worn,TShirt:Blue:worn,Shoes:White:worn,Hat:Blue:worn,Coat:Beige:home,PeShirt:Red:home,PeShorts:Red:home,Bottle:Blue:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
person f22.p0 Захаров Виталий Андреевич female=False roles=parent class=- born=1966-04-08 traits= skills=Agility=29,Communication=62,Endurance=82,Hauling=33,RussianLanguage=65,Strength=58,UkrainianLanguage=18 items=Underwear:Black:worn,Socks:Black:worn,Pants:Beige:worn,TShirt:LightBlue:worn,Shoes:Black:worn,Coat:Black:worn,Hat:Gray:worn,Bottle:White:bag,Phone:Black:bag
person f22.p1 Захарова Елизавета Егоровна female=True roles=parent class=- born=1967-09-05 traits= skills=Administration=37,Agility=42,BelarusianLanguage=28,Communication=82,Cooking=65,Endurance=60,English=52,Hauling=37,Mathematics=67,RussianLanguage=54,Strength=51 items=Underwear:Gray:worn,Socks:White:worn,Dress:Gray:worn,WinterBoots:Gray:worn,FurCoat:Gray:home,Bottle:Green:bag
person f23.c0 Орловская Нина Константиновна female=True roles=student class=class-classroom-02 born=2001-11-01 traits=AbsentMinded,Quiet skills=Agility=44,BelarusianLanguage=13,Biology=42,Communication=9,Endurance=51,Hauling=45,Literature=53,Mathematics=34,PhysicalEducation=70,RussianLanguage=46,Strength=38 items=Underwear:White:worn,Socks:White:worn,Trousers:Blue:worn,TShirt:Blue:worn,Shoes:Beige:worn,Sweater:Blue:worn,Coat:Beige:worn,Hat:Gray:worn,PeShirt:Blue:home,PeShorts:Red:home,Bottle:White:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
-person f23.c1 Орловская Полина Константиновна female=True roles=student class=class-classroom-01 born=2003-03-30 traits=Curious,Quiet skills=Agility=39,BelarusianLanguage=20,Biology=70,Communication=19,Endurance=53,Hauling=54,Literature=73,Mathematics=46,PhysicalEducation=55,RussianLanguage=47,Strength=67 items=Underwear:White:worn,Socks:Black:worn,Dress:Blue:worn,Shoes:Black:worn,Sweater:Beige:worn,Jacket:Red:home,PeShirt:Black:home,PeShorts:Gray:home,Bottle:White:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
-person f23.p0 Орловский Константин Михаилович female=False roles=parent class=- born=1965-04-02 traits=Diligent,HotTempered skills=Agility=45,BelarusianLanguage=23,Communication=55,Endurance=50,Hauling=50,History=40,Librarianship=56,Physics=61,RussianLanguage=52,Strength=32,UkrainianLanguage=17 items=Underwear:Gray:worn,Socks:White:worn,Jeans:Gray:worn,Shirt:Blue:worn,WinterBoots:Beige:worn,FurCoat:Black:worn,Hat:Beige:worn
+person f23.c1 Орловская Полина Константиновна female=True roles=student class=class-classroom-01 born=2003-03-30 traits=ColdLoving,Quiet skills=Agility=39,BelarusianLanguage=20,Biology=70,Communication=19,Endurance=53,Hauling=54,Literature=73,Mathematics=46,PhysicalEducation=55,RussianLanguage=47,Strength=67 items=Underwear:White:worn,Socks:Black:worn,Dress:Blue:worn,Shoes:Black:worn,Sweater:Beige:worn,Jacket:Red:home,PeShirt:Black:home,PeShorts:Gray:home,Bottle:White:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
+person f23.p0 Орловский Константин Михаилович female=False roles=parent class=- born=1965-04-02 traits=Curious,Diligent skills=Agility=45,BelarusianLanguage=23,Communication=55,Endurance=50,Hauling=50,History=40,Librarianship=56,Physics=67,RussianLanguage=52,Strength=32,UkrainianLanguage=17 items=Underwear:Gray:worn,Socks:White:worn,Jeans:Gray:worn,Shirt:Blue:worn,WinterBoots:Beige:worn,FurCoat:Black:worn,Hat:Beige:worn
person f23.p1 Орловская Яна Степановна female=True roles=parent class=- born=1965-10-22 traits=Lazy skills=Agility=56,BelarusianLanguage=28,Communication=44,Endurance=40,Hauling=81,Pedagogy=45,RussianLanguage=62,Strength=56,UkrainianLanguage=26 items=Underwear:Gray:worn,Socks:Gray:worn,Pants:Gray:worn,TShirt:Blue:worn,Shoes:White:worn,Hat:Blue:worn,Coat:Black:home
-person f24.c0 Орлов Фёдор Алексеевич female=False roles=student class=class-classroom-03 born=2000-12-05 traits=Neat skills=Agility=40,BelarusianLanguage=8,Biology=54,Communication=6,Endurance=40,Hauling=45,Literature=41,Mathematics=10,PhysicalEducation=46,RussianLanguage=52,Strength=63 items=Underwear:Black:worn,Socks:Black:worn,Jeans:Gray:worn,Shirt:Black:worn,Shoes:Gray:worn,Sweater:Black:worn,Jacket:Blue:worn,PeShirt:Blue:home,PeShorts:Blue:home,Bottle:Green:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
-person f24.c1 Орлова Оксана Алексеевна female=True roles=student class=class-classroom-00 born=2004-03-01 traits=Kind,Lazy skills=Agility=75,Biology=30,Communication=23,Endurance=54,Hauling=35,Literature=62,Mathematics=43,PhysicalEducation=50,RussianLanguage=26,Strength=41,UkrainianLanguage=31 items=Underwear:Black:worn,Socks:White:worn,Pants:Beige:worn,TShirt:White:worn,WinterBoots:Black:worn,Scarf:Beige:worn,Jacket:Red:home,PeShirt:Black:home,PeShorts:Red:home,Bottle:Green:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
+person f24.c0 Орлов Фёдор Алексеевич female=False roles=student class=class-classroom-03 born=2000-12-05 traits=Leader skills=Agility=40,BelarusianLanguage=8,Biology=54,Communication=6,Endurance=40,Hauling=45,Literature=41,Mathematics=10,PhysicalEducation=46,RussianLanguage=52,Strength=63 items=Underwear:Black:worn,Socks:Black:worn,Jeans:Gray:worn,Shirt:Black:worn,Shoes:Gray:worn,Sweater:Black:worn,Jacket:Blue:worn,PeShirt:Blue:home,PeShorts:Blue:home,Bottle:Green:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
+person f24.c1 Орлова Оксана Алексеевна female=True roles=student class=class-classroom-00 born=2004-03-01 traits=HeatLoving,HotTempered skills=Agility=75,Biology=30,Communication=23,Endurance=54,Hauling=35,Literature=62,Mathematics=49,PhysicalEducation=58,RussianLanguage=26,Strength=41,UkrainianLanguage=31 items=Underwear:Black:worn,Socks:White:worn,Pants:Beige:worn,TShirt:White:worn,WinterBoots:Black:worn,Scarf:Beige:worn,Jacket:Red:home,PeShirt:Black:home,PeShorts:Red:home,Bottle:Green:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
person f24.p0 Орлов Алексей Денисович female=False roles=parent class=- born=1977-04-17 traits= skills=Agility=45,BelarusianLanguage=17,Communication=51,Endurance=18,English=61,Hauling=46,Literature=56,Mathematics=60,RussianLanguage=55,Strength=62,UkrainianLanguage=16 items=Underwear:Black:worn,Socks:Black:worn,Trousers:Gray:worn,Shirt:Gray:worn,WinterBoots:Black:worn,Sweater:Black:worn,Hat:Gray:worn,Jacket:Black:home
person f24.p1 Орлова Надежда Владиславовна female=True roles=parent class=- born=1977-12-04 traits=Kind skills=Agility=25,BelarusianLanguage=25,Communication=52,Endurance=22,English=70,Hauling=52,Librarianship=51,Medicine=10,RussianLanguage=50,Strength=52,UkrainianLanguage=19 items=Underwear:Black:worn,Socks:Gray:worn,Trousers:Blue:worn,TShirt:LightBlue:worn,WinterBoots:Gray:worn,Sweater:Black:worn,FurCoat:Black:worn,Belt:Black:worn,Bottle:Blue:bag,Phone:Black:bag
person f25.c0 Петрова Александра Игоревна female=True roles=student class=class-classroom-02 born=2001-12-19 traits= skills=Agility=57,BelarusianLanguage=29,Biology=64,Communication=32,Endurance=40,Hauling=58,Literature=47,Mathematics=40,PhysicalEducation=79,RussianLanguage=64,Strength=65,UkrainianLanguage=7 items=Underwear:White:worn,Socks:White:worn,Trousers:Black:worn,TShirt:Blue:worn,WinterBoots:Black:worn,Sweater:Gray:worn,Coat:Beige:worn,Hat:Beige:worn,Scarf:Beige:worn,PeShirt:Red:home,PeShorts:Blue:home,Bottle:Green:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
person f25.p1 Петрова Полина Юриевна female=True roles=parent class=- born=1965-12-11 traits=Kind,Neat skills=Agility=53,Communication=50,Endurance=59,English=70,Geography=41,Hauling=56,Medicine=42,RussianLanguage=72,Strength=45 items=Underwear:Gray:worn,Socks:White:worn,Pants:Beige:worn,TShirt:LightBlue:worn,WinterBoots:Beige:worn,Coat:Beige:worn,Hat:Black:worn,Bottle:White:bag
person f26.c0 Яковлев Павел Михаилович female=False roles=student class=class-classroom-01 born=2002-12-29 traits=Kind,Neat skills=Agility=45,BelarusianLanguage=30,Biology=59,Communication=8,Endurance=59,Hauling=53,Literature=60,Mathematics=47,PhysicalEducation=43,RussianLanguage=43,Strength=45,UkrainianLanguage=23 items=Underwear:White:worn,Socks:White:worn,Pants:Beige:worn,TShirt:LightBlue:worn,Shoes:Black:worn,Sweater:Blue:worn,Coat:Beige:worn,PeShirt:Gray:home,PeShorts:Red:home,Bottle:Green:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
person f26.c1 Яковлев Вадим Михаилович female=False roles=student class=class-classroom-00 born=2004-06-28 traits= skills=Agility=40,BelarusianLanguage=24,Biology=36,Communication=14,Endurance=66,Hauling=41,Literature=71,Mathematics=26,PhysicalEducation=55,RussianLanguage=25,Strength=40,UkrainianLanguage=18 items=Underwear:Black:worn,Socks:Black:worn,Jeans:Blue:worn,TShirt:Black:worn,Shoes:Black:worn,FurCoat:Black:home,PeShirt:Blue:home,PeShorts:Red:home,Bottle:Blue:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
-person f26.p0 Яковлев Михаил Степанович female=False roles=parent class=- born=1975-12-27 traits=HotTempered,Neat skills=Agility=44,Communication=59,Endurance=66,Hauling=52,Mathematics=44,Medicine=71,PhysicalEducation=53,RussianLanguage=55,Strength=45,UkrainianLanguage=28 items=Underwear:Black:worn,Socks:Black:worn,Jeans:Blue:worn,TShirt:Black:worn,Shoes:Gray:worn,Scarf:Blue:worn,Jacket:Blue:home,Phone:Gray:bag
+person f26.p0 Яковлев Михаил Степанович female=False roles=parent class=- born=1975-12-27 traits=HeatLoving,Neat skills=Agility=44,Communication=59,Endurance=66,Hauling=52,Mathematics=44,Medicine=71,PhysicalEducation=53,RussianLanguage=55,Strength=45,UkrainianLanguage=28 items=Underwear:Black:worn,Socks:Black:worn,Jeans:Blue:worn,TShirt:Black:worn,Shoes:Gray:worn,Scarf:Blue:worn,Jacket:Blue:home,Phone:Gray:bag
person f26.p1 Яковлева Марина Кирилловна female=True roles=parent class=- born=1976-12-10 traits=Diligent,Neat skills=Agility=36,BelarusianLanguage=22,Communication=53,Endurance=66,English=40,Hauling=47,History=43,Medicine=66,PhysicalEducation=45,RussianLanguage=65,Strength=43 items=Underwear:White:worn,Socks:White:worn,Jeans:Black:worn,TShirt:Gray:worn,WinterBoots:Black:worn,FurCoat:Gray:worn,Bottle:Blue:bag,Phone:Gray:bag
person f27.c0 Иванов Виктор Константинович female=False roles=student class=class-classroom-01 born=2002-12-27 traits=Bully skills=Agility=29,BelarusianLanguage=19,Biology=56,Communication=26,Endurance=46,Hauling=65,Literature=45,Mathematics=53,PhysicalEducation=58,RussianLanguage=27,Strength=45,UkrainianLanguage=22 items=Underwear:Black:worn,Socks:Gray:worn,Pants:Gray:worn,TShirt:Gray:worn,Shoes:Beige:worn,FurCoat:Black:worn,PeShirt:Gray:home,PeShorts:Red:home,Bottle:White:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
-person f27.c1 Иванов Владислав Константинович female=False roles=student class=class-classroom-00 born=2004-07-04 traits=Diligent,Neat skills=Agility=38,BelarusianLanguage=23,Biology=27,Communication=31,Endurance=53,Hauling=65,Literature=62,Mathematics=33,PhysicalEducation=64,RussianLanguage=49,Strength=53 items=Underwear:White:worn,Socks:White:worn,Trousers:Black:worn,Shirt:Gray:worn,WinterBoots:Black:worn,Sweater:Blue:worn,Jacket:Gray:worn,Hat:Gray:worn,Scarf:Blue:worn,PeShirt:Blue:home,PeShorts:Green:home,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
+person f27.c1 Иванов Владислав Константинович female=False roles=student class=class-classroom-00 born=2004-07-04 traits=Diligent,Leader skills=Agility=38,BelarusianLanguage=23,Biology=27,Communication=31,Endurance=53,Hauling=65,Literature=62,Mathematics=33,PhysicalEducation=64,RussianLanguage=49,Strength=53 items=Underwear:White:worn,Socks:White:worn,Trousers:Black:worn,Shirt:Gray:worn,WinterBoots:Black:worn,Sweater:Blue:worn,Jacket:Gray:worn,Hat:Gray:worn,Scarf:Blue:worn,PeShirt:Blue:home,PeShorts:Green:home,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
person f27.c2 Иванов Александр Константинович female=False roles=student class=class-classroom-00 born=2004-05-25 traits= skills=Agility=58,Biology=51,Communication=37,Endurance=60,Hauling=37,Literature=65,Mathematics=35,PhysicalEducation=33,RussianLanguage=42,Strength=45,UkrainianLanguage=38 items=Underwear:Black:worn,Socks:Black:worn,Pants:Black:worn,TShirt:White:worn,WinterBoots:Beige:worn,Sweater:Black:worn,Hat:Beige:worn,Scarf:Blue:worn,FurCoat:Gray:home,PeShirt:Blue:home,PeShorts:Black:home,Bottle:White:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
person f27.p0 Иванов Константин Иванович female=False roles=parent class=- born=1974-08-25 traits=Neat skills=Agility=44,BelarusianLanguage=27,Communication=77,Endurance=42,English=53,Hauling=62,Medicine=52,Pedagogy=40,RussianLanguage=32,Strength=28 items=Underwear:Black:worn,Socks:Black:worn,Trousers:Blue:worn,Shirt:Blue:worn,Shoes:Gray:worn,Sweater:Blue:worn,Scarf:Beige:worn,Jacket:Green:home,Bottle:White:bag,Phone:White:bag
person f27.p1 Иванова Марина Константиновна female=True roles=parent class=- born=1969-11-29 traits=Curious skills=Agility=43,BelarusianLanguage=16,Communication=71,Endurance=63,English=50,Hauling=59,RussianLanguage=56,Strength=35,UkrainianLanguage=21 items=Underwear:Black:worn,Socks:Gray:worn,Skirt:Beige:worn,TShirt:Black:worn,WinterBoots:Beige:worn,Sweater:Black:worn,FurCoat:Beige:worn,Bottle:Green:bag,Phone:Black:bag
person f28.c0 Иванов Пётр Петрович female=False roles=student class=class-classroom-02 born=2002-08-02 traits= skills=Agility=43,BelarusianLanguage=34,Biology=36,Communication=42,Endurance=50,Hauling=36,Literature=33,Mathematics=4,PhysicalEducation=78,RussianLanguage=46,Strength=44 items=Underwear:Gray:worn,Socks:White:worn,Pants:Beige:worn,Shirt:Gray:worn,WinterBoots:Black:worn,Sweater:Blue:worn,Jacket:Red:home,PeShirt:Green:home,PeShorts:Green:home,Bottle:White:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
person f28.c1 Иванова Наталья Петровна female=True roles=student class=class-classroom-03 born=2000-12-07 traits=Quiet skills=Agility=58,BelarusianLanguage=30,Biology=46,Communication=52,Endurance=36,Hauling=58,Literature=38,Mathematics=73,PhysicalEducation=77,RussianLanguage=51,Strength=45,UkrainianLanguage=23 items=Underwear:White:worn,Socks:Gray:worn,Pants:Beige:worn,TShirt:Gray:worn,Shoes:Beige:worn,Hat:Black:worn,Coat:Beige:home,PeShirt:Green:home,PeShorts:Gray:home,Phone:Black:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
person f28.c2 Иванов Максим Петрович female=False roles=student class=class-classroom-00 born=2004-04-05 traits=Lazy skills=Agility=67,BelarusianLanguage=23,Biology=32,Communication=26,Endurance=54,Hauling=51,Literature=57,Mathematics=10,PhysicalEducation=57,RussianLanguage=47,Strength=75,UkrainianLanguage=35 items=Underwear:Black:worn,Socks:Gray:worn,Pants:Beige:worn,TShirt:Black:worn,Shoes:Gray:worn,Sweater:Gray:worn,Scarf:Black:worn,Coat:Black:home,PeShirt:Black:home,PeShorts:Red:home,Bottle:White:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
-person f28.p0 Иванов Пётр Олегович female=False roles=parent class=- born=1965-06-23 traits=AbsentMinded,Kind skills=Agility=75,BelarusianLanguage=24,Biology=51,Communication=60,Cooking=51,Endurance=74,English=57,Geography=66,Hauling=48,RussianLanguage=45,Strength=40,UkrainianLanguage=36 items=Underwear:Black:worn,Socks:Gray:worn,Trousers:Black:worn,Shirt:White:worn,Shoes:White:worn,Sweater:Black:worn,Coat:Gray:worn,Hat:Black:worn,Scarf:Black:worn
-person f28.p1 Иванова Полина Алексеевна female=True roles=parent class=- born=1965-11-18 traits=AbsentMinded,Leader skills=Agility=43,Communication=61,Cooking=49,Endurance=43,Hauling=60,RussianLanguage=46,Strength=45 items=Underwear:White:worn,Socks:White:worn,Dress:Beige:worn,WinterBoots:Gray:worn,Hat:Beige:worn,Belt:Black:worn,FurCoat:Gray:home,Bottle:Green:bag,Phone:Black:bag
-person f29.c0 Макарова Ксения Александровна female=True roles=student class=class-classroom-01 born=2002-11-08 traits=AbsentMinded,HotTempered skills=Agility=52,Biology=73,Communication=35,Endurance=53,Hauling=68,Literature=64,Mathematics=28,PhysicalEducation=67,RussianLanguage=18,Strength=45 items=Underwear:Gray:worn,Socks:White:worn,Pants:Blue:worn,TShirt:Gray:worn,WinterBoots:Black:worn,Jacket:Black:worn,Hat:Blue:worn,Belt:Beige:worn,PeShirt:Gray:home,PeShorts:Blue:home,Bottle:Green:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
+person f28.p0 Иванов Пётр Олегович female=False roles=parent class=- born=1965-06-23 traits=AbsentMinded,HotTempered skills=Agility=75,BelarusianLanguage=24,Biology=51,Communication=60,Cooking=51,Endurance=74,English=57,Geography=66,Hauling=48,RussianLanguage=45,Strength=40,UkrainianLanguage=36 items=Underwear:Black:worn,Socks:Gray:worn,Trousers:Black:worn,Shirt:White:worn,Shoes:White:worn,Sweater:Black:worn,Coat:Gray:worn,Hat:Black:worn,Scarf:Black:worn
+person f28.p1 Иванова Полина Алексеевна female=True roles=parent class=- born=1965-11-18 traits=AbsentMinded,Lazy skills=Agility=43,Communication=61,Cooking=49,Endurance=43,Hauling=60,RussianLanguage=46,Strength=45 items=Underwear:White:worn,Socks:White:worn,Dress:Beige:worn,WinterBoots:Gray:worn,Hat:Beige:worn,Belt:Black:worn,FurCoat:Gray:home,Bottle:Green:bag,Phone:Black:bag
+person f29.c0 Макарова Ксения Александровна female=True roles=student class=class-classroom-01 born=2002-11-08 traits=Bully,HeatLoving skills=Agility=52,Biology=73,Communication=35,Endurance=53,Hauling=68,Literature=60,Mathematics=34,PhysicalEducation=67,RussianLanguage=18,Strength=45 items=Underwear:Gray:worn,Socks:White:worn,Pants:Blue:worn,TShirt:Gray:worn,WinterBoots:Black:worn,Jacket:Black:worn,Hat:Blue:worn,Belt:Beige:worn,PeShirt:Gray:home,PeShorts:Blue:home,Bottle:Green:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
person f29.p0 Макаров Александр Николаевич female=False roles=parent class=- born=1981-11-08 traits=AbsentMinded skills=Agility=50,BelarusianLanguage=34,Communication=44,Endurance=55,Hauling=42,Informatics=34,PhysicalEducation=74,RussianLanguage=54,Strength=64,UkrainianLanguage=10 items=Underwear:Black:worn,Socks:White:worn,Jeans:Blue:worn,Shirt:Blue:worn,WinterBoots:Black:worn,Sweater:Gray:worn,Coat:Black:worn,Hat:Gray:worn,Scarf:Black:worn,Bottle:Blue:bag,Phone:Black:bag
-person f29.p1 Макарова Мария Владиславовна female=True roles=parent class=- born=1977-10-31 traits=Curious,Diligent skills=Administration=44,Agility=44,BelarusianLanguage=8,Communication=56,Endurance=62,English=40,Hauling=44,Medicine=15,RussianLanguage=41,Strength=52 items=Underwear:White:worn,Socks:Black:worn,Trousers:Black:worn,Shirt:Blue:worn,Shoes:Beige:worn,Sweater:Black:worn,Hat:Blue:worn,Coat:Black:home,Bottle:White:bag,Phone:White:bag
+person f29.p1 Макарова Мария Владиславовна female=True roles=parent class=- born=1977-10-31 traits=ColdLoving,Curious skills=Administration=44,Agility=44,BelarusianLanguage=8,Communication=56,Endurance=62,English=40,Hauling=44,Medicine=15,RussianLanguage=35,Strength=52 items=Underwear:White:worn,Socks:Black:worn,Trousers:Black:worn,Shirt:Blue:worn,Shoes:Beige:worn,Sweater:Black:worn,Hat:Blue:worn,Coat:Black:home,Bottle:White:bag,Phone:White:bag
person f3.c0 Макарова Вероника Петровна female=True roles=student class=class-classroom-02 born=2002-05-15 traits= skills=Agility=68,BelarusianLanguage=24,Biology=34,Communication=45,Endurance=50,Hauling=48,Literature=37,Mathematics=41,PhysicalEducation=50,RussianLanguage=28,Strength=45,UkrainianLanguage=23 items=Underwear:Gray:worn,Socks:Black:worn,Trousers:Blue:worn,Shirt:Gray:worn,WinterBoots:Gray:worn,Jacket:Black:home,PeShirt:Gray:home,PeShorts:Blue:home,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
-person f3.c1 Макаров Роман Петрович female=False roles=student class=class-classroom-02 born=2002-04-16 traits=Lazy skills=Agility=50,BelarusianLanguage=22,Biology=59,Communication=22,Endurance=46,Hauling=51,Literature=39,Mathematics=17,PhysicalEducation=37,RussianLanguage=31,Strength=45,UkrainianLanguage=23 items=Underwear:Gray:worn,Socks:Black:worn,Jeans:Blue:worn,Shirt:White:worn,WinterBoots:Black:worn,Hat:Beige:worn,Coat:Black:home,PeShirt:Black:home,PeShorts:Black:home,Bottle:White:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
+person f3.c1 Макаров Роман Петрович female=False roles=student class=class-classroom-02 born=2002-04-16 traits=Kind skills=Agility=50,BelarusianLanguage=22,Biology=59,Communication=22,Endurance=46,Hauling=51,Literature=39,Mathematics=23,PhysicalEducation=45,RussianLanguage=31,Strength=45,UkrainianLanguage=23 items=Underwear:Gray:worn,Socks:Black:worn,Jeans:Blue:worn,Shirt:White:worn,WinterBoots:Black:worn,Hat:Beige:worn,Coat:Black:home,PeShirt:Black:home,PeShorts:Black:home,Bottle:White:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
person f3.p0 Макаров Пётр Егорович female=False roles=parent class=- born=1977-02-28 traits=Diligent,Neat skills=Agility=67,Communication=28,Endurance=53,English=57,Hauling=43,History=43,Pedagogy=54,RussianLanguage=43,Strength=40 items=Underwear:Black:worn,Socks:White:worn,Pants:Gray:worn,Shirt:Blue:worn,WinterBoots:Black:worn,Sweater:Beige:worn,Hat:Beige:worn,Coat:Black:home
-person f3.p1 Макарова Алина Антоновна female=True roles=parent class=- born=1978-04-05 traits=Kind,Neat skills=Agility=45,BelarusianLanguage=20,Biology=41,Communication=45,Cooking=68,Endurance=51,English=54,Hauling=52,History=69,Medicine=78,RussianLanguage=63,Strength=44 items=Underwear:Black:worn,Socks:Black:worn,Jeans:Black:worn,TShirt:Gray:worn,WinterBoots:Gray:worn,Sweater:Gray:worn,Hat:Beige:worn,FurCoat:Beige:home,Phone:Black:bag
+person f3.p1 Макарова Алина Антоновна female=True roles=parent class=- born=1978-04-05 traits=HotTempered,Neat skills=Agility=45,BelarusianLanguage=20,Biology=41,Communication=45,Cooking=68,Endurance=51,English=54,Hauling=52,History=69,Medicine=78,RussianLanguage=63,Strength=44 items=Underwear:Black:worn,Socks:Black:worn,Jeans:Black:worn,TShirt:Gray:worn,WinterBoots:Gray:worn,Sweater:Gray:worn,Hat:Beige:worn,FurCoat:Beige:home,Phone:Black:bag
person f30.c0 Иванова Светлана Владимировна female=True roles=student class=class-classroom-03 born=2000-10-08 traits=Leader skills=Agility=43,Biology=53,Communication=26,Endurance=44,Hauling=49,Literature=68,Mathematics=39,PhysicalEducation=73,RussianLanguage=51,Strength=36,UkrainianLanguage=21 items=Underwear:White:worn,Socks:Black:worn,Pants:Blue:worn,Shirt:Gray:worn,Shoes:Gray:worn,Coat:Black:worn,Hat:Blue:worn,Scarf:Gray:worn,PeShirt:Gray:home,PeShorts:Black:home,Bottle:Blue:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
-person f30.c1 Иванова Надежда Владимировна female=True roles=student class=class-classroom-03 born=2000-12-10 traits=AbsentMinded,Lazy skills=Agility=43,BelarusianLanguage=19,Biology=57,Communication=38,Endurance=66,Hauling=46,Literature=70,Mathematics=17,PhysicalEducation=48,RussianLanguage=41,Strength=40,UkrainianLanguage=10 items=Underwear:White:worn,Socks:Black:worn,Pants:Black:worn,Shirt:White:worn,Shoes:Black:worn,Scarf:Beige:worn,Jacket:Blue:home,PeShirt:Blue:home,PeShorts:Red:home,Bottle:White:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
+person f30.c1 Иванова Надежда Владимировна female=True roles=student class=class-classroom-03 born=2000-12-10 traits=AbsentMinded,Kind skills=Agility=43,BelarusianLanguage=19,Biology=57,Communication=38,Endurance=66,Hauling=46,Literature=70,Mathematics=23,PhysicalEducation=56,RussianLanguage=41,Strength=40,UkrainianLanguage=10 items=Underwear:White:worn,Socks:Black:worn,Pants:Black:worn,Shirt:White:worn,Shoes:Black:worn,Scarf:Beige:worn,Jacket:Blue:home,PeShirt:Blue:home,PeShorts:Red:home,Bottle:White:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
person f30.p0 Иванов Владимир Денисович female=False roles=parent class=- born=1974-06-12 traits=Lazy skills=Agility=40,Biology=17,Communication=72,Cooking=61,Endurance=50,Hauling=32,RussianLanguage=58,Strength=40,UkrainianLanguage=26 items=Underwear:Black:worn,Socks:Black:worn,Trousers:Black:worn,Shirt:Gray:worn,WinterBoots:Gray:worn,Coat:Beige:home,Bottle:Blue:bag
person f30.p1 Иванова Мария Андреевна female=True roles=parent class=- born=1973-09-24 traits=Diligent skills=Agility=52,BelarusianLanguage=40,Communication=61,Endurance=62,Hauling=65,Medicine=47,RussianLanguage=43,Strength=39 items=Underwear:White:worn,Socks:Gray:worn,Skirt:Black:worn,Shirt:Blue:worn,Shoes:Gray:worn,Hat:Beige:worn,Jacket:Blue:home,Phone:Gray:bag
person f31.c0 Иванов Николай Вадимович female=False roles=student class=class-classroom-01 born=2003-08-29 traits= skills=Agility=20,BelarusianLanguage=24,Biology=40,Communication=27,Endurance=43,Hauling=46,Literature=36,Mathematics=40,PhysicalEducation=53,RussianLanguage=32,Strength=73,UkrainianLanguage=20 items=Underwear:Gray:worn,Socks:White:worn,Jeans:Black:worn,TShirt:Beige:worn,WinterBoots:Beige:worn,FurCoat:Black:home,PeShirt:Blue:home,PeShorts:Black:home,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
-person f31.p0 Иванов Вадим Андреевич female=False roles=parent class=- born=1985-06-23 traits=Diligent,Quiet skills=Administration=28,Agility=45,BelarusianLanguage=32,Communication=54,Endurance=71,English=55,German=47,Hauling=53,PhysicalEducation=44,RussianLanguage=70,Strength=44,UkrainianLanguage=17 items=Underwear:White:worn,Socks:White:worn,Jeans:Black:worn,Shirt:Black:worn,Shoes:Black:worn,Hat:Blue:worn,Coat:Gray:home,Bottle:Blue:bag,Phone:Gray:bag
-person f31.p1 Иванова Александра Юриевна female=True roles=parent class=- born=1980-08-20 traits=Diligent,HotTempered skills=Agility=70,BelarusianLanguage=29,Communication=50,Endurance=41,English=50,Hauling=51,Librarianship=35,PhysicalEducation=73,Physics=54,RussianLanguage=59,Strength=62,UkrainianLanguage=23 items=Underwear:Black:worn,Socks:Black:worn,Pants:Black:worn,TShirt:Gray:worn,Shoes:Black:worn,Jacket:Black:worn,Hat:Black:worn,Scarf:Blue:worn,Phone:Gray:bag
+person f31.p0 Иванов Вадим Андреевич female=False roles=parent class=- born=1985-06-23 traits=Curious,Quiet skills=Administration=28,Agility=45,BelarusianLanguage=32,Communication=54,Endurance=71,English=55,German=47,Hauling=53,PhysicalEducation=44,RussianLanguage=64,Strength=44,UkrainianLanguage=17 items=Underwear:White:worn,Socks:White:worn,Jeans:Black:worn,Shirt:Black:worn,Shoes:Black:worn,Hat:Blue:worn,Coat:Gray:home,Bottle:Blue:bag,Phone:Gray:bag
+person f31.p1 Иванова Александра Юриевна female=True roles=parent class=- born=1980-08-20 traits=Curious,Diligent skills=Agility=70,BelarusianLanguage=29,Communication=50,Endurance=41,English=50,Hauling=51,Librarianship=35,PhysicalEducation=73,Physics=60,RussianLanguage=59,Strength=62,UkrainianLanguage=23 items=Underwear:Black:worn,Socks:Black:worn,Pants:Black:worn,TShirt:Gray:worn,Shoes:Black:worn,Jacket:Black:worn,Hat:Black:worn,Scarf:Blue:worn,Phone:Gray:bag
person f32.c0 Петров Артём Евгениевич female=False roles=student class=class-classroom-02 born=2002-07-14 traits=Neat skills=Agility=78,Biology=58,Communication=47,Endurance=32,Hauling=19,Literature=31,Mathematics=29,PhysicalEducation=60,RussianLanguage=65,Strength=18,UkrainianLanguage=18 items=Underwear:Black:worn,Socks:Black:worn,Trousers:Blue:worn,Shirt:Gray:worn,Shoes:Beige:worn,Sweater:Black:worn,FurCoat:Black:worn,PeShirt:Gray:home,PeShorts:Gray:home,Bottle:Green:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
-person f32.c1 Петрова Елена Евгениевна female=True roles=student class=class-classroom-00 born=2003-10-27 traits=AbsentMinded,HotTempered skills=Agility=37,Biology=54,Communication=39,Endurance=60,Hauling=53,Literature=58,Mathematics=33,PhysicalEducation=75,RussianLanguage=34,Strength=42,UkrainianLanguage=30 items=Underwear:Gray:worn,Socks:Black:worn,Pants:Blue:worn,TShirt:White:worn,Shoes:Gray:worn,Coat:Black:worn,Hat:Gray:worn,Belt:Gray:worn,PeShirt:Blue:home,PeShorts:Green:home,Bottle:Green:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
-person f32.c2 Петрова Ирина Евгениевна female=True roles=student class=class-classroom-02 born=2001-09-18 traits=Kind,Quiet skills=Agility=41,BelarusianLanguage=32,Biology=80,Communication=32,Endurance=42,Hauling=67,Literature=51,Mathematics=26,PhysicalEducation=33,RussianLanguage=32,Strength=45 items=Underwear:White:worn,Socks:White:worn,Dress:Beige:worn,WinterBoots:Black:worn,Sweater:Black:worn,Jacket:Beige:worn,Belt:Gray:worn,PeShirt:Black:home,PeShorts:Blue:home,Bottle:White:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
+person f32.c1 Петрова Елена Евгениевна female=True roles=student class=class-classroom-00 born=2003-10-27 traits=AbsentMinded,Curious skills=Agility=37,Biology=54,Communication=39,Endurance=60,Hauling=53,Literature=58,Mathematics=33,PhysicalEducation=75,RussianLanguage=34,Strength=42,UkrainianLanguage=30 items=Underwear:Gray:worn,Socks:Black:worn,Pants:Blue:worn,TShirt:White:worn,Shoes:Gray:worn,Coat:Black:worn,Hat:Gray:worn,Belt:Gray:worn,PeShirt:Blue:home,PeShorts:Green:home,Bottle:Green:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
+person f32.c2 Петрова Ирина Евгениевна female=True roles=student class=class-classroom-02 born=2001-09-18 traits=HotTempered,Neat skills=Agility=41,BelarusianLanguage=32,Biology=80,Communication=32,Endurance=42,Hauling=67,Literature=51,Mathematics=26,PhysicalEducation=33,RussianLanguage=32,Strength=45 items=Underwear:White:worn,Socks:White:worn,Dress:Beige:worn,WinterBoots:Black:worn,Sweater:Black:worn,Jacket:Beige:worn,Belt:Gray:worn,PeShirt:Black:home,PeShorts:Blue:home,Bottle:White:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
person f32.p0 Петров Евгений Станиславович female=False roles=parent class=- born=1963-05-02 traits= skills=Agility=45,Communication=66,Endurance=43,English=53,Hauling=42,PhysicalEducation=51,RussianLanguage=62,Strength=52,UkrainianLanguage=26 items=Underwear:Black:worn,Socks:White:worn,Jeans:Black:worn,Shirt:Black:worn,Shoes:White:worn,Sweater:Black:worn,Belt:Beige:worn,Coat:Beige:home,Bottle:Blue:bag
-person f33.c0 Морозова Елизавета Григориевна female=True roles=student class=class-classroom-00 born=2003-11-04 traits=AbsentMinded,Lazy skills=Agility=59,BelarusianLanguage=25,Biology=55,Communication=45,Endurance=48,Hauling=50,Literature=58,Mathematics=0,PhysicalEducation=54,RussianLanguage=28,Strength=38,UkrainianLanguage=26 items=Underwear:Gray:worn,Socks:Black:worn,Skirt:Gray:worn,TShirt:White:worn,Shoes:Beige:worn,Sweater:Blue:worn,Hat:Blue:worn,Scarf:Gray:worn,Coat:Gray:home,PeShirt:Gray:home,PeShorts:Green:home,Bottle:White:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
+person f33.c0 Морозова Елизавета Григориевна female=True roles=student class=class-classroom-00 born=2003-11-04 traits=AbsentMinded,Kind skills=Agility=59,BelarusianLanguage=25,Biology=55,Communication=45,Endurance=48,Hauling=50,Literature=58,Mathematics=3,PhysicalEducation=62,RussianLanguage=28,Strength=38,UkrainianLanguage=26 items=Underwear:Gray:worn,Socks:Black:worn,Skirt:Gray:worn,TShirt:White:worn,Shoes:Beige:worn,Sweater:Blue:worn,Hat:Blue:worn,Scarf:Gray:worn,Coat:Gray:home,PeShirt:Gray:home,PeShorts:Green:home,Bottle:White:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
person f33.p0 Морозов Григорий Михаилович female=False roles=parent class=- born=1968-05-01 traits= skills=Agility=28,BelarusianLanguage=8,Communication=68,Endurance=62,Hauling=51,Pedagogy=72,RussianLanguage=56,Strength=46,UkrainianLanguage=22 items=Underwear:Gray:worn,Socks:White:worn,Trousers:Gray:worn,TShirt:Blue:worn,WinterBoots:Beige:worn,Sweater:Blue:worn,Coat:Gray:home,Phone:Black:bag
person f33.p1 Морозова Марина Владимировна female=True roles=parent class=- born=1972-10-24 traits=Leader skills=Administration=54,Agility=41,BelarusianLanguage=28,Communication=42,Endurance=80,English=30,Geography=60,Hauling=41,PhysicalEducation=76,RussianLanguage=74,Strength=37 items=Underwear:Black:worn,Socks:White:worn,Pants:Beige:worn,Shirt:Gray:worn,WinterBoots:Black:worn,Hat:Beige:worn,Belt:Gray:worn,Jacket:Beige:home
-person f34.c0 Кузнецова Валентина Ивановна female=True roles=student class=class-classroom-00 born=2004-03-25 traits=Curious,Kind skills=Agility=29,Biology=32,Communication=21,Endurance=69,Hauling=54,Literature=58,Mathematics=28,PhysicalEducation=53,RussianLanguage=16,Strength=43,UkrainianLanguage=24 items=Underwear:Black:worn,Socks:Black:worn,Pants:Black:worn,Shirt:White:worn,Shoes:Black:worn,Sweater:Gray:worn,Jacket:Black:worn,PeShirt:Green:home,PeShorts:Red:home,Bottle:Blue:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
+person f34.c0 Кузнецова Валентина Ивановна female=True roles=student class=class-classroom-00 born=2004-03-25 traits=Bully,HotTempered skills=Agility=29,Biology=32,Communication=21,Endurance=69,Hauling=54,Literature=54,Mathematics=28,PhysicalEducation=53,RussianLanguage=16,Strength=45,UkrainianLanguage=24 items=Underwear:Black:worn,Socks:Black:worn,Pants:Black:worn,Shirt:White:worn,Shoes:Black:worn,Sweater:Gray:worn,Jacket:Black:worn,PeShirt:Green:home,PeShorts:Red:home,Bottle:Blue:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
person f34.p0 Кузнецов Иван Сергеевич female=False roles=parent class=- born=1970-02-02 traits= skills=Agility=44,Communication=43,Endurance=58,English=45,German=55,Hauling=31,Literature=54,RussianLanguage=55,Strength=72 items=Underwear:Black:worn,Socks:Black:worn,Pants:Beige:worn,Shirt:Gray:worn,WinterBoots:Gray:worn,Hat:Black:worn,Scarf:Beige:worn,Coat:Black:home
-person f34.p1 Кузнецова Дарья Валериевна female=True roles=parent class=- born=1967-03-25 traits=HotTempered skills=Agility=47,BelarusianLanguage=23,Communication=52,Endurance=52,German=38,Hauling=32,Informatics=21,RussianLanguage=56,Strength=67,UkrainianLanguage=15 items=Underwear:Black:worn,Socks:Gray:worn,Jeans:Blue:worn,Shirt:Black:worn,Shoes:Black:worn,Sweater:Gray:worn,Jacket:Blue:home,Bottle:Blue:bag,Phone:Gray:bag
+person f34.p1 Кузнецова Дарья Валериевна female=True roles=parent class=- born=1967-03-25 traits=Diligent skills=Agility=47,BelarusianLanguage=23,Communication=52,Endurance=52,German=38,Hauling=32,Informatics=21,RussianLanguage=62,Strength=67,UkrainianLanguage=15 items=Underwear:Black:worn,Socks:Gray:worn,Jeans:Blue:worn,Shirt:Black:worn,Shoes:Black:worn,Sweater:Gray:worn,Jacket:Blue:home,Bottle:Blue:bag,Phone:Gray:bag
person f4.c0 Васильев Денис Федорович female=False roles=student class=class-classroom-01 born=2003-02-01 traits=Neat skills=Agility=50,BelarusianLanguage=9,Biology=58,Communication=27,Endurance=35,Hauling=25,Literature=52,Mathematics=53,PhysicalEducation=43,RussianLanguage=68,Strength=45,UkrainianLanguage=27 items=Underwear:Gray:worn,Socks:White:worn,Trousers:Black:worn,TShirt:Gray:worn,Shoes:Gray:worn,Sweater:Gray:worn,Jacket:Red:home,PeShirt:Black:home,PeShorts:Black:home,Bottle:White:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
person f4.p0 Васильев Фёдор Игоревич female=False roles=parent class=- born=1970-05-21 traits=AbsentMinded skills=Agility=66,Communication=49,Endurance=44,English=25,Geography=47,Hauling=45,Medicine=44,RussianLanguage=45,Strength=47,UkrainianLanguage=31 items=Underwear:Black:worn,Socks:Black:worn,Jeans:Gray:worn,TShirt:LightBlue:worn,Shoes:Gray:worn,Coat:Beige:worn,Hat:Black:worn,Belt:Gray:worn,Phone:White:bag
person f4.p1 Васильева Анастасия Федоровна female=True roles=parent class=- born=1975-01-20 traits= skills=Agility=64,BelarusianLanguage=22,Communication=59,Endurance=64,English=43,Hauling=53,Librarianship=49,Medicine=75,RussianLanguage=49,Strength=73,UkrainianLanguage=13 items=Underwear:Gray:worn,Socks:White:worn,Pants:Blue:worn,TShirt:Blue:worn,Shoes:Beige:worn,Sweater:Gray:worn,Hat:Black:worn,Coat:Gray:home,Bottle:White:bag
@@ -117,18 +117,18 @@ person f5.c0 Захарова Екатерина Борисовна female=True
person f5.p0 Захаров Борис Николаевич female=False roles=parent class=- born=1980-04-15 traits= skills=Agility=45,BelarusianLanguage=29,Communication=37,Cooking=51,Endurance=51,Hauling=32,Pedagogy=36,RussianLanguage=66,Strength=51,UkrainianLanguage=15 items=Underwear:Black:worn,Socks:Gray:worn,Jeans:Blue:worn,Shirt:White:worn,Shoes:Gray:worn,Scarf:Beige:worn,Jacket:Gray:home
person f5.p1 Захарова Вероника Григориевна female=True roles=parent class=- born=1979-06-22 traits= skills=Administration=39,Agility=68,Communication=64,Endurance=40,English=31,Hauling=61,Informatics=35,PhysicalEducation=35,RussianLanguage=55,Strength=65 items=Underwear:Black:worn,Socks:White:worn,Skirt:Beige:worn,Shirt:Blue:worn,WinterBoots:Gray:worn,Sweater:Black:worn,Jacket:Gray:worn,Hat:Beige:worn
person f6.c0 Козловская Юлия Олеговна female=True roles=student class=class-classroom-00 born=2004-08-15 traits= skills=Agility=45,Biology=49,Communication=42,Endurance=61,Hauling=67,Literature=49,Mathematics=54,PhysicalEducation=39,RussianLanguage=23,Strength=58 items=Underwear:Black:worn,Socks:White:worn,Jeans:Black:worn,Shirt:Black:worn,WinterBoots:Black:worn,Sweater:Gray:worn,Coat:Gray:worn,Hat:Blue:worn,Scarf:Black:worn,PeShirt:Gray:home,PeShorts:Green:home,Bottle:White:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
-person f6.c1 Козловская Светлана Олеговна female=True roles=student class=class-classroom-02 born=2001-12-31 traits=HotTempered,Leader skills=Agility=53,BelarusianLanguage=37,Biology=59,Communication=36,Endurance=40,Hauling=26,Literature=66,Mathematics=34,PhysicalEducation=51,RussianLanguage=53,Strength=47,UkrainianLanguage=10 items=Underwear:White:worn,Socks:Black:worn,Jeans:Gray:worn,Shirt:Black:worn,WinterBoots:Beige:worn,Coat:Black:worn,Hat:Beige:worn,PeShirt:Blue:home,PeShorts:Green:home,Bottle:Blue:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
-person f6.c2 Козловская Вероника Олеговна female=True roles=student class=class-classroom-00 born=2003-09-29 traits=Diligent skills=Agility=55,Biology=77,Communication=27,Endurance=56,Hauling=46,Literature=56,Mathematics=40,PhysicalEducation=46,RussianLanguage=26,Strength=49,UkrainianLanguage=25 items=Underwear:Gray:worn,Socks:Black:worn,Jeans:Black:worn,TShirt:White:worn,Shoes:White:worn,Coat:Gray:home,PeShirt:Blue:home,PeShorts:Red:home,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
+person f6.c1 Козловская Светлана Олеговна female=True roles=student class=class-classroom-02 born=2001-12-31 traits=HotTempered,Lazy skills=Agility=53,BelarusianLanguage=37,Biology=59,Communication=36,Endurance=40,Hauling=26,Literature=66,Mathematics=28,PhysicalEducation=43,RussianLanguage=53,Strength=47,UkrainianLanguage=10 items=Underwear:White:worn,Socks:Black:worn,Jeans:Gray:worn,Shirt:Black:worn,WinterBoots:Beige:worn,Coat:Black:worn,Hat:Beige:worn,PeShirt:Blue:home,PeShorts:Green:home,Bottle:Blue:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
+person f6.c2 Козловская Вероника Олеговна female=True roles=student class=class-classroom-00 born=2003-09-29 traits=Curious skills=Agility=55,Biology=77,Communication=27,Endurance=56,Hauling=46,Literature=56,Mathematics=32,PhysicalEducation=46,RussianLanguage=20,Strength=49,UkrainianLanguage=25 items=Underwear:Gray:worn,Socks:Black:worn,Jeans:Black:worn,TShirt:White:worn,Shoes:White:worn,Coat:Gray:home,PeShirt:Blue:home,PeShorts:Red:home,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
person f6.p0 Козловский Олег Станиславович female=False roles=parent class=- born=1967-12-18 traits=Kind skills=Agility=42,Communication=82,Endurance=64,Hauling=52,Informatics=53,Literature=33,RussianLanguage=58,Strength=21 items=Underwear:Black:worn,Socks:Black:worn,Pants:Beige:worn,TShirt:White:worn,Shoes:White:worn,Sweater:Gray:worn,Coat:Beige:home,Bottle:Blue:bag
-person f6.p1 Козловская Ирина Кирилловна female=True roles=parent class=- born=1963-12-28 traits=AbsentMinded,Lazy skills=Agility=57,BelarusianLanguage=11,Communication=67,Endurance=42,English=48,Hauling=57,RussianLanguage=79,Strength=45 items=Underwear:Gray:worn,Socks:White:worn,Jeans:Gray:worn,TShirt:Gray:worn,WinterBoots:Beige:worn,Coat:Black:worn,Bottle:White:bag
-person f7.c0 Андреев Виктор Степанович female=False roles=student class=class-classroom-01 born=2003-07-19 traits=Diligent,Kind skills=Agility=50,BelarusianLanguage=27,Biology=78,Communication=24,Endurance=50,Hauling=73,Literature=51,Mathematics=34,PhysicalEducation=26,RussianLanguage=56,Strength=64,UkrainianLanguage=34 items=Underwear:Gray:worn,Socks:Black:worn,Jeans:Black:worn,Shirt:Black:worn,Shoes:Gray:worn,Belt:Beige:worn,Jacket:Red:home,PeShirt:Blue:home,PeShorts:Green:home,Bottle:White:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
+person f6.p1 Козловская Ирина Кирилловна female=True roles=parent class=- born=1963-12-28 traits=AbsentMinded,Kind skills=Agility=57,BelarusianLanguage=11,Communication=67,Endurance=42,English=48,Hauling=57,RussianLanguage=79,Strength=45 items=Underwear:Gray:worn,Socks:White:worn,Jeans:Gray:worn,TShirt:Gray:worn,WinterBoots:Beige:worn,Coat:Black:worn,Bottle:White:bag
+person f7.c0 Андреев Виктор Степанович female=False roles=student class=class-classroom-01 born=2003-07-19 traits=Curious,Kind skills=Agility=50,BelarusianLanguage=27,Biology=78,Communication=24,Endurance=50,Hauling=73,Literature=51,Mathematics=26,PhysicalEducation=26,RussianLanguage=50,Strength=64,UkrainianLanguage=34 items=Underwear:Gray:worn,Socks:Black:worn,Jeans:Black:worn,Shirt:Black:worn,Shoes:Gray:worn,Belt:Beige:worn,Jacket:Red:home,PeShirt:Blue:home,PeShorts:Green:home,Bottle:White:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
person f7.p0 Андреев Степан Андреевич female=False roles=parent class=- born=1973-08-21 traits= skills=Agility=64,Communication=76,Endurance=36,English=60,Hauling=46,History=47,Literature=57,RussianLanguage=64,Strength=62,UkrainianLanguage=27 items=Underwear:Black:worn,Socks:White:worn,Jeans:Gray:worn,Shirt:Black:worn,Shoes:White:worn,Sweater:Black:worn,Hat:Beige:worn,FurCoat:Gray:home
-person f7.p1 Андреева Мария Алексеевна female=True roles=parent class=- born=1977-07-16 traits=HotTempered skills=Agility=35,BelarusianLanguage=18,Communication=56,Cooking=47,Endurance=45,Hauling=56,Medicine=47,RussianLanguage=58,Strength=64 items=Underwear:Gray:worn,Socks:Black:worn,Trousers:Blue:worn,TShirt:Black:worn,Shoes:Gray:worn,Hat:Blue:worn,FurCoat:Beige:home,Bottle:White:bag
+person f7.p1 Андреева Мария Алексеевна female=True roles=parent class=- born=1977-07-16 traits=Diligent skills=Agility=35,BelarusianLanguage=18,Communication=56,Cooking=47,Endurance=45,Hauling=56,Medicine=47,RussianLanguage=64,Strength=64 items=Underwear:Gray:worn,Socks:Black:worn,Trousers:Blue:worn,TShirt:Black:worn,Shoes:Gray:worn,Hat:Blue:worn,FurCoat:Beige:home,Bottle:White:bag
person f8.c0 Фролов Евгений Павелович female=False roles=student class=class-classroom-00 born=2003-12-20 traits=AbsentMinded,Kind skills=Agility=69,BelarusianLanguage=26,Biology=50,Communication=28,Endurance=29,Hauling=45,Literature=52,Mathematics=31,PhysicalEducation=44,RussianLanguage=43,Strength=31,UkrainianLanguage=24 items=Underwear:Gray:worn,Socks:Black:worn,Trousers:Black:worn,Shirt:White:worn,WinterBoots:Beige:worn,Sweater:Gray:worn,FurCoat:Beige:worn,PeShirt:Green:home,PeShorts:Black:home,Bottle:Blue:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
person f8.c1 Фролов Станислав Павелович female=False roles=student class=class-classroom-03 born=2000-10-11 traits=AbsentMinded,Lazy skills=Agility=57,BelarusianLanguage=20,Biology=56,Communication=57,Endurance=34,Hauling=42,Literature=71,Mathematics=23,PhysicalEducation=25,RussianLanguage=39,Strength=45,UkrainianLanguage=24 items=Underwear:Black:worn,Socks:White:worn,Jeans:Black:worn,TShirt:Black:worn,Shoes:Beige:worn,Jacket:Black:worn,Hat:Black:worn,PeShirt:Red:home,PeShorts:Green:home,Bottle:White:bag,Phone:White:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
person f8.p0 Фролов Павел Егорович female=False roles=parent class=- born=1968-06-07 traits=Leader skills=Administration=51,Agility=71,Chemistry=39,Communication=87,Endurance=39,English=42,Hauling=45,Informatics=65,Librarianship=57,RussianLanguage=73,Strength=45,UkrainianLanguage=26 items=Underwear:Gray:worn,Socks:Black:worn,Trousers:Gray:worn,TShirt:White:worn,WinterBoots:Black:worn,Sweater:Gray:worn,Coat:Black:home,Phone:Black:bag
-person f8.p1 Фролова Виктория Степановна female=True roles=parent class=- born=1963-10-04 traits=Curious,Leader skills=Agility=57,Chemistry=51,Communication=77,Endurance=45,English=33,Hauling=52,Librarianship=83,RussianLanguage=58,Strength=55 items=Underwear:White:worn,Socks:White:worn,Skirt:Beige:worn,Shirt:Gray:worn,WinterBoots:Black:worn,Coat:Gray:home,Bottle:Green:bag
-person f9.c0 Кузьмина Любовь Александровна female=True roles=student class=class-classroom-01 born=2003-06-13 traits=Kind skills=Agility=45,Biology=47,Communication=31,Endurance=32,Hauling=40,Literature=63,Mathematics=46,PhysicalEducation=48,RussianLanguage=41,Strength=44,UkrainianLanguage=5 items=Underwear:Black:worn,Socks:Gray:worn,Skirt:Beige:worn,Shirt:Blue:worn,WinterBoots:Gray:worn,Sweater:Blue:worn,FurCoat:Beige:worn,PeShirt:Gray:home,PeShorts:Green:home,Bottle:White:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
+person f8.p1 Фролова Виктория Степановна female=True roles=parent class=- born=1963-10-04 traits=Curious,Lazy skills=Agility=57,Chemistry=51,Communication=77,Endurance=45,English=33,Hauling=52,Librarianship=83,RussianLanguage=58,Strength=55 items=Underwear:White:worn,Socks:White:worn,Skirt:Beige:worn,Shirt:Gray:worn,WinterBoots:Black:worn,Coat:Gray:home,Bottle:Green:bag
+person f9.c0 Кузьмина Любовь Александровна female=True roles=student class=class-classroom-01 born=2003-06-13 traits=HotTempered skills=Agility=45,Biology=47,Communication=31,Endurance=32,Hauling=40,Literature=63,Mathematics=46,PhysicalEducation=48,RussianLanguage=41,Strength=44,UkrainianLanguage=5 items=Underwear:Black:worn,Socks:Gray:worn,Skirt:Beige:worn,Shirt:Blue:worn,WinterBoots:Gray:worn,Sweater:Blue:worn,FurCoat:Beige:worn,PeShirt:Gray:home,PeShorts:Green:home,Bottle:White:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
person f9.c1 Кузьмин Сергей Александрович female=False roles=student class=class-classroom-03 born=2001-08-20 traits=Curious,Quiet skills=Agility=11,Biology=58,Communication=41,Endurance=59,Hauling=40,Literature=39,Mathematics=31,PhysicalEducation=40,RussianLanguage=51,Strength=52 items=Underwear:Gray:worn,Socks:Black:worn,Trousers:Gray:worn,TShirt:Gray:worn,Shoes:Beige:worn,Sweater:Beige:worn,Hat:Beige:worn,Scarf:Beige:worn,Coat:Gray:home,PeShirt:Gray:home,PeShorts:Blue:home,Bottle:White:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
person f9.c2 Кузьмина Алина Александровна female=True roles=student class=class-classroom-03 born=2001-01-22 traits= skills=Agility=52,Biology=38,Communication=29,Endurance=53,Hauling=34,Literature=63,Mathematics=31,PhysicalEducation=55,RussianLanguage=42,Strength=63,UkrainianLanguage=12 items=Underwear:White:worn,Socks:Gray:worn,Trousers:Blue:worn,Shirt:Blue:worn,WinterBoots:Gray:worn,Jacket:Gray:worn,Scarf:Gray:worn,PeShirt:Blue:home,PeShorts:Black:home,Bottle:Blue:bag,SchoolBag:-:bag,Textbook:-:bag:PhysicalEducation,Textbook:-:bag:PrimarySchool
person f9.p0 Кузьмин Александр Денисович female=False roles=parent class=- born=1981-12-09 traits= skills=Agility=40,Chemistry=55,Communication=58,Endurance=78,English=57,Hauling=43,Pedagogy=52,RussianLanguage=67,Strength=63 items=Underwear:White:worn,Socks:White:worn,Jeans:Gray:worn,Shirt:Gray:worn,Shoes:Beige:worn,FurCoat:Beige:worn,Belt:Beige:worn,Bottle:Green:bag
diff --git a/tests/HSchool.Protocol.Tests/ProtocolCodecTests.cs b/tests/HSchool.Protocol.Tests/ProtocolCodecTests.cs
index 4fd326a..0f032de 100644
--- a/tests/HSchool.Protocol.Tests/ProtocolCodecTests.cs
+++ b/tests/HSchool.Protocol.Tests/ProtocolCodecTests.cs
@@ -121,7 +121,7 @@ public class ProtocolCodecTests
}
[Fact]
- public void Clock_RoundTripsAndIsTwentyFourBytes()
+ public void Clock_RoundTripsAndIsTwentySevenBytes()
{
var message = new ServerClockMessage(
7,
@@ -129,12 +129,14 @@ public class ProtocolCodecTests
Running: true,
SpeedIndex: 2,
SkipAllowed: true,
- SkipTargetUnixMs: 1_333_516_800_000);
+ SkipTargetUnixMs: 1_333_516_800_000,
+ TemperatureTenths: -50,
+ Precipitation: 2);
Span buffer = stackalloc byte[ProtocolCodec.MaxFrameSize];
var length = ProtocolCodec.WriteClock(buffer, message);
- Assert.Equal(24, length);
+ Assert.Equal(27, length);
Assert.Equal((byte)MessageType.ServerClock, buffer[0]);
Assert.Equal(7, BitConverter.ToInt32(buffer[1..5]));
Assert.Equal(1_333_432_800_000, BitConverter.ToInt64(buffer[5..13]));
@@ -142,6 +144,8 @@ public class ProtocolCodecTests
Assert.Equal(2, buffer[14]);
Assert.Equal(1, buffer[15]);
Assert.Equal(1_333_516_800_000, BitConverter.ToInt64(buffer[16..24]));
+ Assert.Equal(-50, BitConverter.ToInt16(buffer[24..26]));
+ Assert.Equal(2, buffer[26]);
Assert.Equal(message, ProtocolCodec.ReadClock(buffer[..length]));
}
@@ -154,9 +158,11 @@ public class ProtocolCodecTests
var length = ProtocolCodec.WriteClock(buffer, message);
var read = ProtocolCodec.ReadClock(buffer[..length]);
- Assert.Equal(24, length);
+ Assert.Equal(27, length);
Assert.False(read.SkipAllowed);
Assert.Equal(0, read.SkipTargetUnixMs);
+ Assert.Equal(0, read.TemperatureTenths);
+ Assert.Equal(PrecipitationKind.None, read.Precipitation);
}
[Fact]
diff --git a/tests/HSchool.Simulation.Tests/WarmthTests.cs b/tests/HSchool.Simulation.Tests/WarmthTests.cs
new file mode 100644
index 0000000..1486751
--- /dev/null
+++ b/tests/HSchool.Simulation.Tests/WarmthTests.cs
@@ -0,0 +1,132 @@
+using Arch.Core;
+using HSchool.Ai;
+using HSchool.Content;
+using HSchool.People;
+
+namespace HSchool.Simulation.Tests;
+
+public class WarmthTests
+{
+ private static readonly DateTime JanuaryMorning = new(2012, 1, 15, 6, 0, 0, DateTimeKind.Utc);
+
+ [Fact]
+ public void NakedOnFrost_LosesWarmth_AndHomeRestoresItOvernight()
+ {
+ using var school = Open(JanuaryMorning);
+ PutFirstPerson(school, "yard", PersonInsulation.Naked, warmth: 1f);
+ school.SyncWeather(force: true);
+ Assert.True(school.Weather.TemperatureC < 0f);
+
+ WarmthDecay.Apply(school, gameMinutes: 60);
+ var afterHour = WarmthOf(school);
+ Assert.True(afterHour < 1f, $"warmth stayed {afterHour} on the frost");
+
+ PutFirstPerson(school, nodeId: null, PersonInsulation.Naked, afterHour);
+ NeedDecay.Apply(school.World, school.Catalog!, gameMinutes: 60);
+ Assert.Equal(school.Catalog!.Needs["Warmth"].Max, WarmthOf(school));
+ }
+
+ [Fact]
+ public void JacketOnFrost_LosesLessWarmthThanNaked()
+ {
+ using var school = Open(JanuaryMorning);
+ var catalog = school.Catalog!;
+ school.SyncWeather(force: true);
+
+ PutFirstPerson(school, "yard", PersonInsulation.Naked, warmth: 1f);
+ WarmthDecay.Apply(school, gameMinutes: 60);
+ var naked = WarmthOf(school);
+
+ PutFirstPerson(school, "yard", PersonInsulation.FromWorn(catalog, "Jacket"), warmth: 1f);
+ WarmthDecay.Apply(school, gameMinutes: 60);
+ var coated = WarmthOf(school);
+
+ Assert.True(naked < 1f);
+ Assert.True(coated > naked, $"jacket {coated} should beat naked {naked}");
+ }
+
+ [Fact]
+ public void FromPerson_SumsWornInsulation_AndIgnoresHome()
+ {
+ using var school = Open(JanuaryMorning);
+ var catalog = school.Catalog!;
+ var person = school.Roster!.People.First(row =>
+ row.Items.Any(item => item.Location.Equals(ItemLocations.Worn, StringComparison.Ordinal)));
+ Assert.True(PersonInsulation.FromPerson(person, catalog).Value > 0);
+
+ var atHome = person with
+ {
+ Items = [.. person.Items.Select(item => item with { Location = ItemLocations.Home })],
+ };
+ Assert.Equal(0, PersonInsulation.FromPerson(atHome, catalog).Value);
+ }
+
+ private static void PutFirstPerson(School school, string? nodeId, PersonInsulation insulation, float warmth)
+ {
+ var query = new QueryDescription().WithAll();
+ var first = true;
+ school.World.Query(
+ in query,
+ (ref PersonNeeds needs, ref PersonInsulation worn, ref Presence presence) =>
+ {
+ if (!first)
+ {
+ return;
+ }
+
+ first = false;
+ worn = insulation;
+ needs.Values["Warmth"] = warmth;
+ presence = nodeId is null
+ ? Presence.OffCampus
+ : new Presence(nodeId, 0f, nodeId, false, []);
+ });
+ }
+
+ private static float WarmthOf(School school)
+ {
+ var value = float.NaN;
+ var query = new QueryDescription().WithAll();
+ school.World.Query(in query, (ref PersonNeeds needs) =>
+ {
+ if (float.IsNaN(value) && needs.Values.TryGetValue("Warmth", out var warmth))
+ {
+ value = warmth;
+ }
+ });
+ return value;
+ }
+
+ private static School Open(DateTime start)
+ {
+ var (catalog, map) = Vanilla();
+ var roster = RosterGenerator.Generate(catalog, map, schoolSeed: 1, "Russia", start);
+ var pool = ApplicantPool.Create(catalog, roster, schoolSeed: 1, "Russia", start);
+ var school = School.Create(1, "Тепло", start, catalog, map);
+ school.InstallPeople(roster, seed: 1, "Russia", pool, climatePresetId: "TemperateContinental");
+ school.ConfigurePresence();
+ return school;
+ }
+
+ private static (DefCatalog Catalog, MapLayout Map) Vanilla()
+ {
+ var root = Path.Combine(AppContext.BaseDirectory, "vanilla");
+ var documents = new List();
+ foreach (var path in Directory.EnumerateFiles(root, "*.*", SearchOption.AllDirectories))
+ {
+ if (!path.EndsWith(".jsonc", StringComparison.OrdinalIgnoreCase)
+ && !path.EndsWith(".json", StringComparison.OrdinalIgnoreCase))
+ {
+ continue;
+ }
+
+ var relative = Path.GetRelativePath(root, path).Replace('\\', '/');
+ documents.Add(new ContentDocument(CatalogLoader.CorePackId, relative, File.ReadAllText(path)));
+ }
+
+ var catalog = new CatalogLoader().Load([CatalogLoader.CorePackId], documents);
+ var map = CatalogLoader.LastDefaultMap([CatalogLoader.CorePackId], documents);
+ Assert.NotNull(map);
+ return (catalog, map);
+ }
+}
diff --git a/tests/HSchool.Simulation.Tests/WeatherTests.cs b/tests/HSchool.Simulation.Tests/WeatherTests.cs
new file mode 100644
index 0000000..e2a57f7
--- /dev/null
+++ b/tests/HSchool.Simulation.Tests/WeatherTests.cs
@@ -0,0 +1,134 @@
+using HSchool.Content;
+using HSchool.People;
+
+namespace HSchool.Simulation.Tests;
+
+public class WeatherTests
+{
+ private static readonly DateTime JanuaryMorning = new(2012, 1, 15, 6, 0, 0, DateTimeKind.Utc);
+ private static readonly DateTime JulyAfternoon = new(2012, 7, 15, 15, 0, 0, DateTimeKind.Utc);
+
+ [Fact]
+ public void SamePresetSeedAndTimestamp_YieldTheSameStreet()
+ {
+ var preset = Vanilla().ClimatePresets["TemperateContinental"];
+ var first = WeatherSampler.Sample(preset, schoolSeed: 7, JanuaryMorning);
+ var second = WeatherSampler.Sample(preset, schoolSeed: 7, JanuaryMorning);
+ Assert.Equal(first, second);
+ }
+
+ [Fact]
+ public void January_IsColderThanJuly_OnTheVanillaPreset()
+ {
+ var preset = Vanilla().ClimatePresets["TemperateContinental"];
+ var january = WeatherSampler.Sample(preset, schoolSeed: 3, JanuaryMorning);
+ var july = WeatherSampler.Sample(preset, schoolSeed: 3, JulyAfternoon);
+ Assert.True(january.TemperatureC < july.TemperatureC, $"{january.TemperatureC} vs {july.TemperatureC}");
+ }
+
+ [Fact]
+ public void PrecipitationBelowZero_IsSnowNotRain()
+ {
+ var wet = new ClimatePresetDef
+ {
+ DefName = "AlwaysWet",
+ MonthlyNorms = [-12, -10, -4, 5, 12, 17, 20, 18, 12, 5, -1, -8],
+ DaySpread = 0,
+ HourSpread = 0,
+ PrecipitationChance = 1f,
+ };
+
+ var january = WeatherSampler.Sample(wet, schoolSeed: 1, JanuaryMorning);
+ var july = WeatherSampler.Sample(wet, schoolSeed: 1, JulyAfternoon);
+ Assert.True(january.TemperatureC < 0);
+ Assert.Equal(Precipitation.Snow, january.Precipitation);
+ Assert.True(july.TemperatureC > 0);
+ Assert.Equal(Precipitation.Rain, july.Precipitation);
+ }
+
+ [Fact]
+ public void VanillaJanuary_SnowIsNeverRain()
+ {
+ var preset = Vanilla().ClimatePresets["TemperateContinental"];
+ for (var seed = 1; seed <= 40; seed++)
+ {
+ var weather = WeatherSampler.Sample(preset, seed, JanuaryMorning);
+ if (weather.Precipitation == Precipitation.None)
+ {
+ continue;
+ }
+
+ Assert.True(weather.TemperatureC < 0);
+ Assert.Equal(Precipitation.Snow, weather.Precipitation);
+ }
+ }
+
+ [Fact]
+ public void SkipEmpty_SetsMondayMorningWeather_NotSaturdays()
+ {
+ var saturday = new DateTime(2012, 1, 14, 22, 0, 0, DateTimeKind.Utc);
+ var monday = new DateTime(2012, 1, 16, 6, 0, 0, DateTimeKind.Utc);
+ using var school = Open(saturday);
+ var evening = school.Weather;
+ Assert.True(school.TrySkipEmpty().Succeeded);
+ Assert.Equal(monday, school.Clock.Time);
+
+ var expected = WeatherSampler.Sample(
+ school.Catalog!.ClimatePresets["TemperateContinental"],
+ school.PeopleSeed,
+ monday);
+ Assert.Equal(expected, school.Weather);
+ Assert.NotEqual(evening, school.Weather);
+ }
+
+ [Fact]
+ public void Indoor_IsWarmerThanTheYard_ButNotComfortableInJanuary()
+ {
+ using var school = Open(JanuaryMorning);
+ var outdoor = PlaceClimate.TemperatureC(school, "yard");
+ var porch = PlaceClimate.TemperatureC(school, "porch");
+ var room = PlaceClimate.TemperatureC(school, "classroom-101");
+ Assert.Equal(outdoor, porch);
+ Assert.True(room > outdoor);
+ Assert.True(room < 18f, $"walls only, got {room}");
+ }
+
+ private static School Open(DateTime start)
+ {
+ var (catalog, map) = VanillaMap();
+ var roster = RosterGenerator.Generate(catalog, map, schoolSeed: 1, "Russia", start);
+ var pool = ApplicantPool.Create(catalog, roster, schoolSeed: 1, "Russia", start);
+ var school = School.Create(1, "Погода", start, catalog, map);
+ school.InstallPeople(roster, seed: 1, "Russia", pool, climatePresetId: "TemperateContinental");
+ school.ConfigurePresence();
+ return school;
+ }
+
+ private static DefCatalog Vanilla()
+ {
+ var (catalog, _) = VanillaMap();
+ return catalog;
+ }
+
+ private static (DefCatalog Catalog, MapLayout Map) VanillaMap()
+ {
+ var root = Path.Combine(AppContext.BaseDirectory, "vanilla");
+ var documents = new List();
+ foreach (var path in Directory.EnumerateFiles(root, "*.*", SearchOption.AllDirectories))
+ {
+ if (!path.EndsWith(".jsonc", StringComparison.OrdinalIgnoreCase)
+ && !path.EndsWith(".json", StringComparison.OrdinalIgnoreCase))
+ {
+ continue;
+ }
+
+ var relative = Path.GetRelativePath(root, path).Replace('\\', '/');
+ documents.Add(new ContentDocument(CatalogLoader.CorePackId, relative, File.ReadAllText(path)));
+ }
+
+ var catalog = new CatalogLoader().Load([CatalogLoader.CorePackId], documents);
+ var map = CatalogLoader.LastDefaultMap([CatalogLoader.CorePackId], documents);
+ Assert.NotNull(map);
+ return (catalog, map);
+ }
+}