"use strict"; (function (global) { function markWorldAfterStructureChange(worldRef, reason = "structure-change", item = null, radius = 40) { if (!worldRef) return; worldRef.drawListDirty = true; worldRef.markItemBucketsDirty?.(reason); worldRef.markSpatialDirty?.(reason); if (item && Number.isFinite(item.x) && Number.isFinite(item.y)) { worldRef.markTerrainDirtyAt?.(item.x, item.y, Math.max(24, Number(radius || item.r || 40) || 40), reason); } else { worldRef.markTerrainDirty?.(reason); } } function markLooseItemGone(item, reason = "item-removed") { if (!item || item.dead) return false; // Normal Item.dead is a getter, so all non-structure removals go through // amount/hp zeroing instead of assigning to .dead. item.amount = 0; item.hp = 0; item.foodServingsRemaining = 0; if (item.type === "grass") { item.growth = 0; item.grassStage = -1; item.seedTimer = Math.max(Number(item.seedTimer || 0), 2.5); } item.removedReason = String(reason || "item-removed"); return true; } function markStructureGone(structure, reason = "structure-removed") { if (!structure || structure.dead) return false; structure.dead = true; structure.hp = 0; structure.amount = 0; structure.removedReason = String(reason || "structure-removed"); return true; } function ownedStructureDestroyedLabel(structure) { if (structure?.type === "plushie") return "ぬいぐるみ"; if (structure?.type === "grass_bed") return "かんたんベッド"; return structure?.label || "自分のもの"; } function shouldPanicOwnerOnStructureGone(structure, reason = "") { if (!structure || !structure.ownerId) return false; if (!(structure.type === "plushie" || structure.type === "grass_bed")) return false; if (String(reason || "") === "grass-bed-wake-used") return false; return true; } function triggerOwnedStructureDestroyedPanic(worldRef, structure, breaker = null, reason = "structure-destroyed") { if (!worldRef || !shouldPanicOwnerOnStructureGone(structure, reason)) return false; const owner = (worldRef.tarinai || []).find(t => t && !t.dead && t.id === structure.ownerId); if (!owner) return false; const label = ownedStructureDestroyedLabel(structure); const detail = `自分の${label}が壊された`; const panicReason = `自分の${label}を壊されてパニックになっている`; const threat = breaker || { id: `${structure.id || "structure"}:destroyed`, x: Number(structure.x) || owner.x, y: Number(structure.y) || owner.y, type: "owned_structure_destroyed", dead: false, }; owner.lastPanicDetail = detail; owner.fearTimer = Math.max(owner.fearTimer || 0, structure.type === "plushie" ? 3.2 : 2.6); owner.surpriseTimer = Math.max(owner.surpriseTimer || 0, 0.85); owner.awakeLockTimer = Math.max(owner.awakeLockTimer || 0, 2.4); if (owner.enterPanic) { owner.enterPanic({ threat, target: owner.panicDestination?.(threat, true) || null, forceDestination: true, reason: panicReason, fear: structure.type === "plushie" ? 1.65 : 1.35, stress: structure.type === "plushie" ? 20 : 15, wake: true, cause: `${structure.type}_destroyed`, surpriseTimer: 0.85, awakeLockTimer: 2.4, }); } else { owner.setActionState?.("panic", { target: owner.panicDestination?.(threat, true) || null, reason: panicReason, wake: true, sleeping: false }); } if (typeof global.setBehaviorText === "function") { global.setBehaviorText(owner, { need: "safety", actionId: "panic_escape", actionLabel: "パニックになっている", reasonText: panicReason, causeText: detail, target: threat, phase: "perform", source: "behavior", }); } owner.thought = panicReason; owner.behaviorLockTimer = Math.max(owner.behaviorLockTimer || 0, 1.2); worldRef.spawnBubble?.(owner.x, owner.y - (owner.radius || 20) * 1.20, "!?", "rgba(188,66,76,0.88)"); worldRef.log?.(`${owner.name || "たりない"}は自分の${label}を壊されてパニックになった。`, "danger"); worldRef.drawListDirty = true; return true; } function releaseStructureUsers(worldRef, structure, opts = {}) { if (!worldRef || !structure) return 0; const reason = opts.reason || "\u306a\u304f\u306a\u3063\u305f"; const wake = opts.wake !== false; let released = 0; for (const t of worldRef.tarinai || []) { if (!t || t.dead) continue; const targetMatch = t.target === structure; const sleepTargetMatch = t.sleepSession?.targetId && t.sleepSession.targetId === structure.id; const nestMatch = structure.type === "nest_box" && t.insideNestBoxId === structure.id; if (!targetMatch && !sleepTargetMatch && !nestMatch) continue; if (nestMatch) { t.insideNestBoxId = null; } if (sleepTargetMatch && t.sleepSession) { t.sleepSession.targetId = null; t.sleepSession.consumesGrassBedOnWake = false; } if (targetMatch) t.target = null; if (t.state === "sleep" || t.sleeping || t.state === "seek_bed" || nestMatch) { t.setActionState?.("idle", { clearTarget: true, reason, wake, sleeping: false }); } released += 1; } return released; } function deleteStructure(worldRef, structure, opts = {}) { if (!structure || structure.dead) return false; const reason = opts.reason || "structure-deleted"; releaseStructureUsers(worldRef, structure, { reason: opts.userReason || "\u7f6e\u304d\u3082\u306e\u304c\u306a\u304f\u306a\u3063\u305f", wake: opts.wake !== false }); const ownerPanic = shouldPanicOwnerOnStructureGone(structure, reason); const changed = markStructureGone(structure, reason); if (!changed) return false; if (ownerPanic && opts.panicOwner !== false) triggerOwnedStructureDestroyedPanic(worldRef, structure, opts.breaker || null, reason); markWorldAfterStructureChange(worldRef, reason); worldRef?.emit?.("structure:deleted", { structure, type: structure.type, reason }); return true; } function consumeGrassBedOnWake(worldRef, bed, user = null, reason = "wake") { if (!bed || bed.dead || bed.type !== "grass_bed") return false; bed.singleUsePending = false; bed.singleUseWakeReason = String(reason || "wake"); bed.singleUseConsumedById = user?.id || bed.singleUseConsumedById || null; if (user?.target === bed) user.target = null; const changed = markStructureGone(bed, "grass-bed-wake-used"); if (!changed) return false; markWorldAfterStructureChange(worldRef, "grass-bed-wake-used"); worldRef?.emit?.("structure:usedUp", { structure: bed, user, reason: "wake" }); return true; } function deleteItem(worldRef, item, opts = {}) { if (!item || item.dead) return false; const reason = opts.reason || "delete-tool"; if (item.isStructure) return deleteStructure(worldRef, item, { reason, userReason: opts.userReason, wake: opts.wake, breaker: opts.breaker || null, panicOwner: opts.panicOwner }); const changed = markLooseItemGone(item, reason); if (!changed) return false; markWorldAfterStructureChange(worldRef, reason, item, Math.max(36, Number(item.r || item.radius || 24) + 18)); worldRef?.emit?.("item:deleted", { item, type: item.type, reason }); return true; } const api = Object.freeze({ consumeGrassBedOnWake, deleteItem, triggerOwnedStructureDestroyedPanic, }); global.TarinaiStructureLifecycle = api; })(typeof window !== "undefined" ? window : globalThis);