This commit is contained in:
Renan LE CARO 2025-03-20 18:44:46 +01:00
parent e8621614ee
commit 5e4faf2878
14 changed files with 423 additions and 274 deletions

View file

@ -24,6 +24,7 @@ There's also an easy mode for kids (slower ball).
# Next # Next
- stop scrolling back to top in menu
- render next level behind upgrade picker again - render next level behind upgrade picker again
- sturdy bricks: map of remaining hits - 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. - 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 # New perks ideas
- bricks are invisible, but ..
- second puck (symmetric to the first one) - second puck (symmetric to the first one)
- offer next level choice after upgrade pick - offer next level choice after upgrade pick
- ban 3 random perks from pool, doesn't tell you which ones, gain 2 upgrades - ban 3 random perks from pool, doesn't tell you which ones, gain 2 upgrades

View file

@ -11,8 +11,8 @@ android {
applicationId = "me.lecaro.breakout" applicationId = "me.lecaro.breakout"
minSdk = 21 minSdk = 21
targetSdk = 34 targetSdk = 34
versionCode = 29040298 versionCode = 29041544
versionName = "29040298" versionName = "29041544"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables { vectorDrawables {
useSupportLibrary = true useSupportLibrary = true

File diff suppressed because one or more lines are too long

471
dist/index.html vendored

File diff suppressed because one or more lines are too long

View file

@ -1,5 +1,5 @@
// The version of the cache. // The version of the cache.
const VERSION = "29040298"; const VERSION = "29041544";
// The name of the cache // The name of the cache
const CACHE_NAME = `breakout-71-${VERSION}`; const CACHE_NAME = `breakout-71-${VERSION}`;

View file

@ -12,7 +12,20 @@ export type AsyncAlertAction<t> = {
className?: string; className?: string;
}; };
export function asyncAlert<t>({ 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<t>({
title, title,
text, text,
actions, actions,
@ -27,36 +40,27 @@ export function asyncAlert<t>({
allowClose?: boolean; allowClose?: boolean;
actionsAsGrid?: boolean; actionsAsGrid?: boolean;
}): Promise<t | void> { }): Promise<t | void> {
alertsOpen++; updateAlertsOpen(+1);
return new Promise((resolve) => { return new Promise((resolve) => {
const popupWrap = document.createElement("div"); popupWrap.className = actionsAsGrid ? " " : "";
document.body.appendChild(popupWrap); closeModaleButton.style.display = allowClose ? "" : "none";
popupWrap.className = "popup " + (actionsAsGrid ? "actionsAsGrid " : "");
const popup = document.createElement("div");
function closeWithResult(value: t | undefined) { function closeWithResult(value: t | undefined) {
document.body.style.minHeight = document.body.scrollHeight + "px";
setTimeout(() => (document.body.style.minHeight = ""), 100);
popup.remove();
resolve(value); resolve(value);
// Doing this async lets the menu scroll persist if it's shown a second time
setTimeout(() => {
document.body.removeChild(popupWrap);
});
} }
if (allowClose) { 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 = () => { closeModal = () => {
closeWithResult(undefined); closeWithResult(undefined);
}; };
popupWrap.appendChild(closeButton); } else {
closeModal = null;
} }
const popup = document.createElement("div");
if (title) { if (title) {
const p = document.createElement("h2"); const p = document.createElement("h2");
p.innerHTML = title; p.innerHTML = title;
@ -70,11 +74,13 @@ export function asyncAlert<t>({
} }
const buttons = document.createElement("section"); const buttons = document.createElement("section");
buttons.className = "actions";
popup.appendChild(buttons); popup.appendChild(buttons);
actions actions
?.filter((i) => i) ?.filter((i) => i)
.forEach(({ text, value, help, disabled, className = "", icon = "" }) => { .forEach(
({ text, value, help, disabled, className = "", icon = "" }, index) => {
const button = document.createElement("button"); const button = document.createElement("button");
button.innerHTML = ` button.innerHTML = `
@ -89,13 +95,18 @@ ${icon}
} else { } else {
button.addEventListener("click", (e) => { button.addEventListener("click", (e) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation();
closeWithResult(value); closeWithResult(value);
// Focus "same" button if it's still there
lastClickedItemIndex = index;
}); });
} }
button.className = className;
buttons.appendChild(button);
});
button.className =
className + (lastClickedItemIndex === index ? " needs-focus" : "");
buttons.appendChild(button);
},
);
if (textAfterButtons) { if (textAfterButtons) {
const p = document.createElement("div"); const p = document.createElement("div");
p.className = "textAfterButtons"; p.className = "textAfterButtons";
@ -105,17 +116,28 @@ ${icon}
popupWrap.appendChild(popup); popupWrap.appendChild(popup);
( (
popup.querySelector("button:not([disabled])") as HTMLButtonElement popupWrap.querySelector(
`section.actions > button.needs-focus`,
) as HTMLButtonElement
)?.focus(); )?.focus();
lastClickedItemIndex = -1;
}).then( }).then(
(v: unknown) => { (v: unknown) => {
alertsOpen--; updateAlertsOpen(-1);
closeModal = null; closeModal = null;
return v as t | undefined; return v as t | undefined;
}, },
() => { () => {
closeModal = null; 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");
}

View file

@ -1 +1 @@
"29040298" "29041544"

View file

@ -23,7 +23,7 @@ body {
} }
#game { #game {
position: absolute; position: fixed;
top: 0; top: 0;
left: 0; left: 0;
height: 100vh; height: 100vh;
@ -66,16 +66,29 @@ body {
#menu { #menu {
left: 0; left: 0;
} }
body.has-alert-open {
.popup { height: auto;
overflow: visible;
}
body:not(.has-alert-open) #popup {
display: none;
}
#popup {
&::before {
z-index: 10;
content: "";
display: block;
position: fixed; position: fixed;
inset: 0; inset: 0;
background: rgba(0, 0, 0, 0.9); background: rgba(0, 0, 0, 0.9);
z-index: 10; }
display: flex; display: flex;
overflow: auto; overflow: auto;
& > div { & > div {
z-index: 11;
position: relative;
margin: auto; margin: auto;
padding: 20px 10px; padding: 20px 10px;
transform-origin: center; transform-origin: center;
@ -157,7 +170,7 @@ body {
} }
} }
button.close-modale { button#close-modale {
color: white; color: white;
position: absolute; position: absolute;
top: 0; top: 0;
@ -168,6 +181,7 @@ body {
border: none; border: none;
cursor: pointer; cursor: pointer;
overflow: hidden; overflow: hidden;
z-index: 11;
&:before { &:before {
content: "+"; content: "+";
@ -259,13 +273,6 @@ body {
margin: 20px auto; margin: 20px auto;
} }
} }
@media (min-width: 1200px) {
position: absolute;
top: 40px;
left: 40px;
max-width: calc((100vw - 450px) / 2 - 80px);
}
} }
.histogram { .histogram {

View file

@ -61,7 +61,8 @@ export function play() {
startRecordingGame(gameState); startRecordingGame(gameState);
getAudioContext()?.resume(); getAudioContext()?.resume();
resumeRecording(); resumeRecording();
document.body.className = gameState.running ? " running " : " paused ";
// document.body.classList[gameState.running ? 'add' : 'remove']('running')
} }
export function pause(playerAskedForPause: boolean) { export function pause(playerAskedForPause: boolean) {
@ -78,7 +79,7 @@ export function pause(playerAskedForPause: boolean) {
pauseRecording(); pauseRecording();
gameState.pauseTimeout = null; gameState.pauseTimeout = null;
document.body.className = gameState.running ? " running " : " paused "; // document.body.className = gameState.running ? " running " : " paused ";
scoreDisplay.className = ""; scoreDisplay.className = "";
gameState.needsRender = true; gameState.needsRender = true;
}, },
@ -373,7 +374,9 @@ window.addEventListener("visibilitychange", () => {
scoreDisplay.addEventListener("click", (e) => { scoreDisplay.addEventListener("click", (e) => {
e.preventDefault(); e.preventDefault();
if (!alertsOpen) {
openScorePanel(); openScorePanel();
}
}); });
document.addEventListener("visibilitychange", () => { document.addEventListener("visibilitychange", () => {
@ -420,7 +423,9 @@ async function openScorePanel() {
"click", "click",
(e) => { (e) => {
e.preventDefault(); e.preventDefault();
if (!alertsOpen) {
openSettingsPanel(); openSettingsPanel();
}
}, },
); );

View file

@ -501,10 +501,15 @@ export async function setLevel(gameState: GameState, l: number) {
gameState.runStatistics.levelsPlayed++; gameState.runStatistics.levelsPlayed++;
// Reset combo silently // Reset combo silently
const finalCombo=gameState.combo const finalCombo = gameState.combo;
gameState.combo = baseCombo(gameState); gameState.combo = baseCombo(gameState);
if (!gameState.perks.shunt) { 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; 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) { if (gameState.perks.top_is_lava && borderHitCode >= 2) {
resetCombo(gameState, ball.x, ball.y + gameState.ballSize); 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); schedulGameSound(gameState, "wallBeep", ball.x, 1);
gameState.levelWallBounces++; gameState.levelWallBounces++;

View file

@ -230,7 +230,7 @@
"upgrades.top_is_lava.help": "More coins if you don't touch the top.", "upgrades.top_is_lava.help": "More coins if you don't touch the top.",
"upgrades.top_is_lava.name": "Sky is the limit", "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.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.trampoline.name": "Trampoline",
"upgrades.unbounded.fullHelp": "I hope you've found a way to keep your ball on screen", "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", "upgrades.unbounded.help": "+1 combo per brick, no more sides",

View file

@ -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.help": "Plus de pièces si vous ne touchez pas le haut de la zone de jeu",
"upgrades.top_is_lava.name": "Icare ", "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.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.trampoline.name": "Trampoline",
"upgrades.unbounded.fullHelp": "J'espère que vous avez prévu un moyen de récupérer vos balles", "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", "upgrades.unbounded.help": "+1 combo par brique, plus de cotés",

View file

@ -23,6 +23,9 @@
<button id="menu"><span id="menuLabel">menu</span></button> <button id="menu"><span id="menuLabel">menu</span></button>
<button id="score"></button> <button id="score"></button>
<canvas id="game"></canvas> <canvas id="game"></canvas>
<div id="popup">
<button id="close-modale"></button>
</div>
<script type="module"> <script type="module">
import "./game.ts"; import "./game.ts";
</script> </script>

View file

@ -3,6 +3,7 @@ import { t } from "./i18n/i18n";
export const rawUpgrades = [ export const rawUpgrades = [
{ {
requires: "", requires: "",
rejects: "",
threshold: 0, threshold: 0,
giftable: false, giftable: false,
id: "extra_life", id: "extra_life",
@ -16,6 +17,7 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
rejects: "",
threshold: 0, threshold: 0,
id: "streak_shots", id: "streak_shots",
giftable: true, giftable: true,
@ -27,6 +29,7 @@ export const rawUpgrades = [
{ {
requires: "", requires: "",
rejects: "",
threshold: 0, threshold: 0,
id: "base_combo", id: "base_combo",
giftable: true, giftable: true,
@ -38,6 +41,7 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
rejects: "",
threshold: 0, threshold: 0,
giftable: false, giftable: false,
id: "slow_down", id: "slow_down",
@ -48,6 +52,7 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
rejects: "",
threshold: 0, threshold: 0,
giftable: false, giftable: false,
id: "bigger_puck", id: "bigger_puck",
@ -58,6 +63,7 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
rejects: "",
threshold: 0, threshold: 0,
giftable: false, giftable: false,
id: "viscosity", id: "viscosity",
@ -69,6 +75,7 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
rejects: "",
threshold: 0, threshold: 0,
id: "left_is_lava", id: "left_is_lava",
giftable: true, giftable: true,
@ -80,6 +87,7 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
rejects: "",
threshold: 0, threshold: 0,
id: "right_is_lava", id: "right_is_lava",
giftable: true, giftable: true,
@ -90,6 +98,7 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
rejects: "",
threshold: 0, threshold: 0,
id: "top_is_lava", id: "top_is_lava",
giftable: true, giftable: true,
@ -100,6 +109,7 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
rejects: "",
threshold: 0, threshold: 0,
giftable: false, giftable: false,
id: "skip_last", id: "skip_last",
@ -113,6 +123,7 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
rejects: "",
threshold: 500, threshold: 500,
id: "telekinesis", id: "telekinesis",
giftable: true, giftable: true,
@ -126,6 +137,7 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
rejects: "",
threshold: 1000, threshold: 1000,
giftable: false, giftable: false,
id: "coin_magnet", id: "coin_magnet",
@ -139,6 +151,7 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
rejects: "",
threshold: 1500, threshold: 1500,
id: "multiball", id: "multiball",
giftable: true, giftable: true,
@ -149,6 +162,7 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
rejects: "",
threshold: 2000, threshold: 2000,
giftable: false, giftable: false,
id: "smaller_puck", id: "smaller_puck",
@ -162,6 +176,7 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
rejects: "",
threshold: 3000, threshold: 3000,
id: "pierce", id: "pierce",
giftable: true, giftable: true,
@ -172,6 +187,7 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
rejects: "",
threshold: 4000, threshold: 4000,
id: "picky_eater", id: "picky_eater",
giftable: true, giftable: true,
@ -182,6 +198,7 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
rejects: "",
threshold: 5000, threshold: 5000,
giftable: false, giftable: false,
id: "metamorphosis", id: "metamorphosis",
@ -192,6 +209,7 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
rejects: "",
threshold: 6000, threshold: 6000,
id: "compound_interest", id: "compound_interest",
giftable: true, giftable: true,
@ -202,6 +220,7 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
rejects: "",
threshold: 7000, threshold: 7000,
id: "hot_start", id: "hot_start",
giftable: true, giftable: true,
@ -216,6 +235,7 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
rejects: "",
threshold: 9000, threshold: 9000,
id: "sapper", id: "sapper",
giftable: true, giftable: true,
@ -229,6 +249,7 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
rejects: "",
threshold: 11000, threshold: 11000,
id: "bigger_explosions", id: "bigger_explosions",
giftable: false, giftable: false,
@ -239,6 +260,7 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
rejects: "",
threshold: 13000, threshold: 13000,
giftable: false, giftable: false,
id: "extra_levels", id: "extra_levels",
@ -249,6 +271,7 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
rejects: "",
threshold: 15000, threshold: 15000,
giftable: false, giftable: false,
id: "pierce_color", id: "pierce_color",
@ -259,6 +282,7 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
rejects: "",
threshold: 18000, threshold: 18000,
giftable: false, giftable: false,
id: "soft_reset", id: "soft_reset",
@ -295,6 +319,7 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
rejects: "",
threshold: 30000, threshold: 30000,
giftable: false, giftable: false,
id: "puck_repulse_ball", id: "puck_repulse_ball",
@ -308,6 +333,7 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
rejects: "",
threshold: 35000, threshold: 35000,
giftable: false, giftable: false,
id: "wind", id: "wind",
@ -319,6 +345,7 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
rejects: "",
threshold: 40000, threshold: 40000,
giftable: false, giftable: false,
id: "sturdy_bricks", id: "sturdy_bricks",
@ -332,6 +359,7 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
rejects: "",
threshold: 45000, threshold: 45000,
giftable: false, giftable: false,
id: "respawn", id: "respawn",
@ -343,6 +371,7 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
rejects: "",
threshold: 50000, threshold: 50000,
giftable: false, giftable: false,
id: "one_more_choice", id: "one_more_choice",
@ -353,6 +382,7 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
rejects: "",
threshold: 55000, threshold: 55000,
giftable: false, giftable: false,
id: "instant_upgrade", id: "instant_upgrade",
@ -363,6 +393,7 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
rejects: "",
threshold: 60000, threshold: 60000,
giftable: false, giftable: false,
id: "concave_puck", id: "concave_puck",
@ -373,6 +404,7 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
rejects: "",
threshold: 65000, threshold: 65000,
giftable: false, giftable: false,
id: "helium", id: "helium",
@ -383,6 +415,7 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
rejects: "",
threshold: 70000, threshold: 70000,
giftable: false, giftable: false,
id: "asceticism", id: "asceticism",
@ -393,6 +426,7 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
rejects: "",
threshold: 75000, threshold: 75000,
giftable: false, giftable: false,
id: "unbounded", id: "unbounded",
@ -403,6 +437,7 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
rejects: "",
threshold: 80000, threshold: 80000,
giftable: false, giftable: false,
id: "shunt", id: "shunt",
@ -413,6 +448,7 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
rejects: "",
threshold: 85000, threshold: 85000,
giftable: false, giftable: false,
id: "yoyo", id: "yoyo",
@ -423,6 +459,7 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
rejects: "",
threshold: 90000, threshold: 90000,
giftable: false, giftable: false,
id: "nbricks", id: "nbricks",
@ -433,6 +470,7 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
rejects: "",
threshold: 95000, threshold: 95000,
giftable: false, giftable: false,
id: "etherealcoins", id: "etherealcoins",
@ -453,6 +491,7 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
rejects: "",
threshold: 105000, threshold: 105000,
giftable: false, giftable: false,
id: "zen", id: "zen",
@ -484,6 +523,7 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
rejects: "",
threshold: 120000, threshold: 120000,
giftable: false, giftable: false,
id: "ghost_coins", id: "ghost_coins",
@ -504,6 +544,7 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
rejects: "",
threshold: 130000, threshold: 130000,
giftable: false, giftable: false,
id: "ball_attracts_coins", id: "ball_attracts_coins",