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

Завершает вынос баланса в данные: 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>
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>
+9 -1
View File
@@ -228,7 +228,15 @@ public sealed class WorldScene : Scene
)
);
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 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
{
private const float Interval = 0.6f;
private const float VisionCells = 14f; // базовый радиус зрения в клетках (× ген vision)
private readonly UtilityAi<AnimalContext> _brain;
private readonly GameClock _clock;
@@ -257,6 +256,7 @@ public sealed class AnimalDecisionSystem : BaseSystem
brain.DecideIn = Interval + _rng.NextSingle() * Interval; // джиттер — расфазировать скан
var pos = t[i].Position;
var adult = AnimalFactory.IsAdult(_animalSet[o[i].Species], gr[i].Stage);
var radius = VisionRadius(o[i].Species, o[i].Traits.Vision);
var name =
_brain.Select(new AnimalContext(n[i].Values))?.Name ?? AnimalActions.Wander;
@@ -264,7 +264,7 @@ public sealed class AnimalDecisionSystem : BaseSystem
{
case AnimalActions.Eat
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.TargetPlant = plant;
brain.Target = pp;
@@ -280,13 +280,7 @@ public sealed class AnimalDecisionSystem : BaseSystem
break;
case AnimalActions.Mate
when adult
&& TryFindMate(
pos,
o[i].IsMale,
o[i].Traits.Vision,
out var mateId,
out var matePos
):
&& TryFindMate(pos, o[i].IsMale, radius, out var mateId, out var matePos):
brain.Action = AnimalActions.Mate;
brain.TargetPlant = -1;
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) появятся на горизонте.
private bool EatsPlants(int species)
{
@@ -321,10 +319,9 @@ public sealed class AnimalDecisionSystem : BaseSystem
return false;
}
// Ближайшее растение в радиусе зрения (квадрат расстояния); ничьи — по меньшему id (детерминизм).
private bool TryFindPlant(Vector2 from, float vision, out int plantId, out Vector2 position)
// Ближайшее растение в радиусе (квадрат расстояния); ничьи — по меньшему id (детерминизм).
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;
plantId = -1;
position = default;
@@ -374,12 +371,11 @@ public sealed class AnimalDecisionSystem : BaseSystem
private bool TryFindMate(
Vector2 from,
bool selfMale,
float vision,
float radius,
out int mateId,
out Vector2 position
)
{
var radius = VisionCells * MathF.Max(0.2f, vision) * _cellSize;
var bestSq = radius * radius;
mateId = -1;
position = default;
@@ -428,12 +424,11 @@ public sealed class AnimalDecisionSystem : BaseSystem
/// </summary>
public sealed class AnimalActionSystem : BaseSystem
{
private const float BaseSpeed = 28f; // мировых единиц в секунду при moveSpeed 1
private const float GrazePerDay = 40f; // на сколько игровых дней роста убавляется выеденное растение
private const float GrazeKillAge = 1f; // трава с возрастом ниже этого после выедания исчезает
private readonly EntityStore _store;
private readonly PlantSet _plants;
private readonly AnimalSet _animalSet;
private readonly NeedSet _needs;
private readonly GameClock _clock;
private readonly float _secondsPerDay;
@@ -445,6 +440,7 @@ public sealed class AnimalActionSystem : BaseSystem
public AnimalActionSystem(
EntityStore store,
PlantSet plants,
AnimalSet animals,
NeedSet needs,
GameClock clock,
float secondsPerDay,
@@ -453,6 +449,7 @@ public sealed class AnimalActionSystem : BaseSystem
{
_store = store;
_plants = plants;
_animalSet = animals;
_needs = needs;
_clock = clock;
_secondsPerDay = secondsPerDay;
@@ -483,7 +480,8 @@ public sealed class AnimalActionSystem : BaseSystem
ref var brain = ref b[i];
var values = n[i].Values;
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)
{
@@ -495,7 +493,7 @@ public sealed class AnimalActionSystem : BaseSystem
if (MoveTo(ref pos, brain.Target, speed * seconds))
{
Refill(values, AnimalActions.Eat, days);
Graze(brain.TargetPlant, days);
Graze(brain.TargetPlant, days, def.ForageBiteDays);
}
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 (
plantId < 0
@@ -584,7 +582,7 @@ public sealed class AnimalActionSystem : BaseSystem
}
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;
if (isGrass && grow.AgeDays <= GrazeKillAge && !_eaten.Contains(plant))
{