Enhance plant genetics system with cold and heat hardiness traits. Updated genes.json to include GeneColdHardiness and GeneHeatHardiness, and modified plant definitions to incorporate these traits. Adjusted temperature tolerance calculations in PlantPhenotype and PlantLifecycleSystem to account for temperature stress. Improved documentation and added temperature stress management in the lifecycle system.
This commit is contained in:
@@ -19,6 +19,15 @@
|
|||||||
{ "defName": "GeneTemperatureTolerance", "parent": "BaseNumericGene", "label": "gene.temperatureTolerance",
|
{ "defName": "GeneTemperatureTolerance", "parent": "BaseNumericGene", "label": "gene.temperatureTolerance",
|
||||||
"default": 16, "min": 2, "max": 40, "tags": ["environment", "temperature"],
|
"default": 16, "min": 2, "max": 40, "tags": ["environment", "temperature"],
|
||||||
"effects": { "temperatureTolerance": "value" } },
|
"effects": { "temperatureTolerance": "value" } },
|
||||||
|
// Жёсткие края диапазона роста (модель RimWorld): на сколько °C ниже/выше плато оптимума рост
|
||||||
|
// спадает до нуля. Ниже (optimal-tolerance-coldHardiness) или выше (optimal+tolerance+heatHardiness)
|
||||||
|
// растение дормантно и копит температурный стресс. Асимметрично: вид может терпеть жару лучше холода.
|
||||||
|
{ "defName": "GeneColdHardiness", "parent": "BaseNumericGene", "label": "gene.coldHardiness",
|
||||||
|
"default": 8, "min": 0, "max": 40, "tags": ["environment", "temperature"],
|
||||||
|
"effects": { "coldHardiness": "value" } },
|
||||||
|
{ "defName": "GeneHeatHardiness", "parent": "BaseNumericGene", "label": "gene.heatHardiness",
|
||||||
|
"default": 10, "min": 0, "max": 40, "tags": ["environment", "temperature"],
|
||||||
|
"effects": { "heatHardiness": "value" } },
|
||||||
{ "defName": "GeneOptimalFertility", "parent": "BaseNumericGene", "label": "gene.optimalFertility",
|
{ "defName": "GeneOptimalFertility", "parent": "BaseNumericGene", "label": "gene.optimalFertility",
|
||||||
"default": 1.4, "min": 0.1, "max": 3.0, "tags": ["environment", "soil"],
|
"default": 1.4, "min": 0.1, "max": 3.0, "tags": ["environment", "soil"],
|
||||||
"effects": { "optimalFertility": "value" } },
|
"effects": { "optimalFertility": "value" } },
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
"defs": [
|
"defs": [
|
||||||
{ "defName": "BaseTree", "abstract": true, "sizeCells": 2.0, "trunkRadiusCells": 0.28,
|
{ "defName": "BaseTree", "abstract": true, "sizeCells": 2.0, "trunkRadiusCells": 0.28,
|
||||||
"harvestProduct": "ProductWood", "fruitProduct": "ProductAcorn",
|
"harvestProduct": "ProductWood", "fruitProduct": "ProductAcorn",
|
||||||
"genome": { "optimalLight": 0.6, "lightTolerance": 0.5, "optimalTemperature": 14, "temperatureTolerance": 16,
|
"genome": { "optimalLight": 0.6, "lightTolerance": 0.5, "optimalTemperature": 14, "temperatureTolerance": 10,
|
||||||
|
"coldHardiness": 14, "heatHardiness": 12,
|
||||||
"optimalFertility": 1.4, "fertilityTolerance": 0.9, "vigor": 1.0,
|
"optimalFertility": 1.4, "fertilityTolerance": 0.9, "vigor": 1.0,
|
||||||
"lifespan": 160, "dispersalRange": 2, "reproduceInterval": 14, "selfPollination": 0.2,
|
"lifespan": 160, "dispersalRange": 2, "reproduceInterval": 14, "selfPollination": 0.2,
|
||||||
"mutationRate": 0.05, "variantChance": 0.15, "spread": 0.08,
|
"mutationRate": 0.05, "variantChance": 0.15, "spread": 0.08,
|
||||||
@@ -20,7 +21,8 @@
|
|||||||
|
|
||||||
{ "defName": "GrassA", "label": "plant.grass", "texture": "things/plant/grass/grassa", "sizeCells": 1.2,
|
{ "defName": "GrassA", "label": "plant.grass", "texture": "things/plant/grass/grassa", "sizeCells": 1.2,
|
||||||
"harvestProduct": "ProductGrass",
|
"harvestProduct": "ProductGrass",
|
||||||
"genome": { "optimalLight": 0.85, "lightTolerance": 0.35, "optimalTemperature": 20, "temperatureTolerance": 12,
|
"genome": { "optimalLight": 0.85, "lightTolerance": 0.35, "optimalTemperature": 22, "temperatureTolerance": 8,
|
||||||
|
"coldHardiness": 8, "heatHardiness": 6,
|
||||||
"optimalFertility": 1.0, "fertilityTolerance": 1.0, "vigor": 1.3,
|
"optimalFertility": 1.0, "fertilityTolerance": 1.0, "vigor": 1.3,
|
||||||
"lifespan": 22, "dispersalRange": 3, "reproduceInterval": 3, "selfPollination": 0.8,
|
"lifespan": 22, "dispersalRange": 3, "reproduceInterval": 3, "selfPollination": 0.8,
|
||||||
"mutationRate": 0.06, "variantChance": 0.2, "spread": 0.1,
|
"mutationRate": 0.06, "variantChance": 0.2, "spread": 0.1,
|
||||||
@@ -32,7 +34,8 @@
|
|||||||
|
|
||||||
{ "defName": "BaseBush", "abstract": true, "label": "plant.bush", "sizeCells": 1.4,
|
{ "defName": "BaseBush", "abstract": true, "label": "plant.bush", "sizeCells": 1.4,
|
||||||
"fruitProduct": "ProductBerry",
|
"fruitProduct": "ProductBerry",
|
||||||
"genome": { "optimalLight": 0.6, "lightTolerance": 0.5, "optimalTemperature": 17, "temperatureTolerance": 13,
|
"genome": { "optimalLight": 0.6, "lightTolerance": 0.5, "optimalTemperature": 17, "temperatureTolerance": 9,
|
||||||
|
"coldHardiness": 10, "heatHardiness": 8,
|
||||||
"optimalFertility": 1.1, "fertilityTolerance": 1.0, "vigor": 1.0,
|
"optimalFertility": 1.1, "fertilityTolerance": 1.0, "vigor": 1.0,
|
||||||
"lifespan": 60, "dispersalRange": 2, "reproduceInterval": 7, "selfPollination": 0.5,
|
"lifespan": 60, "dispersalRange": 2, "reproduceInterval": 7, "selfPollination": 0.5,
|
||||||
"mutationRate": 0.05, "variantChance": 0.18, "spread": 0.1,
|
"mutationRate": 0.05, "variantChance": 0.18, "spread": 0.1,
|
||||||
|
|||||||
@@ -93,9 +93,15 @@ public sealed class GenomeDef
|
|||||||
/// <summary>Оптимальная температура (°C) и её толерантность.</summary>
|
/// <summary>Оптимальная температура (°C) и её толерантность.</summary>
|
||||||
public float OptimalTemperature { get; init; } = 18f;
|
public float OptimalTemperature { get; init; } = 18f;
|
||||||
|
|
||||||
/// <summary>Ширина толерантности по температуре (°C).</summary>
|
/// <summary>Ширина толерантности по температуре (°C) — половина плато полного роста вокруг оптимума.</summary>
|
||||||
public float TemperatureTolerance { get; init; } = 12f;
|
public float TemperatureTolerance { get; init; } = 12f;
|
||||||
|
|
||||||
|
/// <summary>Морозостойкость (°C): на сколько ниже плато рост спадает до нуля (жёсткая нижняя граница).</summary>
|
||||||
|
public float ColdHardiness { get; init; } = 8f;
|
||||||
|
|
||||||
|
/// <summary>Жаростойкость (°C): на сколько выше плато рост спадает до нуля (жёсткая верхняя граница).</summary>
|
||||||
|
public float HeatHardiness { get; init; } = 10f;
|
||||||
|
|
||||||
/// <summary>Оптимальная плодородность почвы и её толерантность.</summary>
|
/// <summary>Оптимальная плодородность почвы и её толерантность.</summary>
|
||||||
public float OptimalFertility { get; init; } = 1f;
|
public float OptimalFertility { get; init; } = 1f;
|
||||||
|
|
||||||
|
|||||||
@@ -103,6 +103,8 @@ public sealed class PlantSet
|
|||||||
Numeric("GeneLightTolerance", g.LightTolerance),
|
Numeric("GeneLightTolerance", g.LightTolerance),
|
||||||
Numeric("GeneOptimalTemperature", g.OptimalTemperature),
|
Numeric("GeneOptimalTemperature", g.OptimalTemperature),
|
||||||
Numeric("GeneTemperatureTolerance", g.TemperatureTolerance),
|
Numeric("GeneTemperatureTolerance", g.TemperatureTolerance),
|
||||||
|
Numeric("GeneColdHardiness", g.ColdHardiness),
|
||||||
|
Numeric("GeneHeatHardiness", g.HeatHardiness),
|
||||||
Numeric("GeneOptimalFertility", g.OptimalFertility),
|
Numeric("GeneOptimalFertility", g.OptimalFertility),
|
||||||
Numeric("GeneFertilityTolerance", g.FertilityTolerance),
|
Numeric("GeneFertilityTolerance", g.FertilityTolerance),
|
||||||
Numeric("GeneVigor", g.Vigor),
|
Numeric("GeneVigor", g.Vigor),
|
||||||
|
|||||||
@@ -168,6 +168,7 @@ public sealed class WorldScene : Scene
|
|||||||
Store,
|
Store,
|
||||||
_plants,
|
_plants,
|
||||||
calendar,
|
calendar,
|
||||||
|
climate,
|
||||||
Context.Clock,
|
Context.Clock,
|
||||||
_config.Width,
|
_config.Width,
|
||||||
_config.Height,
|
_config.Height,
|
||||||
@@ -587,6 +588,11 @@ public sealed class WorldScene : Scene
|
|||||||
$" vigor {traits.Vigor:0.##}, lifespan {traits.Lifespan:0} d, "
|
$" vigor {traits.Vigor:0.##}, lifespan {traits.Lifespan:0} d, "
|
||||||
+ $"optimalLight {traits.OptimalLight:0.##}, leafHue {traits.LeafHue:0.##}"
|
+ $"optimalLight {traits.OptimalLight:0.##}, leafHue {traits.LeafHue:0.##}"
|
||||||
);
|
);
|
||||||
|
var (tMin, tLow, tHigh, tMax) = traits.TemperatureBand();
|
||||||
|
console.WriteLine(
|
||||||
|
$" temp: grows {tMin:0.#}..{tMax:0.#}°C, optimal {tLow:0.#}..{tHigh:0.#}°C "
|
||||||
|
+ $"(cold {traits.ColdHardiness:0.#}, heat {traits.HeatHardiness:0.#})"
|
||||||
|
);
|
||||||
|
|
||||||
if (def.HarvestProduct is { } harvest)
|
if (def.HarvestProduct is { } harvest)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -56,7 +56,8 @@ public static class PlantFactory
|
|||||||
CellFertility = cellFertility,
|
CellFertility = cellFertility,
|
||||||
},
|
},
|
||||||
new PlantOrganism { Genome = genome, Traits = traits },
|
new PlantOrganism { Genome = genome, Traits = traits },
|
||||||
new Fruiting()
|
new Fruiting(),
|
||||||
|
new TemperatureStress()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -53,14 +53,12 @@ public sealed class PlantGrowthSystem(
|
|||||||
|
|
||||||
ref var traits = ref orgs[i].Traits;
|
ref var traits = ref orgs[i].Traits;
|
||||||
var light = lighting.SampleAt(t[i].Position);
|
var light = lighting.SampleAt(t[i].Position);
|
||||||
|
// Температура — трапеция RimWorld: плато полного роста, жёсткие края (дормантность вне).
|
||||||
|
var (tMin, tLow, tHigh, tMax) = traits.TemperatureBand();
|
||||||
var rate =
|
var rate =
|
||||||
traits.Vigor
|
traits.Vigor
|
||||||
* Suitability.Gaussian(light, traits.OptimalLight, traits.LightTolerance)
|
* Suitability.Gaussian(light, traits.OptimalLight, traits.LightTolerance)
|
||||||
* Suitability.Gaussian(
|
* Suitability.Trapezoid(temperature, tMin, tLow, tHigh, tMax)
|
||||||
temperature,
|
|
||||||
traits.OptimalTemperature,
|
|
||||||
traits.TemperatureTolerance
|
|
||||||
)
|
|
||||||
* Suitability.Gaussian(
|
* Suitability.Gaussian(
|
||||||
grow.CellFertility,
|
grow.CellFertility,
|
||||||
traits.OptimalFertility,
|
traits.OptimalFertility,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using Friflo.Engine.ECS;
|
|||||||
using Friflo.Engine.ECS.Systems;
|
using Friflo.Engine.ECS.Systems;
|
||||||
using LittleSim.Content;
|
using LittleSim.Content;
|
||||||
using Microsoft.Xna.Framework;
|
using Microsoft.Xna.Framework;
|
||||||
|
using MrGameEng.AI;
|
||||||
using MrGameEng.Core;
|
using MrGameEng.Core;
|
||||||
using MrGameEng.Genetics;
|
using MrGameEng.Genetics;
|
||||||
using MrGameEng.Graphics;
|
using MrGameEng.Graphics;
|
||||||
@@ -9,21 +10,30 @@ using MrGameEng.Graphics;
|
|||||||
namespace LittleSim.Sim;
|
namespace LittleSim.Sim;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Жизненный цикл растений: размножение (наследование по Менделю) и смерть от старости. Запускается
|
/// Жизненный цикл растений: размножение (наследование по Менделю), смерть от старости и от
|
||||||
/// не каждый кадр, а раз в <see cref="IntervalDays"/> игрового дня. За тик: строит по-клеточную сетку
|
/// температурного стресса (вымерзание/перегрев). Запускается не каждый кадр, а раз в
|
||||||
/// (счётчик плотности + «представитель» вида/генома), собирает умерших по возрасту, затем зрелые с
|
/// <see cref="IntervalDays"/> игрового дня. За тик: строит по-клеточную сетку (счётчик плотности +
|
||||||
/// вероятностью <c>elapsed/ReproduceInterval</c> сеют потомка — приоритет перекрёстного опыления
|
/// «представитель» вида/генома), копит/рассасывает температурный стресс по текущей температуре,
|
||||||
/// (зрелый сосед того же вида в радиусе расселения), иначе самоопыление по гену. Семя падает в
|
/// собирает умерших (старость или превышенный стресс), затем зрелые с вероятностью
|
||||||
/// случайную клетку радиуса, если это суша и не превышен лимит плотности. Структурные изменения
|
/// <c>elapsed/ReproduceInterval</c> сеют потомка — приоритет перекрёстного опыления (зрелый сосед
|
||||||
/// (создание/удаление сущностей) применяются после проходов по запросу — это безопасно.
|
/// того же вида в радиусе расселения), иначе самоопыление по гену. Семя падает в случайную клетку
|
||||||
|
/// радиуса, если это суша и не превышен лимит плотности. Структурные изменения (создание/удаление
|
||||||
|
/// сущностей) применяются после проходов по запросу — это безопасно.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class PlantLifecycleSystem : BaseSystem
|
public sealed class PlantLifecycleSystem : BaseSystem
|
||||||
{
|
{
|
||||||
private const float IntervalDays = 0.25f;
|
private const float IntervalDays = 0.25f;
|
||||||
|
|
||||||
|
/// <summary>Сколько игровых дней дормантности вне диапазона роста убивают растение.</summary>
|
||||||
|
private const float LethalStressDays = 6f;
|
||||||
|
|
||||||
|
/// <summary>Во сколько раз стресс рассасывается быстрее в пригодной температуре, чем копится вне.</summary>
|
||||||
|
private const float StressRecovery = 1.5f;
|
||||||
|
|
||||||
private readonly EntityStore _store;
|
private readonly EntityStore _store;
|
||||||
private readonly PlantSet _plants;
|
private readonly PlantSet _plants;
|
||||||
private readonly Calendar _calendar;
|
private readonly Calendar _calendar;
|
||||||
|
private readonly Climate _climate;
|
||||||
private readonly GameClock _clock;
|
private readonly GameClock _clock;
|
||||||
private readonly int _width;
|
private readonly int _width;
|
||||||
private readonly int _height;
|
private readonly int _height;
|
||||||
@@ -32,7 +42,12 @@ public sealed class PlantLifecycleSystem : BaseSystem
|
|||||||
private readonly bool[] _cellLand;
|
private readonly bool[] _cellLand;
|
||||||
private readonly int _densityCap;
|
private readonly int _densityCap;
|
||||||
private readonly Random _rng;
|
private readonly Random _rng;
|
||||||
private readonly ArchetypeQuery<PlantGrowth, PlantOrganism, Transform2D> _query;
|
private readonly ArchetypeQuery<
|
||||||
|
PlantGrowth,
|
||||||
|
PlantOrganism,
|
||||||
|
Transform2D,
|
||||||
|
TemperatureStress
|
||||||
|
> _query;
|
||||||
|
|
||||||
private readonly int[] _count;
|
private readonly int[] _count;
|
||||||
private readonly CellRep[] _rep;
|
private readonly CellRep[] _rep;
|
||||||
@@ -45,6 +60,7 @@ public sealed class PlantLifecycleSystem : BaseSystem
|
|||||||
EntityStore store,
|
EntityStore store,
|
||||||
PlantSet plants,
|
PlantSet plants,
|
||||||
Calendar calendar,
|
Calendar calendar,
|
||||||
|
Climate climate,
|
||||||
GameClock clock,
|
GameClock clock,
|
||||||
int width,
|
int width,
|
||||||
int height,
|
int height,
|
||||||
@@ -58,6 +74,7 @@ public sealed class PlantLifecycleSystem : BaseSystem
|
|||||||
_store = store;
|
_store = store;
|
||||||
_plants = plants;
|
_plants = plants;
|
||||||
_calendar = calendar;
|
_calendar = calendar;
|
||||||
|
_climate = climate;
|
||||||
_clock = clock;
|
_clock = clock;
|
||||||
_width = width;
|
_width = width;
|
||||||
_height = height;
|
_height = height;
|
||||||
@@ -66,7 +83,7 @@ public sealed class PlantLifecycleSystem : BaseSystem
|
|||||||
_cellLand = cellLand;
|
_cellLand = cellLand;
|
||||||
_densityCap = densityCap;
|
_densityCap = densityCap;
|
||||||
_rng = new Random(seed);
|
_rng = new Random(seed);
|
||||||
_query = store.Query<PlantGrowth, PlantOrganism, Transform2D>();
|
_query = store.Query<PlantGrowth, PlantOrganism, Transform2D, TemperatureStress>();
|
||||||
_count = new int[width * height];
|
_count = new int[width * height];
|
||||||
_rep = new CellRep[width * height];
|
_rep = new CellRep[width * height];
|
||||||
}
|
}
|
||||||
@@ -82,13 +99,13 @@ public sealed class PlantLifecycleSystem : BaseSystem
|
|||||||
var elapsed = _accumulator;
|
var elapsed = _accumulator;
|
||||||
_accumulator = 0f;
|
_accumulator = 0f;
|
||||||
|
|
||||||
BuildGridAndCollect();
|
BuildGridAndCollect(elapsed);
|
||||||
Reproduce(elapsed);
|
Reproduce(elapsed);
|
||||||
Apply();
|
Apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Проход 1: сетка плотности + представители, сбор умерших и зрелых-кандидатов.
|
// Проход 1: сетка плотности + представители, температурный стресс, сбор умерших и зрелых-кандидатов.
|
||||||
private void BuildGridAndCollect()
|
private void BuildGridAndCollect(float elapsed)
|
||||||
{
|
{
|
||||||
Array.Clear(_count);
|
Array.Clear(_count);
|
||||||
for (var c = 0; c < _rep.Length; c++)
|
for (var c = 0; c < _rep.Length; c++)
|
||||||
@@ -100,11 +117,14 @@ public sealed class PlantLifecycleSystem : BaseSystem
|
|||||||
_candidates.Clear();
|
_candidates.Clear();
|
||||||
_births.Clear();
|
_births.Clear();
|
||||||
|
|
||||||
foreach (var (growths, organisms, transforms, entities) in _query.Chunks)
|
var temperature = _climate.Temperature; // глобальная — едина для всех растений в этот тик
|
||||||
|
|
||||||
|
foreach (var (growths, organisms, transforms, stresses, entities) in _query.Chunks)
|
||||||
{
|
{
|
||||||
var g = growths.Span;
|
var g = growths.Span;
|
||||||
var orgs = organisms.Span;
|
var orgs = organisms.Span;
|
||||||
var t = transforms.Span;
|
var t = transforms.Span;
|
||||||
|
var st = stresses.Span;
|
||||||
for (var i = 0; i < g.Length; i++)
|
for (var i = 0; i < g.Length; i++)
|
||||||
{
|
{
|
||||||
ref var grow = ref g[i];
|
ref var grow = ref g[i];
|
||||||
@@ -115,6 +135,23 @@ public sealed class PlantLifecycleSystem : BaseSystem
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Температурный стресс: вне диапазона роста (трапеция = 0) копится, внутри — рассасывается.
|
||||||
|
var (tMin, tLow, tHigh, tMax) = org.Traits.TemperatureBand();
|
||||||
|
ref var stress = ref st[i].Days;
|
||||||
|
if (Suitability.Trapezoid(temperature, tMin, tLow, tHigh, tMax) <= 0f)
|
||||||
|
{
|
||||||
|
stress += elapsed;
|
||||||
|
if (stress >= LethalStressDays)
|
||||||
|
{
|
||||||
|
_deaths.Add(entities.EntityAt(i)); // вымерзло/перегрелось
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
stress = MathF.Max(0f, stress - elapsed * StressRecovery);
|
||||||
|
}
|
||||||
|
|
||||||
var pos = t[i].Position;
|
var pos = t[i].Position;
|
||||||
var cx = Math.Clamp((int)(pos.X / _cellSize), 0, _width - 1);
|
var cx = Math.Clamp((int)(pos.X / _cellSize), 0, _width - 1);
|
||||||
var cy = Math.Clamp((int)(pos.Y / _cellSize), 0, _height - 1);
|
var cy = Math.Clamp((int)(pos.Y / _cellSize), 0, _height - 1);
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ public struct PlantPhenotype
|
|||||||
public float LightTolerance;
|
public float LightTolerance;
|
||||||
public float OptimalTemperature;
|
public float OptimalTemperature;
|
||||||
public float TemperatureTolerance;
|
public float TemperatureTolerance;
|
||||||
|
public float ColdHardiness;
|
||||||
|
public float HeatHardiness;
|
||||||
public float OptimalFertility;
|
public float OptimalFertility;
|
||||||
public float FertilityTolerance;
|
public float FertilityTolerance;
|
||||||
public float Vigor;
|
public float Vigor;
|
||||||
@@ -33,6 +35,18 @@ public struct PlantPhenotype
|
|||||||
/// <summary>Морфа: рецессивный вариант (выраженное значение гена морфы ≈ 1) — другой тинт спрайта.</summary>
|
/// <summary>Морфа: рецессивный вариант (выраженное значение гена морфы ≈ 1) — другой тинт спрайта.</summary>
|
||||||
public bool IsVariant;
|
public bool IsVariant;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Четыре точки температурной трапеции роста (модель RimWorld): жёсткий минимум, нижний край плато,
|
||||||
|
/// верхний край плато, жёсткий максимум. Плато — оптимум ± толерантность; жёсткие края отстоят от
|
||||||
|
/// плато на морозо-/жаростойкость. Кормит <see cref="MrGameEng.AI.Suitability.Trapezoid"/>.
|
||||||
|
/// </summary>
|
||||||
|
public readonly (float Min, float OptimalLow, float OptimalHigh, float Max) TemperatureBand()
|
||||||
|
{
|
||||||
|
var low = OptimalTemperature - TemperatureTolerance;
|
||||||
|
var high = OptimalTemperature + TemperatureTolerance;
|
||||||
|
return (low - ColdHardiness, low, high, high + HeatHardiness);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Собирает фенотип из карты признаков, посчитанной <see cref="Phenotype.Compute"/>.</summary>
|
/// <summary>Собирает фенотип из карты признаков, посчитанной <see cref="Phenotype.Compute"/>.</summary>
|
||||||
public static PlantPhenotype FromTraits(IReadOnlyDictionary<string, float> traits)
|
public static PlantPhenotype FromTraits(IReadOnlyDictionary<string, float> traits)
|
||||||
{
|
{
|
||||||
@@ -43,6 +57,8 @@ public struct PlantPhenotype
|
|||||||
LightTolerance = T("lightTolerance"),
|
LightTolerance = T("lightTolerance"),
|
||||||
OptimalTemperature = T("optimalTemperature"),
|
OptimalTemperature = T("optimalTemperature"),
|
||||||
TemperatureTolerance = T("temperatureTolerance"),
|
TemperatureTolerance = T("temperatureTolerance"),
|
||||||
|
ColdHardiness = T("coldHardiness"),
|
||||||
|
HeatHardiness = T("heatHardiness"),
|
||||||
OptimalFertility = T("optimalFertility"),
|
OptimalFertility = T("optimalFertility"),
|
||||||
FertilityTolerance = T("fertilityTolerance"),
|
FertilityTolerance = T("fertilityTolerance"),
|
||||||
Vigor = T("vigor"),
|
Vigor = T("vigor"),
|
||||||
@@ -85,3 +101,15 @@ public struct Fruiting : IComponent
|
|||||||
/// <summary>Накоплено зрелых плодов (0..FruitYield).</summary>
|
/// <summary>Накоплено зрелых плодов (0..FruitYield).</summary>
|
||||||
public float RipeFruit;
|
public float RipeFruit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Накопленный температурный стресс растения в игровых днях. Пока температура вне диапазона роста
|
||||||
|
/// (трапеция <see cref="PlantPhenotype.TemperatureBand"/> даёт ноль — слишком холодно или жарко),
|
||||||
|
/// стресс копится; внутри диапазона — рассасывается. Превышение порога убивает растение
|
||||||
|
/// (вымерзание/перегрев). Управляется <c>PlantLifecycleSystem</c>.
|
||||||
|
/// </summary>
|
||||||
|
public struct TemperatureStress : IComponent
|
||||||
|
{
|
||||||
|
/// <summary>Накоплено дней дормантности вне диапазона роста; при превышении лимита — гибель.</summary>
|
||||||
|
public float Days;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user