2026-06-21 14:09:35 +09:00
|
|
|
"use strict";
|
2026-06-27 19:22:38 +09:00
|
|
|
(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);
|
|
|
|
|
|
2026-06-21 14:09:35 +09:00
|
|
|
|
|
|
|
|
(function (global) {
|
|
|
|
|
const Tarinai = global.Tarinai;
|
|
|
|
|
if (!Tarinai) throw new Error("Tarinai is not available for mixin: tarinai_disease_nest.js");
|
|
|
|
|
Object.defineProperties(Tarinai.prototype, Object.getOwnPropertyDescriptors({
|
|
|
|
|
infectZunchiDisease(source = null, force = false) {
|
|
|
|
|
if (this.dead || this.zunchiDisease) return false;
|
|
|
|
|
if (!force && (this.zunchiDiseaseCooldown || 0) > 0) return false;
|
|
|
|
|
this.zunchiDisease = true;
|
|
|
|
|
this.zunchiDiseaseSeverity = Math.max(this.zunchiDiseaseSeverity || 0, source ? 0.72 : 0.86);
|
|
|
|
|
this.zunchiDiseaseCooldown = 12;
|
|
|
|
|
this.fearTimer = Math.max(this.fearTimer || 0, 0.35);
|
|
|
|
|
this.surpriseTimer = Math.max(this.surpriseTimer || 0, 0.45);
|
|
|
|
|
this.thought = "\u305a\u3093\u3061\u75c5\u306b\u304b\u304b\u3063\u305f";
|
|
|
|
|
audio.disease?.();
|
|
|
|
|
this.world?.spawnBubble?.(this.x, this.y - this.radius * 1.20, "\u3046\u3045\u2026", "rgba(38,82,38,0.82)");
|
2026-06-21 22:29:00 +09:00
|
|
|
this.world?.emit?.("disease:infected", { tarinai: this, diseaseId: "disease_zunchi", source });
|
|
|
|
|
this.recordChangeCause?.("\u75c5\u6c17\u767a\u75c7", "\u305a\u3093\u3061\u75c5");
|
2026-06-21 14:09:35 +09:00
|
|
|
return true;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
recoverZunchiDisease(reason = "") {
|
|
|
|
|
if (!this.zunchiDisease) return false;
|
|
|
|
|
this.zunchiDisease = false;
|
|
|
|
|
this.zunchiDiseaseSeverity = 0;
|
|
|
|
|
this.zunchiDiseaseCooldown = 18;
|
|
|
|
|
this.zunchiStain = clamp((this.zunchiStain || 0) - 35, 0, 100);
|
2026-06-23 18:11:42 +09:00
|
|
|
if (typeof applyNeedRelief === "function") applyNeedRelief(this, { health: -18, safety: -6 });
|
2026-06-21 14:09:35 +09:00
|
|
|
this.thought = reason || "\u305a\u3093\u3061\u75c5\u304c\u6cbb\u307e\u3063\u305f";
|
|
|
|
|
audio.heal?.();
|
|
|
|
|
this.world?.spawnBubble?.(this.x, this.y - this.radius * 1.18, "\u306f\u3046", "rgba(62,104,62,0.78)");
|
2026-06-21 22:29:00 +09:00
|
|
|
this.world?.emit?.("disease:cured", { tarinai: this, diseaseId: "disease_zunchi", reason });
|
|
|
|
|
this.recordChangeCause?.("\u75c5\u6c17\u56de\u5fa9", "\u305a\u3093\u3061\u75c5");
|
2026-06-21 14:09:35 +09:00
|
|
|
return true;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
emitZunchiDiseaseEffect(dt) {
|
|
|
|
|
if (!this.zunchiDisease || !this.world?.effects) return;
|
2026-06-27 19:22:38 +09:00
|
|
|
if (deterministicChance(this.world, "zunchi-disease-miasma", dt * (1.2 + (this.zunchiDiseaseSeverity || 0) * 1.4), this)) {
|
|
|
|
|
this.world.effects.push(new Effect("zunchi_miasma", this.x + deterministicRange(this.world, "zunchi-miasma-x", -this.radius * 0.45, this.radius * 0.45, this), this.y - this.radius * deterministicRange(this.world, "zunchi-miasma-y", 0.05, 0.72, this), {
|
|
|
|
|
vx: deterministicRange(this.world, "zunchi-miasma-vx", -5, 5, this),
|
|
|
|
|
vy: deterministicRange(this.world, "zunchi-miasma-vy", -10, -3, this),
|
|
|
|
|
life: deterministicRange(this.world, "zunchi-miasma-life", 1.4, 2.4, this),
|
|
|
|
|
size: deterministicRange(this.world, "zunchi-miasma-size", 8, 18, this) * (0.8 + (this.zunchiDiseaseSeverity || 0) * 0.45),
|
2026-06-21 14:09:35 +09:00
|
|
|
color: "rgba(16,78,28,0.70)",
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
updateZunchiDisease(dt) {
|
|
|
|
|
this.zunchiStain = clamp(this.zunchiStain || 0, 0, 100);
|
|
|
|
|
this.zunchiDiseaseCooldown = Math.max(0, (this.zunchiDiseaseCooldown || 0) - dt);
|
|
|
|
|
if (this.world?.weather === "light_rain") {
|
|
|
|
|
this.zunchiStain = clamp(this.zunchiStain - dt * 2.4, 0, 100);
|
|
|
|
|
if (this.zunchiDisease) this.zunchiDiseaseSeverity = Math.max(0, (this.zunchiDiseaseSeverity || 1) - dt * 0.006);
|
|
|
|
|
}
|
2026-06-27 19:22:38 +09:00
|
|
|
const zunchiSelfInfectChance = this.diseaseChance ? this.diseaseChance(dt * clamp((this.zunchiStain - 82) / 18, 0, 1) * 0.045) : dt * clamp((this.zunchiStain - 82) / 18, 0, 1) * 0.045;
|
|
|
|
|
if (!this.zunchiDisease && this.zunchiStain > 82 && deterministicChance(this.world, "zunchi-self-infect", zunchiSelfInfectChance, this)) {
|
2026-06-21 14:09:35 +09:00
|
|
|
this.infectZunchiDisease(null, true);
|
|
|
|
|
this.world?.log?.(`${this.name}\u306f\u305a\u3093\u3061\u75c5\u3092\u767a\u75c7\u3057\u305f\u3002`, "accident", { participants: [this] });
|
|
|
|
|
}
|
|
|
|
|
if (this.zunchiDisease) {
|
|
|
|
|
this.zunchiDiseaseSeverity = clamp((this.zunchiDiseaseSeverity || 0.7) + dt * 0.004, 0, 1.25);
|
2026-06-23 18:11:42 +09:00
|
|
|
if (typeof applyNeedShock === "function") applyNeedShock(this, { health: dt * (5 + 4 * this.zunchiDiseaseSeverity) });
|
2026-06-26 12:46:32 +09:00
|
|
|
this.energy = clamp(this.energy - dt * 0.035 * this.zunchiDiseaseSeverity, 0, typeof tarinaiMaxEnergy === "function" ? tarinaiMaxEnergy(this) : (Number(this.maxEnergy) || 100));
|
2026-06-21 14:09:35 +09:00
|
|
|
if (this.energy > 82) this.zunchiDiseaseSeverity = Math.max(0, this.zunchiDiseaseSeverity - dt * (this.energy > 94 ? 0.020 : 0.010));
|
|
|
|
|
this.emitZunchiDiseaseEffect(dt);
|
|
|
|
|
if (this.zunchiDiseaseSeverity <= 0.04) {
|
|
|
|
|
if (this.recoverZunchiDisease("\u4f53\u529b\u304c\u623b\u308a\u3001\u305a\u3093\u3061\u75c5\u304c\u6cbb\u307e\u3063\u305f")) this.world?.log?.(`${this.name}\u306e\u305a\u3093\u3061\u75c5\u304c\u6cbb\u307e\u3063\u305f\u3002`, "accident", { participants: [this] });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
infectSleepDisease(source = null) {
|
|
|
|
|
if (this.dead || this.sleepDisease) return false;
|
|
|
|
|
this.sleepDisease = true;
|
|
|
|
|
this.sleepDiseaseCooldown = 8;
|
|
|
|
|
this.sleepDiseaseAnchorX = this.x;
|
|
|
|
|
this.sleepDiseaseAnchorY = this.y;
|
2026-06-24 13:48:22 +09:00
|
|
|
this.startSleeping(null, "\u306d\u3080\u308a\u75c5\u3067\u7720\u308a\u7d9a\u3051\u3066\u3044\u308b");
|
2026-06-21 14:09:35 +09:00
|
|
|
this.fightTimer = 0;
|
|
|
|
|
this.intimidateTimer = 0;
|
|
|
|
|
this.fightTargetIds = [];
|
|
|
|
|
this.thought = "\u306d\u3080\u308a\u75c5\u3067\u7720\u308a\u7d9a\u3051\u3066\u3044\u308b";
|
|
|
|
|
audio.disease?.();
|
|
|
|
|
this.world?.spawnBubble?.(this.x, this.y - this.radius * 1.18, "Zzz...", "rgba(72,76,120,0.76)");
|
|
|
|
|
this.world?.log?.(`${this.name}\u306f\u306d\u3080\u308a\u75c5\u306b\u304b\u304b\u3063\u305f\u3002`, "accident", { participants: [this] });
|
2026-06-21 22:29:00 +09:00
|
|
|
this.world?.emit?.("disease:infected", { tarinai: this, diseaseId: "disease_sleep", source });
|
|
|
|
|
this.recordChangeCause?.("\u75c5\u6c17\u767a\u75c7", "\u306d\u3080\u308a\u75c5");
|
2026-06-21 14:09:35 +09:00
|
|
|
return true;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
recoverSleepDisease(reason = "") {
|
|
|
|
|
if (!this.sleepDisease) return false;
|
|
|
|
|
this.sleepDisease = false;
|
|
|
|
|
this.sleepDiseaseCooldown = 14;
|
|
|
|
|
this.sleepDiseaseAnchorX = null;
|
|
|
|
|
this.sleepDiseaseAnchorY = null;
|
2026-06-24 13:48:22 +09:00
|
|
|
this.goIdle(reason || "\u306d\u3080\u308a\u75c5\u304c\u6cbb\u3063\u305f");
|
2026-06-21 14:09:35 +09:00
|
|
|
this.sleeping = false;
|
2026-06-21 16:26:12 +09:00
|
|
|
this.awakeLockTimer = Math.max(this.awakeLockTimer || 0, 10);
|
2026-06-21 14:09:35 +09:00
|
|
|
audio.heal?.();
|
|
|
|
|
this.surpriseTimer = Math.max(this.surpriseTimer || 0, 0.65);
|
|
|
|
|
this.world?.spawnBubble?.(this.x, this.y - this.radius * 1.18, "!", "rgba(72,76,120,0.76)");
|
|
|
|
|
this.world?.log?.(`${this.name}\u306e\u306d\u3080\u308a\u75c5\u304c\u6cbb\u3063\u305f\u3002`, "accident", { participants: [this] });
|
2026-06-21 22:29:00 +09:00
|
|
|
this.world?.emit?.("disease:cured", { tarinai: this, diseaseId: "disease_sleep", reason });
|
|
|
|
|
this.recordChangeCause?.("\u75c5\u6c17\u56de\u5fa9", "\u306d\u3080\u308a\u75c5");
|
2026-06-21 14:09:35 +09:00
|
|
|
return true;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
infectExplosionDisease(source = null) {
|
|
|
|
|
if (this.dead || this.explosionDisease) return false;
|
|
|
|
|
this.explosionDisease = true;
|
2026-06-21 22:29:00 +09:00
|
|
|
this.explosionDiseaseTimer = Math.max(this.explosionDiseaseTimer || 0, 60);
|
2026-06-21 14:09:35 +09:00
|
|
|
this.fearTimer = Math.max(this.fearTimer || 0, 0.55);
|
|
|
|
|
this.thought = "\u7206\u767a\u75c5\u3067\u843d\u3061\u7740\u304b\u306a\u3044";
|
|
|
|
|
audio.disease?.();
|
|
|
|
|
this.world?.spawnBubble?.(this.x, this.y - this.radius * 1.2, "!", "rgba(178,78,42,0.82)");
|
|
|
|
|
this.world?.log?.(`${this.name}\u306f\u7206\u767a\u75c5\u3092\u767a\u75c7\u3057\u305f\u3002`, "accident", { participants: [this] });
|
2026-06-21 22:29:00 +09:00
|
|
|
this.world?.emit?.("disease:infected", { tarinai: this, diseaseId: "disease_explosion", source });
|
|
|
|
|
this.recordChangeCause?.("\u75c5\u6c17\u767a\u75c7", "\u7206\u767a\u75c5");
|
2026-06-21 14:09:35 +09:00
|
|
|
return true;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
recoverExplosionDisease(reason = "") {
|
|
|
|
|
if (!this.explosionDisease) return false;
|
|
|
|
|
this.explosionDisease = false;
|
|
|
|
|
this.explosionDiseaseTimer = 0;
|
|
|
|
|
this.thought = reason || "\u7206\u767a\u75c5\u304c\u6cbb\u3063\u305f";
|
|
|
|
|
audio.heal?.();
|
2026-06-23 18:11:42 +09:00
|
|
|
if (typeof applyNeedRelief === "function") applyNeedRelief(this, { health: -22, safety: -8 });
|
2026-06-21 14:09:35 +09:00
|
|
|
this.world?.spawnBubble?.(this.x, this.y - this.radius * 1.18, "\u306f\u3046", "rgba(82,126,88,0.76)");
|
|
|
|
|
this.world?.log?.(`${this.name}\u306e\u7206\u767a\u75c5\u304c\u6cbb\u3063\u305f\u3002`, "accident", { participants: [this] });
|
2026-06-21 22:29:00 +09:00
|
|
|
this.world?.emit?.("disease:cured", { tarinai: this, diseaseId: "disease_explosion", reason });
|
|
|
|
|
this.recordChangeCause?.("\u75c5\u6c17\u56de\u5fa9", "\u7206\u767a\u75c5");
|
2026-06-21 14:09:35 +09:00
|
|
|
return true;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
recordBleedExposure() {
|
|
|
|
|
this.lastBleedAt = this.world?.time || 0;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
infectFightDisease(source = null) {
|
|
|
|
|
if (this.dead || this.fightDisease) return false;
|
|
|
|
|
this.fightDisease = true;
|
|
|
|
|
this.fightDiseaseCooldown = 18;
|
|
|
|
|
this.fearTimer = Math.max(this.fearTimer || 0, 0.35);
|
|
|
|
|
this.thought = "\u304d\u305a\u3064\u304d\u75c5\u3067\u5f53\u3066\u3082\u306a\u304f\u5f77\u5fa8\u3063\u3066\u3044\u308b";
|
|
|
|
|
audio.disease?.();
|
|
|
|
|
this.world?.spawnBubble?.(this.x, this.y - this.radius * 1.20, "?", "rgba(150,78,44,0.80)");
|
|
|
|
|
this.world?.log?.(`${this.name}\u306f\u304d\u305a\u3064\u304d\u75c5\u3092\u767a\u75c7\u3057\u305f\u3002`, "fight", { participants: [this] });
|
2026-06-21 22:29:00 +09:00
|
|
|
this.world?.emit?.("disease:infected", { tarinai: this, diseaseId: "disease_fight", source });
|
|
|
|
|
this.recordChangeCause?.("\u75c5\u6c17\u767a\u75c7", "\u304d\u305a\u3064\u304d\u75c5");
|
2026-06-21 14:09:35 +09:00
|
|
|
return true;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
recoverFightDisease(reason = "") {
|
|
|
|
|
if (!this.fightDisease) return false;
|
|
|
|
|
this.fightDisease = false;
|
|
|
|
|
this.fightDiseaseCooldown = 20;
|
|
|
|
|
this.thought = reason || "\u304d\u305a\u3064\u304d\u75c5\u304c\u6cbb\u3063\u305f";
|
|
|
|
|
audio.heal?.();
|
2026-06-23 18:11:42 +09:00
|
|
|
if (typeof applyNeedRelief === "function") applyNeedRelief(this, { health: -24, safety: -10 });
|
2026-06-21 14:09:35 +09:00
|
|
|
this.world?.spawnBubble?.(this.x, this.y - this.radius * 1.18, "\u306f\u3046", "rgba(82,126,88,0.76)");
|
|
|
|
|
this.world?.log?.(`${this.name}\u306e\u304d\u305a\u3064\u304d\u75c5\u304c\u6cbb\u3063\u305f\u3002`, "fight", { participants: [this] });
|
2026-06-21 22:29:00 +09:00
|
|
|
this.world?.emit?.("disease:cured", { tarinai: this, diseaseId: "disease_fight", reason });
|
|
|
|
|
this.recordChangeCause?.("\u75c5\u6c17\u56de\u5fa9", "\u304d\u305a\u3064\u304d\u75c5");
|
2026-06-21 14:09:35 +09:00
|
|
|
return true;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
freezeSleepDiseaseMotion(dt = 0) {
|
|
|
|
|
if (!this.sleepDisease) return false;
|
2026-06-21 16:26:12 +09:00
|
|
|
const beingDraggedByAnt = Boolean((this.antDraggedTimer || 0) > 0 && this.antDraggedBy);
|
2026-06-21 14:09:35 +09:00
|
|
|
if (!Number.isFinite(this.sleepDiseaseAnchorX)) this.sleepDiseaseAnchorX = this.x;
|
|
|
|
|
if (!Number.isFinite(this.sleepDiseaseAnchorY)) this.sleepDiseaseAnchorY = this.y;
|
2026-06-24 13:48:22 +09:00
|
|
|
this.startSleeping(null, beingDraggedByAnt ? "\u7720\u3063\u305f\u307e\u307e\u30a2\u30ea\u306b\u904b\u3070\u308c\u3066\u3044\u308b" : "\u306d\u3080\u308a\u75c5\u3067\u7720\u308a\u7d9a\u3051\u3066\u3044\u308b");
|
2026-06-21 14:09:35 +09:00
|
|
|
this.vx = 0;
|
|
|
|
|
this.vy = 0;
|
2026-06-21 16:26:12 +09:00
|
|
|
if (beingDraggedByAnt) {
|
|
|
|
|
this.sleepDiseaseAnchorX = this.x;
|
|
|
|
|
this.sleepDiseaseAnchorY = this.y;
|
|
|
|
|
} else {
|
|
|
|
|
this.x = this.sleepDiseaseAnchorX;
|
|
|
|
|
this.y = this.sleepDiseaseAnchorY;
|
|
|
|
|
}
|
2026-06-21 14:09:35 +09:00
|
|
|
this.wanderAngle = this.wanderAngle || 0;
|
2026-06-21 22:29:00 +09:00
|
|
|
this.thought = beingDraggedByAnt ? "\u7720\u3063\u305f\u307e\u307e\u30a2\u30ea\u306b\u904b\u3070\u308c\u3066\u3044\u308b" : "\u306d\u3080\u308a\u75c5\u3067\u7720\u308a\u7d9a\u3051\u3066\u3044\u308b";
|
2026-06-21 14:09:35 +09:00
|
|
|
if (this.world && this.world.time > (this.nextSleepBubbleAt || 0)) {
|
|
|
|
|
this.nextSleepBubbleAt = this.world.time + 5.0;
|
|
|
|
|
this.world.spawnBubble?.(this.x, this.y - this.radius * 1.18, "Zzz...", "rgba(65,70,92,0.72)");
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
nestBoxById(id) {
|
|
|
|
|
if (!id) return null;
|
|
|
|
|
return this.world?.items?.find?.(it => it && !it.dead && it.id === id && it.type === "nest_box") || null;
|
|
|
|
|
},
|
|
|
|
|
wantsNestBox(box = null) {
|
|
|
|
|
if (!box || box.dead || box.type !== "nest_box") return false;
|
2026-06-24 13:48:22 +09:00
|
|
|
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;
|
2026-06-21 14:09:35 +09:00
|
|
|
return this.target === box || this.insideNestBoxId === box.id;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
enterNestBox(box, dt = 0.016) {
|
|
|
|
|
if (!box || box.dead || box.type !== "nest_box" || this.dead) return false;
|
|
|
|
|
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;
|
|
|
|
|
if (!alreadyInside) {
|
|
|
|
|
this.nestBoxEnteredAt = this.world?.time || 0;
|
|
|
|
|
this.nestBoxStayUntil = Math.max(this.nestBoxStayUntil || -Infinity, (this.world?.time || 0) + 14);
|
|
|
|
|
}
|
|
|
|
|
this.insideNestBoxId = box.id;
|
|
|
|
|
this.target = box;
|
2026-06-24 13:48:22 +09:00
|
|
|
if (this.state === "seek_bed") this.startSleeping(box, "\u5de3\u7bb1\u306e\u4e2d\u3067\u7720\u3063\u3066\u3044\u308b");
|
2026-06-21 14:09:35 +09:00
|
|
|
if (this.state === "sleep") this.sleeping = true;
|
|
|
|
|
const inner = this.world?.nestBoxInnerPoint ? this.world.nestBoxInnerPoint(box, this) : { x: box.x, y: box.y };
|
|
|
|
|
const pull = clamp(dt * (alreadyInside ? 10.0 : 13.5), 0, 1);
|
|
|
|
|
this.x = lerp(this.x, inner.x, pull);
|
|
|
|
|
this.y = lerp(this.y, inner.y, pull);
|
|
|
|
|
if (this.nestFade >= 0.88 || distXY(this.x, this.y, inner.x, inner.y) < 3.5) {
|
|
|
|
|
this.x = inner.x;
|
|
|
|
|
this.y = inner.y;
|
|
|
|
|
}
|
|
|
|
|
this.nestFade = clamp((this.nestFade || 0) + dt * 5.8, 0, 1);
|
|
|
|
|
this.vx = 0;
|
|
|
|
|
this.vy = 0;
|
|
|
|
|
this.impulseVx = 0;
|
|
|
|
|
this.impulseVy = 0;
|
|
|
|
|
return true;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
leaveNestBox(box = null) {
|
|
|
|
|
const b = box || this.nestBoxById(this.insideNestBoxId);
|
|
|
|
|
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);
|
2026-06-27 19:22:38 +09:00
|
|
|
this.vx = deterministicRange(this.world, "sleep-disease-shiver-vx", -8, 8, this);
|
|
|
|
|
this.vy = deterministicRange(this.world, "sleep-disease-shiver-vy", 6, 18, this);
|
2026-06-21 14:09:35 +09:00
|
|
|
}
|
|
|
|
|
this.insideNestBoxId = null;
|
|
|
|
|
this.nestFade = 0;
|
|
|
|
|
this.nestBoxEnteredAt = -Infinity;
|
|
|
|
|
this.nestBoxStayUntil = -Infinity;
|
|
|
|
|
return true;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
updateNestBoxPresence(dt = 0) {
|
|
|
|
|
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;
|
|
|
|
|
if (!box) {
|
|
|
|
|
this.insideNestBoxId = null;
|
|
|
|
|
this.nestFade = clamp((this.nestFade || 0) - dt * 3.0, 0, 1);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (currentBox && !this.wantsNestBox(currentBox)) {
|
|
|
|
|
this.leaveNestBox(currentBox);
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (currentBox) {
|
2026-06-24 13:48:22 +09:00
|
|
|
if ((this.world?.time || 0) < (this.nestBoxStayUntil || -Infinity) && (this.state === "sleep" || this.state === "seek_bed" || this.sleeping)) {
|
|
|
|
|
this.startSleeping(box, "\u5de3\u7bb1\u306e\u4e2d\u3067\u7720\u3063\u3066\u3044\u308b");
|
2026-06-21 14:09:35 +09:00
|
|
|
this.sleeping = true;
|
|
|
|
|
this.target = currentBox;
|
|
|
|
|
}
|
|
|
|
|
this.enterNestBox(currentBox, dt);
|
|
|
|
|
return currentBox;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!this.wantsNestBox(targetBox)) {
|
|
|
|
|
this.nestFade = clamp((this.nestFade || 0) - dt * 3.0, 0, 1);
|
|
|
|
|
return 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(118, r * 2.05, this.radius + 82);
|
|
|
|
|
const nearEnough = entryD <= captureRange || centerD <= Math.max(104, r * 1.90) || inDoorColumn;
|
|
|
|
|
if (nearEnough) {
|
|
|
|
|
this.enterNestBox(targetBox, dt || 0.016);
|
|
|
|
|
return targetBox;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this.nestFade = clamp((this.nestFade || 0) - dt * 3.0, 0, 1);
|
|
|
|
|
return null;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
applyWaterEffect(dt, source = "water") {
|
|
|
|
|
const power = Math.max(0, dt || 0);
|
2026-06-23 18:11:42 +09:00
|
|
|
if (typeof applyNeedRelief === "function") applyNeedRelief(this, { health: -power * 12, safety: -power * 6 });
|
2026-06-26 12:46:32 +09:00
|
|
|
this.energy = clamp(this.energy + power * 0.5, 0, typeof tarinaiMaxEnergy === "function" ? tarinaiMaxEnergy(this) : (Number(this.maxEnergy) || 100));
|
2026-06-21 14:09:35 +09:00
|
|
|
this.hunger = clamp(this.hunger - power * 0.2, 0, 115);
|
|
|
|
|
this.zunchiStain = clamp((this.zunchiStain || 0) - power * 10.5, 0, 100);
|
|
|
|
|
if (this.zunchiDisease) {
|
|
|
|
|
this.zunchiDiseaseSeverity = Math.max(0, (this.zunchiDiseaseSeverity || 1) - power * (this.energy > 70 ? 0.050 : 0.028));
|
|
|
|
|
if (this.zunchiDiseaseSeverity <= 0.04 && this.recoverZunchiDisease("\u6c34\u3067\u305a\u3093\u3061\u75c5\u304c\u6cbb\u307e\u3063\u305f")) {
|
|
|
|
|
this.world?.log?.(`${this.name}\u306f\u6c34\u3067\u305a\u3093\u3061\u75c5\u304c\u6cbb\u307e\u3063\u305f\u3002`, "accident", { participants: [this] });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (this.explosionDisease) this.recoverExplosionDisease("\u6c34\u3067\u7206\u767a\u75c5\u304c\u6cbb\u3063\u305f");
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
updateSpecialDiseases(dt) {
|
|
|
|
|
this.sleepDiseaseCooldown = Math.max(0, (this.sleepDiseaseCooldown || 0) - dt);
|
|
|
|
|
this.fightDiseaseCooldown = Math.max(0, (this.fightDiseaseCooldown || 0) - dt);
|
|
|
|
|
if (this.explosionDisease) {
|
|
|
|
|
this.explosionDiseaseTimer = Math.max(0, (this.explosionDiseaseTimer || 0) - dt);
|
2026-06-23 18:11:42 +09:00
|
|
|
if (this.addStress) this.addStress(dt * 0.22, { threshold: 9, duration: 3.6 });
|
2026-06-27 19:22:38 +09:00
|
|
|
if (deterministicChance(this.world, "explosion-disease-spark", dt * 2.1, this)) {
|
|
|
|
|
const a = deterministicAngle(this.world, "explosion-disease-spark-angle", this);
|
|
|
|
|
this.world?.effects?.push(new Effect("explosion", this.x + Math.cos(a) * this.radius * deterministicRange(this.world, "explosion-disease-spark-rx", 0.2, 0.9, this), this.y + Math.sin(a) * this.radius * deterministicRange(this.world, "explosion-disease-spark-ry", 0.1, 0.8, this), {
|
|
|
|
|
vx: Math.cos(a) * deterministicRange(this.world, "explosion-disease-spark-vx", 10, 38, this), vy: Math.sin(a) * deterministicRange(this.world, "explosion-disease-spark-vy", 10, 38, this) - deterministicRange(this.world, "explosion-disease-spark-up", 8, 22, this), size: deterministicRange(this.world, "explosion-disease-spark-size", 5, 12, this), life: deterministicRange(this.world, "explosion-disease-spark-life", 0.18, 0.42, this), color: "rgba(255,64,48,0.86)"
|
2026-06-21 14:09:35 +09:00
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
if (this.explosionDiseaseTimer <= 0.01) {
|
|
|
|
|
this.world?.explodeDiseaseTarinai?.(this);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2026-06-27 19:22:38 +09:00
|
|
|
if (this.explosionDiseaseTimer < 8 && deterministicChance(this.world, "explosion-disease-bubble", dt * 0.45, this)) this.bubble("!", 1.0, "rgba(178,78,42,0.82)");
|
2026-06-21 14:09:35 +09:00
|
|
|
}
|
|
|
|
|
const bleedAge = (this.world?.time || 0) - (this.lastBleedAt || -999);
|
|
|
|
|
if (!this.fightDisease && this.fightDiseaseCooldown <= 0 && bleedAge >= (CONFIG.dayLength || 120) * 5 / 24 && bleedAge < (CONFIG.dayLength || 120) * 0.85) {
|
2026-06-27 19:22:38 +09:00
|
|
|
if (deterministicChance(this.world, "fight-disease-from-bleed", this.diseaseChance ? this.diseaseChance(dt * 0.000375) : dt * 0.000375, this)) this.infectFightDisease();
|
2026-06-21 14:09:35 +09:00
|
|
|
}
|
2026-06-27 19:22:38 +09:00
|
|
|
if (this.zunchiDisease && deterministicChance(this.world, "zunchi-natural-recover", dt * 0.00055 * (this.energy > 66 ? 1.8 : 1.0), this)) {
|
2026-06-21 14:09:35 +09:00
|
|
|
if (this.recoverZunchiDisease("\u81ea\u7136\u306b\u305a\u3093\u3061\u75c5\u304c\u6cbb\u307e\u3063\u305f")) this.world?.log?.(`${this.name}\u306e\u305a\u3093\u3061\u75c5\u304c\u81ea\u7136\u306b\u6cbb\u3063\u305f\u3002`, "accident", { participants: [this] });
|
|
|
|
|
}
|
2026-06-27 19:22:38 +09:00
|
|
|
if (this.fightDisease && deterministicChance(this.world, "fight-disease-natural-recover", dt * 0.00070 * (this.energy > 58 ? 1.4 : 1.0), this)) {
|
2026-06-21 14:09:35 +09:00
|
|
|
this.recoverFightDisease("\u81ea\u7136\u306b\u304d\u305a\u3064\u304d\u75c5\u304c\u6cbb\u3063\u305f");
|
|
|
|
|
}
|
|
|
|
|
if (this.fightDisease) {
|
2026-06-23 18:11:42 +09:00
|
|
|
if (typeof applyNeedShock === "function") applyNeedShock(this, { health: dt * 3, safety: dt * 2 });
|
2026-06-27 19:22:38 +09:00
|
|
|
if (deterministicChance(this.world, "fight-disease-wander", dt * 0.30, this)) this.wanderAngle += deterministicRange(this.world, "fight-disease-wander-angle", -2.8, 2.8, this);
|
|
|
|
|
if (deterministicChance(this.world, "fight-disease-bubble", dt * 0.08, this)) this.bubble("?", 2.6, "rgba(150,78,44,0.80)");
|
2026-06-21 14:09:35 +09:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}));
|
|
|
|
|
})(typeof window !== "undefined" ? window : globalThis);
|