diff --git a/src/LittleSim/Sim/AnimalOrganism.cs b/src/LittleSim/Sim/AnimalOrganism.cs
index 1d277a9..6523e76 100644
--- a/src/LittleSim/Sim/AnimalOrganism.cs
+++ b/src/LittleSim/Sim/AnimalOrganism.cs
@@ -191,9 +191,11 @@ public sealed class HealthState
///
/// Пересчитывает боль (из хедифов) и способности: базовые из частей/крови/боли, затем модификаторы
- /// способностей от хедифов (лерп 1→factor по тяжести). Вызывать после изменения частей/крови/хедифов.
+ /// способностей от хедифов (лерп 1→factor по тяжести), затем общий множитель
+ /// (старение/сенесценс: дряхлеющий организм слабее по всем способностям). Вызывать после изменения
+ /// частей/крови/хедифов; =1 — без возрастного спада.
///
- public void RecomputeCapacities()
+ public void RecomputeCapacities(float capacityScale = 1f)
{
var pain = 0f;
foreach (var h in _hediffs)
@@ -218,6 +220,15 @@ public sealed class HealthState
}
}
+ if (capacityScale < 1f)
+ {
+ var scale = Math.Clamp(capacityScale, 0f, 1f);
+ foreach (var capacity in new List(caps.Keys))
+ {
+ caps[capacity] *= scale;
+ }
+ }
+
Capacities = caps;
}
diff --git a/src/LittleSim/Sim/AnimalSystems.cs b/src/LittleSim/Sim/AnimalSystems.cs
index 92539f5..c7d78bf 100644
--- a/src/LittleSim/Sim/AnimalSystems.cs
+++ b/src/LittleSim/Sim/AnimalSystems.cs
@@ -790,6 +790,7 @@ public sealed class AnimalActionSystem : BaseSystem
private const float GrazeKillAge = 1f; // трава с возрастом ниже этого после выедания исчезает
private const float ThirstReliefBelow = 0.15f; // ниже этого жажда — питьё даёт мысль «напилась»
private const float FleeSpeedMult = 1.2f; // страх подгоняет жертву чуть быстрее обычного шага
+ private const float PostReproductiveFrac = 0.9f; // доля жизни, после которой самка уже не зачинает
private readonly EntityStore _store;
private readonly PlantSet _plants;
@@ -1216,14 +1217,24 @@ public sealed class AnimalActionSystem : BaseSystem
if (!mother.HasComponent())
{
ref readonly var mom = ref mother.GetComponent();
- mother.AddComponent(
- new Pregnant
- {
- FatherGenome = father.GetComponent().Genome,
- DueInDays = MathF.Max(1f, mom.Traits.GestationDays),
- Litter = Math.Max(1, (int)MathF.Round(mom.Traits.LitterSize)),
- }
- );
+ // Плодовитость падает со старостью (тот же фактор сенесценса): дряхлая самка приносит
+ // меньший помёт, а после порога пострепродуктивного возраста уже не зачинает.
+ var momAge = mother.HasComponent()
+ ? mother.GetComponent().AgeDays
+ : 0f;
+ var ageFrac = mom.Traits.Lifespan > 0f ? momAge / mom.Traits.Lifespan : 0f;
+ if (ageFrac <= PostReproductiveFrac)
+ {
+ var fert = AnimalHealthSystem.SenescenceFactor(momAge, mom.Traits.Lifespan);
+ mother.AddComponent(
+ new Pregnant
+ {
+ FatherGenome = father.GetComponent().Genome,
+ DueInDays = MathF.Max(1f, mom.Traits.GestationDays),
+ Litter = Math.Max(1, (int)MathF.Round(mom.Traits.LitterSize * fert)),
+ }
+ );
+ }
}
ResetMating(a, mateNeed);
@@ -1720,10 +1731,15 @@ public sealed class AnimalHealthSystem : BaseSystem
private readonly float _secondsPerDay;
private readonly HediffDef[] _ambient;
private readonly Random _rng;
- private readonly ArchetypeQuery _query;
+ private readonly ArchetypeQuery _query;
private readonly List _deaths = [];
private float _accum;
+ // Сенесценс (старение): спад способностей начинается с этой доли продолжительности жизни и достигает
+ // максимума к её концу. Дряхлеющий зверь слабее (медленнее, хуже видит/фильтрует кровь) и уязвимее.
+ private const float SenescenceStart = 0.6f;
+ private const float SenescenceMax = 0.45f;
+
public AnimalHealthSystem(
EntityStore store,
AnimalSet animals,
@@ -1741,7 +1757,19 @@ public sealed class AnimalHealthSystem : BaseSystem
_secondsPerDay = secondsPerDay;
_ambient = ambient;
_rng = new Random(seed);
- _query = store.Query();
+ _query = store.Query();
+ }
+
+ /// Множитель способностей от старения (1 — молод/расцвет, <1 — дряхлеет к концу жизни).
+ public static float SenescenceFactor(float ageDays, float lifespan)
+ {
+ if (lifespan <= 0f)
+ {
+ return 1f;
+ }
+
+ var frac = (ageDays / lifespan - SenescenceStart) / MathF.Max(0.01f, 1f - SenescenceStart);
+ return 1f - SenescenceMax * Math.Clamp(frac, 0f, 1f);
}
protected override void OnUpdateGroup()
@@ -1756,9 +1784,11 @@ public sealed class AnimalHealthSystem : BaseSystem
_accum = 0f;
_deaths.Clear();
- foreach (var (healths, organisms, entities) in _query.Chunks)
+ foreach (var (healths, organisms, growths, entities) in _query.Chunks)
{
var h = healths.Span;
+ var o = organisms.Span;
+ var g = growths.Span;
for (var i = 0; i < h.Length; i++)
{
var state = h[i].State;
@@ -1804,7 +1834,9 @@ public sealed class AnimalHealthSystem : BaseSystem
);
}
- state.RecomputeCapacities();
+ // Старение: к концу жизни способности дряхлеют (медленнее/слабее, уязвимее к ранам/болезни).
+ var senescence = SenescenceFactor(g[i].AgeDays, o[i].Traits.Lifespan);
+ state.RecomputeCapacities(senescence);
if (
lethal
|| state.BloodLevel <= 0f