about summary refs log blame commit diff stats
path: root/js/puzzle-dungeon/game.js
blob: 79a3f769c4a4d3681bdb129bca3caeef6abef533 (plain) (tree)
1
2
3
4
5
6
7
8
9




                                                           
              
             
             
           
                   


                                    
                    
 







                                        
     
                

 

                             
                    



                                                
                         


               
                                                      
                                                             
                               


                                   





                                                           
                                                                  


                                                                                           

 





                                                           
                                                                           
                                                         
     
 
 










                                                              




                                                       


                                                                               
 
 





                                                                                          
 

                                  
                                        
                                                                           
 


                                                                             
                                          





                                                       
                                                        
                                                       
                                                       
                                                                       
                                                                                             
             

           
 
                  
                                                          
                                                                                                 
                              



                                                                                                                

 
                                  
                                             
                                         

                                                                                                                           
                               
                                                                                                        

                             


                              

     
                                                   


                                                                     



                         
 

                                                         
                                                                        


                          

 






                                                  
 
                                      
                                                                                    

                                                                                                                     
                                                                                  
                                                                                  







                                                                                                                  
 
 
















                                                                                     
                                              







                                                                                         
                

                                           
         
                                                                  

                                                                          
                                                                                   


     
                                 
                                                                                                                                         

                                                


                     
 
export let grid = createGrid(10, 10);  // Example grid size
export let player = {
    position: { x: 0, y: 0 },
    inventory: [],
    health: 10,
    power: 10,
    level: 0,
    steps: 0,
    par: 0,
    didScan: false,
    flashing: false
};
let targetPosition = { x: 0, y: 0 };
export let levelPar;

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);
    }
    return grid;
}

function generatePar(level) {
    let par;
    if (level < 4) {
        par = 7;
    } else {
        par = Math.floor(Math.random() * 7) + 1;
    }
    updatePlayerStatus();
    return par;
}

function generateCollectables(grid, numCollectables) {
    const collectableTypes = ['potion', 'shield', 'battery'];
    const collectableColors = {
        'potion': 'peachpuff',
        'shield': 'cornflowerBlue',
        'battery': 'gold'
    };
    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);  // don't put items at (0,0)
        const type = collectableTypes[Math.floor(Math.random() * collectableTypes.length)];
        grid[x][y] = { type, color: collectableColors[type] };
    }
}

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);  // don't put damaging stuff at (0,0)
        grid[x][y] = { type: 'damage', color: 'tomato' };
    }
}

function generateTraps(grid, numTraps) {
    for (let i = 0; i < numTraps; 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);  // don't be an asshole
        grid[x][y] = { type: 'trap', color: 'rgba(0,0,0,0)' };
    }
}

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);  // don't make it too easy
    grid[x][y] = { type: 'target', color: 'lightgreen' };  // x marks the spot!
    targetPosition = { x, y };
}

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);

    grid.forEach((row, x) => {
        row.forEach((cell, y) => {
            ctx.strokeStyle = '#2d2d2d';
            ctx.strokeRect(x * cellSize, y * cellSize, cellSize, cellSize);

            if (cell && cell.color) {
                ctx.fillStyle = cell.color;
                ctx.fillRect(x * cellSize, y * cellSize, cellSize, cellSize);
                ctx.fillStyle = '#2d2d2d';
                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 === 'battery') char = 'b';
                if (cell.type === 'damage') char = '!';
                if (cell.type === 'target') char = '#';
                if (cell.type === 'trap' && player.didScan) char = 't';
                ctx.fillText(char, x * cellSize + cellSize / 2, y * cellSize + cellSize / 2);
            }
        });
    });

    // Draw player
    ctx.fillStyle = player.flashing ? 'white' : 'thistle';
    ctx.fillRect(player.position.x * cellSize, player.position.y * cellSize, cellSize, cellSize);
    ctx.fillStyle = '#2d2d2d';
    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);
}

export function initializeGame() {
    grid = createGrid(10, 10);  // reset grid
    levelPar = generatePar(player.level);
    generateCollectables(grid, Math.floor(Math.random() * 7) + 1);  // how many collectables?
    generateDamagingSpaces(grid, Math.floor(Math.random() * 22) + 1);  // random number of damaging spaces, no more than 22
    generateTargetBlock(grid); 
    generateTraps(grid, Math.floor(Math.random() * 10) + 1);  // random number of traps, no more than 10

    if (player.level === 0) {
        player.inventory = [];
        player.health = 10;
        player.power = 10;
    }

    player.par = 0; // always reset player par to 0
    player.position = { x: 0, y: 0 };  // player always starts at 0,0
    player.steps = 0; // steps are counted per-level
    player.didScan = false;  // scans are counted per-level
    resizeCanvas();
    drawGrid(grid);
    updatePlayerStatus();
}

export function resizeCanvas() {
    const canvas = document.getElementById('gameCanvas');
    const width = Math.min(window.innerWidth, window.innerHeight) * 0.5;
    canvas.width = width;
    canvas.height = width;
    drawGrid(grid);
}

export function updatePlayerPosition(newX, newY) {
    player.position.x = newX;
    player.position.y = newY;
    checkForDamageOrTarget();
    drawGrid(grid);
    updatePlayerStatus();
}

export function updatePlayerStatus() {
    document.getElementById('par').textContent = `Par: ${player.par} : ${levelPar}`;
    document.getElementById('playerPosition').textContent = `Position: (${player.position.x}, ${player.position.y})`;
    document.getElementById('playerHealth').textContent = `Health: ${player.health}`;
    document.getElementById('playerPower').textContent = `Power: ${player.power}`;
    document.getElementById('playerLevel').textContent = `Level: ${player.level}`;
    const inventoryCounts = player.inventory.reduce((acc, item) => {
        acc[item] = acc[item] ? acc[item] + 1 : 1;
        return acc;
    }, {});
    const inventoryString = Object.entries(inventoryCounts).map(([item, count]) => `${item} ${count}`).join(', ');
    document.getElementById('playerInventory').textContent = `Inventory: ${inventoryString}`;
    

}

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 (cell && cell.type === 'trap') {
        const itemIndex = Math.floor(Math.random() * player.inventory.length);
        player.inventory.splice(itemIndex, 1);
        console.log(`Stepped on a trap and lost an item`);
        player.health -= Math.floor(Math.random() * 5) + 1;
        console.log(`Stepped on a trap and took damage, health is now ${player.health}`);
        if (player.health <= 0) {
            alertGameOver();
            return;
        } else {
            alert('Ouch! You hit a trap.');
            flashPlayer();
        }
    } else if (x === targetPosition.x && y === targetPosition.y) {
        player.level += 1;
        console.log(`Reached target block, level is now ${player.level}`);
        initializeGame();  // generate a new level and reset player position to 0,0
    }
}

export function alertGameOver() {
    const gameStatsString = `Level: ${player.level}, Par: ${player.par} : ${levelPar}, Health: ${player.health}, Power: ${player.power}`;
    alert('Game Over' + '\n' + gameStatsString);
    player.level = 0;
    initializeGame();
}