Senescence: aging degrades capacities and fertility
GeneLifespan already set the moment of death from old age; now aging actually manifests before it. RecomputeCapacities takes a capacityScale; AnimalHealthSystem applies SenescenceFactor(age, lifespan) — capacities fade from 60% of lifespan to ~0.55× near the end, so old animals move/see/filter blood worse and are frailer. Fertility falls with the same factor (smaller litters), and past 90% of lifespan females no longer conceive (post-reproductive). Makes GeneLifespan and the Senior stage meaningful. Build clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
249b603497
commit
e6b1367a3a
@@ -191,9 +191,11 @@ public sealed class HealthState
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Пересчитывает боль (из хедифов) и способности: базовые из частей/крови/боли, затем модификаторы
|
/// Пересчитывает боль (из хедифов) и способности: базовые из частей/крови/боли, затем модификаторы
|
||||||
/// способностей от хедифов (лерп 1→factor по тяжести). Вызывать после изменения частей/крови/хедифов.
|
/// способностей от хедифов (лерп 1→factor по тяжести), затем общий множитель <paramref name="capacityScale"/>
|
||||||
|
/// (старение/сенесценс: дряхлеющий организм слабее по всем способностям). Вызывать после изменения
|
||||||
|
/// частей/крови/хедифов; <paramref name="capacityScale"/>=1 — без возрастного спада.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void RecomputeCapacities()
|
public void RecomputeCapacities(float capacityScale = 1f)
|
||||||
{
|
{
|
||||||
var pain = 0f;
|
var pain = 0f;
|
||||||
foreach (var h in _hediffs)
|
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<string>(caps.Keys))
|
||||||
|
{
|
||||||
|
caps[capacity] *= scale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Capacities = caps;
|
Capacities = caps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -790,6 +790,7 @@ public sealed class AnimalActionSystem : BaseSystem
|
|||||||
private const float GrazeKillAge = 1f; // трава с возрастом ниже этого после выедания исчезает
|
private const float GrazeKillAge = 1f; // трава с возрастом ниже этого после выедания исчезает
|
||||||
private const float ThirstReliefBelow = 0.15f; // ниже этого жажда — питьё даёт мысль «напилась»
|
private const float ThirstReliefBelow = 0.15f; // ниже этого жажда — питьё даёт мысль «напилась»
|
||||||
private const float FleeSpeedMult = 1.2f; // страх подгоняет жертву чуть быстрее обычного шага
|
private const float FleeSpeedMult = 1.2f; // страх подгоняет жертву чуть быстрее обычного шага
|
||||||
|
private const float PostReproductiveFrac = 0.9f; // доля жизни, после которой самка уже не зачинает
|
||||||
|
|
||||||
private readonly EntityStore _store;
|
private readonly EntityStore _store;
|
||||||
private readonly PlantSet _plants;
|
private readonly PlantSet _plants;
|
||||||
@@ -1216,15 +1217,25 @@ public sealed class AnimalActionSystem : BaseSystem
|
|||||||
if (!mother.HasComponent<Pregnant>())
|
if (!mother.HasComponent<Pregnant>())
|
||||||
{
|
{
|
||||||
ref readonly var mom = ref mother.GetComponent<AnimalOrganism>();
|
ref readonly var mom = ref mother.GetComponent<AnimalOrganism>();
|
||||||
|
// Плодовитость падает со старостью (тот же фактор сенесценса): дряхлая самка приносит
|
||||||
|
// меньший помёт, а после порога пострепродуктивного возраста уже не зачинает.
|
||||||
|
var momAge = mother.HasComponent<AnimalGrowth>()
|
||||||
|
? mother.GetComponent<AnimalGrowth>().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(
|
mother.AddComponent(
|
||||||
new Pregnant
|
new Pregnant
|
||||||
{
|
{
|
||||||
FatherGenome = father.GetComponent<AnimalOrganism>().Genome,
|
FatherGenome = father.GetComponent<AnimalOrganism>().Genome,
|
||||||
DueInDays = MathF.Max(1f, mom.Traits.GestationDays),
|
DueInDays = MathF.Max(1f, mom.Traits.GestationDays),
|
||||||
Litter = Math.Max(1, (int)MathF.Round(mom.Traits.LitterSize)),
|
Litter = Math.Max(1, (int)MathF.Round(mom.Traits.LitterSize * fert)),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ResetMating(a, mateNeed);
|
ResetMating(a, mateNeed);
|
||||||
ResetMating(b, mateNeed);
|
ResetMating(b, mateNeed);
|
||||||
@@ -1720,10 +1731,15 @@ public sealed class AnimalHealthSystem : BaseSystem
|
|||||||
private readonly float _secondsPerDay;
|
private readonly float _secondsPerDay;
|
||||||
private readonly HediffDef[] _ambient;
|
private readonly HediffDef[] _ambient;
|
||||||
private readonly Random _rng;
|
private readonly Random _rng;
|
||||||
private readonly ArchetypeQuery<Health, AnimalOrganism> _query;
|
private readonly ArchetypeQuery<Health, AnimalOrganism, AnimalGrowth> _query;
|
||||||
private readonly List<Entity> _deaths = [];
|
private readonly List<Entity> _deaths = [];
|
||||||
private float _accum;
|
private float _accum;
|
||||||
|
|
||||||
|
// Сенесценс (старение): спад способностей начинается с этой доли продолжительности жизни и достигает
|
||||||
|
// максимума к её концу. Дряхлеющий зверь слабее (медленнее, хуже видит/фильтрует кровь) и уязвимее.
|
||||||
|
private const float SenescenceStart = 0.6f;
|
||||||
|
private const float SenescenceMax = 0.45f;
|
||||||
|
|
||||||
public AnimalHealthSystem(
|
public AnimalHealthSystem(
|
||||||
EntityStore store,
|
EntityStore store,
|
||||||
AnimalSet animals,
|
AnimalSet animals,
|
||||||
@@ -1741,7 +1757,19 @@ public sealed class AnimalHealthSystem : BaseSystem
|
|||||||
_secondsPerDay = secondsPerDay;
|
_secondsPerDay = secondsPerDay;
|
||||||
_ambient = ambient;
|
_ambient = ambient;
|
||||||
_rng = new Random(seed);
|
_rng = new Random(seed);
|
||||||
_query = store.Query<Health, AnimalOrganism>();
|
_query = store.Query<Health, AnimalOrganism, AnimalGrowth>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Множитель способностей от старения (1 — молод/расцвет, <1 — дряхлеет к концу жизни).</summary>
|
||||||
|
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()
|
protected override void OnUpdateGroup()
|
||||||
@@ -1756,9 +1784,11 @@ public sealed class AnimalHealthSystem : BaseSystem
|
|||||||
_accum = 0f;
|
_accum = 0f;
|
||||||
_deaths.Clear();
|
_deaths.Clear();
|
||||||
|
|
||||||
foreach (var (healths, organisms, entities) in _query.Chunks)
|
foreach (var (healths, organisms, growths, entities) in _query.Chunks)
|
||||||
{
|
{
|
||||||
var h = healths.Span;
|
var h = healths.Span;
|
||||||
|
var o = organisms.Span;
|
||||||
|
var g = growths.Span;
|
||||||
for (var i = 0; i < h.Length; i++)
|
for (var i = 0; i < h.Length; i++)
|
||||||
{
|
{
|
||||||
var state = h[i].State;
|
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 (
|
if (
|
||||||
lethal
|
lethal
|
||||||
|| state.BloodLevel <= 0f
|
|| state.BloodLevel <= 0f
|
||||||
|
|||||||
Reference in New Issue
Block a user