573 lines
55 KiB
Markdown
573 lines
55 KiB
Markdown
# Животные (зоология)
|
||
|
||
> Статус: **A1–A8 реализованы целиком + сериализация животных** — базовый план зоологии и его сквозное
|
||
> требование сейв/лоада закрыты; дальше экологический горизонт (хищники, опека, эпидемии, миграция) и
|
||
> бэклог реализма. Это живой план — стиль и правила как в [концепте](concept.md) и [модах](mods.md).
|
||
>
|
||
> **Сериализация животных (готово): сейв/лоад популяции и трупов.** `WorldSave` получил `Animals`
|
||
> (`AnimalSave`: геном→признаки+пол, поколение, стадия/возраст, нужды+истощение, здоровье — кровь/HP
|
||
> частей/хедифы тяжесть+иммунитет, настроение+мысли, беременность+геном отца) и `Corpses` (`CorpseSave`:
|
||
> вид/позиция/таймер-стадия разложения/мясо). `SaveAnimals` снимает популяцию (Health/Mood/Pregnant — через
|
||
> entity, т.к. запрос ≤5 типов); `RestoreAnimals`/`RestoreAnimalState` собирают особь фабрикой из генома и
|
||
> накладывают динамическое состояние (ref-правки до структурного `AddComponent(Pregnant)`; труп
|
||
> пере-тинтится через `Stage=-1`). `Corpse` получил поле `Species` (для спрайта/разделки); `CreateCorpse`
|
||
> теперь принимает индекс вида. На загрузке стадо НЕ пересыпается, если в сейве есть животные; старый/пустой
|
||
> сейв — регенерируется из сида (не крашит). Эволюция/болезни/беременности переживают сейв/лоад. Сборка +
|
||
> `--check-content` чистые.
|
||
>
|
||
> **A8 часть 2 (готово): инспектор животных.** `InspectPanel` теперь осматривает и животных (не только
|
||
> растения): вкладки **Обзор** (стадия/возраст/пол/поколение/беременность), **Нужды** (значения нужд %),
|
||
> **Здоровье** (кровь/боль, способности Consciousness/Moving/Sight, активные хедифы с тяжестью),
|
||
> **Настроение** (значение % + список мыслей с их сдвигом). Набор вкладок зависит от типа выбранного
|
||
> (растение vs животное). `SelectionSystem` теперь пикает и животных (запрос расширен с `PlantGrowth` на
|
||
> `AnimalOrganism`). Локализация `inspect.*`/`need.*`/`hediff.*`/`cap.*`/`animalstage.*` (ru/en). Сборка +
|
||
> `--check-content` чистые.
|
||
>
|
||
> **A8 часть 1 (готово): настроение — ядро.** Тип дефа `ThoughtDef` + `thoughts.json` (мысли: роды/
|
||
> спаривание/утоление жажды — сила и длительность данными); компонент `Mood` (значение 0..1, активность по
|
||
> тиру мозга, список затухающих мыслей) на всех животных; `AnimalMoodSystem` (тир 4+: настроение из
|
||
> непрерывного фона — средняя удовлетворённость убывающих нужд минус боль из здоровья — плюс сумма мыслей;
|
||
> мысли угасают). Реестр `ThoughtSet` + id-константы `AnimalThoughts`; событийные триггеры: роды
|
||
> (`AnimalPregnancySystem`), спаривание и утоление сильной жажды (`AnimalActionSystem`). **Эффект на
|
||
> поведение**: низкое настроение (стресс/истощение) подавляет спаривание в `AnimalDecisionSystem`. Команда
|
||
> `mood [species]`. Сборка + `--check-content` чистые (11 типов дефов). Осталось A8.2: вкладки инспектора
|
||
> (needs/health/mood) — сейчас животные в инспекторе не осматриваются (он только растительный).
|
||
>
|
||
> **A7 (готово): мозг → интеллект → гейт нужд/действий.** Эффективный интеллект = `brainSize` (ген) ×
|
||
> `Consciousness` (capacity из здоровья) — `AnimalFactory.Intelligence`. `AnimalContext` гейтит выбор ИИ:
|
||
> нужда с `minBrain` выше интеллекта особи в выбор не входит (`AnimalDecisionSystem` читает `Health`
|
||
> поэлементно). Пороги в `needs.json` — тиры мозга (голод 0.0, жажда 0.25, сон/секс 0.40; у оленя мозг
|
||
> ~0.45 → здоров весь набор). **Обратная связь от повреждения мозга**: болезнь/боль/кровопотеря роняют
|
||
> сознание → эффективный интеллект падает → первыми отключаются сон/спаривание (лихорадящий олень
|
||
> сводится к еде/питью). Команда `intel <species> [consciousness]`. Сборка + `--check-content` чистые.
|
||
>
|
||
> **A5 часть 1 (готово): смерть → труп → разложение.** `Corpse`-компонент + `CorpseSystem`
|
||
> (Fresh→Rotting→Skeleton→исчезает, скорость от температуры, тинт по стадии); смерть теперь оставляет
|
||
> труп (`AnimalFactory.CreateCorpse`, мясо ∝ размер тела) вместо деспавна; `AnimalDef.CorpseTexture` +
|
||
> `corpseTexture` оленя (`Dessicated_*`, скелет — бледный тинт, своего спрайта пока нет). Продукты
|
||
> `ProductMeat/Bone/Leather` в `products.json` (разделка/падальщики — позже). Сборка/линт чистые.
|
||
> **A5 часть 2 (готово): части тела, кровь, capacities.** `BodyDef`/`BodyPartDef` + `bodies.json`
|
||
> (`Quadruped` — данные: иерархия органов, вклады в способности); `AnimalDef.Body`; `HealthState`
|
||
> расширен `Parts`/`BloodLevel`/`Pain`/`Capacities`; `CapacityCalc` (обобщённый расчёт по зависимостям
|
||
> кровь→сердце/лёгкие→сознание→движение/зрение, модовые способности — сырыми); фабрика строит части
|
||
> (HP × размер тела) и считает способности. Источников урона ещё нет → всё на полном HP, способности =
|
||
> 1 (каркас под болезни A6/хищников). Команда `body <species>`. Сборка/линт чистые (10 типов дефов).
|
||
> Осталось в A5: смерть через здоровье (vital-часть/кровь) + истощение как hediff — когда появятся раны.
|
||
>
|
||
> **A6 (готово): болезни и иммунитет.** `HediffDef` расширён (severity/immunity/lethal/pain/capMods/
|
||
> ambient); хедифы теперь инстансы (`Hediff` с тяжестью/иммунитетом). `AnimalHealthSystem` (медленный
|
||
> тик ~час): фоновое заражение, гонка тяжесть↔иммунитет (выздоровление или смерть), пересчёт способностей
|
||
> с учётом боли и capMods; смерть по здоровью (летальная болезнь/кровь/потеря сознания) → труп. **Первый
|
||
> реальный урон** — `Moving` подключён к скорости (больные звери медленнее). Болезнь `Fever` в
|
||
> `hediffs.json`; команда `infect [hediff]`. Сборка/линт чистые.
|
||
>
|
||
> **A4 (готово):** нужда `Mating`; лёгкий hediff-каркас (`HediffDef`, `hediffs.json` с `Rut`,
|
||
> компонент `Health`/`HealthState`); гены `GeneBreedingSeason`/`GeneGestationDays`/`GeneLitterSize`;
|
||
> `AnimalRutSystem` (сезонный гон-хедиф поднимает влечение — вне сезона зачатия нет); действие
|
||
> `Mate` (поиск партнёра, сближение); `Pregnant` + `AnimalPregnancySystem` (вынашивание → роды
|
||
> через `Genome.Breed`, наследование пола без YY, поколение+1, потолок численности). Сборка
|
||
> чистая, `--check-content` (33 гена).
|
||
>
|
||
> **Рефактор расширяемости (готово):** устранены хардкод-блокеры — данными стали: **геном вида**
|
||
> (`AnimalDef.Genome` = словарь `geneId→base`, шаблон строится generically); **нужды**
|
||
> (`NeedDef`/`needs.json` + `NeedSet` + динамический `AnimalNeeds.Values` + реестр поведений по id);
|
||
> **диета** (`AnimalDef.Diet`); **стадии роста** (`AnimalStageDef`/`AnimalDef.Stages` — возраст входа,
|
||
> масштаб, текстуры, флаг «взрослая»; пусто → встроенный дефолт); **спавн** (`AnimalDef.SpawnPer1000Cells`
|
||
> — все виды рассыпаются по суше из данных, не только олень). Добавить ген/нужду/рацион/стадию/спавн
|
||
> виду = правка JSON без кода. Остаются как известные: реестр действий-кода (новое *поведение* = код,
|
||
> сознательная граница), часть глобальных баланс-констант (скорость/выпас/потолок).
|
||
>
|
||
> **A3 (готово):** компонент `AnimalGrowth` (стадия + возраст); ген пола `GeneSex` (XX/XY,
|
||
> генерация основателя без YY через `AnimalSet.GenerateGenome`) и ген `GeneMaturityAge`;
|
||
> стадии Baby→Juvenile→Adult→Senior с порогами из генов; `AnimalGrowthSystem` (смена
|
||
> спрайта детёныш/самец/самка и размера по стадии) + `AnimalMortalitySystem` (смерть от
|
||
> старости и от истощения → деспавн, замыкает контур ёмкости среды). Спавн стада —
|
||
> разновозрастный; команда `animal` показывает пол/созревание. Сборка чистая, `--check-content`
|
||
> (30 генов).
|
||
>
|
||
> **A1 (готово):** тип дефа `Animal` (`AnimalDef : PawnDef`) + `animals.json` (олень);
|
||
> 8 животных генов в `genes.json`; `AnimalPhenotype`/`AnimalOrganism`; `AnimalSet`
|
||
> (шаблон генома вида) + `AnimalFactory`; детерминированный спавн стада оленей по суше в
|
||
> `WorldScene` с рендером и тинтом меха по гену; консольная команда `animal <species>`.
|
||
>
|
||
> **A2 (готово):** нужды `AnimalNeeds` (голод/жажда/отдых) с падением по гену метаболизма;
|
||
> два слоя ИИ — `AnimalDecisionSystem` (движковый `UtilityAi`, выбор + поиск ближайшей
|
||
> еды/воды) и `AnimalActionSystem` (движение к цели + утоление: выедание растений, питьё у
|
||
> кромки воды, сон); `AnimalAppearanceSystem` (яркость по острой нужде); кромка воды из
|
||
> террейна; команда `popstats [species]` (средние признаков + размах поколений). Контур
|
||
> ёмкости среды частичный (трава выедается, но смерть от голода — в A3). Сборка чистая;
|
||
> визуальная проверка (олени ищут корм/воду, пасутся) — вручную: «Новый мир».
|
||
|
||
## Видение
|
||
|
||
Животные — следующая большая система после генов. Главный принцип: животное живёт по
|
||
**тем же законам, что и растение** — гены → признаки (фенотип) → поведение систем, —
|
||
но разворачивает их в полноценную зоологию: двуполое размножение с вынашиванием,
|
||
здоровье по модели RimWorld (части тела, органы, кровь, болезни), мозг и интеллект,
|
||
нужды и настроение. Цель прежняя: **эмерджентность** (динамика популяций, отбор,
|
||
пищевые цепи складываются из правил, а не скриптуются) и **наблюдаемость** (всё видно
|
||
в мире, HUD, инспекторе, консоли).
|
||
|
||
Первое показательное животное — **олень** (богатый набор текстур: ♂/♀/детёныш/труп).
|
||
|
||
## Опора на существующий фундамент
|
||
|
||
Ничего из перечисленного не строим заново — переиспользуем:
|
||
|
||
- организм-агностичный `GeneDef`, управляемый `Genome` (диплоидные аллели),
|
||
`Genome.Breed(мать, отец, mutationChance)`, `Phenotype.Compute`, `GenomeTemplate`
|
||
(шаблон вида, ср. `PlantSet.BuildTemplate`);
|
||
- формульный движок и regex-группы (`gsum('Gene.*')`) для производных признаков;
|
||
- движковый `UtilityAi<TContext>`, `ResponseCurve`, `Suitability.Trapezoid`;
|
||
- день/ночь, сезоны, климат, террейн `water`, `ProductDef`;
|
||
- паттерн компонентов растений: `…Organism { Genome; Phenotype Traits }`,
|
||
`…Growth { species, stage, ageDays }`, `…Needs`, `…Brain`, `AppearanceSystem` (sim→`Sprite`).
|
||
- Friflo допускает managed-ссылку в `IComponent` (доказано `Genome`/`Tilemap`) —
|
||
поэтому `Health`/`Pregnant` несут managed-объекты.
|
||
|
||
## Принцип моддинга: всё расширяется данными
|
||
|
||
Главное требование — **система должна целиком модифицироваться через моды без правки
|
||
кода**. Для этого:
|
||
|
||
1. **Никаких C#-`enum` для видового контента.** Виды, части тела, органы, способности
|
||
(capacities), болезни, нужды, мысли — это **дефы со строковыми id**, интернируемые
|
||
в int при загрузке (ровно как уже сделано с `geneId`). Enum нельзя расширить модом —
|
||
строковый id можно.
|
||
2. **Формулы вместо констант** везде, где можно (как `effects` у генов): модер меняет
|
||
баланс формулой в дефе, а не пересборкой.
|
||
3. **Реестр поведенческих действий** (`actionId → исполнитель-код`). Деф нужды/ИИ
|
||
ссылается на `actionId`. Граница честная и в духе проекта (движок без сэмпла,
|
||
фичи демонстрируются игрой): **комбинировать готовые кирпичи — мод; принципиально
|
||
новое поведение — код + деф.**
|
||
4. **Patch-система (фаза G5)** уже позволяет модеру менять чужие дефы по regex
|
||
(например, «всем хищникам +агрессия»).
|
||
5. Любой деф переопределяется одноимённым из позднего мода; локализация — в `Languages`.
|
||
|
||
Итог: модер добавляет новый вид зверя, орган, болезнь, нужду или мысль одними JSON-файлами.
|
||
|
||
## Геном животного
|
||
|
||
Животные гены живут в `genes.json` рядом с растительными (один организм-агностичный
|
||
тип `Gene`). Предлагаемый набор (числовые, если не сказано иное):
|
||
|
||
| Группа | Гены |
|
||
|---|---|
|
||
| Тело/метаболизм | `GeneMaxBodySize`, `GeneMetabolism`, `GeneMoveSpeed`, `GeneBloodVolume`, `GeneVision`, `GeneInsulation` (мех/терморегуляция) |
|
||
| Жизненный цикл | `GeneMaturityAge`, `GeneLifespan` (есть), `GeneGestationDays`, `GeneLitterSize` |
|
||
| Органы | `GeneHeartEfficiency`, `GeneLungCapacity`, `GeneImmunityGain`, `GeneHealRate`, `GenePainTolerance` |
|
||
| Мозг | `GeneBrainSize` → признак `brainSize` (см. «Мозг и интеллект») |
|
||
| Поведение | `GeneAggression`, `GeneSociability`, `GeneActivityPattern` (дневной/ночной/сумеречный) |
|
||
| Среда | переиспользуем `GeneOptimalTemperature/Tolerance/Cold/HeatHardiness` — звери тоже мёрзнут/перегреваются через ту же трапецию |
|
||
| Морфология | `GeneFurColor` (тинт спрайта, как `leafHue`), `GeneSex` (дискретный, см. ниже) |
|
||
|
||
**Пол выводится из диплоидности бесплатно.** Локус с аллелями {X=0, Y=1}: самка = XX,
|
||
самец = XY. При `Genome.Breed` мать может отдать только X (у неё нет Y), отец — X/Y
|
||
поровну → потомок XX/XY ровно 50/50, **без спец-кода в скрещивании**. У гена пола
|
||
`mutationChance: 0` (как у `GeneFruitSeason`), чтобы X не мутировал в Y.
|
||
|
||
> Тонкость генерации (не скрещивания): обычный `Generate` дискретного гена выбрал бы
|
||
> два аллеля независимо и мог бы выдать невозможный **YY**. Поэтому пол особи-основателя
|
||
> назначается явно — XX или XY (по 50/50) — а дальнейшее наследование корректно само.
|
||
|
||
Признаки собираются в `AnimalPhenotype.FromTraits(traits)` — зеркало `PlantPhenotype`.
|
||
Организм: `AnimalOrganism { Genome; AnimalPhenotype Traits; int Generation }` (поколение =
|
||
max(род.) + 1, у основателей 0 — для `popstats` и видимого дрейфа).
|
||
|
||
## Жизненный цикл и стадии роста
|
||
|
||
Зеркало `PlantGrowth`: `AnimalGrowth { species, stage, ageDays }`. Стадии задаются
|
||
данными в дефе вида: `Embryo` (в утробе) → `Baby` → `Juvenile` → `Adult` → `Senior`,
|
||
пороги из генов (`MaturityAge`, `Lifespan`). `AnimalGrowthSystem` двигает стадию,
|
||
масштабирует `sizeCells`, меняет спрайт/набор текстур (Baby ↔ ♀/♂). Стадия `Senior`
|
||
повышает шанс возрастных hediff и смерти — стык со здоровьем.
|
||
|
||
> **Путь смерти по фазам.** Чтобы на A4 (размножение) популяция не взрывалась без
|
||
> убыли, простая **смерть по старости и от голода/жажды (деспавн)** появляется уже в A3.
|
||
> На A5 она «апгрейдится»: смерть идёт через здоровье (возрастные hediff, истощение, отказ
|
||
> органов) и оставляет **труп** вместо деспавна.
|
||
|
||
## Нужды и ИИ
|
||
|
||
Обобщаем `PawnNeeds` (одна энергия) → `AnimalNeeds`: голод, жажда, сон, секс (0..1).
|
||
|
||
- скорости падения из `GeneMetabolism` × размер (масштаб по Клайберу: ∝ size^0.75);
|
||
- **голод** — ест растения/продукты. **Поедание реально воздействует на сущность
|
||
растения** (важно для контура ёмкости среды): выедает накопленные плоды
|
||
(`Fruiting.RipeFruit`) и/или убавляет биомассу/стадию растения; перевыпас может убить
|
||
растение. **Диета** (`AnimalDef.diet`) — список съедобного: теги растений/`ProductDef`
|
||
(для будущих хищников — живая добыча/мясо/трупы), плюс способ (выедание плодов vs выпас);
|
||
- **жажда** — пьёт на клетках террейна `water`;
|
||
- **сон** — спит в неактивную фазу суток (`GeneActivityPattern`): дневные ночью и т.д.;
|
||
- **секс** — удовлетворяется размножением.
|
||
|
||
ИИ разбит на **два слоя — выбор и исполнение**:
|
||
|
||
1. **Выбор** — `AnimalDecisionSystem` на движковом `UtilityAi`: из активных нужд выбирает
|
||
действие `FindFood / FindWater / Sleep / SeekMate / Wander(default)` (готовые
|
||
`ResponseCurve`) и пишет его + цель в `AnimalBrain { Action; Target; DecideIn }`.
|
||
Действия и нужды описаны дефами `NeedDef` (decay-формула, `minBrain`).
|
||
2. **Исполнение** — отдельный слой систем (реестр `actionId → исполнитель`, см. «Принцип
|
||
моддинга») доводит выбранное действие до результата по схеме **найти цель → дойти →
|
||
выполнить → удовлетворить нужду**:
|
||
- *Eat*: ближайшее съедобное (плод/биомасса) → подойти → съесть (убавить растение) → ↑ сытость;
|
||
- *Drink*: ближайшая клетка `water` → подойти → попить → ↓ жажда;
|
||
- *Sleep*: остановиться → спать → ↓ усталость;
|
||
- *Mate*: подходящий партнёр → сблизиться → акт → `Pregnant` + сброс `Mating`;
|
||
- *Wander*: движение по умолчанию.
|
||
Без этого слоя нужда только падает, но не исполняется.
|
||
|
||
**Передвижение и поиск цели.** Движение к цели — рулевое смещение к точке (реюз
|
||
`Transform2D` + `WanderSystem`/`SeparationSystem` для обхода препятствий: стволы деревьев,
|
||
другие звери), без полноценного A* на старте. Вода непроходима для сухопутных → `Drink`
|
||
целит клетку суши у кромки воды. «Ближайшее съедобное/вода/партнёр» — **пространственный
|
||
запрос по сетке/тайлмапу** с радиусом из `Vision`/`Sight` (не O(n²)); ничьи в «ближайшем»
|
||
разрешаются детерминированно (по id сущности).
|
||
|
||
> Порядок фаз: формальный **гейтинг нужд интеллектом — фаза A7**. В A2 у оленя просто
|
||
> полный набор нужд зверя (тир 4); поле `minBrain` уже пишем в дефы, но активируется оно
|
||
> на A7 (см. «Мозг и интеллект»).
|
||
|
||
## Размножение (млекопитающие)
|
||
|
||
Главное отличие от растений — двуполость и отложенное во времени вынашивание:
|
||
|
||
1. **Влечение и гон (через хедиф).** Нужда `Mating` есть **круглый год** на низком фоне.
|
||
В брачный сезон вида на взрослых вешается **хедиф `Rut` (гон)**, который поднимает
|
||
влечение (множитель на рост `Mating` / снижение порога действия). **Только под `Rut`
|
||
влечение переходит порог `SeekMate` → вне сезона зачатия фактически нет**, хотя нужда
|
||
формально существует. Хедиф снимается с концом сезона и **виден в инспекторе** (как
|
||
любой hediff). Это унифицирует сезонность с hediff-механизмом, а не городит спец-флаг.
|
||
2. **Спаривание → беременность.** Два взрослых разного пола сближаются (действие `Mate`) →
|
||
акт → у самки появляется `Pregnant : IComponent { Genome FatherGenome; float
|
||
ConceivedDay; float GestationDays; int LitterSize }` — managed-ссылка на геном отца
|
||
(оплодотворение = «слепок» отца). Беременность повышает метаболизм (голод) и снижает
|
||
`Moving`. (Можно реализовать и как hediff, но держим компонентом ради managed-ссылки
|
||
на геном отца.)
|
||
3. **Роды.** По достижении срока спавним `LitterSize` детёнышей, каждому
|
||
`Genome.Breed(мать, отец, mutationRate)`, стадия `Baby`, рядом с матерью. Лактация/
|
||
опека — опционально после.
|
||
|
||
Стратегии r/K выводятся из генов: мелкие (заяц) — большой помёт, короткая жизнь;
|
||
крупные (олень/медведь) — мало потомства, долгая жизнь.
|
||
|
||
**Решённые параметры (A4):**
|
||
- **Гон = хедиф `Rut`** (а не «нужда растёт в сезон»): навешивается в брачный сезон
|
||
`GeneBreedingSeason` (как `fruitSeason`, 0..3) — у **оленя осень** (→ роды весной к
|
||
обилию корма); у r-стратегов (заяц) — круглогодично/частый гон. Под `Rut` влечение
|
||
переходит порог действия; вне `Rut` зачатия нет.
|
||
- **Hediff-каркас рождается здесь (A4).** Гон — первый хедиф, поэтому в A4 появляется
|
||
лёгкий каркас: контейнер `Health`/`HealthState` со списком хедифов, `HediffDef` со
|
||
стадиями и модификаторами, навешивание/снятие по триггеру. A5/A6 расширяют его
|
||
частями тела, кровью, болезнями.
|
||
- **Триггер акта** — взрослые, разный пол, у обоих `Mating > 0.6`, рядом (1–2 клетки),
|
||
самка не беременна, оба не спят/не убегают.
|
||
- **Послеродовый кулдаун** — самка не зачинает снова до родов + восстановления;
|
||
самец восстанавливается быстро → **полигиния** (один самец кроет нескольких самок).
|
||
- **Выбор партнёра (половой отбор)** — НЕ в A4: на старте берётся ближайший подходящий;
|
||
предпочтение по размеру/здоровью/настроению добавим позже отдельной итерацией.
|
||
|
||
## Здоровье — полная модель RimWorld
|
||
|
||
### Данные (дефы)
|
||
|
||
- **`BodyDef`** — дерево частей. У части: `coverage` (доля тела → вес попадания),
|
||
`parent`, `maxHp` (масштаб от размера тела), флаги (`vital`, `solid`, `skinCovered`),
|
||
**вклады в capacities** `{ breathing: 0.5, consciousness: 1.0, moving: 0.25, … }`.
|
||
Пример (олень): `body → torso → (heart, lungs×2, liver, stomach, kidneys×2, spine),
|
||
head → (brain, eyes×2, ears×2, jaw), legs×4 → hooves`.
|
||
- **`HediffDef`** — *всё* есть hediff (рана, потеря части, болезнь, имплант, можно и
|
||
беременность): `stages` с порогами по severity; стадия задаёт `capMods`
|
||
(offset/factor/setMax по capacity), `painOffset/painFactor`, `bleedRate`, для болезней —
|
||
параметры иммунитета.
|
||
- **`CapacityDef`** — общий каталог способностей строковыми id: `Consciousness, Moving,
|
||
Manipulation, Sight, Hearing, Breathing, BloodPumping, BloodFiltration, Digestion,
|
||
Metabolism` (расширяемо модом). Это **каталог**, а не обязательный набор: вид имеет
|
||
лишь те способности, в которые вкладываются части его `BodyDef` (у копытного оленя нет
|
||
`Manipulation` — некому вкладываться).
|
||
|
||
### Компонент
|
||
|
||
`Health : IComponent { HealthState State }` — managed-класс (как `Genome`):
|
||
`List<PartInstance>` (деф + текущий hp), `List<Hediff>` (severity, привязка к части,
|
||
иммунитет), кэш `Capacities`, `Pain`, `BloodLevel`, `Dead`.
|
||
|
||
> Контейнер растёт по фазам: **рождается в A4** с одним лишь `List<Hediff>` (под гон-хедиф
|
||
> `Rut`); части тела, кровь, capacities и `Dead` добавляются в A5, болезни/иммунитет — A6.
|
||
|
||
### Расчёт capacities (обобщённая дата-модель)
|
||
|
||
Способности зависят друг от друга, считаем в порядке зависимостей:
|
||
|
||
1. `BloodLevel` (0..1) ← запас минус кровопотеря;
|
||
2. `BloodPumping` ← сердце × BloodLevel; `Breathing` ← лёгкие × BloodLevel;
|
||
`BloodFiltration` ← печень/почки;
|
||
3. `Consciousness` ← мозг × BloodPumping × Breathing × (1 − болевой штраф);
|
||
4. `Moving` ← ноги/позвоночник × Consciousness × BloodPumping; `Sight` ← глаза ×
|
||
Consciousness; `Digestion` и пр. — домножаются на вышестоящие.
|
||
|
||
Формула одна и общая: `capacity = Σ(вклады работающих частей × partHp/maxHp) ×
|
||
вышестоящие capacity`. **Без хардкода на каждую способность** — новую способность
|
||
вводит деф. Каждая capacity — множитель на действия: `Moving`→скорость, `Sight`→радиус
|
||
поиска, `Digestion`→скорость насыщения, `Consciousness`→интеллект и сам факт сознания.
|
||
|
||
### Кровь — как hediff (реалистичная модель RW)
|
||
|
||
Кровопотеря = hediff `BloodLoss` (severity 0..1). Кровоточащие раны поднимают её через
|
||
`bleedRate`; без кровотечения медленно рассасывается; severity = 1 → смерть; влияет на
|
||
`BloodPumping`/`Consciousness` через `capMods`. `GeneBloodVolume` масштабирует скорость
|
||
роста severity (больше крови → медленнее теряет).
|
||
|
||
### Болезни и иммунитет (гонка)
|
||
|
||
severity болезни растёт за день; параллельно растёт иммунитет (0..1) со скоростью из
|
||
стата (зависит от `BloodFiltration`, отдыха, сытости, `GeneImmunityGain`). Иммунитет→1
|
||
раньше = выздоровел; severity→летальный порог раньше = смерть. Голодный/раненый зверь
|
||
борется с болезнью медленнее.
|
||
|
||
### Смерть
|
||
|
||
Триггеры: разрушена `vital`-часть (мозг/сердце), `Consciousness ≤ порога`,
|
||
`BloodLevel = 0`, летальная severity болезни, старость (возрастные hediff: катаракта,
|
||
больная спина, деменция, закупорка артерий + шанс смерти от старости). На смерти:
|
||
стоп-ИИ, переход в труп.
|
||
|
||
### Тик
|
||
|
||
Медленный «health tick» раз в игровой час + событийный пересчёт при уроне.
|
||
Детерминизм: привязка к игровому времени, не к FPS.
|
||
|
||
### Источники урона в v1
|
||
|
||
**Почти никаких.** Строим весь каркас (части, кровь, capacities, hediff-framework, условия
|
||
смерти), но не вводим *травмирующих* источников. На старте (A1–A2) смерть только **от
|
||
старости**.
|
||
|
||
Порядок источников урона по фазам: **истощение/обезвоживание** — уже в **A3** (простое
|
||
правило «нужда=0 дольше N дней → смерть», замыкает контур ёмкости среды; в A5
|
||
переоформляется в hediff истощения) → **температура** (гипотермия/тепловой удар, hediff на
|
||
той же трапеции, что у растений) → **болезни** (A6) → **хищничество** (горизонт).
|
||
|
||
## Труп и разложение
|
||
|
||
Труп — отдельная сущность с `Corpse { RotDays; Stage }`. Конвейер (скорость зависит от
|
||
температуры/сезона — реюз климата):
|
||
|
||
```
|
||
Fresh ──→ Rotting ──→ Skeleton ──→ (исчезает)
|
||
│ │ │
|
||
│ разделка │ разделка └─ остаётся ProductBone
|
||
└──────────┴─→ ProductMeat (кол-во ∝ GeneBodySize) + опц. ProductLeather
|
||
```
|
||
|
||
- свежий/гниющий можно **разделать** → мясо (объём от размера тела) + шкура;
|
||
спрайт `Dessicated_*` (для оленя уже есть) на стадии Rotting;
|
||
- **скелет** — финальная стадия разложения (как просили, скелет — часть процесса):
|
||
мясо уже не получить, остаются кости (`ProductBone`);
|
||
- после скелета труп **исчезает в ничто**. На будущее — возврат плодородия в почву
|
||
(ниточка к растениям, опционально).
|
||
- мясо/кости/падаль → кормовая база будущих хищников и падальщиков.
|
||
|
||
Новые продукты: `ProductMeat`, `ProductBone`, `ProductLeather`.
|
||
|
||
**Спрайт скелета — нужен настоящий (решено).** В проекте костяных/скелетных текстур
|
||
нет (проверено) → это **ассет-задача**: создать/найти спрайт скелета (желательно
|
||
универсальный + по-видовой при наличии), путь вида `things/pawn/animal/deer/Skeleton_*`.
|
||
До появления ассета стадию Skeleton можно временно рисовать обработкой `Dessicated_*`,
|
||
но финальная версия требует своего спрайта.
|
||
|
||
## Мозг и интеллект
|
||
|
||
`GeneBrainSize` → признак `brainSize`. **Эффективный интеллект** = `brainSize ×
|
||
Consciousness` — т.е. повреждение/болезнь мозга, боль, кровопотеря динамически снижают
|
||
интеллект (сильная связка со здоровьем).
|
||
|
||
Интеллект **гейтит набор нужд и действий**. У `NeedDef`/действия поле `minBrain`:
|
||
активный набор особи = те, у кого `minBrain ≤ brainSize`. **Без enum** — шкала
|
||
непрерывная, тиры ниже лишь ярлыки для авторов контента (числа порогов — контент,
|
||
правятся модом). `brainSize` нормирован 0..1.
|
||
|
||
| Тир | `minBrain` | Кто | Что открывается |
|
||
|---|---|---|---|
|
||
| 1 — рефлекс | 0.00 | губки, простейшие | пассивный голод, реактивность |
|
||
| 2 — примитив | ~0.10 | насекомые, черви | голод; реактивное поведение |
|
||
| 3 — низший позвоночный | ~0.25 | рыбы, рептилии, амфибии | +жажда; простое избегание |
|
||
| 4 — зверь | ~0.45 | млекопитающие, птицы (**олень**, заяц, волк) | +сон, секс, Flee, SeekMate; **настроение**; факультативно стадность/память |
|
||
| 5 — умный (на будущее) | ~0.75 | приматы, врановые, люди | +социальные нужды, игра, любопытство, память о ресурсах, планирование, обучение, сложное настроение/отношения |
|
||
|
||
Олень = **тир 4**. Настроение, сон и брачное поведение включаются с тира 4.
|
||
|
||
## Настроение
|
||
|
||
`Mood` (0..1) собирается **из двух источников** (решено):
|
||
|
||
1. **Непрерывные состояния → в настроение напрямую** по кривой `ResponseCurve`:
|
||
уровни голода, жажды, усталости, боли (из здоровья), температуры вне комфорта.
|
||
Это не дискретные мысли, а текущий вклад нужд/состояния.
|
||
2. **События → `ThoughtDef { moodOffset, durationDays, trigger }`** (модель thoughts
|
||
RimWorld, данными): «дала потомство», «спарилась», «напилась после долгой жажды»,
|
||
«потеряла детёныша» — временный сдвиг настроения с затуханием.
|
||
|
||
Настроение есть только с тира 4 (зверь) — гейт интеллектом; у примитивных его нет.
|
||
Влияет на поведение (низкое → паника/бегство, не спаривается) и видно в инспекторе.
|
||
|
||
## Ориентировочные числа для оленя (старт)
|
||
|
||
Шкала времени мира: **год = 60 игровых дней**, сезон = 15 дней (для справки растения
|
||
живут ~160 дней). Числа — стартовая точка, балансируются при реализации; это значения
|
||
по умолчанию генома вида (`GenomeTemplate`), вокруг которых идёт разброс и мутации.
|
||
|
||
| Параметр | Ген/поле | Значение | Заметка |
|
||
|---|---|---|---|
|
||
| Размер тела | `GeneMaxBodySize` | ♀ 1.4 / ♂ 1.6 sizeCells | как в текущих `DeerDoe`/`DeerBuck` |
|
||
| Продолжительность жизни | `GeneLifespan` | ~360 дн (~6 лет) | дольше растений, видно несколько поколений |
|
||
| Взросление (→Adult) | `GeneMaturityAge` | ~90 дн (~1.5 года) | Baby 0–20, Juvenile 20–90, Adult 90–~290, Senior 290+ |
|
||
| Срок беременности | `GeneGestationDays` | ~30 дн (2 сезона) | гон осенью → роды весной |
|
||
| Помёт | `GeneLitterSize` | 1 (изредка 2) | K-стратег |
|
||
| Сезон гона | `GeneBreedingSeason` | осень | у зайца — круглый год |
|
||
| Размер мозга | `GeneBrainSize` | ~0.45 (тир 4) | открывает сон/секс/настроение |
|
||
| Объём крови | `GeneBloodVolume` | ~1.0 (×размер) | масштабирует скорость кровопотери |
|
||
| Скорость | `GeneMoveSpeed` | ~1.1–1.3 | олень быстрый |
|
||
| Голод | `NeedDef` decay | полный голод за ~1.5 дн | по Клайберу ∝ size^0.75 |
|
||
| Жажда | `NeedDef` decay | полная жажда за ~0.75 дн | быстрее голода |
|
||
| Сон | `NeedDef` decay | суточный цикл | спит в неактивную фазу |
|
||
|
||
## Идеи для реализма (бэклог)
|
||
|
||
Опционально, по мере фаз — каждая ложится на дата-модель:
|
||
|
||
- **Гон/брачный сезон** — хедиф `Rut`, навешиваемый в сезон, поднимает влечение (реализовано в A4).
|
||
- **Терморегуляция мехом** — `GeneInsulation` сдвигает температурную трапецию;
|
||
новорождённые мёрзнут сильнее.
|
||
- **Метаболизм по Клайберу** — расход ∝ size^0.75 (крупные едят больше абсолютно,
|
||
меньше на кг).
|
||
- **Циркадные ритмы** — дневные/ночные/сумеречные виды (`GeneActivityPattern`).
|
||
- **Стадность** — `GeneSociability`: стадные держатся группой (реюз `SeparationSystem`
|
||
+ когезия) → эмерджентные стада.
|
||
- **Сезонный окрас** — арктические лиса/заяц/волк белеют зимой (текстуры `*_arctic`
|
||
есть) — сезонный свитч спрайта/тинта.
|
||
- **Половой диморфизм** — рога/размер у самцов (ген, проявляется при XY).
|
||
- **r/K-стратегии** — выводятся из `litterSize/gestation/lifespan`.
|
||
- **Инбридинг-депрессия** (продвинутое) — родственное скрещивание снижает fitness
|
||
через накопление рецессивных аллелей.
|
||
- **Память о ресурсах/территория** (для умных) — водопои, кормовые места.
|
||
- **Возврат нутриентов** — полное разложение трупа повышает плодородие клетки.
|
||
|
||
## Новые типы дефов (для модеров)
|
||
|
||
| Тип | Назначение |
|
||
|---|---|
|
||
| `AnimalDef : PawnDef` (подтип) | вид: геном-шаблон (ссылки на гены), `BodyDef`, диета, текстуры по стадиям/полу, размеры. Наследник `PawnDef` — существующие 9 животных-`PawnDef` остаются валидными; поведение на компонентах, не на типе дефа |
|
||
| `BodyDef` | дерево частей тела с coverage/parent/maxHp/флагами/вкладами в capacities |
|
||
| `HediffDef` | раны/болезни/состояния: стадии, capMods, боль, кровотечение, иммунитет |
|
||
| `CapacityDef` | именованная способность (строковый id) |
|
||
| `NeedDef` | нужда: формула расхода, `minBrain`, привязка к действию |
|
||
| `ThoughtDef` | мысль настроения: offset, длительность, условие-триггер |
|
||
| `ProductDef` (есть) | + `ProductMeat`, `ProductBone`, `ProductLeather` |
|
||
|
||
CLR-классы — `src/LittleSim/Content/GameDefs.cs`; новый тип = класс + `RegisterType` в
|
||
`GameContent.Load` (см. [mods.md](mods.md)).
|
||
|
||
## Отбор, экология и наблюдаемость
|
||
|
||
Смысл генетики — чтобы за поколениями популяция **реально адаптировалась к климату
|
||
сида** (дрейф признаков под отбором). Чтобы это было видно (столп «наблюдаемость»):
|
||
|
||
- **Команда `popstats <species>`** — среднее и разброс ключевых признаков популяции +
|
||
счётчик поколений. Без неё отбор «есть, но невидим». Появляется, как только в мире
|
||
есть популяция (≈A1–A2), расширяется по мере добавления признаков.
|
||
- **Контур ёмкости среды (Лотка–Вольтерра через корм).** Даже без хищников травоядные
|
||
могут **выесть растения → голод → крах популяции → восстановление флоры → новый рост**.
|
||
Это бесплатная эмерджентность уже на A2, **если поедание реально уменьшает растения**.
|
||
Явно проверяем этот контур на A2 (поедание убавляет плод/растение, голод растёт при
|
||
нехватке корма). **Важно:** полный «крах» популяции требует **смерти от голода** — она
|
||
появляется в **A3** (простое правило). На A2 контур наблюдается частично (звери голодают
|
||
и тормозят, но не вымирают), полностью замыкается с A3.
|
||
|
||
## Сквозные требования (каждая фаза)
|
||
|
||
- **Детерминизм.** Любая случайность (спавн, выбор партнёра, мутации, выбор части под
|
||
урон, шанс инфекции) — из seeded `Random`, выведенного из сида мира (+ id сущности),
|
||
никогда `Random.Shared`. Один сид = воспроизводимый мир. Ничьи в пространственных
|
||
запросах («ближайший корм/вода/партнёр») разрешаются по id сущности, не по порядку обхода.
|
||
- **Сериализация — чек-лист по каждой фазе.** Каждая фаза сразу делает сейв/лоад своих
|
||
данных: A1 геном (уже умеет, geneId→Allele) → A3 стадия/возраст/пол → A4 беременность
|
||
(срок + **геном отца**) + список хедифов (гон `Rut`) → A5 здоровье (части тела, кровь,
|
||
capacities) + труп (стадия/таймер разложения) → A8 настроение/мысли. Пустой/старый
|
||
сейв — регенерируется, не крашит.
|
||
- **Sim/presentation split.** Системы симуляции пишут только компоненты; спрайт/тинт
|
||
(пол, стадия, мех, гниль) ставит `AppearanceSystem`. Никаких draw/UI из симуляции.
|
||
|
||
## План реализации (фазы A1–A8)
|
||
|
||
По аналогии с фазами генов G1–G5. Каждая фаза — отдельный коммит, дев-команда для
|
||
проверки, проверка через `--check-content` (где можно), и пункты сквозных требований выше.
|
||
**Вкладки инспектора добавляются по мере появления данных:** needs@A2, health@A5, mood@A8.
|
||
|
||
1. **A1 — фундамент.** `AnimalDef` + животные гены + `AnimalPhenotype`/`AnimalOrganism`
|
||
+ per-species `GenomeTemplate` + спавн оленя в `WorldScene` + рендер + команда
|
||
`animal <species>`.
|
||
2. **A2 — нужды + ИИ (выбор + исполнение).** `AnimalNeeds` + `AnimalDecisionSystem`
|
||
(выбор) + **слой исполнения** (Eat/Drink/Sleep: найти→дойти→выполнить→удовлетворить) +
|
||
еда-растения / вода-террейн / сон по фазе суток + `AppearanceSystem`. **Проверить
|
||
контур ёмкости среды** (поедание убавляет растения → колебания). Команда `popstats`.
|
||
3. **A3 — жизненный цикл.** Стадии Baby→Adult→Senior, ген пола (XX/XY, генерация без
|
||
YY), спрайт по стадии/полу, **простая смерть по старости и от голода/жажды (деспавн)**
|
||
— последняя замыкает контур ёмкости среды.
|
||
4. **A4 — размножение.** Нужда `Mating` + **лёгкий hediff-каркас** + гон-хедиф `Rut`
|
||
(сезон) + исполнение `Mate` → `Pregnant` → роды через `Genome.Breed`.
|
||
5. **A5 — здоровье I.** `BodyDef`, части тела, кровь (hediff), capacities, **апгрейд
|
||
смерти**: через здоровье + **труп** с разложением до скелета (вместо деспавна A3).
|
||
6. **A6 — здоровье II.** Болезни/`HediffDef` с иммунитетом, боль.
|
||
7. **A7 — мозг/интеллект.** Ген→интеллект→гейт нужд/действий + обратная связь от
|
||
повреждения мозга.
|
||
8. **A8 — настроение.** `Mood` + `ThoughtDef` + вкладки инспектора (needs/health/mood —
|
||
`InspectPanel` уже умеет вкладки overview/genes/products).
|
||
|
||
## После A8 — экологический горизонт
|
||
|
||
То, ради чего вся глубина: превращает «зоопарк» в живую экосистему. Каждая фича опирается
|
||
на уже построенные системы.
|
||
|
||
> **ГОТОВО — хищный кластер (часть 1): диета-гены + охота + урон + бегство.** Диета теперь
|
||
> ДАННЫЕ-через-ГЕНЫ: `GeneHerbivory`/`GeneCarnivory`/`GeneOmnivory` → признаки
|
||
> `herbivory/carnivory/omnivory`; рацион выводит `AnimalFactory.Diet` (ест растения, если
|
||
> max(herb,omni) ≥ 0.5; ест мясо, если max(carn,omni) ≥ 0.5 — всеядность включает оба; откат
|
||
> на `AnimalDef.Diet` у видов без генов диеты). Новые виды: **Wolf** (хищник, охотится) и
|
||
> **WildBoar** (всеядный); у оленя добавлен `GeneHerbivory`. Действие `Hunt`
|
||
> (`AnimalDecisionSystem` ищет ближайший труп-падаль ИЛИ живую добычу не крупнее себя;
|
||
> `AnimalActionSystem.Hunt` ведёт к цели и при контакте кусает или ест труп). **Первый
|
||
> травматический урон**: `HealthState.ApplyInjury` (урон части тела по весу попадания + острая
|
||
> кровопотеря + хедиф `Bleeding`); `Bleeding` (`bloodLossPerDay`) точит кровь в
|
||
> `AnimalHealthSystem` (+восстановление крови в покое), кровь на нуле → смерть → труп → падаль.
|
||
> Боевые параметры — данные на `AnimalDef` (`attackDamage/attackBleed/attackBloodLoss/
|
||
> attackRangeCells`). **Бегство**: `AnimalActions.Flee` — жертва, заметившая хищника не мельче
|
||
> себя в радиусе восприятия, убегает (приоритет над нуждами); сам хищник бежит лишь от заметно
|
||
> более крупного мясоеда. Общий путь смерти — `AnimalFactory.Die`. Консоль: `diet [species]`;
|
||
> `--check-content` печатает роль каждого вида. **Не покрыто (горизонт):** падальщики как
|
||
> отдельная роль, удар по настроению от страха, снижение страха стадом, спрайт скелета.
|
||
|
||
- **Хищный кластер.** Хищничество (волк→олень): диета = живая добыча; действие `Hunt`
|
||
(выследить → преследовать → атака → **урон через здоровье A5** → добить → съесть труп).
|
||
Это **первый «активный» (травмирующий) источник урона** для системы здоровья и
|
||
полноценная Лотка–Вольтерра с хищником. Рядом: **падальщики** (едят трупы Fresh/Rotting)
|
||
и **угроза/бегство** (хищник в радиусе `Sight` → страх → действие `Flee`, удар по
|
||
настроению; стадо снижает страх).
|
||
- **Родительская опека / импринтинг.** Детёныш ходит за матерью, голод гасится близостью
|
||
к лактирующей матери до ювенильной стадии; мать ведёт/защищает. Реализм + детёныши не
|
||
мрут от голода в одиночку.
|
||
- **Эпидемии.** Болезни (A6) распространяются по контакту/плотности → плотностно-зависимый
|
||
регулятор популяции (вспышки-крахи). Исход решает иммунитет (`GeneImmunityGain`).
|
||
- **Миграция.** При локальном истощении корма / некомфортном климате — уход к лучшим
|
||
клеткам (концепт: «голод+неурожай→миграция»). Эмерджентно из нужд + пространственного
|
||
поиска с большим радиусом.
|
||
|
||
Сюда же тяготеют пункты бэклога: половой отбор (выбор партнёра), сезонный окрас,
|
||
инбридинг-депрессия, возврат нутриентов в почву.
|
||
|
||
## Открытые вопросы
|
||
|
||
- Конкретный список `ThoughtDef`-событий для оленя (набор и величины offset/длительности)
|
||
— детализируется при реализации A8.
|
||
- Ассет: создать/найти спрайт скелета (`things/pawn/animal/deer/Skeleton_*`) к фазе A5.
|