diff --git a/Readme.md b/Readme.md index 9cac504..55fae55 100644 --- a/Readme.md +++ b/Readme.md @@ -68,7 +68,7 @@ Break colourful bricks, catch bouncing coins and select powerful upgrades ! - trampoline: nerfed a little bit, now all sides and top hit reduce combo - Quality of life - - udated discord invite link that had expired + - Udated discord invite link that had expired - Fullscreen is now a persistent option, when it's on the game will switch to fullscreen before starting - Added an option to always get colored coins - Made the "combo lost" text last 500ms instead of the pointless 150ms diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 08766a8..cf34cb0 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -11,8 +11,8 @@ android { applicationId = "me.lecaro.breakout" minSdk = 21 targetSdk = 34 - versionCode = 29057568 - versionName = "29057568" + versionCode = 29058459 + versionName = "29058459" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { useSupportLibrary = true diff --git a/app/src/main/assets/index.html b/app/src/main/assets/index.html index c331a5e..872ffc6 100644 --- a/app/src/main/assets/index.html +++ b/app/src/main/assets/index.html @@ -1 +1 @@ -Breakout 71
\ No newline at end of file +Breakout 71 \ No newline at end of file diff --git a/dist/index.html b/dist/index.html index 2c4c9a3..de352b7 100644 --- a/dist/index.html +++ b/dist/index.html @@ -435,7 +435,7 @@ h2.histogram-title strong { - + diff --git a/src/PWA/sw-b71.js b/src/PWA/sw-b71.js index b62fb18..441b1f8 100644 --- a/src/PWA/sw-b71.js +++ b/src/PWA/sw-b71.js @@ -1,5 +1,5 @@ // The version of the cache. -const VERSION = "29057568"; +const VERSION = "29058459"; // The name of the cache const CACHE_NAME = `breakout-71-${VERSION}`; diff --git a/src/asyncAlert.ts b/src/asyncAlert.ts index 7f8b06a..f1c1108 100644 --- a/src/asyncAlert.ts +++ b/src/asyncAlert.ts @@ -105,7 +105,8 @@ export async function asyncAlert({ help, disabled, className = "", - icon = "",tooltip, + icon = "", + tooltip, } = entry; const button = document.createElement("button"); @@ -117,8 +118,8 @@ ${icon} ${help || ""} `; - if(tooltip){ - button.setAttribute('data-tooltip',tooltip) + if (tooltip) { + button.setAttribute("data-tooltip", tooltip); } if (disabled) { button.setAttribute("disabled", "disabled"); @@ -165,4 +166,3 @@ function updateAlertsOpen(delta: number) { } document.body.classList[alertsOpen ? "add" : "remove"]("has-alert-open"); } - diff --git a/src/creative.ts b/src/creative.ts index 1730438..1733c9a 100644 --- a/src/creative.ts +++ b/src/creative.ts @@ -4,15 +4,17 @@ import { t } from "./i18n/i18n"; import { getSettingValue, getTotalScore, setSettingValue } from "./settings"; import { confirmRestart, creativeModeThreshold, restart } from "./game"; import { asyncAlert, requiredAsyncAlert } from "./asyncAlert"; -import {describeLevel, highScoreForMode} from "./game_utils"; +import { describeLevel, highScoreForMode } from "./game_utils"; export function creativeMode(gameState: GameState) { return { icon: icons["icon:sandbox"], text: t("lab.menu_entry"), - help: highScoreForMode('creative')||( - getTotalScore() < creativeModeThreshold && t("lab.unlocks_at", { score: creativeModeThreshold })) || - t("lab.help"), + help: + highScoreForMode("creative") || + (getTotalScore() < creativeModeThreshold && + t("lab.unlocks_at", { score: creativeModeThreshold })) || + t("lab.help"), disabled: getTotalScore() < creativeModeThreshold, async value() { if (await confirmRestart(gameState)) { @@ -68,14 +70,14 @@ export async function openCreativeModePerksPicker( className: creativeModePerks[u.id] ? "sandbox" : "sandbox grey-out-unless-hovered", - tooltip: u.help(creativeModePerks[u.id]||1) + tooltip: u.help(creativeModePerks[u.id] || 1), })), t("lab.select_level"), ...allLevels.map((l) => ({ icon: icons[l.name], text: l.name, value: l, - tooltip:describeLevel(l) + tooltip: describeLevel(l), })), ], })) @@ -95,5 +97,3 @@ export async function openCreativeModePerksPicker( } } } - - diff --git a/src/data/levels.json b/src/data/levels.json index 6c91dcf..a5afaa4 100644 --- a/src/data/levels.json +++ b/src/data/levels.json @@ -1108,4 +1108,4 @@ "color": "", "credit": "https://prohama.com/balloon-1/" } -] \ No newline at end of file +] diff --git a/src/data/version.json b/src/data/version.json index 118cf5e..c909a24 100644 --- a/src/data/version.json +++ b/src/data/version.json @@ -1 +1 @@ -"29057568" +"29058459" diff --git a/src/game.less b/src/game.less index 48d8c66..2f06f4f 100644 --- a/src/game.less +++ b/src/game.less @@ -184,12 +184,14 @@ body:not(.has-alert-open) #popup { &.actionsAsGrid > div { max-width: none; - &>div,&>p,&>h1,&>h2{ + & > div, + & > p, + & > h1, + & > h2 { width: 100%; max-width: 550px; margin-left: auto; margin-right: auto; - } section { display: grid; @@ -423,13 +425,13 @@ h2.histogram-title strong { #tooltip { display: block; - position:fixed; + position: fixed; left: 0; - top:0; - background:black; - color:white; - padding:10px; - z-index:11; + top: 0; + background: black; + color: white; + padding: 10px; + z-index: 11; border-radius: 2px; pointer-events: none; @@ -437,4 +439,4 @@ h2.histogram-title strong { opacity: 1; border: 1px solid white; max-width: 300px; -} \ No newline at end of file +} diff --git a/src/game.ts b/src/game.ts index 450925b..f550b5d 100644 --- a/src/game.ts +++ b/src/game.ts @@ -13,8 +13,10 @@ import { } from "./types"; import { getAudioContext, playPendingSounds } from "./sounds"; import { - currentLevelInfo, describeLevel, - getRowColIndex, highScoreForMode, + currentLevelInfo, + describeLevel, + getRowColIndex, + highScoreForMode, levelsListHTMl, max_levels, pickedUpgradesHTMl, @@ -65,7 +67,7 @@ import { hashCode } from "./getLevelBackground"; import { hoursSpentPlaying } from "./pure_functions"; import { helpMenuEntry } from "./help"; import { creativeMode } from "./creative"; -import {setupTooltips} from "./tooltip"; +import { setupTooltips } from "./tooltip"; export function play() { if (applyFullScreenChoice()) return; @@ -471,16 +473,14 @@ export const creativeModeThreshold = Math.max( ...upgrades.map((u) => u.threshold), ); - export async function openMainMenu() { pause(true); - const actions: AsyncAlertAction<() => void>[] = [ { icon: icons["icon:7_levels_run"], text: t("main_menu.normal"), - help: highScoreForMode('short')||t("main_menu.normal_help"), + help: highScoreForMode("short") || t("main_menu.normal_help"), value: () => { restart({ levelToAvoid: currentLevelInfo(gameState).name, @@ -490,12 +490,12 @@ export async function openMainMenu() { }, { icon: icons["icon:loop"], - text: t("main_menu.loop_run"), - help:highScoreForMode('long')|| - - (getTotalScore() < creativeModeThreshold && t("lab.unlocks_at", { score: creativeModeThreshold })) || - - t("main_menu.loop_run_help"), + text: t("main_menu.loop_run"), + help: + highScoreForMode("long") || + (getTotalScore() < creativeModeThreshold && + t("lab.unlocks_at", { score: creativeModeThreshold })) || + t("main_menu.loop_run_help"), value: () => { restart({ @@ -831,8 +831,7 @@ async function openUnlocksList() { disabled: ts < threshold, value: { perks: { [id]: 1 } } as RunParams, icon, - tooltip:help(1) - + tooltip: help(1), })); const levelActions = allLevels @@ -844,7 +843,7 @@ async function openUnlocksList() { disabled: !available, value: { level: l.name } as RunParams, icon: icons[l.name], - tooltip:describeLevel(l) + tooltip: describeLevel(l), }; }); @@ -998,6 +997,10 @@ restart( ); tick(); -setupTooltips() - document.getElementById('menu')?.setAttribute('data-tooltip', t('play.menu_tooltip')) - document.getElementById('score')?.setAttribute('data-tooltip', t('play.score_tooltip')) \ No newline at end of file +setupTooltips(); +document + .getElementById("menu") + ?.setAttribute("data-tooltip", t("play.menu_tooltip")); +document + .getElementById("score") + ?.setAttribute("data-tooltip", t("play.score_tooltip")); diff --git a/src/gameStateMutators.ts b/src/gameStateMutators.ts index 2e1d289..68d452e 100644 --- a/src/gameStateMutators.ts +++ b/src/gameStateMutators.ts @@ -551,7 +551,6 @@ export function schedulGameSound( ex.vol += vol; } - export function addToScore(gameState: GameState, coin: Coin) { gameState.score += coin.points; gameState.lastScoreIncrease = gameState.levelTime; @@ -1488,7 +1487,7 @@ export function ballTick(gameState: GameState, ball: Ball, delta: number) { if (gameState.perks.top_is_lava && borderHitCode >= 2) { resetCombo(gameState, ball.x, ball.y + gameState.ballSize); } - if (gameState.perks.trampoline ) { + if (gameState.perks.trampoline) { decreaseCombo( gameState, gameState.perks.trampoline, diff --git a/src/game_utils.ts b/src/game_utils.ts index 52b5fa4..9700ceb 100644 --- a/src/game_utils.ts +++ b/src/game_utils.ts @@ -1,26 +1,27 @@ -import {Ball, GameState, Level, PerkId, PerksMap} from "./types"; +import { Ball, GameState, Level, PerkId, PerksMap } from "./types"; import { icons, upgrades } from "./loadGameData"; import { t } from "./i18n/i18n"; -export function describeLevel(level:Level){ - let bricks=0, colors=new Set(), bombs=0; - level.bricks.forEach(color=>{ - if(!color) return - if(color==='black') { - bombs++ +export function describeLevel(level: Level) { + let bricks = 0, + colors = new Set(), + bombs = 0; + level.bricks.forEach((color) => { + if (!color) return; + if (color === "black") { + bombs++; return; + } else { + colors.add(color); + bricks++; } - else { - colors.add(color) - bricks++ - } - }) - return t('unlocks.level_description',{ - size:level.size, + }); + return t("unlocks.level_description", { + size: level.size, bricks, - colors:colors.size, - bombs - }) + colors: colors.size, + bombs, + }); } export function getMajorityValue(arr: string[]): string { @@ -227,17 +228,15 @@ export function countBricksBelow(gameState: GameState, index: number) { } return count; } -export function highScoreForMode(mode:GameState['mode']){ - try{ - const score = parseInt(localStorage.getItem( - "breakout-3-hs-" + mode, - - )||"0") - if(score){ - return t('main_menu.high_score',{score}) +export function highScoreForMode(mode: GameState["mode"]) { + try { + const score = parseInt( + localStorage.getItem("breakout-3-hs-" + mode) || "0", + ); + if (score) { + return t("main_menu.high_score", { score }); } - }catch (e){ - } + } catch (e) {} - return'' + return ""; } diff --git a/src/i18n/en.json b/src/i18n/en.json index 566bd71..ff57e68 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -86,7 +86,7 @@ "main_menu.mobile": "Mobile mode", "main_menu.mobile_help": "Leaves space under the puck.", "main_menu.normal": "New short game", - "main_menu.normal_help": "Start a quick game with random starting perk", + "main_menu.normal_help": "Play 7 levels with a random starting perk", "main_menu.pointer_lock": "Mouse pointer lock", "main_menu.pointer_lock_help": "Locks and hides the mouse cursor.", "main_menu.record": "Record gameplay videos", diff --git a/src/i18n/fr.json b/src/i18n/fr.json index beb244e..af7d983 100644 --- a/src/i18n/fr.json +++ b/src/i18n/fr.json @@ -66,7 +66,7 @@ "main_menu.footer_html": "

\nProgrammé en France par Renan LE CARO.\nDonner\nDiscord\nF-Droid\nGoogle Play\nitch.io\nGitlab\nVersion web\nHackerNews\nPolitique de confidentialité \nv.{{appVersion}}\n

", "main_menu.fullscreen": "Plein écran", "main_menu.fullscreen_help": "Le jeu essaiera de passer en plein écran quand vous le démarrez", - "main_menu.help_content": "# Objectif\n\nLe but est d'attraper un maximum de pièces au cours des 7 niveaux.\nLes pièces apparaissent lorsque vous cassez des briques.\nElles volent, rebondissent et roulent, et vous devez les attraper avec votre palet pour augmenter votre score.\nVotre score est affiché en haut à droite de l'écran.\nVous devez supprimer toutes les briques pour passer au niveau suivant.\nSi vous laissez tomber la balle, la partie est terminée, sauf si vous aviez l'amélioration « Vie supplémentaire ».\n\n# Améliorations\n\nAprès avoir terminé un niveau, vous pourrez choisir des améliorations parmi une petite sélection qui vous sera présentée.\nL'amélioration choisie sera valable jusqu'à la fin de la partie. Vous aurez plus de choix d'améliorations, et même la possibilité d'en choisir plusieurs à la fin du niveau si vous jouez bien : attrapez toutes les pièces, terminez le niveau rapidement et ne ratez jamais votre cible.\nVous obtenez également une amélioration aléatoire gratuite au début de chaque partie. Vous pouvez voir vos améliorations (et quelques détails supplémentaires) en cliquant sur votre score en haut à droite de l'écran.\n\nLes améliorations s'appliquent à l'ensemble de la partie et peuvent être synergiques. Par exemple, si vous combinez « sapeur » et « perforant », la première brique que vous touchez après un rebond de palet se transformera immédiatement en brique explosive et explosera avec la même balle, vous donnant ainsi une balle explosive.\n\nCertaines améliorations facilitent la visée, comme les « balles de contrôle de palet ». Certaines améliorations peuvent être sélectionnées plusieurs fois pour augmenter leur effet. Par exemple, « +1 balle niveau 2 » ajoute une troisième balle.\n\nLors de votre première partie, seules quelques améliorations sont disponibles ; vous débloquez les autres en jouant et en marquant des points. Un mécanisme similaire s'applique au déblocage des niveaux. À la fin d'une partie, les éléments que vous venez de débloquer seront affichés et vous pourrez consulter leur contenu complet dans le menu / Déblocages.\n\nDe nombreuses améliorations influencent votre combo.\n\n# Combo\n\nVotre « combo » correspond au nombre de pièces générées lorsqu'une brique se brise. L'affichage est sur votre palet. Par exemple, x4 signifie que chaque brique rapporte 4 pièces. Le jeu se réinitialise si vous ratez votre tentative.\n\n# Configuration requise\n\nBreakout 71 peut fonctionner hors ligne (ajoutez-le à l'écran d'accueil) et fonctionne bien même sur les appareils bas de gamme.\nL'application est très simple et occupe peu d'espace de stockage (environ 0,1 Mo).\nSi l'application est instable, activez le « mode rapide » dans les paramètres pour afficher une vue simplifiée et plus rapide. Il existe également un mode facile pour les enfants (balle plus lente).\n\n# Boucle\n\nUne fois toutes les améliorations débloquées, vous aurez la possibilité de jouer une partie plus longue.\nDans ce mode, vous pouvez boucler le jeu jusqu'à 7 fois après votre première tentative.\n\nChaque boucle est plus courte et vous ne pouvez pas utiliser les niveaux d'avantages utilisés dans les niveaux précédents, à l'exception d'un avantage que vous pouvez améliorer continuellement. Si vous épuisez complètement un avantage, il ne sera plus proposé dans les boucles suivantes. L'avantage que vous améliorez gagne instantanément +1 niveau et +2 niveaux max.\nChaque boucle est plus courte d'un niveau. La balle démarre légèrement plus vite.\nLa partie se termine lorsque vous perdez le ballon ou terminez la 7e boucle.\n\n# Visée\n\nSeule la position du palet frappé détermine la trajectoire du ballon. Si le ballon touche le palet en plein centre, il rebondira verticalement, tandis que si vous frappez davantage d'un côté, l'angle sera plus grand.\nLa vitesse du palet et l'angle d'arrivée n'ont aucun impact sur la direction du ballon après le rebond.\n\nUn palet plus petit facilitera peut-être la visée près des coins, mais rendra aussi beaucoup plus difficile la capture de pièces.\nLes options « Vent » et « Palet contrôle le ballon » peuvent vous aider à viser même après un rebond dans la mauvaise direction.\n« Balle plus lente » vous donne un peu plus de temps pour viser, ce qui est particulièrement utile dans les niveaux avancés où la balle va plus vite. La balle accélère également à mesure que vous progressez dans chaque niveau.", + "main_menu.help_content": "# Objectif\n\nLe but est d'attraper un maximum de pièces au cours des 7 niveaux.\nLes pièces apparaissent lorsque vous cassez des briques.\nElles volent, rebondissent et roulent, et vous devez les attraper avec votre palet pour augmenter votre score.\nVotre score est affiché en haut à droite de l'écran.\nVous devez supprimer toutes les briques pour passer au niveau suivant.\nSi vous laissez tomber la balle, la partie est terminée, sauf si vous aviez l'amélioration « Vie supplémentaire ».\n\n# Améliorations\n\nAprès avoir terminé un niveau, vous pourrez choisir des améliorations parmi une petite sélection qui vous sera présentée.\nL'amélioration choisie sera valable jusqu'à la fin de la partie. Vous aurez plus de choix d'améliorations, et même la possibilité d'en choisir plusieurs à la fin du niveau si vous jouez bien : attrapez toutes les pièces, terminez le niveau rapidement et ne ratez jamais votre cible.\nVous obtenez également une amélioration aléatoire gratuite au début de chaque partie. Vous pouvez voir vos améliorations (et quelques détails supplémentaires) en cliquant sur votre score en haut à droite de l'écran.\n\nLes améliorations s'appliquent à l'ensemble de la partie et peuvent être synergiques. Par exemple, si vous combinez « sapeur » et « perforant », la première brique que vous touchez après un rebond de palet se transformera immédiatement en brique explosive et explosera avec la même balle, vous donnant ainsi une balle explosive.\n\nCertaines améliorations facilitent la visée, comme les « balles de contrôle de palet ». Certaines améliorations peuvent être sélectionnées plusieurs fois pour augmenter leur effet. Par exemple, « +1 balle niveau 2 » ajoute une troisième balle.\n\nLors de votre première partie, seules quelques améliorations sont disponibles ; vous débloquez les autres en jouant et en marquant des points. Un mécanisme similaire s'applique au déblocage des niveaux. À la fin d'une partie, les éléments que vous venez de débloquer seront affichés et vous pourrez consulter leur contenu complet dans le menu / Déblocages.\n\nDe nombreuses améliorations influencent votre combo.\n\n# Combo\n\nVotre « combo » correspond au nombre de pièces générées lorsqu'une brique se brise. L'affichage est sur votre palet. Par exemple, x4 signifie que chaque brique rapporte 4 pièces. Le jeu se réinitialise si vous ratez votre tentative.\n\n# Configuration requise\n\nBreakout 71 peut fonctionner hors ligne (ajoutez-le à l'écran d'accueil) et fonctionne bien même sur les appareils bas de gamme.\nL'application est très simple et occupe peu d'espace de stockage (environ 0,1 Mo).\nSi l'application est instable, activez le « mode rapide » dans les paramètres pour afficher une vue simplifiée et plus rapide. Il existe également un mode facile pour les enfants (balle plus lente).\n\n# Boucle\n\nUne fois toutes les améliorations débloquées, vous aurez la possibilité de jouer une partie plus longue.\nDans ce mode, vous pouvez boucler le jeu jusqu'à 7 fois après votre première tentative.\n\nChaque boucle est plus courte et vous ne pouvez pas utiliser les niveaux d'avantages utilisés dans les niveaux précédents, à l'exception d'un avantage que vous pouvez améliorer continuellement. Si vous épuisez complètement un avantage, il ne sera plus proposé dans les boucles suivantes. L'avantage que vous améliorez gagne instantanément +1 niveau et +2 niveaux max.\n\nChaque boucle est plus courte d'un niveau. La balle démarre légèrement plus vite. La partie se termine lorsque vous perdez le ballon ou terminez la 7e boucle.\n\n# Visée\n\nSeule la position du palet frappé détermine la trajectoire du ballon. Si le ballon touche le palet en plein centre, il rebondira verticalement, tandis que si vous frappez davantage d'un côté, l'angle sera plus grand.\nLa vitesse du palet et l'angle d'arrivée n'ont aucun impact sur la direction du ballon après le rebond.\n\nUn palet plus petit facilitera peut-être la visée près des coins, mais rendra aussi beaucoup plus difficile la capture de pièces.\nLes options « Vent » et « Télékinesis » peuvent vous aider à viser même après un rebond dans la mauvaise direction.\n« Balle plus lente » vous donne un peu plus de temps pour viser, ce qui est particulièrement utile dans les niveaux avancés où la balle va plus vite. La balle accélère également à mesure que vous progressez dans chaque niveau.", "main_menu.help_help": "Découvrez le jeu en détail", "main_menu.help_title": "Aide et crédits", "main_menu.help_upgrades": "

Améliorations

", diff --git a/src/index.html b/src/index.html index 341d444..eda395b 100644 --- a/src/index.html +++ b/src/index.html @@ -27,7 +27,7 @@ - + diff --git a/src/tooltip.ts b/src/tooltip.ts index 0a94646..637c172 100644 --- a/src/tooltip.ts +++ b/src/tooltip.ts @@ -1,39 +1,49 @@ -import {isOptionOn} from "./options"; +import { isOptionOn } from "./options"; export function setupTooltips() { - const tooltip = document.getElementById('tooltip') as HTMLDivElement - if (isOptionOn('mobile-mode')) { - tooltip.style.display = 'none'; - return - } + const tooltip = document.getElementById("tooltip") as HTMLDivElement; + if (isOptionOn("mobile-mode")) { + tooltip.style.display = "none"; + return; + } - function updateTooltipPosition(e:MouseEvent){ - tooltip.style.transform=`translate(${e.clientX}px,${e.clientY + 20}px) `+(e.clientX > window.innerWidth / 2 ? ' translate(-100%,0)':'') - } - - document.body.addEventListener('mouseenter', (e:MouseEvent) => { - let parent: HTMLElement | null = e.target as HTMLElement - while (parent && !parent.hasAttribute('data-tooltip')) { - parent = parent.parentElement - } - if (parent?.hasAttribute('data-tooltip')) { - tooltip.innerHTML = parent?.getAttribute('data-tooltip') - tooltip.style.display = ''; - updateTooltipPosition(e) - }else{ - - tooltip.style.display = 'none'; - } - }, true) - document.body.addEventListener('mousemove', e => { - if (!tooltip.style.display) { - updateTooltipPosition(e) - } - }, true) - document.body.addEventListener('mouseleave', e => { - // tooltip.style.display = 'none'; - }, true) + function updateTooltipPosition(e: MouseEvent) { + tooltip.style.transform = + `translate(${e.clientX}px,${e.clientY + 20}px) ` + + (e.clientX > window.innerWidth / 2 ? " translate(-100%,0)" : ""); + } + document.body.addEventListener( + "mouseenter", + (e: MouseEvent) => { + let parent: HTMLElement | null = e.target as HTMLElement; + while (parent && !parent.hasAttribute("data-tooltip")) { + parent = parent.parentElement; + } + if (parent?.hasAttribute("data-tooltip")) { + tooltip.innerHTML = parent?.getAttribute("data-tooltip"); + tooltip.style.display = ""; + updateTooltipPosition(e); + } else { + tooltip.style.display = "none"; + } + }, + true, + ); + document.body.addEventListener( + "mousemove", + (e) => { + if (!tooltip.style.display) { + updateTooltipPosition(e); + } + }, + true, + ); + document.body.addEventListener( + "mouseleave", + (e) => { + // tooltip.style.display = 'none'; + }, + true, + ); } - -