Рефактор: зрение/скорость/выедание животного — из дефа вида

Завершает вынос баланса в данные: AnimalDecisionSystem.VisionRadius берёт дальность из AnimalDef.VisionCells (× ген), TryFindPlant/TryFindMate принимают готовый радиус; AnimalActionSystem берёт BaseSpeed и ForageBiteDays из дефа вида (через AnimalSet). Убраны хардкод-константы скорости/зрения/выедания.

Сборка чистая, --check-content (9 типов дефов).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Leonid Pershin
2026-06-14 05:16:44 +03:00
co-authored by Claude Opus 4.8
parent 5d0cbd2115
commit cbc74c0794
3 changed files with 36 additions and 21 deletions
+9
View File
@@ -270,6 +270,15 @@ public sealed class AnimalDef : PawnDef
/// <summary>Плотность стартового спавна: голов на 1000 клеток суши (0 — вид не спавнится сам).</summary> /// <summary>Плотность стартового спавна: голов на 1000 клеток суши (0 — вид не спавнится сам).</summary>
public float SpawnPer1000Cells { get; init; } public float SpawnPer1000Cells { get; init; }
/// <summary>Базовая скорость передвижения (мировых единиц/сек при гене moveSpeed = 1).</summary>
public float BaseSpeed { get; init; } = 28f;
/// <summary>Базовый радиус восприятия в клетках (× ген vision) — поиск корма/воды/партнёра.</summary>
public float VisionCells { get; init; } = 14f;
/// <summary>Сила выедания: на сколько игровых дней роста убавляется растение за день кормёжки.</summary>
public float ForageBiteDays { get; init; } = 40f;
} }
/// <summary> /// <summary>
+9 -1
View File
@@ -228,7 +228,15 @@ public sealed class WorldScene : Scene
) )
); );
UpdateSystems.Add( UpdateSystems.Add(
new AnimalActionSystem(Store, _plants, _needs, Context.Clock, SecondsPerDay, _bounds) new AnimalActionSystem(
Store,
_plants,
_animals,
_needs,
Context.Clock,
SecondsPerDay,
_bounds
)
); );
UpdateSystems.Add(new AnimalAppearanceSystem(_needs)); UpdateSystems.Add(new AnimalAppearanceSystem(_needs));
UpdateSystems.Add(new AnimalGrowthSystem(Context.Clock, SecondsPerDay, _animals, CellSize)); UpdateSystems.Add(new AnimalGrowthSystem(Context.Clock, SecondsPerDay, _animals, CellSize));
+18 -20
View File
@@ -170,7 +170,6 @@ public sealed class AnimalRutSystem(AnimalSet animals, Climate climate, HediffDe
public sealed class AnimalDecisionSystem : BaseSystem public sealed class AnimalDecisionSystem : BaseSystem
{ {
private const float Interval = 0.6f; private const float Interval = 0.6f;
private const float VisionCells = 14f; // базовый радиус зрения в клетках (× ген vision)
private readonly UtilityAi<AnimalContext> _brain; private readonly UtilityAi<AnimalContext> _brain;
private readonly GameClock _clock; private readonly GameClock _clock;
@@ -257,6 +256,7 @@ public sealed class AnimalDecisionSystem : BaseSystem
brain.DecideIn = Interval + _rng.NextSingle() * Interval; // джиттер — расфазировать скан brain.DecideIn = Interval + _rng.NextSingle() * Interval; // джиттер — расфазировать скан
var pos = t[i].Position; var pos = t[i].Position;
var adult = AnimalFactory.IsAdult(_animalSet[o[i].Species], gr[i].Stage); var adult = AnimalFactory.IsAdult(_animalSet[o[i].Species], gr[i].Stage);
var radius = VisionRadius(o[i].Species, o[i].Traits.Vision);
var name = var name =
_brain.Select(new AnimalContext(n[i].Values))?.Name ?? AnimalActions.Wander; _brain.Select(new AnimalContext(n[i].Values))?.Name ?? AnimalActions.Wander;
@@ -264,7 +264,7 @@ public sealed class AnimalDecisionSystem : BaseSystem
{ {
case AnimalActions.Eat case AnimalActions.Eat
when EatsPlants(o[i].Species) when EatsPlants(o[i].Species)
&& TryFindPlant(pos, o[i].Traits.Vision, out var plant, out var pp): && TryFindPlant(pos, radius, out var plant, out var pp):
brain.Action = AnimalActions.Eat; brain.Action = AnimalActions.Eat;
brain.TargetPlant = plant; brain.TargetPlant = plant;
brain.Target = pp; brain.Target = pp;
@@ -280,13 +280,7 @@ public sealed class AnimalDecisionSystem : BaseSystem
break; break;
case AnimalActions.Mate case AnimalActions.Mate
when adult when adult
&& TryFindMate( && TryFindMate(pos, o[i].IsMale, radius, out var mateId, out var matePos):
pos,
o[i].IsMale,
o[i].Traits.Vision,
out var mateId,
out var matePos
):
brain.Action = AnimalActions.Mate; brain.Action = AnimalActions.Mate;
brain.TargetPlant = -1; brain.TargetPlant = -1;
brain.TargetMate = mateId; brain.TargetMate = mateId;
@@ -306,6 +300,10 @@ public sealed class AnimalDecisionSystem : BaseSystem
} }
} }
// Радиус восприятия особи: базовая дальность вида (данные) × ген зрения × размер клетки.
private float VisionRadius(int species, float vision) =>
MathF.Max(1f, _animalSet[species].Def.VisionCells) * MathF.Max(0.2f, vision) * _cellSize;
// Ест ли вид растения (диета данными) — гейт поиска корма; хищники (meat) появятся на горизонте. // Ест ли вид растения (диета данными) — гейт поиска корма; хищники (meat) появятся на горизонте.
private bool EatsPlants(int species) private bool EatsPlants(int species)
{ {
@@ -321,10 +319,9 @@ public sealed class AnimalDecisionSystem : BaseSystem
return false; return false;
} }
// Ближайшее растение в радиусе зрения (квадрат расстояния); ничьи — по меньшему id (детерминизм). // Ближайшее растение в радиусе (квадрат расстояния); ничьи — по меньшему id (детерминизм).
private bool TryFindPlant(Vector2 from, float vision, out int plantId, out Vector2 position) private bool TryFindPlant(Vector2 from, float radius, out int plantId, out Vector2 position)
{ {
var radius = VisionCells * MathF.Max(0.2f, vision) * _cellSize;
var bestSq = radius * radius; var bestSq = radius * radius;
plantId = -1; plantId = -1;
position = default; position = default;
@@ -374,12 +371,11 @@ public sealed class AnimalDecisionSystem : BaseSystem
private bool TryFindMate( private bool TryFindMate(
Vector2 from, Vector2 from,
bool selfMale, bool selfMale,
float vision, float radius,
out int mateId, out int mateId,
out Vector2 position out Vector2 position
) )
{ {
var radius = VisionCells * MathF.Max(0.2f, vision) * _cellSize;
var bestSq = radius * radius; var bestSq = radius * radius;
mateId = -1; mateId = -1;
position = default; position = default;
@@ -428,12 +424,11 @@ public sealed class AnimalDecisionSystem : BaseSystem
/// </summary> /// </summary>
public sealed class AnimalActionSystem : BaseSystem public sealed class AnimalActionSystem : BaseSystem
{ {
private const float BaseSpeed = 28f; // мировых единиц в секунду при moveSpeed 1
private const float GrazePerDay = 40f; // на сколько игровых дней роста убавляется выеденное растение
private const float GrazeKillAge = 1f; // трава с возрастом ниже этого после выедания исчезает private const float GrazeKillAge = 1f; // трава с возрастом ниже этого после выедания исчезает
private readonly EntityStore _store; private readonly EntityStore _store;
private readonly PlantSet _plants; private readonly PlantSet _plants;
private readonly AnimalSet _animalSet;
private readonly NeedSet _needs; private readonly NeedSet _needs;
private readonly GameClock _clock; private readonly GameClock _clock;
private readonly float _secondsPerDay; private readonly float _secondsPerDay;
@@ -445,6 +440,7 @@ public sealed class AnimalActionSystem : BaseSystem
public AnimalActionSystem( public AnimalActionSystem(
EntityStore store, EntityStore store,
PlantSet plants, PlantSet plants,
AnimalSet animals,
NeedSet needs, NeedSet needs,
GameClock clock, GameClock clock,
float secondsPerDay, float secondsPerDay,
@@ -453,6 +449,7 @@ public sealed class AnimalActionSystem : BaseSystem
{ {
_store = store; _store = store;
_plants = plants; _plants = plants;
_animalSet = animals;
_needs = needs; _needs = needs;
_clock = clock; _clock = clock;
_secondsPerDay = secondsPerDay; _secondsPerDay = secondsPerDay;
@@ -483,7 +480,8 @@ public sealed class AnimalActionSystem : BaseSystem
ref var brain = ref b[i]; ref var brain = ref b[i];
var values = n[i].Values; var values = n[i].Values;
ref var pos = ref t[i].Position; ref var pos = ref t[i].Position;
var speed = BaseSpeed * MathF.Max(0.2f, o[i].Traits.MoveSpeed); var def = _animalSet[o[i].Species].Def;
var speed = def.BaseSpeed * MathF.Max(0.2f, o[i].Traits.MoveSpeed);
switch (brain.Action) switch (brain.Action)
{ {
@@ -495,7 +493,7 @@ public sealed class AnimalActionSystem : BaseSystem
if (MoveTo(ref pos, brain.Target, speed * seconds)) if (MoveTo(ref pos, brain.Target, speed * seconds))
{ {
Refill(values, AnimalActions.Eat, days); Refill(values, AnimalActions.Eat, days);
Graze(brain.TargetPlant, days); Graze(brain.TargetPlant, days, def.ForageBiteDays);
} }
break; break;
@@ -571,7 +569,7 @@ public sealed class AnimalActionSystem : BaseSystem
} }
// Выедание: убавляет рост растения; траву (без ствола), выеденную до нуля, помечает на гибель. // Выедание: убавляет рост растения; траву (без ствола), выеденную до нуля, помечает на гибель.
private void Graze(int plantId, float days) private void Graze(int plantId, float days, float bitePerDay)
{ {
if ( if (
plantId < 0 plantId < 0
@@ -584,7 +582,7 @@ public sealed class AnimalActionSystem : BaseSystem
} }
ref var grow = ref plant.GetComponent<PlantGrowth>(); ref var grow = ref plant.GetComponent<PlantGrowth>();
grow.AgeDays = MathF.Max(0f, grow.AgeDays - GrazePerDay * days); grow.AgeDays = MathF.Max(0f, grow.AgeDays - bitePerDay * days);
var isGrass = _plants[grow.Species].Def.TrunkRadiusCells <= 0f; var isGrass = _plants[grow.Species].Def.TrunkRadiusCells <= 0f;
if (isGrass && grow.AgeDays <= GrazeKillAge && !_eaten.Contains(plant)) if (isGrass && grow.AgeDays <= GrazeKillAge && !_eaten.Contains(plant))
{ {