diff options
Diffstat (limited to 'html/rogue/js')
-rw-r--r-- | html/rogue/js/camera.js | 56 | ||||
-rw-r--r-- | html/rogue/js/player.js | 64 | ||||
-rw-r--r-- | html/rogue/js/rogue.js | 108 | ||||
-rw-r--r-- | html/rogue/js/world.js | 236 |
4 files changed, 464 insertions, 0 deletions
diff --git a/html/rogue/js/camera.js b/html/rogue/js/camera.js new file mode 100644 index 0000000..e5d5d14 --- /dev/null +++ b/html/rogue/js/camera.js @@ -0,0 +1,56 @@ +const createCamera = (x, y) => ({ + x, + y, + width: window.innerWidth, + height: window.innerHeight, + // Define the dead zone (the area where camera won't move) + deadZone: { + x: window.innerWidth * 0.3, // 30% of screen width + y: window.innerHeight * 0.3, // 30% of screen height + } +}); + +const updateCamera = (camera, target) => { + // Calculate the center point of the screen + const screenCenterX = camera.x + camera.width / 2; + const screenCenterY = camera.y + camera.height / 2; + + // Calculate the distance from the target to the screen center + const distanceX = target.x - screenCenterX; + const distanceY = target.y - screenCenterY; + + // Calculate the dead zone boundaries + const deadZoneLeft = -camera.deadZone.x / 2; + const deadZoneRight = camera.deadZone.x / 2; + const deadZoneTop = -camera.deadZone.y / 2; + const deadZoneBottom = camera.deadZone.y / 2; + + // Calculate new camera position with smooth following + let newX = camera.x; + let newY = camera.y; + + // Horizontal camera movement + if (distanceX < deadZoneLeft) { + newX += distanceX - deadZoneLeft; + } else if (distanceX > deadZoneRight) { + newX += distanceX - deadZoneRight; + } + + // Vertical camera movement + if (distanceY < deadZoneTop) { + newY += distanceY - deadZoneTop; + } else if (distanceY > deadZoneBottom) { + newY += distanceY - deadZoneBottom; + } + + // Add subtle smoothing to camera movement + const smoothing = 0.1; + newX = camera.x + (newX - camera.x) * smoothing; + newY = camera.y + (newY - camera.y) * smoothing; + + return { + ...camera, + x: newX, + y: newY + }; +}; diff --git a/html/rogue/js/player.js b/html/rogue/js/player.js index e69de29..270b26f 100644 --- a/html/rogue/js/player.js +++ b/html/rogue/js/player.js @@ -0,0 +1,64 @@ +const createPlayer = (x, y) => ({ + x, + y, + width: 32, + height: 32, + velocityX: 0, + velocityY: 0, + speed: 300, + jumping: false +}); + +const updatePlayer = (player, deltaTime) => { + const keys = getKeys(); + const seconds = deltaTime / 1000; + + let velocityX = 0; + let velocityY = player.velocityY; + + // Horizontal movement + if (keys.ArrowLeft) velocityX -= player.speed; + if (keys.ArrowRight) velocityX += player.speed; + + // Simple jumping (can be improved) + if (keys.ArrowUp && !player.jumping) { + velocityY = -500; + } + + // Apply gravity + velocityY += 980 * seconds; // 980 pixels/secondĀ² + + // Update position + const x = player.x + velocityX * seconds; + const y = player.y + velocityY * seconds; + + // Create updated player state + let updatedPlayer = { + ...player, + x, + y, + velocityX, + velocityY + }; + + // Handle collisions with the world + return handleWorldCollisions(updatedPlayer, window.gameState.world); +}; + +const renderPlayer = (ctx, player) => { + ctx.fillStyle = '#f00'; + ctx.fillRect(player.x, player.y, player.width, player.height); +}; + +// Key handling +const keys = {}; + +window.addEventListener('keydown', (e) => { + keys[e.key] = true; +}); + +window.addEventListener('keyup', (e) => { + keys[e.key] = false; +}); + +const getKeys = () => ({...keys}); diff --git a/html/rogue/js/rogue.js b/html/rogue/js/rogue.js index e69de29..8276d8d 100644 --- a/html/rogue/js/rogue.js +++ b/html/rogue/js/rogue.js @@ -0,0 +1,108 @@ +window.gameState = null; + +const initGame = () => { + const canvas = document.getElementById('gameCanvas'); + const ctx = canvas.getContext('2d'); + + // Set canvas to full viewport size + const resizeCanvas = () => { + canvas.width = window.innerWidth; + canvas.height = window.innerHeight; + }; + + window.addEventListener('resize', resizeCanvas); + resizeCanvas(); + + // Game state + let gameState = { + time: 0, + player: createPlayer(100, 100), // Starting position + camera: createCamera(0, 0), + world: createWorld() + }; + + // Make gameState globally accessible + window.gameState = gameState; + + // Game loop + const gameLoop = (timestamp) => { + // Calculate delta time + const deltaTime = timestamp - gameState.time; + gameState.time = timestamp; + + // Update + gameState = updateGame(gameState, deltaTime); + + // Render + render(ctx, gameState); + + // Next frame + requestAnimationFrame(gameLoop); + }; + + // Start the game loop + requestAnimationFrame(gameLoop); +}; + +const updateGame = (state, deltaTime) => { + const updatedPlayer = updatePlayer(state.player, deltaTime); + const updatedCamera = updateCamera(state.camera, updatedPlayer); + + return { + ...state, + player: updatedPlayer, + camera: updatedCamera + }; +}; + +const render = (ctx, state) => { + // Clear the canvas + ctx.fillStyle = '#000'; + ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height); + + // Apply camera transform + ctx.save(); + ctx.translate(-state.camera.x, -state.camera.y); + + // 1. Render background objects + state.world.backgroundTrees.forEach(tree => { + renderTree(ctx, tree, ctx.canvas.height - state.world.groundHeight); + }); + + state.world.backgroundRocks.forEach(rock => { + renderRock(ctx, rock, ctx.canvas.height - state.world.groundHeight); + }); + + // 2. Render platforms + state.world.platforms.forEach(platform => { + ctx.fillStyle = platform.color; + ctx.fillRect(platform.x, platform.y, platform.width, platform.height); + }); + + // 3. Render player + renderPlayer(ctx, state.player); + + // 4. Render foreground objects + state.world.foregroundTrees.forEach(tree => { + renderTree(ctx, tree, ctx.canvas.height - state.world.groundHeight); + }); + + state.world.foregroundRocks.forEach(rock => { + renderRock(ctx, rock, ctx.canvas.height - state.world.groundHeight); + }); + + // 5. Render ground + const groundY = ctx.canvas.height - state.world.groundHeight; + ctx.fillStyle = '#4a4'; + ctx.fillRect( + state.camera.x - 1000, + groundY, + ctx.canvas.width + 2000, + state.world.groundHeight + ); + + ctx.restore(); +}; + +// Initialize the game when the window loads +window.addEventListener('load', initGame); diff --git a/html/rogue/js/world.js b/html/rogue/js/world.js index e69de29..b20aa54 100644 --- a/html/rogue/js/world.js +++ b/html/rogue/js/world.js @@ -0,0 +1,236 @@ +// Define world object types +const WORLD_OBJECTS = { + PLATFORM: 'platform', + TREE_BACKGROUND: 'tree_background', + TREE_FOREGROUND: 'tree_foreground', + ROCK_BACKGROUND: 'rock_background', + ROCK_FOREGROUND: 'rock_foreground' +}; + +// Tree configurations +const TREE_TYPES = { + SMALL: { + width: 80, + height: 120, + canopyOffset: 40, // Distance from bottom where canopy starts + canopyColor: '#2a4', + trunkColor: '#531' + }, + LARGE: { + width: 120, + height: 200, + canopyOffset: 60, + canopyColor: '#1a4', + trunkColor: '#420' + } +}; + +// Rock configurations +const ROCK_TYPES = { + SMALL: { + width: 40, + height: 30, + color: '#666', + highlights: '#888' + }, + MEDIUM: { + width: 70, + height: 45, + color: '#555', + highlights: '#777' + }, + LARGE: { + width: 100, + height: 60, + color: '#444', + highlights: '#666' + } +}; + +// Create a world with platforms, trees, and rocks +const createWorld = () => ({ + groundHeight: 12, + // Separate arrays for different layers + backgroundTrees: [ + { + type: WORLD_OBJECTS.TREE_BACKGROUND, + x: 50, + config: TREE_TYPES.LARGE + }, + { + type: WORLD_OBJECTS.TREE_BACKGROUND, + x: 500, + config: TREE_TYPES.SMALL + } + ], + backgroundRocks: [ + { + type: WORLD_OBJECTS.ROCK_BACKGROUND, + x: 150, + config: ROCK_TYPES.LARGE + }, + { + type: WORLD_OBJECTS.ROCK_BACKGROUND, + x: 400, + config: ROCK_TYPES.SMALL + } + ], + platforms: [ + { + type: WORLD_OBJECTS.PLATFORM, + x: 300, + y: 300, + width: 200, + height: 20, + color: '#484' + }, + { + type: WORLD_OBJECTS.PLATFORM, + x: 600, + y: 200, + width: 200, + height: 20, + color: '#484' + }, + { + type: WORLD_OBJECTS.PLATFORM, + x: -200, + y: 250, + width: 200, + height: 20, + color: '#484' + } + ], + foregroundTrees: [ + { + type: WORLD_OBJECTS.TREE_FOREGROUND, + x: 200, + config: TREE_TYPES.SMALL + }, + { + type: WORLD_OBJECTS.TREE_FOREGROUND, + x: 800, + config: TREE_TYPES.LARGE + } + ], + foregroundRocks: [ + { + type: WORLD_OBJECTS.ROCK_FOREGROUND, + x: 300, + config: ROCK_TYPES.MEDIUM + }, + { + type: WORLD_OBJECTS.ROCK_FOREGROUND, + x: 700, + config: ROCK_TYPES.SMALL + } + ] +}); + +const renderTree = (ctx, tree, groundY) => { + const { x, config } = tree; + const { width, height, canopyOffset, trunkColor, canopyColor } = config; + + // Draw trunk + ctx.fillStyle = trunkColor; + ctx.fillRect( + x - width/6, + groundY - height, + width/3, + height + ); + + // Draw canopy (triangular shape) + ctx.fillStyle = canopyColor; + ctx.beginPath(); + ctx.moveTo(x - width/2, groundY - canopyOffset); + ctx.lineTo(x + width/2, groundY - canopyOffset); + ctx.lineTo(x, groundY - height); + ctx.closePath(); + ctx.fill(); +}; + +const renderRock = (ctx, rock, groundY) => { + const { x, config } = rock; + const { width, height, color, highlights } = config; + + // Draw main rock shape (slightly irregular pentagon) + ctx.fillStyle = color; + ctx.beginPath(); + ctx.moveTo(x - width/2, groundY); + ctx.lineTo(x - width/2 + width/6, groundY - height); + ctx.lineTo(x + width/3, groundY - height); + ctx.lineTo(x + width/2, groundY - height/2); + ctx.lineTo(x + width/2, groundY); + ctx.closePath(); + ctx.fill(); + + // Add highlights + ctx.fillStyle = highlights; + ctx.beginPath(); + ctx.moveTo(x - width/4, groundY - height); + ctx.lineTo(x, groundY - height); + ctx.lineTo(x + width/6, groundY - height/2); + ctx.lineTo(x - width/6, groundY - height/2); + ctx.closePath(); + ctx.fill(); +}; + +// Collision detection helper +const checkCollision = (player, platform) => { + return player.x < platform.x + platform.width && + player.x + player.width > platform.x && + player.y < platform.y + platform.height && + player.y + player.height > platform.y; +}; + +// World physics helper +const handleWorldCollisions = (player, world) => { + let onGround = false; + + // Check ground collision first + const groundY = window.innerHeight - world.groundHeight; + if (player.y + player.height > groundY) { + player.y = groundY - player.height; + player.velocityY = 0; + onGround = true; + } + + // Then check platform collisions + for (const platform of world.platforms) { + if (checkCollision(player, platform)) { + // Calculate overlap on each axis + const overlapX = Math.min( + player.x + player.width - platform.x, + platform.x + platform.width - player.x + ); + const overlapY = Math.min( + player.y + player.height - platform.y, + platform.y + platform.height - player.y + ); + + // Resolve collision on the axis with smallest overlap + if (overlapX < overlapY) { + // Horizontal collision + if (player.x < platform.x) { + player.x = platform.x - player.width; + } else { + player.x = platform.x + platform.width; + } + player.velocityX = 0; + } else { + // Vertical collision + if (player.y < platform.y) { + player.y = platform.y - player.height; + player.velocityY = 0; + onGround = true; + } else { + player.y = platform.y + platform.height; + player.velocityY = 0; + } + } + } + } + + return { ...player, jumping: !onGround }; +}; |