aboutsummaryrefslogtreecommitdiffstats
path: root/src/ui.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui.ts')
-rw-r--r--src/ui.ts170
1 files changed, 154 insertions, 16 deletions
diff --git a/src/ui.ts b/src/ui.ts
index 95ceb5c..e942642 100644
--- a/src/ui.ts
+++ b/src/ui.ts
@@ -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