Рефактор: зрение/скорость/выедание животного — из дефа вида
Завершает вынос баланса в данные: 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:
co-authored by
Claude Opus 4.8
parent
5d0cbd2115
commit
cbc74c0794
@@ -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>
|
||||||
|
|||||||
@@ -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));
|
||||||
|
|||||||
@@ -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))
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user