Split menus, fps display, set max coins and max particles

This commit is contained in:
Renan LE CARO 2025-03-23 15:48:21 +01:00
parent e3003f1c25
commit 2022b41937
17 changed files with 576 additions and 200 deletions

View file

@ -42,7 +42,7 @@ export async function asyncAlert<t>({
}): Promise<t | void> {
updateAlertsOpen(+1);
return new Promise((resolve) => {
popupWrap.className = actionsAsGrid ? " " : "";
popupWrap.className = actionsAsGrid ? " actionsAsGrid" : "";
closeModaleButton.style.display = allowClose ? "" : "none";
const popup = document.createElement("div");

View file

@ -954,5 +954,40 @@
"bricks": "__________tttt____tttt_______________W___________________rWWWr__",
"svg": null,
"color": ""
},
{
"name": "icon:restart",
"size": 10,
"bricks": "__GGGGGGGG__GGGGGGGG________GG________GG__G_____GG_GGG____GGGGGGG___GG_GGG____GG_GGGGGGGGG_GGGGGGGGG",
"svg": null,
"color": ""
},
{
"name": "icon:settings",
"size": 8,
"bricks": "__l__l___llllll_llllllll_ll__ll__ll__ll_llllllll_llllll___l__l__",
"svg": null,
"color": ""
},
{
"name": "icon:unlocks",
"size": 7,
"bricks": "eeee___e__e___e__e______llll___llll___llll___llll",
"svg": null,
"color": ""
},
{
"name": "icon:sandbox",
"size": 8,
"bricks": "________________________y_ttt__yyyttt_yyyytttyyyytttttyyyyyyyyyy",
"svg": null,
"color": ""
},
{
"name": "icon:continue",
"size": 7,
"bricks": "___t______tt__tttttt_ttttttttttttt____tt_____t___",
"svg": null,
"color": ""
}
]
]

View file

