This commit is contained in:
33333-33333 2026-07-03 00:45:22 +09:00
commit ee22e1a929
49 changed files with 1380 additions and 338 deletions

View file

@ -1,27 +1,34 @@
"use strict";
// Layer: tarinai/nest-sleep
// Centralizes sleep furniture, nest occupancy/reservation, and sleep-target
// selection policy so nest-box entrance logic and bed targeting do not drift.
// Centralizes sleep furniture, nest/pipe occupancy/reservation, and sleep-target
// selection policy so entrance logic and bed targeting do not drift.
(function (global) {
const distFn = typeof dist === "function" ? dist : ((a, b) => Math.hypot((a?.x || 0) - (b?.x || 0), (a?.y || 0) - (b?.y || 0)));
const clampFn = typeof clamp === "function" ? clamp : ((v, a, b) => Math.max(a, Math.min(b, v)));
function isNestContainer(item) {
return Boolean(item && (typeof global.isNestContainerType === "function" ? global.isNestContainerType(item.type) : (item.type === "nest_box" || item.type === "pipe")));
}
function isSleepFurniture(item) {
return Boolean(item && (item.roles?.sleepPlace || (typeof global.isSleepFurnitureType === "function" ? global.isSleepFurnitureType(item.type) : (item.type === "bed" || item.type === "nest_box" || item.type === "grass_bed"))));
return Boolean(item && (item.roles?.sleepPlace || (typeof global.isSleepFurnitureType === "function" ? global.isSleepFurnitureType(item.type) : (item.type === "bed" || item.type === "nest_box" || item.type === "pipe" || item.type === "grass_bed"))));
}
function sleepTargetBlocked(worldRef, tarinai, item) {
if (!worldRef || !tarinai || !item) return false;
// Pipe mouths are floor-level entry points. Treat them as reachable even if
// their circular sprite overlaps the tarinai body.
if (item.type === "pipe") return false;
return Boolean(worldRef.targetBlockedByObstacle?.(tarinai, item, Math.max(18, (tarinai.radius || 20) * 0.65)));
}
function nestBoxCapacity(worldRef, box = null) {
return typeof worldRef?.nestBoxCapacity === "function" ? worldRef.nestBoxCapacity(box) : 5;
return typeof worldRef?.nestBoxCapacity === "function" ? worldRef.nestBoxCapacity(box) : (box?.type === "pipe" ? 3 : 5);
}
function nestBoxOccupants(worldRef, box, limit = 5) {
if (!worldRef || !box || box.dead || box.type !== "nest_box") return [];
if (!worldRef || !box || box.dead || !isNestContainer(box)) return [];
const occupants = (worldRef.tarinai || []).filter(t => t && !t.dead && t.insideNestBoxId === box.id);
return Number.isFinite(limit) ? occupants.slice(0, Math.max(0, limit)) : occupants;
}
@ -44,7 +51,7 @@
}
function nestBoxReservationCount(worldRef, box) {
if (!worldRef || !box || box.dead || box.type !== "nest_box") return 0;
if (!worldRef || !box || box.dead || !isNestContainer(box)) return 0;
const now = Number(worldRef.time || 0) || 0;
let n = 0;
for (const t of worldRef.tarinai || []) if (tarinaiIsReservingNest(t, box, now)) n += 1;
@ -53,7 +60,7 @@
function bedOccupancy(worldRef, bed) {
if (!worldRef || !isSleepFurniture(bed)) return 0;
if (bed.type === "nest_box") {
if (isNestContainer(bed)) {
return nestBoxOccupants(worldRef, bed, Infinity).length + nestBoxReservationCount(worldRef, bed);
}
let n = 0;
@ -69,7 +76,8 @@
function bedComfort(worldRef, bed) {
if (!isSleepFurniture(bed)) return 0.7;
const occ = bedOccupancy(worldRef, bed);
return clampFn((bed.comfort ?? 1) - Math.max(0, occ - 3) * 0.075 - (bed.wear || 0) * 0.18, 0.42, 1.18);
const base = bed.type === "pipe" ? Math.min(Number(bed.comfort ?? 0.74) || 0.74, 0.82) : (bed.comfort ?? 1);
return clampFn(base - Math.max(0, occ - 3) * 0.075 - (bed.wear || 0) * 0.18, bed.type === "pipe" ? 0.46 : 0.42, bed.type === "pipe" ? 0.88 : 1.18);
}
function sleepPlaceAvailableFor(worldRef, tarinai, item) {
@ -79,7 +87,7 @@
if (tarinai.shouldAvoidTarget?.(item)) return false;
if (worldRef?.sleepTargetTemperatureBlocked?.(tarinai, item)) return false;
if (sleepTargetBlocked(worldRef, tarinai, item)) return false;
const capacity = item.type === "nest_box" ? nestBoxCapacity(worldRef, item) : 1;
const capacity = isNestContainer(item) ? nestBoxCapacity(worldRef, item) : 1;
const occupancy = bedOccupancy(worldRef, item);
return occupancy < capacity || tarinai.target === item || (tarinai.insideNestBoxId && tarinai.insideNestBoxId === item.id);
}
@ -135,6 +143,7 @@
return findOwnedStructure(worldRef, tarinai, "grass_bed", maxDist)
|| nearestSleepPlaceOfType(worldRef, tarinai, "nest_box", maxDist)
|| nearestSleepPlaceOfType(worldRef, tarinai, "bed", maxDist)
|| nearestSleepPlaceOfType(worldRef, tarinai, "pipe", maxDist)
|| nearestSleepPlaceOfType(worldRef, tarinai, "grass_bed", maxDist)
|| null;
}
@ -145,6 +154,7 @@
}
global.TarinaiNestSleepSystem = Object.freeze({
isNestContainer,
isSleepFurniture,
isTarinaiHiddenInNestBox,
nestBoxOccupants,