This commit is contained in:
33333-33333 2026-07-17 12:25:23 +09:00
commit 61718e2981
18 changed files with 572 additions and 190 deletions

View file

@ -4,6 +4,22 @@
// Owns zunchi item motion, high-speed collision, and lifecycle stage ticking.
// Prototype methods delegate here.
(function (global) {
function burstOwnerHitRadius(item, tarinai) {
return (tarinai?.radius || 20) * 0.72 + (item?.r || 14);
}
function updateBurstOwnerClearance(item, worldRef) {
if (!item?.achievementBurstOwnerId || item.achievementBurstClearedOwner === true || !worldRef) return false;
const owner = worldRef.liveTarinaiById?.(String(item.achievementBurstOwnerId))
|| (worldRef.tarinai || []).find(candidate => candidate && !candidate.dead && String(candidate.id || "") === String(item.achievementBurstOwnerId))
|| null;
if (!owner) return false;
const clearance = burstOwnerHitRadius(item, owner) + 3;
if (distXY(item.x, item.y, owner.x, owner.y) <= clearance) return false;
item.achievementBurstClearedOwner = true;
return true;
}
function updateMotion(item, dt, worldRef) {
if (!item || item.dead || item.type !== "zunchi") return false;
if (item.playerHeld || item._heldByPlayer) {
@ -37,6 +53,7 @@
worldRef.drawListDirty = true;
}
global.TarinaiItemDynamicBallSystem.resolveObstacleCollisions(item, dt, worldRef);
updateBurstOwnerClearance(item, worldRef);
const collisionSpeed = Math.hypot(item.vx || 0, item.vy || 0);
if (collisionSpeed < 0.08 && item.achievementBurstOwnerId) item.achievementBurstNeverStopped = false;
resolveHighSpeedTarinaiCollision(item, dt, worldRef, collisionSpeed);
@ -50,7 +67,13 @@
for (const t of worldRef.nearbyTarinai?.(item.x, item.y, search, true) || []) {
if (!t || t.dead || worldRef.isTarinaiHiddenInNestBox?.(t)) continue;
const d = distXY(item.x, item.y, t.x, t.y);
if (d > (t.radius || 20) * 0.72 + (item.r || 14)) continue;
if (d > burstOwnerHitRadius(item, t)) continue;
const hitTarinaiIdBeforeDamage = String(t.id || t.releasedLiveId || "");
const isBurstOwner = Boolean(item.achievementBurstOwnerId && String(item.achievementBurstOwnerId) === hitTarinaiIdBeforeDamage);
// Burst zunchi spawn inside their owner. Ignore that initial overlap
// completely; only a projectile that has first cleared the owner can
// return and become a valid self-collision.
if (isBurstOwner && item.achievementBurstClearedOwner !== true) continue;
const now = worldRef.time || 0;
if ((item.lastHitTarinaiAt || {})[t.id] && item.lastHitTarinaiAt[t.id] + 0.55 > now) continue;
item.lastHitTarinaiAt = item.lastHitTarinaiAt || {};
@ -62,8 +85,15 @@
t.vy = (t.vy || 0) + ny * impulse * 0.72 + rand(-18, 10);
if (speed >= 185) {
const damage = Math.min(18, Math.max(1, (speed - 185) / 22));
// A fatal damage call releases the tarinai live ID before control returns here.
// Capture ownership eligibility first so a valid self-zunchi death is not lost.
const eligibleSelfZunchiDeath = Boolean(
isBurstOwner
&& item.achievementBurstClearedOwner === true
&& item.achievementBurstNeverStopped !== false
);
worldRef.applyImpactDamage?.(t, damage, "\u885d\u7a81", { source: item }) || t.damage?.(damage, "\u885d\u7a81");
if (t.dead && item.achievementBurstOwnerId === t.id && item.achievementBurstNeverStopped !== false) {
if (t.dead && eligibleSelfZunchiDeath) {
globalThis.TarinaiAchievements?.recordSelfZunchiDeath?.({ world: worldRef, tarinai: t, zunchi: item, speed });
}
}