@ -66,6 +66,26 @@ body {
#menu {
left: 0;
}
#FPSDisplay{
z-index: 1;
white-space: nowrap;
padding: 10px;
line-height: 20px;
pointer-events: none;
user-select: none;
opacity: 0.8;
color:white;
padding: 0;
position: fixed;
bottom: 0;
left: 0;
transform-origin: top left;
transform: rotate(-90deg);
}
body.has-alert-open {
height: auto;
overflow: visible;
@ -162,7 +182,7 @@ body:not(.has-alert-open) #popup {
}
&.actionsAsGrid > div {
max-width: 800px;
max-width: none;
section {
display: grid;

View file

@ -21,10 +21,17 @@ import {
import "./PWA/sw_loader";
import { getCurrentLang, t } from "./i18n/i18n";
import { getSettingValue, getTotalScore, setSettingValue } from "./settings";
import {
cycleMaxCoins, cycleMaxParticles,
getCurrentMaxCoins,
getCurrentMaxParticles,
getSettingValue,
getTotalScore,
setSettingValue
} from "./settings";
import {
forEachLiveOne,
gameStateTick,
gameStateTick, liveCount,
normalizeGameState,
pickRandomUpgrades,
setLevel,
@ -363,9 +370,28 @@ export function tick() {
if (isOptionOn("sound")) {
playPendingSounds(gameState);
}
requestAnimationFrame(tick);
FPSCounter++
}
let FPSCounter=0
let FPSDisplay=document.getElementById('FPSDisplay') as HTMLDivElement
setInterval(()=>{
if(isOptionOn('show_fps')){
FPSDisplay.innerText=FPSCounter+' FPS '+
liveCount(gameState.coins)+' COINS '+
(
liveCount(gameState.particles)+
liveCount(gameState.texts)+
liveCount(gameState.lights)
) + ' PARTICLES '
}else{
FPSDisplay.innerText=''
}
FPSCounter=0
},1000)
window.addEventListener("visibilitychange", () => {
if (document.hidden) {
pause(true);
@ -398,25 +424,8 @@ async function openScorePanel() {
<p>${t("score_panel.upgrades_picked")}</p>
<p>${pickedUpgradesHTMl(gameState)}</p>
`,
allowClose: true,
actions: [
{
text: t("score_panel.resume"),
help: t("score_panel.resume_help"),
value: () => {},
},
{
text: t("score_panel.restart"),
help: t("score_panel.restart_help"),
value: () => {
restart({ levelToAvoid: currentLevelInfo(gameState).name });
},
},
],
allowClose: true
});
if (cb) {
cb();
}
}
(document.getElementById("menu") as HTMLButtonElement).addEventListener(
@ -424,70 +433,34 @@ async function openScorePanel() {
(e) => {
e.preventDefault();
if (!alertsOpen) {
openSettingsPanel();
openMainMenu();
}
},
);
async function openSettingsPanel() {
async function openMainMenu() {
pause(true);
const actions: AsyncAlertAction<() => void>[] = [];
for (const key of Object.keys(options) as OptionId[]) {
if (options[key])
actions.push({
icon: isOptionOn(key)
? icons["icon:checkmark_checked"]
: icons["icon:checkmark_unchecked"],
text: options[key].name,
help: options[key].help,
value: () => {
toggleOption(key);
if (key === "mobile-mode") fitSize();
openSettingsPanel();
},
});
}
const creativeModeThreshold = Math.max(...upgrades.map((u) => u.threshold));
const actions:AsyncAlertAction<()=>void>[]=[{
if (document.fullscreenEnabled || document.webkitFullscreenEnabled) {
if (document.fullscreenElement !== null) {
actions.push({
text: t("main_menu.fullscreen_exit"),
help: t("main_menu.fullscreen_exit_help"),
icon: icons["icon:exit_fullscreen"],
value() {
toggleFullScreen();
},
});
} else {
actions.push({
text: t("main_menu.fullscreen"),
help: t("main_menu.fullscreen_help"),
icon: icons["icon:fullscreen"],
value() {
toggleFullScreen();
},
});
}
}
actions.push({
text: t("main_menu.resume"),
help: t("main_menu.resume_help"),
value() {},
});
actions.push({
text: t("main_menu.settings_title"),
help: t("main_menu.settings_help"),
icon:icons['icon:settings'],
value() {
openSettingsMenu()
},
},{
icon:icons['icon:unlocks'],
text: t("main_menu.unlocks"),
help: t("main_menu.unlocks_help"),
value() {
openUnlocksList();
},
});
actions.push({
},{
icon:icons['icon:sandbox'],
text: t("sandbox.title"),
help:
getTotalScore() < creativeModeThreshold
@ -495,6 +468,7 @@ async function openSettingsPanel() {
: t("sandbox.help"),
disabled: getTotalScore() < creativeModeThreshold,
async value() {
let creativeModePerks: Partial<{ [id in PerkId]: number }> =
getSettingValue("creativeModePerks", {}),
choice: "start" | Upgrade | void;
@ -517,22 +491,98 @@ async function openSettingsPanel() {
{
text: t("sandbox.start"),
value: "start",
icon:icons['icon:continue'],
},
],
}))
) {
if (choice === "start") {
setSettingValue("creativeModePerks", creativeModePerks);
restart({ perks: creativeModePerks });
break;
break
} else if (choice) {
creativeModePerks[choice.id] =
((creativeModePerks[choice.id] || 0) + 1) % (choice.max + 1);
setSettingValue("creativeModePerks", creativeModePerks);
}
}
},
},
{
icon:icons['icon:restart'],
text: t("score_panel.restart"),
help: t("score_panel.restart_help"),
value: () => {
restart({ levelToAvoid: currentLevelInfo(gameState).name });
},
},
{
icon:icons['icon:continue'],
text: t("main_menu.resume"),
help: t("main_menu.resume_help"),
value() {},
},
] ;
const cb = await asyncAlert<() => void>({
title: t("main_menu.title"),
text: ``,
allowClose: true,
actions,
textAfterButtons: t("main_menu.footer_html", { appVersion }),
});
if (cb) {
cb();
gameState.needsRender = true;
}
}
async function openSettingsMenu() {
pause(true);
const actions: AsyncAlertAction<() => void>[] = [];
for (const key of Object.keys(options) as OptionId[]) {
if (options[key])
actions.push({
icon: isOptionOn(key)
? icons["icon:checkmark_checked"]
: icons["icon:checkmark_unchecked"],
text: options[key].name,
help: options[key].help,
value: () => {
toggleOption(key);
if (key === "mobile-mode") fitSize();
openSettingsMenu();
},
});
}
if (document.fullscreenEnabled || document.webkitFullscreenEnabled) {
if (document.fullscreenElement !== null) {
actions.push({
text: t("main_menu.fullscreen_exit"),
help: t("main_menu.fullscreen_exit_help"),
icon: icons["icon:exit_fullscreen"],
value() {
toggleFullScreen();
openSettingsMenu();
},
});
} else {
actions.push({
text: t("main_menu.fullscreen"),
help: t("main_menu.fullscreen_help"),
icon: icons["icon:fullscreen"],
value() {
toggleFullScreen();
openSettingsMenu();
},
});
}
}
actions.push({
text: t("main_menu.reset"),
help: t("main_menu.reset_help"),
@ -714,12 +764,34 @@ async function openSettingsPanel() {
},
});
actions.push({
text: t("main_menu.max_coins",{max:getCurrentMaxCoins()}),
help: t("main_menu.max_coins_help"),
async value() {
cycleMaxCoins()
await openSettingsMenu()
},
});
actions.push({
text: t("main_menu.max_particles",{max:getCurrentMaxParticles()}),
help: t("main_menu.max_particles_help"),
async value() {
cycleMaxParticles()
await openSettingsMenu()
},
});
actions.push({
text: t("main_menu.resume"),
help: t("main_menu.resume_help"),
value() {},
});
const cb = await asyncAlert<() => void>({
title: t("main_menu.title"),
text: ``,
title: t("main_menu.settings_title"),
text: t("main_menu.settings_help"),
allowClose: true,
actions,
textAfterButtons: t("main_menu.footer_html", { appVersion }),
actions
});
if (cb) {
cb();
@ -873,7 +945,7 @@ document.addEventListener("keyup", async (e) => {
} else if (e.key === "Escape" && gameState.running) {
pause(true);
} else if (e.key.toLowerCase() === "m" && !alertsOpen) {
openSettingsPanel().then();
openMainMenu().then();
} else if (e.key.toLowerCase() === "s" && !alertsOpen) {
openScorePanel().then();
} else if (e.key.toLowerCase() === "r" && !alertsOpen) {

View file

@ -31,7 +31,7 @@ import {
import { t } from "./i18n/i18n";
import { icons } from "./loadGameData";
import { addToTotalScore } from "./settings";
import {addToTotalScore, getCurrentMaxCoins, getCurrentMaxParticles} from "./settings";
import { background } from "./render";
import { gameOver } from "./gameOver";
import {
@ -226,7 +226,7 @@ export function spawnExplosion(
) {
if (!!isOptionOn("basic")) return;
if (liveCount(gameState.particles) > gameState.MAX_PARTICLES) {
if (liveCount(gameState.particles) > getCurrentMaxParticles()) {
// Avoid freezing when lots of explosion happen at once
count = 1;
}
@ -333,9 +333,9 @@ export function explodeBrick(
gameState.levelSpawnedCoins += coinsToSpawn;
gameState.runStatistics.coins_spawned += coinsToSpawn;
gameState.runStatistics.bricks_broken++;
const maxCoins = gameState.MAX_COINS * (isOptionOn("basic") ? 0.5 : 1);
const maxCoins = getCurrentMaxCoins() * (isOptionOn("basic") ? 0.5 : 1);
const spawnableCoins =
liveCount(gameState.coins) > gameState.MAX_COINS
liveCount(gameState.coins) > getCurrentMaxCoins()
? 1
: Math.floor(maxCoins - liveCount(gameState.coins)) / 3;
@ -1319,7 +1319,7 @@ export function ballTick(gameState: GameState, ball: Ball, delta: number) {
if (gameState.perks.extra_life < 0) {
gameState.perks.extra_life = 0;
} else if (gameState.perks.sacrifice) {
if (liveCount(gameState.coins) < gameState.MAX_COINS / 2) {
if (liveCount(gameState.coins) < getCurrentMaxCoins() / 2) {
// true duplication
let remaining = liveCount(gameState.coins);

View file

@ -1,6 +1,8 @@
import { Ball, GameState, PerkId, PerksMap } from "./types";
import { icons, upgrades } from "./loadGameData";
export function getMajorityValue(arr: string[]): string {
const count: { [k: string]: number } = {};
arr.forEach((v) => (count[v] = (count[v] || 0) + 1));

View file

@ -832,6 +832,66 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>max_coins</name>
<description/>
<comment/>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>max_coins_help</name>
<description/>
<comment/>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>max_particles</name>
<description/>
<comment/>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>max_particles_help</name>
<description/>
<comment/>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>mobile</name>
<description/>
@ -1102,6 +1162,66 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>settings_help</name>
<description/>
<comment/>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>settings_title</name>
<description/>
<comment/>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>show_fps</name>
<description/>
<comment/>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>show_fps_help</name>
<description/>
<comment/>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>sounds</name>
<description/>

View file

@ -37,9 +37,9 @@
"level_up.unlocked_perk": " (Perk)",
"level_up.upgrade_perk_to_level": " lvl {{level}}",
"main_menu.basic": "Basic graphics",
"main_menu.basic_help": "Fewer particles and flashes, better performance.",
"main_menu.download_save_file": "Download save file",
"main_menu.download_save_file_help": "Get a transferable file with your score and stats",
"main_menu.basic_help": "Better performance.",
"main_menu.download_save_file": "Download score and stats",
"main_menu.download_save_file_help": "Get a save file",
"main_menu.footer_html": "<p> \n<span>Made in France by <a href=\"https://lecaro.me\">Renan LE CARO</a>.</span> \n<a href=\"https://paypal.me/renanlecaro\" target=\"_blank\">Donate</a>\n<a href=\"https://discord.gg/DZSPqyJkwP\" target=\"_blank\">Discord</a>\n<a href=\"https://f-droid.org/en/packages/me.lecaro.breakout/\" target=\"_blank\">F-Droid</a>\n<a href=\"https://play.google.com/store/apps/details?id=me.lecaro.breakout\" target=\"_blank\">Google Play</a>\n<a href=\"https://renanlecaro.itch.io/breakout71\" target=\"_blank\">itch.io</a> \n<a href=\"https://gitlab.com/lecarore/breakout71\" target=\"_blank\">Gitlab</a>\n<a href=\"https://breakout.lecaro.me/\" target=\"_blank\">Web version</a>\n<a href=\"https://news.ycombinator.com/item?id=43183131\" target=\"_blank\">HackerNews</a>\n<a href=\"https://breakout.lecaro.me/privacy.html\" target=\"_blank\">Privacy Policy</a>\n<span>v.{{appVersion}}</span>\n</p>\n",
"main_menu.fullscreen": "Fullscreen",
"main_menu.fullscreen_exit": "Exit Fullscreen",
@ -51,8 +51,12 @@
"main_menu.language_help": "Choose the game's language",
"main_menu.load_save_file": "Load save file",
"main_menu.load_save_file_help": "Select a save file on your device",
"main_menu.max_coins": " {{max}} coins on screen maximum",
"main_menu.max_coins_help": "Cosmetic only, no effect on score",
"main_menu.max_particles": " {{max}} particles maximum",
"main_menu.max_particles_help": "Limits the number of particles show on screen for visual effect. ",
"main_menu.mobile": "Mobile mode",
"main_menu.mobile_help": "Leaves space for your thumb under the puck.",
"main_menu.mobile_help": "Leaves space under the puck.",
"main_menu.pointer_lock": "Mouse pointer lock",
"main_menu.pointer_lock_help": "Locks and hides the mouse cursor.",
"main_menu.record": "Record gameplay videos",
@ -69,6 +73,10 @@
"main_menu.save_file_loaded": "Save file loaded",
"main_menu.save_file_loaded_help": "The app will now reload to apply your save",
"main_menu.save_file_loaded_ok": "Ok",
"main_menu.settings_help": "Tailor the game play to your needs and taste",
"main_menu.settings_title": "Settings",
"main_menu.show_fps": "FPS counter",
"main_menu.show_fps_help": "Monitor the app's performance",
"main_menu.sounds": "Game sounds",
"main_menu.sounds_help": "Can slow down some phones.",
"main_menu.title": "Breakout 71",

View file

@ -37,31 +37,35 @@
"level_up.unlocked_perk": " (Amélioration)",
"level_up.upgrade_perk_to_level": " niveau {{level}}",
"main_menu.basic": "Graphismes simplifiés",
"main_menu.basic_help": "Moins de particules et effets, meilleures performances.",
"main_menu.basic_help": "Meilleures performances.",
"main_menu.download_save_file": "Sauvegarder mes progrès",
"main_menu.download_save_file_help": "Obtenir un fichier de sauvegarde transférable",
"main_menu.download_save_file_help": "Obtenir un fichier de sauvegarde",
"main_menu.footer_html": " <p> \n<span>Programmé en France par <a href=\"https://lecaro.me\">Renan LE CARO</a>.</span>\n<a href=\"https://paypal.me/renanlecaro\" target=\"_blank\">Donner</a>\n<a href=\"https://discord.gg/DZSPqyJkwP\" target=\"_blank\">Discord</a>\n<a href=\"https://f-droid.org/en/packages/me.lecaro.breakout/\" target=\"_blank\">F-Droid</a>\n<a href=\"https://play.google.com/store/apps/details?id=me.lecaro.breakout\" target=\"_blank\">Google Play</a>\n<a href=\"https://renanlecaro.itch.io/breakout71\" target=\"_blank\">itch.io</a>\n<a href=\"https://gitlab.com/lecarore/breakout71\" target=\"_blank\">Gitlab</a>\n<a href=\"https://breakout.lecaro.me/\" target=\"_blank\">Version web</a>\n<a href=\"https://news.ycombinator.com/item?id=43183131\" target=\"_blank\">HackerNews</a>\n<a href=\"https://breakout.lecaro.me/privacy.html\" target=\"_blank\">Politique de confidentialité</a> \n<span>v.{{appVersion}}</span>\n</p>",
"main_menu.fullscreen": "Plein écran",
"main_menu.fullscreen_exit": "Quitter le plein écran",
"main_menu.fullscreen_exit_help": "Peut ne pas fonctionner sur certaines machines",
"main_menu.fullscreen_help": "Peut ne pas fonctionner sur certaines machines",
"main_menu.fullscreen_exit_help": "Ne fonctionne pas toujours",
"main_menu.fullscreen_help": "Ne fonctionne pas toujours",
"main_menu.kid": "Mode enfants",
"main_menu.kid_help": "Balle plus lente",
"main_menu.language": "Langue",
"main_menu.language_help": "Changer la langue d'affichage",
"main_menu.load_save_file": "Charger une sauvegarde",
"main_menu.load_save_file_help": "Sélectionnez une sauvegarde sur votre appareil ",
"main_menu.load_save_file_help": "Depuis un fichier ",
"main_menu.max_coins": "{{max}} pièces affichées maximum",
"main_menu.max_coins_help": "Visuel uniquement, pas d'impact sur le score",
"main_menu.max_particles": " {{max}} particules maximum",
"main_menu.max_particles_help": "Limite le nombre de particules affichées à l'écran pour les effets visuels",
"main_menu.mobile": "Mode mobile",
"main_menu.mobile_help": "Laisse un espace pour le pouce sous le palet.",
"main_menu.pointer_lock": "Verrouillage du pointeur de la souris",
"main_menu.pointer_lock_help": "Verrouille et cache le curseur de la souris.",
"main_menu.mobile_help": "Laisse un espace sous le palet.",
"main_menu.pointer_lock": "Verrouillage du pointeur",
"main_menu.pointer_lock_help": "Cache aussi le curseur de la souris.",
"main_menu.record": "Enregistrer des vidéos de jeu",
"main_menu.record_download": "Télécharger la vidéo ({{size}} MB)",
"main_menu.record_help": "Obtenez une vidéo de chaque niveau.",
"main_menu.reset": "Réinitialiser le jeu",
"main_menu.reset_cancel": "Non",
"main_menu.reset_confirm": "Oui",
"main_menu.reset_help": "Effacer le meilleur score et les statistiques",
"main_menu.reset_help": "Effacer les scores et statistiques",
"main_menu.reset_instruction": "Vous perdrez tous les progrès que vous avez faits dans le jeu, êtes-vous sûr ?",
"main_menu.resume": "Retourner à la partie",
"main_menu.resume_help": "Continuer la partie en cours",
@ -69,6 +73,10 @@
"main_menu.save_file_loaded": "Sauvegarde chargée",
"main_menu.save_file_loaded_help": "L'appli va redémarrer",
"main_menu.save_file_loaded_ok": "Ok",
"main_menu.settings_help": "Adaptez le jeu à vos besoins",
"main_menu.settings_title": "Paramètre",
"main_menu.show_fps": "Compteur de FPS",
"main_menu.show_fps_help": "Surveiller la perf du jeu",
"main_menu.sounds": "Sons du jeu",
"main_menu.sounds_help": "Ralentis certains téléphones.",
"main_menu.title": "Breakout 71",

View file

@ -22,6 +22,7 @@
<body>
<button id="menu"><span id="menuLabel">menu</span></button>
<button id="score"></button>
<div id="FPSDisplay"></div>
<canvas id="game"></canvas>
<div id="popup">
<button id="close-modale"></button>

View file

@ -65,8 +65,6 @@ export function newGameState(params: RunParams): GameState {
levelMisses: 0,
levelSpawnedCoins: 0,
lastPlayedCoinGrab: 0,
MAX_COINS: 400,
MAX_PARTICLES: 600,
puckColor: "#FFF",
ballSize: 20,
coinSize: 14,

View file

@ -19,6 +19,11 @@ export const options = {
name: t("main_menu.basic"),
help: t("main_menu.basic_help"),
},
show_fps: {
default: false,
name: t("main_menu.show_fps"),
help: t("main_menu.show_fps_help"),
},
pointerLock: {
default: false,
name: t("main_menu.pointer_lock"),

View file

@ -281,6 +281,7 @@ export function render(gameState: GameState) {
const comboTextWidth = (comboText.length * gameState.puckHeight) / 1.8;
const totalWidth = comboTextWidth + gameState.coinSize * 2;
const left = gameState.puckPosition - totalWidth / 2;
if (totalWidth < gameState.puckWidth) {
drawCoin(
ctx,

View file

@ -33,3 +33,17 @@ export function addToTotalScore(gameState: GameState, points: number) {
if (gameState.isCreativeModeRun) return;
setSettingValue("breakout_71_total_score", getTotalScore() + points);
}
export function getCurrentMaxCoins(){
return Math.pow(2,getSettingValue('max_coins', 1))*200
}
export function getCurrentMaxParticles(){
return Math.pow(2,getSettingValue('max_particles', 1))*200
}
export function cycleMaxCoins(){
setSettingValue('max_coins', (getSettingValue('max_coins', 1)+1)%6)
}
export function cycleMaxParticles(){
setSettingValue('max_particles', (getSettingValue('max_particles', 1)+1)%6)
}

4
src/types.d.ts vendored
View file

@ -236,8 +236,8 @@ export type GameState = {
levelSpawnedCoins: number;
lastPlayedCoinGrab: number;
MAX_COINS: number;
MAX_PARTICLES: number;
// MAX_COINS: number;
// MAX_PARTICLES: number;
puckColor: colorString;
ballSize: number;
coinSize: number;