stable
This commit is contained in:
parent
fbdac9ebf1
commit
86226ccc94
97 changed files with 5790 additions and 2307 deletions
136
js/item_dynamic_tool_system.js
Normal file
136
js/item_dynamic_tool_system.js
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
"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);
|
||||
Loading…
Add table
Add a link
Reference in a new issue