718 lines
43 KiB
JavaScript
718 lines
43 KiB
JavaScript
"use strict";
|
|
|
|
|
|
(function (global) {
|
|
const Tarinai = global.Tarinai;
|
|
if (!Tarinai) throw new Error("Tarinai is not available for mixin: tarinai_disease_nest.js");
|
|
function isNestContainerItem(item) {
|
|
return Boolean(global.TarinaiToolRuntime?.isNestContainerItem?.(item));
|
|
}
|
|
function containerLabel(item) { return item?.type === "pipe" ? "\u571f\u7ba1" : "\u5de3\u7bb1"; }
|
|
function containerInsideSleepText(item, verb = "\u7720\u3063\u3066\u3044\u308b") { return `${containerLabel(item)}\u306e\u4e2d\u3067${verb}`; }
|
|
function containerFullText(item) { return `${containerLabel(item)}\u304c\u3044\u3063\u3071\u3044\u3067\u5165\u308c\u306a\u3044`; }
|
|
function targetShelterItem(target = null) {
|
|
return isNestContainerItem(target?.shelterItem) ? target.shelterItem : (isNestContainerItem(target?.hostItem) ? target.hostItem : null);
|
|
}
|
|
function targetReferencesNestBox(target = null, box = null) {
|
|
if (!box || !isNestContainerItem(box)) return false;
|
|
return target === box || targetShelterItem(target) === box;
|
|
}
|
|
function zunchiDiseaseTemperatureInfectionMultiplier(actor = null) {
|
|
const worldRef = actor?.world || global.world || null;
|
|
if (!actor || !worldRef) return 1;
|
|
const felt = worldRef.feltTemperatureFor?.(actor) ?? worldRef.temperatureAt?.(actor.x, actor.y, actor) ?? actor.feltTemperature ?? actor.tempComfort ?? (CONFIG.standardTemperature ?? 15);
|
|
const status = worldRef.temperatureStatusFor?.(felt, actor) || null;
|
|
if (!status || status.comfortable) return 1;
|
|
const discomfort = Math.max(0, Number(status.discomfort || 0) || 0);
|
|
if (status.direction === "hot") return clamp(1 + discomfort * 0.070, 1, 2.85);
|
|
if (status.direction === "cold") return clamp(1 - discomfort * 0.030, 0.42, 1);
|
|
return 1;
|
|
}
|
|
if (typeof global !== "undefined") global.zunchiDiseaseTemperatureInfectionMultiplier = zunchiDiseaseTemperatureInfectionMultiplier;
|
|
function temperatureShelterStillUseful(t, box = null) {
|
|
if (!t || !box || !t.world) return false;
|
|
const now = Number(t.world.time || 0) || 0;
|
|
if (now < Number(t._nestBoxShelterUntil || 0)) return true;
|
|
const status = t.world.temperatureStatusFor?.(t.world.feltTemperatureFor?.(t) ?? t.tempComfort ?? t.feltTemperature ?? (CONFIG.standardTemperature ?? 15), t);
|
|
return Boolean(status && !status.comfortable && (status.discomfort || 0) >= 1.2 && targetReferencesNestBox(t.target, box));
|
|
}
|
|
|
|
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)");
|
|
this.recordChangeCause?.("\u75c5\u6c17\u767a\u75c7", "\u305a\u3093\u3061\u75c5");
|
|
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);
|
|
if (typeof applyNeedRelief === "function") applyNeedRelief(this, { health: -18, safety: -6 });
|
|
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)");
|
|
this.recordChangeCause?.("\u75c5\u6c17\u56de\u5fa9", "\u305a\u3093\u3061\u75c5");
|
|
return true;
|
|
},
|
|
|
|
emitZunchiDiseaseEffect(dt) {
|
|
if (!this.zunchiDisease || !this.world?.effects) return;
|
|
if (deterministicChance(this.world, "zunchi-disease-miasma", dt * (0.48 + (this.zunchiDiseaseSeverity || 0) * 0.62), this)) {
|
|
this.world.spawnEffect("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.6, 2.8, this),
|
|
size: deterministicRange(this.world, "zunchi-miasma-size", 12, 26, this) * (1.0 + (this.zunchiDiseaseSeverity || 0) * 0.55),
|
|
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);
|
|
const shelteredFromRain = globalThis.TarinaiRainShelterSystem?.isSheltered?.(this.world, this.x, this.y, this) || false;
|
|
if (this.world?.weather === "light_rain" && !shelteredFromRain) {
|
|
this.zunchiStain = clamp(this.zunchiStain - dt * 2.4, 0, 100);
|
|
if (this.zunchiDisease) this.zunchiDiseaseSeverity = Math.max(0, (this.zunchiDiseaseSeverity || 1) - dt * 0.006);
|
|
}
|
|
const zunchiTempInfectMul = zunchiDiseaseTemperatureInfectionMultiplier(this);
|
|
const zunchiSelfInfectBase = dt * clamp((this.zunchiStain - 82) / 18, 0, 1) * 0.045 * zunchiTempInfectMul;
|
|
const zunchiSelfInfectChance = this.diseaseChance ? this.diseaseChance(zunchiSelfInfectBase) : zunchiSelfInfectBase;
|
|
if (!this.zunchiDisease && this.zunchiStain > 82 && deterministicChance(this.world, "zunchi-self-infect", zunchiSelfInfectChance, this)) {
|
|
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);
|
|
if (typeof applyNeedShock === "function") applyNeedShock(this, { health: dt * (5 + 4 * this.zunchiDiseaseSeverity) });
|
|
this.energy = clamp(this.energy - dt * 0.035 * this.zunchiDiseaseSeverity, 0, typeof tarinaiMaxEnergy === "function" ? tarinaiMaxEnergy(this) : (Number(this.maxEnergy) || 100));
|
|
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;
|
|
this.startSleeping(null, "\u306d\u3080\u308a\u75c5\u3067\u7720\u308a\u7d9a\u3051\u3066\u3044\u308b");
|
|
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] });
|
|
this.recordChangeCause?.("\u75c5\u6c17\u767a\u75c7", "\u306d\u3080\u308a\u75c5");
|
|
return true;
|
|
},
|
|
|
|
recoverSleepDisease(reason = "") {
|
|
if (!this.sleepDisease) return false;
|
|
this.sleepDisease = false;
|
|
this.sleepDiseaseCooldown = 14;
|
|
this.sleepDiseaseAnchorX = null;
|
|
this.sleepDiseaseAnchorY = null;
|
|
this.goIdle(reason || "\u306d\u3080\u308a\u75c5\u304c\u6cbb\u3063\u305f");
|
|
this.sleeping = false;
|
|
this.awakeLockTimer = Math.max(this.awakeLockTimer || 0, 10);
|
|
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] });
|
|
this.recordChangeCause?.("\u75c5\u6c17\u56de\u5fa9", "\u306d\u3080\u308a\u75c5");
|
|
return true;
|
|
},
|
|
|
|
infectExplosionDisease(source = null) {
|
|
if (this.dead || this.explosionDisease) return false;
|
|
this.explosionDisease = true;
|
|
this.explosionDiseaseTimer = Math.max(this.explosionDiseaseTimer || 0, 60);
|
|
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] });
|
|
this.recordChangeCause?.("\u75c5\u6c17\u767a\u75c7", "\u7206\u767a\u75c5");
|
|
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?.();
|
|
if (typeof applyNeedRelief === "function") applyNeedRelief(this, { health: -22, safety: -8 });
|
|
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] });
|
|
this.recordChangeCause?.("\u75c5\u6c17\u56de\u5fa9", "\u7206\u767a\u75c5");
|
|
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] });
|
|
this.recordChangeCause?.("\u75c5\u6c17\u767a\u75c7", "\u304d\u305a\u3064\u304d\u75c5");
|
|
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?.();
|
|
if (typeof applyNeedRelief === "function") applyNeedRelief(this, { health: -24, safety: -10 });
|
|
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] });
|
|
this.recordChangeCause?.("\u75c5\u6c17\u56de\u5fa9", "\u304d\u305a\u3064\u304d\u75c5");
|
|
return true;
|
|
},
|
|
|
|
freezeSleepDiseaseMotion(dt = 0) {
|
|
if (!this.sleepDisease) return false;
|
|
const beingDraggedByAnt = Boolean((this.antDraggedTimer || 0) > 0 && this.antDraggedBy);
|
|
const heldByPlayer = Boolean(this.playerHeld || this._heldByPlayer);
|
|
if (!Number.isFinite(this.sleepDiseaseAnchorX)) this.sleepDiseaseAnchorX = this.x;
|
|
if (!Number.isFinite(this.sleepDiseaseAnchorY)) this.sleepDiseaseAnchorY = this.y;
|
|
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");
|
|
// \u306d\u3080\u308a\u75c5\u306f\u610f\u8b58\u3060\u3051\u3092\u56fa\u5b9a\u3057\u3001\u7269\u7406\u4f4d\u7f6e\u306f\u56fa\u5b9a\u3057\u306a\u3044\u3002
|
|
// \u4ee5\u524d\u306f anchor \u306b\u6bce\u56de\u30b9\u30ca\u30c3\u30d7\u3057\u3066\u3044\u305f\u305f\u3081\u3001\u5916\u529b\u30fb\u3064\u307e\u307f\u30fb\u6a5f\u68b0\u63a5\u89e6\u3092\u7121\u8996\u3057\u3066\u3044\u305f\u3002
|
|
if (heldByPlayer || beingDraggedByAnt || Math.hypot(Number(this.vx || 0) + Number(this.impulseVx || 0), Number(this.vy || 0) + Number(this.impulseVy || 0)) > 0.1) {
|
|
this.sleepDiseaseAnchorX = this.x;
|
|
this.sleepDiseaseAnchorY = this.y;
|
|
}
|
|
this.wanderAngle = this.wanderAngle || 0;
|
|
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";
|
|
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 && isNestContainerItem(it)) || 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 = containerFullText(box)) {
|
|
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 = "";
|
|
const finalReason = reason || containerFullText(box);
|
|
this.goIdle?.(finalReason);
|
|
this.thought = finalReason;
|
|
this.pinchThoughtTimer = Math.max(this.pinchThoughtTimer || 0, 1.0);
|
|
return false;
|
|
},
|
|
|
|
wantsNestBox(box = null) {
|
|
if (!isNestContainerItem(box)) return false;
|
|
const now = this.world?.time || 0;
|
|
if (this._nestBoxAvoidId === box.id && now < (this._nestBoxAvoidUntil || -Infinity)) return false;
|
|
const sleepIntent = this.state === "sleep" || this.state === "seek_bed" || this.sleeping;
|
|
const temperatureIntent = this.state === "seek_temperature" || targetReferencesNestBox(this.target, box) || temperatureShelterStillUseful(this, box);
|
|
if (this.insideNestBoxId === box.id && now < (this.nestBoxStayUntil || -Infinity) && (sleepIntent || temperatureIntent)) return true;
|
|
if (this.insideNestBoxId === box.id && temperatureShelterStillUseful(this, box)) return true;
|
|
if (!(sleepIntent || temperatureIntent)) return false;
|
|
return targetReferencesNestBox(this.target, box) || this.insideNestBoxId === box.id;
|
|
},
|
|
|
|
enterNestBox(box, dt = 0.016, options = {}) {
|
|
if (!isNestContainerItem(box) || this.dead) return false;
|
|
const alreadyInside = this.insideNestBoxId === box.id;
|
|
if (!alreadyInside && box?.type === "nest_box" && globalThis.TarinaiItemDynamicToolSystem?.nestBoxFireActive?.(box)) {
|
|
return this.abortNestBoxEntry?.(box, "\u5de3\u7bb1\u304c\u71c3\u3048\u3066\u3044\u3066\u5165\u308c\u306a\u3044") || false;
|
|
}
|
|
const capacity = this.world?.nestBoxCapacity ? this.world.nestBoxCapacity(box) : 5;
|
|
let occupants = this.world?.nestBoxOccupants ? this.world.nestBoxOccupants(box, Infinity) : [];
|
|
const now = this.world?.time || 0;
|
|
if (!alreadyInside && occupants.length >= capacity) {
|
|
const victim = globalThis.TarinaiNestSleepSystem?.lowerPriorityNestOccupant?.(this.world, box, this, occupants) || null;
|
|
if (victim) {
|
|
victim.leaveNestBox?.(box);
|
|
victim.awakeLockTimer = Math.max(victim.awakeLockTimer || 0, 0.8);
|
|
victim.fearTimer = Math.max(victim.fearTimer || 0, 0.22);
|
|
victim.thought = `${containerLabel(box)}\u304b\u3089\u62bc\u3057\u51fa\u3055\u308c\u305f`;
|
|
victim.pinchThoughtTimer = Math.max(victim.pinchThoughtTimer || 0, 0.65);
|
|
victim.goIdle?.(`${containerLabel(box)}\u304b\u3089\u62bc\u3057\u51fa\u3055\u308c\u305f`);
|
|
this.world?.log?.(`${this.name}\u304c${containerLabel(box)}\u306b\u5165\u308a\u3001${victim.name}\u3092\u62bc\u3057\u51fa\u3057\u305f\u3002`, "event", { participants: [this, victim] });
|
|
occupants = this.world?.nestBoxOccupants ? this.world.nestBoxOccupants(box, Infinity) : occupants.filter(t => t !== victim);
|
|
}
|
|
if (occupants.length >= capacity) return this.abortNestBoxEntry?.(box, containerFullText(box)) || false;
|
|
}
|
|
const shelterPurpose = options?.purpose === "temperature" || this.state === "seek_temperature" || targetShelterItem(this.target) === box;
|
|
if (shelterPurpose) {
|
|
this._nestBoxShelterUntil = Math.max(Number(this._nestBoxShelterUntil || 0) || 0, now + 12);
|
|
}
|
|
if (!alreadyInside) {
|
|
this._nestBoxSeekStartedAt = NaN;
|
|
this._nestBoxSeekTargetId = "";
|
|
const entry = this.world?.nestBoxEntryPoint ? this.world.nestBoxEntryPoint(box) : { x: box.x, y: box.y };
|
|
this.nestBoxStayUntil = Math.max(this.nestBoxStayUntil || -Infinity, now + (shelterPurpose ? 12 : 14));
|
|
this.nestBoxSlotIndex = Math.min(occupants.length, Math.max(0, capacity - 1));
|
|
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;
|
|
if (this.state === "seek_bed") this.startSleeping(box, containerInsideSleepText(box, "\u7720\u3063\u3066\u3044\u308b"));
|
|
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 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.vx = 0;
|
|
this.vy = 0;
|
|
this.impulseVx = 0;
|
|
this.impulseVy = 0;
|
|
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;
|
|
return false;
|
|
}
|
|
const exitContainer = b.type === "pipe" && this.world?.randomPipeExitFor ? (this.world.randomPipeExitFor(b, this) || b) : b;
|
|
const inner = this.world?.nestBoxInnerPoint ? this.world.nestBoxInnerPoint(exitContainer, this) : { x: exitContainer.x, y: exitContainer.y };
|
|
const door = this.world.nestBoxEntryPoint(exitContainer);
|
|
const exit = this.world.nestBoxExitPoint ? this.world.nestBoxExitPoint(exitContainer, 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) > (exitContainer.r || b.r || 42) * 0.7) {
|
|
this._nestBoxExitStartX = inner.x;
|
|
this._nestBoxExitStartY = inner.y;
|
|
}
|
|
this._nestBoxExitDoorX = Number.isFinite(door.x) ? door.x : exitContainer.x;
|
|
this._nestBoxExitDoorY = Number.isFinite(door.y) ? door.y : exitContainer.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;
|
|
if (b?.type === "nest_box" && globalThis.TarinaiItemDynamicToolSystem?.nestBoxFireActive?.(b)) {
|
|
this._burningNestExitBoxId = b.id || "";
|
|
this._burningNestExitUntil = Math.max(Number(this._burningNestExitUntil || 0) || 0, now + this._nestBoxExitDuration + 1.2);
|
|
}
|
|
this.nestFade = Math.max(this.nestFade || 0, 0.96);
|
|
return true;
|
|
},
|
|
|
|
forceIgniteFromBurningNestExit(box = null) {
|
|
const now = Number(this.world?.time || 0) || 0;
|
|
const b = box || this.nestBoxById?.(this._burningNestExitBoxId);
|
|
const active = Boolean(b?.type === "nest_box" && globalThis.TarinaiItemDynamicToolSystem?.nestBoxFireActive?.(b));
|
|
const taggedExit = Boolean(this._burningNestExitBoxId && now < Number(this._burningNestExitUntil || 0));
|
|
if (!active && !taggedExit) return false;
|
|
if (!this.burning || !(this.burnTimer > 0.02)) {
|
|
if (typeof this.igniteFire === "function") this.igniteFire(b || box || null, true);
|
|
else {
|
|
const text = globalThis.TarinaiItemDynamicToolSystem?.fireBurningText || "\u71c3\u3048\u3066\u9003\u3052\u307e\u3069\u3063\u3066\u3044\u308b\u3002";
|
|
this.burning = true;
|
|
this.burnTimer = Math.max(Number(this.burnTimer || 0) || 0, 5.2);
|
|
this.thought = text;
|
|
this.sleeping = false;
|
|
this.setActionState?.("panic", { target: null, reason: text, wake: true, sleeping: false });
|
|
}
|
|
}
|
|
this._burningNestExitUntil = Math.max(Number(this._burningNestExitUntil || 0) || 0, now + 0.75);
|
|
return true;
|
|
},
|
|
|
|
updateNestBoxExitAnimation(dt = 0.016) {
|
|
const now = this.world?.time || 0;
|
|
const until = this._nestBoxExitSmoothUntil || -Infinity;
|
|
if (!(now < until)) return false;
|
|
if (this._burningNestExitBoxId && now < Number(this._burningNestExitUntil || 0)) this.forceIgniteFromBurningNestExit?.(this.nestBoxById?.(this._burningNestExitBoxId));
|
|
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._nestBoxExitStartX = this._nestBoxExitStartY = this._nestBoxExitDoorX = this._nestBoxExitDoorY = this._nestBoxExitEndX = this._nestBoxExitEndY = NaN;
|
|
}
|
|
return true;
|
|
},
|
|
|
|
leaveNestBox(box = null) {
|
|
const b = box || this.nestBoxById(this.insideNestBoxId);
|
|
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.insideNestBoxId = null;
|
|
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?.(`${containerLabel(b)}\u304b\u3089\u51fa\u305f`);
|
|
this.nestBoxStayUntil = -Infinity;
|
|
this._nestBoxShelterUntil = 0;
|
|
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 = "";
|
|
if (b?.type === "nest_box" && globalThis.TarinaiItemDynamicToolSystem?.nestBoxFireActive?.(b)) {
|
|
const now = Number(this.world?.time || 0) || 0;
|
|
this._burningNestExitBoxId = b.id || this._burningNestExitBoxId || "";
|
|
this._burningNestExitUntil = Math.max(Number(this._burningNestExitUntil || 0) || 0, now + 1.4);
|
|
this.forceIgniteFromBurningNestExit?.(b);
|
|
}
|
|
return true;
|
|
},
|
|
|
|
updateNestBoxPresence(dt = 0) {
|
|
if (this.updateNestBoxExitAnimation?.(dt || 0.016)) return null;
|
|
const currentBox = this.nestBoxById(this.insideNestBoxId);
|
|
const shelterBox = targetShelterItem(this.target);
|
|
const targetBox = isNestContainerItem(this.target) ? this.target : shelterBox;
|
|
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) {
|
|
if ((this.world?.time || 0) < (this.nestBoxStayUntil || -Infinity) && (this.state === "sleep" || this.state === "seek_bed" || this.sleeping)) {
|
|
this.startSleeping(box, containerInsideSleepText(box, "\u7720\u3063\u3066\u3044\u308b"));
|
|
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);
|
|
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, containerFullText(targetBox || box)) || 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.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) {
|
|
if (this.enterNestBox(targetBox, dt || 0.016)) return targetBox;
|
|
return null;
|
|
}
|
|
this.nestFade = clamp((this.nestFade || 0) - dt * 3.0, 0, 1);
|
|
return null;
|
|
},
|
|
|
|
applyWaterEffect(dt, source = "water") {
|
|
const power = Math.max(0, dt || 0);
|
|
if (typeof applyNeedRelief === "function") applyNeedRelief(this, { health: -power * 12, safety: -power * 6 });
|
|
this.energy = clamp(this.energy + power * 0.5, 0, typeof tarinaiMaxEnergy === "function" ? tarinaiMaxEnergy(this) : (Number(this.maxEnergy) || 100));
|
|
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");
|
|
},
|
|
|
|
|
|
igniteFire(source = null, force = false) {
|
|
if (this.dead) return false;
|
|
const already = Boolean(this.burning || (this.burnTimer || 0) > 0.02);
|
|
if (already && !force) return false;
|
|
const ignitedDuringBirthRitual = !already && Boolean((this.birthRitualTimer || 0) > 0.04 || this.state === "birth_ritual");
|
|
const text = globalThis.TarinaiItemDynamicToolSystem?.fireBurningText || "\u71c3\u3048\u3066\u9003\u3052\u307e\u3069\u3063\u3066\u3044\u308b\u3002";
|
|
this.burning = true;
|
|
const dangerSource = source?.roles?.danger ? source : (source?._achievementDangerSource?.roles?.danger ? source._achievementDangerSource : null);
|
|
if (dangerSource) this._achievementDangerSource = dangerSource;
|
|
this.burnTimer = Math.max(Number(this.burnTimer || 0) || 0, 5.2 + deterministicRange(this.world, "tarinai-burn-duration", 0, 2.8, this, source || null));
|
|
this.firePanicTimer = 0;
|
|
this.lastPanicCause = "burning";
|
|
this.lastPanicDetail = text;
|
|
this.thought = text;
|
|
this.sleeping = false;
|
|
this.awakeLockTimer = Math.max(this.awakeLockTimer || 0, 0.8);
|
|
this.fearTimer = Math.max(this.fearTimer || 0, 2.2);
|
|
this.surpriseTimer = Math.max(this.surpriseTimer || 0, 0.65);
|
|
this.eatTimer = 0;
|
|
this.target = null;
|
|
this.burnWanderAngle = Number.isFinite(this.burnWanderAngle) ? this.burnWanderAngle : deterministicAngle(this.world, "tarinai-burn-angle", this, source || null);
|
|
if (typeof this.enterPanic === "function") this.enterPanic({ threat: source, target: null, reason: text, fear: 2.2, wake: true, cause: "burning", surpriseTimer: 0.65, awakeLockTimer: 0.8 });
|
|
else this.setActionState?.("panic", { target: null, reason: text, wake: true });
|
|
this.world?.queueEffect?.("ring", this.x, this.y, { size: Math.max(26, this.radius * 1.35), life: 0.26, color: "rgba(255,110,36,0.58)" });
|
|
const now = Number(this.world?.time || 0);
|
|
if (force || !already || now >= Number(this.nextBurnVoiceAt || -999)) {
|
|
this.nextBurnVoiceAt = now + 2.6;
|
|
this.bubble?.("\u3042\u3061\u3085\u3044\uff01", 1.35, "rgba(214,82,38,0.90)", { force: true });
|
|
}
|
|
this.recordChangeCause?.("\u708e\u4e0a", "\u708e\u4e0a");
|
|
if (!already) this.world?.log?.(`${this.name}\u306b\u706b\u304c\u71c3\u3048\u79fb\u3063\u305f\u3002`, "accident", { participants: [this] });
|
|
if (ignitedDuringBirthRitual) globalThis.TarinaiAchievements?.recordIgnition?.({ world: this.world, tarinai: this, source });
|
|
return true;
|
|
},
|
|
|
|
extinguishFire(reason = "") {
|
|
if (!this.burning && !(this.burnTimer > 0)) return false;
|
|
this.burning = false;
|
|
this.burnTimer = 0;
|
|
this._achievementDangerSource = null;
|
|
this.firePanicTimer = Math.max(this.firePanicTimer || 0, 0.25);
|
|
this.hurtTimer = Math.max(this.hurtTimer || 0, 0.35);
|
|
if (reason) this.thought = reason;
|
|
this.world?.queueEffect?.("ring", this.x, this.y, { size: Math.max(22, this.radius * 1.2), life: 0.22, color: "rgba(108,192,236,0.60)" });
|
|
this.world?.log?.(`${this.name}\u306e\u706b\u304c\u6d88\u3048\u305f\u3002`, "accident", { participants: [this] });
|
|
return true;
|
|
},
|
|
|
|
updateBurning(dt) {
|
|
this.firePanicTimer = Math.max(0, Number(this.firePanicTimer || 0) - dt);
|
|
if (!this.burning && !(this.burnTimer > 0)) return;
|
|
const text = globalThis.TarinaiItemDynamicToolSystem?.fireBurningText || "\u71c3\u3048\u3066\u9003\u3052\u307e\u3069\u3063\u3066\u3044\u308b\u3002";
|
|
this.burning = true;
|
|
this.burnTimer = Math.max(0, Number(this.burnTimer || 0) - dt);
|
|
this.lastPanicCause = "burning";
|
|
this.lastPanicDetail = text;
|
|
this.thought = text;
|
|
this.fearTimer = Math.max(this.fearTimer || 0, 0.8);
|
|
this.sleeping = false;
|
|
this.eatTimer = 0;
|
|
const currentNest = this.nestBoxById?.(this.insideNestBoxId);
|
|
if (currentNest) {
|
|
const water = globalThis.TarinaiItemDynamicToolSystem?.contactWaterDrop?.(this.world, currentNest.x, currentNest.y, Math.max(this.radius || 18, (currentNest.r || 42) * 1.15));
|
|
if (water) {
|
|
globalThis.TarinaiItemDynamicToolSystem?.consumeWaterForFire?.(this.world, water, Math.max(10, (this.radius || 18) * 0.55));
|
|
this.extinguishFire("\u6c34\u3067\u6d88\u706b\u3055\u308c\u305f");
|
|
return;
|
|
}
|
|
const mul = globalThis.TarinaiItemDynamicToolSystem?.fireTempMultiplier?.(this.world, currentNest.x, currentNest.y, this) || 1;
|
|
if (deterministicChance(this.world, "tarinai-nest-fire-extinguish", dt * 0.014 * mul, this, currentNest)) {
|
|
this.extinguishFire("\u81ea\u7136\u306b\u706b\u304c\u6d88\u3048\u305f");
|
|
return;
|
|
}
|
|
if (this.burnTimer <= 0.01) this.extinguishFire("\u81ea\u7136\u306b\u706b\u304c\u6d88\u3048\u305f");
|
|
return;
|
|
}
|
|
if (typeof applyNeedShock === "function") applyNeedShock(this, { health: dt * 6.2, safety: dt * 3.0 });
|
|
if (typeof this.damage === "function") this.damage(dt * 3.8, "\u708e\u4e0a", { source: this._achievementDangerSource || null });
|
|
else this.energy = clamp((this.energy || 0) - dt * 0.12, 0, typeof tarinaiMaxEnergy === "function" ? tarinaiMaxEnergy(this) : (Number(this.maxEnergy) || 100));
|
|
this.hurtTimer = Math.max(this.hurtTimer || 0, 0.22);
|
|
const water = globalThis.TarinaiItemDynamicToolSystem?.contactWaterDrop?.(this.world, this.x, this.y, Math.max(this.radius || 18, 16));
|
|
if (water) {
|
|
globalThis.TarinaiItemDynamicToolSystem?.consumeWaterForFire?.(this.world, water, Math.max(10, (this.radius || 18) * 0.55));
|
|
this.extinguishFire("\u6c34\u3067\u6d88\u706b\u3055\u308c\u305f");
|
|
return;
|
|
}
|
|
const mul = globalThis.TarinaiItemDynamicToolSystem?.fireTempMultiplier?.(this.world, this.x, this.y, this) || 1;
|
|
if (deterministicChance(this.world, "tarinai-fire-extinguish", dt * 0.016 * mul, this)) {
|
|
this.extinguishFire("\u81ea\u7136\u306b\u706b\u304c\u6d88\u3048\u305f");
|
|
return;
|
|
}
|
|
for (const viewer of [...(this.world?.nearbyTarinai?.(this.x, this.y, 190, true) || [])]) {
|
|
if (!viewer || viewer === this || viewer.dead || viewer.insideNestBoxId || this.world?.isTarinaiHiddenInNestBox?.(viewer)) continue;
|
|
globalThis.TarinaiItemDynamicToolSystem?.panicTarinaiFromFire?.(viewer, this, this.world);
|
|
}
|
|
const contactRadius = Math.max(20, (this.radius || 18) * 1.1);
|
|
for (const other of [...(this.world?.nearbyTarinai?.(this.x, this.y, contactRadius + 28, true) || [])]) {
|
|
if (!other || other === this || other.dead || other.insideNestBoxId || this.world?.isTarinaiHiddenInNestBox?.(other)) continue;
|
|
if (distXY(this.x, this.y, other.x, other.y) <= contactRadius + Math.max(other.radius || 18, 12) * 0.72) other.igniteFire?.(this, false);
|
|
}
|
|
for (const ant of [...(this.world?.nearbyAnts?.(this.x, this.y, contactRadius + 18, true) || [])]) {
|
|
if (!ant || ant.dead) continue;
|
|
if (distXY(this.x, this.y, ant.x, ant.y) <= contactRadius + Math.max(ant.r || 4, 4)) globalThis.TarinaiItemDynamicToolSystem?.igniteAntByFire?.(ant, this, this.world);
|
|
}
|
|
for (const it of [...(this.world?.nearbyGrass?.(this.x, this.y, contactRadius + 32, true) || [])]) {
|
|
if (!it || it.dead) continue;
|
|
if (distXY(this.x, this.y, it.x, it.y) <= contactRadius + Math.max(it.r || 16, 12)) globalThis.TarinaiItemDynamicToolSystem?.igniteGrassByFire?.(it, this, this.world, dt);
|
|
}
|
|
if (this.burnTimer <= 0.01) this.extinguishFire("\u81ea\u7136\u306b\u706b\u304c\u6d88\u3048\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);
|
|
if (this.addStress) this.addStress(dt * 0.22, { threshold: 9, duration: 3.6 });
|
|
if (deterministicChance(this.world, "explosion-disease-spark", dt * 2.1, this)) {
|
|
const a = deterministicAngle(this.world, "explosion-disease-spark-angle", this);
|
|
this.world?.queueEffect?.("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)"
|
|
});
|
|
}
|
|
if (this.explosionDiseaseTimer <= 0.01) {
|
|
this.world?.explodeDiseaseTarinai?.(this);
|
|
return;
|
|
}
|
|
if (this.explosionDiseaseTimer < 8 && deterministicChance(this.world, "explosion-disease-bubble", dt * 0.45, this)) this.bubble("!", 1.0, "rgba(178,78,42,0.82)");
|
|
}
|
|
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) {
|
|
if (deterministicChance(this.world, "fight-disease-from-bleed", this.diseaseChance ? this.diseaseChance(dt * 0.000375) : dt * 0.000375, this)) this.infectFightDisease();
|
|
}
|
|
if (this.zunchiDisease && deterministicChance(this.world, "zunchi-natural-recover", dt * 0.00055 * (this.energy > 66 ? 1.8 : 1.0), this)) {
|
|
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] });
|
|
}
|
|
if (this.fightDisease && deterministicChance(this.world, "fight-disease-natural-recover", dt * 0.00070 * (this.energy > 58 ? 1.4 : 1.0), this)) {
|
|
this.recoverFightDisease("\u81ea\u7136\u306b\u304d\u305a\u3064\u304d\u75c5\u304c\u6cbb\u3063\u305f");
|
|
}
|
|
if (this.fightDisease) {
|
|
if (typeof applyNeedShock === "function") applyNeedShock(this, { health: dt * 3, safety: dt * 2 });
|
|
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)");
|
|
}
|
|
}
|
|
}));
|
|
})(typeof window !== "undefined" ? window : globalThis);
|