"use strict"; (function (global) { const Tarinai = global.Tarinai; if (!Tarinai) throw new Error("Tarinai is not available for mixin: tarinai_behavior_state.js"); const isObject = global.TarinaiCoreHelpers?.isPlainObject || (value => Boolean(value && typeof value === "object" && !Array.isArray(value))); const finiteOr = global.TarinaiCoreHelpers?.finiteOr || ((value, fallback) => { const n = Number(value); return Number.isFinite(n) ? n : fallback; }); function nowFor(tarinai = null) { return finiteOr(tarinai?.world?.time, 0); } function targetIdFor(target = null) { if (!target) return null; if (target.id != null) return String(target.id); if (Number.isFinite(target.x) && Number.isFinite(target.y)) return `pos:${Math.round(target.x)},${Math.round(target.y)}`; return null; } function normalizeBehaviorTarget(input = undefined, fallback = undefined) { const target = input !== undefined ? input : fallback; if (!target) return null; if (target.kind) { const out = { kind: String(target.kind) }; if (target.id != null) out.id = String(target.id); if (Number.isFinite(Number(target.x))) out.x = Number(target.x); if (Number.isFinite(Number(target.y))) out.y = Number(target.y); return out; } if (target.id != null) { const type = String(target.type || ""); const kind = target.kind === "tarinai" || target.familyKey || type === "tarinai" ? "tarinai" : "item"; return { kind, id: String(target.id) }; } if (Number.isFinite(Number(target.x)) && Number.isFinite(Number(target.y))) { return { kind: "position", x: Number(target.x), y: Number(target.y) }; } return null; } function normalizeForcedRequestValue(value = null, fallback = null) { const src = isObject(value) ? value : {}; const prev = isObject(fallback) ? fallback : {}; const uid = String(src.uid || src.forcedId || prev.uid || prev.forcedId || ""); const id = String(src.id || src.actionId || prev.id || prev.actionId || ""); if (!uid && !id) return null; return { uid, id, source: String(src.source || prev.source || "external"), priority: finiteOr(src.priority, finiteOr(prev.priority, 0)), status: String(src.status || prev.status || "active"), reasonText: String(src.reasonText || src.reason || prev.reasonText || prev.reason || ""), causeText: String(src.causeText || prev.causeText || ""), targetId: src.targetId !== undefined ? src.targetId : (prev.targetId !== undefined ? prev.targetId : null), createdAt: finiteOr(src.createdAt, finiteOr(prev.createdAt, 0)), startedAt: finiteOr(src.startedAt, finiteOr(src.activeSince, finiteOr(prev.startedAt, finiteOr(prev.activeSince, 0)))), activeSince: finiteOr(src.activeSince, finiteOr(src.startedAt, finiteOr(prev.activeSince, finiteOr(prev.startedAt, 0)))), expiresAt: Number.isFinite(Number(src.expiresAt)) ? Number(src.expiresAt) : (Number.isFinite(Number(prev.expiresAt)) ? Number(prev.expiresAt) : undefined), minDuration: finiteOr(src.minDuration, finiteOr(prev.minDuration, 0)), duration: finiteOr(src.duration, finiteOr(prev.duration, 0)), attempt: finiteOr(src.attempt ?? src.attempts, finiteOr(prev.attempt ?? prev.attempts, 0)), failReason: String(src.failReason || prev.failReason || ""), }; } function isBehaviorForcedValue(behavior = null) { if (!isObject(behavior)) return false; return Boolean(behavior.source === "forced" || behavior.forcedId || behavior.forcedRequest || behavior.data?.forcedSource || behavior.forced === true); } function forcedBehaviorSource(behavior = null) { if (!isObject(behavior)) return ""; return String(behavior.forcedRequest?.source || behavior.data?.forcedSource || behavior.sourceDetail || ""); } const isEffectCauseText = global.TarinaiCoreHelpers?.isEffectCauseText || (text => /\u306E\u52B9\u679C$/.test(String(text || "").trim().replace(/[\u3002\uFF01\uFF1F]+$/, ""))); function normalizeBehaviorValue(input, tarinai = null, previous = null) { if (!isObject(input)) return null; const now = nowFor(tarinai); const previousActionId = previous?.actionId || ""; const actionId = String(input.actionId || previousActionId || tarinai?.state || "idle"); const targetInput = input.target !== undefined ? input.target : (input.targetRef !== undefined ? input.targetRef : undefined); const target = normalizeBehaviorTarget(targetInput, tarinai?.target); const forcedRequest = normalizeForcedRequestValue( input.forcedRequest || input.request || null, (input.forced || input.forcedId || previous?.forcedRequest) ? { ...(previous?.forcedRequest || {}), uid: input.forcedId || previous?.forcedId || previous?.forcedRequest?.uid || "", id: actionId, source: input.sourceDetail || input.source || previous?.data?.forcedSource || previous?.forcedRequest?.source || "external", priority: input.priority ?? previous?.priority ?? 0, reasonText: input.sourceReasonText || input.forcedReasonText || input.reason || input.reasonText || previous?.sourceReasonText || previous?.forcedRequest?.reasonText || "", causeText: input.causeText || previous?.causeText || "", targetId: input.targetId ?? targetIdFor(target) } : null ); const forced = Boolean(forcedRequest || input.forced || input.forcedId || previous?.forcedId && input.source === "forced"); const source = forced ? "forced" : String(input.source || previous?.source || "need"); const data = isObject(previous?.data) ? { ...previous.data } : {}; if (isObject(input.data)) Object.assign(data, input.data); if (forcedRequest) data.forcedSource = forcedRequest.source || data.forcedSource || "external"; else if (forced && input.source && input.source !== "forced") data.forcedSource = String(input.source); const reason = String(input.reason ?? input.reasonText ?? input.presentText ?? previous?.reason ?? previous?.reasonText ?? previous?.presentText ?? input.label ?? previous?.label ?? ""); const label = String(input.label || input.actionLabel || previous?.label || actionId || "\u884c\u52d5\u3057\u3066\u3044\u308b"); const need = String(input.need || previous?.need || "fulfill"); const tiedNeeds = Array.isArray(input.tiedNeeds) && input.tiedNeeds.length ? [...input.tiedNeeds].filter(Boolean).map(String) : (Array.isArray(previous?.tiedNeeds) && previous.tiedNeeds.length ? [...previous.tiedNeeds] : [need]); const behavior = { actionId, phase: String(input.phase || previous?.phase || (String(tarinai?.state || "") === "idle" ? "idle" : "acting")), target, source, reason, priority: finiteOr(input.priority, finiteOr(previous?.priority, 0)), startedAt: finiteOr(input.startedAt, finiteOr(previous?.startedAt, now)), elapsed: finiteOr(input.elapsed, Math.max(0, now - finiteOr(input.startedAt ?? previous?.startedAt, now))), timeout: Number.isFinite(Number(input.timeout ?? input.deadlineAt)) ? Number(input.timeout ?? input.deadlineAt) : (previous?.timeout ?? null), interruptible: input.interruptible !== undefined ? input.interruptible !== false : previous?.interruptible !== false, data, need, subNeed: String(input.subNeed || previous?.subNeed || ""), tiedNeeds, label, text: String(input.text ?? input.presentText ?? input.reasonText ?? previous?.text ?? previous?.presentText ?? reason ?? label ?? ""), lockSeconds: finiteOr(input.lockSeconds, finiteOr(previous?.lockSeconds, 1.0)), minDuration: finiteOr(input.minDuration, finiteOr(previous?.minDuration, 0.45)), deadlineAt: Number.isFinite(Number(input.deadlineAt)) ? Number(input.deadlineAt) : previous?.deadlineAt, forcedId: forcedRequest?.uid || String(input.forcedId || previous?.forcedId || ""), forcedRequest, sourceReasonText: String(input.sourceReasonText || input.forcedReasonText || forcedRequest?.reasonText || previous?.sourceReasonText || ""), causeText: String(input.causeText || forcedRequest?.causeText || ((forced || !isEffectCauseText(previous?.causeText)) ? previous?.causeText : "") || ""), specId: String(input.specId || input.actionSpecId || previous?.specId || actionId), specKind: String(input.specKind || previous?.specKind || "need_action"), }; if (Number.isFinite(Number(input.serial))) behavior.serial = Number(input.serial); return behavior; } function normalizeBehaviorState(opts = {}, tarinai = null) { const previous = isObject(tarinai?.behavior) ? tarinai.behavior : null; return normalizeBehaviorValue(opts.behavior || null, tarinai, previous); } function ensureBehavior(tarinai) { if (!tarinai || !isObject(tarinai.behavior)) return null; const b = tarinai.behavior; return b; } function assignBehavior(tarinai, value) { if (!tarinai) return null; const next = normalizeBehaviorValue(value, tarinai, tarinai.behavior || null); tarinai.behavior = next; return next; } function patchTarinaiBehavior(tarinai, fields = {}) { if (!tarinai || !isObject(fields)) return ensureBehavior(tarinai); const previous = ensureBehavior(tarinai) || {}; const next = normalizeBehaviorValue({ ...previous, ...fields }, tarinai, previous); tarinai.behavior = next; return next; } function clearTarinaiBehavior(tarinai, opts = {}) { if (!tarinai) return null; tarinai.behavior = null; if (opts.clearTarget) tarinai.target = null; return null; } function getTarinaiBehavior(tarinai) { return ensureBehavior(tarinai); } function getTarinaiBehaviorId(tarinai, fallback = "") { return String(ensureBehavior(tarinai)?.actionId || fallback || ""); } function getTarinaiBehaviorNeed(tarinai, fallback = "") { return String(ensureBehavior(tarinai)?.need || fallback || ""); } function getTarinaiBehaviorTiedNeeds(tarinai, fallbackNeed = "fulfill") { const b = ensureBehavior(tarinai); if (Array.isArray(b?.tiedNeeds) && b.tiedNeeds.length) return [...b.tiedNeeds]; return [String(b?.need || fallbackNeed || "fulfill")]; } function getTarinaiBehaviorText(tarinai, fallback = "") { const b = ensureBehavior(tarinai); return String(b?.text || b?.reason || b?.label || fallback || "").trim(); } function setTarinaiForcedBehavior(tarinai, value = null) { if (!tarinai) return null; const b = ensureBehavior(tarinai); if (!b) return null; if (!value) { const data = isObject(b.data) ? { ...b.data } : {}; delete data.forcedSource; const next = normalizeBehaviorValue({ ...b, source: b.source === "forced" ? "need" : (b.source || "need"), forcedId: "", forcedRequest: null, sourceReasonText: "", causeText: "", data }, tarinai, null); tarinai.behavior = next; return next; } const request = normalizeForcedRequestValue(value, { uid: b.forcedId || "", id: b.actionId || "idle", source: b.data?.forcedSource || "external", priority: b.priority || 0, reasonText: b.sourceReasonText || b.reason || "", causeText: b.causeText || "", targetId: targetIdFor(b.target) }); return patchTarinaiBehavior(tarinai, { ...b, actionId: request?.id || value.actionId || value.id || b.actionId || "idle", source: "forced", priority: finiteOr(request?.priority, finiteOr(value.priority, finiteOr(b.priority, 0))), forcedId: String(request?.uid || value.uid || value.forcedId || b.forcedId || ""), forcedRequest: request, sourceReasonText: request?.reasonText || b.sourceReasonText || "", causeText: request?.causeText || b.causeText || "", data: { ...(b.data || {}), forcedSource: request?.source || value.source || "external" }, }); } function getTarinaiForcedBehavior(tarinai) { const b = ensureBehavior(tarinai); if (!isBehaviorForcedValue(b)) return null; return b.forcedRequest || { uid: b.forcedId || "", id: b.actionId || "", source: forcedBehaviorSource(b) || "external", priority: finiteOr(b.priority, 0), reasonText: b.sourceReasonText || b.reason || "", causeText: b.causeText || "" }; } Object.assign(global, { normalizeTarinaiBehaviorState: normalizeBehaviorState, currentTarinaiBehavior: getTarinaiBehavior, getTarinaiBehaviorId, getTarinaiBehaviorNeed, getTarinaiBehaviorTiedNeeds, getTarinaiBehaviorText, patchTarinaiBehavior, setTarinaiBehavior: assignBehavior, clearTarinaiBehavior, normalizeTarinaiForcedRequest: normalizeForcedRequestValue, getTarinaiForcedBehavior, setTarinaiForcedBehavior, clearTarinaiForcedBehavior: function clearTarinaiForcedBehavior(tarinai) { return setTarinaiForcedBehavior(tarinai, null); }, isTarinaiBehaviorForced: function isTarinaiBehaviorForced(tarinai) { return isBehaviorForcedValue(ensureBehavior(tarinai)); }, isTarinaiBehaviorValueForced: isBehaviorForcedValue, isTarinaiEffectCauseText: isEffectCauseText, }); })(typeof window !== "undefined" ? window : globalThis);