This commit is contained in:
33333-33333 2026-07-15 14:44:29 +09:00
commit 63e10af865
86 changed files with 5400 additions and 1209 deletions

View file

@ -275,7 +275,7 @@
const base = Math.max(1, Number((body?.hazard?.damage ?? global.TarinaiPhysicsBodySystem.scalar(rect.item, "damage", 7)) || 7) || 7);
const damage = clamp(base * (solid ? 1.0 : 0.72) + speed * 0.018, 2.0, 18.0);
if (typeof worldRef.applyImpactDamage === "function") worldRef.applyImpactDamage(t, damage, "\u6bd2\u30d6\u30ed\u30c3\u30af", { source: rect.item, x: t.x, y: t.y });
else if (typeof t.damage === "function") t.damage(damage, "\u6bd2\u30d6\u30ed\u30c3\u30af");
else if (typeof t.damage === "function") t.damage(damage, "\u6bd2\u30d6\u30ed\u30c3\u30af", { source: rect.item });
t.showHpBar?.(4.8);
t.hurtTimer = Math.max(t.hurtTimer || 0, 0.82);
t.pokeFlashTimer = Math.max(t.pokeFlashTimer || 0, 0.28);
@ -287,6 +287,63 @@
return true;
}
function mechanicalPressureForTarinaiLocal(worldRef, t) {
const pressure = worldRef?._mechanicalCrowdPressure;
if (!pressure?.active || !pressure.ids || !t) return null;
return pressure.ids.has(t.id || t.familyKey || t) ? pressure : null;
}
function consumeMechanicalPressureContactLocal(worldRef, t) {
const pressure = mechanicalPressureForTarinaiLocal(worldRef, t);
if (!pressure) return true;
const budget = Math.max(1, Number(pressure.contactBudget || 8) || 8);
if ((pressure.detailedContacts || 0) < budget) {
pressure.detailedContacts = (pressure.detailedContacts || 0) + 1;
return true;
}
pressure.skippedContacts = (pressure.skippedContacts || 0) + 1;
return false;
}
function cheapMechanicalPressureContactLocal(worldRef, t, rr, opts = {}) {
const pressure = mechanicalPressureForTarinaiLocal(worldRef, t);
if (!pressure || !Array.isArray(pressure.sources) || !pressure.sources.length) return false;
const maxSources = Math.max(1, Math.min(4, Number(opts.maxSources || 2) || 2));
const maxRects = Math.max(1, Math.min(12, Number(opts.maxRects || 5) || 5));
const x = Number(t.x || 0) || 0;
const y = Number(t.y || 0) || 0;
let sourceChecks = 0;
let rectChecks = 0;
let pushed = false;
for (const item of pressure.sources) {
if (!item || item.dead) continue;
const reach = global.TarinaiMechanicalShapeSystem?.reach?.(item) || Math.max(64, Number(item.r || item.radius || 64) || 64);
if (distXY(x, y, Number(item.x || 0) || 0, Number(item.y || 0) || 0) > reach + rr + 56) continue;
sourceChecks += 1;
for (const rect of worldRef.solidObstacleRects?.(item) || []) {
rectChecks += 1;
if (Geometry.circleOverlapsRect(x, y, rr, rect, 2.0) && worldRef.pushTarinaiOutOfRect(t, rect, rr, { slop: 0.55, pressureCheap: true })) {
pushed = true;
break;
}
if (rectChecks >= maxRects) break;
}
if (pushed || sourceChecks >= maxSources || rectChecks >= maxRects) break;
}
pressure.degradedContacts = (pressure.degradedContacts || 0) + 1;
if (pushed) {
t.lastSolidObstacleCollisionAt = worldRef.time || 0;
t.vx = clamp(Number(t.vx || 0) || 0, -420, 420) * 0.82;
t.vy = clamp(Number(t.vy || 0) || 0, -420, 420) * 0.82;
t.impulseVx = clamp(Number(t.impulseVx || 0) || 0, -260, 260) * 0.72;
t.impulseVy = clamp(Number(t.impulseVy || 0) || 0, -260, 260) * 0.72;
} else {
t.vx = clamp(Number(t.vx || 0) || 0, -520, 520) * 0.94;
t.vy = clamp(Number(t.vy || 0) || 0, -520, 520) * 0.94;
}
return pushed;
}
Object.defineProperties(World.prototype, Object.getOwnPropertyDescriptors({
nearest(entity, types, maxDist = Infinity) {
let best = null, bestD = maxDist;
@ -780,6 +837,17 @@
t.lastBounceFenceEffectAt = now;
this.effects?.push(new Effect("ring", t.x, t.y, { size: Math.max(18, rr * 1.15), life: 0.18, color: "rgba(82,153,230,0.46)" }));
}
} else if (r.mechanical && opts.pressureCheap) {
const omega = r.rotator ? clamp(Number(r.angularVelocity || 0) || 0, -4.5, 4.5) : 0;
const rx = Number(t.x || 0) - Number(r.centerX ?? r.item?.x ?? r.cx ?? 0);
const ry = Number(t.y || 0) - Number(r.centerY ?? r.item?.y ?? r.cy ?? 0);
const tvx = r.rotator && Math.abs(omega) > 0.012 ? clamp(-ry * omega, -180, 180) : 0;
const tvy = r.rotator && Math.abs(omega) > 0.012 ? clamp(rx * omega, -180, 180) : 0;
t.vx = clamp(preVx * 0.62 + nx * 46 + tvx * 0.42, -360, 360);
t.vy = clamp(preVy * 0.62 + ny * 46 + tvy * 0.42, -360, 360);
t.impulseVx = clamp((Number(t.impulseVx || 0) || 0) * 0.45 + nx * 34, -220, 220);
t.impulseVy = clamp((Number(t.impulseVy || 0) || 0) * 0.45 + ny * 34, -220, 220);
t.surpriseTimer = Math.max(t.surpriseTimer || 0, 0.05);
} else if (r.mechanical) {
global.TarinaiMechanicalSystem.applySurfaceVelocityToCircle(t, r, nx, ny, preVx, preVy, {
damping: r.rotator ? 0.78 : (r.poisonBlock ? 0.74 : 0.80),
@ -809,13 +877,24 @@
return false;
}
const rr = Math.max(8, (Number(t.radius) || 22) * 0.74);
const maxStep = Math.max(5, Math.min(12, Number(opts.maxStep || rr * 0.48) || 8));
const pressure = mechanicalPressureForTarinaiLocal(this, t);
if (pressure && !consumeMechanicalPressureContactLocal(this, t)) {
t.x += moveX * 0.72;
t.y += moveY * 0.72;
const pushed = cheapMechanicalPressureContactLocal(this, t, rr, { maxSources: 2, maxRects: 5 });
if (pushed) this.markSpatialDirty?.("tarinai-pressure-cheap-contact");
return pushed;
}
const pressureLight = Boolean(pressure);
const maxStep = pressureLight ? Math.max(8, Math.min(16, Number(opts.maxStep || rr * 0.72) || 12)) : Math.max(5, Math.min(12, Number(opts.maxStep || rr * 0.48) || 8));
const steps = Math.max(1, Math.min(48, Math.ceil(dist / maxStep)));
const stepX = moveX / steps;
const stepY = moveY / steps;
const fastSweep = dist > rr * 1.25 || Math.hypot(Number(t.vx || 0) || 0, Number(t.vy || 0) || 0) > 170;
const sweepMaxChecks = Math.max(opts.light ? (fastSweep ? 18 : 10) : (fastSweep ? 44 : 32), Number(opts.maxChecks || 0) || CONFIG.maxFenceCollisionChecks || 24);
const sweepMaxRects = fastSweep ? 108 : 72;
const sweepMaxChecks = pressureLight
? Math.min(fastSweep ? 16 : 10, Math.max(6, Number(opts.maxChecks || 0) || 10))
: Math.max(opts.light ? (fastSweep ? 18 : 10) : (fastSweep ? 44 : 32), Number(opts.maxChecks || 0) || CONFIG.maxFenceCollisionChecks || 24);
const sweepMaxRects = pressureLight ? (fastSweep ? 36 : 24) : (fastSweep ? 108 : 72);
let collided = false;
for (let i = 0; i < steps; i++) {
const sx = t.x;
@ -825,8 +904,9 @@
const hit = firstSweptCircleObstacleHitLocal(this, t, sx, sy, ex, ey, rr, {
searchRadius: Math.max(rr + 110, rr + Math.hypot(stepX, stepY) + 170),
maxChecks: sweepMaxChecks,
maxRects: sweepMaxRects,
});
maxRects: sweepMaxRects,
mechanicalFallback: !pressureLight,
});
if (hit && hit.t <= 1) {
const safeT = Math.max(0, hit.t - 0.035);
t.x = sx + stepX * safeT;
@ -842,6 +922,7 @@
maxChecks: sweepMaxChecks,
maxRects: sweepMaxRects,
slop: 0.30,
skipPressureBudget: true,
oneWayDx: stepX,
oneWayDy: stepY,
}) || true;
@ -855,6 +936,7 @@
maxChecks: sweepMaxChecks,
maxRects: sweepMaxRects,
slop: 0.35,
skipPressureBudget: true,
oneWayDx: stepX,
oneWayDy: stepY,
});
@ -870,6 +952,10 @@
resolveSolidObstacleCollision(t, opts = {}) {
if (!t || t.dead || this.isTarinaiHiddenInNestBox(t)) return false;
const rr = Math.max(8, (Number(t.radius) || 22) * 0.74);
const pressure = mechanicalPressureForTarinaiLocal(this, t);
if (pressure && !opts.pressureCheap && !opts.skipPressureBudget && !consumeMechanicalPressureContactLocal(this, t)) {
return cheapMechanicalPressureContactLocal(this, t, rr, { maxSources: 2, maxRects: 5 });
}
this.resolvePoisonBlockContactDamage(t, rr);
let pushed = false;
let bounced = false;