diff --git a/src/components/App/index.tsx b/src/components/App/index.tsx index 81706be..579590b 100644 --- a/src/components/App/index.tsx +++ b/src/components/App/index.tsx @@ -1,4 +1,5 @@ import { useState } from "react"; +import { GlobalContext } from "../../contexts"; import World from "../World"; import Player from "../Player"; import Npc from "../Npc"; @@ -9,6 +10,7 @@ import Lever from "../Lever"; import House from "../House"; import Fire from "../Fire"; import GameOver from "../GameOver"; +import { GAME_STATES } from "../../constants"; import "./style.css"; /* @@ -17,31 +19,34 @@ import "./style.css"; * - Use context to connect components */ export default function App() { + const [gameState, setGameState] = useState(GAME_STATES.Game); const [isCellarDoorOpen, setIsCellarDoorOpen] = useState(false); const [isLeverUsed, setIsLeverUsed] = useState(false); const [playerHealth, setPlayerHealth] = useState(4); return (
- {playerHealth <= 0 && } - - - - - - - - - + + {gameState === GAME_STATES.GameOver && } + + + + + + + + + +
); } diff --git a/src/components/CellarDoor/index.tsx b/src/components/CellarDoor/index.tsx index ecb7211..3c37be8 100644 --- a/src/components/CellarDoor/index.tsx +++ b/src/components/CellarDoor/index.tsx @@ -1,6 +1,12 @@ import { useEffect, FunctionComponent } from "react"; +import { TILE_SETS } from "../../constants"; import "./style.css"; +const WIDTH = 64; +const HEIGHT = 64; +const TILE_X = 992; +const TILE_Y = 160; + /* * TODO: * - useRef instead of getElementById @@ -23,20 +29,42 @@ const CellarDoor: FunctionComponent<{ isOpen?: boolean }> = ({ canvas.style.top = "272px"; const tileSet = new Image(); - tileSet.src = "assets/overworld.png"; + tileSet.src = TILE_SETS.World; tileSet.onload = () => { - ctx.clearRect(0, 0, 64, 64); + ctx.clearRect(0, 0, WIDTH, HEIGHT); if (isOpen) { - ctx.drawImage(tileSet, 1056, 160, 64, 64, 0, 0, 64, 64); + ctx.drawImage( + tileSet, + TILE_X + WIDTH, + TILE_Y, + WIDTH, + HEIGHT, + 0, + 0, + WIDTH, + HEIGHT + ); } else { - ctx.drawImage(tileSet, 992, 160, 64, 64, 0, 0, 64, 64); + ctx.drawImage( + tileSet, + TILE_X, + TILE_Y, + WIDTH, + HEIGHT, + 0, + 0, + WIDTH, + HEIGHT + ); } }; } }, [isOpen]); - return ; + return ( + + ); }; export default CellarDoor; diff --git a/src/components/Coin/index.tsx b/src/components/Coin/index.tsx index 5f2a940..52c0673 100644 --- a/src/components/Coin/index.tsx +++ b/src/components/Coin/index.tsx @@ -1,6 +1,13 @@ import { useEffect, FunctionComponent } from "react"; +import { TILE_SIZE, TILE_SETS } from "../../constants"; import "./style.css"; +const WIDTH = TILE_SIZE; +const HEIGHT = TILE_SIZE; +const TILE_X = 0; +const TILE_Y = 128; +const ANIMATION_LENGTH = 3; + /* * TODO: * - useRef instead of getElementById @@ -24,30 +31,71 @@ const Coin: FunctionComponent<{ left: number; top: number }> = ({ canvas.style.top = `${top}px`; const tileSet = new Image(); - tileSet.src = "assets/objects.png"; + tileSet.src = TILE_SETS.Objects; tileSet.onload = () => { let currentFrame = 0; setInterval(() => { - ctx.clearRect(0, 0, 32, 32); + ctx.clearRect(0, 0, WIDTH, HEIGHT); if (currentFrame === 0) { - ctx.drawImage(tileSet, 0, 128, 32, 32, 0, 0, 32, 32); + ctx.drawImage( + tileSet, + TILE_X, + TILE_Y, + WIDTH, + HEIGHT, + 0, + 0, + WIDTH, + HEIGHT + ); } else if (currentFrame === 1) { - ctx.drawImage(tileSet, 32, 128, 32, 32, 0, 0, 32, 32); + ctx.drawImage( + tileSet, + TILE_X + WIDTH, + TILE_Y, + WIDTH, + HEIGHT, + 0, + 0, + WIDTH, + HEIGHT + ); } else if (currentFrame === 2) { - ctx.drawImage(tileSet, 64, 128, 32, 32, 0, 0, 32, 32); + ctx.drawImage( + tileSet, + TILE_X + WIDTH * 2, + TILE_Y, + WIDTH, + HEIGHT, + 0, + 0, + WIDTH, + HEIGHT + ); } else if (currentFrame === 3) { - ctx.drawImage(tileSet, 96, 128, 32, 32, 0, 0, 32, 32); + ctx.drawImage( + tileSet, + TILE_X + WIDTH * 3, + TILE_Y, + WIDTH, + HEIGHT, + 0, + 0, + WIDTH, + HEIGHT + ); } - currentFrame = currentFrame === 3 ? 0 : currentFrame + 1; + currentFrame = + currentFrame === ANIMATION_LENGTH ? 0 : currentFrame + 1; }, 100); }; } }, [left, top]); - return ; + return ; }; export default Coin; diff --git a/src/components/Fire/index.tsx b/src/components/Fire/index.tsx index 8458675..8a0bdc7 100644 --- a/src/components/Fire/index.tsx +++ b/src/components/Fire/index.tsx @@ -1,6 +1,13 @@ import { useEffect, FunctionComponent } from "react"; +import { TILE_SIZE, TILE_SETS } from "../../constants"; import "./style.css"; +const WIDTH = TILE_SIZE; +const HEIGHT = TILE_SIZE; +const TILE_X = 130; +const TILE_Y = 98; +const ANIMATION_LENGTH = 6; + /* * TODO: * - useRef instead of getElementById @@ -24,36 +31,107 @@ const Fire: FunctionComponent<{ left: number; top: number }> = ({ canvas.style.top = `${top}px`; const tileSet = new Image(); - tileSet.src = "assets/objects.png"; + tileSet.src = TILE_SETS.Objects; tileSet.onload = () => { let currentFrame = 0; setInterval(() => { - ctx.clearRect(0, 0, 32, 32); + ctx.clearRect(0, 0, WIDTH, HEIGHT); if (currentFrame === 0) { - ctx.drawImage(tileSet, 130, 98, 32, 32, 0, 0, 32, 32); + ctx.drawImage( + tileSet, + TILE_X, + TILE_Y, + WIDTH, + HEIGHT, + 0, + 0, + WIDTH, + HEIGHT + ); } else if (currentFrame === 1) { - ctx.drawImage(tileSet, 162, 98, 32, 32, 0, 0, 32, 32); + ctx.drawImage( + tileSet, + TILE_X + WIDTH, + TILE_Y, + WIDTH, + HEIGHT, + 0, + 0, + WIDTH, + HEIGHT + ); } else if (currentFrame === 2) { - ctx.drawImage(tileSet, 194, 98, 32, 32, 0, 0, 32, 32); + ctx.drawImage( + tileSet, + TILE_X + WIDTH * 2, + TILE_Y, + WIDTH, + HEIGHT, + 0, + 0, + WIDTH, + HEIGHT + ); } else if (currentFrame === 3) { - ctx.drawImage(tileSet, 226, 98, 32, 32, 0, 0, 32, 32); + ctx.drawImage( + tileSet, + TILE_X + WIDTH * 3, + TILE_Y, + WIDTH, + HEIGHT, + 0, + 0, + WIDTH, + HEIGHT + ); } else if (currentFrame === 4) { - ctx.drawImage(tileSet, 258, 98, 32, 32, 0, 0, 32, 32); + ctx.drawImage( + tileSet, + TILE_X + WIDTH * 4, + TILE_Y, + WIDTH, + HEIGHT, + 0, + 0, + WIDTH, + HEIGHT + ); } else if (currentFrame === 5) { - ctx.drawImage(tileSet, 290, 98, 32, 32, 0, 0, 32, 32); + ctx.drawImage( + tileSet, + TILE_X + WIDTH * 5, + TILE_Y, + WIDTH, + HEIGHT, + 0, + 0, + WIDTH, + HEIGHT + ); } else if (currentFrame === 6) { - ctx.drawImage(tileSet, 322, 98, 32, 32, 0, 0, 32, 32); + ctx.drawImage( + tileSet, + TILE_X + WIDTH * 6, + TILE_Y, + WIDTH, + HEIGHT, + 0, + 0, + WIDTH, + HEIGHT + ); } - currentFrame = currentFrame === 6 ? 0 : currentFrame + 1; + currentFrame = + currentFrame === ANIMATION_LENGTH ? 0 : currentFrame + 1; }, 125); }; } }, [left, top]); - return ; + return ; }; export default Fire; diff --git a/src/components/Heart/index.tsx b/src/components/Heart/index.tsx index fb6b04a..12e19ac 100644 --- a/src/components/Heart/index.tsx +++ b/src/components/Heart/index.tsx @@ -1,6 +1,13 @@ import { useEffect, FunctionComponent } from "react"; +import { TILE_SIZE, TILE_SETS } from "../../constants"; import "./style.css"; +const WIDTH = TILE_SIZE; +const HEIGHT = TILE_SIZE; +const TILE_X = 0; +const TILE_Y = 96; +const ANIMATION_LENGTH = 3; + /* * TODO: * - useRef instead of getElementById @@ -24,30 +31,71 @@ const Heart: FunctionComponent<{ left: number; top: number }> = ({ canvas.style.top = `${top}px`; const tileSet = new Image(); - tileSet.src = "assets/objects.png"; + tileSet.src = TILE_SETS.Objects; tileSet.onload = () => { let currentFrame = 0; setInterval(() => { - ctx.clearRect(0, 0, 32, 32); + ctx.clearRect(0, 0, WIDTH, HEIGHT); if (currentFrame === 0) { - ctx.drawImage(tileSet, 0, 96, 32, 32, 0, 0, 32, 32); + ctx.drawImage( + tileSet, + TILE_X, + TILE_Y, + WIDTH, + HEIGHT, + 0, + 0, + WIDTH, + HEIGHT + ); } else if (currentFrame === 1) { - ctx.drawImage(tileSet, 32, 96, 32, 32, 0, 0, 32, 32); + ctx.drawImage( + tileSet, + TILE_X + WIDTH, + TILE_Y, + WIDTH, + HEIGHT, + 0, + 0, + WIDTH, + HEIGHT + ); } else if (currentFrame === 2) { - ctx.drawImage(tileSet, 64, 96, 32, 32, 0, 0, 32, 32); + ctx.drawImage( + tileSet, + TILE_X + WIDTH * 2, + TILE_Y, + WIDTH, + HEIGHT, + 0, + 0, + WIDTH, + HEIGHT + ); } else if (currentFrame === 3) { - ctx.drawImage(tileSet, 96, 96, 32, 32, 0, 0, 32, 32); + ctx.drawImage( + tileSet, + TILE_X + WIDTH * 3, + TILE_Y, + WIDTH, + HEIGHT, + 0, + 0, + WIDTH, + HEIGHT + ); } - currentFrame = currentFrame === 3 ? 0 : currentFrame + 1; + currentFrame = + currentFrame === ANIMATION_LENGTH ? 0 : currentFrame + 1; }, 75); }; } }, [left, top]); - return ; + return ; }; export default Heart; diff --git a/src/components/House/index.tsx b/src/components/House/index.tsx index fd30d71..55af9d5 100644 --- a/src/components/House/index.tsx +++ b/src/components/House/index.tsx @@ -1,6 +1,12 @@ import { useEffect, FunctionComponent } from "react"; +import { TILE_SETS } from "../../constants"; import "./style.css"; +const WIDTH = 148; +const HEIGHT = 160; +const TILE_X = 198; +const TILE_Y = 0; + /* * TODO: * - useRef instead of getElementById @@ -20,14 +26,24 @@ const House: FunctionComponent = () => { canvas.style.top = "192px"; const tileSet = new Image(); - tileSet.src = "assets/overworld.png"; + tileSet.src = TILE_SETS.World; tileSet.onload = () => { - ctx.drawImage(tileSet, 198, 0, 148, 160, 0, 0, 148, 160); + ctx.drawImage( + tileSet, + TILE_X, + TILE_Y, + WIDTH, + HEIGHT, + 0, + 0, + WIDTH, + HEIGHT + ); }; } }, []); - return ; + return ; }; export default House; diff --git a/src/components/Lever/index.tsx b/src/components/Lever/index.tsx index 92d5b66..c69c16c 100644 --- a/src/components/Lever/index.tsx +++ b/src/components/Lever/index.tsx @@ -1,6 +1,12 @@ import { useEffect, FunctionComponent } from "react"; +import { TILE_SIZE, TILE_SETS } from "../../constants"; import "./style.css"; +const WIDTH = TILE_SIZE; +const HEIGHT = TILE_SIZE; +const TILE_X = 64; +const TILE_Y = 288; + /* * TODO: * - useRef instead of getElementById @@ -26,20 +32,40 @@ const Lever: FunctionComponent<{ canvas.style.top = `${top}px`; const tileSet = new Image(); - tileSet.src = "assets/objects.png"; + tileSet.src = TILE_SETS.Objects; tileSet.onload = () => { - ctx.clearRect(0, 0, 32, 32); + ctx.clearRect(0, 0, WIDTH, HEIGHT); onInteract(used); if (used) { - ctx.drawImage(tileSet, 96, 288, 32, 32, 0, 0, 32, 32); + ctx.drawImage( + tileSet, + TILE_X + WIDTH, + TILE_Y, + WIDTH, + HEIGHT, + 0, + 0, + WIDTH, + HEIGHT + ); } else { - ctx.drawImage(tileSet, 64, 288, 32, 32, 0, 0, 32, 32); + ctx.drawImage( + tileSet, + TILE_X, + TILE_Y, + WIDTH, + HEIGHT, + 0, + 0, + WIDTH, + HEIGHT + ); } }; } }, [left, top, used, onInteract]); - return ; + return ; }; export default Lever; diff --git a/src/components/Npc/index.tsx b/src/components/Npc/index.tsx index ff784e2..ed03469 100644 --- a/src/components/Npc/index.tsx +++ b/src/components/Npc/index.tsx @@ -1,7 +1,14 @@ import { useEffect, FunctionComponent } from "react"; +import { TILE_SETS } from "../../constants"; import "./style.css"; -let increment = 10; +const WIDTH = 32; +const HEIGHT = 48; +const TILE_X = 0; +const TILE_Y = 8; +const HUE_STEP = 10; + +let increment = HUE_STEP; /* * TODO: @@ -26,10 +33,20 @@ const Npc: FunctionComponent<{ canvas.style.top = `${top}px`; const tileSet = new Image(); - tileSet.src = "assets/npc.png"; + tileSet.src = TILE_SETS.Npc; tileSet.onload = () => { - ctx.clearRect(0, 0, 48, 48); - ctx.drawImage(tileSet, 0, 8, 32, 48, 8, 0, 32, 48); + ctx.clearRect(0, 0, WIDTH, HEIGHT); + ctx.drawImage( + tileSet, + TILE_X, + TILE_Y, + WIDTH, + HEIGHT, + 0, + 0, + WIDTH, + HEIGHT + ); }; window.setInterval(() => { @@ -37,9 +54,9 @@ const Npc: FunctionComponent<{ canvas.style.filter.match(/\d+/)?.[0] || "0" ); if (currentHue === 360) { - increment = -10; + increment = -HUE_STEP; } else if (currentHue === 0) { - increment = 10; + increment = HUE_STEP; } const hue = Math.max(0, Math.min(360, currentHue + increment)); canvas.style.filter = `hue-rotate(${hue}deg)`; @@ -47,7 +64,6 @@ const Npc: FunctionComponent<{ } }, [left, top]); - return ; + return ; }; - export default Npc; diff --git a/src/components/Player/index.tsx b/src/components/Player/index.tsx index b287cc0..13b98ae 100644 --- a/src/components/Player/index.tsx +++ b/src/components/Player/index.tsx @@ -1,6 +1,14 @@ -import { useEffect, FunctionComponent } from "react"; +import { useEffect, FunctionComponent, useContext } from "react"; +import { GAME_STATES, TILE_SETS, TILE_SIZE } from "../../constants"; +import { GlobalContext } from "../../contexts"; import "./style.css"; +const WIDTH = 32; +const HEIGHT = 48; +const TILE_X = 0; +const TILE_Y = 8; +const ANIMATION_LENGTH = 3; + /* * TODO: * - 2D Vectors for movement direction @@ -19,6 +27,7 @@ const Player: FunctionComponent<{ onInteract: (isOpen: boolean | ((wasOpen: boolean) => boolean)) => void; onCollision: (health: number | ((prev: number) => number)) => void; }> = ({ health, onInteract, onCollision }) => { + const { setGameState } = useContext(GlobalContext); useEffect(() => { const canvas = document.getElementById( "player-canvas" @@ -40,7 +49,7 @@ const Player: FunctionComponent<{ const ctx = healthCanvas.getContext("2d"); if (ctx) { const tileSet = new Image(); - tileSet.src = "assets/objects.png"; + tileSet.src = TILE_SETS.Objects; tileSet.onload = () => { ctx.clearRect(0, 0, 30, 26); if (health === 4) { @@ -65,25 +74,75 @@ const Player: FunctionComponent<{ if (ctx) { const tileSet = new Image(); - tileSet.src = "assets/character.png"; + tileSet.src = TILE_SETS.Player; tileSet.onload = () => { let keyPressed = false; let direction = "down"; let currentFrame = 0; - ctx.drawImage(tileSet, 0, 8, 32, 48, 8, 0, 32, 48); + ctx.drawImage( + tileSet, + TILE_X, + TILE_Y, + WIDTH, + HEIGHT, + 0, + 0, + WIDTH, + HEIGHT + ); window.onkeyup = () => { currentFrame = 0; keyPressed = false; - ctx.clearRect(0, 0, 48, 48); + ctx.clearRect(0, 0, WIDTH, HEIGHT); if (direction === "up") { - ctx.drawImage(tileSet, 0, 136, 32, 48, 8, 0, 32, 48); + ctx.drawImage( + tileSet, + TILE_X, + TILE_Y + TILE_SIZE * 4, + WIDTH, + HEIGHT, + 0, + 0, + WIDTH, + HEIGHT + ); } else if (direction === "left") { - ctx.drawImage(tileSet, 0, 200, 32, 48, 8, 0, 32, 48); + ctx.drawImage( + tileSet, + TILE_X, + TILE_Y + TILE_SIZE * 6, + WIDTH, + HEIGHT, + 0, + 0, + WIDTH, + HEIGHT + ); } else if (direction === "down") { - ctx.drawImage(tileSet, 0, 8, 32, 48, 8, 0, 32, 48); + ctx.drawImage( + tileSet, + TILE_X, + TILE_Y, + WIDTH, + HEIGHT, + 0, + 0, + WIDTH, + HEIGHT + ); } else if (direction === "right") { - ctx.drawImage(tileSet, 0, 72, 32, 48, 8, 0, 32, 48); + ctx.drawImage( + tileSet, + TILE_X, + TILE_Y + TILE_SIZE * 2, + WIDTH, + HEIGHT, + 0, + 0, + WIDTH, + HEIGHT + ); } }; @@ -193,77 +252,240 @@ const Player: FunctionComponent<{ if (!keyPressed) { keyPressed = true; - ctx.clearRect(0, 0, 48, 48); + ctx.clearRect(0, 0, WIDTH, HEIGHT); if (currentFrame === 0) { if (direction === "up") { - ctx.drawImage(tileSet, 0, 136, 32, 48, 8, 0, 32, 48); + ctx.drawImage( + tileSet, + TILE_X, + TILE_Y + TILE_SIZE * 4, + WIDTH, + HEIGHT, + 0, + 0, + WIDTH, + HEIGHT + ); } if (direction === "left") { - ctx.drawImage(tileSet, 0, 200, 32, 48, 8, 0, 32, 48); + ctx.drawImage( + tileSet, + TILE_X, + TILE_Y + TILE_SIZE * 6, + WIDTH, + HEIGHT, + 0, + 0, + WIDTH, + HEIGHT + ); } if (direction === "down") { - ctx.drawImage(tileSet, 0, 8, 32, 48, 8, 0, 32, 48); + ctx.drawImage( + tileSet, + TILE_X, + TILE_Y, + WIDTH, + HEIGHT, + 0, + 0, + WIDTH, + HEIGHT + ); } if (direction === "right") { - ctx.drawImage(tileSet, 0, 72, 32, 48, 8, 0, 32, 48); + ctx.drawImage( + tileSet, + TILE_X, + TILE_Y + TILE_SIZE * 2, + WIDTH, + HEIGHT, + 0, + 0, + WIDTH, + HEIGHT + ); } } else if (currentFrame === 1) { if (direction === "up") { - ctx.drawImage(tileSet, 32, 136, 32, 48, 8, 0, 32, 48); + ctx.drawImage( + tileSet, + TILE_X + WIDTH, + TILE_Y + TILE_SIZE * 4, + WIDTH, + HEIGHT, + 0, + 0, + WIDTH, + HEIGHT + ); } if (direction === "left") { - ctx.drawImage(tileSet, 32, 200, 32, 48, 8, 0, 32, 48); + ctx.drawImage( + tileSet, + TILE_X + WIDTH, + TILE_Y + TILE_SIZE * 6, + WIDTH, + HEIGHT, + 0, + 0, + WIDTH, + HEIGHT + ); } if (direction === "down") { - ctx.drawImage(tileSet, 32, 8, 32, 48, 8, 0, 32, 48); + ctx.drawImage( + tileSet, + TILE_X + WIDTH, + TILE_Y, + WIDTH, + HEIGHT, + 0, + 0, + WIDTH, + HEIGHT + ); } if (direction === "right") { - ctx.drawImage(tileSet, 32, 72, 32, 48, 8, 0, 32, 48); + ctx.drawImage( + tileSet, + TILE_X + WIDTH, + TILE_Y + TILE_SIZE * 2, + WIDTH, + HEIGHT, + 0, + 0, + WIDTH, + HEIGHT + ); } } else if (currentFrame === 2) { if (direction === "up") { - ctx.drawImage(tileSet, 0, 136, 32, 48, 8, 0, 32, 48); + ctx.drawImage( + tileSet, + TILE_X, + TILE_Y + TILE_SIZE * 4, + WIDTH, + HEIGHT, + 0, + 0, + WIDTH, + HEIGHT + ); } if (direction === "left") { - ctx.drawImage(tileSet, 0, 200, 32, 48, 8, 0, 32, 48); + ctx.drawImage( + tileSet, + TILE_X, + TILE_Y + TILE_SIZE * 6, + WIDTH, + HEIGHT, + 0, + 0, + WIDTH, + HEIGHT + ); } if (direction === "down") { - ctx.drawImage(tileSet, 0, 8, 32, 48, 8, 0, 32, 48); + ctx.drawImage( + tileSet, + TILE_X, + TILE_Y, + WIDTH, + HEIGHT, + 0, + 0, + WIDTH, + HEIGHT + ); } if (direction === "right") { - ctx.drawImage(tileSet, 0, 72, 32, 48, 8, 0, 32, 48); + ctx.drawImage( + tileSet, + TILE_X, + TILE_Y + TILE_SIZE * 2, + WIDTH, + HEIGHT, + 0, + 0, + WIDTH, + HEIGHT + ); } } else if (currentFrame === 3) { if (direction === "up") { - ctx.drawImage(tileSet, 96, 136, 32, 48, 8, 0, 32, 48); + ctx.drawImage( + tileSet, + TILE_X + WIDTH * 3, + TILE_Y + TILE_SIZE * 4, + WIDTH, + HEIGHT, + 0, + 0, + WIDTH, + HEIGHT + ); } if (direction === "left") { - ctx.drawImage(tileSet, 96, 200, 32, 48, 8, 0, 32, 48); + ctx.drawImage( + tileSet, + TILE_X + WIDTH * 3, + TILE_Y + TILE_SIZE * 6, + WIDTH, + HEIGHT, + 0, + 0, + WIDTH, + HEIGHT + ); } if (direction === "down") { - ctx.drawImage(tileSet, 96, 8, 32, 48, 8, 0, 32, 48); + ctx.drawImage( + tileSet, + TILE_X + WIDTH * 3, + TILE_Y, + WIDTH, + HEIGHT, + 0, + 0, + WIDTH, + HEIGHT + ); } if (direction === "right") { - ctx.drawImage(tileSet, 96, 72, 32, 48, 8, 0, 32, 48); + ctx.drawImage( + tileSet, + TILE_X + WIDTH * 3, + TILE_Y + TILE_SIZE * 2, + WIDTH, + HEIGHT, + 0, + 0, + WIDTH, + HEIGHT + ); } } setTimeout(() => { keyPressed = false; - currentFrame = currentFrame === 3 ? 0 : currentFrame + 1; + currentFrame = + currentFrame === ANIMATION_LENGTH ? 0 : currentFrame + 1; }, 125); } + } else { + setGameState(GAME_STATES.GameOver); } }; }; } } - }, [onInteract, onCollision, health]); + }, [onInteract, onCollision, health, setGameState]); return ( <> - + ); diff --git a/src/components/Player/style.css b/src/components/Player/style.css index b92f69b..51d62d6 100644 --- a/src/components/Player/style.css +++ b/src/components/Player/style.css @@ -4,6 +4,6 @@ #health-canvas { z-index: 99; - top: 8px; - left: 8px; + top: calc((1536px - 100vh) / 2 + 8px); + left: calc((2048px - 100vw) / 2 + 8px); } diff --git a/src/components/World/index.tsx b/src/components/World/index.tsx index 7a3f7dd..43d6f60 100644 --- a/src/components/World/index.tsx +++ b/src/components/World/index.tsx @@ -1,7 +1,16 @@ import { useEffect } from "react"; +import { + WORLD_WIDTH, + WORLD_HEIGHT, + TILE_SIZE, + TILE_SETS, +} from "../../constants"; import level from "../../level.json"; import "./style.css"; +// Number of tiles in the tilset +const TILES_COUNT = 40; + /* * Refactor: * - useRef instead of getElementById @@ -19,30 +28,31 @@ export default function World() { if (canvas && ctx) { const tileSet = new Image(); - tileSet.src = "assets/overworld.png"; + tileSet.src = TILE_SETS.World; tileSet.onload = () => { level.layers.forEach(({ chunks }) => { let i = 0; for (let y = 0; y < level.height; y++) { for (let x = 0; x < level.width; x++) { + // Tiled exports tiles counting from 1 rather than 0 const tile = chunks[0].data[i++] - 1; if (tile < 0) { continue; } - const tileX = (tile % 40) * 32; - const tileY = Math.floor(tile / 40) * 32; + const tileX = (tile % TILES_COUNT) * TILE_SIZE; + const tileY = Math.floor(tile / TILES_COUNT) * TILE_SIZE; ctx.drawImage( tileSet, tileX, tileY, - 32, - 32, - x * 32, - y * 32, - 32, - 32 + TILE_SIZE, + TILE_SIZE, + x * TILE_SIZE, + y * TILE_SIZE, + TILE_SIZE, + TILE_SIZE ); } } @@ -51,5 +61,11 @@ export default function World() { } }, []); - return ; + return ( + + ); } diff --git a/src/constants.ts b/src/constants.ts new file mode 100644 index 0000000..5073bea --- /dev/null +++ b/src/constants.ts @@ -0,0 +1,15 @@ +export enum GAME_STATES { + Game, + GameOver, +} + +export enum TILE_SETS { + Player = "assets/character.png", + Npc = "assets/npc.png", + Objects = "assets/objects.png", + World = "assets/overworld.png", +} + +export const TILE_SIZE = 32; +export const WORLD_WIDTH = 2048; +export const WORLD_HEIGHT = 1536; diff --git a/src/contexts/global.ts b/src/contexts/global.ts new file mode 100644 index 0000000..4c5a3a0 --- /dev/null +++ b/src/contexts/global.ts @@ -0,0 +1,12 @@ +import { createContext } from "react"; +import { GAME_STATES } from "../constants"; + +export type GlobalContextType = { + readonly gameState: GAME_STATES; + setGameState(newState: GAME_STATES): void; +}; + +export const GlobalContext = createContext({ + gameState: GAME_STATES.Game, + setGameState: () => {}, +}); diff --git a/src/contexts/index.ts b/src/contexts/index.ts new file mode 100644 index 0000000..0937023 --- /dev/null +++ b/src/contexts/index.ts @@ -0,0 +1 @@ +export * from "./global";