2026-06-22 02:03:19 +09:00
|
|
|
"use strict";
|
|
|
|
|
|
|
|
|
|
(function (global) {
|
|
|
|
|
const Tarinai = global.Tarinai;
|
|
|
|
|
if (!Tarinai) throw new Error("Tarinai is not available for mixin: tarinai_action_state.js");
|
|
|
|
|
|
|
|
|
|
Object.defineProperties(Tarinai.prototype, Object.getOwnPropertyDescriptors({
|
|
|
|
|
setActionState(state, opts = {}) {
|
|
|
|
|
if (this.dead) return false;
|
2026-06-23 22:39:20 +09:00
|
|
|
const nextState = state || "idle";
|
|
|
|
|
this.state = nextState;
|
2026-06-22 02:03:19 +09:00
|
|
|
if ("target" in opts) this.target = opts.target;
|
|
|
|
|
if (opts.reason != null) this.thought = String(opts.reason || "");
|
|
|
|
|
if (opts.clearTarget) this.target = null;
|
|
|
|
|
if (opts.sleeping != null) this.sleeping = Boolean(opts.sleeping);
|
|
|
|
|
if (opts.wake) {
|
|
|
|
|
this.sleeping = false;
|
2026-06-23 22:39:20 +09:00
|
|
|
this.sleepSession = null;
|
2026-06-22 02:03:19 +09:00
|
|
|
if (this.insideNestBoxId && this.leaveNestBox) this.leaveNestBox(this.world?.nestBoxById?.(this.insideNestBoxId));
|
|
|
|
|
this.insideNestBoxId = "";
|
|
|
|
|
}
|
2026-06-23 22:39:20 +09:00
|
|
|
if (nextState !== "sleep" && opts.sleeping === false) this.sleepSession = null;
|
2026-06-22 02:03:19 +09:00
|
|
|
return true;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
beginEating(item = null, reason = "") {
|
2026-06-23 22:39:20 +09:00
|
|
|
const text = reason || (item?.type && typeof toolLabel === "function" ? `${toolLabel(item.type)}を食べている` : "食べている");
|
|
|
|
|
const ok = this.setActionState("eat", {
|
2026-06-22 02:03:19 +09:00
|
|
|
target: item || null,
|
2026-06-23 22:39:20 +09:00
|
|
|
reason: text,
|
2026-06-22 02:03:19 +09:00
|
|
|
sleeping: false,
|
|
|
|
|
});
|
2026-06-23 22:39:20 +09:00
|
|
|
if (typeof setLiveActionText === "function") setLiveActionText(this, { need: "food", actionId: "eat_food", actionLabel: "食べている", reasonText: text, target: item || null, phase: "perform", source: "behavior" });
|
|
|
|
|
return ok;
|
2026-06-22 02:03:19 +09:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
currentLodgedPin() {
|
|
|
|
|
return typeof lodgedPinFor === "function" ? lodgedPinFor(this, this.world) : null;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
currentLodgedPinBehavior() {
|
|
|
|
|
const pin = this.currentLodgedPin ? this.currentLodgedPin() : null;
|
|
|
|
|
return typeof pinBehaviorFor === "function" ? pinBehaviorFor(pin?.type) : null;
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
hasLodgedPinEffect(effectKey = "") {
|
|
|
|
|
return Boolean(this.currentLodgedPinBehavior?.()?.[effectKey]);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
goIdle(reason = "") {
|
2026-06-23 22:39:20 +09:00
|
|
|
if (typeof clearActiveBehavior === "function") clearActiveBehavior(this, reason || this.thought || "待っている");
|
2026-06-22 02:03:19 +09:00
|
|
|
return this.setActionState("idle", { target: null, reason: reason || this.thought || "待っている", sleeping: false });
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
startSleeping(target = null, reason = "") {
|
2026-06-23 22:39:20 +09:00
|
|
|
const now = this.world?.time || 0;
|
|
|
|
|
if (!this.sleepSession) {
|
|
|
|
|
const inFurniture = Boolean(target);
|
|
|
|
|
this.sleepSession = {
|
|
|
|
|
startedAt: now,
|
|
|
|
|
minDuration: inFurniture ? rand(16, 28) : rand(10, 18),
|
|
|
|
|
targetEnergy: inFurniture ? rand(84, 94) : rand(74, 86),
|
|
|
|
|
maxDuration: inFurniture ? rand(42, 68) : rand(28, 48),
|
|
|
|
|
targetId: target?.id || null,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
this.intent = this.intent || { need: "sleep", tiedNeeds: ["sleep"], actionId: target ? "sleep_in_bed" : "sleep_anywhere", actionLabel: reason || "眠っている", reasonText: "ねむいので、眠っている。", startedAt: now };
|
|
|
|
|
this.currentAction = this.currentAction || { id: this.intent.actionId || "sleep", need: "sleep", startedAt: this.sleepSession.startedAt, minDuration: this.sleepSession.minDuration, lockSeconds: this.sleepSession.minDuration };
|
|
|
|
|
const text = reason || (target?.type === "nest_box" ? "巣箱の中で眠っている" : "眠っている");
|
|
|
|
|
if (typeof setLiveActionText === "function") setLiveActionText(this, { need: "sleep", actionId: target ? "sleep_in_bed" : "sleep_anywhere", actionLabel: "眠っている", reasonText: text, target, phase: "perform", source: "behavior" });
|
|
|
|
|
return this.setActionState("sleep", { target, reason: text, sleeping: true });
|
2026-06-22 02:03:19 +09:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
seekTarget(state, target = null, reason = "") {
|
|
|
|
|
return this.setActionState(state, { target, reason, sleeping: false });
|
|
|
|
|
},
|
|
|
|
|
|
2026-06-23 22:39:20 +09:00
|
|
|
forceBehavior(actionId = "", opts = {}) {
|
|
|
|
|
if (typeof queueForcedTarinaiBehavior !== "function") return false;
|
|
|
|
|
return queueForcedTarinaiBehavior(this, actionId, opts);
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
currentBehaviorText() {
|
|
|
|
|
if (typeof activeBehaviorText === "function") return activeBehaviorText(this);
|
|
|
|
|
return String(this.activeBehavior?.presentText || this.activeBehavior?.reasonText || this.thought || "").trim();
|
|
|
|
|
},
|
|
|
|
|
|
2026-06-22 02:03:19 +09:00
|
|
|
enterPanic(opts = {}) {
|
|
|
|
|
if (this.dead) return false;
|
|
|
|
|
const profile = this.personalityProfile ? this.personalityProfile() : { fear: 1 };
|
|
|
|
|
const fearMul = opts.fearMultiplier ?? profile.fear ?? 1;
|
|
|
|
|
const threat = opts.threat ?? opts.target ?? null;
|
2026-06-23 18:11:42 +09:00
|
|
|
const safetyShock = Math.max(40, (opts.fearTimer ?? opts.fear ?? 0.55) * 32 * fearMul, Number(opts.stress || 0) * 1.8);
|
|
|
|
|
const healthShock = Math.max(0, Number(opts.hurtTimer || 0) > 0 ? 45 : 0);
|
|
|
|
|
if (typeof applyNeedShock === "function") {
|
|
|
|
|
applyNeedShock(this, { safety: safetyShock, health: healthShock }, threat);
|
|
|
|
|
} else {
|
|
|
|
|
this.needShock = this.needShock || {};
|
|
|
|
|
this.needShock.safety = Math.max(this.needShock.safety || 0, safetyShock);
|
|
|
|
|
if (healthShock > 0) this.needShock.health = Math.max(this.needShock.health || 0, healthShock);
|
|
|
|
|
}
|
|
|
|
|
let handled = false;
|
|
|
|
|
if (typeof triggerNeedShockReaction === "function") handled = triggerNeedShockReaction(this, threat, opts.reason || "パニックになっている");
|
|
|
|
|
if (!handled) {
|
|
|
|
|
let target = null;
|
|
|
|
|
if ("target" in opts) target = opts.target;
|
|
|
|
|
else if (opts.destination) target = opts.destination;
|
|
|
|
|
else if (this.panicDestination) target = this.panicDestination(threat, !!opts.forceDestination);
|
|
|
|
|
this.setActionState("panic", {
|
|
|
|
|
target,
|
|
|
|
|
reason: opts.reason || this.thought || "パニックになっている",
|
|
|
|
|
wake: opts.wake !== false,
|
|
|
|
|
sleeping: false,
|
|
|
|
|
});
|
|
|
|
|
this.fearTimer = Math.max(this.fearTimer || 0, 2.4);
|
|
|
|
|
}
|
2026-06-22 02:03:19 +09:00
|
|
|
if (Number.isFinite(opts.surpriseTimer)) this.surpriseTimer = Math.max(this.surpriseTimer || 0, opts.surpriseTimer);
|
|
|
|
|
if (Number.isFinite(opts.hurtTimer)) this.hurtTimer = Math.max(this.hurtTimer || 0, opts.hurtTimer);
|
|
|
|
|
if (Number.isFinite(opts.awakeLockTimer)) this.awakeLockTimer = Math.max(this.awakeLockTimer || 0, opts.awakeLockTimer);
|
|
|
|
|
if (Number.isFinite(opts.fallTimer)) {
|
|
|
|
|
this.fallTimer = Math.max(this.fallTimer || 0, opts.fallTimer);
|
|
|
|
|
this.fallMax = Math.max(this.fallMax || 0, this.fallTimer);
|
|
|
|
|
}
|
|
|
|
|
if (opts.bubble) this.bubble?.(opts.bubble, opts.bubbleCooldown ?? 0.9, opts.bubbleColor || "rgba(84,66,86,0.80)");
|
|
|
|
|
this.lastPanicCause = opts.cause || opts.reason || this.thought || "panic";
|
|
|
|
|
this.lastPanicAt = this.world?.time || 0;
|
|
|
|
|
return true;
|
|
|
|
|
},
|
|
|
|
|
}));
|
|
|
|
|
})(typeof window !== "undefined" ? window : globalThis);
|