This commit is contained in:
Renan LE CARO 2025-05-03 20:22:25 +02:00
parent 9e12f62b81
commit 892f800107
14 changed files with 2866 additions and 2840 deletions

View file

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

File diff suppressed because one or more lines are too long

1385
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.
const VERSION = "29104759";
const VERSION = "29104940";
// The name of the cache
const CACHE_NAME = `breakout-71-${VERSION}`;

View file

@ -1 +1 @@
"29104759"
"29104940"

View file

@ -8,7 +8,6 @@
box-sizing: border-box;
}
body {
margin: 0;
padding: 0;
@ -642,7 +641,6 @@ h2.histogram-title strong {
opacity: 0.8;
transform: none;
}
}
.gridEdit > div > span,
@ -732,24 +730,24 @@ h2.histogram-title strong {
color: #8a8a8a;
}
@palette_B:black;
@palette_W:#FFFFFF;
@palette_g:#231f20;
@palette_y:#FFD300;
@palette_b:#6262EA;
@palette_t:#5DA3EA;
@palette_s:#E67070;
@palette_r:#e32119;
@palette_R:#ab0c0c;
@palette_c:#59EEA3;
@palette_G:#A1F051;
@palette_v:#A664E8;
@palette_p:#E869E8;
@palette_a:#5BECEC;
@palette_C:#53EE53;
@palette_S:#F44848;
@palette_P:#E66BA8;
@palette_O:#F29E4A;
@palette_k:#618227;
@palette_e:#e1c8b4;
@palette_l:#9b9fa;
@palette_B: black;
@palette_W: #ffffff;
@palette_g: #231f20;
@palette_y: #ffd300;
@palette_b: #6262ea;
@palette_t: #5da3ea;
@palette_s: #e67070;
@palette_r: #e32119;
@palette_R: #ab0c0c;
@palette_c: #59eea3;
@palette_G: #a1f051;
@palette_v: #a664e8;
@palette_p: #e869e8;
@palette_a: #5becec;
@palette_C: #53ee53;
@palette_S: #f44848;
@palette_P: #e66ba8;
@palette_O: #f29e4a;
@palette_k: #618227;
@palette_e: #e1c8b4;
@palette_l: #9b9fa;

View file

