breakout71/src/newGameState.ts

154 lines
3.9 KiB
TypeScript
Raw Normal View History

2025-04-07 14:50:35 +02:00
import { GameState, PerkId, RunParams } from "./types";
import { allLevels, allLevelsAndIcons, upgrades } from "./loadGameData";
2025-03-18 14:16:12 +01:00
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";
2025-04-08 14:29:00 +02:00
import { getSettingValue, getTotalScore } from "./settings";
2025-04-10 13:17:38 +02:00
import { isBlackListedForStart, isStartingPerk } from "./startingPerks";
2025-04-06 15:38:30 +02:00
2025-04-07 14:50:35 +02:00
export function getRunLevels(
params: RunParams,
randomGift: PerkId | undefined,
) {
2025-04-08 14:29:00 +02:00
const unlockedBefore = new Set(
getSettingValue("breakout_71_unlocked_levels", []),
);
2025-04-06 15:38:30 +02:00
const history = getHistory();
const unlocked = allLevels.filter(
2025-04-08 14:29:00 +02:00
(l, li) =>
unlockedBefore.has(l.name) || !reasonLevelIsLocked(li, history, false),
2025-04-06 15:38:30 +02:00
);
2025-04-10 14:49:28 +02:00
const firstLevel = allLevelsAndIcons.filter(
(l) => l.name == (params?.level || "icon:" + randomGift),
);
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-04-12 15:39:32 +02:00
return firstLevel
.concat(
restInRandomOrder.slice(0, 7 + 3).sort((a, b) => a.sortKey - b.sortKey),
)
.concat(restInRandomOrder.slice(7 + 3));
}
export function newGameState(params: RunParams): GameState {
2025-04-07 14:50:35 +02:00
const highScore = getHighScore();
2025-03-16 17:45:29 +01:00
const perks = { ...makeEmptyPerksMap(upgrades), ...(params?.perks || {}) };
2025-04-07 14:50:35 +02:00
let randomGift: PerkId | undefined = undefined;
2025-04-07 14:22:59 +02:00
if (!sumOfValues(perks)) {
2025-04-10 13:17:38 +02:00
let giftable = upgrades.filter((u) => isStartingPerk(u));
if (!giftable.length) {
giftable = upgrades.filter((u) => !isBlackListedForStart(u));
}
2025-04-07 14:22:59 +02:00
2025-04-07 14:50:35 +02:00
randomGift =
2025-04-07 14:22:59 +02:00
(isOptionOn("easy") && "slow_down") ||
giftable[Math.floor(Math.random() * giftable.length)].id;
perks[randomGift] = 1;
}
2025-04-07 14:50:35 +02:00
const runLevels = getRunLevels(params, randomGift);
2025-04-07 14:22:59 +02: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,
2025-04-08 08:57:41 +02:00
lastCombo: 1,
2025-03-16 17:45:29 +01:00
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,
lastBrickBroken: 0,
2025-04-07 14:22:59 +02:00
highScore,
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: [],
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-08 10:36:30 +02:00
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-10 21:40:45 +02:00
creative:
sumOfValues(params.perks) > 1 ||
2025-04-11 20:34:11 +02:00
(params.level && !params.level.startsWith("icon:")),
2025-03-16 17:45:29 +01:00
};
resetBalls(gameState);
2025-03-16 17:45:29 +01:00
for (let perk of upgrades) {
2025-04-07 14:22:59 +02:00
if (perks[perk.id]) {
2025-03-16 17:45:29 +01:00
dontOfferTooSoon(gameState, perk.id);
}
2025-03-16 17:45:29 +01:00
}
return gameState;
}