"use strict"; function quoteNameInRecordText(text, name) { const raw = String(text || ""); const n = String(name || "").trim(); if (!n) return raw; const parts = raw.split(n); if (parts.length <= 1) return raw; let out = ""; for (let i = 0; i < parts.length - 1; i++) { const before = parts[i].slice(-1); const after = parts[i + 1].charAt(0); out += parts[i] + ((before === "\u300c" && after === "\u300d") ? n : `\u300c${n}\u300d`); } return out + parts[parts.length - 1]; } function renameSelectedTarinai(rawName) { const t = world.selected; if (!t || t.dead || !world.tarinai.includes(t)) return false; const next = String(rawName || "").replace(/\s+/g, " ").trim().slice(0, 18); if (!next) { uiCache.selectedSnapshot = ""; renderSelected(); return false; } if (next === t.name) return false; const oldName = t.name; t.name = next; if (t.isZunchiSlave) t.formerName = next; world.recordFamily?.(t); world.markFamilyTreeDirty?.("rename"); uiCache.archiveFamilyVersion = null; uiCache.selectedSnapshot = ""; renderSelected(); renderArchive?.(); render?.(); showToast(`\u300c${oldName}\u300d\u3092\u300c${next}\u300d\u306b\u6539\u540d\u3057\u307e\u3057\u305f\u3002`); return true; } function personalityValueText(value) { const n = clamp(Number(value) || 0, -1, 1); return `${n >= 0 ? "+" : ""}${n.toFixed(2)}`; } function personalityRowsHtml(personality = {}) { const p = normalizePersonality(personality); return PERSONALITY_KEYS.map(key => `${escapeHtml(PERSONALITY_LABELS[key]?.label || key)}: ${personalityValueText(p[key])}`).join(""); } function personalityShortLabel(key) { return ({ aggression: "\u653b\u6483", openness: "\u958b\u653e", sociability: "\u793e\u4ea4", neuroticism: "\u795e\u7d4c", })[key] || (PERSONALITY_LABELS[key]?.label || key); } function personalityDisplayCauseText(cause = "") { return String(cause || "").replace(/(?:\u306e)?\u5f71\u97ff\u3067$/g, "").trim(); } function geneticPercentText(t, key = "") { const ratio = t?.geneticStatRatio ? t.geneticStatRatio(key) : 1; const pct = Math.round((ratio - 1) * 100); return `${pct >= 0 ? "+" : ""}${pct}%`; } function geneticSizeText(t) { const adult = Number.isFinite(t?.adultScale) ? t.adultScale : (t?.scale || 0.28); const now = Number.isFinite(t?.scale) ? t.scale : adult; const growth = Math.round((t?.growth ?? 1) * 100); return `${geneticPercentText(t, "size")} / ${now.toFixed(2)}\u2192${adult.toFixed(2)} (${growth}%)`; } function personalityDailyChangeHtml(t) { const state = typeof personalityDailyState === "function" ? personalityDailyState(t) : (t?.personalityDaily || { day: world?.day || 1, byKey: {}, byCause: {} }); const dayLabel = `D${state.day || world?.day || 1}`; const causeMap = new Map(); const addCauseChange = (cause, key, up, down) => { const cleanCause = personalityDisplayCauseText(cause || "") || "\u8981\u56e0\u4e0d\u660e"; if (!causeMap.has(cleanCause)) causeMap.set(cleanCause, new Map()); const byKey = causeMap.get(cleanCause); const current = byKey.get(key) || { up: 0, down: 0 }; current.up += Math.max(0, Number(up) || 0); current.down += Math.max(0, Number(down) || 0); byKey.set(key, current); }; for (const [cause, entry] of Object.entries(state.byCause || {})) { for (const key of PERSONALITY_KEYS) { const v = entry?.byKey?.[key] || {}; const up = Math.max(0, Number(v.up) || 0); const down = Math.max(0, Number(v.down) || 0); if (up > 0.0001 || down > 0.0001) addCauseChange(cause, key, up, down); } } if (!causeMap.size) { for (const key of PERSONALITY_KEYS) { const entry = state.byKey?.[key] || {}; const up = Math.max(0, Number(entry.up) || 0); const down = Math.max(0, Number(entry.down) || 0); if (up <= 0.0001 && down <= 0.0001) continue; const causes = Array.isArray(entry.causes) ? [...new Set(entry.causes.map(c => personalityDisplayCauseText(c)).filter(Boolean))].slice(0, 4) : []; for (const cause of causes.length ? causes : ["\u8981\u56e0\u4e0d\u660e"]) addCauseChange(cause, key, up, down); } } const rows = []; for (const [cause, byKey] of causeMap.entries()) { const parts = []; for (const key of PERSONALITY_KEYS) { const entry = byKey.get(key); if (!entry) continue; const up = Math.max(0, Number(entry.up) || 0); const down = Math.max(0, Number(entry.down) || 0); if (up > 0.0001) parts.push(`${personalityShortLabel(key)} +${up.toFixed(3)}`); if (down > 0.0001) parts.push(`${personalityShortLabel(key)} -${down.toFixed(3)}`); } if (parts.length) rows.push(`
  • ${escapeHtml(cause)}${escapeHtml(parts.join(" / "))}${escapeHtml(dayLabel)}
  • `); } return rows.join("") || `
  • \u8981\u56e0\u5909\u5316\u306a\u3057${escapeHtml(dayLabel)}
  • `; } function personalityDailySnapshot(t) { const state = typeof personalityDailyState === "function" ? personalityDailyState(t) : (t?.personalityDaily || {}); const keyPart = PERSONALITY_KEYS.map(key => { const entry = state.byKey?.[key] || {}; const causes = Array.isArray(entry.causes) ? entry.causes.join("/") : ""; return `${key}:${Number(entry.up || 0).toFixed(4)}:${Number(entry.down || 0).toFixed(4)}:${causes}`; }).join(","); const causePart = Object.entries(state.byCause || {}).map(([cause, entry]) => { const values = PERSONALITY_KEYS.map(key => { const v = entry?.byKey?.[key] || {}; return `${key}:${Number(v.up || 0).toFixed(4)}:${Number(v.down || 0).toFixed(4)}`; }).join(";"); return `${cause}=${values}`; }).join("|"); return `${keyPart}#${causePart}`; } function recentChangeCausesHtml(t) { const rows = Array.isArray(t?.recentChangeCauses) ? t.recentChangeCauses.slice(0, 8) : []; if (!rows.length) return `
  • -\u6700\u8fd1\u306e\u5909\u5316\u306a\u3057
  • `; return rows.map(entry => { const timeText = world.clockStringFromTime ? world.clockStringFromTime(entry.time || 0) : formatRecordTime(entry.time || 0); const reason = String(entry.reason || "\u5909\u5316"); const target = String(entry.target || "\u72b6\u614b"); const value = entry.value === "" || entry.value == null ? "" : ` ${entry.value}`; return `
  • ${escapeHtml(timeText)}${escapeHtml(reason)} / ${escapeHtml(target)}${escapeHtml(value)}
  • `; }).join(""); } function selectedDataCategoryHtml(id, label, bodyHtml) { if (!uiCache.selectedCollapsedCategories) uiCache.selectedCollapsedCategories = new Set(); const collapsed = uiCache.selectedCollapsedCategories.has(id); return `
    ${bodyHtml}
    `; } function selectedInfoRowHtml(label, valueHtml, metric = "") { const metricAttr = metric ? ` data-metric="${escapeHtml(metric)}"` : ""; return `
    ${escapeHtml(label)}${valueHtml}
    `; } function selectedNeedRowsHtml(t) { const system = window.TarinaiNeedSystem; const keys = system?.NEED_KEYS || ["food", "sleep", "health", "safety", "social", "fulfill"]; const labels = system?.NEED_LABELS || { food: "\u6442\u990c", sleep: "\u7761\u7720", health: "\u5065\u5eb7", safety: "\u5b89\u5168", social: "\u95a2\u4fc2", fulfill: "\u5145\u8db3", }; const active = t?.intent?.need || t?.need || ""; return keys.map(key => { const value = system?.getNeedDisplayValue ? system.getNeedDisplayValue(t?.needs?.[key] || 0) : Math.round((t?.needs?.[key] || 0) / 10); const mark = key === active ? ` \u25b2` : ""; return `
    ${escapeHtml(labels[key] || key)}${escapeHtml(value)}${mark}
    `; }).join(""); } function renderSelected() { const t = world.selected; if (!t || t.dead || !world.tarinai.includes(t)) { ui.selectedCard?.classList.add("hidden"); if (uiCache.selectedEmpty) return; uiCache.selectedSnapshot = ""; uiCache.selectedEmpty = true; ui.selectedInfo.className = "selected-info empty"; ui.selectedInfo.textContent = "\u672a\u9078\u629e"; return; } ui.selectedCard?.classList.remove("hidden"); uiCache.selectedEmpty = false; const activeNameInput = document.activeElement?.matches?.("[data-selected-name]") && document.activeElement?.dataset?.selectedId === t.id; if (activeNameInput) return; const rel = t.relationSummary ? t.relationSummary() : {}; const generationDisplay = t.displayGeneration ? t.displayGeneration() : ((t.hasPaired || t.parents?.length || t.children?.length) ? t.generation : ""); ensurePersonality(t); const personalityTags = t.personalityTags ? t.personalityTags() : getPersonalityTraitTags(t); const personalitySnapshot = [ ...PERSONALITY_KEYS.map(key => personalityValueText(t.birthPersonality?.[key])), ...PERSONALITY_KEYS.map(key => personalityValueText(t.currentPersonality?.[key])), personalityTags.join(",") ].join(","); const itemEffectSummary = t.activeItemEffectSummary ? t.activeItemEffectSummary() : []; const snapshot = [ t.id, t.type, t.name, generationDisplay, t.state, t.thought, targetLabel(t.target), fmt(t.age), fmt(t.lifeSpan), fmt(t.hunger), fmt(t.loneliness), fmt(t.energy), fmt(t.stress), fmt(t.mood), t.parentNames?.join("+") || "", t.children?.length || 0, fmt(t.lack), relationEntries(t, "friend").map(r => `${r.id}:${fmt(r.score)}:${r.name}`).join(","), relationEntries(t, "enemy").map(r => `${r.id}:${fmt(r.score)}:${r.name}`).join(","), fightWinRateLabel(t), t.isZunchiSlave ? 1 : 0, fmt(t.totalFightLosses || 0), fmt(t.loveMochiTimer || 0), fmt(t.fightMochiTimer || 0), t.zunchiDisease ? 1 : 0, fmt(t.zunchiDiseaseSeverity || 0), fmt(t.zunchiStain || 0), t.sleepDisease ? 1 : 0, t.explosionDisease ? 1 : 0, fmt(t.explosionDiseaseTimer || 0), t.fightDisease ? 1 : 0, t.favorite ? 1 : 0, JSON.stringify(t.needs || {}), t.intent?.need || "", t.intent?.actionId || "", t.intent?.actionLabel || "", t.intent?.reasonText || "", personalitySnapshot, personalityDailySnapshot(t), ["life", "attack", "speed", "size"].map(key => geneticPercentText(t, key)).join(","), fmt((t.growth || 0) * 100), itemEffectSummary.map(e => `${e.id}:${e.detail}`).join(","), (t.recentChangeCauses || []).slice(0, 8).map(c => `${Math.round(c.time || 0)}:${c.reason || ""}:${c.target || ""}:${c.value ?? ""}`).join("~"), (t.records || []).slice(0, 6).map(r => `${Math.round(r.time || 0)}:${r.kind || ""}:${r.text || ""}`).join("~") ].join("|"); if (uiCache.selectedSnapshot === snapshot) return; uiCache.selectedSnapshot = snapshot; ui.selectedInfo.className = "selected-info"; const effectBadges = []; if (t.isZunchiSlave) effectBadges.push(`\u305a\u3093\u3061\u3069\u308c\u3044`); if (t.favorite) effectBadges.push(`\u2605`); if (t.zunchiDisease) effectBadges.push(`\u305a\u3093\u3061\u75c5 ${fmt((t.zunchiDiseaseSeverity || 0) * 100)}`); if (t.sleepDisease) effectBadges.push(`\u306d\u3080\u308a\u75c5`); if (t.explosionDisease) effectBadges.push(`\u7206\u767a\u75c5 ${fmt(t.explosionDiseaseTimer || 0)}`); if (t.fightDisease) effectBadges.push(`\u304d\u305a\u3064\u304d\u75c5`); if ((t.loveMochiTimer || 0) > 0.1) effectBadges.push(`\u3078\u3053\u9905 ${fmt(t.loveMochiTimer)}`); if ((t.fightMochiTimer || 0) > 0.1) effectBadges.push(`\u3051\u3093\u304b\u9905 ${fmt(t.fightMochiTimer)}`); const personalityTagHtml = personalityTags.length ? personalityTags.map(tag => `${escapeHtml(tag)}`).join("") : `\u4e2d\u7acb`; const recentChangeHtml = personalityDailyChangeHtml(t); const personalityHtml = `
    \u6027\u683c\u30bf\u30b0${personalityTagHtml}
    \u8a95\u751f\u6642\u6027\u683c${personalityRowsHtml(t.birthPersonality)}
    \u73fe\u5728\u6027\u683c${personalityRowsHtml(t.currentPersonality)}
    \u4eca\u65e5\u306e\u5909\u5316
      ${recentChangeHtml}
    `; const recordHtml = (t.records || []).slice(0, 8).map(r => { const timeText = world.clockString ? (world.clockStringFromTime ? world.clockStringFromTime(r.time || 0) : formatRecordTime(r.time || 0)) : formatRecordTime(r.time || 0); const recordText = quoteNameInRecordText(r.text || "", t.name); return `
  • ${escapeHtml(timeText)}${escapeHtml(recordText)}
  • `; }).join("") || `
  • -\u8a18\u9332\u306a\u3057
  • `; const recentCauseHtml = recentChangeCausesHtml(t); const basicHtml = `
    \u4e16\u4ee3${escapeHtml(generationDisplay || "\u4e0d\u660e")}
    \u72b6\u614b${escapeHtml(stateLabel(t.state))}
    \u3044\u307e${escapeHtml(t.intent?.actionLabel || stateLabel(t.state))}
    \u7406\u7531${escapeHtml(t.intent?.reasonText || reasonLabel(t))}
    \u5bfe\u8c61${escapeHtml(targetLabel(t.target))}
    \u5e74\u9f62${fmt(t.age)} / ${fmt(t.lifeSpan)}
    \u6700\u5927\u5bff\u547d${geneticPercentText(t, "life")}
    \u653b\u6483\u529b${geneticPercentText(t, "attack")}
    \u79fb\u52d5\u901f\u5ea6${geneticPercentText(t, "speed")}
    \u30b5\u30a4\u30ba${geneticSizeText(t)}
    `; const healthHtml = selectedNeedRowsHtml(t) + selectedInfoRowHtml("\u30b9\u30c8\u30ec\u30b9", fmt(t.stress), "stress"); const itemEffectHtml = itemEffectSummary.length ? itemEffectSummary.map(e => `
    ${escapeHtml(e.label)}${escapeHtml(e.detail)}
    `).join("") : `
    \u30a2\u30a4\u30c6\u30e0\u52b9\u679c\u306a\u3057
    `; const relationHtml = `
    \u53cb\u9054${relationListHtml(t, "friend")}
    \u6575\u5bfe${relationListHtml(t, "enemy")}
    \u3051\u3093\u304b\u52dd\u7387${escapeHtml(fightWinRateLabel(t))}
    `; const familyHtml = `
    \u89aa${escapeHtml(t.parentNames?.length ? t.parentNames.join(" + ") : "\u4e0d\u660e")}
    \u5b50${t.children?.length || 0}
    `; ui.selectedInfo.innerHTML = `
    ${effectBadges.length ? `
    ${effectBadges.join("")}
    ` : ""}
    ${selectedDataCategoryHtml("basic", "\u57fa\u672c", basicHtml)} ${selectedDataCategoryHtml("health", "\u4f53\u8abf", healthHtml)} ${selectedDataCategoryHtml("effects", "\u30a2\u30a4\u30c6\u30e0\u52b9\u679c", itemEffectHtml)} ${selectedDataCategoryHtml("personality", "\u6027\u683c", personalityHtml)} ${selectedDataCategoryHtml("relation", "\u95a2\u4fc2", relationHtml)} ${selectedDataCategoryHtml("family", "\u5bb6\u65cf", familyHtml)} ${selectedDataCategoryHtml("records", "\u8a18\u9332", `

    \u6700\u8fd1\u306e\u5909\u5316\u539f\u56e0

      ${recentCauseHtml}

    \u500b\u4f53\u5225\u306e\u8a18\u9332

      ${recordHtml}
    `)}
    `; } function formatRecordTime(time) { const totalMinutes = Math.floor(((time || 0) % (CONFIG.dayLength || 120)) / (CONFIG.dayLength || 120) * 24 * 60); const hh = String(Math.floor(totalMinutes / 60)).padStart(2, "0"); const mm = String(totalMinutes % 60).padStart(2, "0"); return `${hh}:${mm}`; } function stateLabel(s) { return window.TEXT_CATALOG?.stateLabel?.(s) || s || ""; } function reasonLabel(t) { return window.TEXT_CATALOG?.reasonLabel?.(t) || "なし"; } function targetLabel(target) { return window.TEXT_CATALOG?.targetLabel?.(target) || "なし"; } function fightWinRateLabel(t) { const won = Number(t.totalFightWins || 0); const lost = Number(t.totalFightLosses || 0); const total = won + lost; if (!total) return "\u306a\u3057"; return `${Math.round((won / total) * 100)}% (${won}\u52dd${lost}\u6557)`; } function setTextIfChanged(el, key, value) { if (!el) return; const next = String(value); if (uiCache.stats[key] === next) return; uiCache.stats[key] = next; el.textContent = next; } function setValueIfChanged(el, key, value) { if (!el) return; if (uiCache.stats[key] === value) return; uiCache.stats[key] = value; el.value = value; } function escapeHtml(value) { const helper = window.TarinaiUIHelpers?.htmlEscape; if (typeof helper === "function" && helper !== escapeHtml) return helper(value); return String(value ?? "").replace(/[&<>"']/g, c => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[c] || c)); } function escapeRegExp(value) { return String(value ?? "").replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); } function formatLogDisplayText(text) { let out = String(text ?? ""); const names = new Set(); for (const t of world?.tarinai || []) if (t?.name) names.add(t.name); for (const n of Object.values(world?.family || {})) if (n?.name) names.add(n.name); const sorted = [...names].filter(Boolean).sort((a, b) => b.length - a.length); for (const name of sorted) { const re = new RegExp(`(^|[^\u300c])(${escapeRegExp(name)})(?=[\u3068\u306f\u304c\u3092\u306b\u306e\u3082\u3078\u304b\u3089\u3067\u3001\u3002]|$)`, "g"); out = out.replace(re, (m, prefix, found) => `${prefix}\u300c${found}\u300d`); } return out; }