Birth type gene (live-bearing vs egg-laying) + hybrid-creation foundation
GeneEggLaying (species-fixed) selects the conception cycle: viviparous animals carry to a live birth (existing path); oviparous animals, after the same gravid period, LAY eggs instead. New Egg entity (carries the already-bred offspring genome) + EggSystem incubates and hatches it into a baby. AnimalPregnancySystem branches on the mother's birth type. Adds an oviparous showcase species, Chicken. Eggs are serialized (WorldSave.Eggs) like corpses, so clutches survive save/load. Also lays the foundation for creating any species or hybrid (full command/UI deferred): AnimalSet.HybridGenome(base, other) produces a cross anchored to a base species' body/gene-set with shared genes blended Mendelian-style — so Create(species, GenerateGenome) makes any species and Create(species, HybridGenome) makes any hybrid. EggTexture on AnimalDef (defaults to the seed dot placeholder); reproduction line added to the `animal` console command. Build + --check-content clean (41 genes, 40 traits). Not GUI-verified. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
e6b1367a3a
commit
b2e3eb490e
@@ -101,6 +101,36 @@
|
||||
"GeneOmnivory": 0.85,
|
||||
"GeneToxinTolerance": 0.25
|
||||
}
|
||||
},
|
||||
|
||||
{
|
||||
"defName": "Chicken",
|
||||
"label": "pawn.chicken",
|
||||
"kind": "animal",
|
||||
"texture": "things/pawn/animal/chicken/Chicken_east",
|
||||
"body": "Quadruped",
|
||||
"diet": ["plant"],
|
||||
"spawnPer1000Cells": 1.5,
|
||||
"baseSpeed": 22,
|
||||
"visionCells": 10,
|
||||
"genome": {
|
||||
"GeneMaxBodySize": 0.5,
|
||||
"GeneMetabolism": 1.3,
|
||||
"GeneMoveSpeed": 0.9,
|
||||
"GeneBloodVolume": 0.7,
|
||||
"GeneVision": 1.1,
|
||||
"GeneBrainSize": 0.3,
|
||||
"GeneInsulation": 0.45,
|
||||
"GeneFurColor": 0.6,
|
||||
"GeneMaturityAge": 50,
|
||||
"GeneLifespan": 180,
|
||||
"GeneBreedingSeason": 1,
|
||||
"GeneGestationDays": 12,
|
||||
"GeneLitterSize": 6,
|
||||
"GeneHerbivory": 0.8,
|
||||
"GeneToxinTolerance": 0.15,
|
||||
"GeneEggLaying": 1.0
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -59,6 +59,13 @@
|
||||
{ "defName": "GeneSex", "kind": "Discrete", "label": "gene.sex",
|
||||
"variants": 2, "variantWeights": [0.5, 0.5], "mutationChance": 0, "tags": ["animal", "sex"] },
|
||||
|
||||
// Тип рождения (цикл зачатия): живородящие (eggLaying < 0.5) вынашивают и рожают живых детёнышей;
|
||||
// яйцекладущие (≥ 0.5) после вынашивания ОТКЛАДЫВАЮТ яйца-сущности, которые инкубируются и
|
||||
// вылупляются (см. AnimalPregnancySystem/EggSystem). Признак фиксирован по виду (spread/mutation 0).
|
||||
{ "defName": "GeneEggLaying", "parent": "BaseNumericGene", "label": "gene.eggLaying",
|
||||
"default": 0.0, "min": 0.0, "max": 1.0, "spread": 0, "mutationChance": 0, "tags": ["animal", "reproduction"],
|
||||
"effects": { "eggLaying": "value" } },
|
||||
|
||||
// Размножение (фаза A4): сезон гона фиксирован по виду (0 весна … 3 зима), срок вынашивания и помёт.
|
||||
{ "defName": "GeneBreedingSeason", "parent": "BaseNumericGene", "label": "gene.breedingSeason",
|
||||
"default": 2, "min": 0, "max": 3, "spread": 0, "mutationChance": 0, "tags": ["animal", "reproduction"],
|
||||
|
||||
@@ -163,6 +163,7 @@
|
||||
"pawn.hare": "hare",
|
||||
"pawn.boar": "boar",
|
||||
"pawn.wolf": "wolf",
|
||||
"pawn.chicken": "chicken",
|
||||
"pawn.muffalo": "muffalo",
|
||||
"pawn.squirrel": "squirrel",
|
||||
"thought.gaveBirth": "gave birth",
|
||||
|
||||
@@ -163,6 +163,7 @@
|
||||
"pawn.hare": "заяц",
|
||||
"pawn.boar": "кабан",
|
||||
"pawn.wolf": "волк",
|
||||
"pawn.chicken": "курица",
|
||||
"pawn.muffalo": "муффало",
|
||||
"pawn.squirrel": "белка",
|
||||
"thought.gaveBirth": "родила потомство",
|
||||
|
||||
@@ -173,6 +173,28 @@ public sealed class CorpseSave
|
||||
public float Meat { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>Сериализуемое яйцо (тип рождения «яйцекладка»): вид, позиция, геном детёныша, поколение, инкубация.</summary>
|
||||
public sealed class EggSave
|
||||
{
|
||||
/// <summary>Имя дефа вида — по нему берётся индекс/спрайт/тело при вылуплении.</summary>
|
||||
public string Species { get; set; } = "";
|
||||
|
||||
/// <summary>Позиция X в мировых координатах.</summary>
|
||||
public float X { get; set; }
|
||||
|
||||
/// <summary>Позиция Y в мировых координатах.</summary>
|
||||
public float Y { get; set; }
|
||||
|
||||
/// <summary>Поколение будущего детёныша.</summary>
|
||||
public int Generation { get; set; }
|
||||
|
||||
/// <summary>Остаток инкубации в игровых днях.</summary>
|
||||
public float IncubateDays { get; set; }
|
||||
|
||||
/// <summary>Геном будущего детёныша (уже скрещён на момент кладки).</summary>
|
||||
public Dictionary<string, Allele> Genome { get; set; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Полное состояние мира для сохранения/загрузки: конфиг мира (имя/размер/сид/сглаживание),
|
||||
/// прошедшее время симуляции и снимок всех жителей. Рельеф не сохраняется — он
|
||||
@@ -216,6 +238,9 @@ public sealed class WorldSave
|
||||
/// <summary>Снимок трупов (вид, позиция, таймер/стадия разложения, мясо).</summary>
|
||||
public List<CorpseSave> Corpses { get; set; } = [];
|
||||
|
||||
/// <summary>Снимок яиц (вид, позиция, геном детёныша, поколение, инкубация).</summary>
|
||||
public List<EggSave> Eggs { get; set; } = [];
|
||||
|
||||
/// <summary>Конфиг мира для пересоздания сцены.</summary>
|
||||
public WorldConfig ToConfig() =>
|
||||
new()
|
||||
|
||||
@@ -30,6 +30,9 @@ public sealed class AnimalSet
|
||||
/// <summary>Спрайт трупа (fallback — самка).</summary>
|
||||
public required Texture2DRegion Corpse { get; init; }
|
||||
|
||||
/// <summary>Спрайт яйца (тип рождения «яйцекладка»); по умолчанию — общий спрайт-точка.</summary>
|
||||
public required Texture2DRegion Egg { get; init; }
|
||||
|
||||
/// <summary>Стадии роста (из дефа или встроенный дефолт), по возрастанию возраста входа.</summary>
|
||||
public required AnimalStageDef[] Stages { get; init; }
|
||||
|
||||
@@ -43,10 +46,40 @@ public sealed class AnimalSet
|
||||
// Встроенный набор стадий по умолчанию (если вид не задал свой): как было до выноса в данные.
|
||||
private static readonly AnimalStageDef[] DefaultStages =
|
||||
[
|
||||
new() { Name = "Baby", EnterAt = 0f, RelativeTo = "maturity", Scale = 0.45f, Texture = "baby" },
|
||||
new() { Name = "Juvenile", EnterAt = 0.22f, RelativeTo = "maturity", Scale = 0.7f, Texture = "baby" },
|
||||
new() { Name = "Adult", EnterAt = 1f, RelativeTo = "maturity", Scale = 1f, Texture = "adult", Adult = true },
|
||||
new() { Name = "Senior", EnterAt = 0.8f, RelativeTo = "lifespan", Scale = 0.92f, Texture = "adult", Adult = true },
|
||||
new()
|
||||
{
|
||||
Name = "Baby",
|
||||
EnterAt = 0f,
|
||||
RelativeTo = "maturity",
|
||||
Scale = 0.45f,
|
||||
Texture = "baby",
|
||||
},
|
||||
new()
|
||||
{
|
||||
Name = "Juvenile",
|
||||
EnterAt = 0.22f,
|
||||
RelativeTo = "maturity",
|
||||
Scale = 0.7f,
|
||||
Texture = "baby",
|
||||
},
|
||||
new()
|
||||
{
|
||||
Name = "Adult",
|
||||
EnterAt = 1f,
|
||||
RelativeTo = "maturity",
|
||||
Scale = 1f,
|
||||
Texture = "adult",
|
||||
Adult = true,
|
||||
},
|
||||
new()
|
||||
{
|
||||
Name = "Senior",
|
||||
EnterAt = 0.8f,
|
||||
RelativeTo = "lifespan",
|
||||
Scale = 0.92f,
|
||||
Texture = "adult",
|
||||
Adult = true,
|
||||
},
|
||||
];
|
||||
|
||||
private readonly Species[] _species;
|
||||
@@ -82,6 +115,9 @@ public sealed class AnimalSet
|
||||
Corpse = string.IsNullOrEmpty(def.CorpseTexture)
|
||||
? female
|
||||
: atlases.GetRegion(device, def.CorpseTexture),
|
||||
Egg = string.IsNullOrEmpty(def.EggTexture)
|
||||
? female
|
||||
: atlases.GetRegion(device, def.EggTexture),
|
||||
Stages = def.Stages.Length > 0 ? def.Stages : DefaultStages,
|
||||
Body = content.Defs.TryGet<BodyDef>(def.Body, out var body) ? body : null,
|
||||
Template = BuildTemplate(def.Genome, genes),
|
||||
@@ -111,6 +147,35 @@ public sealed class AnimalSet
|
||||
return genome;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// ОСНОВА создания гибрида (полноценная команда/UI — позже): геном помеси, привязанный к базовому виду
|
||||
/// <paramref name="baseSpecies"/> (его тело/спрайт/набор генов), но с общими генами, скрещёнными с
|
||||
/// <paramref name="otherSpecies"/> (по одной аллели-гамете от каждого родителя, как при размножении).
|
||||
/// Пол берётся от базового вида; гены, которых нет у базового, игнорируются (его тело их не читает).
|
||||
/// Сущность создаётся обычной <see cref="Sim.AnimalFactory.Create"/> с этим геномом и базовым видом —
|
||||
/// то есть «создать любой вид» = Create(вид, GenerateGenome), «любой гибрид» = Create(вид, HybridGenome).
|
||||
/// </summary>
|
||||
public Genome HybridGenome(int baseSpecies, int otherSpecies, Random random)
|
||||
{
|
||||
var hybrid = GenerateGenome(baseSpecies, random);
|
||||
var other = _species[otherSpecies].Template.Generate(random);
|
||||
foreach (var (geneId, allele) in other.ToDictionary())
|
||||
{
|
||||
if (string.Equals(geneId, "GeneSex", StringComparison.Ordinal) || !hybrid.Has(geneId))
|
||||
{
|
||||
continue; // пол — от базового вида; чужие гены базовое тело не читает
|
||||
}
|
||||
|
||||
var mine = hybrid[geneId];
|
||||
hybrid[geneId] = new Allele(
|
||||
random.NextSingle() < 0.5f ? mine.A : mine.B,
|
||||
random.NextSingle() < 0.5f ? allele.A : allele.B
|
||||
);
|
||||
}
|
||||
|
||||
return hybrid;
|
||||
}
|
||||
|
||||
// Строит шаблон генома вида из ДАННЫХ (geneId→base из Defs/Animals/) generically: разброс берётся из
|
||||
// самого гена. Состав открыт — модер добавляет ген строкой JSON, без правки кода. Дискретные гены
|
||||
// (база/разброс не нужны — варианты из GeneDef) тоже поддержаны.
|
||||
|
||||
@@ -261,6 +261,9 @@ public sealed class AnimalDef : PawnDef
|
||||
/// <summary>Текстура трупа (фаза A5); пусто — берётся <see cref="PawnDef.Texture"/>.</summary>
|
||||
public string CorpseTexture { get; init; } = "";
|
||||
|
||||
/// <summary>Текстура яйца (тип рождения «яйцекладка»); по умолчанию — общий спрайт семени-точки.</summary>
|
||||
public string EggTexture { get; init; } = "things/plant/seed_default";
|
||||
|
||||
/// <summary>Имя <see cref="BodyDef"/> — анатомия вида (части тела/органы); пусто — без частей тела.</summary>
|
||||
public string Body { get; init; } = "";
|
||||
|
||||
|
||||
@@ -294,6 +294,17 @@ public sealed class WorldScene : Scene
|
||||
_config.Seed + 0x4BED
|
||||
)
|
||||
);
|
||||
UpdateSystems.Add(
|
||||
new EggSystem(
|
||||
Store,
|
||||
_animals,
|
||||
_needs,
|
||||
Context.Clock,
|
||||
SecondsPerDay,
|
||||
CellSize,
|
||||
_config.Seed + 0x6E66
|
||||
)
|
||||
);
|
||||
|
||||
UpdateSystems.Add(
|
||||
new GodCameraSystem(camera, input, renderer, console, () => _pause.IsOpen)
|
||||
@@ -519,7 +530,7 @@ public sealed class WorldScene : Scene
|
||||
new SaveStore().Write(save);
|
||||
Log.Info(
|
||||
$"World '{_config.Name}' saved ({save.Plants.Count} plants, "
|
||||
+ $"{save.Animals.Count} animals, {save.Corpses.Count} corpses)"
|
||||
+ $"{save.Animals.Count} animals, {save.Corpses.Count} corpses, {save.Eggs.Count} eggs)"
|
||||
);
|
||||
return _config.Name;
|
||||
}
|
||||
@@ -614,6 +625,23 @@ public sealed class WorldScene : Scene
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
Store
|
||||
.Query<Egg, Transform2D>()
|
||||
.ForEachEntity(
|
||||
(ref Egg egg, ref Transform2D transform, Entity _) =>
|
||||
save.Eggs.Add(
|
||||
new EggSave
|
||||
{
|
||||
Species = _animals[egg.Species].Def.DefName,
|
||||
X = transform.Position.X,
|
||||
Y = transform.Position.Y,
|
||||
Generation = egg.Generation,
|
||||
IncubateDays = egg.IncubateDays,
|
||||
Genome = egg.Genome?.ToDictionary() ?? new(),
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private void RestorePlants(GameContent content)
|
||||
@@ -695,8 +723,31 @@ public sealed class WorldScene : Scene
|
||||
data.Meat = saved.Meat;
|
||||
}
|
||||
|
||||
foreach (var saved in _save.Eggs)
|
||||
{
|
||||
if (!content.Defs.TryGet<AnimalDef>(saved.Species, out var def))
|
||||
{
|
||||
continue; // вид пропал (мод убрали) — пропускаем яйцо
|
||||
}
|
||||
|
||||
var index = _animals.IndexOf(def);
|
||||
var genome = saved.Genome is { Count: > 0 }
|
||||
? new Genome(saved.Genome)
|
||||
: _animals.GenerateGenome(index, fallback);
|
||||
AnimalFactory.CreateEgg(
|
||||
Store,
|
||||
_animals,
|
||||
index,
|
||||
new Vector2(saved.X, saved.Y),
|
||||
genome,
|
||||
saved.Generation,
|
||||
saved.IncubateDays,
|
||||
CellSize
|
||||
);
|
||||
}
|
||||
|
||||
Log.Info(
|
||||
$"Restored {_save.Animals.Count} animals, {_save.Corpses.Count} corpses from save"
|
||||
$"Restored {_save.Animals.Count} animals, {_save.Corpses.Count} corpses, {_save.Eggs.Count} eggs from save"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1136,6 +1187,10 @@ public sealed class WorldScene : Scene
|
||||
$" diet: herb {traits.Herbivory:0.##} / carn {traits.Carnivory:0.##} / omni {traits.Omnivory:0.##}, "
|
||||
+ $"toxinTolerance {traits.ToxinTolerance:0.##}"
|
||||
);
|
||||
console.WriteLine(
|
||||
$" reproduction: {(traits.Oviparous ? "oviparous (lays eggs)" : "viviparous (live birth)")}, "
|
||||
+ $"gestation {traits.GestationDays:0} d, litter {traits.LitterSize:0.#}"
|
||||
);
|
||||
}
|
||||
|
||||
// Диета из генов (предатор-кластер): по каждому виду печатает herbivory/carnivory/omnivory,
|
||||
|
||||
@@ -110,6 +110,46 @@ public static class AnimalFactory
|
||||
);
|
||||
}
|
||||
|
||||
// Тинт яйца (бледно-кремовый) и доля размера клетки, до которой масштабируется спрайт яйца.
|
||||
private static readonly Color EggTint = new(238, 230, 208);
|
||||
private const float EggSizeFraction = 0.45f;
|
||||
|
||||
/// <summary>
|
||||
/// Создаёт сущность-яйцо (тип рождения «яйцекладка»): спрайт-яйцо и компонент <see cref="Egg"/> с уже
|
||||
/// скрещённым геномом будущего детёныша. Инкубируется и вылупляется в <see cref="EggSystem"/>.
|
||||
/// </summary>
|
||||
public static Entity CreateEgg(
|
||||
EntityStore store,
|
||||
AnimalSet animals,
|
||||
int species,
|
||||
Vector2 position,
|
||||
Genome genome,
|
||||
int generation,
|
||||
float incubateDays,
|
||||
int cellSize
|
||||
)
|
||||
{
|
||||
var sp = animals[species];
|
||||
var region = sp.Egg;
|
||||
var sprite = new Sprite(region, GameLayers.Beings);
|
||||
sprite.CenterOrigin();
|
||||
sprite.Color = EggTint;
|
||||
return store.CreateEntity(
|
||||
new Transform2D(
|
||||
position,
|
||||
scale: new Vector2(cellSize * EggSizeFraction / region.Width)
|
||||
),
|
||||
sprite,
|
||||
new Egg
|
||||
{
|
||||
Species = species,
|
||||
Genome = genome,
|
||||
Generation = generation,
|
||||
IncubateDays = incubateDays,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>Порог выраженности признака диеты, с которого вид реально ест данный корм.</summary>
|
||||
public const float DietThreshold = 0.5f;
|
||||
|
||||
|
||||
@@ -63,6 +63,12 @@ public struct AnimalPhenotype
|
||||
/// <summary>Размер помёта.</summary>
|
||||
public float LitterSize;
|
||||
|
||||
/// <summary>Тип рождения [0..1]: ≥0.5 — яйцекладущий (откладывает яйца), иначе живородящий.</summary>
|
||||
public float EggLaying;
|
||||
|
||||
/// <summary>Яйцекладущий ли вид (тип рождения) — определяет цикл зачатия.</summary>
|
||||
public readonly bool Oviparous => EggLaying >= 0.5f;
|
||||
|
||||
/// <summary>Собирает фенотип из карты признаков, посчитанной <see cref="Phenotype.Compute"/>.</summary>
|
||||
public static AnimalPhenotype FromTraits(IReadOnlyDictionary<string, float> traits)
|
||||
{
|
||||
@@ -86,6 +92,7 @@ public struct AnimalPhenotype
|
||||
BreedingSeason = (int)MathF.Round(Math.Clamp(T("breedingSeason"), 0f, 3f)),
|
||||
GestationDays = T("gestationDays"),
|
||||
LitterSize = T("litterSize"),
|
||||
EggLaying = T("eggLaying"),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -457,6 +464,26 @@ public struct Corpse : IComponent
|
||||
public float Meat;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Яйцо (тип рождения «яйцекладка»): отдельная сущность, отложенная яйцекладущей самкой вместо живых
|
||||
/// родов. Несёт уже скрещённый геном будущего детёныша, вид и поколение; инкубируется во времени и
|
||||
/// вылупляется в детёныша (<see cref="Sim.EggSystem"/>). Геном — managed-ссылка (как у трупа/беременности).
|
||||
/// </summary>
|
||||
public struct Egg : IComponent
|
||||
{
|
||||
/// <summary>Индекс вида в <see cref="Content.AnimalSet"/> (для спрайта при вылуплении и сейва).</summary>
|
||||
public int Species;
|
||||
|
||||
/// <summary>Геном будущего детёныша (уже скрещён из родителей на момент кладки).</summary>
|
||||
public Genome Genome;
|
||||
|
||||
/// <summary>Поколение будущего детёныша.</summary>
|
||||
public int Generation;
|
||||
|
||||
/// <summary>Остаток инкубации в игровых днях; ≤0 — вылупление.</summary>
|
||||
public float IncubateDays;
|
||||
}
|
||||
|
||||
/// <summary>Инстанс мысли настроения на особи (фаза A8): деф события + остаток времени до угасания.</summary>
|
||||
public struct ThoughtInstance
|
||||
{
|
||||
|
||||
@@ -1656,6 +1656,8 @@ public sealed class AnimalPregnancySystem : BaseSystem
|
||||
Litter = preg.Litter,
|
||||
Generation = o[i].Generation + 1,
|
||||
Position = t[i].Position,
|
||||
Oviparous = o[i].Traits.Oviparous,
|
||||
IncubateDays = MathF.Max(1f, o[i].Traits.GestationDays),
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -1685,6 +1687,22 @@ public sealed class AnimalPregnancySystem : BaseSystem
|
||||
);
|
||||
var offset =
|
||||
new Vector2(_rng.NextSingle() - 0.5f, _rng.NextSingle() - 0.5f) * _cellSize;
|
||||
if (birth.Oviparous)
|
||||
{
|
||||
// Яйцекладка: вместо живого детёныша откладываем яйцо с его геномом — вылупится позже.
|
||||
AnimalFactory.CreateEgg(
|
||||
_store,
|
||||
_animals,
|
||||
birth.Species,
|
||||
birth.Position + offset,
|
||||
child,
|
||||
birth.Generation,
|
||||
birth.IncubateDays,
|
||||
_cellSize
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
AnimalFactory.Create(
|
||||
_store,
|
||||
_animals,
|
||||
@@ -1696,6 +1714,8 @@ public sealed class AnimalPregnancySystem : BaseSystem
|
||||
birth.Generation,
|
||||
_cellSize
|
||||
);
|
||||
}
|
||||
|
||||
count++;
|
||||
}
|
||||
}
|
||||
@@ -1710,6 +1730,90 @@ public sealed class AnimalPregnancySystem : BaseSystem
|
||||
public int Litter;
|
||||
public int Generation;
|
||||
public Vector2 Position;
|
||||
public bool Oviparous;
|
||||
public float IncubateDays;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Инкубация яиц (тип рождения «яйцекладка»): тикает остаток инкубации каждого яйца; по истечении
|
||||
/// вылупляет детёныша (<see cref="AnimalFactory.Create"/> из хранимого генома, стадия Baby) рядом с
|
||||
/// яйцом и удаляет яйцо. Зеркало родов для яйцекладущих видов.
|
||||
/// </summary>
|
||||
public sealed class EggSystem : BaseSystem
|
||||
{
|
||||
private readonly EntityStore _store;
|
||||
private readonly AnimalSet _animals;
|
||||
private readonly NeedSet _needs;
|
||||
private readonly GameClock _clock;
|
||||
private readonly float _secondsPerDay;
|
||||
private readonly int _cellSize;
|
||||
private readonly Random _rng;
|
||||
private readonly ArchetypeQuery<Egg, Transform2D> _query;
|
||||
private readonly List<Entity> _hatched = [];
|
||||
|
||||
public EggSystem(
|
||||
EntityStore store,
|
||||
AnimalSet animals,
|
||||
NeedSet needs,
|
||||
GameClock clock,
|
||||
float secondsPerDay,
|
||||
int cellSize,
|
||||
int seed
|
||||
)
|
||||
{
|
||||
_store = store;
|
||||
_animals = animals;
|
||||
_needs = needs;
|
||||
_clock = clock;
|
||||
_secondsPerDay = secondsPerDay;
|
||||
_cellSize = cellSize;
|
||||
_rng = new Random(seed);
|
||||
_query = store.Query<Egg, Transform2D>();
|
||||
}
|
||||
|
||||
protected override void OnUpdateGroup()
|
||||
{
|
||||
var days = _clock.DeltaTime / _secondsPerDay;
|
||||
if (days <= 0f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_hatched.Clear();
|
||||
foreach (var (eggs, transforms, entities) in _query.Chunks)
|
||||
{
|
||||
var e = eggs.Span;
|
||||
for (var i = 0; i < e.Length; i++)
|
||||
{
|
||||
ref var egg = ref e[i];
|
||||
egg.IncubateDays -= days;
|
||||
if (egg.IncubateDays > 0f)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var offset =
|
||||
new Vector2(_rng.NextSingle() - 0.5f, _rng.NextSingle() - 0.5f) * _cellSize;
|
||||
AnimalFactory.Create(
|
||||
_store,
|
||||
_animals,
|
||||
_needs,
|
||||
egg.Species,
|
||||
transforms.Span[i].Position + offset,
|
||||
ageDays: 0f,
|
||||
egg.Genome,
|
||||
egg.Generation,
|
||||
_cellSize
|
||||
);
|
||||
_hatched.Add(entities.EntityAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var egg in _hatched)
|
||||
{
|
||||
egg.DeleteEntity();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user