"use strict"; // Layer: entity-runtime/items/dynamic-system // Owns dynamic tool behavior for fan, magnet, rotator, firecracker, genkotsu, // and static gate-fence ticking. Item.prototype methods delegate here for // compatibility; lifecycle dispatch also calls this boundary directly. (function (global) { function support() { return global.TarinaiItemLifecycleRuntimeSupport || {}; } function updateFirecracker(item, dt, worldRef) { item.fuseTimer = Math.max(0, (item.fuseTimer ?? 5) - dt); if (item.fuseTimer <= 0) { if (worldRef?.explodeFirecracker) worldRef.explodeFirecracker(item); else item.amount = 0; } } function updateGenkotsu(item, dt) { item.impactFlash = Math.max(0, (item.impactFlash || 0) - dt); if (item.dropImpactDone) { item.lingerTimer = Math.max(0, (item.lingerTimer || 0) - dt); if ((item.lingerTimer || 0) <= 0) item.amount = 0; } else { item.amount = Math.max(item.amount || 0, 100); } } function updateFan(item, dt, worldRef) { if (!item || item.dead || item.type !== "fan") return false; item.amount = 999; if (!worldRef) return false; const { fanApplyToBody, itemAngleForRuntime } = support(); if (typeof fanApplyToBody !== "function") return false; let changed = false; const range = Math.max(190, (item.r || 32) * 7.4); for (const t of worldRef.nearbyTarinai?.(item.x, item.y, range + 80, true) || []) { if (!t || t.dead || worldRef.isTarinaiHiddenInNestBox?.(t)) continue; changed = fanApplyToBody(item, t, dt, worldRef, 1.0) || changed; } const kinematicTypes = new Set(["ball", "zunchi", "pushpin", "oshibyo"]); for (const it of worldRef.nearbyItems?.(item.x, item.y, range + 80, true) || []) { if (!it || it === item || it.dead || !kinematicTypes.has(it.type)) continue; changed = fanApplyToBody(item, it, dt, worldRef, it.type === "ball" ? 1.12 : 0.82) || changed; } const now = worldRef.time || 0; if (changed && (item.fanPulseAt || -999) + 0.12 <= now) { item.fanPulseAt = now; const angle = typeof itemAngleForRuntime === "function" ? itemAngleForRuntime(item) : (Number(item.angle) || 0); const x = item.x + Math.cos(angle) * (item.r || 32) * rand(0.8, 2.8); const y = item.y + Math.sin(angle) * (item.r || 32) * rand(0.8, 2.8) + rand(-10, 10); worldRef.effects?.push(new Effect("wind", x, y, { vx: Math.cos(angle) * rand(16, 40), vy: Math.sin(angle) * rand(16, 40), size: rand(8, 18), life: rand(0.18, 0.32), color: "rgba(160,210,245,0.28)" })); } if (changed) { worldRef.drawListDirty = true; worldRef.markSpatialDirty?.("fan-force"); } return changed; } function updateMagnet(item, dt, worldRef) { if (!item || item.dead || item.type !== "magnet") return false; item.amount = 999; if (!worldRef) return false; const { isMagnetTargetItem, magnetApplyToBody, magnetPolePoint } = support(); if (typeof isMagnetTargetItem !== "function" || typeof magnetApplyToBody !== "function") return false; let changed = false; const range = Math.max(170, (item.r || 30) * 7.4); for (const it of worldRef.nearbyItems?.(item.x, item.y, range + 80, true) || []) { if (!isMagnetTargetItem(it)) continue; if (it.type === "pushpin" || it.type === "oshibyo") { if (it.pinState === "lodged") continue; } changed = magnetApplyToBody(item, it, dt, worldRef, it.type === "ball" ? 0.42 : 1.0) || changed; } for (const t of worldRef.nearbyTarinai?.(item.x, item.y, range + 80, true) || []) { if (!t || t.dead || worldRef.isTarinaiHiddenInNestBox?.(t)) continue; const lodged = t.currentLodgedPin?.() || (worldRef.items || []).find(it => it && isPinType(it.type) && it.pinState === "lodged" && it.pinTargetId === t.id); if (!lodged) continue; changed = magnetApplyToBody(item, t, dt, worldRef, 0.55) || changed; } const now = worldRef.time || 0; if (changed && (item.magnetPulseAt || -999) + 0.18 <= now) { item.magnetPulseAt = now; const pole = typeof magnetPolePoint === "function" ? magnetPolePoint(item) : { x: item.x, y: item.y }; worldRef.effects?.push(new Effect("ring", pole.x, pole.y, { size: Math.max(12, (item.r || 30) * 0.85), life: 0.16, color: "rgba(120,150,230,0.34)" })); } if (changed) { worldRef.drawListDirty = true; worldRef.markSpatialDirty?.("magnet-force"); } return changed; } function updateRotator(item, dt, worldRef) { if (!item || item.dead || item.type !== "rotator") return false; item.amount = 999; const speed = Number(item.rotatorSpeed || 0) || 0; if (!speed || !Number.isFinite(speed)) return false; const before = Number(item.angle || 0) || 0; item.angle = typeof normalizedItemAngle === "function" ? normalizedItemAngle(before + speed * dt, 0) : before + speed * dt; const extent = worldRef?.rotatorExtent?.(item); if (Number.isFinite(extent)) item.r = Math.max(item.r || 64, Math.min(460, extent)); item.rotatorSpinPhase = (item.rotatorSpinPhase || 0) + Math.abs(speed) * dt; worldRef.drawListDirty = true; worldRef.markSpatialDirty?.("rotator-spin"); return true; } function updateGateFence(item) { if (item) item.amount = 999; return Boolean(item); } function update(item, dt, worldRef) { if (!item || item.dead) return false; if (item.type === "firecracker") return updateFirecracker(item, dt, worldRef); if (item.type === "genkotsu") return updateGenkotsu(item, dt, worldRef); if (item.type === "fan") return updateFan(item, dt, worldRef); if (item.type === "magnet") return updateMagnet(item, dt, worldRef); if (item.type === "rotator") return updateRotator(item, dt, worldRef); if (item.type === "gate_fence") return updateGateFence(item, dt, worldRef); return false; } global.TarinaiItemDynamicToolSystem = Object.freeze({ update, updateFirecracker, updateGenkotsu, updateFan, updateMagnet, updateRotator, updateGateFence, }); })(typeof window !== "undefined" ? window : globalThis);