From aff5a947c5f0b1ff0130b24e553c263b24d901c2 Mon Sep 17 00:00:00 2001 From: Leonid Pershin Date: Fri, 12 Jun 2026 16:46:05 +0300 Subject: [PATCH] Phase A: wire climate + day/night into the world; design doc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bump the engine submodule to a684c23 (Climate + day/night ambient). The world scene now registers UseClimate and UseDayNight, so it darkens at night and the HUD shows the season and temperature alongside the date. Add docs/растения.md capturing the whole plant-ecosystem design (growth as a suitability product, hybrid Mendelian genome, climate, lighting with shadows, reproduction, persistence) and the A-D phase plan. Co-Authored-By: Claude Opus 4.8 --- Mods/Core/Languages/en/ui.json | 5 ++ Mods/Core/Languages/ru/ui.json | 5 ++ docs/растения.md | 84 ++++++++++++++++++++++++++++++ engine | 2 +- src/LittleSim/Scenes/WorldScene.cs | 13 ++++- src/LittleSim/Sim/SimSystems.cs | 11 +++- 6 files changed, 117 insertions(+), 3 deletions(-) create mode 100644 docs/растения.md diff --git a/Mods/Core/Languages/en/ui.json b/Mods/Core/Languages/en/ui.json index 57e4aaa..db6ac32 100644 --- a/Mods/Core/Languages/en/ui.json +++ b/Mods/Core/Languages/en/ui.json @@ -1,6 +1,11 @@ { "hud.world": "LittleSim | seed {0} | {1} | {2} FPS", "hud.datetime": "day {0}, {1:00}:{2:00}", + "hud.climate": "{0} {1}°C", + "season.spring": "spring", + "season.summer": "summer", + "season.autumn": "autumn", + "season.winter": "winter", "hud.controls": "WASD — camera, wheel — zoom, ` — console, F1 — inspector", "hud.paused": "PAUSED", diff --git a/Mods/Core/Languages/ru/ui.json b/Mods/Core/Languages/ru/ui.json index fd95518..138d77e 100644 --- a/Mods/Core/Languages/ru/ui.json +++ b/Mods/Core/Languages/ru/ui.json @@ -1,6 +1,11 @@ { "hud.world": "LittleSim | сид {0} | {1} | {2} FPS", "hud.datetime": "день {0}, {1:00}:{2:00}", + "hud.climate": "{0} {1}°C", + "season.spring": "весна", + "season.summer": "лето", + "season.autumn": "осень", + "season.winter": "зима", "hud.controls": "WASD — камера, колесо — зум, ` — консоль, F1 — инспектор", "hud.paused": "ПАУЗА", diff --git a/docs/растения.md b/docs/растения.md new file mode 100644 index 0000000..a219a17 --- /dev/null +++ b/docs/растения.md @@ -0,0 +1,84 @@ +# Растения как экосистема + +Дизайн системы растений: рост зависит от среды, среда описана факторами, реакция растения на +факторы задаётся **генами**, а размножение передаёт гены потомкам с мутацией — появляется +естественный отбор. Документ — единый ориентир для фаз A–D; детали каждой фазы уточняются её +отдельным планом. + +## Модель роста + +Скорость роста растения в данный момент: + +``` +скорость = база × пригодность(свет) × пригодность(температура) × пригодность(плодородность) +``` + +Каждая «пригодность» — число в [0..1] из кривой отклика (движковый `ResponseCurve`, модуль +`MrGameEng.AI`) с **оптимумом** и **шириной толерантности**, которые берутся из генов растения. +Растение вне своих оптимумов растёт медленно или стоит; в своих — быстро. Так все факторы среды +сводятся к одному механизму, а различие видов/особей — целиком в генах. + +Фактор «возраст/стадия» остаётся как сейчас (`PlantGrowthStage` в дефах): пригодности умножают +прирост `AgeDays`, стадии переключают спрайт/размер по накопленному возрасту. + +## Факторы среды + +- **Свет** — амбиент день/ночь (`MrGameEng.Lighting.DayNight`), сэмплируемый в точке + (`SampleAt(world)`); глобальный в фазе A, локальный (с тенями) в фазе D. +- **Температура** — непрерывная, из климата (`MrGameEng.Core.Climate`): сезонный синус по дню года + + суточные колебания (холоднее ночью). +- **Плодородность** — пока константа типа террейна (`TerrainDef.Fertility`); позже возможна + по-клеточная почва с истощением/восстановлением. + +## Гены (диплоидный геном, гибридная экспрессия) + +Каждое растение несёт **геном**: по паре аллелей на ген. Экспрессия гибридная: + +- **Числовые гены** (оптимум света, толерантность к свету, оптимум температуры, толерантность, + потребность в плодородности, макс. размер, базовая скорость, продолжительность жизни, дальность + расселения, плодовитость, темп мутаций, способность к самоопылению) — фенотип = **смешение** + (среднее двух аллелей). Реализм отбора: промежуточные значения, плавный дрейф. +- **Дискретные гены** (например, цвет/морфа, наличие признака) — **доминант/рецессив**: рецессивный + фенотип виден только в гомозиготе (aa). + +Наследование (мейоз): каждый родитель отдаёт по одному случайному аллелю на ген; потомок получает +по одному от каждого. Затем — мутация (с вероятностью из гена темпа мутаций сдвигаем числовой +аллель/переключаем дискретный). Всё детерминировано сидом мира. + +Стартовая популяция: геномы генерируются из сида (вид задаёт базовый геном и разброс аллелей в +дефе; особи отклоняются). + +## Размножение (полуреалистичное) + +- **Перекрёстное опыление (приоритет):** зрелое растение ищет зрелого соседа того же вида в радиусе + (ген дальности; запрос через spatial-hash движка). Есть партнёр → семя с геномом от двух + родителей (Мендель + мутация). +- **Самоопыление (запас):** нет партнёра и ген «способность к самоопылению» позволяет → семя из + одного родителя (само-скрещивание аллелей + мутация). +- **Расселение:** семя падает в клетку в радиусе расселения; прорастает, если клетка подходит + (суша, не занято сверх лимита, пригодность не нулевая). +- **Лимит плотности:** не больше N растений на клетку/площадь — иначе взрыв численности. +- **Старение/смерть:** возраст превышает ген жизни → смерть; или длительный стресс (пригодность + около нуля) → гибель. Смерть освобождает место — баланс с размножением и основа отбора. + +## Производительность и сейв + +- Представление — **по сущности на растение** (видно в рендере и ECS-инспекторе, индивидуальные + гены). Защита от взрыва: лимит плотности, смерть, разумные радиусы, проверки размножения по + таймеру/сезону (не каждый кадр на каждое растение), spatial-hash для соседей. +- **Персист:** геномы, позиции, возраст и стадия растений сохраняются в `WorldSave` (мир больше не + воспроизводится только из сида — эволюция переживает загрузку). +- **Детерминизм:** мутации/расселение/опыление — на сидированном `Random`, как в остальной симуляции. + +## Фазы реализации + +- **A — климат и день/ночь (движок).** `Climate` (температура/сезон поверх `Calendar`), + `DayNight` (амбиент, `Renderer2D.AmbientLight` на World-слоях, сэмплируемый `SampleAt`), + затемнение мира ночью, HUD: сезон/температура/время. Фундамент для влияния среды на рост. +- **B — гены и рост по пригодностям (игра).** Компонент `PlantGenome`, расчёт пригодностей + (свет из `DayNight.SampleAt`, температура из `Climate`, плодородность из террейна), замена + плоского `GrowthRate`. Геном виден/правится в инспекторе. +- **C — размножение и смерть (игра).** Перекрёстное/самоопыление, расселение, лимит плотности, + старение/стресс-смерть, персист популяции в сейв. +- **D — полноценное 2D-освещение (движок).** Точечные источники + тени (окклюзия от препятствий); + `DayNight.SampleAt` начинает возвращать локальную освещённость, и затенённые растения растут хуже. diff --git a/engine b/engine index f39ee9e..a684c23 160000 --- a/engine +++ b/engine @@ -1 +1 @@ -Subproject commit f39ee9e787b1e7a731ee3efdbfb812a12781bbd0 +Subproject commit a684c23cd96249dc686c4747a3bc52b2c6361421 diff --git a/src/LittleSim/Scenes/WorldScene.cs b/src/LittleSim/Scenes/WorldScene.cs index c67d286..f9b38c0 100644 --- a/src/LittleSim/Scenes/WorldScene.cs +++ b/src/LittleSim/Scenes/WorldScene.cs @@ -13,6 +13,7 @@ using MrGameEng.DevConsole; using MrGameEng.Graphics; using MrGameEng.Input; using MrGameEng.Inspector; +using MrGameEng.Lighting; using MrGameEng.Tilemaps; using MrGameEng.UI; using Myra.Graphics2D; @@ -74,6 +75,8 @@ public sealed class WorldScene : Scene this.UseTilemaps(); var calendar = Context.UseCalendar(SecondsPerDay); + var climate = Context.UseClimate(ClimateSettings.Default); + this.UseDayNight(renderer); // мир темнеет ночью — амбиент идёт в рендер // Рельеф и расстановка растений детерминированы сидом мира (независимые потоки seed). var plants = new PlantSet(content, atlases, device); @@ -105,7 +108,15 @@ public sealed class WorldScene : Scene new GodCameraSystem(camera, input, renderer, console, () => _pause.IsOpen) ); UpdateSystems.Add( - new HudSystem(Context, content.Languages, hudLabel, "hud.world", _config.Seed, calendar) + new HudSystem( + Context, + content.Languages, + hudLabel, + "hud.world", + _config.Seed, + calendar, + climate + ) ); UpdateSystems.Add(new CallbackSystem(() => Hotkeys(input))); diff --git a/src/LittleSim/Sim/SimSystems.cs b/src/LittleSim/Sim/SimSystems.cs index 76140cd..061c7b6 100644 --- a/src/LittleSim/Sim/SimSystems.cs +++ b/src/LittleSim/Sim/SimSystems.cs @@ -185,7 +185,8 @@ public sealed class HudSystem( Myra.Graphics2D.UI.Label label, string titleKey, int seed, - MrGameEng.Core.Calendar? calendar = null + MrGameEng.Core.Calendar? calendar = null, + MrGameEng.Core.Climate? climate = null ) : BaseSystem { private float _accumulated; @@ -206,6 +207,14 @@ public sealed class HudSystem( var when = calendar is null ? "" : languages.Format("hud.datetime", calendar.Day, calendar.Hour, calendar.Minute); + if (climate is not null) + { + var season = languages.Get("season." + climate.Season.ToString().ToLowerInvariant()); + when += + " | " + + languages.Format("hud.climate", season, (int)MathF.Round(climate.Temperature)); + } + label.Text = languages.Format(titleKey, seed, when, fps) + "\n" + languages.Get("hud.controls"); }