Xstoryplayer [updated] -
// apply a choice by target id makeChoice(targetId) if (!targetId) return false; // check if target exists in graph or fallback let targetNode = this.graph[targetId]; if (!targetNode) // if invalid, maybe use fallback as new node, but also preserve history targetNode = this.fallback; targetId = targetNode.id; // push current node into history before transition this.history.push(this.currentNodeId); this.currentNodeId = targetId; this._notify(); return true;
/* main player container */ .story-player max-width: 800px; width: 100%; background: rgba(18, 25, 45, 0.75); backdrop-filter: blur(12px); border-radius: 2.5rem; box-shadow: 0 25px 45px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(255, 255, 255, 0.08); overflow: hidden; transition: all 0.2s ease; xstoryplayer
window.addEventListener("keydown", handleKeyboard); // apply a choice by target id makeChoice(targetId) if (
// generate choices if (currentNode.choices && currentNode.choices.length > 0) choicesContainer.innerHTML = ""; currentNode.choices.forEach((choice, idx) => const btn = document.createElement("button"); btn.className = "choice-btn"; btn.textContent = choice.text; // store target id btn.dataset.target = choice.targetId; btn.addEventListener("click", (e) => e.stopPropagation(); const target = btn.dataset.target; if (target) player.makeChoice(target); ); choicesContainer.appendChild(btn); ); else // ending screen: special restart hint and maybe extra flair choicesContainer.innerHTML = ` <button class="choice-btn" id="restartFromEnding">✨ Start a new legend ✨</button> `; const restartEndBtn = document.getElementById("restartFromEnding"); if (restartEndBtn) restartEndBtn.addEventListener("click", () => player.reset(); ); if (!targetNode) // if invalid
// subscribe to player updates player.subscribe((updatedPlayer) => fullUpdate(updatedPlayer); );
// -------- Story Player State ---------- class StoryPlayer constructor(storyGraph, startId, fallbackNode) this.graph = storyGraph; this.startId = startId; this.fallback = fallbackNode; this.history = []; // stack of visited node IDs (strings) this.currentNodeId = startId; this.statsListeners = [];
// returns true if current node is ending (no choices) isEnding() node.choices.length === 0;