summaryrefslogtreecommitdiff
path: root/src/game/index.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/game/index.ts')
-rw-r--r--src/game/index.ts113
1 files changed, 113 insertions, 0 deletions
diff --git a/src/game/index.ts b/src/game/index.ts
new file mode 100644
index 0000000..3ab20ca
--- /dev/null
+++ b/src/game/index.ts
@@ -0,0 +1,113 @@
+import { GAME_SIZE, PADDLE_HEIGHT, PADDLE_WIDTH, SessionState } from "../state";
+import { VELOCITY_SCALING_FACTOR } from "./const";
+import { Action, Collider } from "./types";
+import { applyBallBounce, applyBallVelocity } from "./utils";
+
+export const advanceState = async (
+ curState: SessionState,
+ action: Action | undefined
+): Promise<SessionState> => {
+ //simulate network
+ await new Promise((res) => setTimeout(res, 15));
+
+ let candidatePaddle = curState.localPlayerGameState.paddle;
+
+ if (action === Action.MOVE_LEFT) {
+ candidatePaddle.position[0] = Math.max(0, candidatePaddle.position[0] - 1);
+ } else if (action === Action.MOVE_RIGHT) {
+ candidatePaddle.position[0] = Math.min(
+ GAME_SIZE.cols - 1 - PADDLE_WIDTH,
+ candidatePaddle.position[0] + 1
+ );
+ }
+
+ let candidateBricks = curState.localPlayerGameState.bricks;
+
+ const colliders: Collider[] = [];
+
+ //paddle collider
+ colliders.push({
+ normal: [0, -1],
+ boundingBox: [
+ {
+ min: candidatePaddle.position[0],
+ max: candidatePaddle.position[0] + PADDLE_WIDTH,
+ },
+ {
+ min: candidatePaddle.position[1],
+ max: candidatePaddle.position[1] + PADDLE_HEIGHT,
+ },
+ ],
+ });
+
+ //brick colliders
+ candidateBricks.forEach(({ position }, i) => {
+ colliders.push({
+ boundingBox: position.map((pos) => ({
+ min: pos - 0.5,
+ max: pos + 0.5,
+ })) as Collider["boundingBox"],
+ normal: [0, 1],
+ onHit: () => candidateBricks.splice(i, 1),
+ });
+ });
+
+ //wall colliders
+ colliders.push(
+ ...([
+ //left wall
+ {
+ boundingBox: [
+ { min: -1, max: 0 },
+ { min: 0, max: GAME_SIZE.rows },
+ ],
+ normal: [1, 0],
+ },
+ //top wall
+ {
+ boundingBox: [
+ { min: -1, max: GAME_SIZE.cols + 1 },
+ { min: -1, max: 0 },
+ ],
+ normal: [0, 1],
+ },
+ //right wall
+ {
+ boundingBox: [
+ { min: GAME_SIZE.cols, max: GAME_SIZE.cols + 1 },
+ { min: 0, max: GAME_SIZE.rows },
+ ],
+ normal: [-1, 0],
+ },
+ ] satisfies Collider[])
+ );
+
+ const candidateBalls = curState.localPlayerGameState.balls
+ .map((ball) => {
+ let candidateBall = applyBallVelocity(ball);
+
+ const hitCollider = colliders.find(({ boundingBox }) =>
+ candidateBall.position.every(
+ (pos, i) => pos >= boundingBox[i].min && pos <= boundingBox[i].max
+ )
+ );
+
+ if (hitCollider) {
+ hitCollider.onHit && hitCollider.onHit();
+ candidateBall = applyBallBounce(candidateBall, hitCollider.normal);
+ }
+
+ return candidateBall;
+ })
+ .filter((ball) => !!ball);
+
+ return {
+ ...curState,
+ localPlayerGameState: {
+ ...curState.localPlayerGameState,
+ bricks: candidateBricks,
+ paddle: candidatePaddle,
+ balls: candidateBalls,
+ },
+ };
+};