about summary refs log tree commit diff stats
path: root/js
diff options
context:
space:
mode:
authorelioat <elioat@tilde.institute>2024-07-07 00:01:23 -0400
committerelioat <elioat@tilde.institute>2024-07-07 00:01:23 -0400
commit83140c20ccbd7c251e45aa560de4b99475b975b5 (patch)
tree2ca10735211ce14fd003550b7b69cab12c7fb4c4 /js
parent5989722c24c3091984a95e5917426e4371e4752f (diff)
downloadtour-83140c20ccbd7c251e45aa560de4b99475b975b5.tar.gz
*
Diffstat (limited to 'js')
-rw-r--r--js/puzzle-dungeon/commandHandler.js152
-rw-r--r--js/puzzle-dungeon/game.js267
-rw-r--r--js/puzzle-dungeon/index.html28
-rw-r--r--js/puzzle-dungeon/parser.js15
4 files changed, 321 insertions, 141 deletions
diff --git a/js/puzzle-dungeon/commandHandler.js b/js/puzzle-dungeon/commandHandler.js
index 12140c2..b6a8246 100644
--- a/js/puzzle-dungeon/commandHandler.js
+++ b/js/puzzle-dungeon/commandHandler.js
@@ -1,28 +1,138 @@
-const createCommandHandler = () => {
-    const commands = {};
+// commandHandler.js
+import { player, grid, drawGrid, updatePlayerPosition, updatePlayerStatus, alertGameOver } from './game.js';
 
-    const registerCommand = (name, callback) => {
-        commands[name] = callback;
-    };
+let commandQueue = [];
+let processingCommands = false;
 
-    const executeCommand = (parsedCommand) => {
-        const { command, args } = parsedCommand;
-        if (commands[command]) {
-            commands[command](...args);
+export function handleCommand(command) {
+    const [action, itemOrSteps] = command.split(' ');
+
+    if (action === 'get' || action === 'use' || isMovementCommand(action)) {
+        commandQueue.push({ action, itemOrSteps });
+    }
+
+    if (!processingCommands) {
+        processCommandQueue();
+    }
+}
+
+function processCommandQueue() {
+    if (commandQueue.length === 0) {
+        processingCommands = false;
+        return;
+    }
+
+    processingCommands = true;
+    const { action, itemOrSteps } = commandQueue.shift();
+
+    if (action === 'get') {
+        handleGetCommand(itemOrSteps, processCommandQueue);
+    } else if (action === 'use') {
+        handleUseCommand(itemOrSteps, processCommandQueue);
+    } else if (isMovementCommand(action)) {
+        const steps = parseInt(itemOrSteps, 10) || 1;
+        handleMovement(action, steps, processCommandQueue);
+    }
+}
+
+function handleGetCommand(item, callback) {
+    const { x, y } = player.position;
+    const cell = grid[x][y];
+    if (cell && cell.type === item) {
+        player.inventory.push(cell.type);
+        grid[x][y] = null; // Remove the item from the grid
+        console.log(`Picked up ${item}`);
+        flashPlayer(callback);
+    } else {
+        player.health -= 1;
+        console.log(`Tried to get ${item} but it's not there, health is now ${player.health}`);
+        if (player.health <= 0) {
+            alertGameOver();
+            return;
+        }
+        flashPlayer(callback);
+    }
+}
+
+function handleUseCommand(item, callback) {
+    const itemIndex = player.inventory.indexOf(item);
+    if (itemIndex > -1) {
+        player.inventory.splice(itemIndex, 1); // Remove the item from the inventory
+        if (item === 'potion') {
+            player.health += 1; // Increase health by 1
+            console.log(`Used ${item}, health is now ${player.health}`);
+            flashPlayer(callback);
+        } else if (item === 'food') {
+            player.endurance = Math.min(player.endurance + 2, 10); // Increase endurance by 2, up to a max of 10
+            console.log(`Used ${item}, endurance is now ${player.endurance}`);
+            flashPlayer(callback);
         } else {
-            console.error(`Unknown command: ${command}`);
+            callback();
+        }
+    } else {
+        player.health -= 1;
+        console.log(`Tried to use ${item} but it's not in inventory, health is now ${player.health}`);
+        if (player.health <= 0) {
+            alertGameOver();
+            return;
+        }
+        flashPlayer(callback);
+    }
+}
+
+function handleMovement(direction, steps, callback) {
+    let stepCount = 0;
+
+    const moveInterval = setInterval(() => {
+        if (stepCount >= steps) {
+            clearInterval(moveInterval);
+            callback();
+            return;
+        }
+
+        const { x, y } = player.position;
+        if (direction === 'up' && y > 0) {
+            updatePlayerPosition(x, y - 1);
+        } else if (direction === 'down' && y < grid[0].length - 1) {
+            updatePlayerPosition(x, y + 1);
+        } else if (direction === 'left' && x > 0) {
+            updatePlayerPosition(x - 1, y);
+        } else if (direction === 'right' && x < grid.length - 1) {
+            updatePlayerPosition(x + 1, y);
+        }
+        console.log(`Moved ${direction}, new position is (${player.position.x}, ${player.position.y})`);
+
+        player.steps += 1;
+        if (player.steps % 8 === 0) {
+            player.endurance = Math.max(player.endurance - 1, 0);  // Decrease endurance but not below 0
+            if (player.endurance <= 0) {
+                player.health -= 1;
+                console.log(`Endurance depleted, health is now ${player.health}`);
+                if (player.health <= 0) {
+                    alertGameOver();
+                    return;
+                }
+            }
+            console.log(`Endurance decreased, endurance is now ${player.endurance}`);
         }
-    };
 
-    const executeCommands = (parsedCommands) => {
-        parsedCommands.forEach(cmd => executeCommand(cmd));
-    };
+        drawGrid(grid);
+        updatePlayerStatus();
+        stepCount++;
+    }, 500); // Adjust the interval time for speed of animation
+}
 
-    return {
-        registerCommand,
-        executeCommand,
-        executeCommands
-    };
-};
+function flashPlayer(callback) {
+    player.flashing = true;
+    drawGrid(grid);
+    setTimeout(() => {
+        player.flashing = false;
+        drawGrid(grid);
+        updatePlayerStatus();
+        if (callback) callback();
+    }, 250); // Flash duration
+}
 
-export default createCommandHandler;
+function isMovementCommand(action) {
+    return ['up', 'down', 'left', 'right'].includes(action);
+}
diff --git a/js/puzzle-dungeon/game.js b/js/puzzle-dungeon/game.js
index f742844..4ca91d3 100644
--- a/js/puzzle-dungeon/game.js
+++ b/js/puzzle-dungeon/game.js
@@ -1,131 +1,182 @@
-const canvas = document.getElementById('gameCanvas');
-const ctx = canvas.getContext('2d');
-const cellSize = canvas.width / 10;
-const stepDuration = 200; // Duration for each step
+// game.js
+export let grid = createGrid(10, 10);  // Example grid size
+export let player = {
+    position: { x: 0, y: 0 },
+    inventory: [],
+    health: 10,
+    endurance: 10,
+    score: 0,
+    steps: 0,
+    flashing: false
+};
+let targetPosition = { x: 0, y: 0 };
 
-let player = { x: 0, y: 0 };
-
-function drawGrid() {
-    ctx.clearRect(0, 0, canvas.width, canvas.height);
-    for (let x = 0; x <= canvas.width; x += cellSize) {
-        ctx.moveTo(x, 0);
-        ctx.lineTo(x, canvas.height);
-    }
-    for (let y = 0; y <= canvas.height; y += cellSize) {
-        ctx.moveTo(0, y);
-        ctx.lineTo(canvas.width, y);
+function createGrid(rows, cols) {
+    let grid = [];
+    for (let i = 0; i < rows; i++) {
+        let row = [];
+        for (let j = 0; j < cols; j++) {
+            row.push(null);
+        }
+        grid.push(row);
     }
-    ctx.strokeStyle = '#ddd';
-    ctx.stroke();
+    return grid;
 }
 
-function drawPlayer() {
-    ctx.fillStyle = 'blue';
-    ctx.fillRect(player.x * cellSize, player.y * cellSize, cellSize, cellSize);
+function generateCollectables(grid, numCollectables) {
+    const collectableTypes = ['potion', 'shield', 'food'];
+    const collectableColors = {
+        'potion': 'purple',
+        'shield': 'gold',
+        'food': 'red'
+    };
+    for (let i = 0; i < numCollectables; i++) {
+        let x, y;
+        do {
+            x = Math.floor(Math.random() * grid.length);
+            y = Math.floor(Math.random() * grid[0].length);
+        } while (x === 0 && y === 0);  // Ensure no items at (0,0)
+        const type = collectableTypes[Math.floor(Math.random() * collectableTypes.length)];
+        grid[x][y] = { type, color: collectableColors[type] };
+    }
 }
 
-document.getElementById('commandForm').addEventListener('submit', function (e) {
-    e.preventDefault();
-    const commands = document.getElementById('commands').value.trim();
-    try {
-        const parsedCommands = parseLispCommands(commands);
-        console.log("Parsed Commands:", JSON.stringify(parsedCommands, null, 2)); // Debugging output
-        executeCommands(parsedCommands);
-    } catch (error) {
-        console.error("Error parsing commands:", error);
+function generateDamagingSpaces(grid, numSpaces) {
+    for (let i = 0; i < numSpaces; i++) {
+        let x, y;
+        do {
+            x = Math.floor(Math.random() * grid.length);
+            y = Math.floor(Math.random() * grid[0].length);
+        } while (x === 0 && y === 0);  // Ensure no damaging spaces at (0,0)
+        grid[x][y] = { type: 'damage', color: 'pink' };
     }
-});
+}
 
-function parseLispCommands(commands) {
-    const parse = input => {
-        const tokens = tokenize(input);
-        const ast = buildAST(tokens);
-        return ast;
-    };
+function generateTargetBlock(grid) {
+    let x, y;
+    do {
+        x = Math.floor(Math.random() * grid.length);
+        y = Math.floor(Math.random() * grid[0].length);
+    } while (x === 0 && y === 0);  // Ensure no target block at (0,0)
+    grid[x][y] = { type: 'target', color: 'green' };  // Target block represented by 'X'
+    targetPosition = { x, y };  // Store the target block position
+}
 
-    const tokenize = input => {
-        return input.replace(/\(/g, ' ( ').replace(/\)/g, ' ) ').trim().split(/\s+/);
-    };
+export function drawGrid(grid) {
+    const canvas = document.getElementById('gameCanvas');
+    const ctx = canvas.getContext('2d');
+    const cellSize = Math.min(canvas.width / grid.length, canvas.height / grid[0].length);
+
+    ctx.clearRect(0, 0, canvas.width, canvas.height);
 
-    const buildAST = tokens => {
-        if (!tokens.length) throw new SyntaxError("Unexpected end of input");
+    grid.forEach((row, x) => {
+        row.forEach((cell, y) => {
+            ctx.strokeStyle = 'black';
+            ctx.strokeRect(x * cellSize, y * cellSize, cellSize, cellSize);
 
-        const token = tokens.shift();
-        if (token === '(') {
-            const list = [];
-            while (tokens[0] !== ')') {
-                list.push(buildAST(tokens));
-                if (!tokens.length) throw new SyntaxError("Missing closing parenthesis");
+            if (cell && cell.color) {
+                ctx.fillStyle = cell.color;
+                ctx.fillRect(x * cellSize, y * cellSize, cellSize, cellSize);
+                ctx.fillStyle = 'black';
+                ctx.font = `${cellSize / 2}px Arial`;
+                ctx.textAlign = 'center';
+                ctx.textBaseline = 'middle';
+                let char = '';
+                if (cell.type === 'potion') char = 'p';
+                if (cell.type === 'shield') char = 's';
+                if (cell.type === 'food') char = 'f';
+                if (cell.type === 'damage') char = '!';
+                if (cell.type === 'target') char = 'X';
+                ctx.fillText(char, x * cellSize + cellSize / 2, y * cellSize + cellSize / 2);
             }
-            tokens.shift();
-            return list;
-        } else if (token === ')') {
-            throw new SyntaxError("Unexpected ')'");
-        } else {
-            return isNaN(token) ? token : Number(token);
-        }
-    };
+        });
+    });
 
-    return parse(commands);
+    // Draw player
+    ctx.fillStyle = player.flashing ? 'white' : 'blue';
+    ctx.fillRect(player.position.x * cellSize, player.position.y * cellSize, cellSize, cellSize);
+    ctx.fillStyle = 'black';
+    ctx.font = `${cellSize / 2}px Arial`;
+    ctx.textAlign = 'center';
+    ctx.textBaseline = 'middle';
+    ctx.fillText('@', player.position.x * cellSize + cellSize / 2, player.position.y * cellSize + cellSize / 2);
 }
 
-function executeCommands(commands) {
-    if (commands[0] !== 'move') {
-        console.error("Invalid command: root command must be 'move'");
-        return;
-    }
+export function initializeGame() {
+    grid = createGrid(10, 10);  // Reset grid
+    generateCollectables(grid, 10);  // Adjust the number as needed
+    generateDamagingSpaces(grid, Math.floor(Math.random() * 7) + 1);  // Random number of damaging spaces, no more than 7
+    generateTargetBlock(grid);  // Add target block
+    player.position = { x: 0, y: 0 };  // Reset player position
+    player.inventory = [];  // Clear inventory
+    player.health = 10;  // Reset health
+    player.endurance = 10;  // Reset endurance
+    player.steps = 0;  // Reset steps
+    resizeCanvas();
+    drawGrid(grid);
+    updatePlayerStatus();
+}
 
-    let delay = 0;
-    commands.slice(1).forEach(moveCommand => {
-        const steps = moveCommand[1];
-        for (let i = 0; i < steps; i++) {
-            setTimeout(() => {
-                movePlayer([moveCommand[0], 1]);
-                drawGrid();
-                drawPlayer();
-            }, delay);
-            delay += stepDuration;
-        }
-        if (moveCommand[0] === 'rest') {
-            setTimeout(() => {
-                drawGrid();
-                drawPlayer();
-            }, delay);
-            delay += stepDuration;
-        }
-    });
+export function resizeCanvas() {
+    const canvas = document.getElementById('gameCanvas');
+    const width = Math.min(window.innerWidth, window.innerHeight) * 0.9;
+    canvas.width = width;
+    canvas.height = width;
+    drawGrid(grid);
 }
 
-function movePlayer(moveCommand) {
-    const direction = moveCommand[0];
-    const steps = moveCommand[1];
+export function updatePlayerPosition(newX, newY) {
+    player.position.x = newX;
+    player.position.y = newY;
+    checkForDamageOrTarget();
+    drawGrid(grid);
+    updatePlayerStatus();
+}
 
-    if (typeof direction !== 'string' || typeof steps !== 'number') {
-        console.error(`Invalid move command: ${JSON.stringify(moveCommand)}`);
-        return;
-    }
+export function updatePlayerStatus() {
+    document.getElementById('playerPosition').textContent = `Position: (${player.position.x}, ${player.position.y})`;
+    document.getElementById('playerHealth').textContent = `Health: ${player.health}`;
+    document.getElementById('playerEndurance').textContent = `Endurance: ${player.endurance}`;
+    document.getElementById('playerInventory').textContent = `Inventory: [${player.inventory.join(', ')}]`;
+    document.getElementById('playerScore').textContent = `Score: ${player.score}`;
+}
 
-    switch (direction) {
-        case 'north':
-            player.y = Math.max(0, player.y - steps);
-            break;
-        case 'south':
-            player.y = Math.min(9, player.y + steps);
-            break;
-        case 'east':
-            player.x = Math.min(9, player.x + steps);
-            break;
-        case 'west':
-            player.x = Math.max(0, player.x - steps);
-            break;
-        case 'rest':
-            // No movement, just wait for the duration
-            break;
-        default:
-            console.error(`Unknown direction: ${direction}`);
-            return;
+function checkForDamageOrTarget() {
+    const { x, y } = player.position;
+    const cell = grid[x][y];
+    if (cell && cell.type === 'damage') {
+        const shieldIndex = player.inventory.indexOf('shield');
+        if (shieldIndex > -1) {
+            player.inventory.splice(shieldIndex, 1);  // Use one shield
+            console.log('Used shield to avoid damage');
+        } else {
+            player.health -= 2;
+            console.log(`Stepped on damaging space, health is now ${player.health}`);
+            if (player.health <= 0) {
+                alertGameOver();
+                return;
+            }
+            flashPlayer();
+        }
+    } else if (x === targetPosition.x && y === targetPosition.y) {
+        player.score += 1;
+        console.log(`Reached target block, score is now ${player.score}`);
+        initializeGame();  // Generate new level and reset player position
     }
 }
 
-drawGrid();
-drawPlayer();
+export function alertGameOver() {
+    alert('You have lost the game!');
+    initializeGame();
+}
+
+function flashPlayer(callback) {
+    player.flashing = true;
+    drawGrid(grid);
+    setTimeout(() => {
+        player.flashing = false;
+        drawGrid(grid);
+        updatePlayerStatus();
+        if (callback) callback();
+    }, 250); // Flash duration
+}
diff --git a/js/puzzle-dungeon/index.html b/js/puzzle-dungeon/index.html
index ed13d3d..c183ea0 100644
--- a/js/puzzle-dungeon/index.html
+++ b/js/puzzle-dungeon/index.html
@@ -6,19 +6,39 @@
     <style>
         canvas {
             border: 1px solid black;
+            display: block;
+            margin: 0 auto;
         }
     </style>
 </head>
 <body>
-    <canvas id="gameCanvas" width="500" height="500"></canvas>
+    <canvas id="gameCanvas"></canvas>
     <form id="commandForm">
         <textarea id="commands" rows="10" cols="30"></textarea><br>
         <button type="submit">Submit</button>
     </form>
+    <div id="playerStatus">
+        <h3>Player Status</h3>
+        <p id="playerPosition">Position: (0, 0)</p>
+        <p id="playerHealth">Health: 10</p>
+        <p id="playerEndurance">Endurance: 10</p>
+        <p id="playerInventory">Inventory: []</p>
+        <p id="playerScore">Score: 0</p>
+    </div>
     <script type="module">
-    import './parser.js';
-    import './commandHandler.js';
-    import './game.js';
+        import { parseCommands } from './parser.js';
+        import { initializeGame, resizeCanvas } from './game.js';
+
+        document.getElementById('commandForm').addEventListener('submit', function(event) {
+            event.preventDefault();
+            const commands = document.getElementById('commands').value;
+            parseCommands(commands);
+        });
+
+        window.addEventListener('resize', resizeCanvas);
+
+        // Initialize the game
+        initializeGame();
     </script>
 </body>
 </html>
diff --git a/js/puzzle-dungeon/parser.js b/js/puzzle-dungeon/parser.js
index ec7f33f..f962677 100644
--- a/js/puzzle-dungeon/parser.js
+++ b/js/puzzle-dungeon/parser.js
@@ -1,10 +1,9 @@
-export function parseCommands(input) {
-    const commands = input.split('\n').map(cmd => cmd.trim()).filter(cmd => cmd.length > 0);
-    return commands.map(command => {
-        const parts = command.split(' ');
-        return {
-            command: parts[0],
-            args: parts.slice(1)
-        };
+// parser.js
+import { handleCommand } from './commandHandler.js';
+
+export function parseCommands(commands) {
+    const commandList = commands.trim().split('\n');
+    commandList.forEach(command => {
+        handleCommand(command.trim());
     });
 }