-
-
-
-
-
-
-
-
-
+
+ Story
+
+
+
+
+
+ Data
+
+
+
+
+
+
+
+
+ Logic
+
+
+
+
+
+
+
+
- {nodes.length} node{nodes.length !== 1 ? "s" : ""}, {edges.length} edge
- {edges.length !== 1 ? "s" : ""}
+ {nodes.length} node{nodes.length !== 1 ? "s" : ""}, {edges.length} edge{edges.length !== 1 ? "s" : ""}
);
@@ -293,28 +292,27 @@ export function NarrativeFlowEditor() {
className="!border-zinc-700"
style={{ background: "#18181b" }}
nodeColor={(n) => {
- switch (n.type) {
- case "choice":
- return "#d97706"; // amber
- case "outcome":
- return "#10b981"; // emerald
- case "narrativeBeat":
- return "#3b82f6"; // blue
- case "wait":
- return "#facc15"; // yellow
- case "number":
- return "#a855f7"; // purple
- case "string":
- return "#14b8a6"; // teal
- case "random":
- return "#ec4899"; // pink
- case "boolean":
- return "#34d399"; // light green
- case "date":
- return "#06b6d4"; // cyan
- default:
- return "#6366f1"; // indigo
- }
+ const colors: Record
= {
+ narrativeBeat: "#3b82f6",
+ choice: "#d97706",
+ outcome: "#10b981",
+ wait: "#facc15",
+ number: "#a855f7",
+ string: "#14b8a6",
+ random: "#ec4899",
+ boolean: "#34d399",
+ date: "#06b6d4",
+ gameData: "#6366f1",
+ flag: "#f97316",
+ compare: "#64748b",
+ math: "#8b5cf6",
+ format: "#0ea5e9",
+ logic: "#84cc16",
+ not: "#ef4444",
+ branch: "#f43f5e",
+ sequence: "#d946ef",
+ };
+ return colors[n.type as string] ?? "#6366f1";
}}
/>
diff --git a/src/components/admin/narrative-flow/NodeEditDialog.tsx b/src/components/admin/narrative-flow/NodeEditDialog.tsx
index da9cac5..de8fadb 100644
--- a/src/components/admin/narrative-flow/NodeEditDialog.tsx
+++ b/src/components/admin/narrative-flow/NodeEditDialog.tsx
@@ -423,38 +423,207 @@ export const NodeEditDialog: FC = ({
);
}
+ case "gameData": {
+ const GAME_DATA_FIELDS = [
+ { value: "structure_health", label: "Structure Health" },
+ { value: "structure_storage", label: "Structure Storage" },
+ { value: "deliveries_pending", label: "Pending Deliveries" },
+ { value: "deliveries_completed", label: "Completed Deliveries" },
+ { value: "resources_total", label: "Total Resources" },
+ { value: "faction_reputation", label: "Faction Reputation" },
+ { value: "player_count", label: "Player Count" },
+ { value: "tick_count", label: "Current Tick" },
+ ];
+ return (
+
+
+
+
+ {GAME_DATA_FIELDS.map((f) => (
+
+ ))}
+
+
+
+ );
+ }
+
+ case "flag": {
+ return (
+
+
+
+ updateField("flagName", e.target.value)}
+ placeholder="e.g. quest_started, boss_defeated"
+ className="dark:bg-zinc-800 dark:border-zinc-700 dark:text-zinc-100"
+ />
+
+
+ );
+ }
+
+ case "compare": {
+ const operators = [">", "<", "==", "!=", ">=", "<="];
+ return (
+
+
+
+
+ {operators.map((op) => (
+
+ ))}
+
+
+
+ );
+ }
+
+ case "math": {
+ const operators = ["+", "-", "*", "/"];
+ return (
+
+
+
+
+ {operators.map((op) => (
+
+ ))}
+
+
+
+ );
+ }
+
+ case "format": {
+ return (
+
+
+
+
updateField("template", e.target.value)}
+ placeholder="e.g. {0} items delivered to {1}"
+ className="dark:bg-zinc-800 dark:border-zinc-700 dark:text-zinc-100 font-mono"
+ />
+
+ Use {"{0}"}, {"{1}"}, etc. for connected data inputs.
+
+
+
+ );
+ }
+
+ case "logic": {
+ const operators = ["AND", "OR", "XOR"];
+ return (
+
+
+
+
+ {operators.map((op) => (
+
+ ))}
+
+
+
+ );
+ }
+
+ case "not":
+ case "branch":
+ case "sequence": {
+ return (
+
+ No configurable properties.
+
+ );
+ }
+
default:
return Unknown node type.
;
}
};
- const typeLabel =
- nodeType === "narrativeBeat"
- ? "Beat"
- : nodeType === "choice"
- ? "Choice"
- : nodeType === "outcome"
- ? "Outcome"
- : nodeType === "wait"
- ? "Wait"
- : nodeType === "number"
- ? "Number"
- : nodeType === "string"
- ? "String"
- : nodeType === "random"
- ? "Random"
- : nodeType === "boolean"
- ? "Boolean"
- : nodeType === "date"
- ? "Date"
- : "Node";
+ const typeLabel: Record = {
+ narrativeBeat: "Beat",
+ choice: "Choice",
+ outcome: "Outcome",
+ wait: "Wait",
+ number: "Number",
+ string: "String",
+ random: "Random",
+ boolean: "Boolean",
+ date: "Date",
+ gameData: "Game Data",
+ flag: "Flag",
+ compare: "Compare",
+ math: "Math",
+ format: "Format",
+ logic: "Logic",
+ not: "NOT",
+ branch: "Branch",
+ sequence: "Sequence",
+ };
+ const label = typeLabel[nodeType] ?? "Node";
return (