breakout71/src/newGameState.ts
Renan LE CARO 64a85200b9 wip
2025-04-14 13:39:30 +02:00

156 lines
4.1 KiB
TypeScript

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 = params?.level
? [params.level]
: allLevelsAndIcons.filter((l) => l.name == "icon:" + randomGift);
const restInRandomOrder = unlocked
.filter((l) => l.name !== params?.level?.name)
.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.name.startsWith("icon:")),
computer_controlled: params?.computer_controlled || false,
isEditorTrialRun: params?.isEditorTrialRun,
};
resetBalls(gameState);
for (let perk of upgrades) {
if (perks[perk.id]) {
dontOfferTooSoon(gameState, perk.id);
}
}
return gameState;
}