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-14 13:39:30 +02:00
|
|
|
const firstLevel = params?.level
|
|
|
|
? [params.level]
|
|
|
|
: allLevelsAndIcons.filter((l) => l.name == "icon:" + randomGift);
|
2025-03-16 14:29:14 +01:00
|
|
|
|
2025-04-06 15:38:30 +02:00
|
|
|
const restInRandomOrder = unlocked
|
2025-04-14 13:39:30 +02:00
|
|
|
.filter((l) => l.name !== params?.level?.name)
|
2025-03-16 17:45:29 +01:00
|
|
|
.filter((l) => l.name !== params?.levelToAvoid)
|
|
|
|
.sort(() => Math.random() - 0.5);
|
2025-03-16 14:29:14 +01:00
|
|
|
|
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));
|
2025-03-28 10:21:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
export function newGameState(params: RunParams): GameState {
|
2025-04-07 14:50:35 +02:00
|
|
|
const highScore = getHighScore();
|
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-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-04-18 17:15:47 +02:00
|
|
|
startParams: params,
|
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,
|
2025-03-30 21:07:58 +02:00
|
|
|
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: [],
|
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-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:
|
2025-04-12 20:01:43 +02:00
|
|
|
params?.computer_controlled ||
|
2025-04-10 21:40:45 +02:00
|
|
|
sumOfValues(params.perks) > 1 ||
|
2025-04-18 17:15:47 +02:00
|
|
|
(params.level && !params.level.name.startsWith("icon:"))
|
2025-03-16 17:45:29 +01:00
|
|
|
};
|
|
|
|
resetBalls(gameState);
|
2025-03-16 14:29:14 +01:00
|
|
|
|
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 14:29:14 +01:00
|
|
|
}
|
2025-03-16 17:45:29 +01:00
|
|
|
}
|
|
|
|
return gameState;
|
|
|
|
}
|