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);
|
||||
|
||||
|
||||
(function (global) {
|
||||
|
|
@ -224,8 +195,54 @@
|
|||
if (!id) return null;
|
||||
return this.world?.items?.find?.(it => it && !it.dead && it.id === id && it.type === "nest_box") || null;
|
||||
},
|
||||
|
||||
nestBoxEntryActive() {
|
||||
const now = this.world?.time || 0;
|
||||
if (now < (this._nestBoxExitSmoothUntil || -Infinity)) return true;
|
||||
if (!this.insideNestBoxId) return false;
|
||||
return now < (this._nestBoxEntrySmoothUntil || -Infinity) || ((this.nestFade || 0) > 0.01 && (this.nestFade || 0) < 0.985);
|
||||
},
|
||||
|
||||
abortNestBoxEntry(box = null, reason = "巣箱がいっぱいで入れない") {
|
||||
const now = this.world?.time || 0;
|
||||
if (this.insideNestBoxId && (!box || this.insideNestBoxId === box.id)) this.leaveNestBox?.(box);
|
||||
if (!this.insideNestBoxId) this.nestFade = 0;
|
||||
if (!box || this.target === box) {
|
||||
this.target = null;
|
||||
this.targetKey = "";
|
||||
}
|
||||
this.sleeping = false;
|
||||
this.sleepSession = null;
|
||||
this._nestBoxAvoidId = box?.id || this._nestBoxAvoidId || "";
|
||||
this._nestBoxAvoidUntil = now + 10.0;
|
||||
this.actionIdCooldowns = this.actionIdCooldowns || {};
|
||||
this.actionCooldowns = this.actionCooldowns || {};
|
||||
this.actionIdCooldowns.sleep_in_bed = Math.max(this.actionIdCooldowns.sleep_in_bed || 0, 8.0);
|
||||
this.actionIdCooldowns.return_owned_structure = Math.max(this.actionIdCooldowns.return_owned_structure || 0, 5.0);
|
||||
this.actionCooldowns.sleep = Math.max(this.actionCooldowns.sleep || 0, 4.5);
|
||||
if (box) {
|
||||
const entry = this.world?.nestBoxEntryPoint ? this.world.nestBoxEntryPoint(box) : { x: box.x, y: box.y };
|
||||
const dx = this.x - (entry.x || box.x || this.x);
|
||||
const dy = this.y - (entry.y || box.y || this.y);
|
||||
const d = Math.hypot(dx, dy) || 1;
|
||||
// Do not teleport/push away from the nest. A failed entry becomes a normal
|
||||
// short avoidance target so ordinary movement handles the exit path.
|
||||
this.vx = clamp((this.vx || 0) + dx / d * 10, -80, 80);
|
||||
this.vy = clamp((this.vy || 0) + dy / d * 8, -80, 80);
|
||||
}
|
||||
this.awakeLockTimer = Math.max(this.awakeLockTimer || 0, 0.7);
|
||||
this._nestBoxSeekStartedAt = NaN;
|
||||
this._nestBoxSeekTargetId = "";
|
||||
this.goIdle?.(reason);
|
||||
this.thought = reason;
|
||||
this.pinchThoughtTimer = Math.max(this.pinchThoughtTimer || 0, 1.0);
|
||||
return false;
|
||||
},
|
||||
|
||||
wantsNestBox(box = null) {
|
||||
if (!box || box.dead || box.type !== "nest_box") return false;
|
||||
const now = this.world?.time || 0;
|
||||
if (this._nestBoxAvoidId === box.id && now < (this._nestBoxAvoidUntil || -Infinity)) return false;
|
||||
if (this.insideNestBoxId === box.id && (this.world?.time || 0) < (this.nestBoxStayUntil || -Infinity) && (this.state === "sleep" || this.state === "seek_bed" || this.sleeping)) return true;
|
||||
if (!(this.state === "sleep" || this.state === "seek_bed" || this.sleeping)) return false;
|
||||
return this.target === box || this.insideNestBoxId === box.id;
|
||||
|
|
@ -236,12 +253,22 @@
|
|||
const capacity = this.world?.nestBoxCapacity ? this.world.nestBoxCapacity(box) : 5;
|
||||
const occupants = this.world?.nestBoxOccupants ? this.world.nestBoxOccupants(box, Infinity) : [];
|
||||
const alreadyInside = this.insideNestBoxId === box.id;
|
||||
if (!alreadyInside && occupants.length >= capacity) return false;
|
||||
const now = this.world?.time || 0;
|
||||
if (!alreadyInside && occupants.length >= capacity) return this.abortNestBoxEntry?.(box, "巣箱がいっぱいで入れない") || false;
|
||||
if (!alreadyInside) {
|
||||
this.nestBoxEnteredAt = this.world?.time || 0;
|
||||
this.nestBoxStayUntil = Math.max(this.nestBoxStayUntil || -Infinity, (this.world?.time || 0) + 14);
|
||||
this._nestBoxSeekStartedAt = NaN;
|
||||
this._nestBoxSeekTargetId = "";
|
||||
const entry = this.world?.nestBoxEntryPoint ? this.world.nestBoxEntryPoint(box) : { x: box.x, y: box.y };
|
||||
this.nestBoxEnteredAt = now;
|
||||
this.nestBoxStayUntil = Math.max(this.nestBoxStayUntil || -Infinity, now + 14);
|
||||
this.nestBoxSlotIndex = Math.min(occupants.length, Math.max(0, capacity - 1));
|
||||
this._nestBoxEntrySmoothUntil = (this.world?.time || 0) + 0.85;
|
||||
this._nestBoxEntryStartX = Number.isFinite(this.x) ? this.x : entry.x;
|
||||
this._nestBoxEntryStartY = Number.isFinite(this.y) ? this.y : entry.y;
|
||||
this._nestBoxEntryDoorX = Number.isFinite(entry.x) ? entry.x : box.x;
|
||||
this._nestBoxEntryDoorY = Number.isFinite(entry.y) ? entry.y : box.y;
|
||||
this._nestBoxEntryDuration = 0.72;
|
||||
this._nestBoxEntrySmoothUntil = now + this._nestBoxEntryDuration;
|
||||
this.nestFade = Math.min(this.nestFade || 0, 0.14);
|
||||
}
|
||||
this.insideNestBoxId = box.id;
|
||||
this.target = box;
|
||||
|
|
@ -249,15 +276,31 @@
|
|||
if (this.state === "sleep") this.sleeping = true;
|
||||
const inner = this.world?.nestBoxInnerPoint ? this.world.nestBoxInnerPoint(box, this) : { x: box.x, y: box.y };
|
||||
const smoothDt = clamp(Number(dt || 0.016) || 0.016, 0.008, 0.045);
|
||||
const firstPhase = (this.world?.time || 0) < (this._nestBoxEntrySmoothUntil || -Infinity);
|
||||
const pull = clamp(smoothDt * (alreadyInside ? (firstPhase ? 5.2 : 8.5) : 7.2), 0, 0.55);
|
||||
this.x = lerp(this.x, inner.x, pull);
|
||||
this.y = lerp(this.y, inner.y, pull);
|
||||
if (!firstPhase && (this.nestFade >= 0.97 || distXY(this.x, this.y, inner.x, inner.y) < 2.0)) {
|
||||
this.x = inner.x;
|
||||
this.y = inner.y;
|
||||
const duration = Math.max(0.35, Number(this._nestBoxEntryDuration || 0.95) || 0.95);
|
||||
const progress = clamp(1 - Math.max(0, (this._nestBoxEntrySmoothUntil || now) - now) / duration, 0, 1);
|
||||
const eased = progress * progress * (3 - 2 * progress);
|
||||
if (progress < 1) {
|
||||
const sx = Number.isFinite(this._nestBoxEntryStartX) ? this._nestBoxEntryStartX : this.x;
|
||||
const sy = Number.isFinite(this._nestBoxEntryStartY) ? this._nestBoxEntryStartY : this.y;
|
||||
const doorX = Number.isFinite(this._nestBoxEntryDoorX) ? this._nestBoxEntryDoorX : box.x;
|
||||
const doorY = Number.isFinite(this._nestBoxEntryDoorY) ? this._nestBoxEntryDoorY : box.y;
|
||||
const ax = lerp(sx, doorX, eased);
|
||||
const ay = lerp(sy, doorY, eased);
|
||||
const bx = lerp(doorX, inner.x, eased);
|
||||
const by = lerp(doorY, inner.y, eased);
|
||||
this.x = lerp(ax, bx, eased);
|
||||
this.y = lerp(ay, by, eased);
|
||||
this.nestFade = clamp(Math.max(this.nestFade || 0, eased * 0.92), 0, 1);
|
||||
} else {
|
||||
const pull = clamp(1 - Math.exp(-smoothDt * 5.2), 0, 0.22);
|
||||
this.x = lerp(this.x, inner.x, pull);
|
||||
this.y = lerp(this.y, inner.y, pull);
|
||||
if (this.nestFade >= 0.97 || distXY(this.x, this.y, inner.x, inner.y) < 1.6) {
|
||||
this.x = inner.x;
|
||||
this.y = inner.y;
|
||||
}
|
||||
this.nestFade = clamp((this.nestFade || 0) + smoothDt * 1.8, 0, 1);
|
||||
}
|
||||
this.nestFade = clamp((this.nestFade || 0) + smoothDt * 4.2, 0, 1);
|
||||
this.vx = 0;
|
||||
this.vy = 0;
|
||||
this.impulseVx = 0;
|
||||
|
|
@ -265,25 +308,108 @@
|
|||
return true;
|
||||
},
|
||||
|
||||
beginNestBoxExitAnimation(box = null) {
|
||||
const b = box || this.nestBoxById(this.insideNestBoxId);
|
||||
const now = this.world?.time || 0;
|
||||
if (!b || !this.world?.nestBoxEntryPoint) {
|
||||
this._nestBoxExitSmoothUntil = -Infinity;
|
||||
this._nestBoxExitDuration = 0;
|
||||
this._nestBoxExitBoxId = "";
|
||||
return false;
|
||||
}
|
||||
const inner = this.world?.nestBoxInnerPoint ? this.world.nestBoxInnerPoint(b, this) : { x: b.x, y: b.y };
|
||||
const door = this.world.nestBoxEntryPoint(b);
|
||||
const exit = this.world.nestBoxExitPoint ? this.world.nestBoxExitPoint(b, this) : door;
|
||||
this._nestBoxExitStartX = Number.isFinite(this.x) ? this.x : inner.x;
|
||||
this._nestBoxExitStartY = Number.isFinite(this.y) ? this.y : inner.y;
|
||||
if ((this.nestFade || 0) > 0.82 || distXY(this._nestBoxExitStartX, this._nestBoxExitStartY, inner.x, inner.y) > (b.r || 42) * 0.7) {
|
||||
this._nestBoxExitStartX = inner.x;
|
||||
this._nestBoxExitStartY = inner.y;
|
||||
}
|
||||
this._nestBoxExitDoorX = Number.isFinite(door.x) ? door.x : b.x;
|
||||
this._nestBoxExitDoorY = Number.isFinite(door.y) ? door.y : b.y;
|
||||
this._nestBoxExitEndX = Number.isFinite(exit.x) ? exit.x : this._nestBoxExitDoorX;
|
||||
this._nestBoxExitEndY = Number.isFinite(exit.y) ? exit.y : this._nestBoxExitDoorY;
|
||||
this._nestBoxExitDuration = 0.58;
|
||||
this._nestBoxExitSmoothUntil = now + this._nestBoxExitDuration;
|
||||
this._nestBoxExitBoxId = b.id || "";
|
||||
this.nestFade = Math.max(this.nestFade || 0, 0.96);
|
||||
return true;
|
||||
},
|
||||
|
||||
updateNestBoxExitAnimation(dt = 0.016) {
|
||||
const now = this.world?.time || 0;
|
||||
const until = this._nestBoxExitSmoothUntil || -Infinity;
|
||||
if (!(now < until)) return false;
|
||||
const duration = Math.max(0.20, Number(this._nestBoxExitDuration || 0.58) || 0.58);
|
||||
const progress = clamp(1 - Math.max(0, until - now) / duration, 0, 1);
|
||||
const eased = progress * progress * (3 - 2 * progress);
|
||||
const sx = Number.isFinite(this._nestBoxExitStartX) ? this._nestBoxExitStartX : this.x;
|
||||
const sy = Number.isFinite(this._nestBoxExitStartY) ? this._nestBoxExitStartY : this.y;
|
||||
const dx = Number.isFinite(this._nestBoxExitDoorX) ? this._nestBoxExitDoorX : sx;
|
||||
const dy = Number.isFinite(this._nestBoxExitDoorY) ? this._nestBoxExitDoorY : sy;
|
||||
const ex = Number.isFinite(this._nestBoxExitEndX) ? this._nestBoxExitEndX : dx;
|
||||
const ey = Number.isFinite(this._nestBoxExitEndY) ? this._nestBoxExitEndY : dy;
|
||||
const ax = lerp(sx, dx, eased);
|
||||
const ay = lerp(sy, dy, eased);
|
||||
const bx = lerp(dx, ex, eased);
|
||||
const by = lerp(dy, ey, eased);
|
||||
const pad = CONFIG.worldPadding || 30;
|
||||
this.x = clamp(lerp(ax, bx, eased), pad, Math.max(pad, (this.world?.w || this.x) - pad));
|
||||
this.y = clamp(lerp(ay, by, eased), pad, Math.max(pad, (this.world?.h || this.y) - pad));
|
||||
this.nestFade = clamp(1 - eased, 0, 1);
|
||||
this.vx = 0;
|
||||
this.vy = 0;
|
||||
if (progress >= 0.995 || until - now <= Math.max(0.018, dt || 0.016)) {
|
||||
this.x = clamp(ex, pad, Math.max(pad, (this.world?.w || ex) - pad));
|
||||
this.y = clamp(ey, pad, Math.max(pad, (this.world?.h || ey) - pad));
|
||||
const outDx = ex - dx;
|
||||
const outDy = ey - dy;
|
||||
const outD = Math.hypot(outDx, outDy) || 1;
|
||||
this.vx = outDx / outD * deterministicRange(this.world, "nest-exit-vx", 6, 14, this);
|
||||
this.vy = outDy / outD * deterministicRange(this.world, "nest-exit-vy", 8, 18, this);
|
||||
this.nestFade = 0;
|
||||
this._nestBoxExitSmoothUntil = -Infinity;
|
||||
this._nestBoxExitDuration = 0;
|
||||
this._nestBoxExitBoxId = "";
|
||||
this._nestBoxExitStartX = this._nestBoxExitStartY = this._nestBoxExitDoorX = this._nestBoxExitDoorY = this._nestBoxExitEndX = this._nestBoxExitEndY = NaN;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
leaveNestBox(box = null) {
|
||||
const b = box || this.nestBoxById(this.insideNestBoxId);
|
||||
if (b && this.world?.nestBoxEntryPoint) {
|
||||
const hadInside = Boolean(this.insideNestBoxId);
|
||||
if (b && this.world?.nestBoxEntryPoint && hadInside) this.beginNestBoxExitAnimation?.(b);
|
||||
else if (b && this.world?.nestBoxEntryPoint) {
|
||||
const entry = this.world.nestBoxExitPoint ? this.world.nestBoxExitPoint(b, this) : this.world.nestBoxEntryPoint(b);
|
||||
this.x = clamp(entry.x, CONFIG.worldPadding, this.world.w - CONFIG.worldPadding);
|
||||
this.y = clamp(entry.y, CONFIG.worldPadding, this.world.h - CONFIG.worldPadding);
|
||||
this.vx = deterministicRange(this.world, "sleep-disease-shiver-vx", -8, 8, this);
|
||||
this.vy = deterministicRange(this.world, "sleep-disease-shiver-vy", 6, 18, this);
|
||||
}
|
||||
this.insideNestBoxId = null;
|
||||
this.nestFade = 0;
|
||||
if (this.target === b) {
|
||||
this.target = null;
|
||||
this.targetKey = "";
|
||||
}
|
||||
this.sleeping = false;
|
||||
this.sleepSession = null;
|
||||
if (this.state === "sleep" || this.state === "seek_bed") this.goIdle?.("巣箱から出た");
|
||||
this.nestBoxEnteredAt = -Infinity;
|
||||
this.nestBoxStayUntil = -Infinity;
|
||||
this.nestBoxSlotIndex = -1;
|
||||
this._nestBoxEntrySmoothUntil = -Infinity;
|
||||
this._nestBoxEntryStartX = NaN;
|
||||
this._nestBoxEntryStartY = NaN;
|
||||
this._nestBoxEntryDoorX = NaN;
|
||||
this._nestBoxEntryDoorY = NaN;
|
||||
this._nestBoxEntryDuration = 0;
|
||||
this._nestBoxSeekStartedAt = NaN;
|
||||
this._nestBoxSeekTargetId = "";
|
||||
return true;
|
||||
},
|
||||
|
||||
updateNestBoxPresence(dt = 0) {
|
||||
if (this.updateNestBoxExitAnimation?.(dt || 0.016)) return null;
|
||||
const currentBox = this.nestBoxById(this.insideNestBoxId);
|
||||
const targetBox = this.target && !this.target.dead && this.target.type === "nest_box" ? this.target : null;
|
||||
const box = currentBox || targetBox;
|
||||
|
|
@ -310,22 +436,37 @@
|
|||
|
||||
if (!this.wantsNestBox(targetBox)) {
|
||||
this.nestFade = clamp((this.nestFade || 0) - dt * 3.0, 0, 1);
|
||||
if (targetBox && this.target === targetBox && !this.insideNestBoxId) {
|
||||
const now = this.world?.time || 0;
|
||||
this.target = null;
|
||||
this.targetKey = "";
|
||||
this._nestBoxAvoidId = targetBox.id || this._nestBoxAvoidId || "";
|
||||
this._nestBoxAvoidUntil = Math.max(this._nestBoxAvoidUntil || -Infinity, now + 3.2);
|
||||
if (this.state === "idle" || this.state === "wander") this.wanderAngle = (this.wanderAngle || 0) + deterministicRange(this.world, "nest-idle-dispersal-angle", -1.1, 1.1, this, targetBox);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!Number.isFinite(this._nestBoxSeekStartedAt) || this._nestBoxSeekTargetId !== targetBox.id) {
|
||||
this._nestBoxSeekStartedAt = this.world?.time || 0;
|
||||
this._nestBoxSeekTargetId = targetBox.id;
|
||||
}
|
||||
if (!this.insideNestBoxId && global.TarinaiNestSleepSystem?.sleepPlaceAvailableFor && !global.TarinaiNestSleepSystem.sleepPlaceAvailableFor(this.world, this, targetBox)) {
|
||||
return this.abortNestBoxEntry?.(targetBox, "巣箱がいっぱいで入れない") || null;
|
||||
}
|
||||
|
||||
const entry = this.world?.nestBoxEntryPoint ? this.world.nestBoxEntryPoint(targetBox) : { x: targetBox.x, y: targetBox.y };
|
||||
const base = this.world?.nestBoxBaseRect ? this.world.nestBoxBaseRect(targetBox) : null;
|
||||
const r = targetBox.r || 42;
|
||||
const entryD = distXY(this.x, this.y, entry.x, entry.y);
|
||||
const centerD = distXY(this.x, this.y, targetBox.x, targetBox.y);
|
||||
const inDoorColumn = base && this.x >= base.left + r * 0.18 && this.x <= base.right - r * 0.18 && this.y >= base.top + r * 0.06 && this.y <= base.bottom + r * 0.48;
|
||||
const captureRange = Math.max(76, r * 1.55, this.radius + 48);
|
||||
const nearEnough = entryD <= captureRange || centerD <= Math.max(78, r * 1.38) || inDoorColumn;
|
||||
const inDoorColumn = base && this.x >= base.left + r * 0.30 && this.x <= base.right - r * 0.30 && this.y >= base.top + r * 0.16 && this.y <= base.bottom + r * 0.34;
|
||||
const captureRange = Math.max(58, r * 1.18, (this.radius || 20) + 30);
|
||||
const nearEnough = entryD <= captureRange || centerD <= Math.max(58, r * 1.08) || inDoorColumn;
|
||||
if (nearEnough) {
|
||||
this.enterNestBox(targetBox, dt || 0.016);
|
||||
return targetBox;
|
||||
if (this.enterNestBox(targetBox, dt || 0.016)) return targetBox;
|
||||
return null;
|
||||
}
|
||||
|
||||
this.nestFade = clamp((this.nestFade || 0) - dt * 3.0, 0, 1);
|
||||
return null;
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue