about summary refs log tree commit diff stats
path: root/html
diff options
context:
space:
mode:
Diffstat (limited to 'html')
-rw-r--r--html/broughlike/broughlike.js146
-rw-r--r--html/broughlike/config.js13
-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
8 files changed, 273 insertions, 78 deletions
diff --git a/html/broughlike/broughlike.js b/html/broughlike/broughlike.js
index 585ae66..87718f3 100644
--- a/html/broughlike/broughlike.js
+++ b/html/broughlike/broughlike.js
@@ -1,14 +1,4 @@
-import { CONFIG, COLORS } from './config.js';
-
-// FIXME: canvas, ctx, and tileSize are all regularly accessed. 
-// I'd like to refactor this to be more modular so that these can all be contained to the CONFIG object or something similar.
-let { ctx, canvas, tileSize } = initializeCanvasContext();
-function initializeCanvasContext() {
-    const canvas = document.getElementById('gameCanvas');
-    const ctx = canvas.getContext('2d');
-    let tileSize = canvas.width / CONFIG.GRID_SIZE;
-    return { ctx, canvas, tileSize };
-}
+import { CONFIG, COLORS, CANVAS } from './config.js';
 
 const DEBUG = false;
 
@@ -331,59 +321,59 @@ function isReachable(startX, startY, targetX, targetY) {
 }
 
 function drawGrid() {
-    ctx.clearRect(0, 0, canvas.width, canvas.height);
-    ctx.lineWidth = 2;
-    ctx.strokeStyle = COLORS.grid;
+    CANVAS.ctx.clearRect(0, 0, CANVAS.canvas.width, CANVAS.canvas.height);
+    CANVAS.ctx.lineWidth = 2;
+    CANVAS.ctx.strokeStyle = COLORS.grid;
     for (let row = 0; row < CONFIG.GRID_SIZE; row++) {
         for (let col = 0; col < CONFIG.GRID_SIZE; col++) {
-            ctx.strokeRect(col * tileSize, row * tileSize, tileSize, tileSize);
+            CANVAS.ctx.strokeRect(col * CANVAS.tileSize, row * CANVAS.tileSize, CANVAS.tileSize, CANVAS.tileSize);
         }
     }
 }
 
 function drawExit() {
-    const x = exit.x * tileSize + tileSize / 2;
-    const y = exit.y * tileSize + tileSize / 2;
-    ctx.beginPath();
-    ctx.moveTo(x, y - tileSize / 3);
-    ctx.lineTo(x + tileSize / 3, y + tileSize / 3);
-    ctx.lineTo(x - tileSize / 3, y + tileSize / 3);
-    ctx.closePath();
-    ctx.fillStyle = COLORS.exit;
-    ctx.fill();
+    const x = exit.x * CANVAS.tileSize + CANVAS.tileSize / 2;
+    const y = exit.y * CANVAS.tileSize + CANVAS.tileSize / 2;
+    CANVAS.ctx.beginPath();
+    CANVAS.ctx.moveTo(x, y - CANVAS.tileSize / 3);
+    CANVAS.ctx.lineTo(x + CANVAS.tileSize / 3, y + CANVAS.tileSize / 3);
+    CANVAS.ctx.lineTo(x - CANVAS.tileSize / 3, y + CANVAS.tileSize / 3);
+    CANVAS.ctx.closePath();
+    CANVAS.ctx.fillStyle = COLORS.exit;
+    CANVAS.ctx.fill();
 }
 
 function drawWalls() {
-    ctx.fillStyle = COLORS.walls;
+    CANVAS.ctx.fillStyle = COLORS.walls;
     walls.forEach(wall => {
-        ctx.fillRect(wall.x * tileSize, wall.y * tileSize, tileSize, tileSize);
+        CANVAS.ctx.fillRect(wall.x * CANVAS.tileSize, wall.y * CANVAS.tileSize, CANVAS.tileSize, CANVAS.tileSize);
     });
 }
 
 function drawItems() {
     items.forEach(item => {
-        const x = item.x * tileSize + tileSize / 2;
-        const y = item.y * tileSize + tileSize / 2;
-        ctx.fillStyle = item.type === 'diamond' ? COLORS.diamond : COLORS.pentagon;
-        ctx.beginPath();
+        const x = item.x * CANVAS.tileSize + CANVAS.tileSize / 2;
+        const y = item.y * CANVAS.tileSize + CANVAS.tileSize / 2;
+        CANVAS.ctx.fillStyle = item.type === 'diamond' ? COLORS.diamond : COLORS.pentagon;
+        CANVAS.ctx.beginPath();
         if (item.type === 'diamond') {
-            ctx.moveTo(x, y - tileSize / 4);
-            ctx.lineTo(x + tileSize / 4, y);
-            ctx.lineTo(x, y + tileSize / 4);
-            ctx.lineTo(x - tileSize / 4, y);
+            CANVAS.ctx.moveTo(x, y - CANVAS.tileSize / 4);
+            CANVAS.ctx.lineTo(x + CANVAS.tileSize / 4, y);
+            CANVAS.ctx.lineTo(x, y + CANVAS.tileSize / 4);
+            CANVAS.ctx.lineTo(x - CANVAS.tileSize / 4, y);
         } else {
             const sides = 5;
-            const radius = tileSize / 4;
+            const radius = CANVAS.tileSize / 4;
             for (let i = 0; i < sides; i++) {
                 const angle = (i * 2 * Math.PI) / sides - Math.PI / 2;
                 const pointX = x + radius * Math.cos(angle);
                 const pointY = y + radius * Math.sin(angle);
-                if (i === 0) ctx.moveTo(pointX, pointY);
-                else ctx.lineTo(pointX, pointY);
+                if (i === 0) CANVAS.ctx.moveTo(pointX, pointY);
+                else CANVAS.ctx.lineTo(pointX, pointY);
             }
         }
-        ctx.closePath();
-        ctx.fill();
+        CANVAS.ctx.closePath();
+        CANVAS.ctx.fill();
     });
 }
 
@@ -391,54 +381,54 @@ function drawCharacterBorder(x, y, radius, damageTaken) {
     const dashLength = 5;
     const gapLength = Math.max(1, damageTaken * 2); // More damage, larger gaps
 
-    ctx.lineWidth = 2;
-    ctx.strokeStyle = '#2d2d2d';
-    ctx.setLineDash([dashLength, gapLength]);
-    ctx.beginPath();
-    ctx.arc(x, y, radius, 0, 2 * Math.PI);
-    ctx.stroke();
-    ctx.setLineDash([]); // Reset to a solid line
+    CANVAS.ctx.lineWidth = 2;
+    CANVAS.ctx.strokeStyle = '#2d2d2d';
+    CANVAS.ctx.setLineDash([dashLength, gapLength]);
+    CANVAS.ctx.beginPath();
+    CANVAS.ctx.arc(x, y, radius, 0, 2 * Math.PI);
+    CANVAS.ctx.stroke();
+    CANVAS.ctx.setLineDash([]); // Reset to a solid line
 }
 
 function drawEnemies() {
     enemies.forEach(enemy => {
-        const x = enemy.x * tileSize + tileSize / 2;
-        const y = enemy.y * tileSize + tileSize / 2;
+        const x = enemy.x * CANVAS.tileSize + CANVAS.tileSize / 2;
+        const y = enemy.y * CANVAS.tileSize + CANVAS.tileSize / 2;
         const opacity = enemy.health / CONFIG.MAX_ENEMY_HEALTH; // Opacity based on health
-        const radius = tileSize / 3;
+        const radius = CANVAS.tileSize / 3;
         const damageTaken = CONFIG.MAX_ENEMY_HEALTH - enemy.health;
 
-        ctx.beginPath();
-        ctx.arc(x, y, radius, 0, 2 * Math.PI);
-        ctx.fillStyle = `${enemy.color}${opacity})`;
-        ctx.fill();
+        CANVAS.ctx.beginPath();
+        CANVAS.ctx.arc(x, y, radius, 0, 2 * Math.PI);
+        CANVAS.ctx.fillStyle = `${enemy.color}${opacity})`;
+        CANVAS.ctx.fill();
 
         drawCharacterBorder(x, y, radius, damageTaken);
     });
 }
 
 function drawPlayer() {
-    const x = player.x * tileSize + tileSize / 2;
-    const y = player.y * tileSize + tileSize / 2;
-    const radius = tileSize / 3;
+    const x = player.x * CANVAS.tileSize + CANVAS.tileSize / 2;
+    const y = player.y * CANVAS.tileSize + CANVAS.tileSize / 2;
+    const radius = CANVAS.tileSize / 3;
     const playerOpacity = player.health / CONFIG.PLAYER_HEALTH; // Opacity based on health
-    ctx.beginPath();
+    CANVAS.ctx.beginPath();
     for (let i = 0; i < 6; i++) {
         const angle = (Math.PI / 3) * i;
         const hexX = x + radius * Math.cos(angle);
         const hexY = y + radius * Math.sin(angle);
         if (i === 0) {
-            ctx.moveTo(hexX, hexY);
+            CANVAS.ctx.moveTo(hexX, hexY);
         } else {
-            ctx.lineTo(hexX, hexY);
+            CANVAS.ctx.lineTo(hexX, hexY);
         }
     }
-    ctx.closePath();
-    ctx.fillStyle = `${COLORS.player}${playerOpacity})`;
-    ctx.fill();
-    ctx.lineWidth = 2;
-    ctx.strokeStyle = '#2d2d2d';
-    ctx.stroke();
+    CANVAS.ctx.closePath();
+    CANVAS.ctx.fillStyle = `${COLORS.player}${playerOpacity})`;
+    CANVAS.ctx.fill();
+    CANVAS.ctx.lineWidth = 2;
+    CANVAS.ctx.strokeStyle = '#2d2d2d';
+    CANVAS.ctx.stroke();
 }
 
 function drawCombatDots() {
@@ -446,11 +436,11 @@ function drawCombatDots() {
         const [cellX, cellY] = key.split(',').map(Number);
         const dots = combatDots[key];
         dots.forEach(dot => {
-            ctx.beginPath();
-            ctx.arc(cellX * tileSize + dot.x, cellY * tileSize + dot.y, 2, 0, Math.PI * 2);
-            ctx.fillStyle = dot.color;
-            ctx.fill();
-            ctx.closePath();
+            CANVAS.ctx.beginPath();
+            CANVAS.ctx.arc(cellX * CANVAS.tileSize + dot.x, cellY * CANVAS.tileSize + dot.y, 2, 0, Math.PI * 2);
+            CANVAS.ctx.fillStyle = dot.color;
+            CANVAS.ctx.fill();
+            CANVAS.ctx.closePath();
         });
     }
 }
