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

@ -30,8 +30,7 @@ There's also an easy mode for kids (slower ball).
# bugs
- perk : travel map
- stairs level has no bg, probably svg -1
- perk : travel map
* [colin] parfois je dois appuyer plusieurs fois sur "Start a new run" pour vraiment commencer une nouvelle partie. dans ce cas, lhécran de jeu derrière se "désassombrit" comme si le jeu avait démarré plusieurs parties en même temps.
* [colin] lorsque le puck est trop petit, l'affichage du combo disparaît. mais c'est peut-être volontaire pour qu'il ne dépasse pas du puck ? afficher simplement le chiffre serait suffisant et tiendrait dans le puck
* [colin] le niveau bug parfois et ne peux pas démarrer. dans ce cas, la balle apparait comme démarrant sans être attachée au puck, comme si la partie avait déjà commencée. il faut redémarrer B71 pour que ça fonctionne

283
dist/index.html vendored

File diff suppressed because one or more lines are too long

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;