tarinai/js/item_lifecycle_decay_system.js
2026-07-18 13:06:02 +09:00

66 lines
2.7 KiB
JavaScript

"use strict";
// Layer: entity-runtime/items/decay-system
// Owns passive amount decay for water, food, transient traces, splats,
// and ant corpses. The lifecycle step is now only a
// thin pipeline adapter.
(function (global) {
const MEDICINE_LIKE_FOOD_TYPES = Object.freeze([
"sweet", "love_mochi", "fight_mochi", "sleep_drug", "laxative", "protein",
"niteropu", "ammo", "mystery_drug", "mercury", "giant_drug", "dwarf_drug", "zunda_juice",
]);
function decayServingFood(item, dt, worldRef) {
if (!isServingFoodType(item.type)) return false;
const interval = passiveFoodDecayInterval(worldRef);
item.passiveFoodDecayTimer = (item.passiveFoodDecayTimer || 0) + dt;
let changed = false;
let ticks = 0;
while (item.passiveFoodDecayTimer >= interval && ticks < 3) {
item.passiveFoodDecayTimer -= interval;
ticks++;
if (Number.isFinite(item.foodServingsRemaining)) {
const before = Math.max(0, item.foodServingsRemaining || 0);
const lost = Math.min(before, interval * passiveFoodDecayRate(item));
if (lost > 0) {
changed = true;
item.foodServingsRemaining = Math.max(0, before - lost);
item.amount = item.foodServingsRemaining;
const resized = (typeof updateServingFoodVisualSize === "function" ? updateServingFoodVisualSize(item) : false);
if (resized) {
worldRef?.markItemBucketsDirty?.("food-passive-resize");
worldRef?.markSpatialDirty?.("food-passive-resize");
if (worldRef) worldRef.drawListDirty = true;
}
if (item.foodServingsRemaining <= 0.015) item.amount = 0;
}
} else if (MEDICINE_LIKE_FOOD_TYPES.includes(item.type)) {
const before = item.amount || 0;
const lost = Math.min(before, interval * 0.12);
item.amount -= lost;
if (lost > 0) {
changed = true;
}
}
}
return changed;
}
function update(item, dt, worldRef) {
if (!item || item.dead) return { done: true };
if (item.type === "water") item.amount -= dt * 0.9;
decayServingFood(item, dt, worldRef);
if (item.type === "trace") item.amount -= dt * 1.35;
if (item.type === "splat") {
const before = Number(item.amount || 0) || 0;
item.amount -= dt * 1.05;
if (Math.floor(before / 18) !== Math.floor(Math.max(0, item.amount) / 18)) {
worldRef?.markTerrainDirtyAt?.(item.x, item.y, Math.max(item.r || item.radius || 24, 36), "splat-fade");
}
}
if (item.type === "ant_corpse") item.amount -= dt * 0.018;
return { done: false };
}
global.TarinaiItemDecaySystem = Object.freeze({ update });
})(typeof window !== "undefined" ? window : globalThis);