From 5e4faf287878db13c14b324982e5454c17a8faa5 Mon Sep 17 00:00:00 2001 From: Renan LE CARO Date: Thu, 20 Mar 2025 18:44:46 +0100 Subject: [PATCH] Build 29041544 --- Readme.md | 3 +- app/build.gradle.kts | 4 +- app/src/main/assets/index.html | 2 +- dist/index.html | 475 ++++++++++++++++++--------------- src/PWA/sw-b71.js | 2 +- src/asyncAlert.ts | 94 ++++--- src/data/version.json | 2 +- src/game.less | 35 ++- src/game.ts | 13 +- src/gameStateMutators.ts | 17 +- src/i18n/en.json | 2 +- src/i18n/fr.json | 2 +- src/index.html | 3 + src/upgrades.ts | 43 ++- 14 files changed, 423 insertions(+), 274 deletions(-) diff --git a/Readme.md b/Readme.md index ef35c66..76191f2 100644 --- a/Readme.md +++ b/Readme.md @@ -24,6 +24,7 @@ There's also an easy mode for kids (slower ball). # Next +- stop scrolling back to top in menu - render next level behind upgrade picker again - sturdy bricks: map of remaining hits @@ -76,7 +77,7 @@ There's also an easy mode for kids (slower ball). - the white outline on bricks associated with picky eater kinda works but i feel it's more distracting than anything. maybe try something different ? put a cross on matching coloured bricks, or the contrary, grey out other bricks. # New perks ideas - +- bricks are invisible, but .. - second puck (symmetric to the first one) - offer next level choice after upgrade pick - ban 3 random perks from pool, doesn't tell you which ones, gain 2 upgrades diff --git a/app/build.gradle.kts b/app/build.gradle.kts index e05b412..fc71482 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 = 29040298 - versionName = "29040298" + versionCode = 29041544 + versionName = "29041544" 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 e26198d..a0e280d 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 04375df..4e1a17d 100644 --- a/dist/index.html +++ b/dist/index.html @@ -1,6 +1,6 @@ - + @@ -31,7 +31,7 @@ body { height: 100vh; height: calc(var(--vh, 1vh) * 100); width: 100vw; - position: absolute; + position: fixed; top: 0; left: 0; } @@ -72,16 +72,31 @@ body { left: 0; } -.popup { - z-index: 10; - background: #000000e6; +body.has-alert-open { + height: auto; + overflow: visible; +} + +body:not(.has-alert-open) #popup { + display: none; +} + +#popup { display: flex; - position: fixed; - inset: 0; overflow: auto; } -.popup > div { +#popup:before { + z-index: 10; + content: ""; + background: #000000e6; + display: block; + position: fixed; + inset: 0; +} + +#popup > div { + z-index: 11; transform-origin: center; flex-direction: column; align-items: stretch; @@ -90,25 +105,26 @@ body { margin: auto; padding: 20px 10px; display: flex; + position: relative; } -.popup > div > * { +#popup > div > * { margin: 0; padding: 0; } -.popup > div > h2, .popup > div > p { +#popup > div > h2, #popup > div > p { margin-bottom: 20px; } -.popup > div > section { +#popup > div > section { flex-direction: column; align-items: stretch; margin-top: 20px; display: flex; } -.popup > div > section button { +#popup > div > section button { font: inherit; color: #fff; cursor: pointer; @@ -121,47 +137,48 @@ body { display: flex; } -.popup > div > section button:not([disabled]):hover, .popup > div > section button:not([disabled]):focus { +#popup > div > section button:not([disabled]):hover, #popup > div > section button:not([disabled]):focus { z-index: 1; border-color: #f1d33b; position: relative; } -.popup > div > section button[disabled] { +#popup > div > section button[disabled] { opacity: .5; filter: saturate(0); pointer-events: none; } -.popup > div > section button > div { +#popup > div > section button > div { flex-grow: 1; } -.popup > div > section button > div > em { +#popup > div > section button > div > em { opacity: .8; display: block; } -.popup > div > section button.grey-out-unless-hovered:not(:hover) { +#popup > div > section button.grey-out-unless-hovered:not(:hover) { opacity: .6; } -.popup > div > section button.grey-out-unless-hovered:not(:hover) img { +#popup > div > section button.grey-out-unless-hovered:not(:hover) img { filter: saturate(0); } -.popup.actionsAsGrid > div { +#popup.actionsAsGrid > div { max-width: 800px; } -.popup.actionsAsGrid > div section { +#popup.actionsAsGrid > div section { grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); display: grid; } -.popup button.close-modale { +#popup button#close-modale { color: #fff; cursor: pointer; + z-index: 11; background: none; border: none; width: 60px; @@ -172,7 +189,7 @@ body { overflow: hidden; } -.popup button.close-modale:before { +#popup button#close-modale:before { content: "+"; font-size: 80px; display: inline-block; @@ -182,20 +199,20 @@ body { transform: translate(-50%, -50%)rotate(45deg); } -.popup button.close-modale:hover { +#popup button#close-modale:hover { background: #000; font-weight: bold; } -.popup .textAfterButtons { +#popup .textAfterButtons { color: #ffffff94; } -.popup a[href] { +#popup a[href] { color: inherit; } -.popup a[href]:hover, .popup a[href]:focus { +#popup a[href]:hover, #popup a[href]:focus { color: #fff; } @@ -257,15 +274,6 @@ body { box-shadow: 2px 2px 5px #000; } -@media (width >= 1200px) { - #level-recording-container { - max-width: calc(50vw - 305px); - position: absolute; - top: 40px; - left: 40px; - } -} - .histogram { align-items: stretch; gap: 10px; @@ -326,6 +334,9 @@ h2.histogram-title strong { + diff --git a/src/PWA/sw-b71.js b/src/PWA/sw-b71.js index 5f284a4..7bf2092 100644 --- a/src/PWA/sw-b71.js +++ b/src/PWA/sw-b71.js @@ -1,5 +1,5 @@ // The version of the cache. -const VERSION = "29040298"; +const VERSION = "29041544"; // The name of the cache const CACHE_NAME = `breakout-71-${VERSION}`; diff --git a/src/asyncAlert.ts b/src/asyncAlert.ts index 9c3ec93..4ab1109 100644 --- a/src/asyncAlert.ts +++ b/src/asyncAlert.ts @@ -12,7 +12,20 @@ export type AsyncAlertAction = { className?: string; }; -export function asyncAlert({ +const popupWrap = document.getElementById("popup") as HTMLDivElement; +const closeModaleButton = document.getElementById( + "close-modale", +) as HTMLButtonElement; +closeModaleButton.addEventListener("click", (e) => { + e.preventDefault(); + if (closeModal) closeModal(); +}); + +closeModaleButton.title = t("play.close_modale_window_tooltip"); + +let lastClickedItemIndex = -1; + +export async function asyncAlert({ title, text, actions, @@ -27,36 +40,27 @@ export function asyncAlert({ allowClose?: boolean; actionsAsGrid?: boolean; }): Promise { - alertsOpen++; + updateAlertsOpen(+1); return new Promise((resolve) => { - const popupWrap = document.createElement("div"); - document.body.appendChild(popupWrap); - popupWrap.className = "popup " + (actionsAsGrid ? "actionsAsGrid " : ""); + popupWrap.className = actionsAsGrid ? " " : ""; + closeModaleButton.style.display = allowClose ? "" : "none"; + const popup = document.createElement("div"); function closeWithResult(value: t | undefined) { + document.body.style.minHeight = document.body.scrollHeight + "px"; + setTimeout(() => (document.body.style.minHeight = ""), 100); + popup.remove(); resolve(value); - // Doing this async lets the menu scroll persist if it's shown a second time - setTimeout(() => { - document.body.removeChild(popupWrap); - }); } if (allowClose) { - const closeButton = document.createElement("button"); - closeButton.title = t("play.close_modale_window_tooltip"); - closeButton.className = "close-modale"; - closeButton.addEventListener("click", (e) => { - e.preventDefault(); - closeWithResult(undefined); - }); closeModal = () => { closeWithResult(undefined); }; - popupWrap.appendChild(closeButton); + } else { + closeModal = null; } - const popup = document.createElement("div"); - if (title) { const p = document.createElement("h2"); p.innerHTML = title; @@ -70,32 +74,39 @@ export function asyncAlert({ } const buttons = document.createElement("section"); + buttons.className = "actions"; popup.appendChild(buttons); actions ?.filter((i) => i) - .forEach(({ text, value, help, disabled, className = "", icon = "" }) => { - const button = document.createElement("button"); + .forEach( + ({ text, value, help, disabled, className = "", icon = "" }, index) => { + const button = document.createElement("button"); - button.innerHTML = ` + button.innerHTML = ` ${icon}
${text} ${help || ""}
`; - if (disabled) { - button.setAttribute("disabled", "disabled"); - } else { - button.addEventListener("click", (e) => { - e.preventDefault(); - closeWithResult(value); - }); - } - button.className = className; - buttons.appendChild(button); - }); + if (disabled) { + button.setAttribute("disabled", "disabled"); + } else { + button.addEventListener("click", (e) => { + e.preventDefault(); + e.stopPropagation(); + closeWithResult(value); + // Focus "same" button if it's still there + lastClickedItemIndex = index; + }); + } + button.className = + className + (lastClickedItemIndex === index ? " needs-focus" : ""); + buttons.appendChild(button); + }, + ); if (textAfterButtons) { const p = document.createElement("div"); p.className = "textAfterButtons"; @@ -105,17 +116,28 @@ ${icon} popupWrap.appendChild(popup); ( - popup.querySelector("button:not([disabled])") as HTMLButtonElement + popupWrap.querySelector( + `section.actions > button.needs-focus`, + ) as HTMLButtonElement )?.focus(); + lastClickedItemIndex = -1; }).then( (v: unknown) => { - alertsOpen--; + updateAlertsOpen(-1); closeModal = null; return v as t | undefined; }, () => { closeModal = null; - alertsOpen--; + updateAlertsOpen(-1); }, ); } + +function updateAlertsOpen(delta: number) { + alertsOpen += delta; + if (alertsOpen > 1) { + throw new Error("Cannot open two alerts at once"); + } + document.body.classList[alertsOpen ? "add" : "remove"]("has-alert-open"); +} diff --git a/src/data/version.json b/src/data/version.json index e13484e..dd5c354 100644 --- a/src/data/version.json +++ b/src/data/version.json @@ -1 +1 @@ -"29040298" +"29041544" diff --git a/src/game.less b/src/game.less index fe41149..299c5f3 100644 --- a/src/game.less +++ b/src/game.less @@ -23,7 +23,7 @@ body { } #game { - position: absolute; + position: fixed; top: 0; left: 0; height: 100vh; @@ -66,16 +66,29 @@ body { #menu { left: 0; } +body.has-alert-open { + height: auto; + overflow: visible; +} +body:not(.has-alert-open) #popup { + display: none; +} +#popup { + &::before { + z-index: 10; + content: ""; + display: block; + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.9); + } -.popup { - position: fixed; - inset: 0; - background: rgba(0, 0, 0, 0.9); - z-index: 10; display: flex; overflow: auto; & > div { + z-index: 11; + position: relative; margin: auto; padding: 20px 10px; transform-origin: center; @@ -157,7 +170,7 @@ body { } } - button.close-modale { + button#close-modale { color: white; position: absolute; top: 0; @@ -168,6 +181,7 @@ body { border: none; cursor: pointer; overflow: hidden; + z-index: 11; &:before { content: "+"; @@ -259,13 +273,6 @@ body { margin: 20px auto; } } - - @media (min-width: 1200px) { - position: absolute; - top: 40px; - left: 40px; - max-width: calc((100vw - 450px) / 2 - 80px); - } } .histogram { diff --git a/src/game.ts b/src/game.ts index cefd415..3486be9 100644 --- a/src/game.ts +++ b/src/game.ts @@ -61,7 +61,8 @@ export function play() { startRecordingGame(gameState); getAudioContext()?.resume(); resumeRecording(); - document.body.className = gameState.running ? " running " : " paused "; + + // document.body.classList[gameState.running ? 'add' : 'remove']('running') } export function pause(playerAskedForPause: boolean) { @@ -78,7 +79,7 @@ export function pause(playerAskedForPause: boolean) { pauseRecording(); gameState.pauseTimeout = null; - document.body.className = gameState.running ? " running " : " paused "; + // document.body.className = gameState.running ? " running " : " paused "; scoreDisplay.className = ""; gameState.needsRender = true; }, @@ -373,7 +374,9 @@ window.addEventListener("visibilitychange", () => { scoreDisplay.addEventListener("click", (e) => { e.preventDefault(); - openScorePanel(); + if (!alertsOpen) { + openScorePanel(); + } }); document.addEventListener("visibilitychange", () => { @@ -420,7 +423,9 @@ async function openScorePanel() { "click", (e) => { e.preventDefault(); - openSettingsPanel(); + if (!alertsOpen) { + openSettingsPanel(); + } }, ); diff --git a/src/gameStateMutators.ts b/src/gameStateMutators.ts index 9e08aeb..0ad2977 100644 --- a/src/gameStateMutators.ts +++ b/src/gameStateMutators.ts @@ -501,10 +501,15 @@ export async function setLevel(gameState: GameState, l: number) { gameState.runStatistics.levelsPlayed++; // Reset combo silently - const finalCombo=gameState.combo + const finalCombo = gameState.combo; gameState.combo = baseCombo(gameState); if (!gameState.perks.shunt) { - gameState.combo += Math.round(Math.max(0,(finalCombo-gameState.combo)*25*gameState.perks.shunt/100)) + gameState.combo += Math.round( + Math.max( + 0, + ((finalCombo - gameState.combo) * 25 * gameState.perks.shunt) / 100, + ), + ); } gameState.combo += gameState.perks.hot_start * 15; @@ -1225,6 +1230,14 @@ 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 && borderHitCode >= 2) { + decreaseCombo( + gameState, + gameState.perks.trampoline, + ball.x, + ball.y + gameState.ballSize, + ); + } schedulGameSound(gameState, "wallBeep", ball.x, 1); gameState.levelWallBounces++; diff --git a/src/i18n/en.json b/src/i18n/en.json index 76d1c55..94f989b 100644 --- a/src/i18n/en.json +++ b/src/i18n/en.json @@ -230,7 +230,7 @@ "upgrades.top_is_lava.help": "More coins if you don't touch the top.", "upgrades.top_is_lava.name": "Sky is the limit", "upgrades.trampoline.fullHelp": "One of the rare combo upgrades that don't add a reset condition", - "upgrades.trampoline.help": "+{{lvl}} combo per puck bounce", + "upgrades.trampoline.help": "+{{lvl}} combo per puck bounce,-{{lvl}} combo per ceiling bounce", "upgrades.trampoline.name": "Trampoline", "upgrades.unbounded.fullHelp": "I hope you've found a way to keep your ball on screen", "upgrades.unbounded.help": "+1 combo per brick, no more sides", diff --git a/src/i18n/fr.json b/src/i18n/fr.json index ca448e6..e595929 100644 --- a/src/i18n/fr.json +++ b/src/i18n/fr.json @@ -230,7 +230,7 @@ "upgrades.top_is_lava.help": "Plus de pièces si vous ne touchez pas le haut de la zone de jeu", "upgrades.top_is_lava.name": "Icare ", "upgrades.trampoline.fullHelp": "Une des rares améliorations à ne pas avoir de condition de remise à zéro", - "upgrades.trampoline.help": "+{{lvl}} combo à chaque rebond d'une balle sur le palet", + "upgrades.trampoline.help": "+{{lvl}} combo à chaque rebond d'une balle sur le palet,-{{lvl}} combo à chaque rebond au plafond", "upgrades.trampoline.name": "Trampoline", "upgrades.unbounded.fullHelp": "J'espère que vous avez prévu un moyen de récupérer vos balles", "upgrades.unbounded.help": "+1 combo par brique, plus de cotés", diff --git a/src/index.html b/src/index.html index b2b6c72..2559d13 100644 --- a/src/index.html +++ b/src/index.html @@ -23,6 +23,9 @@ + diff --git a/src/upgrades.ts b/src/upgrades.ts index 3dfc880..7c9aa06 100644 --- a/src/upgrades.ts +++ b/src/upgrades.ts @@ -3,6 +3,7 @@ import { t } from "./i18n/i18n"; export const rawUpgrades = [ { requires: "", + rejects: "", threshold: 0, giftable: false, id: "extra_life", @@ -16,6 +17,7 @@ export const rawUpgrades = [ }, { requires: "", + rejects: "", threshold: 0, id: "streak_shots", giftable: true, @@ -27,6 +29,7 @@ export const rawUpgrades = [ { requires: "", + rejects: "", threshold: 0, id: "base_combo", giftable: true, @@ -38,6 +41,7 @@ export const rawUpgrades = [ }, { requires: "", + rejects: "", threshold: 0, giftable: false, id: "slow_down", @@ -48,6 +52,7 @@ export const rawUpgrades = [ }, { requires: "", + rejects: "", threshold: 0, giftable: false, id: "bigger_puck", @@ -58,6 +63,7 @@ export const rawUpgrades = [ }, { requires: "", + rejects: "", threshold: 0, giftable: false, id: "viscosity", @@ -69,6 +75,7 @@ export const rawUpgrades = [ }, { requires: "", + rejects: "", threshold: 0, id: "left_is_lava", giftable: true, @@ -80,6 +87,7 @@ export const rawUpgrades = [ }, { requires: "", + rejects: "", threshold: 0, id: "right_is_lava", giftable: true, @@ -90,6 +98,7 @@ export const rawUpgrades = [ }, { requires: "", + rejects: "", threshold: 0, id: "top_is_lava", giftable: true, @@ -100,6 +109,7 @@ export const rawUpgrades = [ }, { requires: "", + rejects: "", threshold: 0, giftable: false, id: "skip_last", @@ -113,6 +123,7 @@ export const rawUpgrades = [ }, { requires: "", + rejects: "", threshold: 500, id: "telekinesis", giftable: true, @@ -126,6 +137,7 @@ export const rawUpgrades = [ }, { requires: "", + rejects: "", threshold: 1000, giftable: false, id: "coin_magnet", @@ -139,6 +151,7 @@ export const rawUpgrades = [ }, { requires: "", + rejects: "", threshold: 1500, id: "multiball", giftable: true, @@ -149,6 +162,7 @@ export const rawUpgrades = [ }, { requires: "", + rejects: "", threshold: 2000, giftable: false, id: "smaller_puck", @@ -162,6 +176,7 @@ export const rawUpgrades = [ }, { requires: "", + rejects: "", threshold: 3000, id: "pierce", giftable: true, @@ -172,6 +187,7 @@ export const rawUpgrades = [ }, { requires: "", + rejects: "", threshold: 4000, id: "picky_eater", giftable: true, @@ -182,6 +198,7 @@ export const rawUpgrades = [ }, { requires: "", + rejects: "", threshold: 5000, giftable: false, id: "metamorphosis", @@ -192,6 +209,7 @@ export const rawUpgrades = [ }, { requires: "", + rejects: "", threshold: 6000, id: "compound_interest", giftable: true, @@ -202,6 +220,7 @@ export const rawUpgrades = [ }, { requires: "", + rejects: "", threshold: 7000, id: "hot_start", giftable: true, @@ -216,6 +235,7 @@ export const rawUpgrades = [ }, { requires: "", + rejects: "", threshold: 9000, id: "sapper", giftable: true, @@ -229,6 +249,7 @@ export const rawUpgrades = [ }, { requires: "", + rejects: "", threshold: 11000, id: "bigger_explosions", giftable: false, @@ -239,6 +260,7 @@ export const rawUpgrades = [ }, { requires: "", + rejects: "", threshold: 13000, giftable: false, id: "extra_levels", @@ -249,6 +271,7 @@ export const rawUpgrades = [ }, { requires: "", + rejects: "", threshold: 15000, giftable: false, id: "pierce_color", @@ -259,6 +282,7 @@ export const rawUpgrades = [ }, { requires: "", + rejects: "", threshold: 18000, giftable: false, id: "soft_reset", @@ -295,6 +319,7 @@ export const rawUpgrades = [ }, { requires: "", + rejects: "", threshold: 30000, giftable: false, id: "puck_repulse_ball", @@ -308,6 +333,7 @@ export const rawUpgrades = [ }, { requires: "", + rejects: "", threshold: 35000, giftable: false, id: "wind", @@ -319,6 +345,7 @@ export const rawUpgrades = [ }, { requires: "", + rejects: "", threshold: 40000, giftable: false, id: "sturdy_bricks", @@ -332,6 +359,7 @@ export const rawUpgrades = [ }, { requires: "", + rejects: "", threshold: 45000, giftable: false, id: "respawn", @@ -343,6 +371,7 @@ export const rawUpgrades = [ }, { requires: "", + rejects: "", threshold: 50000, giftable: false, id: "one_more_choice", @@ -353,6 +382,7 @@ export const rawUpgrades = [ }, { requires: "", + rejects: "", threshold: 55000, giftable: false, id: "instant_upgrade", @@ -363,6 +393,7 @@ export const rawUpgrades = [ }, { requires: "", + rejects: "", threshold: 60000, giftable: false, id: "concave_puck", @@ -373,6 +404,7 @@ export const rawUpgrades = [ }, { requires: "", + rejects: "", threshold: 65000, giftable: false, id: "helium", @@ -383,6 +415,7 @@ export const rawUpgrades = [ }, { requires: "", + rejects: "", threshold: 70000, giftable: false, id: "asceticism", @@ -393,6 +426,7 @@ export const rawUpgrades = [ }, { requires: "", + rejects: "", threshold: 75000, giftable: false, id: "unbounded", @@ -403,16 +437,18 @@ export const rawUpgrades = [ }, { requires: "", + rejects: "", threshold: 80000, giftable: false, id: "shunt", max: 3, name: t("upgrades.shunt.name"), - help: (lvl: number) => t("upgrades.shunt.help",{percent:lvl*25}), + help: (lvl: number) => t("upgrades.shunt.help", { percent: lvl * 25 }), fullHelp: t("upgrades.shunt.fullHelp"), }, { requires: "", + rejects: "", threshold: 85000, giftable: false, id: "yoyo", @@ -423,6 +459,7 @@ export const rawUpgrades = [ }, { requires: "", + rejects: "", threshold: 90000, giftable: false, id: "nbricks", @@ -433,6 +470,7 @@ export const rawUpgrades = [ }, { requires: "", + rejects: "", threshold: 95000, giftable: false, id: "etherealcoins", @@ -453,6 +491,7 @@ export const rawUpgrades = [ }, { requires: "", + rejects: "", threshold: 105000, giftable: false, id: "zen", @@ -484,6 +523,7 @@ export const rawUpgrades = [ }, { requires: "", + rejects: "", threshold: 120000, giftable: false, id: "ghost_coins", @@ -504,6 +544,7 @@ export const rawUpgrades = [ }, { requires: "", + rejects: "", threshold: 130000, giftable: false, id: "ball_attracts_coins",