hm
This commit is contained in:
parent
cefa2ace5a
commit
0e432c62ae
87 changed files with 4282 additions and 3066 deletions
|
|
@ -1,33 +1,4 @@
|
|||
"use strict";
|
||||
(function ensureDeterministicHelpers(global) {
|
||||
if (typeof global.deterministicChance === "function" && typeof global.deterministicRange === "function") return;
|
||||
const clamp01 = v => Math.max(0, Math.min(1, Number(v) || 0));
|
||||
const keyPart = value => {
|
||||
if (value == null) return "null";
|
||||
if (typeof value === "number") return Number.isFinite(value) ? value.toFixed(3) : "nan";
|
||||
if (typeof value === "string" || typeof value === "boolean") return String(value);
|
||||
if (typeof value === "object") {
|
||||
const id = value.familyKey || value.id || value.seed || value.liveToken || value.name;
|
||||
if (id) return `${value.type || value.constructor?.name || "entity"}:${id}`;
|
||||
const x = Number.isFinite(value.x) ? value.x.toFixed(1) : "x";
|
||||
const y = Number.isFinite(value.y) ? value.y.toFixed(1) : "y";
|
||||
return `${value.type || value.constructor?.name || "entity"}@${x},${y}`;
|
||||
}
|
||||
return String(value);
|
||||
};
|
||||
const hashUnit = (seed, salt = "") => {
|
||||
const str = `${seed}:${salt}`;
|
||||
let h = 2166136261;
|
||||
for (let i = 0; i < str.length; i++) { h ^= str.charCodeAt(i); h = Math.imul(h, 16777619); }
|
||||
return ((h >>> 0) % 100000) / 100000;
|
||||
};
|
||||
global.deterministicFrame = global.deterministicFrame || ((worldRef = null, hz = 60) => Math.floor((Number(worldRef?.time || 0) || 0) * Math.max(1, Number(hz) || 60) + 1e-6));
|
||||
global.deterministicUnit = global.deterministicUnit || ((worldRef = null, salt = "", ...parts) => hashUnit(String(worldRef?.worldSeed || "tarinai-world"), [salt, global.deterministicFrame(worldRef, 60), Number(worldRef?._deterministicEpoch || 0) || 0, ...parts.map(keyPart)].join("|")));
|
||||
global.deterministicRange = global.deterministicRange || ((worldRef = null, salt = "", min = 0, max = 1, ...parts) => (Number(min) || 0) + global.deterministicUnit(worldRef, salt, ...parts) * ((Number(max) || 0) - (Number(min) || 0)));
|
||||
global.deterministicSigned = global.deterministicSigned || ((worldRef = null, salt = "", ...parts) => global.deterministicUnit(worldRef, salt, ...parts) < 0.5 ? -1 : 1);
|
||||
global.deterministicChance = global.deterministicChance || ((worldRef = null, salt = "", chance = 0, ...parts) => { const p = clamp01(chance); return p >= 1 || (p > 0 && global.deterministicUnit(worldRef, salt, ...parts) < p); });
|
||||
global.deterministicAngle = global.deterministicAngle || ((worldRef = null, salt = "", ...parts) => global.deterministicRange(worldRef, salt, 0, Math.PI * 2, ...parts));
|
||||
})(typeof window !== "undefined" ? window : globalThis);
|
||||
|
||||
|
||||
// Layer: entity-runtime/items/dynamic-system
|
||||
|
|
@ -35,7 +6,121 @@
|
|||
// behavior. Prototype methods delegate here.
|
||||
(function (global) {
|
||||
function isPin(item) {
|
||||
return Boolean(item && typeof global.isPinType === "function" && global.isPinType(item.type));
|
||||
return Boolean(global.TarinaiPinAttachmentSystem?.isPin?.(item) || (item && typeof global.isPinType === "function" && global.isPinType(item.type)));
|
||||
}
|
||||
|
||||
function ballById(worldRef, id) {
|
||||
if (!worldRef || !id) return null;
|
||||
return (worldRef.items || []).find(it => it && !it.dead && it.type === "ball" && it.id === id) || null;
|
||||
}
|
||||
|
||||
function removeFromBall(item, worldRef = null) {
|
||||
const ball = ballById(worldRef, item?.pinBallId) || null;
|
||||
if (ball?.stuckPinIds) ball.stuckPinIds = ball.stuckPinIds.filter(id => id !== item.id);
|
||||
if (ball && ball.stuckPinId === item?.id) ball.stuckPinId = "";
|
||||
if (item) item.pinBallId = "";
|
||||
return ball;
|
||||
}
|
||||
|
||||
function updateBallLodged(item, dt, worldRef) {
|
||||
const ball = ballById(worldRef, item.pinBallId);
|
||||
if (!ball) {
|
||||
detach(item, worldRef, "ball-lost");
|
||||
return;
|
||||
}
|
||||
if (global.TarinaiPinAttachmentSystem?.applyBallPose?.(item, ball, worldRef)) return;
|
||||
const angle = (ball.spin || 0) + (item.pinBallOffsetAngle || 0);
|
||||
const distance = Math.max(4, Number(item.pinBallOffsetDistance || (ball.r || 18) * 0.55) || (ball.r || 18) * 0.55);
|
||||
item.prevX = item.x;
|
||||
item.prevY = item.y;
|
||||
item.x = ball.x + Math.cos(angle) * distance;
|
||||
item.y = ball.y + Math.sin(angle) * distance;
|
||||
item.vx = ball.vx || 0;
|
||||
item.vy = ball.vy || 0;
|
||||
item.pinVisualSide = Math.cos(angle) >= 0 ? 1 : -1;
|
||||
item.spin = angle + (item.type === "oshibyo" ? 0.18 : Math.PI * 0.5);
|
||||
item.deletable = false;
|
||||
if (Number.isFinite(ball.spinVelocity)) item.spinVelocity = ball.spinVelocity;
|
||||
if (worldRef) worldRef.drawListDirty = true;
|
||||
}
|
||||
|
||||
function attachToBall(item, ball, worldRef) {
|
||||
if (!isPin(item) || !ball || ball.dead || ball.type !== "ball" || item.dead) return false;
|
||||
if (item.pinState === "lodged" || item.pinState === "ball_lodged") return false;
|
||||
const now = worldRef?.time || 0;
|
||||
if ((item.noStickUntil || 0) > now) return false;
|
||||
const angle = deterministicAngle(worldRef, "pin-ball-stick-angle", item, ball, now);
|
||||
const dist = Math.max(4, (ball.r || 18) * deterministicRange(worldRef, "pin-ball-stick-distance", 0.24, 0.78, item, ball, now));
|
||||
item.pinState = "ball_lodged";
|
||||
item.pinBallId = ball.id;
|
||||
item.pinTargetId = "";
|
||||
item.pinBallOffsetAngle = angle - (ball.spin || 0);
|
||||
item.pinBallOffsetDistance = dist;
|
||||
item.pinVisualSide = Math.cos(angle) >= 0 ? 1 : -1;
|
||||
item.deletable = false;
|
||||
item.noStickUntil = now + 0.16;
|
||||
ball.stuckPinIds = Array.isArray(ball.stuckPinIds) ? ball.stuckPinIds : [];
|
||||
if (!ball.stuckPinIds.includes(item.id)) ball.stuckPinIds.push(item.id);
|
||||
ball.stuckPinId = ball.stuckPinIds[0] || item.id;
|
||||
updateBallLodged(item, 0, worldRef);
|
||||
worldRef?.effects?.push(new Effect("ring", item.x, item.y, { size: Math.max(12, (item.r || 8) * 1.4), life: 0.16, color: item.type === "oshibyo" ? "rgba(73,119,205,0.42)" : "rgba(214,72,72,0.42)" }));
|
||||
return true;
|
||||
}
|
||||
|
||||
function transferFromBallToTarinai(item, ball, t, worldRef) {
|
||||
if (!item || item.dead || item.pinState !== "ball_lodged" || !ball || !t || t.dead) return false;
|
||||
const now = worldRef?.time || 0;
|
||||
if ((item._ballPinTransferredAt || -999) + 0.18 > now) return false;
|
||||
removeFromBall(item, worldRef);
|
||||
item.pinState = "loose";
|
||||
item.pinTargetId = "";
|
||||
item.noStickUntil = 0;
|
||||
item.noStickTargetId = "";
|
||||
item.noStickTargetUntil = 0;
|
||||
item.x = ball.x + deterministicRange(worldRef, "pin-transfer-x", -(ball.r || 18) * 0.25, (ball.r || 18) * 0.25, item, ball, t);
|
||||
item.y = ball.y + deterministicRange(worldRef, "pin-transfer-y", -(ball.r || 18) * 0.25, (ball.r || 18) * 0.25, item, ball, t);
|
||||
item._ballPinTransferredAt = now;
|
||||
const ok = attach(item, t, worldRef, distXY(item.x, item.y, t.x, t.y));
|
||||
if (!ok) attachToBall(item, ball, worldRef);
|
||||
return ok;
|
||||
}
|
||||
|
||||
function transferBallPinsToTarinai(ball, t, worldRef) {
|
||||
if (!ball || !t || t.dead) return 0;
|
||||
const ids = Array.isArray(ball.stuckPinIds) ? [...ball.stuckPinIds] : (ball.stuckPinId ? [ball.stuckPinId] : []);
|
||||
let moved = 0;
|
||||
for (const id of ids) {
|
||||
const pin = (worldRef?.items || []).find(it => it && !it.dead && it.id === id && isPin(it));
|
||||
if (pin && transferFromBallToTarinai(pin, ball, t, worldRef)) moved += 1;
|
||||
}
|
||||
return moved;
|
||||
}
|
||||
|
||||
function tryBallPickup(ball, worldRef) {
|
||||
if (!ball || ball.dead || ball.type !== "ball" || !worldRef?.nearbyItems) return 0;
|
||||
const speed = Math.hypot(ball.vx || 0, ball.vy || 0);
|
||||
const range = (ball.r || 18) + speed * Math.max(0.016, Number(worldRef.dt || 0.016) || 0.016) + 28;
|
||||
const candidates = worldRef.nearbyItems(ball.x, ball.y, range) || [];
|
||||
let picked = 0;
|
||||
const px = Number.isFinite(ball.prevX) ? ball.prevX : ball.x;
|
||||
const py = Number.isFinite(ball.prevY) ? ball.prevY : ball.y;
|
||||
const sx = ball.x - px;
|
||||
const sy = ball.y - py;
|
||||
const len2 = sx * sx + sy * sy;
|
||||
for (const pin of candidates) {
|
||||
if (!isPin(pin) || pin.dead || pin.pinState === "lodged" || pin.pinState === "ball_lodged") continue;
|
||||
let hitX = ball.x;
|
||||
let hitY = ball.y;
|
||||
if (len2 > 1) {
|
||||
const u = clamp(((pin.x - px) * sx + (pin.y - py) * sy) / len2, 0, 1);
|
||||
hitX = px + sx * u;
|
||||
hitY = py + sy * u;
|
||||
}
|
||||
const hit = (ball.r || 18) + Math.max(5, pin.r || 8) + 2;
|
||||
if (distXY(hitX, hitY, pin.x, pin.y) > hit) continue;
|
||||
if (attachToBall(pin, ball, worldRef)) picked += 1;
|
||||
}
|
||||
return picked;
|
||||
}
|
||||
|
||||
function update(item, dt, worldRef) {
|
||||
|
|
@ -45,8 +130,13 @@
|
|||
updateLodged(item, dt, worldRef);
|
||||
return true;
|
||||
}
|
||||
if (item.pinState === "ball_lodged") {
|
||||
updateBallLodged(item, dt, worldRef);
|
||||
return true;
|
||||
}
|
||||
if (global.TarinaiPhysics?.applyKinematicItemMotion) {
|
||||
global.TarinaiPhysics.applyKinematicItemMotion(item, worldRef, dt, { padding: 26, bounce: 0.44, spinBounce: -0.68, frictionBase: 0.18, frictionRate: 0.85, spinFrictionBase: 0.38, spinRestDamp: 0.08, stopSpeed: 0.05, zeroBelow: 7.5 });
|
||||
global.TarinaiItemDynamicBallSystem?.resolveObstacleCollisions?.(item, dt, worldRef);
|
||||
} else {
|
||||
item.prevX = item.x;
|
||||
item.prevY = item.y;
|
||||
|
|
@ -77,6 +167,17 @@
|
|||
return attach(item, best, worldRef, bestD);
|
||||
}
|
||||
|
||||
function oshibyoAnchorFor(t, visualSide, item = { type: "oshibyo" }) {
|
||||
return global.TarinaiPinAttachmentSystem?.tarinaiAnchor?.(item, t, { visualSide }) || {
|
||||
x: t.x + visualSide * (t?.radius || 16) * 0.96,
|
||||
y: t.y + (t?.radius || 16) * 0.50,
|
||||
angle: visualSide * 0.48,
|
||||
distance: (t?.radius || 16) * 0.96,
|
||||
offsetY: (t?.radius || 16) * 0.48,
|
||||
visualSide,
|
||||
};
|
||||
}
|
||||
|
||||
function attach(item, t, worldRef, d = null) {
|
||||
if (!t || t.dead) return false;
|
||||
if (t.stuckPushpinId && t.stuckPushpinId !== item.id) return false;
|
||||
|
|
@ -88,12 +189,16 @@
|
|||
item.pinState = "lodged";
|
||||
item.deletable = false;
|
||||
item.pinTargetId = t.id;
|
||||
removeFromBall(item, worldRef);
|
||||
const sharedAnchor = global.TarinaiPinAttachmentSystem?.tarinaiAnchor?.(item, t, { dx, dy, distance: distToTarget });
|
||||
const faceDir = t.facingDir ? t.facingDir() : (t.facing || 1);
|
||||
const visualSide = isOshibyo ? -1 : faceDir;
|
||||
const faceSide = faceDir >= 0 ? 1 : -1;
|
||||
const visualSide = sharedAnchor?.visualSide ?? (isOshibyo ? -faceSide : faceSide);
|
||||
item.pinVisualSide = visualSide;
|
||||
item.pinAttachAngle = isOshibyo ? -0.46 : Math.atan2(dy || -1, dx || visualSide);
|
||||
item.pinAttachDistance = isOshibyo ? (t.radius || 16) * 0.88 : clamp(distToTarget || (t.radius || 16) * 0.58, (t.radius || 16) * 0.20, (t.radius || 16) * 0.74);
|
||||
item.pinOffsetY = isOshibyo ? (t.radius || 16) * 0.40 : clamp(dy, -(t.radius || 16) * 0.74, (t.radius || 16) * 0.38);
|
||||
const oshibyoAnchor = isOshibyo ? (sharedAnchor || oshibyoAnchorFor(t, visualSide, item)) : null;
|
||||
item.pinAttachAngle = isOshibyo ? oshibyoAnchor.angle : (sharedAnchor?.angle ?? Math.atan2(dy || -1, dx || visualSide));
|
||||
item.pinAttachDistance = isOshibyo ? oshibyoAnchor.distance : (sharedAnchor?.distance ?? clamp(distToTarget || (t.radius || 16) * 0.58, (t.radius || 16) * 0.20, (t.radius || 16) * 0.74));
|
||||
item.pinOffsetY = isOshibyo ? oshibyoAnchor.offsetY : (sharedAnchor?.offsetY ?? clamp(dy, -(t.radius || 16) * 0.74, (t.radius || 16) * 0.38));
|
||||
item.pinDamageTick = 0;
|
||||
item.pinFallCheckTimer = 0;
|
||||
item.vx = 0;
|
||||
|
|
@ -102,8 +207,8 @@
|
|||
item.spin = item.pinAttachAngle;
|
||||
t.stuckPushpinId = item.id;
|
||||
t.sleeping = false;
|
||||
t.awakeLockTimer = Math.max(t.awakeLockTimer || 0, 7);
|
||||
t.surpriseTimer = Math.max(t.surpriseTimer || 0, isOshibyo ? 0.35 : 0.8);
|
||||
if (!isOshibyo) t.awakeLockTimer = Math.max(t.awakeLockTimer || 0, 7);
|
||||
if (!isOshibyo) t.surpriseTimer = Math.max(t.surpriseTimer || 0, 0.8);
|
||||
if (isOshibyo) {
|
||||
t.thought = "\u304a\u3057\u308a\u92f2\u304c\u523a\u3055\u3063\u3066\u305a\u3093\u3061\u304c\u51fa\u306a\u304f\u306a\u3063\u305f";
|
||||
worldRef.log?.(`${t.name}\u306b\u304a\u3057\u308a\u92f2\u304c\u523a\u3055\u3063\u305f\u3002`, "accident", { participants: [t] });
|
||||
|
|
@ -118,7 +223,7 @@
|
|||
t.setActionState?.("panic", { target: t.panicDestination ? t.panicDestination(item) : { x: t.x + deterministicRange(worldRef, "pin-panic-target-x", -80, 80, item, t), y: t.y + deterministicRange(worldRef, "pin-panic-target-y", -80, 80, item, t) }, reason: "\u753b\u92f2\u304c\u523a\u3055\u3063\u3066\u30d1\u30cb\u30c3\u30af\u306b\u306a\u3063\u3066\u3044\u308b", wake: true });
|
||||
if (t.addStress) t.addStress(behavior?.stressOnAttach ?? 18, { threshold: 8 });
|
||||
}
|
||||
if (t.damage && (behavior?.damageOnAttach ?? 6) > 0) t.damage(behavior?.damageOnAttach ?? 6, "\u753b\u92f2");
|
||||
if (!isOshibyo && t.damage && (behavior?.damageOnAttach ?? 6) > 0) t.damage(behavior?.damageOnAttach ?? 6, "\u753b\u92f2");
|
||||
if ((worldRef.time || 0) >= (item.pinLogAt || -999) + 1.2) {
|
||||
item.pinLogAt = worldRef.time || 0;
|
||||
worldRef.log?.(`${t.name}\u306b\u753b\u92f2\u304c\u523a\u3055\u3063\u305f\u3002`, "accident", { participants: [t] });
|
||||
|
|
@ -137,16 +242,21 @@
|
|||
const isOshibyo = Boolean(behavior?.blocksZunchi);
|
||||
t.stuckPushpinId = item.id;
|
||||
item.deletable = false;
|
||||
const faceDir = t.facingDir ? t.facingDir() : (t.facing || 1);
|
||||
const visualSide = isOshibyo ? -1 : faceDir;
|
||||
item.pinVisualSide = visualSide;
|
||||
item.x = isOshibyo ? t.x - (t.radius || 16) * 0.88 : t.x + visualSide * (t.radius || 16) * 0.36;
|
||||
item.y = isOshibyo ? t.y + (t.radius || 16) * 0.42 : t.y - (t.radius || 16) * 0.04;
|
||||
item.prevX = item.x;
|
||||
item.prevY = item.y;
|
||||
item.spin = isOshibyo ? -0.52 : visualSide * 0.28;
|
||||
item.vx = t.vx || 0;
|
||||
item.vy = t.vy || 0;
|
||||
const sharedPose = global.TarinaiPinAttachmentSystem?.applyTarinaiPose?.(item, t, worldRef);
|
||||
if (!sharedPose) {
|
||||
const faceDir = t.facingDir ? t.facingDir() : (t.facing || 1);
|
||||
const faceSide = faceDir >= 0 ? 1 : -1;
|
||||
const visualSide = isOshibyo ? -faceSide : faceSide;
|
||||
item.pinVisualSide = visualSide;
|
||||
const oshibyoAnchor = isOshibyo ? oshibyoAnchorFor(t, visualSide, item) : null;
|
||||
item.x = isOshibyo ? oshibyoAnchor.x : t.x + visualSide * (t.radius || 16) * 0.36;
|
||||
item.y = isOshibyo ? oshibyoAnchor.y : t.y - (t.radius || 16) * 0.04;
|
||||
item.prevX = item.x;
|
||||
item.prevY = item.y;
|
||||
item.spin = isOshibyo ? oshibyoAnchor.angle : visualSide * 0.28;
|
||||
item.vx = t.vx || 0;
|
||||
item.vy = t.vy || 0;
|
||||
}
|
||||
if (isOshibyo) {
|
||||
if ((t.oshiriByoZunchiStock || 0) >= 6 && t.state !== "eat" && t.state !== "sleep") t.thought = "\u304a\u3057\u308a\u92f2\u3067\u305a\u3093\u3061\u304c\u6e9c\u307e\u3063\u3066\u3064\u3089\u3044";
|
||||
return;
|
||||
|
|
@ -154,7 +264,7 @@
|
|||
item.pinDamageTick = (item.pinDamageTick || 0) + dt;
|
||||
while (item.pinDamageTick >= 0.55) {
|
||||
item.pinDamageTick -= 0.55;
|
||||
if (t.damage && (behavior?.damagePerTick ?? 1.4) > 0) t.damage(behavior?.damagePerTick ?? 1.4, "\u753b\u92f2");
|
||||
if (!isOshibyo && t.damage && (behavior?.damagePerTick ?? 1.4) > 0) t.damage(behavior?.damagePerTick ?? 1.4, "\u753b\u92f2");
|
||||
t.hurtTimer = Math.max(t.hurtTimer || 0, 0.50);
|
||||
if (t.addStress) t.addStress(behavior?.stressPerTick ?? 2.6, { threshold: 8, duration: 3.4 });
|
||||
if (deterministicChance(worldRef, "pin-pain-bubble", 0.22, item, t)) t.bubble?.("!!", 0.6, "rgba(168,72,72,0.82)");
|
||||
|
|
@ -178,6 +288,7 @@
|
|||
}
|
||||
|
||||
function detach(item, worldRef, reason = "released") {
|
||||
const carriedBall = removeFromBall(item, worldRef);
|
||||
const t = worldRef?.liveTarinaiById?.(item.pinTargetId) || null;
|
||||
const behavior = typeof pinBehaviorFor === "function" ? pinBehaviorFor(item.type) : null;
|
||||
const wasOshibyo = Boolean(behavior?.blocksZunchi);
|
||||
|
|
@ -204,22 +315,51 @@
|
|||
item.vy = 0;
|
||||
item.spinVelocity = 0;
|
||||
} else {
|
||||
item.vx = deterministicRange(worldRef, "pin-detach-vx", -24, 24, item, t, reason);
|
||||
item.vy = deterministicRange(worldRef, "pin-detach-vy", -10, 18, item, t, reason);
|
||||
item.spinVelocity = deterministicRange(worldRef, "pin-detach-spin-velocity", -1.6, 1.6, item, t, reason);
|
||||
item.spin += deterministicRange(worldRef, "pin-detach-spin", -0.25, 0.25, item, t, reason);
|
||||
let nx = 0;
|
||||
let ny = 1;
|
||||
if (t) {
|
||||
item.x = clamp(t.x + deterministicRange(worldRef, "pin-detach-x", -(t.radius || 16) * 0.75, (t.radius || 16) * 0.75, item, t, reason), CONFIG.worldPadding || 30, (worldRef?.w || item.x) - (CONFIG.worldPadding || 30));
|
||||
item.y = clamp(t.y + deterministicRange(worldRef, "pin-detach-y", (t.radius || 16) * 0.12, (t.radius || 16) * 0.72, item, t, reason), CONFIG.worldPadding || 30, (worldRef?.h || item.y) - (CONFIG.worldPadding || 30));
|
||||
const dx = Number(item.x || 0) - Number(t.x || 0);
|
||||
const dy = Number(item.y || 0) - Number(t.y || 0);
|
||||
const d = Math.hypot(dx, dy);
|
||||
if (d > 0.001) { nx = dx / d; ny = dy / d; }
|
||||
else { nx = Math.sign(item.pinVisualSide || 0) || (t.facingDir?.() || 1); ny = 0.28; }
|
||||
const len = Math.hypot(nx, ny) || 1;
|
||||
nx /= len; ny /= len;
|
||||
const dropR = Math.max(8, (item.r || 8) + (t.radius || 16) * 0.58);
|
||||
const pad = CONFIG.worldPadding || 30;
|
||||
item.x = clamp((t.x || 0) + nx * dropR, pad, Math.max(pad, (worldRef?.w || item.x) - pad));
|
||||
item.y = clamp((t.y || 0) + ny * dropR + (wasOshibyo ? 4 : 0), pad, Math.max(pad, (worldRef?.h || item.y) - pad));
|
||||
} else if (carriedBall) {
|
||||
const dx = Number(item.x || 0) - Number(carriedBall.x || 0);
|
||||
const dy = Number(item.y || 0) - Number(carriedBall.y || 0);
|
||||
const d = Math.hypot(dx, dy) || 1;
|
||||
nx = dx / d;
|
||||
ny = dy / d;
|
||||
}
|
||||
const baseVx = Number(carriedBall?.vx || t?.vx || 0) || 0;
|
||||
const baseVy = Number(carriedBall?.vy || t?.vy || 0) || 0;
|
||||
item.vx = clamp(baseVx * 0.45 + nx * 18, -90, 90);
|
||||
item.vy = clamp(baseVy * 0.45 + Math.max(12, ny * 18), -60, 110);
|
||||
item.spinVelocity = clamp((item.spinVelocity || 0) * 0.35 + (Math.sign(nx) || 1) * 0.9, -2.2, 2.2);
|
||||
worldRef?.effects?.push(new Effect("ring", item.x, item.y, { size: Math.max(14, (item.r || 16) * 0.92), life: 0.16, color: "rgba(214,112,112,0.40)" }));
|
||||
if (reason === "fall" && t) worldRef?.log?.(`${t.name}\u306e\u753b\u92f2\u304c\u629c\u3051\u843d\u3061\u305f\u3002`, "observe", { participants: [t] });
|
||||
if (reason === "fall" && t) worldRef?.log?.(`${t.name}の画鋲が抜け落ちた。`, "observe", { participants: [t] });
|
||||
}
|
||||
|
||||
if (wasOshibyo && storedZunchi > 0 && worldRef?.spawnBurstZunchi) {
|
||||
worldRef.spawnBurstZunchi(t || item, storedZunchi, item);
|
||||
}
|
||||
}
|
||||
|
||||
global.TarinaiItemDynamicPinSystem = Object.freeze({ update, isPin, tryStick, attach, updateLodged, detach });
|
||||
global.TarinaiItemDynamicPinSystem = Object.freeze({
|
||||
update,
|
||||
isPin,
|
||||
tryStick,
|
||||
attach,
|
||||
updateLodged,
|
||||
detach,
|
||||
attachToBall,
|
||||
transferFromBallToTarinai,
|
||||
transferBallPinsToTarinai,
|
||||
tryBallPickup,
|
||||
updateBallLodged,
|
||||
});
|
||||
})(typeof window !== "undefined" ? window : globalThis);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue