import { GameState, PerkId, RunParams } from "./types"; import { allLevels, allLevelsAndIcons, upgrades } from "./loadGameData"; import { defaultSounds, getHighScore, getPossibleUpgrades, highScoreText, reasonLevelIsLocked, makeEmptyPerksMap, sumOfValues, } from "./game_utils"; import { dontOfferTooSoon, resetBalls } from "./gameStateMutators"; import { isOptionOn } from "./options"; import { getHistory } from "./gameOver"; import { getSettingValue, getTotalScore } from "./settings"; import { isBlackListedForStart, isStartingPerk } from "./startingPerks"; export function getRunLevels( params: RunParams, randomGift: PerkId | undefined, ) { const unlockedBefore = new Set( getSettingValue("breakout_71_unlocked_levels", []), ); const history = getHistory(); const unlocked = allLevels.filter( (l, li) => unlockedBefore.has(l.name) || !reasonLevelIsLocked(li, history, false), ); const firstLevel = allLevelsAndIcons.filter( (l) => l.name == (params?.level || "icon:" + randomGift), ); const restInRandomOrder = unlocked .filter((l) => l.name !== params?.level) .filter((l) => l.name !== params?.levelToAvoid) .sort(() => Math.random() - 0.5); 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 { const highScore = getHighScore(); const perks = { ...makeEmptyPerksMap(upgrades), ...(params?.perks || {}) }; let randomGift: PerkId | undefined = undefined; if (!sumOfValues(perks)) { let giftable = upgrades.filter((u) => isStartingPerk(u)); if (!giftable.length) { giftable = upgrades.filter((u) => !isBlackListedForStart(u)); } randomGift = (isOptionOn("easy") && "slow_down") || giftable[Math.floor(Math.random() * giftable.length)].id; perks[randomGift] = 1; } const runLevels = getRunLevels(params, randomGift); const gameState: GameState = { runLevels, level: runLevels[0], currentLevel: 0, upgradesOfferedFor: -1, perks, puckWidth: 200, baseSpeed: 12, combo: 1, lastCombo: 1, gridSize: 12, running: false, isGameOver: false, ballStickToPuck: true, puckPosition: 400, lastPuckPosition: 400, lastPuckMove: 0, 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, highScore, balls: [], ballsColor: "#FFFFFF", bricks: [], brickHP: [], lights: { indexMin: 0, total: 0, list: [] }, particles: { indexMin: 0, total: 0, list: [] }, texts: { indexMin: 0, total: 0, list: [] }, coins: { indexMin: 0, total: 0, list: [] }, respawns: { indexMin: 0, total: 0, list: [] }, levelStartScore: 0, levelMisses: 0, levelSpawnedCoins: 0, puckColor: "#FFFFFF", ballSize: 20, coinSize: 14, puckHeight: 20, 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, winAt: 0, levelWallBounces: 0, needsRender: true, autoCleanUses: 0, ...defaultSounds(), rerolls: 0, creative: params?.computer_controlled || sumOfValues(params.perks) > 1 || (params.level && !params.level.startsWith("icon:")), computer_controlled: params?.computer_controlled || false, }; resetBalls(gameState); for (let perk of upgrades) { if (perks[perk.id]) { dontOfferTooSoon(gameState, perk.id); } } return gameState; }