Животные A5 (часть 1): смерть → труп → разложение
Компонент Corpse + CorpseSystem: после смерти животное оставляет труп (AnimalFactory.CreateCorpse, мясо ∝ размер тела) вместо деспавна; труп разлагается Fresh→Rotting→Skeleton→исчезает со скоростью от температуры, тинтуясь по стадии. AnimalDef.CorpseTexture + спрайт трупа в AnimalSet; смертность спавнит труп. Продукты мясо/кости/шкура заданы (разделка/падальщики — позже). Сборка чистая, --check-content (9 типов дефов). Части тела/кровь/capacities — часть 2 A5. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
cbc74c0794
commit
c90bf32460
@@ -742,19 +742,25 @@ public sealed class AnimalGrowthSystem(
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Смертность (фаза A3): простая гибель с деспавном — от старости (возраст превысил продолжительность
|
||||
/// жизни) или от истощения (счётчик голода/жажды превысил порог). Замыкает контур ёмкости среды.
|
||||
/// На A5 «апгрейдится» в смерть через здоровье и оставит труп вместо деспавна.
|
||||
/// Смертность (фаза A3/A5): гибель от старости (возраст превысил продолжительность жизни) или истощения
|
||||
/// (счётчик голода/жажды превысил порог). Замыкает контур ёмкости среды. Фаза A5: вместо деспавна
|
||||
/// оставляет <see cref="Corpse"/> (через <see cref="AnimalFactory.CreateCorpse"/>), затем удаляет особь.
|
||||
/// </summary>
|
||||
public sealed class AnimalMortalitySystem : BaseSystem
|
||||
{
|
||||
private const float LethalStarveDays = 3f;
|
||||
|
||||
private readonly EntityStore _store;
|
||||
private readonly AnimalSet _animals;
|
||||
private readonly int _cellSize;
|
||||
private readonly ArchetypeQuery<AnimalGrowth, AnimalOrganism, AnimalNeeds> _query;
|
||||
private readonly List<Entity> _deaths = [];
|
||||
|
||||
public AnimalMortalitySystem(EntityStore store)
|
||||
public AnimalMortalitySystem(EntityStore store, AnimalSet animals, int cellSize)
|
||||
{
|
||||
_store = store;
|
||||
_animals = animals;
|
||||
_cellSize = cellSize;
|
||||
_query = store.Query<AnimalGrowth, AnimalOrganism, AnimalNeeds>();
|
||||
}
|
||||
|
||||
@@ -775,7 +781,99 @@ public sealed class AnimalMortalitySystem : BaseSystem
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var entity in _deaths)
|
||||
foreach (var dead in _deaths)
|
||||
{
|
||||
if (dead.IsNull || !dead.HasComponent<AnimalOrganism>())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ref readonly var org = ref dead.GetComponent<AnimalOrganism>();
|
||||
var position = dead.GetComponent<Transform2D>().Position;
|
||||
AnimalFactory.CreateCorpse(
|
||||
_store,
|
||||
_animals[org.Species],
|
||||
position,
|
||||
org.Traits.BodySize,
|
||||
_cellSize
|
||||
);
|
||||
dead.DeleteEntity();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Разложение трупа (фаза A5): копит дни гниения (ускоряется в тепле, замедляется на морозе),
|
||||
/// переключает стадии Fresh→Rotting→Skeleton по порогам и тинтит спрайт; по достижении предела труп
|
||||
/// исчезает. Скелет рисуется бледным тинтом падали (отдельного спрайта пока нет — см. docs).
|
||||
/// </summary>
|
||||
public sealed class CorpseSystem : BaseSystem
|
||||
{
|
||||
private const float RottingDay = 2f;
|
||||
private const float SkeletonDay = 6f;
|
||||
private const float GoneDay = 14f;
|
||||
|
||||
private static readonly Color FreshTint = new(176, 148, 128);
|
||||
private static readonly Color RottingTint = new(120, 118, 98);
|
||||
private static readonly Color SkeletonTint = new(228, 226, 214);
|
||||
|
||||
private readonly GameClock _clock;
|
||||
private readonly float _secondsPerDay;
|
||||
private readonly Climate _climate;
|
||||
private readonly ArchetypeQuery<Corpse, Sprite> _query;
|
||||
private readonly List<Entity> _gone = [];
|
||||
|
||||
public CorpseSystem(EntityStore store, GameClock clock, float secondsPerDay, Climate climate)
|
||||
{
|
||||
_clock = clock;
|
||||
_secondsPerDay = secondsPerDay;
|
||||
_climate = climate;
|
||||
_query = store.Query<Corpse, Sprite>();
|
||||
}
|
||||
|
||||
protected override void OnUpdateGroup()
|
||||
{
|
||||
var days = _clock.DeltaTime / _secondsPerDay;
|
||||
if (days <= 0f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Тепло ускоряет гниение, около нуля и ниже — резко замедляет (заморозка).
|
||||
var factor = Math.Clamp((_climate.Temperature + 5f) / 20f, 0.1f, 2.5f);
|
||||
var advance = days * factor;
|
||||
_gone.Clear();
|
||||
|
||||
foreach (var (corpses, sprites, entities) in _query.Chunks)
|
||||
{
|
||||
var c = corpses.Span;
|
||||
var s = sprites.Span;
|
||||
for (var i = 0; i < c.Length; i++)
|
||||
{
|
||||
ref var corpse = ref c[i];
|
||||
corpse.RotDays += advance;
|
||||
if (corpse.RotDays >= GoneDay)
|
||||
{
|
||||
_gone.Add(entities.EntityAt(i));
|
||||
continue;
|
||||
}
|
||||
|
||||
var stage =
|
||||
corpse.RotDays >= SkeletonDay ? 2
|
||||
: corpse.RotDays >= RottingDay ? 1
|
||||
: 0;
|
||||
if (stage != corpse.Stage)
|
||||
{
|
||||
corpse.Stage = stage;
|
||||
s[i].Color =
|
||||
stage == 2 ? SkeletonTint
|
||||
: stage == 1 ? RottingTint
|
||||
: FreshTint;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var entity in _gone)
|
||||
{
|
||||
entity.DeleteEntity();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user