Закрывает сквозное требование плана (сейв/лоад данных каждой фазы). WorldSave получил Animals (AnimalSave: геном+пол, поколение, стадия/возраст, нужды, здоровье — кровь/HP частей/хедифы, настроение+мысли, беременность+геном отца) и Corpses (CorpseSave). SaveAnimals снимает популяцию (Health/Mood/Pregnant через entity); RestoreAnimals/RestoreAnimalState собирают особь фабрикой из генома + накладывают состояние (ref-правки до AddComponent(Pregnant); труп пере-тинтится через Stage=-1). Corpse получил Species; CreateCorpse принимает индекс вида + HealthState.AddHediff(def,severity,immunity). На загрузке стадо не пересыпается при наличии животных в сейве; старый/пустой сейв регенерируется из сида. Сборка + --check-content чистые. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
53 KiB
Животные (зоология)
Статус: A1–A8 реализованы целиком + сериализация животных — базовый план зоологии и его сквозное требование сейв/лоада закрыты; дальше экологический горизонт (хищники, опека, эпидемии, миграция) и бэклог реализма. Это живой план — стиль и правила как в концепте и модах.
Сериализация животных (готово): сейв/лоад популяции и трупов.
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-объекты.
Принцип моддинга: всё расширяется данными
Главное требование — система должна целиком модифицироваться через моды без правки кода. Для этого:
- Никаких C#-
enumдля видового контента. Виды, части тела, органы, способности (capacities), болезни, нужды, мысли — это дефы со строковыми id, интернируемые в int при загрузке (ровно как уже сделано сgeneId). Enum нельзя расширить модом — строковый id можно. - Формулы вместо констант везде, где можно (как
effectsу генов): модер меняет баланс формулой в дефе, а не пересборкой. - Реестр поведенческих действий (
actionId → исполнитель-код). Деф нужды/ИИ ссылается наactionId. Граница честная и в духе проекта (движок без сэмпла, фичи демонстрируются игрой): комбинировать готовые кирпичи — мод; принципиально новое поведение — код + деф. - Patch-система (фаза G5) уже позволяет модеру менять чужие дефы по regex (например, «всем хищникам +агрессия»).
- Любой деф переопределяется одноимённым из позднего мода; локализация — в
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): дневные ночью и т.д.; - секс — удовлетворяется размножением.
ИИ разбит на два слоя — выбор и исполнение:
- Выбор —
AnimalDecisionSystemна движковомUtilityAi: из активных нужд выбирает действиеFindFood / FindWater / Sleep / SeekMate / Wander(default)(готовыеResponseCurve) и пишет его + цель вAnimalBrain { Action; Target; DecideIn }. Действия и нужды описаны дефамиNeedDef(decay-формула,minBrain). - Исполнение — отдельный слой систем (реестр
actionId → исполнитель, см. «Принцип моддинга») доводит выбранное действие до результата по схеме найти цель → дойти → выполнить → удовлетворить нужду:- Eat: ближайшее съедобное (плод/биомасса) → подойти → съесть (убавить растение) → ↑ сытость;
- Drink: ближайшая клетка
water→ подойти → попить → ↓ жажда; - Sleep: остановиться → спать → ↓ усталость;
- Mate: подходящий партнёр → сблизиться → акт →
Pregnant+ сбросMating; - Wander: движение по умолчанию. Без этого слоя нужда только падает, но не исполняется.
Передвижение и поиск цели. Движение к цели — рулевое смещение к точке (реюз
Transform2D + WanderSystem/SeparationSystem для обхода препятствий: стволы деревьев,
другие звери), без полноценного A* на старте. Вода непроходима для сухопутных → Drink
целит клетку суши у кромки воды. «Ближайшее съедобное/вода/партнёр» — пространственный
запрос по сетке/тайлмапу с радиусом из Vision/Sight (не O(n²)); ничьи в «ближайшем»
разрешаются детерминированно (по id сущности).
Порядок фаз: формальный гейтинг нужд интеллектом — фаза A7. В A2 у оленя просто полный набор нужд зверя (тир 4); поле
minBrainуже пишем в дефы, но активируется оно на A7 (см. «Мозг и интеллект»).
Размножение (млекопитающие)
Главное отличие от растений — двуполость и отложенное во времени вынашивание:
- Влечение и гон (через хедиф). Нужда
Matingесть круглый год на низком фоне. В брачный сезон вида на взрослых вешается хедифRut(гон), который поднимает влечение (множитель на ростMating/ снижение порога действия). Только подRutвлечение переходит порогSeekMate→ вне сезона зачатия фактически нет, хотя нужда формально существует. Хедиф снимается с концом сезона и виден в инспекторе (как любой hediff). Это унифицирует сезонность с hediff-механизмом, а не городит спец-флаг. - Спаривание → беременность. Два взрослых разного пола сближаются (действие
Mate) → акт → у самки появляетсяPregnant : IComponent { Genome FatherGenome; float ConceivedDay; float GestationDays; int LitterSize }— managed-ссылка на геном отца (оплодотворение = «слепок» отца). Беременность повышает метаболизм (голод) и снижаетMoving. (Можно реализовать и как hediff, но держим компонентом ради managed-ссылки на геном отца.) - Роды. По достижении срока спавним
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 (обобщённая дата-модель)
Способности зависят друг от друга, считаем в порядке зависимостей:
BloodLevel(0..1) ← запас минус кровопотеря;BloodPumping← сердце × BloodLevel;Breathing← лёгкие × BloodLevel;BloodFiltration← печень/почки;Consciousness← мозг × BloodPumping × Breathing × (1 − болевой штраф);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) собирается из двух источников (решено):
- Непрерывные состояния → в настроение напрямую по кривой
ResponseCurve: уровни голода, жажды, усталости, боли (из здоровья), температуры вне комфорта. Это не дискретные мысли, а текущий вклад нужд/состояния. - События →
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).
Отбор, экология и наблюдаемость
Смысл генетики — чтобы за поколениями популяция реально адаптировалась к климату сида (дрейф признаков под отбором). Чтобы это было видно (столп «наблюдаемость»):
- Команда
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.
- A1 — фундамент.
AnimalDef+ животные гены +AnimalPhenotype/AnimalOrganism- per-species
GenomeTemplate+ спавн оленя вWorldScene+ рендер + командаanimal <species>.
- per-species
- A2 — нужды + ИИ (выбор + исполнение).
AnimalNeeds+AnimalDecisionSystem(выбор) + слой исполнения (Eat/Drink/Sleep: найти→дойти→выполнить→удовлетворить) + еда-растения / вода-террейн / сон по фазе суток +AppearanceSystem. Проверить контур ёмкости среды (поедание убавляет растения → колебания). Командаpopstats. - A3 — жизненный цикл. Стадии Baby→Adult→Senior, ген пола (XX/XY, генерация без YY), спрайт по стадии/полу, простая смерть по старости и от голода/жажды (деспавн) — последняя замыкает контур ёмкости среды.
- A4 — размножение. Нужда
Mating+ лёгкий hediff-каркас + гон-хедифRut(сезон) + исполнениеMate→Pregnant→ роды черезGenome.Breed. - A5 — здоровье I.
BodyDef, части тела, кровь (hediff), capacities, апгрейд смерти: через здоровье + труп с разложением до скелета (вместо деспавна A3). - A6 — здоровье II. Болезни/
HediffDefс иммунитетом, боль. - A7 — мозг/интеллект. Ген→интеллект→гейт нужд/действий + обратная связь от повреждения мозга.
- A8 — настроение.
Mood+ThoughtDef+ вкладки инспектора (needs/health/mood —InspectPanelуже умеет вкладки overview/genes/products).
После A8 — экологический горизонт
То, ради чего вся глубина: превращает «зоопарк» в живую экосистему. Каждая фича опирается на уже построенные системы.
- Хищный кластер. Хищничество (волк→олень): диета = живая добыча; действие
Hunt(выследить → преследовать → атака → урон через здоровье A5 → добить → съесть труп). Это первый «активный» (травмирующий) источник урона для системы здоровья и полноценная Лотка–Вольтерра с хищником. Рядом: падальщики (едят трупы Fresh/Rotting) и угроза/бегство (хищник в радиусеSight→ страх → действиеFlee, удар по настроению; стадо снижает страх). - Родительская опека / импринтинг. Детёныш ходит за матерью, голод гасится близостью к лактирующей матери до ювенильной стадии; мать ведёт/защищает. Реализм + детёныши не мрут от голода в одиночку.
- Эпидемии. Болезни (A6) распространяются по контакту/плотности → плотностно-зависимый
регулятор популяции (вспышки-крахи). Исход решает иммунитет (
GeneImmunityGain). - Миграция. При локальном истощении корма / некомфортном климате — уход к лучшим клеткам (концепт: «голод+неурожай→миграция»). Эмерджентно из нужд + пространственного поиска с большим радиусом.
Сюда же тяготеют пункты бэклога: половой отбор (выбор партнёра), сезонный окрас, инбридинг-депрессия, возврат нутриентов в почву.
Открытые вопросы
- Конкретный список
ThoughtDef-событий для оленя (набор и величины offset/длительности) — детализируется при реализации A8. - Ассет: создать/найти спрайт скелета (
things/pawn/animal/deer/Skeleton_*) к фазе A5.