@ -254,34 +254,34 @@ let timers = [];
function startPlayCountDown() {
stopPlayCountDown();
gameState.startCountDown = 3;
gameState.needsRender = true;
gameState.startCountDown = 3
gameState.needsRender = true
timers.push(setTimeout(() => {
gameState.startCountDown = 2
gameState.needsRender = true
}, 1000));
timers.push(setTimeout(() => {
gameState.startCountDown = 1
gameState.needsRender = true
}, 2000));
timers.push(
setTimeout(() => {
gameState.startCountDown = 0
gameState.startCountDown = 2;
gameState.needsRender = true;
}, 1000),
);
timers.push(
setTimeout(() => {
gameState.startCountDown = 1;
gameState.needsRender = true;
}, 2000),
);
timers.push(
setTimeout(() => {
gameState.startCountDown = 0;
play();
}, 3000),
);
}
function stopPlayCountDown() {
if(!timers.length) return
if (!timers.length) return;
gameState.startCountDown = 0
gameState.startCountDown = 0;
timers.forEach((id) => clearTimeout(id));
timers.length = 0;
}
gameCanvas.addEventListener("touchstart", (e) => {
e.preventDefault();

View file

@ -32,18 +32,31 @@ import {
zoneLeftBorderX,
zoneRightBorderX,
} from "./game_utils";
import {t} from "./i18n/i18n";
import { t } from "./i18n/i18n";
import {getCurrentMaxCoins, getCurrentMaxParticles} from "./settings";
import {background} from "./render";
import {gameOver} from "./gameOver";
import {brickIndex, fitSize, gameState, hasBrick, hitsSomething, pause, startComputerControlledGame,} from "./game";
import {stopRecording} from "./recording";
import {isOptionOn} from "./options";
import {ballTransparency, clamp, coinsBoostedCombo, comboKeepingRate,} from "./pure_functions";
import {addToTotalScore} from "./addToTotalScore";
import {hashCode} from "./getLevelBackground";
import {openUpgradesPicker} from "./openUpgradesPicker";
import { getCurrentMaxCoins, getCurrentMaxParticles } from "./settings";
import { background } from "./render";
import { gameOver } from "./gameOver";
import {
brickIndex,
fitSize,
gameState,
hasBrick,
hitsSomething,
pause,
startComputerControlledGame,
} from "./game";
import { stopRecording } from "./recording";
import { isOptionOn } from "./options";
import {
ballTransparency,
clamp,
coinsBoostedCombo,
comboKeepingRate,
} from "./pure_functions";
import { addToTotalScore } from "./addToTotalScore";
import { hashCode } from "./getLevelBackground";
import { openUpgradesPicker } from "./openUpgradesPicker";
export function setMousePos(gameState: GameState, x: number) {
if (gameState.startParams.computer_controlled) return;
@ -422,7 +435,7 @@ export function explodeBrick(
while (coinsToSpawn > 0) {
const points = Math.min(pointsPerCoin, coinsToSpawn);
if (points < 0 || isNaN(points)) {
console.error({points});
console.error({ points });
debugger;
}
@ -568,8 +581,7 @@ export function addToScore(gameState: GameState, coin: Coin) {
gameState.highScore = gameState.score;
try {
localStorage.setItem("breakout-3-hs-short", gameState.score.toString());
} catch (e) {
}
} catch (e) {}
}
if (!isOptionOn("basic")) {
makeParticle(
@ -802,7 +814,7 @@ export function attract(gameState: GameState, a: Ball, b: Ball, power: number) {
export function coinBrickHitCheck(gameState: GameState, coin: Coin) {
// Make ball/coin bonce, and return bricks that were hit
const radius = coin.size / 2;
const {x, y, previousX, previousY} = coin;
const { x, y, previousX, previousY } = coin;
const vhit = hitsSomething(previousX, y, radius);
const hhit = hitsSomething(x, previousY, radius);
@ -983,7 +995,7 @@ export function gameStateTick(
if (
// Lost ball while waiting to win, will level up for fairness
(gameState.winAt && !gameState.balls.find(b => !b.destroyed)) ||
(gameState.winAt && !gameState.balls.find((b) => !b.destroyed)) ||
// Delayed win when coins are still flying
(gameState.winAt && gameState.levelTime > gameState.winAt) ||
// instant win condition
@ -999,7 +1011,7 @@ export function gameStateTick(
} else {
gameOver(
t("gameOver.win.title"),
t("gameOver.win.summary", {score: gameState.score}),
t("gameOver.win.summary", { score: gameState.score }),
);
}
} else {
@ -1387,8 +1399,7 @@ export function gameStateTick(
if (gameState.perks.wind) {
const windD =
((gameState.puckPosition -
canvasCenterX(gameState)) /
((gameState.puckPosition - canvasCenterX(gameState)) /
gameState.gameZoneWidth) *
2 *
gameState.perks.wind;
@ -1558,7 +1569,7 @@ export function gameStateTick(
setBrick(gameState, r.index, r.color);
destroy(gameState.respawns, ri);
} else {
const {index, color} = r;
const { index, color } = r;
const vertical = Math.random() > 0.5;
const dx = Math.random() > 0.5 ? 1 : -1;
const dy = Math.random() > 0.5 ? 1 : -1;
@ -1697,9 +1708,18 @@ export function ballTick(gameState: GameState, ball: Ball, frames: number) {
const d = Math.sqrt(ball.vy * ball.vy + ball.vx * ball.vx);
ball.vy = Math.sin(angle) * d;
ball.vx = Math.cos(angle) * d;
if (Math.random() < frames && !isOptionOn('basic')) {
makeParticle(gameState, ball.x, ball.y, -ball.vx / 10, -ball.vy / 10,
'#6262EA', true, 8, 500)
if (Math.random() < frames && !isOptionOn("basic")) {
makeParticle(
gameState,
ball.x,
ball.y,
-ball.vx / 10,
-ball.vy / 10,
"#6262EA",
true,
8,
500,
);
}
}
}
@ -1885,21 +1905,24 @@ export function ballTick(gameState: GameState, ball: Ball, frames: number) {
}
// If you loose a ball while waiting to level up, setLevel is called and pauses the game
// In that case it's ok to not have any ball, don't game over
if (!gameState.balls.find((b) => !b.destroyed) && gameState.running && !gameState.winAt) {
if (
!gameState.balls.find((b) => !b.destroyed) &&
gameState.running &&
!gameState.winAt
) {
if (gameState.startParams.computer_controlled) {
startComputerControlledGame(gameState.startParams.stress);
} else {
gameOver(
t("gameOver.lost.title"),
t("gameOver.lost.summary", {score: gameState.score}),
t("gameOver.lost.summary", { score: gameState.score }),
);
}
}
}
const radius = gameState.ballSize / 2;
// Make ball/coin bonce, and return bricks that were hit
const {x, y, previousX, previousY} = ball;
const { x, y, previousX, previousY } = ball;
const vhit = hitsSomething(previousX, y, radius);
const hhit = hitsSomething(x, previousY, radius);
@ -2022,7 +2045,6 @@ export function ballTick(gameState: GameState, ball: Ball, frames: number) {
);
}
}
}
function justLostALife(gameState: GameState, ball: Ball, x: number, y: number) {
@ -2176,7 +2198,7 @@ export function append<T>(
makeItem(where.list[where.indexMin]);
where.indexMin++;
} else {
const p = {destroyed: false};
const p = { destroyed: false };
makeItem(p);
where.list.push(p);
}

View file

@ -12,7 +12,6 @@ import { t } from "./i18n/i18n";
import { clamp } from "./pure_functions";
import { getSettingValue, getTotalScore } from "./settings";
import { isOptionOn } from "./options";
import {gameCanvas} from "./render";
export function describeLevel(level: Level) {
let bricks = 0,
@ -347,13 +346,12 @@ export function escapeAttribute(str: String) {
.replace(/'/gi, "&#39;");
}
export function canvasCenterX(gameState:GameState){
return gameState.canvasWidth/2
export function canvasCenterX(gameState: GameState) {
return gameState.canvasWidth / 2;
}
export function zoneLeftBorderX(gameState:GameState){
return gameState.offsetXRoundedDown - 1
export function zoneLeftBorderX(gameState: GameState) {
return gameState.offsetXRoundedDown - 1;
}
export function zoneRightBorderX(gameState:GameState){
return gameCanvas.width - gameState.offsetXRoundedDown + 1
export function zoneRightBorderX(gameState: GameState) {
return gameState.canvasWidth - gameState.offsetXRoundedDown + 1;
}

View file

@ -9,8 +9,6 @@ import {
levelTimeGood,
missesBest,
missesGood,
wallBouncedBest,
wallBouncedGood,
} from "./pure_functions";
export function helpMenuEntry() {

View file

@ -95,9 +95,7 @@ export function firstWhere<Input, Output>(
}
}
export const wallBouncedBest = 2,
wallBouncedGood = 7,
levelTimeBest = 25,
export const levelTimeBest = 25,
levelTimeGood = 45,
catchRateBest = 98,
catchRateGood = 90,

View file

@ -10,7 +10,9 @@ import {
max_levels,
reachRedRowIndex,
telekinesisEffectRate,
yoyoEffectRate, zoneLeftBorderX, zoneRightBorderX,
yoyoEffectRate,
zoneLeftBorderX,
zoneRightBorderX,
} from "./game_utils";
import { colorString, GameState } from "./types";
import { t } from "./i18n/i18n";
@ -26,8 +28,6 @@ import {
levelTimeGood,
missesBest,
missesGood,
wallBouncedBest,
wallBouncedGood,
} from "./pure_functions";
export const gameCanvas = document.getElementById("game") as HTMLCanvasElement;
@ -642,21 +642,36 @@ export function render(gameState: GameState) {
);
}
startWork("render:timeout");
if(gameState.winAt || gameState.startCountDown){
const remaining = gameState.startCountDown || Math.ceil((gameState.winAt-gameState.levelTime)/1000)
if(remaining>0 && remaining<5){
ctx.globalAlpha=1
ctx.globalCompositeOperation="destination-out";
drawText(ctx, remaining.toString(), 'white', 65, gameState.canvasWidth/2, gameState.canvasHeight/2)
if (gameState.winAt || gameState.startCountDown) {
const remaining =
gameState.startCountDown ||
Math.ceil((gameState.winAt - gameState.levelTime) / 1000);
if (remaining > 0 && remaining < 5) {
ctx.globalAlpha = 1;
ctx.globalCompositeOperation = "destination-out";
drawText(
ctx,
remaining.toString(),
"white",
65,
gameState.canvasWidth / 2,
gameState.canvasHeight / 2,
);
ctx.globalCompositeOperation="screen";
ctx.globalAlpha=1/remaining
drawText(ctx, remaining.toString(), 'white', 60, gameState.canvasWidth/2, gameState.canvasHeight/2)
ctx.globalCompositeOperation = "screen";
ctx.globalAlpha = 1 / remaining;
drawText(
ctx,
remaining.toString(),
"white",
60,
gameState.canvasWidth / 2,
gameState.canvasHeight / 2,
);
}
}
ctx.globalAlpha=1
ctx.globalAlpha = 1;
startWork("render:askForWakeLock");
askForWakeLock(gameState);

View file

@ -3,16 +3,16 @@ div.classList = "hidden toast";
document.body.appendChild(div);
let timeout: NodeJS.Timeout | undefined;
export function toast(html: string, className = "") {
clearToasts()
clearToasts();
div.classList = "toast visible " + className;
div.innerHTML = html;
timeout = setTimeout(clearToasts, 1500);
}
export function clearToasts(){
export function clearToasts() {
if (timeout) {
clearTimeout(timeout);
timeout = undefined
timeout = undefined;
}
div.classList = "hidden toast";
}

2
src/types.d.ts vendored
View file

@ -283,7 +283,7 @@ export type GameState = {
rerolls: number;
creative: boolean;
startParams: RunParams;
startCountDown:number;
startCountDown: number;
};
export type RunParams = {