import Graphics from "./graphics"; import UI from "./ui"; import World from "./world"; import { pointsEquals, prevent } from "./utils"; export default class Binario implements IGame { private running = false; private mouseDown: false | number = false; private graphics; private world; private ui; private prevWorldPos: Point | null = null; private prevFrame: number = performance.now(); private paused = false; constructor(private canvas: HTMLCanvasElement) { canvas.focus(); canvas.addEventListener('wheel', this.onScroll); canvas.addEventListener('mousedown', this.onMouseDown); canvas.addEventListener('mousemove', this.onMouseMove); canvas.addEventListener('mouseup', this.onMouseUp); document.addEventListener('keypress', this.onKeyPress); document.addEventListener('contextmenu', prevent); document.addEventListener('select', prevent); document.addEventListener('selectstart', prevent); this.graphics = new Graphics(canvas); this.world = new World(); this.ui = new UI(); window.addEventListener('resize', this.onResize); this.onResize(); this.graphics.resetView(); } private onResize = () => { this.canvas.width = window.innerWidth; this.canvas.height = window.innerHeight; this.graphics.resetStyle(); } private onScroll = (event: WheelEvent) => { const direction = event.deltaY / Math.abs(event.deltaY); let scale = 1; if (direction < 0) { scale = 1.1; } else if (direction > 0) { scale = 1 / 1.1; } this.graphics.applyScale(scale, [event.clientX, event.clientY]); event.preventDefault(); } private onMouseDown = (event: MouseEvent) => { this.mouseDown = event.button; event.preventDefault(); } private onMouseUp = (event: MouseEvent) => { this.canvas.style.cursor = 'default'; this.mouseDown = false; this.prevWorldPos = null; const mousePos: Point = [event.clientX, event.clientY]; const worldPos = this.graphics.screenToWorld(mousePos); if (event.button === 0 && this.ui.selectedTool.tileType != null) { const tileType = this.ui.selectedTool.tileType; this.world.placeTile(worldPos, tileType); } else if (event.button === 2) { this.world.removeTile(worldPos); } event.preventDefault(); } private onMouseMove = (event: MouseEvent) => { const mousePos: Point = [event.clientX, event.clientY]; const mouseDelta: Point = [event.movementX, event.movementY]; const worldPos = this.graphics.screenToWorld(mousePos); if (this.mouseDown === 1) { this.canvas.style.cursor = 'grabbing'; this.graphics.pan(mouseDelta); } else if (this.mouseDown === 0 && this.ui.selectedTool.tileType != null) { const tileType = this.ui.selectedTool.tileType; this.world.placeTile(worldPos, tileType, this.prevWorldPos); } else if (this.mouseDown === 2) { this.world.removeTile(worldPos); } if (!this.prevWorldPos || !pointsEquals(this.prevWorldPos, worldPos)) { this.prevWorldPos = worldPos; } this.graphics.highlight(mousePos); const tile = this.world.getTile(worldPos); if (tile) { this.graphics.showTooltip(mousePos, JSON.stringify(tile, null, 2)); } else { this.graphics.showTooltip(mousePos, ''); } event.preventDefault(); } private onKeyPress = (event: KeyboardEvent) => { const key = event.key.toLowerCase(); if (key === 'h') { this.graphics.resetView(); } else if (key === ' ') { this.paused = !this.paused; } else if (key.length === 1 && key >= '1' && key <= '9') { const slot = parseInt(key); this.ui.useSlot(slot); } else { console.log(`Pressed: ${key}`); } } run() { this.running = true; this.loop(); } private loop = () => { const now = performance.now(); const dt = now - this.prevFrame; this.prevFrame = now; if (!this.paused) { this.world.update(dt); } this.graphics.clear(); this.graphics.drawGrid(); this.graphics.drawWorld(this.world); this.graphics.drawHighlight(); this.graphics.drawTooltip(); this.graphics.debug(); if (this.running) { requestAnimationFrame(this.loop); } } }