From b31958c6304a25279197c1a08b924b6b44798a99 Mon Sep 17 00:00:00 2001
From: historia <[not public]>
Date: Fri, 10 Jul 2026 18:53:26 -0400
Subject: feat(admin): use intereditor.js for mob talk editor to further unify
condition/action system
---
internal/admin/static/talktree.js | 731 +++++++++++---------------------------
1 file changed, 205 insertions(+), 526 deletions(-)
(limited to 'internal/admin/static/talktree.js')
diff --git a/internal/admin/static/talktree.js b/internal/admin/static/talktree.js
index 4758918..ef6efd0 100644
--- a/internal/admin/static/talktree.js
+++ b/internal/admin/static/talktree.js
@@ -1,6 +1,7 @@
-// talktree.js — Full-featured conversation tree editor.
-// Inline editing for all TalkNode and TalkOption fields.
-// No prompt() dialogs. Clause-based condition editor, action forms.
+// talktree.js — Conversation tree editor with unified condition & action
+// editors powered by intereditor.js. Tree navigation, messages, and options
+// (text / goto) stay here; condition and action editing delegates to
+// intereditor via talk-scoped contexts that target talk.nodes[key]... paths.
(function() {
'use strict';
@@ -52,103 +53,143 @@
else { delete root[lk]; }
}
- // ---- Clause types ----
-
- var CLAUSE_TYPES = [
- { key: 'flag', label: 'Flag', hasValue: true },
- { key: 'player_flag', label: 'Player Flag', hasValue: true },
- { key: 'has_item', label: 'Has Item', hasValue: false },
- { key: 'min_credits', label: 'Min Credits', hasValue: false }
- ];
+ // ---- Talk scopes for intereditor.js ----
+ // Each scope targets a specific condition or action object inside the talk
+ // tree and handles re-render + talkSync on every mutation.
+
+ // ---- Talk scopes for intereditor.js ----
+ // Each scope targets a specific condition or action object inside the talk
+ // tree and handles re-render + talkSync on every mutation.
+
+ var _talkScopeSeq = 0;
+
+ function talkCondScope(nodeKey, optIdx) {
+ var token = 'tk' + (++_talkScopeSeq);
+ return ie_registerScope({
+ getCond: function() {
+ var t = talkTarget(nodeKey, optIdx);
+ return t && t.condition || null;
+ },
+ setCond: function(c) {
+ var t = talkTarget(nodeKey, optIdx);
+ if (!t) return;
+ if (c) t.condition = c; else delete t.condition;
+ },
+ onChange: function() {
+ talkSync();
+ renderTalkTree(window._talkData);
+ }
+ }, token);
+ }
+
+ function talkActionScope(nodeKey, optIdx) {
+ var token = 'tk' + (++_talkScopeSeq);
+ return ie_registerScope({
+ getStep: function() {
+ var t = talkTarget(nodeKey, optIdx);
+ return t && t.action || {};
+ },
+ setStep: function(s) {
+ var t = talkTarget(nodeKey, optIdx);
+ if (!t) return;
+ if (s && Object.keys(s).length > 0) t.action = s; else delete t.action;
+ },
+ onChange: function() {
+ talkSync();
+ renderTalkTree(window._talkData);
+ }
+ }, token);
+ }
- function clauseTypeLabel(key) {
- for (var i = 0; i < CLAUSE_TYPES.length; i++) {
- if (CLAUSE_TYPES[i].key === key) return CLAUSE_TYPES[i].label;
+ function talkTarget(nodeKey, optIdx) {
+ var node = talkNodes() && talkNodes()[nodeKey];
+ if (!node) return null;
+ if (optIdx !== null) {
+ if (!node.options || !node.options[optIdx]) return null;
+ return node.options[optIdx];
}
- return key;
+ return node;
}
- function clauseTypeHasValue(key) {
- for (var i = 0; i < CLAUSE_TYPES.length; i++) {
- if (CLAUSE_TYPES[i].key === key) return CLAUSE_TYPES[i].hasValue;
- }
- return false;
+ // ---- Render a single action step for a talk node or option ----
+
+ function talkRenderActionStep(nodeKey, optIdx) {
+ var t = talkTarget(nodeKey, optIdx);
+ var step = (t && t.action) || {};
+ var scopeToken = talkActionScope(nodeKey, optIdx);
+ var h = '
';
+ h += ie_renderMessages(step, '', 0, 0, '');
+ h += ie_renderAct(step, '', 0, 0, '');
+ h += '
' + ie_renderStepAddbar(step, '', 0, 0, '') + '
';
+ h += '
';
+ return h;
}
- // Extract clauses from a condition object
- function extractClauses(cond) {
- if (!cond || typeof cond !== 'object' || Array.isArray(cond)) return { mode: 'none', clauses: [], groupKey: null };
- if (cond.all_of && Array.isArray(cond.all_of) && cond.all_of.length > 0) {
- return { mode: 'multi', clauses: cond.all_of.slice(), groupKey: 'all_of' };
- }
- if (cond.any_of && Array.isArray(cond.any_of) && cond.any_of.length > 0) {
- return { mode: 'multi', clauses: cond.any_of.slice(), groupKey: 'any_of' };
- }
- // Single clause from flat fields
- for (var i = 0; i < CLAUSE_TYPES.length; i++) {
- if (cond[CLAUSE_TYPES[i].key] !== undefined) {
- var c = {};
- c[CLAUSE_TYPES[i].key] = cond[CLAUSE_TYPES[i].key];
- if (cond.not) c.not = cond.not;
- if (cond.value !== undefined) c.value = cond.value;
- if (cond.has_item && CLAUSE_TYPES[i].key !== 'has_item') c.has_item = cond.has_item;
- if (cond.player_flag && CLAUSE_TYPES[i].key !== 'player_flag') c.player_flag = cond.player_flag;
- if (cond.flag && CLAUSE_TYPES[i].key !== 'flag') c.flag = cond.flag;
- return { mode: 'single', clauses: [c], groupKey: null };
- }
- }
- // Check for has_item or min_credits as the only key
- for (var j = 0; j < CLAUSE_TYPES.length; j++) {
- var ck = CLAUSE_TYPES[j].key;
- if (cond[ck] !== undefined) {
- var cc = {};
- cc[ck] = cond[ck];
- if (cond.not) cc.not = cond.not;
- if (cond.value !== undefined) cc.value = cond.value;
- return { mode: 'single', clauses: [cc], groupKey: null };
- }
- }
- return { mode: 'none', clauses: [], groupKey: null };
+ // ---- talk cond/act add/remove helpers (called from onclick data-action) ----
+
+ function talkAddNodeCond(nodeKey) {
+ var node = talkNodes() && talkNodes()[nodeKey];
+ if (!node || (node.condition && typeof node.condition === 'object')) return;
+ node.condition = {all_of: []};
+ renderTalkTree(window._talkData);
+ talkSync();
}
- // Determine the type key for a clause object
- function clauseObjType(clause) {
- for (var i = 0; i < CLAUSE_TYPES.length; i++) {
- var k = CLAUSE_TYPES[i].key;
- if (clause[k] !== undefined) return k;
- }
- return null;
+ function talkRemoveNodeCond(nodeKey) {
+ var node = talkNodes() && talkNodes()[nodeKey];
+ if (!node) return;
+ delete node.condition;
+ renderTalkTree(window._talkData);
+ talkSync();
}
- // Build a full condition object from clauses
- function buildCondition(clauses, groupKey) {
- if (!clauses || clauses.length === 0) return null;
- if (clauses.length === 1) {
- var flat = {};
- var c = clauses[0];
- for (var k in c) { if (c[k] !== undefined) flat[k] = c[k]; }
- return flat;
- }
- var cond = {};
- cond[groupKey || 'all_of'] = clauses.map(function(c) {
- var sub = {};
- for (var k in c) { if (c[k] !== undefined) sub[k] = c[k]; }
- return sub;
- });
- return cond;
+ function talkAddNodeAct(nodeKey) {
+ var node = talkNodes() && talkNodes()[nodeKey];
+ if (!node || (node.action && typeof node.action === 'object')) return;
+ node.action = {};
+ renderTalkTree(window._talkData);
+ talkSync();
}
- // ---- Action field definitions ----
+ function talkRemoveNodeAct(nodeKey) {
+ var node = talkNodes() && talkNodes()[nodeKey];
+ if (!node) return;
+ delete node.action;
+ renderTalkTree(window._talkData);
+ talkSync();
+ }
+
+ function talkAddOptionCond(nodeKey, optIdx) {
+ var t = talkTarget(nodeKey, optIdx);
+ if (!t || (t.condition && typeof t.condition === 'object')) return;
+ t.condition = {all_of: []};
+ renderTalkTree(window._talkData);
+ talkSync();
+ }
+
+ function talkRemoveOptionCond(nodeKey, optIdx) {
+ var t = talkTarget(nodeKey, optIdx);
+ if (!t) return;
+ delete t.condition;
+ renderTalkTree(window._talkData);
+ talkSync();
+ }
- var ACTION_FIELDS = [
- { key: 'give_item', label: 'Give Item', type: 'item_search' },
- { key: 'take_item', label: 'Take Item', type: 'item_search' },
- { key: 'credits', label: 'Credits (+/-)', type: 'number' },
- { key: 'teleport', label: 'TP Room', type: 'number' },
- { key: 'heal', label: 'Heal HP', type: 'number' },
- ];
+ function talkAddOptionAct(nodeKey, optIdx) {
+ var t = talkTarget(nodeKey, optIdx);
+ if (!t || (t.action && typeof t.action === 'object')) return;
+ t.action = {};
+ renderTalkTree(window._talkData);
+ talkSync();
+ }
- var ACTION_BOOLS = [];
+ function talkRemoveOptionAct(nodeKey, optIdx) {
+ var t = talkTarget(nodeKey, optIdx);
+ if (!t) return;
+ delete t.action;
+ renderTalkTree(window._talkData);
+ talkSync();
+ }
// ---- Event delegation ----
@@ -184,9 +225,6 @@
var nodeKey = el.getAttribute('data-nk');
var optIdx = el.getAttribute('data-oi');
- var subIdx = el.getAttribute('data-si');
- var subKey = el.getAttribute('data-sk');
- var condGroupKey = el.getAttribute('data-cgk');
switch (action) {
case 'delete-node':
@@ -207,32 +245,29 @@
case 'delete-message':
deleteMessageLine(nodeKey, parseInt(optIdx, 10));
break;
- case 'add-condition':
- toggleCondition(nodeKey, optIdx !== null ? parseInt(optIdx, 10) : null);
- break;
- case 'remove-condition':
- removeCondition(nodeKey, optIdx !== null ? parseInt(optIdx, 10) : null);
+ case 'add-node-cond':
+ talkAddNodeCond(nodeKey);
break;
- case 'add-action':
- addAction(nodeKey, optIdx !== null ? parseInt(optIdx, 10) : null);
+ case 'remove-node-cond':
+ talkRemoveNodeCond(nodeKey);
break;
- case 'remove-action':
- removeAction(nodeKey, optIdx !== null ? parseInt(optIdx, 10) : null);
+ case 'add-node-act':
+ talkAddNodeAct(nodeKey);
break;
- case 'add-clause':
- addClause(nodeKey, optIdx !== null ? parseInt(optIdx, 10) : null, el.getAttribute('data-ct'));
+ case 'remove-node-act':
+ talkRemoveNodeAct(nodeKey);
break;
- case 'remove-clause':
- removeClause(nodeKey, optIdx !== null ? parseInt(optIdx, 10) : null, parseInt(subIdx, 10));
+ case 'add-opt-cond':
+ talkAddOptionCond(nodeKey, parseInt(optIdx, 10));
break;
- case 'set-cond-group':
- setCondGroup(nodeKey, optIdx !== null ? parseInt(optIdx, 10) : null, condGroupKey);
+ case 'remove-opt-cond':
+ talkRemoveOptionCond(nodeKey, parseInt(optIdx, 10));
break;
- case 'add-kv':
- addKVPair(nodeKey, optIdx !== null ? parseInt(optIdx, 10) : null, el.getAttribute('data-kvk'));
+ case 'add-opt-act':
+ talkAddOptionAct(nodeKey, parseInt(optIdx, 10));
break;
- case 'remove-kv':
- removeKVPair(nodeKey, optIdx !== null ? parseInt(optIdx, 10) : null, el.getAttribute('data-kvk'), subKey);
+ case 'remove-opt-act':
+ talkRemoveOptionAct(nodeKey, parseInt(optIdx, 10));
break;
}
}
@@ -242,6 +277,13 @@
window.renderTalkTree = function(data) {
var container = document.getElementById('talktree');
if (!container) return;
+ // Purge old talk scopes before re-rendering (new ones will be registered).
+ if (window._ieScopes) {
+ Object.keys(window._ieScopes).forEach(function(k) {
+ if (k.indexOf('tk') === 0) delete window._ieScopes[k];
+ });
+ }
+ _talkScopeSeq = 0;
window._talkData = data;
window._talkOpenNodes = window._talkOpenNodes || new Set();
window._talkOpenNodes.add('start');
@@ -351,7 +393,6 @@
var html = '';
- // Header — fix: check for data-action before toggling
html += '';
- // Body
html += '
';
html += renderNodeBody(rootKey, node, nodes);
- html += '
'; // .talk-node-body
+ html += '
';
- // Children
var childKeys = [];
if (node.goto && nodes[node.goto]) childKeys.push(node.goto);
if (node.options) {
@@ -379,7 +418,7 @@
html += '';
}
- html += ''; // .talk-node
+ html += '';
return html;
}
@@ -388,59 +427,60 @@
return '';
}
- // ---- Node body: condition-gated content ----
+ // ---- Node body: condition, messages, options, action via intereditor ----
- // Track open node state across re-renders
window._talkOpenNodes = window._talkOpenNodes || new Set();
function renderNodeBody(nodeKey, node, nodes) {
- var ec = extractClauses(node.condition);
- var hasCond = ec.mode !== 'none' || (node.condition && typeof node.condition === 'object' && !Array.isArray(node.condition));
+ var hasCond = !!(node.condition && typeof node.condition === 'object');
+ var hasAct = !!(node.action && typeof node.action === 'object' && !Array.isArray(node.action));
var hasOpts = node.options && node.options.length > 0;
var html = '';
+ // Node condition (via intereditor)
if (hasCond) {
html += '';
html += '';
html += '
';
- html += renderConditionClauses(ec, nodeKey, null);
- html += '
';
- html += renderMessagesSection(nodeKey, node);
- html += renderOptionsSection(nodeKey, node);
- html += '
';
+ html += ie_renderCond(node.condition, '', 0, '', '', {scopeToken: talkCondScope(nodeKey, null), alwaysOuterAddbar: true});
+ html += '
';
+ html += '
';
+ } else {
+ html += '';
+ }
- var nodeAction = node.action;
- var actionPrefix = 'nodes.' + escAttr(nodeKey) + '.action';
- if (nodeAction && typeof nodeAction === 'object' && !Array.isArray(nodeAction)) {
- html += '';
- html += '';
- html += '
';
- html += renderActionForm(nodeAction, actionPrefix, nodeKey, null);
- html += '
';
- html += '
';
- } else {
- html += '';
- }
+ // Node messages (unchanged)
+ html += renderMessagesSection(nodeKey, node);
- html += '';
- html += renderGotoFallback(nodeKey, node, 'If condition fails, go to');
- html += ''; // .talk-cond-card-body
- html += ''; // .talk-cond-card
+ // Node options
+ html += renderOptionsSection(nodeKey, node);
+
+ html += '';
+
+ // Node action (via intereditor)
+ if (hasAct) {
+ html += '';
+ html += '';
+ html += '
';
+ html += talkRenderActionStep(nodeKey, null);
+ html += '
';
+ html += '
';
} else {
- html += '';
- html += renderMessagesSection(nodeKey, node);
- html += renderOptionsSection(nodeKey, node);
+ html += '';
+ }
+
+ // Goto fallback
+ if (hasCond) {
html += '';
- html += renderActionSection(nodeKey, null, node.action);
- if (!hasOpts) {
- html += renderGotoFallback(nodeKey, node, 'Auto-advance to');
- }
+ html += renderGotoFallback(nodeKey, node, 'If condition fails, go to');
+ } else if (!hasOpts) {
+ html += renderGotoFallback(nodeKey, node, 'Auto-advance to');
}
return html;
@@ -478,259 +518,6 @@
return html;
}
- // ---- Condition clauses ----
-
- function renderConditionClauses(ec, nodeKey, optIdx) {
- var clauses = ec.clauses;
- var groupKey = ec.groupKey || 'all_of';
- var html = '';
-
- // AND/OR mode selector (only when 2+ clauses)
- if (clauses.length >= 2) {
- html += '' +
- 'Combine with' +
- '' +
- '
';
- }
-
- // Clause rows
- for (var i = 0; i < clauses.length; i++) {
- var typeKey = clauseObjType(clauses[i]) || 'flag';
- var pathPrefix = clauses.length === 1 ?
- 'nodes.' + escAttr(nodeKey) + (optIdx !== null ? '.options.' + optIdx : '') + '.condition' :
- 'nodes.' + escAttr(nodeKey) + (optIdx !== null ? '.options.' + optIdx : '') + '.condition.' + groupKey + '.' + i;
- html += renderClauseRow(clauses[i], pathPrefix, typeKey, nodeKey, optIdx, i);
- }
-
- // Add clause buttons
- html += '';
- CLAUSE_TYPES.forEach(function(ct) {
- html += '';
- });
- html += '
';
-
- return html;
- }
-
- function renderClauseRow(clause, pathPrefix, typeKey, nodeKey, optIdx, clauseIdx) {
- var hasValue = clauseTypeHasValue(typeKey);
- var html = '';
- html += '' + esc(clauseTypeLabel(typeKey)) + '';
-
- if (typeKey === 'flag' || typeKey === 'player_flag') {
- var notLabel = (clause.value !== undefined && clause.value !== '') ? 'Not' : 'Not Exists';
- html += 'key:';
- html += '';
- html += '';
- html += 'val:';
- html += '';
- } else if (typeKey === 'has_item') {
- html += 'item:';
- html += '';
- html += '';
- } else if (typeKey === 'min_credits') {
- html += 'cr:';
- html += '';
- html += '';
- }
-
- html += '';
- html += '
';
- return html;
- }
-
- // ---- Clause add/remove/migrate ----
-
- function getConditionFor(nodeKey, optIdx) {
- var node = talkNodes()[nodeKey];
- if (!node) return null;
- if (optIdx !== null) {
- if (!node.options || !node.options[optIdx]) return null;
- return node.options[optIdx];
- }
- return node;
- }
-
- function condAddClause(target, typeKey, optIdx, nodeKey) {
- var ec = extractClauses(target.condition);
- var newClause = {};
- newClause[typeKey] = '';
- ec.clauses.push(newClause);
-
- if (ec.clauses.length === 1) {
- // First clause: use flat fields
- target.condition = buildCondition(ec.clauses, null);
- } else {
- ec.groupKey = ec.groupKey || 'all_of';
- target.condition = buildCondition(ec.clauses, ec.groupKey);
- }
- }
-
- function addClause(nodeKey, optIdx, typeKey) {
- var target = getConditionFor(nodeKey, optIdx);
- if (!target) return;
- if (!target.condition || typeof target.condition !== 'object' || Array.isArray(target.condition)) {
- target.condition = {};
- }
- condAddClause(target, typeKey, optIdx, nodeKey);
- renderTalkTree(window._talkData);
- talkSync();
- }
-
- function removeClause(nodeKey, optIdx, clauseIdx) {
- var target = getConditionFor(nodeKey, optIdx);
- if (!target) return;
- var ec = extractClauses(target.condition);
- if (clauseIdx < 0 || clauseIdx >= ec.clauses.length) return;
-
- ec.clauses.splice(clauseIdx, 1);
-
- if (ec.clauses.length === 0) {
- delete target.condition;
- } else {
- target.condition = buildCondition(ec.clauses, ec.groupKey);
- }
- renderTalkTree(window._talkData);
- talkSync();
- }
-
- function setCondGroup(nodeKey, optIdx, newGroupKey) {
- var target = getConditionFor(nodeKey, optIdx);
- if (!target) return;
- var ec = extractClauses(target.condition);
- if (ec.clauses.length < 2) return;
- ec.groupKey = newGroupKey;
- target.condition = buildCondition(ec.clauses, ec.groupKey);
- renderTalkTree(window._talkData);
- talkSync();
- }
-
- // Called from onchange on the AND/OR select
- window.talkCondGroupChange = function(select) {
- var nodeKey = select.getAttribute('data-nk');
- var optIdx = select.getAttribute('data-oi');
- setCondGroup(nodeKey, optIdx !== null ? parseInt(optIdx, 10) : null, select.value);
- };
-
- // ---- Add/Remove whole Condition ----
-
- function toggleCondition(nodeKey, optIdx) {
- var target = getConditionFor(nodeKey, optIdx);
- if (!target) return;
- if (target.condition && typeof target.condition === 'object' && !Array.isArray(target.condition)) {
- delete target.condition;
- } else {
- target.condition = {};
- if (optIdx !== null) {
- window._talkAutoOpenDetails = new Set();
- window._talkAutoOpenDetails.add('cond_' + optIdx + '_' + escAttr(nodeKey));
- }
- }
- renderTalkTree(window._talkData);
- talkSync();
- }
-
- function removeCondition(nodeKey, optIdx) {
- var target = getConditionFor(nodeKey, optIdx);
- if (!target) return;
- delete target.condition;
- renderTalkTree(window._talkData);
- talkSync();
- }
-
- // ---- Action editor ----
-
- function renderActionSection(nodeKey, optIdx, action) {
- var prefix = optIdx !== null ? 'nodes.' + escAttr(nodeKey) + '.options.' + optIdx + '.action' : 'nodes.' + escAttr(nodeKey) + '.action';
- var hasAct = action && typeof action === 'object' && !Array.isArray(action);
-
- if (!hasAct) {
- return '' +
- '' +
- '
';
- }
-
- return '' +
- '' +
- renderActionForm(action, prefix, nodeKey, optIdx) +
- '
';
- }
-
- function renderActionForm(action, prefix, nodeKey, optIdx) {
- var html = '';
-
- html += '
';
- ACTION_FIELDS.forEach(function(f) {
- var fpath = prefix + '.' + f.key;
- var val = (action && action[f.key] !== undefined) ? action[f.key] : '';
- if (f.type === 'item_search') {
- html += '
' +
- '
' + esc(f.label) + '' +
- '
' +
- '
';
- } else {
- var narrowStyle = (f.key === 'teleport' || f.key === 'heal') ? ' style="max-width:70px"' : '';
- html += '
' +
- '' + esc(f.label) + '' +
- '' +
- '
';
- }
- });
- html += '
';
-
- html += '
';
- ACTION_BOOLS.forEach(function(b) {
- html += '';
- });
- html += '
';
-
- html += renderKVEditor(action, prefix, 'set_flags', 'Set Flags', nodeKey, optIdx);
- html += renderKVEditor(action, prefix, 'set_player_flags', 'Set Player Flags', nodeKey, optIdx);
-
- html += '
'; // .talk-action-block
- return html;
- }
-
- function renderKVEditor(action, prefix, key, label, nodeKey, optIdx) {
- var kv = (action && action[key]) || {};
- var keys = Object.keys(kv);
- var fcID = prefix.replace(/\./g, '_') + '_' + key;
- var addKeyID = 'kvAddKey_' + fcID;
- var nkAttr = escAttr(nodeKey);
- var oiAttr = optIdx !== null ? ' data-oi="' + optIdx + '"' : '';
- var html = '';
- return html;
- }
-
// ---- Options section ----
function renderOptionsSection(nodeKey, node) {
@@ -747,43 +534,42 @@
html += '';
if (opt.condition && typeof opt.condition === 'object' && !Array.isArray(opt.condition)) {
- html += 'cond';
+ html += 'cond';
} else {
- html += '';
+ html += '';
}
if (opt.action) {
- html += 'act';
+ html += 'act';
} else {
- html += '';
+ html += '';
}
html += '
';
html += '';
html += '';
+ // Option condition detail
if (opt.condition && typeof opt.condition === 'object' && !Array.isArray(opt.condition)) {
- var condDetailKey = 'cond_' + idx + '_' + escAttr(nodeKey);
- var condOpen = window._talkAutoOpenDetails && window._talkAutoOpenDetails.has(condDetailKey) ? 'block' : 'none';
- html += '';
+ html += '
';
html += '
';
- html += '';
+ html += '';
html += '
';
- html += renderConditionClauses(extractClauses(opt.condition), nodeKey, idx);
+ html += ie_renderCond(opt.condition, '', 0, '', '', {scopeToken: talkCondScope(nodeKey, idx), alwaysOuterAddbar: true});
html += '
';
html += '
';
html += '
';
}
+
+ // Option action detail
if (opt.action) {
- var actDetailKey = 'act_' + idx + '_' + escAttr(nodeKey);
- var actOpen = window._talkAutoOpenDetails && window._talkAutoOpenDetails.has(actDetailKey) ? 'block' : 'none';
- html += '
';
+ html += '
';
html += '
';
html += '';
+ 'Action (when this option is chosen)' +
+ '
';
html += '
';
- html += renderActionForm(opt.action, 'nodes.' + escAttr(nodeKey) + '.options.' + idx + '.action', nodeKey, idx);
+ html += talkRenderActionStep(nodeKey, idx);
html += '
';
html += '
';
html += '
';
@@ -801,16 +587,10 @@
'
' +
'
';
- html += ''; // .talk-options
+ html += '';
return html;
}
- window.toggleOptionDetail = function(e, type, idx, nodeKey) {
- e.stopPropagation();
- var el = document.getElementById('optDetail_' + type + '_' + idx + '_' + nodeKey);
- if (el) el.style.display = el.style.display === 'none' ? 'block' : 'none';
- };
-
window.talkToggleNode = function(nodeKey, nodeEl) {
var open = nodeEl.classList.toggle('open');
if (open) {
@@ -867,98 +647,6 @@
talkSync();
}
- // ---- Add/Remove Action ----
-
- function addAction(nodeKey, optIdx) {
- var target = getConditionFor(nodeKey, optIdx);
- if (!target) return;
- target.action = {};
- if (optIdx !== null) {
- window._talkAutoOpenDetails = new Set();
- window._talkAutoOpenDetails.add('act_' + optIdx + '_' + escAttr(nodeKey));
- }
- renderTalkTree(window._talkData);
- talkSync();
- }
-
- function removeAction(nodeKey, optIdx) {
- var target = getConditionFor(nodeKey, optIdx);
- if (!target) return;
- delete target.action;
- renderTalkTree(window._talkData);
- talkSync();
- }
-
- // ---- Add/Remove KV Pairs (set_flags/set_player_flags) ----
-
- function kvPath(nodeKey, optIdx, kvKey, flagKey) {
- var base = optIdx !== null ?
- 'nodes.' + nodeKey + '.options.' + optIdx + '.action.' + kvKey :
- 'nodes.' + nodeKey + '.action.' + kvKey;
- return flagKey ? base + '.' + flagKey : base;
- }
-
- function addKVPair(nodeKey, optIdx, kvKey) {
- var prefixBase = optIdx !== null ?
- 'nodes.' + nodeKey + '.options.' + optIdx + '.action' :
- 'nodes.' + nodeKey + '.action';
- var fcID = prefixBase.replace(/\./g, '_') + '_' + kvKey;
- var addKeyID = 'kvAddKey_' + fcID;
- var input = document.getElementById(addKeyID);
- var key = input ? input.value.trim() : '';
- if (!key) return;
- var valInput = document.getElementById(addKeyID + '_val');
- var rawVal = valInput ? valInput.value.trim() : 'true';
- var val = rawVal === 'true' ? true : rawVal === 'false' ? false : rawVal;
- var numVal = parseFloat(rawVal);
- if (!isNaN(numVal) && String(numVal) === rawVal) val = numVal;
- var basePath = kvPath(nodeKey, optIdx, kvKey);
- var obj = talkGet(basePath);
- if (!obj || typeof obj !== 'object') {
- talkSet(basePath, {});
- obj = talkGet(basePath);
- }
- if (obj[key] !== undefined) { notify('Flag "' + key + '" already exists.', 'error'); return; }
- obj[key] = val;
- renderTalkTree(window._talkData);
- talkSync();
- }
-
- function removeKVPair(nodeKey, optIdx, kvKey, flagKey) {
- var fullPath = kvPath(nodeKey, optIdx, kvKey, flagKey);
- talkDel(fullPath);
- var basePath = kvPath(nodeKey, optIdx, kvKey);
- var obj = talkGet(basePath);
- if (obj && typeof obj === 'object' && Object.keys(obj).length === 0) {
- talkDel(basePath);
- }
- renderTalkTree(window._talkData);
- talkSync();
- }
-
- window.talkRenameKV = function(el) {
- var nodeKey = el.getAttribute('data-nk');
- var optIdxStr = el.getAttribute('data-oi');
- var optIdx = optIdxStr !== null ? parseInt(optIdxStr, 10) : null;
- var kvKey = el.getAttribute('data-kvk');
- var oldKey = el.getAttribute('data-oldkey');
- var newKey = el.value.trim();
- if (!newKey || newKey === oldKey) return;
- var basePath = optIdx !== null ?
- 'nodes.' + nodeKey + '.options.' + optIdx + '.action.' + kvKey :
- 'nodes.' + nodeKey + '.action.' + kvKey;
- var obj = talkGet(basePath);
- if (!obj || obj[newKey] !== undefined) {
- el.value = oldKey;
- if (obj && obj[newKey] !== undefined) notify('Key "' + newKey + '" already exists.', 'error');
- return;
- }
- obj[newKey] = obj[oldKey];
- delete obj[oldKey];
- renderTalkTree(window._talkData);
- talkSync();
- };
-
// ---- Global event listeners ----
document.addEventListener('input', function(e) {
@@ -1004,15 +692,6 @@
addMessageLine(nk);
return;
}
-
- if (el.id && el.id.startsWith('kvAddKey_')) {
- e.preventDefault();
- var nk = el.getAttribute('data-nk');
- var oi = el.getAttribute('data-oi');
- var kvk = el.getAttribute('data-kvk');
- addKVPair(nk, oi !== null ? parseInt(oi, 10) : null, kvk);
- return;
- }
});
})();
--
cgit v1.2.3