This commit is contained in:
33333-33333 2026-07-05 18:01:36 +09:00
commit 8f9f5b5100
108 changed files with 2139 additions and 1500 deletions

View file

@ -47,34 +47,35 @@
},
resolveOwnedBedSleepConflicts() {
const alive = (this.tarinai || []).filter(t => t && !t.dead);
if (alive.length < 2) return 0;
const liveCount = (this.tarinai || []).reduce((count, t) => count + (t && !t.dead ? 1 : 0), 0);
if (liveCount < 2) return 0;
const friendThreshold = typeof FRIEND_AFFINITY_THRESHOLD === "number" ? FRIEND_AFFINITY_THRESHOLD : 14;
const isFriendPair = (a, b) => {
const relAB = a?.relationTo ? a.relationTo(b?.id) : null;
const relBA = b?.relationTo ? b.relationTo(a?.id) : null;
return Boolean((relAB && (relAB.affinity || 0) >= friendThreshold) || (relBA && (relBA.affinity || 0) >= friendThreshold));
};
const bedSleepers = new Map();
for (const sleeper of alive) {
if (!(sleeper.sleeping || sleeper.state === "sleep")) continue;
const bed = sleeper.target;
if (!bed || bed.dead || bed.type !== "grass_bed") continue;
if (distXY(sleeper.x, sleeper.y, bed.x, bed.y) > Math.max((bed.r || 18) * 1.55, 32)) continue;
if (!bedSleepers.has(bed.id)) bedSleepers.set(bed.id, []);
bedSleepers.get(bed.id).push(sleeper);
}
const beds = typeof this.itemsOfType === "function" ? this.itemsOfType("grass_bed") : (this.items || []).filter(it => it && !it.dead && it.type === "grass_bed");
let conflicts = 0;
for (const intruder of alive) {
if (!(intruder.state === "seek_bed" || intruder.state === "sleep")) continue;
const bed = intruder.target;
for (const bed of beds || []) {
if (!bed || bed.dead || bed.type !== "grass_bed") continue;
const sleepers = bedSleepers.get(bed.id) || [];
if (!sleepers.length) continue;
if (distXY(intruder.x, intruder.y, bed.x, bed.y) > Math.max((bed.r || 18) * 2.2, 54)) continue;
const defender = sleepers.find(other => other && other !== intruder && !this.areParentChild?.(intruder, other) && !isFriendPair(intruder, other));
if (!defender) continue;
if (this.startForcedFight?.(defender, intruder)) conflicts += 1;
const sleepReach = Math.max((bed.r || 18) * 1.55, 32);
const intrudeReach = Math.max((bed.r || 18) * 2.2, 54);
const candidates = typeof this.nearbyTarinai === "function" ? this.nearbyTarinai(bed.x, bed.y, intrudeReach + 16, true) : (this.tarinai || []);
const sleepers = [];
const intruders = [];
for (const t of candidates || []) {
if (!t || t.dead || t.target !== bed) continue;
const d = distXY(t.x, t.y, bed.x, bed.y);
if ((t.sleeping || t.state === "sleep") && d <= sleepReach) sleepers.push(t);
if ((t.state === "seek_bed" || t.state === "sleep") && d <= intrudeReach) intruders.push(t);
}
if (!sleepers.length || !intruders.length) continue;
for (const intruder of intruders) {
const defender = sleepers.find(other => other && other !== intruder && !this.areParentChild?.(intruder, other) && !isFriendPair(intruder, other));
if (!defender) continue;
if (this.startForcedFight?.(defender, intruder)) conflicts += 1;
}
}
return conflicts;
},
@ -114,11 +115,13 @@
sanitizeOutOfBounds() {
const pad = Math.max(28, CONFIG.worldPadding || 30);
const safeW = Math.max(pad * 2 + 1, Number(this.w || 1000) || 1000);
const safeH = Math.max(pad * 2 + 1, Number(this.h || 720) || 720);
let changedItems = false;
let changedTarinai = false;
let changedAnts = false;
const repairPoint = (obj, radius = 12) => {
if (!obj) return "missing";
const safeW = Math.max(pad * 2 + 1, Number(this.w || 1000) || 1000);
const safeH = Math.max(pad * 2 + 1, Number(this.h || 720) || 720);
if (!Number.isFinite(obj.x)) obj.x = safeW * 0.5;
if (!Number.isFinite(obj.y)) obj.y = safeH * 0.5;
const minX = pad - radius;
@ -132,15 +135,34 @@
if (Number.isFinite(obj.vy)) obj.vy *= -0.18;
return "moved";
};
for (const it of this.items || []) {
const result = repairPoint(it, it.r || 12);
const forEachMaintenanceSlice = (list, cursorKey, fullLimit, fn) => {
const arr = Array.isArray(list) ? list : [];
const len = arr.length;
if (!len) {
this[cursorKey] = 0;
return;
}
const limit = Math.max(1, Number(fullLimit || len) || len);
if (len <= limit) {
for (let i = 0; i < len; i++) fn(arr[i], i);
this[cursorKey] = 0;
return;
}
let cursor = Math.max(0, Math.floor(Number(this[cursorKey] || 0) || 0)) % len;
for (let n = 0; n < limit; n++) {
const idx = (cursor + n) % len;
fn(arr[idx], idx);
}
this[cursorKey] = (cursor + limit) % len;
};
forEachMaintenanceSlice(this.items, "_sanitizeItemsCursor", 900, (it) => {
const result = repairPoint(it, it?.r || 12);
if (result === "moved") changedItems = true;
}
for (const ef of this.effects || []) {
repairPoint(ef, ef.size || 12);
}
const safeW = Math.max(pad * 2 + 1, Number(this.w || 1000) || 1000);
const safeH = Math.max(pad * 2 + 1, Number(this.h || 720) || 720);
});
forEachMaintenanceSlice(this.effects, "_sanitizeEffectsCursor", 900, (ef) => {
repairPoint(ef, ef?.size || 12);
});
const repairTarinai = (t) => {
t.x = clamp(Number.isFinite(t.x) ? t.x : safeW * 0.5, pad, safeW - pad);
t.y = clamp(Number.isFinite(t.y) ? t.y : safeH * 0.5, pad, safeH - pad);
@ -148,8 +170,8 @@
t.vy = 0;
t.entryTimer = 0;
};
for (const t of this.tarinai || []) {
if (!t || t.dead) continue;
forEachMaintenanceSlice(this.tarinai, "_sanitizeTarinaiCursor", 360, (t) => {
if (!t || t.dead) return;
if (t.state === "sunbath" || (t.sunbathTimer || 0) > 0.04) {
if (!Number.isFinite(t.x)) t.x = Number.isFinite(t.sunbathAnchorX) ? t.sunbathAnchorX : pad;
if (!Number.isFinite(t.y)) t.y = Number.isFinite(t.sunbathAnchorY) ? t.sunbathAnchorY : pad;
@ -157,7 +179,10 @@
t.sunbathAnchorY = clamp(t.y, pad, safeH - pad);
}
const result = repairPoint(t, t.radius || 22);
if (result === "missing" || result === "moved") repairTarinai(t);
if (result === "missing" || result === "moved") {
repairTarinai(t);
changedTarinai = true;
}
if (t.state === "sunbath" || (t.sunbathTimer || 0) > 0.04) {
t.sunbathAnchorX = clamp(Number.isFinite(t.x) ? t.x : (Number.isFinite(t.sunbathAnchorX) ? t.sunbathAnchorX : pad), pad, safeW - pad);
t.sunbathAnchorY = clamp(Number.isFinite(t.y) ? t.y : (Number.isFinite(t.sunbathAnchorY) ? t.sunbathAnchorY : pad), pad, safeH - pad);
@ -168,12 +193,11 @@
t._lastValidX = t.x;
t._lastValidY = t.y;
}
}
let changedAnts = false;
for (const ant of this.ants || []) {
if (!ant || ant.dead) continue;
});
forEachMaintenanceSlice(this.ants, "_sanitizeAntsCursor", 900, (ant) => {
if (!ant || ant.dead) return;
const result = repairPoint(ant, ant.kind === "queen" ? 18 : 10);
if (result === "missing") continue;
if (result === "missing") return;
if (result === "moved") {
ant.vx = 0;
ant.vy = 0;
@ -185,13 +209,15 @@
}
changedAnts = true;
}
}
});
if (changedAnts) this.compactAnts?.();
if (changedItems) {
this.compactItems();
this.updateItemCounts();
this.markSpatialDirty?.("sanitize-out-of-bounds");
this.ensureSpatial?.("sanitize-out-of-bounds");
} else if (changedTarinai || changedAnts) {
this.markSpatialDirty?.("sanitize-out-of-bounds-mobile");
}
},