2025-03-29 21:28:05 +01:00
|
|
|
import { GameState, RunParams } from "./types";
|
2025-03-18 14:16:12 +01:00
|
|
|
import { allLevels, upgrades } from "./loadGameData";
|
|
|
|
import {
|
2025-04-06 15:38:30 +02:00
|
|
|
defaultSounds,
|
|
|
|
getHighScore,
|
|
|
|
getPossibleUpgrades,
|
|
|
|
highScoreText,
|
|
|
|
reasonLevelIsLocked,
|
2025-03-18 14:16:12 +01:00
|
|
|
makeEmptyPerksMap,
|
2025-03-26 14:04:54 +01:00
|
|
|
sumOfValues,
|
2025-03-18 14:16:12 +01:00
|
|
|
} from "./game_utils";
|
|
|
|
import { dontOfferTooSoon, resetBalls } from "./gameStateMutators";
|
|
|
|
import { isOptionOn } from "./options";
|
2025-04-06 15:38:30 +02:00
|
|
|
import { getHistory } from "./gameOver";
|
|
|
|
import { getTotalScore } from "./settings";
|
|
|
|
|
|
|
|
export function getRunLevels(params: RunParams) {
|
|
|
|
const history = getHistory();
|
|
|
|
const unlocked = allLevels.filter(
|
|
|
|
(l, li) => !reasonLevelIsLocked(li, history),
|
|
|
|
);
|
2025-03-18 08:19:20 +01:00
|
|
|
|
2025-03-28 19:40:59 +01:00
|
|
|
const firstLevel = params?.level
|
2025-04-06 15:38:30 +02:00
|
|
|
? unlocked.filter((l) => l.name === params?.level)
|
2025-03-16 17:45:29 +01:00
|
|
|
: [];
|
2025-03-16 14:29:14 +01:00
|
|
|
|
2025-04-06 15:38:30 +02:00
|
|
|
const restInRandomOrder = unlocked
|
2025-03-16 17:45:29 +01:00
|
|
|
.filter((l) => l.name !== params?.level)
|
|
|
|
.filter((l) => l.name !== params?.levelToAvoid)
|
|
|
|
.sort(() => Math.random() - 0.5);
|
2025-03-16 14:29:14 +01:00
|
|
|
|
2025-04-06 18:21:53 +02:00
|
|
|
console.log("getRunLevels", {
|
|
|
|
params,
|
|
|
|
history,
|
|
|
|
unlocked,
|
|
|
|
firstLevel,
|
|
|
|
restInRandomOrder,
|
|
|
|
});
|
2025-03-28 19:40:59 +01:00
|
|
|
return firstLevel.concat(
|
2025-03-16 17:45:29 +01:00
|
|
|
restInRandomOrder.slice(0, 7 + 3).sort((a, b) => a.sortKey - b.sortKey),
|
|
|
|
);
|
2025-03-28 10:21:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
export function newGameState(params: RunParams): GameState {
|
2025-04-06 15:38:30 +02:00
|
|
|
const runLevels = getRunLevels(params);
|
2025-03-16 14:29:14 +01:00
|
|
|
|
2025-03-16 17:45:29 +01:00
|
|
|
const perks = { ...makeEmptyPerksMap(upgrades), ...(params?.perks || {}) };
|
2025-03-16 14:29:14 +01:00
|
|
|
|
2025-03-26 08:35:49 +01:00
|
|
|
const gameState: GameState = {
|
2025-03-16 17:45:29 +01:00
|
|
|
runLevels,
|
2025-03-27 10:52:31 +01:00
|
|
|
level: runLevels[0],
|
2025-03-16 17:45:29 +01:00
|
|
|
currentLevel: 0,
|
2025-03-20 23:11:42 +01:00
|
|
|
upgradesOfferedFor: -1,
|
2025-03-16 17:45:29 +01:00
|
|
|
perks,
|
|
|
|
puckWidth: 200,
|
|
|
|
baseSpeed: 12,
|
|
|
|
combo: 1,
|
|
|
|
gridSize: 12,
|
|
|
|
running: false,
|
2025-03-22 16:04:25 +01:00
|
|
|
isGameOver: false,
|
2025-03-18 14:16:12 +01:00
|
|
|
ballStickToPuck: true,
|
2025-03-16 17:45:29 +01:00
|
|
|
puckPosition: 400,
|
2025-03-25 08:22:58 +01:00
|
|
|
lastPuckPosition: 400,
|
2025-03-26 08:01:12 +01:00
|
|
|
lastPuckMove: 0,
|
2025-03-16 17:45:29 +01:00
|
|
|
pauseTimeout: null,
|
|
|
|
canvasWidth: 0,
|
|
|
|
canvasHeight: 0,
|
|
|
|
offsetX: 0,
|
|
|
|
offsetXRoundedDown: 0,
|
|
|
|
gameZoneWidth: 0,
|
|
|
|
gameZoneWidthRoundedUp: 0,
|
|
|
|
gameZoneHeight: 0,
|
|
|
|
brickWidth: 0,
|
|
|
|
score: 0,
|
|
|
|
lastScoreIncrease: -1000,
|
|
|
|
lastExplosion: -1000,
|
2025-03-30 21:07:58 +02:00
|
|
|
lastBrickBroken: 0,
|
2025-04-06 10:13:10 +02:00
|
|
|
highScore: getHighScore(),
|
2025-03-16 17:45:29 +01:00
|
|
|
balls: [],
|
2025-04-06 10:13:10 +02:00
|
|
|
ballsColor: "#FFFFFF",
|
2025-03-16 17:45:29 +01:00
|
|
|
bricks: [],
|
2025-03-23 17:52:25 +01:00
|
|
|
brickHP: [],
|
2025-03-18 14:16:12 +01:00
|
|
|
lights: { indexMin: 0, total: 0, list: [] },
|
|
|
|
particles: { indexMin: 0, total: 0, list: [] },
|
|
|
|
texts: { indexMin: 0, total: 0, list: [] },
|
|
|
|
coins: { indexMin: 0, total: 0, list: [] },
|
2025-03-29 15:00:44 +01:00
|
|
|
respawns: { indexMin: 0, total: 0, list: [] },
|
2025-03-16 17:45:29 +01:00
|
|
|
levelStartScore: 0,
|
|
|
|
levelMisses: 0,
|
|
|
|
levelSpawnedCoins: 0,
|
2025-04-06 10:13:10 +02:00
|
|
|
puckColor: "#FFFFFF",
|
2025-03-16 17:45:29 +01:00
|
|
|
ballSize: 20,
|
|
|
|
coinSize: 14,
|
|
|
|
puckHeight: 20,
|
2025-04-06 15:38:30 +02:00
|
|
|
totalScoreAtRunStart: getTotalScore(),
|
2025-03-16 17:45:29 +01:00
|
|
|
pauseUsesDuringRun: 0,
|
|
|
|
keyboardPuckSpeed: 0,
|
|
|
|
lastTick: performance.now(),
|
|
|
|
lastTickDown: 0,
|
|
|
|
runStatistics: {
|
|
|
|
started: Date.now(),
|
|
|
|
levelsPlayed: 0,
|
|
|
|
runTime: 0,
|
|
|
|
coins_spawned: 0,
|
|
|
|
score: 0,
|
|
|
|
bricks_broken: 0,
|
|
|
|
misses: 0,
|
|
|
|
balls_lost: 0,
|
|
|
|
puck_bounces: 0,
|
|
|
|
wall_bounces: 0,
|
|
|
|
upgrades_picked: 1,
|
|
|
|
max_combo: 1,
|
|
|
|
},
|
|
|
|
lastOffered: {},
|
|
|
|
levelTime: 0,
|
2025-03-22 16:47:02 +01:00
|
|
|
winAt: 0,
|
2025-03-16 20:11:08 +01:00
|
|
|
levelWallBounces: 0,
|
|
|
|
needsRender: true,
|
2025-03-16 17:45:29 +01:00
|
|
|
autoCleanUses: 0,
|
2025-03-18 14:16:12 +01:00
|
|
|
...defaultSounds(),
|
2025-03-26 08:35:49 +01:00
|
|
|
rerolls: 0,
|
2025-04-06 15:38:30 +02:00
|
|
|
creative: sumOfValues(params.perks) > 1,
|
2025-03-16 17:45:29 +01:00
|
|
|
};
|
|
|
|
resetBalls(gameState);
|
2025-03-16 14:29:14 +01:00
|
|
|
|
2025-03-28 10:21:14 +01:00
|
|
|
if (!sumOfValues(gameState.perks)) {
|
2025-03-16 17:45:29 +01:00
|
|
|
const giftable = getPossibleUpgrades(gameState).filter((u) => u.giftable);
|
|
|
|
const randomGift =
|
|
|
|
(isOptionOn("easy") && "slow_down") ||
|
|
|
|
giftable[Math.floor(Math.random() * giftable.length)].id;
|
|
|
|
perks[randomGift] = 1;
|
|
|
|
dontOfferTooSoon(gameState, randomGift);
|
|
|
|
}
|
|
|
|
for (let perk of upgrades) {
|
|
|
|
if (gameState.perks[perk.id]) {
|
|
|
|
dontOfferTooSoon(gameState, perk.id);
|
2025-03-16 14:29:14 +01:00
|
|
|
}
|
2025-03-16 17:45:29 +01:00
|
|
|
}
|
|
|
|
return gameState;
|
|
|
|
}
|