Froobtris/src/index.ts
2023-08-02 14:20:48 -04:00

103 lines
3.2 KiB
TypeScript

import { KeyEventType, Keyboard } from "./lib/controller";
import { TextureStore } from "./textureStore";
import { $, createCanvas } from "./lib/util";
import { expect, unwrap } from "./lib/option";
import { GameState } from "./gameState";
import { Sidebar } from "./sidebar";
import { KeyEvent } from "./keyEvent";
import {
PLAYFIELD_HEIGHT,
PLAYFIELD_WIDTH,
SIDEBAR_HEIGHT,
SIDEBAR_WIDTH,
} from "./constants";
// Throughout these source files, you will find methods like `expect` and `ifSome` as well as type aliases like `Option<T>`.
// These are a recreation of rust's (programming language) option type. They are found in lib/option.ts
// The game is made up of a few fundamental components
// - The TextureStore -- Loads textures over the network, holds the texture references, passed to all drawing methods
// - The Controller -- Listens to input events and allows for polling of events
// - The SideBar -- Contains the current score and the reference to the next Tetromino. Tetrominos are selected in the SideBar
// - The GameState -- Self explanatory; everything else
// The main function orchestrates all components
async function main(): Promise<void> {
const { canvas: playfieldCanvas, ctx: playfieldContext } = expect(
createCanvas(PLAYFIELD_WIDTH, PLAYFIELD_HEIGHT),
"Could not create playfield canvas"
);
expect(
$("pfcontainer"),
"Could not insert playfield canvas into DOM"
).appendChild(playfieldCanvas);
const { canvas: sidebarCanvas, ctx: sidebarContext } = expect(
createCanvas(SIDEBAR_WIDTH, SIDEBAR_HEIGHT),
"Could not create sidebar canvas"
);
expect(
$("sdcontainer"),
"Could not insert sidebar canvas into DOM"
).appendChild(sidebarCanvas);
const textures = await TextureStore.new();
const sidebar = new Sidebar();
const game = new GameState(sidebar);
const controller = new Keyboard<KeyEvent>();
controller.registerKeyEvent(["ArrowLeft", "a"], KeyEvent.Left);
controller.registerKeyEvent(["ArrowDown", "s"], KeyEvent.Down);
controller.registerKeyEvent(["ArrowRight", "d"], KeyEvent.Right);
controller.registerKeyEvent(
["ArrowUp", "w"],
KeyEvent.Up,
KeyEventType.OnlyOnce
);
// Main loop
function frame(): void {
const now = Date.now();
let gameReset = false;
controller.pollSpecificKey(KeyEvent.Up, () => game.rotate());
if (now - game.movementTimer > 80) {
game.movementTimer = now;
game.inputTick(controller.poll());
} else if (now - game.fallTimer > 500) {
game.fallTimer = now;
if (game.fallTick() === true) {
gameReset = true;
}
}
if (game.reDraw) {
game.drawPlayfield(playfieldContext, textures);
}
if (sidebar.reDraw) {
sidebar.draw(sidebarContext, textures);
}
if (gameReset) {
controller.clear();
const overlay = unwrap($("overlay"));
overlay.classList.remove("hidden");
const scoreCounter = unwrap($("scoreCounter"));
scoreCounter.textContent = `With a score of ${sidebar.score}`;
const tryAgain = unwrap($("tryAgain"));
tryAgain.addEventListener(
"click",
() => {
overlay.classList.add("hidden");
game.reset();
requestAnimationFrame(frame);
},
{ once: true }
);
} else {
requestAnimationFrame(frame);
}
}
requestAnimationFrame(frame);
}
document.addEventListener("DOMContentLoaded", main);