Рефактор: геном животного — данными, а не кодом
AnimalGenomeDef (фикс. поля) убран; AnimalDef.Genome теперь словарь geneId->base. AnimalSet.BuildTemplate строит шаблон generically по реестру генов (разброс из самого GeneDef, дискретные поддержаны). Модер добавляет ген особи одной строкой JSON в animals.json — без правки кода. Устраняет блокер расширяемости #1. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
97521f81b8
commit
d4a370e02d
+13
-14
@@ -12,20 +12,19 @@
|
||||
"maleTexture": "things/pawn/animal/deer/DeerMale_east",
|
||||
"babyTexture": "things/pawn/animal/deer/DeerBaby_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,
|
||||
"maturityAge": 90,
|
||||
"lifespan": 360,
|
||||
"breedingSeason": 2,
|
||||
"gestationDays": 30,
|
||||
"litterSize": 1,
|
||||
"spread": 0.08
|
||||
"GeneMaxBodySize": 1.5,
|
||||
"GeneMetabolism": 1.0,
|
||||
"GeneMoveSpeed": 1.2,
|
||||
"GeneBloodVolume": 1.4,
|
||||
"GeneVision": 1.3,
|
||||
"GeneBrainSize": 0.45,
|
||||
"GeneInsulation": 0.55,
|
||||
"GeneFurColor": 0.45,
|
||||
"GeneMaturityAge": 90,
|
||||
"GeneLifespan": 360,
|
||||
"GeneBreedingSeason": 2,
|
||||
"GeneGestationDays": 30,
|
||||
"GeneLitterSize": 1
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
"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"],
|
||||
"default": 0.45, "min": 0.0, "max": 1.0, "spread": 0.03, "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"],
|
||||
|
||||
@@ -88,39 +88,28 @@ public sealed class AnimalSet
|
||||
return genome;
|
||||
}
|
||||
|
||||
// Переводит базовый геном вида (числа из animals.json) в набор генов: каждое число — центр
|
||||
// аллелей соответствующего общего GeneDef. Зеркало PlantSet.BuildTemplate.
|
||||
// Строит шаблон генома вида из ДАННЫХ (geneId→base из animals.json) generically: разброс берётся из
|
||||
// самого гена. Состав открыт — модер добавляет ген строкой JSON, без правки кода. Дискретные гены
|
||||
// (база/разброс не нужны — варианты из GeneDef) тоже поддержаны.
|
||||
private static GenomeTemplate BuildTemplate(
|
||||
AnimalGenomeDef g,
|
||||
IReadOnlyDictionary<string, float> genome,
|
||||
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("GeneMaturityAge", g.MaturityAge),
|
||||
Numeric("GeneLifespan", g.Lifespan),
|
||||
new GenomeTemplate.Entry(Gene("GeneBreedingSeason"), g.BreedingSeason, 0f),
|
||||
Numeric("GeneGestationDays", g.GestationDays),
|
||||
Numeric("GeneLitterSize", g.LitterSize),
|
||||
]
|
||||
var entries = new List<GenomeTemplate.Entry>(genome.Count);
|
||||
foreach (var (geneId, baseValue) in genome)
|
||||
{
|
||||
if (!genes.TryGetValue(geneId, out var gene))
|
||||
{
|
||||
throw new KeyNotFoundException(
|
||||
$"Animal genome references unknown gene '{geneId}' — check genes.json."
|
||||
);
|
||||
}
|
||||
|
||||
var spread = gene.Kind == GeneKind.Discrete ? 0f : gene.Spread;
|
||||
entries.Add(new GenomeTemplate.Entry(gene, baseValue, spread));
|
||||
}
|
||||
|
||||
return new GenomeTemplate(entries);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,60 +210,9 @@ public class PawnDef : Def
|
||||
}
|
||||
|
||||
/// <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>Возраст полового созревания (игровых дней) — порог стадии Adult.</summary>
|
||||
public float MaturityAge { get; init; } = 90f;
|
||||
|
||||
/// <summary>Продолжительность жизни (игровых дней) до смерти от старости.</summary>
|
||||
public float Lifespan { get; init; } = 160f;
|
||||
|
||||
/// <summary>Сезон гона (0 весна, 1 лето, 2 осень, 3 зима) — фиксирован по виду.</summary>
|
||||
public float BreedingSeason { get; init; } = 2f;
|
||||
|
||||
/// <summary>Срок вынашивания (игровых дней).</summary>
|
||||
public float GestationDays { get; init; } = 30f;
|
||||
|
||||
/// <summary>Размер помёта (число детёнышей за роды).</summary>
|
||||
public float LitterSize { get; init; } = 1f;
|
||||
|
||||
/// <summary>Доля разброса аллелей вокруг базы при генерации особи.</summary>
|
||||
public float Spread { get; init; } = 0.08f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Вид-животное (Defs/animals.json): подтип <see cref="PawnDef"/> (текстура/размер/вид) плюс базовый
|
||||
/// геном вида. Поведение, здоровье и стадии живут на компонентах сущности (фазы A2+), а не на типе
|
||||
/// дефа — существующие 9 животных-<see cref="PawnDef"/> остаются валидными.
|
||||
/// Вид-животное (Defs/animals.json): подтип <see cref="PawnDef"/> (текстура/размер/вид) плюс геном
|
||||
/// вида. Поведение, здоровье и стадии живут на компонентах сущности (фазы A2+), а не на типе дефа —
|
||||
/// существующие 9 животных-<see cref="PawnDef"/> остаются валидными.
|
||||
/// </summary>
|
||||
public sealed class AnimalDef : PawnDef
|
||||
{
|
||||
@@ -273,8 +222,13 @@ public sealed class AnimalDef : PawnDef
|
||||
/// <summary>Текстура детёныша/подростка (фаза A3); пусто — берётся <see cref="PawnDef.Texture"/>.</summary>
|
||||
public string BabyTexture { get; init; } = "";
|
||||
|
||||
/// <summary>Базовый геном вида: центры аллелей животных генов.</summary>
|
||||
public AnimalGenomeDef Genome { get; init; } = new();
|
||||
/// <summary>
|
||||
/// Геном вида как ДАННЫЕ: ключ — id гена (<see cref="MrGameEng.Genetics.GeneDef"/>), значение —
|
||||
/// центр аллелей особи. Состав открыт — модер добавляет/убирает ген одной строкой JSON, без правки
|
||||
/// кода; разброс берётся из самого гена (<c>GeneDef.Spread</c>). Дискретные гены (напр. пол) сюда
|
||||
/// не пишутся — назначаются генерацией.
|
||||
/// </summary>
|
||||
public Dictionary<string, float> Genome { get; init; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -503,7 +503,9 @@ public sealed class WorldScene : Scene
|
||||
}
|
||||
|
||||
var species = _animals.IndexOf(deerDef);
|
||||
var maxAge = deerDef.Genome.Lifespan * 0.85f; // разновозрастное стадо, но не у самой смерти
|
||||
// Разновозрастное стадо, но не у самой смерти; продолжительность жизни — из данных генома вида.
|
||||
var maxAge =
|
||||
(deerDef.Genome.TryGetValue("GeneLifespan", out var lifespan) ? lifespan : 160f) * 0.85f;
|
||||
var random = new Random(_config.Seed + 0x5EED);
|
||||
|
||||
var landCells = 0;
|
||||
|
||||
Reference in New Issue
Block a user