tarinai/js/item_update_policy.js

100 lines
4.6 KiB
JavaScript
Raw Normal View History

2026-06-26 12:46:32 +09:00
"use strict";
// Layer: entity-runtime/items/policy
// Owns the item update cadence and scheduled-item candidate set. Simulation
// helpers and the item scheduler both read this single policy instead of
// duplicating item-type timing rules.
(function (global) {
2026-07-03 20:57:02 +09:00
const REALTIME_ITEM_TYPES = new Set(["ball", "balloon", "fan", "genkotsu", "firecracker", "flame_firecracker", "sticky_bomb", "robot_cleaner"]);
2026-06-26 12:46:32 +09:00
const PIN_ITEM_TYPES = new Set(["pushpin", "oshibyo"]);
const SCHEDULED_ITEM_TYPES = Object.freeze([
2026-07-03 20:57:02 +09:00
"ball", "balloon", "fan", "genkotsu", "firecracker", "flame_firecracker", "sticky_bomb", "fire", "pushpin", "oshibyo",
2026-06-29 21:02:04 +09:00
"plushie", "grass_bed", "zunchi", "robot_cleaner", "duplicator", "water", "trace", "splat", "ant_corpse", "food",
2026-06-26 12:46:32 +09:00
"sweet", "love_mochi", "fight_mochi", "sleep_drug", "laxative", "protein", "niteropu", "ammo", "mystery_drug", "mercury", "giant_drug", "dwarf_drug", "zunda_juice",
2026-07-03 00:45:22 +09:00
"stone", "bed", "nest_box", "pipe", "ant_nest", "signboard", "fence_v", "fence_h", "bounce_fence", "bounce_fence_v", "gate_fence", "rotator", "poison_block", "reciprocator", "rope", "rod",
2026-06-26 12:46:32 +09:00
]);
2026-06-28 13:40:41 +09:00
function mobilePerfHint() {
2026-06-29 16:21:58 +09:00
return Boolean(global.TarinaiInputMode.snapshot().touchFirst || (global.innerWidth || 9999) <= 760);
2026-06-28 13:40:41 +09:00
}
2026-06-26 12:46:32 +09:00
function itemUpdateInterval(item) {
if (!item || item.dead) return Infinity;
2026-06-28 13:40:41 +09:00
const mobile = mobilePerfHint();
2026-06-27 19:22:38 +09:00
// Mechanical bodies and links are owned by the central physics step.
// Keeping them out of the per-item scheduler prevents N independent
// collision passes and makes future physics-format changes cheaper.
if (item.type === "rotator" || item.type === "reciprocator" || item.type === "poison_block" || item.type === "rope" || item.type === "rod") return Infinity;
2026-06-26 12:46:32 +09:00
if (REALTIME_ITEM_TYPES.has(item.type)) return 0;
if (PIN_ITEM_TYPES.has(item.type)) return 0;
if (item.isStructure && item.type === "plushie") return 0;
if (item.isStructure && item.type === "grass_bed") return 3.0;
2026-07-01 14:41:05 +09:00
if (item.type === "fire") {
const fireCount = item.world?.itemsOfType?.("fire")?.length || 0;
if (fireCount > 64) return mobile ? 0.55 : 0.35;
if (fireCount > 36) return mobile ? 0.42 : 0.25;
return mobile ? 0.30 : 0.18;
}
2026-07-05 18:01:36 +09:00
if (item.type === "nest_box" && global.TarinaiItemDynamicToolSystem?.nestBoxFireActive?.(item)) return mobile ? 0.30 : 0.18;
2026-06-26 12:46:32 +09:00
if ((item.dropTimer || 0) > 0 || Math.hypot(item.vx || 0, item.vy || 0) > 0.08) return 0;
2026-07-05 18:01:36 +09:00
if (item.type === "zunchi" && (item.burning || (item.burnTimer || 0) > 0.02)) return mobile ? 0.30 : 0.18;
if (item.type === "zunchi") {
const groundId = item.world?.groundType || (typeof world !== "undefined" ? world?.groundType : "") || "soil";
const decayMul = Number(global.TarinaiGround?.zunchiDecayMultiplier?.(groundId) || 1) || 1;
// High-decay grounds need a shorter update cadence; otherwise the amount
// changes only once every few seconds and the laboratory effect looks idle.
if (decayMul >= 100) return mobile ? 0.18 : 0.12;
if (decayMul > 1.01) return mobile ? 0.42 : 0.28;
return 2.4;
}
2026-06-26 12:46:32 +09:00
if (item.type === "grass") return Infinity;
2026-06-28 13:40:41 +09:00
if (item.type === "duplicator") return mobile ? 0.8 : 0.5;
if (item.type === "water") return mobile ? 2.4 : 1.8;
if (item.type === "trace" || item.type === "splat") return mobile ? 1.8 : 1.2;
2026-06-26 12:46:32 +09:00
if (item.type === "ant_corpse") return 3.5;
if (typeof global.isServingFoodType === "function" && global.isServingFoodType(item.type)) return 5.0;
2026-06-29 16:21:58 +09:00
if (isServingFoodType(item.type)) return 5.0;
2026-06-26 12:46:32 +09:00
return Infinity;
}
function ensureBuckets(worldRef) {
if (worldRef?.ensureItemBuckets) worldRef.ensureItemBuckets("item-update-policy");
}
function forScheduledItems(worldRef, visitor) {
if (!worldRef || typeof visitor !== "function") return 0;
let count = 0;
if (!worldRef.itemsOfType) {
for (const it of worldRef.items || []) {
if (!it || it.dead || it.type === "grass") continue;
visitor(it);
count += 1;
}
return count;
}
ensureBuckets(worldRef);
for (const type of SCHEDULED_ITEM_TYPES) {
const bucket = worldRef.itemsOfType(type);
for (const it of bucket || []) {
if (!it || it.dead) continue;
visitor(it);
count += 1;
}
}
return count;
}
function candidateItems(worldRef) {
const out = [];
forScheduledItems(worldRef, (item) => out.push(item));
return out;
}
global.TarinaiItemUpdatePolicy = Object.freeze({
itemUpdateInterval,
forScheduledItems,
ensureBuckets,
candidateItems,
});
})(typeof window !== "undefined" ? window : globalThis);