tarinai/js/tarinai_behavior_text.js
2026-07-18 13:06:02 +09:00

230 lines
15 KiB
JavaScript

"use strict";
(function (global) {
function fulfillPrimaryReason(tarinai, action = null) {
const behavior = currentTarinaiBehavior(tarinai);
const actionId = action?.id || behavior?.actionId || "";
if (actionId === "build_grass_bed") return "\u5bdd\u5e8a\u304c\u307b\u3057\u3044";
if (actionId === "build_plushie") return "\u306c\u3044\u3050\u308b\u307f\u304c\u307b\u3057\u3044";
if (actionId === "return_owned_structure" || actionId === "hide_at_owned_structure") return "\u81ea\u5206\u306e\u5834\u6240\u304c\u6c17\u306b\u306a\u308b";
if (actionId === "use_plushie") return "\u306c\u3044\u3050\u308b\u307f\u3092\u62b1\u3048\u305f\u3044";
if (actionId === "play_seesaw") return "\u904a\u3073\u305f\u3044";
if (actionId === "play") return "\u9000\u5c48\u3057\u3066\u3044\u308b";
if (actionId === "wander_lightly") return "\u5c11\u3057\u9000\u5c48\u3057\u3066\u3044\u308b";
const parts = tarinai?.fulfillReasonParts || {};
const candidates = [
["bed", "\u5bdd\u5e8a\u304c\u307b\u3057\u3044", parts.bed],
["plushie", "\u306c\u3044\u3050\u308b\u307f\u304c\u307b\u3057\u3044", parts.plushie],
["boredom", "\u9000\u5c48\u3057\u3066\u3044\u308b", parts.boredom],
["material", "\u8349\u3067\u306a\u306b\u304b\u4f5c\u308c\u305d\u3046", parts.material],
["openness", "\u65b0\u3057\u3044\u3053\u3068\u304c\u6c17\u306b\u306a\u308b", parts.openness],
].filter(([, , v]) => Number(v || 0) > 0);
candidates.sort((a, b) => Number(b[2] || 0) - Number(a[2] || 0));
return candidates[0]?.[1] || TARINAI_NEED_PHRASES.fulfill || "\u306a\u306b\u304b\u6e80\u305f\u3055\u308c\u306a\u3044";
}
function needPhraseForReason(need, tarinai = null, action = null) {
if (need === "fulfill") return fulfillPrimaryReason(tarinai, action);
if (need === "social" && action?.subNeed === "mate") return "\u7e41\u6b96\u3067\u304d\u308b\u76f8\u624b\u304c\u6c17\u306b\u306a\u308b";
if (need === "social" && action?.id === "birth_ritual") return "\u76f8\u624b\u3068\u7e41\u6b96\u3057\u3066\u3044\u308b";
if (need === "social" && action?.subNeed === "conflict") return "\u6c17\u306b\u5165\u3089\u306a\u3044\u76f8\u624b\u304c\u3044\u308b";
if (need === "social" && action?.subNeed === "family") return "\u5bb6\u65cf\u304c\u6c17\u306b\u306a\u308b";
if (need === "social" && action?.subNeed === "bond") return "\u4ef2\u9593\u304c\u6c17\u306b\u306a\u308b";
if (need === "safety" && action?.id === "panic_escape") return "\u6016\u3044";
if (need === "social" && action?.subNeed === "crowd") return "\u4ed6\u306e\u305f\u308a\u306a\u3044\u304c\u591a\u3059\u304e\u3066\u843d\u3061\u7740\u304b\u306a\u3044";
return TARINAI_NEED_PHRASES[need] || need;
}
function panicThreatLabel(tarinai) {
const threat = tarinai?.lastPanicThreat || tarinai?.lastNeedShockBreaker || null;
if (!threat || threat === tarinai || threat.dead) return "";
const catalog = globalThis.TEXT_CATALOG || (typeof window !== "undefined" ? window.TEXT_CATALOG : null);
let label = String(threat.name || "").trim();
if (!label && catalog?.targetLabel) label = String(catalog.targetLabel(threat) || "").trim();
if (!label && threat.type && typeof toolLabel === "function") label = String(toolLabel(threat.type) || "").trim();
if (!label || label === "\u76f8\u624b" || label === "\u4f4d\u7f6e" || label === "\u4e0d\u660e") return "";
return label.replace(/[\u3002\uff01\uff1f]+$/, "");
}
function actionTextForReason(action = null, tarinai = null) {
const behavior = currentTarinaiBehavior(tarinai);
const id = action?.id || behavior?.actionId || "";
const target = tarinai?.target || currentBehaviorTarget?.(tarinai) || null;
const name = target?.name || "\u76f8\u624b";
if (id === "birth_ritual") return target?.name ? `${name}\u3068\u3078\u3053\u3078\u3053\u3057\u3066\u3044\u308b` : "\u3078\u3053\u3078\u3053\u3057\u3066\u3044\u308b";
if (id === "fight_rival") {
const cause = String(behavior?.causeText || "");
if (cause.includes("\u3051\u3093\u304b\u3092\u58f2\u3089\u308c")) return "\u53cd\u6483\u3057\u3066\u3044\u308b";
return target?.name ? `${name}\u3068\u55a7\u5629\u3057\u3066\u3044\u308b` : "\u55a7\u5629\u3057\u3066\u3044\u308b";
}
if (id === "panic_escape") return "\u30d1\u30cb\u30c3\u30af\u306b\u306a\u3063\u3066\u3044\u308b";
return String(action?.label || action?.phrase || behavior?.label || "\u307c\u30fc\u3063\u3068\u3057\u3066\u3044\u308b").replace(/[\u3002\uff01\uff1f]+$/, "");
}
function isEffectCauseText(text = "") {
return Boolean(globalThis.TarinaiCoreHelpers?.isEffectCauseText?.(text)
?? globalThis.isTarinaiEffectCauseText?.(text));
}
function forcedCausePhrase(source = "", fallback = "") {
const key = String(source || "");
if (key === "love_mochi") return "\u3078\u3053\u9905\u306e\u52b9\u679c";
if (key === "fight_mochi") return "\u3051\u3093\u304b\u9905\u306e\u52b9\u679c";
if (key === "sleep_drug") return "\u306d\u3080\u308a\u85ac\u306e\u52b9\u679c";
if (key === "drug") return "\u85ac\u306e\u52b9\u679c";
return String(fallback || "").replace(/[\u3002\uff01\uff1f]+$/, "");
}
function formatCauseActionText(cause = "", actionText = "\u884c\u52d5\u3057\u3066\u3044\u308b") {
const c = String(cause || "").replace(/[\u3002\uff01\uff1f]+$/, "").trim();
const a = String(actionText || "\u884c\u52d5\u3057\u3066\u3044\u308b").replace(/[\u3002\uff01\uff1f]+$/, "").trim();
if (!c) return `${a}\u3002`;
if (c.endsWith("\u52b9\u679c") || c.endsWith("\u5f71\u97ff")) return `${c}\u3067\u3001${a}\u3002`;
if (c.endsWith("\u6c17\u4ed8\u3044\u305f")) return `${c.replace(/\u6c17\u4ed8\u3044\u305f$/, "\u6c17\u4ed8\u3044\u3066")}\u3001${a}\u3002`;
if (c.endsWith("\u6c17\u3065\u3044\u305f")) return `${c.replace(/\u6c17\u3065\u3044\u305f$/, "\u6c17\u3065\u3044\u3066")}\u3001${a}\u3002`;
if (c.endsWith("\u3051\u3093\u304b\u3092\u58f2\u3089\u308c\u305f")) return `${c.replace(/\u3051\u3093\u304b\u3092\u58f2\u3089\u308c\u305f$/, "\u3051\u3093\u304b\u3092\u58f2\u3089\u308c")}\u3001${a}\u3002`;
if (c.endsWith("\u5a01\u5687\u3055\u308c\u305f")) return `${c.replace(/\u5a01\u5687\u3055\u308c\u305f$/, "\u5a01\u5687\u3055\u308c")}\u3001${a}\u3002`;
return `${c}\u306e\u3067\u3001${a}\u3002`;
}
function causePhraseForReason(chosenNeed, action, tarinai = null, options = {}) {
if (options?.causeText) return String(options.causeText || "").replace(/[\u3002\uff01\uff1f]+$/, "");
const b = (currentTarinaiBehavior(tarinai)) || {};
if (b.causeText) {
const cause = String(b.causeText).replace(/[\u3002\uff01\uff1f]+$/, "");
const forced = (typeof isTarinaiBehaviorValueForced === "function" ? isTarinaiBehaviorValueForced(b) : Boolean(b.forcedId || b.forcedRequest)) || (typeof isTarinaiBehaviorForced === "function" ? isTarinaiBehaviorForced(tarinai) : false);
if (forced || !isEffectCauseText(cause)) return cause;
}
if ((typeof isTarinaiBehaviorValueForced === "function" ? isTarinaiBehaviorValueForced(b) : Boolean(b.forcedId || b.forcedRequest)) || b.source) {
const forced = forcedCausePhrase(b.source || "", b.sourceReasonText || b.sourceReasonText || "");
if (forced) return forced;
}
if (action?.id === "panic_escape" && tarinai?.lastPanicDetail) return String(tarinai.lastPanicDetail).replace(/[\u3002\uff01\uff1f]+$/, "");
if (action?.id === "fight_rival") {
const target = currentBehaviorTarget?.(tarinai) || tarinai?.target || null;
if (tarinai?.counterAttackFromId && target?.id === tarinai.counterAttackFromId) return `${target.name || "\u76f8\u624b"}\u306b\u3051\u3093\u304b\u3092\u58f2\u3089\u308c\u305f`;
if (target?.name) return `${target.name}\u304c\u6c17\u306b\u5165\u3089\u306a\u3044`;
}
if (action?.id === "birth_ritual" || action?.subNeed === "mate") {
if ((tarinai?.loveMochiTimer || 0) > 0.04) return "\u3078\u3053\u9905\u306e\u52b9\u679c";
}
return needPhraseForReason(chosenNeed, tarinai, action);
}
function buildReasonText(chosenNeed, tiedNeeds, action, tarinai = null, world = null, options = {}) {
if (action?.id === "sunbath" && (options?.leisure || tarinai?.sunbathLeisure || currentTarinaiBehavior(tarinai)?.data?.leisure)) {
return "\u6c17\u5206\u304c\u3044\u3044\u306e\u3067\u3001\u65e5\u5149\u6d74\u3057\u3066\u3044\u308b\u3002";
}
if (action?.id === "seek_comfort_temperature") {
const felt = tarinai?.world?.feltTemperatureFor?.(tarinai) ?? world?.temperatureAt?.(tarinai?.x, tarinai?.y, tarinai) ?? tarinai?.feltTemperature ?? tarinai?.tempComfort ?? (CONFIG.standardTemperature ?? 15);
const status = tarinai?.world?.temperatureStatusFor?.(felt, tarinai) || tarinai?.temperatureStatus || null;
return status?.direction === "cold" ? "\u5BD2\u3044\u306E\u3067\u6E29\u307E\u308D\u3046\u3068\u3057\u3066\u3044\u308B\u3002" : "\u6691\u3044\u306E\u3067\u6DBC\u3082\u3046\u3068\u3057\u3066\u3044\u308B\u3002";
}
const actionText = actionTextForReason(action, tarinai);
const cause = causePhraseForReason(chosenNeed, action, tarinai, options);
const tied = Array.isArray(tiedNeeds) ? tiedNeeds.filter(key => key !== chosenNeed) : [];
const main = formatCauseActionText(cause, actionText);
if (tied.length) {
const ignored = TARINAI_NEED_PRIORITY.find(key => tied.includes(key)) || tied[0];
return `${needPhraseForReason(ignored, tarinai, action)}\u3051\u3069\u3001${main}`;
}
return main;
}
function needsForBehaviorText(tarinai, behavior = {}, fallbackNeed = "fulfill") {
const primary = String(behavior?.need || fallbackNeed || "fulfill");
const raw = tarinai?.needRaw || tarinai?.needs || {};
const list = Array.isArray(behavior?.tiedNeeds) && behavior.tiedNeeds.length ? [...behavior.tiedNeeds] : [primary];
if (!list.includes(primary)) list.unshift(primary);
return list.filter((key, idx) => key && (idx === 0 || Number(raw?.[key] || 0) > 0.5));
}
function causeForBehaviorText(tarinai, behavior = {}, action = null, options = {}) {
const request = behavior?.forcedRequest || (typeof currentForcedBehaviorRequest === "function" ? currentForcedBehaviorRequest(tarinai) : null) || null;
const optionCause = options?.causeText || "";
if (optionCause) return String(optionCause || "").replace(/[\u3002\uff01\uff1f]+$/, "");
const requestCause = request?.causeText || "";
if (requestCause) return String(requestCause || "").replace(/[\u3002\uff01\uff1f]+$/, "");
const behaviorCause = behavior?.causeText || "";
if (behaviorCause) {
const forced = (typeof isTarinaiBehaviorValueForced === "function" ? isTarinaiBehaviorValueForced(behavior) : Boolean(behavior?.forcedId || behavior?.forcedRequest)) || (typeof isTarinaiBehaviorForced === "function" ? isTarinaiBehaviorForced(tarinai) : false);
if (forced || !isEffectCauseText(behaviorCause)) return String(behaviorCause || "").replace(/[\u3002\uff01\uff1f]+$/, "");
}
if ((typeof isTarinaiBehaviorValueForced === "function" ? isTarinaiBehaviorValueForced(behavior) : Boolean(behavior?.forcedId || behavior?.forcedRequest)) || request) {
const source = request?.source || behavior?.source || "external";
const sourceReason = request?.reasonText || behavior?.sourceReasonText || behavior?.sourceReasonText || "";
const forcedCause = forcedCausePhrase(source, sourceReason);
if (forcedCause) return forcedCause;
}
return causePhraseForReason(behavior?.need || action?.need || "fulfill", action, tarinai, options);
}
function hasVisibleFireThreatForBehavior(tarinai) {
if (!tarinai || !tarinai.world) return false;
const target = tarinai.target || currentBehaviorTarget?.(tarinai) || null;
if (target && !target.dead && (target.type === "fire" || target.type === "flame_firecracker" || target.burning || (target.burnTimer || 0) > 0.02)) return true;
if ((tarinai.firePanicTimer || 0) > 0.02) return true;
const fires = typeof tarinai.world.itemsOfType === "function" ? tarinai.world.itemsOfType("fire") : (tarinai.world.items || []).filter(it => it && it.type === "fire");
return (fires || []).some(f => f && !f.dead && (f.amount || 0) > 0 && distXY(tarinai.x, tarinai.y, f.x, f.y) <= Math.max(190, (f.r || 14) + 180));
}
function composeTarinaiBehaviorText(tarinai, behavior = null, options = {}) {
if (tarinai && (tarinai.burning || (tarinai.burnTimer || 0) > 0.02)) return globalThis.TarinaiItemDynamicToolSystem?.fireBurningText || "\u71c3\u3048\u3066\u9003\u3052\u307e\u3069\u3063\u3066\u3044\u308b\u3002";
if (tarinai && hasVisibleFireThreatForBehavior(tarinai)) return globalThis.TarinaiItemDynamicToolSystem?.fireSeenText || "\u708e\u3092\u898b\u3066\u9a5a\u3044\u3066\u3044\u308b\u3002";
if (tarinai && tarinai.lastPanicCause === "fire_seen" && !hasVisibleFireThreatForBehavior(tarinai)) {
tarinai.lastPanicCause = "";
if (tarinai.lastPanicDetail === (globalThis.TarinaiItemDynamicToolSystem?.fireSeenText || "\u708e\u3092\u898b\u3066\u9a5a\u3044\u3066\u3044\u308b\u3002")) tarinai.lastPanicDetail = "";
}
const b = behavior || (currentTarinaiBehavior(tarinai)) || null;
if (!b) return "";
const panicActionId = String(b.specId || b.actionId || "");
if (panicActionId === "panic_escape" || tarinai?.state === "panic") {
const threatLabel = panicThreatLabel(tarinai);
if (threatLabel) return `${threatLabel}\u304c\u6016\u304f\u3066\u3001\u30d1\u30cb\u30c3\u30af\u306b\u306a\u3063\u3066\u3044\u308b\u3002`;
}
const stored = String(b.text || b.reason || "").trim();
if (stored && options.regenerate !== true) return stored;
const action = options.action || actionById(b.specId || b.actionId) || actionById(b.actionId) || null;
const need = String(options.need || b.need || action?.need || "fulfill");
const tiedNeeds = Array.isArray(options.tiedNeeds) && options.tiedNeeds.length ? [...options.tiedNeeds] : needsForBehaviorText(tarinai, b, need);
const causeText = causeForBehaviorText(tarinai, { ...b, need }, action, options);
const generated = action ? buildReasonText(need, tiedNeeds, action, tarinai, tarinai?.world || null, { ...options, causeText }) : "";
const actionLabel = textTarinaiAction(action || b.actionId, tarinai, tarinai?.world || null, { ...options, behavior: b, need, tiedNeeds, causeText, phase: options.phase || b.phase || "active" });
const rawFallback = String(actionLabel || b.label || b.reason || b.text || "").trim();
const catalog = globalThis.TEXT_CATALOG || (typeof window !== "undefined" ? window.TEXT_CATALOG : null);
const fallback = catalog?.cleanBehaviorText
? catalog.cleanBehaviorText(rawFallback, b.actionId || b.specId || "")
: rawFallback;
return String(generated || fallback || "").trim();
}
function patchBehaviorTextFromComposer(tarinai, behavior = null, options = {}) {
const b = behavior || (currentTarinaiBehavior(tarinai)) || null;
if (!b) return "";
const prevReason = String(b.reason || "");
const text = composeTarinaiBehaviorText(tarinai, b, { ...options, regenerate: true });
if (!text) return "";
const tiedNeeds = Array.isArray(options.tiedNeeds) && options.tiedNeeds.length ? [...options.tiedNeeds] : needsForBehaviorText(tarinai, b, b.need || options.need || "fulfill");
if (text !== b.reason || text !== b.text || JSON.stringify(tiedNeeds) !== JSON.stringify(b.tiedNeeds || [])) {
patchTarinaiBehavior(tarinai, { reason: text, text: text, tiedNeeds });
}
if (text && (!tarinai.thought || tarinai.thought === prevReason || tarinai.thought === b.label)) tarinai.thought = text;
return text;
}
function refreshBehaviorReasonText(tarinai, action = null) {
const behavior = currentTarinaiBehavior(tarinai);
if (!behavior?.need) return "";
return patchBehaviorTextFromComposer(tarinai, behavior, { action });
}
Object.assign(global, {
buildReasonText,
composeTarinaiBehaviorText,
refreshBehaviorReasonText,
});
})(typeof window !== "undefined" ? window : globalThis);