about summary refs log tree commit diff stats
path: root/html/story-teller
diff options
context:
space:
mode:
authorelioat <elioat@tilde.institute>2024-12-01 15:00:37 -0500
committerelioat <elioat@tilde.institute>2024-12-01 15:00:37 -0500
commitecff6d35e51ee35dfb34df03acdc9ceb244f7204 (patch)
treefb4411e2ba51b30b728fa720964e673607ece91b /html/story-teller
parent1768e1fd0ddc4a059869caa8ff4b08b5734e3982 (diff)
downloadtour-ecff6d35e51ee35dfb34df03acdc9ceb244f7204.tar.gz
*
Diffstat (limited to 'html/story-teller')
-rw-r--r--html/story-teller/index.html13
-rw-r--r--html/story-teller/js/game.js47
-rw-r--r--html/story-teller/js/renderer.js44
-rw-r--r--html/story-teller/js/scenes.js37
-rw-r--r--html/story-teller/js/state.js43
-rw-r--r--html/story-teller/styles.css8
6 files changed, 192 insertions, 0 deletions
diff --git a/html/story-teller/index.html b/html/story-teller/index.html
new file mode 100644
index 0000000..edd44c2
--- /dev/null
+++ b/html/story-teller/index.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="UTF-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1.0">
+  <title>Choice-based Game</title>
+  <link rel="stylesheet" href="./styles.css">
+</head>
+<body>
+  <div id="app"></div>
+  <script type="module" src="./js/game.js"></script>
+</body>
+</html>
diff --git a/html/story-teller/js/game.js b/html/story-teller/js/game.js
new file mode 100644
index 0000000..d3e8144
--- /dev/null
+++ b/html/story-teller/js/game.js
@@ -0,0 +1,47 @@
+import { createGameState, saveGameState, loadGameState, resetGameState } from './state.js';
+import { renderScene } from './renderer.js';
+
+let gameState = loadGameState(); // Load state on initialization
+
+const updateAndRender = (newState) => {
+  Object.assign(gameState, newState);
+  saveGameState(gameState); // Save automatically after each update
+  renderScene(gameState, updateAndRender);
+};
+
+document.addEventListener('DOMContentLoaded', () => {
+  const app = document.getElementById('app');
+
+  app.innerHTML = '';
+
+  const controls = document.createElement('div');
+  controls.id = 'controls';
+
+  const saveButton = document.createElement('button');
+  saveButton.textContent = 'Save Progress';
+  saveButton.onclick = () => saveGameState(gameState);
+
+  const loadButton = document.createElement('button');
+  loadButton.textContent = 'Load Progress';
+  loadButton.onclick = () => {
+    const loadedState = loadGameState();
+    updateAndRender(loadedState);
+  };
+
+  const resetButton = document.createElement('button');
+  resetButton.textContent = 'Reset Game';
+  resetButton.onclick = () => {
+    if (confirm('Are you sure you want to reset the game? This will erase all progress.')) {
+      gameState = resetGameState(); // Clear state and reset game
+      updateAndRender(gameState);
+    }
+  };
+
+  controls.appendChild(saveButton);
+  controls.appendChild(loadButton);
+  controls.appendChild(resetButton);
+
+  app.appendChild(controls);
+
+  updateAndRender(gameState);
+});
diff --git a/html/story-teller/js/renderer.js b/html/story-teller/js/renderer.js
new file mode 100644
index 0000000..82ab004
--- /dev/null
+++ b/html/story-teller/js/renderer.js
@@ -0,0 +1,44 @@
+import { scenes } from './scenes.js';
+
+export const renderScene = (state, updateState) => {
+  const scene = scenes[state.currentScene];
+  if (!scene) throw new Error(`Scene "${state.currentScene}" not found`);
+
+  const app = document.getElementById('app');
+  app.innerHTML = '';
+
+  const description = document.createElement('p');
+  description.textContent = scene.description;
+  app.appendChild(description);
+
+  const inventory = document.createElement('div');
+  inventory.textContent = `Inventory: ${Array.from(state.inventory).join(', ') || 'None'}`;
+  app.appendChild(inventory);
+
+  const log = document.createElement('div');
+  log.innerHTML = `<strong>Action Log:</strong><br>${state.actionLog.join('<br>')}`;
+  app.appendChild(log);
+
+  scene.choices.forEach((choice) => {
+    if (choice.condition && !choice.condition(state)) {
+      return; // Skip choices that don't meet their conditions
+    }
+
+    const button = document.createElement('button');
+    button.textContent = choice.text;
+    button.onclick = () => {
+      state.actionLog.push(`You chose: "${choice.text}"`);
+      if (choice.action) choice.action(state); // Perform any action tied to the choice
+
+      // Consume items if specified
+      if (choice.consume) {
+        choice.consume.forEach((item) => state.inventory.delete(item));
+        state.actionLog.push(`You used: ${choice.consume.join(', ')}`);
+      }
+
+      if (choice.nextScene) state.currentScene = choice.nextScene;
+      updateState(state); // Re-render after state update
+    };
+    app.appendChild(button);
+  });
+};
diff --git a/html/story-teller/js/scenes.js b/html/story-teller/js/scenes.js
new file mode 100644
index 0000000..7b8db35
--- /dev/null
+++ b/html/story-teller/js/scenes.js
@@ -0,0 +1,37 @@
+export const scenes = {
+    start: {
+      description: "You are standing in a dark cave. There's a faint glimmer in the distance, and a torch on the ground.",
+      choices: [
+        { text: "Explore further", nextScene: "deepCave" },
+        { text: "Pick up the torch", action: (state) => state.inventory.add("torch") },
+        { 
+          text: "Use a torch to illuminate the cave", 
+          nextScene: "litCave", 
+          condition: (state) => state.inventory.has("torch"), 
+          consume: ["torch"] 
+        },
+      ],
+    },
+    deepCave: {
+      description: "The darkness becomes overwhelming. You can't proceed further.",
+      choices: [
+        { text: "Go back", nextScene: "start" },
+      ],
+    },
+    litCave: {
+      description: "With the torchlight, you see glittering gemstones embedded in the walls.",
+      choices: [
+        { text: "Pick up the key", action: (state) => state.inventory.add("key") },
+        { text: "Collect a gemstone", action: (state) => state.inventory.add("gemstone") },
+        { text: "Go back", nextScene: "start" },
+        { text: "Go deeper into the cave", nextScene: "treasureRoom", condition: (state) => state.inventory.has("key") },
+      ],
+    },
+    treasureRoom: {
+      description: "You find a room filled with treasure. You are rich beyond your wildest dreams.",
+      choices: [
+        { text: "Swim in the treasure", nextScene: "treasureRoom" },
+      ],
+    },
+  };
+  
\ No newline at end of file
diff --git a/html/story-teller/js/state.js b/html/story-teller/js/state.js
new file mode 100644
index 0000000..e6c4fe7
--- /dev/null
+++ b/html/story-teller/js/state.js
@@ -0,0 +1,43 @@
+export const createGameState = () => ({
+    currentScene: 'start',
+    inventory: new Set(),
+    actionLog: [],
+    collectedItems: new Set(),
+  });
+  
+  
+  
+export const updateGameState = (state, updates) => ({
+    ...state,
+    ...updates,
+});
+
+const STORAGE_KEY = 'gameState';
+
+export const saveGameState = (state) => {
+  const stateToSave = {
+    currentScene: state.currentScene,
+    inventory: Array.from(state.inventory),
+    actionLog: state.actionLog,
+  };
+  localStorage.setItem(STORAGE_KEY, JSON.stringify(stateToSave));
+};
+
+export const loadGameState = () => {
+  const savedState = localStorage.getItem(STORAGE_KEY);
+  if (savedState) {
+    const parsedState = JSON.parse(savedState);
+    return {
+      currentScene: parsedState.currentScene,
+      inventory: new Set(parsedState.inventory),
+      actionLog: parsedState.actionLog,
+    };
+  }
+  return createGameState();
+};
+
+export const resetGameState = () => {
+    localStorage.removeItem(STORAGE_KEY);
+    return createGameState();
+  };
+  
\ No newline at end of file
diff --git a/html/story-teller/styles.css b/html/story-teller/styles.css
new file mode 100644
index 0000000..51852ba
--- /dev/null
+++ b/html/story-teller/styles.css
@@ -0,0 +1,8 @@
+body {
+    font-family: 'Roboto', sans-serif;
+    background-color: #f0f0f0;
+    margin: 0 auto;
+    display: block;
+    padding: 0;
+    max-width: 42em;
+}
\ No newline at end of file