Животные A1: фундамент — AnimalDef, гены зверя, спавн оленя
Новый тип дефа Animal (AnimalDef : PawnDef) + animals.json (Deer). 8 организм-агностичных животных генов в genes.json. AnimalPhenotype/AnimalOrganism, AnimalSet (шаблон генома вида) и AnimalFactory — зеркала растительных PlantSet/PlantFactory. Детерминированный спавн стада оленей по суше в WorldScene с рендером и тинтом меха по гену; консольная команда 'animal <species>'. Проверено: сборка чистая, --check-content (7 типов дефов, 28 генов). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
516e263c77
commit
5e61491960
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"type": "Animal",
|
||||
// Виды-животные (фаза A1): подтип Pawn (kind=animal) + базовый геном вида из общих Gene-дефов
|
||||
// (см. genes.json). Особь при спавне получает аллели вокруг этих баз, фенотип — формулами генов.
|
||||
// Пол, стадии роста, размножение, здоровье и поведение приходят в фазах A2+.
|
||||
"defs": [
|
||||
{
|
||||
"defName": "Deer",
|
||||
"label": "pawn.deer",
|
||||
"kind": "animal",
|
||||
"texture": "things/pawn/animal/deer/DeerFemale_east",
|
||||
"genome": {
|
||||
"bodySize": 1.5,
|
||||
"metabolism": 1.0,
|
||||
"moveSpeed": 1.2,
|
||||
"bloodVolume": 1.4,
|
||||
"vision": 1.3,
|
||||
"brainSize": 0.45,
|
||||
"insulation": 0.55,
|
||||
"furColor": 0.45,
|
||||
"lifespan": 360,
|
||||
"spread": 0.08
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -74,6 +74,32 @@
|
||||
"default": 0, "min": 0, "max": 1, "spread": 0, "mutationChance": 0, "tags": ["derived"],
|
||||
"effects": { "hardiness": "gsum('Gene.*Tolerance')" } },
|
||||
|
||||
// --- Животные (фаза A1): организм-агностичные гены зверя (их несёт геном животного) ---
|
||||
{ "defName": "GeneMaxBodySize", "parent": "BaseNumericGene", "label": "gene.bodySize",
|
||||
"default": 1.0, "min": 0.1, "max": 4.0, "tags": ["animal", "body"],
|
||||
"effects": { "bodySize": "value" } },
|
||||
{ "defName": "GeneMetabolism", "parent": "BaseNumericGene", "label": "gene.metabolism",
|
||||
"default": 1.0, "min": 0.2, "max": 3.0, "tags": ["animal", "metabolism"],
|
||||
"effects": { "metabolism": "value" } },
|
||||
{ "defName": "GeneMoveSpeed", "parent": "BaseNumericGene", "label": "gene.moveSpeed",
|
||||
"default": 1.0, "min": 0.2, "max": 3.0, "tags": ["animal", "movement"],
|
||||
"effects": { "moveSpeed": "value" } },
|
||||
{ "defName": "GeneBloodVolume", "parent": "BaseNumericGene", "label": "gene.bloodVolume",
|
||||
"default": 1.0, "min": 0.2, "max": 3.0, "tags": ["animal", "health"],
|
||||
"effects": { "bloodVolume": "value" } },
|
||||
{ "defName": "GeneVision", "parent": "BaseNumericGene", "label": "gene.vision",
|
||||
"default": 1.0, "min": 0.2, "max": 3.0, "tags": ["animal", "senses"],
|
||||
"effects": { "vision": "value" } },
|
||||
{ "defName": "GeneBrainSize", "parent": "BaseNumericGene", "label": "gene.brainSize",
|
||||
"default": 0.45, "min": 0.0, "max": 1.0, "tags": ["animal", "brain"],
|
||||
"effects": { "brainSize": "value" } },
|
||||
{ "defName": "GeneInsulation", "parent": "BaseNumericGene", "label": "gene.insulation",
|
||||
"default": 0.5, "min": 0.0, "max": 1.0, "tags": ["animal", "temperature"],
|
||||
"effects": { "insulation": "value" } },
|
||||
{ "defName": "GeneFurColor", "parent": "BaseNumericGene", "label": "gene.furColor",
|
||||
"default": 0.5, "min": 0.0, "max": 1.0, "spread": 0.06, "tags": ["animal", "morphology", "color"],
|
||||
"effects": { "furHue": "value" } },
|
||||
|
||||
// Дискретный ген морфы: вариант 0 доминирует, 1 (рецессивный) виден только в гомозиготе.
|
||||
{ "defName": "GeneMorph", "kind": "Discrete", "label": "gene.morph",
|
||||
"variants": 2, "variantWeights": [0.82, 0.18], "mutationChance": 0.05, "tags": ["morphology"],
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using MrGameEng.Genetics;
|
||||
using MrGameEng.Graphics;
|
||||
|
||||
namespace LittleSim.Content;
|
||||
|
||||
/// <summary>
|
||||
/// Готовая (resolved) таблица животных для сцены: по каждому <see cref="AnimalDef"/> — загруженный
|
||||
/// регион атласа и набор генов вида (<see cref="GenomeTemplate"/>) для генерации особей. Строится
|
||||
/// один раз при загрузке сцены (нужны атлас и устройство), как <see cref="PlantSet"/>. Системы и
|
||||
/// фабрика индексируются в неё без словарей. Фаза A1 — один спрайт на вид; стадии/пол придут позже.
|
||||
/// </summary>
|
||||
public sealed class AnimalSet
|
||||
{
|
||||
/// <summary>Вид животного: его деф, спрайт и набор генов для генерации особи.</summary>
|
||||
public sealed class Species
|
||||
{
|
||||
/// <summary>Исходный деф животного.</summary>
|
||||
public required AnimalDef Def { get; init; }
|
||||
|
||||
/// <summary>Спрайт вида (фаза A1 — единый; стадии/пол позже).</summary>
|
||||
public required Texture2DRegion Region { get; init; }
|
||||
|
||||
/// <summary>Набор генов вида: базовые значения для генерации особи.</summary>
|
||||
public required GenomeTemplate Template { get; init; }
|
||||
}
|
||||
|
||||
private readonly Species[] _species;
|
||||
private readonly Dictionary<AnimalDef, int> _index = new();
|
||||
|
||||
/// <summary>Общий реестр генов (по id) для скрещивания и вычисления признаков животных.</summary>
|
||||
public IReadOnlyDictionary<string, GeneDef> GeneRegistry { get; }
|
||||
|
||||
/// <summary>Строит таблицу из всех (не-abstract) <see cref="AnimalDef"/> мода.</summary>
|
||||
public AnimalSet(GameContent content, ModAtlases atlases, GraphicsDevice device)
|
||||
{
|
||||
var genes = content
|
||||
.Defs.All<GeneDef>()
|
||||
.ToDictionary(g => g.DefName, g => g, StringComparer.Ordinal);
|
||||
GeneRegistry = genes;
|
||||
|
||||
var defs = content.Defs.All<AnimalDef>().ToArray();
|
||||
_species = new Species[defs.Length];
|
||||
for (var i = 0; i < defs.Length; i++)
|
||||
{
|
||||
var def = defs[i];
|
||||
_species[i] = new Species
|
||||
{
|
||||
Def = def,
|
||||
Region = atlases.GetRegion(device, def.Texture),
|
||||
Template = BuildTemplate(def.Genome, genes),
|
||||
};
|
||||
_index[def] = i;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Число видов в таблице.</summary>
|
||||
public int Count => _species.Length;
|
||||
|
||||
/// <summary>Вид по индексу.</summary>
|
||||
public Species this[int index] => _species[index];
|
||||
|
||||
/// <summary>Индекс вида по дефу.</summary>
|
||||
public int IndexOf(AnimalDef def) => _index[def];
|
||||
|
||||
// Переводит базовый геном вида (числа из animals.json) в набор генов: каждое число — центр
|
||||
// аллелей соответствующего общего GeneDef. Зеркало PlantSet.BuildTemplate.
|
||||
private static GenomeTemplate BuildTemplate(
|
||||
AnimalGenomeDef g,
|
||||
IReadOnlyDictionary<string, GeneDef> genes
|
||||
)
|
||||
{
|
||||
GeneDef Gene(string id) =>
|
||||
genes.TryGetValue(id, out var def)
|
||||
? def
|
||||
: throw new KeyNotFoundException(
|
||||
$"Gene def '{id}' not found — is Mods/Core/Defs/genes.json loaded?"
|
||||
);
|
||||
|
||||
GenomeTemplate.Entry Numeric(string id, float baseValue) =>
|
||||
new(Gene(id), baseValue, g.Spread);
|
||||
|
||||
return new GenomeTemplate(
|
||||
[
|
||||
Numeric("GeneMaxBodySize", g.BodySize),
|
||||
Numeric("GeneMetabolism", g.Metabolism),
|
||||
Numeric("GeneMoveSpeed", g.MoveSpeed),
|
||||
Numeric("GeneBloodVolume", g.BloodVolume),
|
||||
Numeric("GeneVision", g.Vision),
|
||||
new GenomeTemplate.Entry(Gene("GeneBrainSize"), g.BrainSize, 0.03f),
|
||||
Numeric("GeneInsulation", g.Insulation),
|
||||
new GenomeTemplate.Entry(Gene("GeneFurColor"), g.FurColor, 0.06f),
|
||||
Numeric("GeneLifespan", g.Lifespan),
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -63,6 +63,7 @@ public sealed class GameContent
|
||||
defs.RegisterType<ProductDef>("Product");
|
||||
defs.RegisterType<PlantDef>("Plant");
|
||||
defs.RegisterType<PawnDef>("Pawn");
|
||||
defs.RegisterType<AnimalDef>("Animal");
|
||||
defs.RegisterType<WorldPresetDef>("WorldPreset");
|
||||
// Валидация имён при загрузке (фаза G5): гены и продукты обязаны иметь префикс типа.
|
||||
defs.RegisterValidator("Gene", "defName", "^Gene", "gene defName must start with 'Gene'");
|
||||
|
||||
@@ -191,7 +191,7 @@ public sealed class WorldPresetDef : Def
|
||||
}
|
||||
|
||||
/// <summary>Существо (Defs/pawns.json): текстура, размер и вид (житель/животное).</summary>
|
||||
public sealed class PawnDef : Def
|
||||
public class PawnDef : Def
|
||||
{
|
||||
/// <summary>Вид существа: жители ("being") живут в мировой сцене, животные ("animal") — в террейне.</summary>
|
||||
public const string KindBeing = "being";
|
||||
@@ -208,3 +208,53 @@ public sealed class PawnDef : Def
|
||||
/// <summary>Вид: <see cref="KindBeing"/> или <see cref="KindAnimal"/>.</summary>
|
||||
public string Kind { get; init; } = "";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Базовый геном вида-животного (вложенный объект <see cref="AnimalDef.Genome"/>): центры аллелей
|
||||
/// животных генов (см. <c>genes.json</c>). Особь при спавне получает аллели рядом с этими базами
|
||||
/// (разброс — <see cref="Spread"/>), а фенотип считается формулами генов над аллелями. Зеркало
|
||||
/// растительного <see cref="GenomeDef"/>, только для зверя.
|
||||
/// </summary>
|
||||
public sealed class AnimalGenomeDef
|
||||
{
|
||||
/// <summary>Размер тела в клетках — масштаб спрайта и (позже) HP/расхода энергии.</summary>
|
||||
public float BodySize { get; init; } = 1f;
|
||||
|
||||
/// <summary>Скорость обмена веществ — множитель расхода нужд (голод/жажда).</summary>
|
||||
public float Metabolism { get; init; } = 1f;
|
||||
|
||||
/// <summary>Скорость передвижения (множитель).</summary>
|
||||
public float MoveSpeed { get; init; } = 1f;
|
||||
|
||||
/// <summary>Объём крови (множитель) — масштаб скорости кровопотери в системе здоровья.</summary>
|
||||
public float BloodVolume { get; init; } = 1f;
|
||||
|
||||
/// <summary>Дальность зрения (множитель радиуса поиска корма/воды/партнёра).</summary>
|
||||
public float Vision { get; init; } = 1f;
|
||||
|
||||
/// <summary>Размер мозга (0..1) — основа интеллекта (гейт нужд/действий).</summary>
|
||||
public float BrainSize { get; init; } = 0.45f;
|
||||
|
||||
/// <summary>Теплоизоляция мехом (0..1) — терморегуляция.</summary>
|
||||
public float Insulation { get; init; } = 0.5f;
|
||||
|
||||
/// <summary>Оттенок меха (0..1) — сдвиг тинта спрайта (как leafHue у растений).</summary>
|
||||
public float FurColor { get; init; } = 0.5f;
|
||||
|
||||
/// <summary>Продолжительность жизни (игровых дней) до смерти от старости.</summary>
|
||||
public float Lifespan { get; init; } = 160f;
|
||||
|
||||
/// <summary>Доля разброса аллелей вокруг базы при генерации особи.</summary>
|
||||
public float Spread { get; init; } = 0.08f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Вид-животное (Defs/animals.json): подтип <see cref="PawnDef"/> (текстура/размер/вид) плюс базовый
|
||||
/// геном вида. Поведение, здоровье и стадии живут на компонентах сущности (фазы A2+), а не на типе
|
||||
/// дефа — существующие 9 животных-<see cref="PawnDef"/> остаются валидными.
|
||||
/// </summary>
|
||||
public sealed class AnimalDef : PawnDef
|
||||
{
|
||||
/// <summary>Базовый геном вида: центры аллелей животных генов.</summary>
|
||||
public AnimalGenomeDef Genome { get; init; } = new();
|
||||
}
|
||||
|
||||
@@ -65,6 +65,7 @@ public sealed class WorldScene : Scene
|
||||
private readonly List<Action> _speedRefreshers = [];
|
||||
|
||||
private PlantSet _plants = null!;
|
||||
private AnimalSet _animals = null!;
|
||||
private float[] _cellFertility = [];
|
||||
private bool[] _cellLand = [];
|
||||
private bool[] _cellOccluderBase = []; // горы (статично из террейна)
|
||||
@@ -109,6 +110,7 @@ public sealed class WorldScene : Scene
|
||||
|
||||
// Рельеф детерминирован сидом. Растения: новый мир — скаттер из сида; загрузка — из сейва.
|
||||
_plants = new PlantSet(content, atlases, device);
|
||||
_animals = new AnimalSet(content, atlases, device);
|
||||
var loadingPlants = _save?.Plants is { Count: > 0 };
|
||||
BuildTerrain(
|
||||
content,
|
||||
@@ -122,6 +124,10 @@ public sealed class WorldScene : Scene
|
||||
RestorePlants(content);
|
||||
}
|
||||
|
||||
// Животные (фаза A1): детерминированный начальный спавн по суше. Пока без сейва/симуляции —
|
||||
// регенерируются из сида и для нового, и для загруженного мира.
|
||||
SpawnAnimals(content);
|
||||
|
||||
// Освещение: лайтмап (день/ночь × окклюзия от гор/крон + точечные) множится поверх мира,
|
||||
// а Lighting.SampleAt даёт локальный свет системе роста (подлесок под кронами растёт хуже).
|
||||
var lighting = this.UseLighting(
|
||||
@@ -454,6 +460,64 @@ public sealed class WorldScene : Scene
|
||||
}
|
||||
}
|
||||
|
||||
// Детерминированный начальный спавн оленей по клеткам суши (фаза A1): количество — лёгкое стадо,
|
||||
// масштаб по площади суши; геном каждой особи генерируется из шаблона вида (разброс + аллели).
|
||||
private void SpawnAnimals(GameContent content)
|
||||
{
|
||||
if (_animals.Count == 0 || !content.Defs.TryGet<AnimalDef>("Deer", out var deerDef))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var species = _animals.IndexOf(deerDef);
|
||||
var template = _animals[species].Template;
|
||||
var random = new Random(_config.Seed + 0x5EED);
|
||||
|
||||
var landCells = 0;
|
||||
for (var i = 0; i < _cellLand.Length; i++)
|
||||
{
|
||||
if (_cellLand[i])
|
||||
{
|
||||
landCells++;
|
||||
}
|
||||
}
|
||||
|
||||
if (landCells == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var count = Math.Clamp(landCells / 400, 6, 40);
|
||||
var placed = 0;
|
||||
var attempts = 0;
|
||||
while (placed < count && attempts < count * 50)
|
||||
{
|
||||
attempts++;
|
||||
var cell = random.Next(_cellLand.Length);
|
||||
if (!_cellLand[cell])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var cx = cell % _config.Width;
|
||||
var cy = cell / _config.Width;
|
||||
var px = (cx + 0.2f + random.NextSingle() * 0.6f) * CellSize;
|
||||
var py = (cy + 0.2f + random.NextSingle() * 0.6f) * CellSize;
|
||||
AnimalFactory.Create(
|
||||
Store,
|
||||
_animals,
|
||||
species,
|
||||
new Vector2(px, py),
|
||||
template.Generate(random),
|
||||
generation: 0,
|
||||
CellSize
|
||||
);
|
||||
placed++;
|
||||
}
|
||||
|
||||
Log.Info($"Spawned {placed} deer");
|
||||
}
|
||||
|
||||
// Текущая сетка окклюдеров для лайтмапа: статичные горы + клетки со зрелыми деревьями.
|
||||
private bool[] BuildOccluders()
|
||||
{
|
||||
@@ -552,6 +616,11 @@ public sealed class WorldScene : Scene
|
||||
"plant <species> [seed] — sample a species genome and show its gene-driven traits/products",
|
||||
(c, args) => RunPlantDemo(c, content, args)
|
||||
);
|
||||
console.Register(
|
||||
"animal",
|
||||
"animal <species> [seed] — sample an animal genome and show its gene-driven traits",
|
||||
(c, args) => RunAnimalDemo(c, content, args)
|
||||
);
|
||||
console.Register(
|
||||
"menu",
|
||||
"menu — return to the main menu",
|
||||
@@ -659,6 +728,40 @@ public sealed class WorldScene : Scene
|
||||
}
|
||||
}
|
||||
|
||||
// Демонстрация животного (фаза A1): по виду берём его набор генов, генерируем особь и печатаем
|
||||
// признаки, вычисленные формулами генов (тело/мозг/метаболизм/чувства/мех).
|
||||
private void RunAnimalDemo(DevConsole console, GameContent content, string[] args)
|
||||
{
|
||||
var species = content.Defs.NamesOf("Animal");
|
||||
if (args.Length == 0)
|
||||
{
|
||||
console.WriteLine("usage: animal <species> [seed] e.g. animal Deer");
|
||||
console.WriteLine($"species: {string.Join(", ", species)}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!content.Defs.TryGet<AnimalDef>(args[0], out var def))
|
||||
{
|
||||
console.WriteLine($"no animal '{args[0]}'; species: {string.Join(", ", species)}");
|
||||
return;
|
||||
}
|
||||
|
||||
var index = _animals.IndexOf(def);
|
||||
var seed = args.Length > 1 ? int.Parse(args[1]) : Random.Shared.Next();
|
||||
var genome = _animals[index].Template.Generate(new Random(seed));
|
||||
var traits = AnimalPhenotype.FromTraits(Phenotype.Compute(genome, _animals.GeneRegistry));
|
||||
|
||||
console.WriteLine($"{def.DefName} (seed {seed}):");
|
||||
console.WriteLine(
|
||||
$" body {traits.BodySize:0.##} cells, lifespan {traits.Lifespan:0} d, "
|
||||
+ $"brain {traits.BrainSize:0.##}, metabolism {traits.Metabolism:0.##}"
|
||||
);
|
||||
console.WriteLine(
|
||||
$" move {traits.MoveSpeed:0.##}, vision {traits.Vision:0.##}, blood {traits.BloodVolume:0.##}, "
|
||||
+ $"insulation {traits.Insulation:0.##}, furHue {traits.FurHue:0.##}"
|
||||
);
|
||||
}
|
||||
|
||||
private static string ProductLabel(GameContent content, string productDefName) =>
|
||||
content.Defs.TryGet<ProductDef>(productDefName, out var product)
|
||||
? content.Languages.Get(product.Label)
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
using Friflo.Engine.ECS;
|
||||
using LittleSim.Content;
|
||||
using LittleSim.Scenes;
|
||||
using Microsoft.Xna.Framework;
|
||||
using MrGameEng.Genetics;
|
||||
using MrGameEng.Graphics;
|
||||
|
||||
namespace LittleSim.Sim;
|
||||
|
||||
/// <summary>
|
||||
/// Единая точка создания сущности-животного: спрайт вида, признаки из генома формулами генов, тинт по
|
||||
/// оттенку меха и компонент-организм. Переиспользуется начальным спавном, размножением и загрузкой —
|
||||
/// чтобы все животные собирались одинаково. Зеркало <see cref="PlantFactory"/>.
|
||||
/// </summary>
|
||||
public static class AnimalFactory
|
||||
{
|
||||
// Якоря оттенка меха: тёплый рыжевато-песочный → холодный серо-голубой (по гену furColor).
|
||||
private static readonly Color FurWarm = new(255, 226, 188);
|
||||
private static readonly Color FurCool = new(206, 222, 235);
|
||||
|
||||
/// <summary>Создаёт животное вида <paramref name="species"/> с заданным геномом и поколением.</summary>
|
||||
public static Entity Create(
|
||||
EntityStore store,
|
||||
AnimalSet animals,
|
||||
int species,
|
||||
Vector2 position,
|
||||
Genome genome,
|
||||
int generation,
|
||||
int cellSize
|
||||
)
|
||||
{
|
||||
var sp = animals[species];
|
||||
var traits = AnimalPhenotype.FromTraits(Phenotype.Compute(genome, animals.GeneRegistry));
|
||||
|
||||
var sprite = new Sprite(sp.Region, GameLayers.Beings);
|
||||
sprite.CenterOrigin();
|
||||
sprite.Color = Tint(traits);
|
||||
|
||||
// Размер спрайта задаёт ген размера тела; деф-fallback — на случай отсутствия гена.
|
||||
var sizeCells = traits.BodySize > 0f ? traits.BodySize : sp.Def.SizeCells;
|
||||
|
||||
return store.CreateEntity(
|
||||
new Transform2D(position, scale: new Vector2(cellSize * sizeCells / sp.Region.Width)),
|
||||
sprite,
|
||||
new AnimalOrganism
|
||||
{
|
||||
Genome = genome,
|
||||
Traits = traits,
|
||||
Species = species,
|
||||
Generation = generation,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Цвет животного из генов: лёгкий тёплый/холодный сдвиг меха (ген furHue) поверх текстуры —
|
||||
// субтильный, чтобы не перекрашивать уже цветной спрайт зверя (как leafHue у растений).
|
||||
private static Color Tint(in AnimalPhenotype traits)
|
||||
{
|
||||
var fur = Color.Lerp(FurWarm, FurCool, Math.Clamp(traits.FurHue, 0f, 1f));
|
||||
return Color.Lerp(Color.White, fur, 0.5f);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
using System.Collections.Generic;
|
||||
using Friflo.Engine.ECS;
|
||||
using MrGameEng.Genetics;
|
||||
|
||||
namespace LittleSim.Sim;
|
||||
|
||||
/// <summary>
|
||||
/// Признаки (фенотип) животного, вычисленные из генома формулами генов (см. <c>genes.json</c>):
|
||||
/// готовые значения, которые читают системы, не касаясь генов напрямую. Зеркало
|
||||
/// <see cref="PlantPhenotype"/>. Имена полей соответствуют именам признаков в эффектах генов.
|
||||
/// </summary>
|
||||
public struct AnimalPhenotype
|
||||
{
|
||||
/// <summary>Размер тела в клетках (масштаб спрайта; позже — HP/расход энергии).</summary>
|
||||
public float BodySize;
|
||||
|
||||
/// <summary>Скорость обмена веществ — множитель расхода нужд.</summary>
|
||||
public float Metabolism;
|
||||
|
||||
/// <summary>Скорость передвижения (множитель).</summary>
|
||||
public float MoveSpeed;
|
||||
|
||||
/// <summary>Объём крови (множитель) — масштаб скорости кровопотери.</summary>
|
||||
public float BloodVolume;
|
||||
|
||||
/// <summary>Дальность зрения (множитель радиуса поиска).</summary>
|
||||
public float Vision;
|
||||
|
||||
/// <summary>Размер мозга (0..1) — основа интеллекта.</summary>
|
||||
public float BrainSize;
|
||||
|
||||
/// <summary>Теплоизоляция мехом (0..1).</summary>
|
||||
public float Insulation;
|
||||
|
||||
/// <summary>Оттенок меха (0..1) — сдвиг тинта спрайта.</summary>
|
||||
public float FurHue;
|
||||
|
||||
/// <summary>Продолжительность жизни (игровых дней).</summary>
|
||||
public float Lifespan;
|
||||
|
||||
/// <summary>Собирает фенотип из карты признаков, посчитанной <see cref="Phenotype.Compute"/>.</summary>
|
||||
public static AnimalPhenotype FromTraits(IReadOnlyDictionary<string, float> traits)
|
||||
{
|
||||
float T(string name) => traits.GetValueOrDefault(name);
|
||||
return new AnimalPhenotype
|
||||
{
|
||||
BodySize = T("bodySize"),
|
||||
Metabolism = T("metabolism"),
|
||||
MoveSpeed = T("moveSpeed"),
|
||||
BloodVolume = T("bloodVolume"),
|
||||
Vision = T("vision"),
|
||||
BrainSize = T("brainSize"),
|
||||
Insulation = T("insulation"),
|
||||
FurHue = T("furHue"),
|
||||
Lifespan = T("lifespan"),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Компонент-организм животного: управляемый <see cref="Genome"/> (для размножения, сейва и
|
||||
/// пересчёта), выраженный фенотип <see cref="Traits"/>, индекс вида в <see cref="Content.AnimalSet"/>
|
||||
/// и номер поколения (для <c>popstats</c> и видимого дрейфа). Геном — ссылочный объект, поэтому
|
||||
/// компонент несёт ссылку, а не копию (как у растений). Зеркало <see cref="PlantOrganism"/>.
|
||||
/// </summary>
|
||||
public struct AnimalOrganism : IComponent
|
||||
{
|
||||
/// <summary>Геном особи (аллели по генам); источник истины для признаков и размножения.</summary>
|
||||
public Genome Genome;
|
||||
|
||||
/// <summary>Выраженные признаки — то, что читают системы.</summary>
|
||||
public AnimalPhenotype Traits;
|
||||
|
||||
/// <summary>Индекс вида в <see cref="Content.AnimalSet"/>.</summary>
|
||||
public int Species;
|
||||
|
||||
/// <summary>Номер поколения: max(родителей) + 1; у основателей 0.</summary>
|
||||
public int Generation;
|
||||
}
|
||||
Reference in New Issue
Block a user