export const ATLAS_TILE_WIDTH = 32 export const GAME_MAP_COUNT = 4 const loadAudio = (src: string, callback: (node: GameAudio) => void) => { let actx = new AudioContext() fetch(src, {mode: "cors"}) .then((resp) => { return resp.arrayBuffer() }) .then((ebuf) => { actx.decodeAudioData(ebuf, (abuf) => { var sourceNode = null, startedAt = 0, pausedAt = 0, playing = false var play = (loop: boolean) => { var offset = pausedAt sourceNode = actx.createBufferSource() sourceNode.connect(actx.destination) sourceNode.buffer = abuf sourceNode.start(0, offset) sourceNode.loop = loop startedAt = actx.currentTime - offset pausedAt = 0 playing = true } var pause = () => { var elapsed = actx.currentTime - startedAt stop() pausedAt = elapsed }; var stop = () => { if (sourceNode) { sourceNode.disconnect() sourceNode.stop(0) sourceNode = null } pausedAt = 0 startedAt = 0 playing = false } var getPlaying = (): boolean => { return playing } callback({play, pause, stop, getPlaying}) }) }) } let EMPTY_AUDIO: GameAudio = { play: (_loop: boolean) => {}, pause: () => {}, stop: () => {}, getPlaying: () => true, } export var INTRO_AUDIO = EMPTY_AUDIO export var DEATH_AUDIO = EMPTY_AUDIO export var MOVE_AUDIO = EMPTY_AUDIO export var GHOST_AUDIO = EMPTY_AUDIO loadAudio('sfx/intro.mp3', (node) => INTRO_AUDIO = node) loadAudio('sfx/death.mp3', (node) => DEATH_AUDIO = node) loadAudio('sfx/move.wav', (node) => MOVE_AUDIO = node) loadAudio('sfx/ghost.wav', (node) => GHOST_AUDIO = node) export enum Tile { EMPTY = 0, WALL = 1, GHOST_WALL = 2, GHOST_EXIT = 11, FOOD = 3, PLAYER_SPAWN_1 = 4, PLAYER_SPAWN_2 = 5, PLAYER_SPAWN_3 = 6, PLAYER_SPAWN_4 = 7, GHOST_SPAWN = 8, THICC_DOT = 9, INITIAL_DOT = 10 } export enum Wall { EMPTY, WALL_HZ, WALL_VT, TURN_Q1, TURN_Q2, TURN_Q3, TURN_Q4, TEE_NORTH, TEE_EAST, TEE_SOUTH, TEE_WEST, CROSS, DOT, WALL_END_NORTH, WALL_END_SOUTH, WALL_END_EAST, WALL_END_WEST } export enum ItemType { DOT, THICC_DOT, FOOD } export enum Key { NOTHING, UP, DOWN, LEFT, RIGHT } export enum GhostType { BLINKY = 0, PINKY = 1, INKY = 2, CLYDE = 3 } export enum GhostState { CHASE, SCATTER, EATEN, SCARED } export type Ghost = { pos: Vec2, type: GhostType, target: Vec2, targetQueue: Vec2[], state: GhostState, currentDirection: Rotation, hasRespawned: boolean, framesInBox: number, } export type KeyMap = { [key: string]: Key } export const GameKeyMap = { "KeyW": Key.UP, "KeyA": Key.LEFT, "KeyS": Key.DOWN, "KeyD": Key.RIGHT, } export enum Rotation { NOTHING, NORTH, EAST, SOUTH, WEST } export type Vec2 = { x: number, y: number } export type InputMap = { [key: number]: Key } export type Player = { pos: Vec2, moveRotation: Rotation, inputRotation: Rotation, velocityRotation: Rotation, moving: boolean, thiccLeft: number, name?: string, dead: boolean, framesDead: number, atePellets: number, } export type PlayerInput = { start: boolean, key: Key, name?: string } export type Input = { players: {[key: number]: PlayerInput}, added?: number[], removed?: number[], } export type Message = { type?: string; connections?: number[], added?: number, removed?: number, id?: number, frame?: number, data?: any, connection?: number, state?: GameState, error?: string } export type Players = { [key: number]: Player } export type Item = { type: ItemType, pos: Vec2 } export type Items = { [key: number]: Item } export enum SpawnIndex { PAC_SPAWN_1 = 1, PAC_SPAWN_2 = 2, PAC_SPAWN_3 = 3, PAC_SPAWN_4 = 4, GHOST_SPAWN = 0, GHOST_EXIT = 5 } export type Map = { data: number[], walls: number[], width: number, height: number, id: number, spawns?: Vec2[] } export type Maps = { [key: number]: Map } export type Ghosts = [Ghost, Ghost, Ghost, Ghost] export type GameState = { started: boolean, input: InputMap, players: Players, ghosts: Ghosts, items: Items, frame: number, rng: number, mapId: number | undefined, roundTimer: number, endRoundTimer: number } export type Frame = { data: GameState, input: Input } export type GameAudio = { getPlaying: () => boolean, play: (loop: boolean) => void, pause: () => void, stop: () => void } export type BoundingBox = { x: number, y: number, w: number, h: number }