diff options
Diffstat (limited to 'src/ui.ts')
| -rw-r--r-- | src/ui.ts | 170 |
1 files changed, 154 insertions, 16 deletions
@@ -1,42 +1,129 @@ -import Ball from "./Ball"; +/** + * @module UI + * The only place with side effects +*/ + +import { Ball } from "./Ball"; import { MAP_WIDTH, MAP_HEIGHT, TD_SIZE } from "./consts"; import { sleep } from "./utils"; import { findToRemove, genGraph, genMap } from "./map"; import findPath, { Path } from "./pathfinder"; +/** @internal */ let selectedBall: Ball | null = null; +/** @internal */ +let newBalls = <Ball[]>[]; +/** @internal */ let currentPath: Path = []; +/** @internal */ let turnIsChanging = false; -const setCurrentPath = (p: any) => { + +/** + * Setter for path, to automatically remove old one + * @param p - new path +*/ +const setCurrentPath = (p: Path) => { for (const v of currentPath) { getTd(v.x!, v.y!).style.backgroundColor = ""; } currentPath = p; } +/** Function that handles making new turn */ export async function nextTurn() { turnIsChanging = true; for (const v of currentPath) { getTd(v.x!, v.y!).style.backgroundColor = "rgba(50, 50, 50, 0.8)"; } - await sleep(1000); + // await sleep(1000); - const toRemove = findToRemove(window.balls); + await clearAndGenerateBalls(); + setCurrentPath([]); + turnIsChanging = false; +} +/** Function to clear balls that should be deleted and generate new ones, also check for some to delete after that */ +export async function clearAndGenerateBalls() { + let toRemove = findToRemove(window.balls); + const remFromGlobal = () => window.balls = window.balls.filter(ba => !toRemove.find(b => ba.isEqual(b))); + const colorNewBalls = () => newBalls.forEach(nb => nb.td.style.backgroundColor = "rgba(123, 45, 99, 0.8)") + const clearNewBalls = () => newBalls.forEach(nb => nb.td.style.backgroundColor = "") - window.balls = window.balls.filter(ba => !toRemove.find(b => ba.isEqual(b))) - for (const b of toRemove) { - clearTd(b.td) - } - pointsEl.innerText = (parseInt(pointsEl.innerText) + toRemove.length).toString() - setCurrentPath([]); - window.balls.push(...Ball.generateNewBalls(window.balls)); - render(); - turnIsChanging = false; + do { + await sleep(1000); + remFromGlobal() + pointsEl.innerText = (parseInt(pointsEl.innerText) + toRemove.length).toString() + clearNewBalls() + newBalls = Ball.generateNewBalls(window.balls) + colorNewBalls() + window.balls.push(...newBalls); + render() + if (window.balls.length >= MAP_WIDTH * MAP_HEIGHT) { + finishGame(); + return; + } + toRemove.clear() + toRemove = findToRemove(window.balls); + } while (toRemove.length) } +// export async function clearAndGenerateBalls2() { +// let toRemove = findToRemove(window.balls); + +// window.balls = window.balls.filter(ba => !toRemove.find(b => ba.isEqual(b))) +// toRemove.clear(); +// pointsEl.innerText = (parseInt(pointsEl.innerText) + toRemove.length).toString() + +// for (const ball of newBalls) { +// ball.td.style.backgroundColor = "" +// } +// newBalls = Ball.generateNewBalls(window.balls) +// for (const ball of newBalls) { +// ball.td.style.backgroundColor = "rgba(123, 45, 99, 0.8)" +// } +// window.balls.push(...newBalls); +// render(); +// if (window.balls.length >= MAP_WIDTH * MAP_HEIGHT) { +// console.log("too many balls") +// finishGame(); +// return; +// } +// toRemove = findToRemove(window.balls); + +// if (toRemove.length) { +// render(); +// await sleep(1000); +// window.balls = window.balls.filter(ba => !toRemove.find(b => ba.isEqual(b))) +// toRemove.clear(); +// pointsEl.innerText = (parseInt(pointsEl.innerText) + toRemove.length).toString() +// } +// if (window.balls.length >= MAP_WIDTH * MAP_HEIGHT) { +// finishGame(); +// return; +// } +// render(); +// } + +/** + * Function that handles finishing the game + * @param finish - should it finish the game or only do everything except it +*/ +export function finishGame(finish = true) { + turnIsChanging = true; + const gameTime = ((Date.now() - window.startTime) / 1000).toFixed(2) + if (finish) { + setTimeout(() => { + alert("You lost, because you are too bad even for simple Boules game, you have made " + pointsEl.innerText + " points. It took you " + `${gameTime} seconds`) + throw new Error("Game has been finished") + }, 1) + } +} +/** + * Function that is set as OnClickHandler for td elements + * @param _e - not used +*/ export function tdOnClick(_e: PointerEvent) { if (!currentPath.length || turnIsChanging) return; const std = getTd(currentPath[0].x!, currentPath[0].y!); @@ -48,6 +135,13 @@ export function tdOnClick(_e: PointerEvent) { render(window.balls); nextTurn(); } + +/** + * Function that is set as MouseEnter for td elements + * @param _e - not used + * @param x - x cordinate of td + * @param y - y cordinate of td +*/ export function tdOnMouseEnter(_e: PointerEvent, x: number, y: number) { if (!selectedBall || turnIsChanging) return; const graph = genGraph(genMap(window.balls), { x: selectedBall.x, y: selectedBall.y }, { x, y }); @@ -58,6 +152,11 @@ export function tdOnMouseEnter(_e: PointerEvent, x: number, y: number) { getTd(v.x!, v.y!).style.backgroundColor = "rgba(45, 123, 78, 0.6)"; } } + +/** + * Function that renders the balls + * @param balls - array of current {@link Ball} if null then use global balls +*/ export function render(balls?: Ball[]) { balls = balls ? balls : window.balls; for (const ball of balls) { @@ -66,11 +165,11 @@ export function render(balls?: Ball[]) { td.firstChild.style.backgroundColor = color; td.onmousedown = () => { - if (turnIsChanging) return; + if (turnIsChanging || ballCantMove(ball)) return; const circle = td.firstChild as HTMLDivElement; setCurrentPath([]); const smallerCircle = () => { - if (!selectedBall) return; + if (!selectedBall || turnIsChanging) return; selectedBall.td.firstChild.style.width = "" selectedBall.td.firstChild.style.height = "" } @@ -94,15 +193,37 @@ export function render(balls?: Ball[]) { }; } } + +/** + * Checks if selcted ball can mpve anywhere + * @param ball ball to check +*/ +export function ballCantMove(ball: Ball): boolean { + const sb = ball; + const top = !!window.balls.find(b => b.x === sb.x && b.y === sb.y + 1) || sb.y + 1 >= MAP_HEIGHT + const bottom = !!window.balls.find(b => b.x === sb.x && b.y === sb.y - 1) || sb.y - 1 < 0 + const right = !!window.balls.find(b => b.x === sb.x + 1 && b.y === sb.y) || sb.x + 1 >= MAP_WIDTH + const left = !!window.balls.find(b => b.x === sb.x - 1 && b.y === sb.y) || sb.x - 1 < 0 + + const out = (top && bottom && left && right) + return out; +} + +/** + * Function that clear visual look of given td + * @param td - td (HTML td element) +*/ export function clearTd(td: Td) { if (td.localName !== "td") { - console.log(td) throw new Error("Not td passed to clearTd()"); } td.innerHTML = "" td.onmousedown = null; + td.style.backgroundColor = "" td.appendChild(document.createElement("div")) } + +/** Funtion that generates game table */ export function genGameTable() { const table = getGameMapEl(); for (let i = 0; i < MAP_HEIGHT; i++) { @@ -119,13 +240,30 @@ export function genGameTable() { table.appendChild(tr) } } + +/** Setter for funny text on funny element */ +export function setFunnyText(t: string) { + document.querySelector("#funny")!.innerHTML = t; +} + +/** Getter for game map HTML element */ export function getGameMapEl(): HTMLTableElement { return document.querySelector('table')! } + +/** Fixes HTMLTableCellElement interface */ export interface Td extends HTMLTableCellElement { firstChild: HTMLDivElement; } + +/** + * Getter for td element at given cordinates + * @param x - x coordinate + * @param y - y coordinate + * @return wanted td +*/ export function getTd(x: number, y: number): Td { return getGameMapEl().children[x].children[y] as Td; } +/** HTML element when points are visible */ export const pointsEl = document.querySelector("h3 span") as HTMLSpanElement |