@@ -492,8 +482,8 @@ function addCombatDots(x, y, color) {
             }
             for (let i = 0; i < CONFIG.DOTS_PER_HIT; i++) {
                 combatDots[key].push({
-                    x: Math.random() * tileSize,
-                    y: Math.random() * tileSize,
+                    x: Math.random() * CANVAS.tileSize,
+                    y: Math.random() * CANVAS.tileSize,
                     color: color
                 });
             }
@@ -749,13 +739,13 @@ document.addEventListener('keydown', (e) => {
 let touchStartX = 0;
 let touchStartY = 0;
 
-canvas.addEventListener('touchstart', (e) => {
+CANVAS.canvas.addEventListener('touchstart', (e) => {
     e.preventDefault(); // Prevent scrolling on touchstart
     touchStartX = e.touches[0].clientX;
     touchStartY = e.touches[0].clientY;
 });
 
-canvas.addEventListener('touchend', (e) => {
+CANVAS.canvas.addEventListener('touchend', (e) => {
     e.preventDefault(); // Prevent scrolling on touchend
     const touchEndX = e.changedTouches[0].clientX;
     const touchEndY = e.changedTouches[0].clientY;
@@ -782,10 +772,10 @@ canvas.addEventListener('touchend', (e) => {
 }, { passive: false }); // TIL you can use passive set to false to help make preventDefault actually work? Feels like superstition
 
 const resizeCanvas = () => {
-    const rect = canvas.getBoundingClientRect();
-    canvas.width = rect.width;
-    canvas.height = rect.height;
-    tileSize = canvas.width / CONFIG.GRID_SIZE; // Update tile size based on canvas dimensions
+    const rect = CANVAS.canvas.getBoundingClientRect();
+    CANVAS.canvas.width = rect.width;
+    CANVAS.canvas.height = rect.height;
+    CANVAS.tileSize = CANVAS.updateTileSize(); // Update tile size based on canvas dimensions
     render();
 };
 
diff --git a/html/broughlike/config.js b/html/broughlike/config.js
index bd95035..8776b2f 100644
--- a/html/broughlike/config.js
+++ b/html/broughlike/config.js
@@ -30,3 +30,16 @@ export const CONFIG = {
     ITEMS_MAX: 3,
     DOTS_PER_HIT: 7
 };
+
+const canvas = document.getElementById('gameCanvas');
+const ctx = canvas.getContext('2d');
+const tileSize = canvas.width / CONFIG.GRID_SIZE;
+
+export const CANVAS = {
+    ctx,
+    canvas,
+    tileSize,
+    updateTileSize() {
+        return canvas.width / CONFIG.GRID_SIZE;
+    }
+};
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