// get story stats (depth / steps count) getStepCount() return this.history.length;
/* footer & micro-interactions */ .footer-note text-align: center; font-size: 0.7rem; padding: 0.8rem; color: #5e6f92; border-top: 1px solid rgba(255, 255, 255, 0.03); letter-spacing: 0.3px;
// provide a small easter egg: console info console.log("✨ xstoryplayer active — use number keys (1-9) for choices, Ctrl+Z undo, Ctrl+R restart"); </script> </body> </html>
/* immersive quote effect */ .quote-mark font-family: serif; font-size: 2rem; opacity: 0.3; margin-right: 6px; vertical-align: middle;
// helper: update stats display (node name / ending indicator) function updateMetaDisplay(playerInstance) const steps = playerInstance.getStepCount(); historyDepthSpan.textContent = steps; const currentNode = playerInstance.getCurrentNode(); let nodeTitle = currentNode.id === "start" ? "Prologue" : currentNode.id.replace(/_/g, ' '); if (playerInstance.isEnding()) nodeTitle = "★ ENDING: " + nodeTitle; else nodeTitle = "chapter · " + nodeTitle;
// returns true if current node is ending (no choices) isEnding() const node = this.getCurrentNode(); return !node.choices
// 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(); );
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"> <title>xstoryplayer | immersive story engine</title> <style> * margin: 0; padding: 0; box-sizing: border-box; user-select: none; /* prevent accidental text selection on UI */ body background: linear-gradient(145deg, #0a0f1e 0%, #0c1222 100%); min-height: 100vh; display: flex; justify-content: center; align-items: center; font-family: 'Segoe UI', 'Inter', system-ui, -apple-system, 'Playfair Display', serif; padding: 20px;
// get story stats (depth / steps count) getStepCount() return this.history.length;
/* footer & micro-interactions */ .footer-note text-align: center; font-size: 0.7rem; padding: 0.8rem; color: #5e6f92; border-top: 1px solid rgba(255, 255, 255, 0.03); letter-spacing: 0.3px;
// provide a small easter egg: console info console.log("✨ xstoryplayer active — use number keys (1-9) for choices, Ctrl+Z undo, Ctrl+R restart"); </script> </body> </html>
/* immersive quote effect */ .quote-mark font-family: serif; font-size: 2rem; opacity: 0.3; margin-right: 6px; vertical-align: middle;
// helper: update stats display (node name / ending indicator) function updateMetaDisplay(playerInstance) const steps = playerInstance.getStepCount(); historyDepthSpan.textContent = steps; const currentNode = playerInstance.getCurrentNode(); let nodeTitle = currentNode.id === "start" ? "Prologue" : currentNode.id.replace(/_/g, ' '); if (playerInstance.isEnding()) nodeTitle = "★ ENDING: " + nodeTitle; else nodeTitle = "chapter · " + nodeTitle;
// returns true if current node is ending (no choices) isEnding() const node = this.getCurrentNode(); return !node.choices
// 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(); );
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no"> <title>xstoryplayer | immersive story engine</title> <style> * margin: 0; padding: 0; box-sizing: border-box; user-select: none; /* prevent accidental text selection on UI */ body background: linear-gradient(145deg, #0a0f1e 0%, #0c1222 100%); min-height: 100vh; display: flex; justify-content: center; align-items: center; font-family: 'Segoe UI', 'Inter', system-ui, -apple-system, 'Playfair Display', serif; padding: 20px;