Build and deploy of version 29030872

This commit is contained in:
Renan LE CARO 2025-03-13 08:53:02 +01:00
parent e14e958686
commit 57cb73128f
10 changed files with 2827 additions and 6487 deletions

View file

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

File diff suppressed because one or more lines are too long

3854
dist/index.html vendored

File diff suppressed because one or more lines are too long

View file

@ -495,9 +495,7 @@ function getPossibleUpgrades() {
function shuffleLevels(nameToAvoid: string | null = null) {
const target = nextRunOverrides?.level;
delete nextRunOverrides.level;
const firstLevel = target
? allLevels.filter((l) => l.name === target)
: [];
const firstLevel = target ? allLevels.filter((l) => l.name === target) : [];
const restInRandomOrder = allLevels
.filter((l) => totalScoreAtRunStart >= l.threshold)
@ -700,7 +698,6 @@ function shouldPierceByColor(
return true;
}
function coinBrickHitCheck(coin: Coin) {
// Make ball/coin bonce, and return bricks that were hit
const radius = coinSize / 2;
@ -762,7 +759,8 @@ function bordersHitCheck(coin: Coin | Ball, radius: number, delta: number) {
hhit = 0;
if (coin.x < offsetXRoundedDown + radius) {
coin.x = offsetXRoundedDown + radius + (offsetXRoundedDown + radius - coin.x);
coin.x =
offsetXRoundedDown + radius + (offsetXRoundedDown + radius - coin.x);
coin.vx *= -1;
hhit = 1;
}
@ -772,7 +770,11 @@ function bordersHitCheck(coin: Coin | Ball, radius: number, delta: number) {
vhit = 1;
}
if (coin.x > lastWidth - offsetXRoundedDown - radius) {
coin.x = lastWidth - offsetXRoundedDown - radius - (coin.x - (lastWidth - offsetXRoundedDown - radius));
coin.x =
lastWidth -
offsetXRoundedDown -
radius -
(coin.x - (lastWidth - offsetXRoundedDown - radius));
coin.vx *= -1;
hhit = 1;
}
@ -780,7 +782,6 @@ function bordersHitCheck(coin: Coin | Ball, radius: number, delta: number) {
return hhit + vhit * 2;
}
let lastTickDown = 0;
function tick() {
@ -837,14 +838,15 @@ function tick() {
coins.forEach((coin) => {
if (coin.destroyed) return;
if (perks.coin_magnet) {
const attractionX = ((delta * (puck - coin.x)) /
const attractionX =
((delta * (puck - coin.x)) /
(100 +
Math.pow(coin.y - gameZoneHeight, 2) +
Math.pow(coin.x - puck, 2))) *
perks.coin_magnet *
100
100;
coin.vx += attractionX;
coin.sa -= attractionX / 10
coin.sa -= attractionX / 10;
}
const ratio = 1 - (perks.viscosity * 0.03 + 0.005) * delta;
@ -1138,16 +1140,13 @@ function ballTick(ball: Ball, delta: number) {
// Puck collision
const ylimit = gameZoneHeight - puckHeight - ballSize / 2;
const ballIsUnderPuck = Math.abs(ball.x - puck) < ballSize / 2 + puckWidth / 2
const ballIsUnderPuck =
Math.abs(ball.x - puck) < ballSize / 2 + puckWidth / 2;
if (
ball.y > ylimit &&
ball.vy > 0 && (
ballIsUnderPuck
|| (perks.extra_life && ball.y > ylimit + puckHeight / 2)
)
ball.vy > 0 &&
(ballIsUnderPuck || (perks.extra_life && ball.y > ylimit + puckHeight / 2))
) {
if (ballIsUnderPuck) {
const speed = Math.sqrt(ball.vx * ball.vx + ball.vy * ball.vy);
const angle = Math.atan2(-puckWidth / 2, ball.x - puck);
@ -1155,15 +1154,15 @@ function ballTick(ball: Ball, delta: number) {
ball.vy = speed * Math.sin(angle);
sounds.wallBeep(ball.x);
} else {
ball.vy *= -1
perks.extra_life = Math.max(0, perks.extra_life - 1)
sounds.lifeLost(ball.x)
ball.vy *= -1;
perks.extra_life = Math.max(0, perks.extra_life - 1);
sounds.lifeLost(ball.x);
if (!isSettingOn("basic")) {
for (let i = 0; i < 10; i++)
flashes.push({
type: 'particle',
type: "particle",
ethereal: false,
color: 'red',
color: "red",
destroyed: false,
duration: 150,
size: coinSize / 2,
@ -1171,8 +1170,8 @@ function ballTick(ball: Ball, delta: number) {
x: ball.x,
y: ball.y,
vx: Math.random() * baseSpeed * 3,
vy: baseSpeed * 3
})
vy: baseSpeed * 3,
});
}
}
if (perks.streak_shots) {
@ -1238,7 +1237,11 @@ function ballTick(ball: Ball, delta: number) {
undefined;
const hitBrick = vhit ?? hhit ?? chit;
let sturdyBounce=hitBrick && bricks[hitBrick]!=='black' && perks.sturdy_bricks && perks.sturdy_bricks > Math.random() * 5
let sturdyBounce =
hitBrick &&
bricks[hitBrick] !== "black" &&
perks.sturdy_bricks &&
perks.sturdy_bricks > Math.random() * 5;
let pierce = false;
if (sturdyBounce || typeof hitBrick === "undefined") {
@ -1246,7 +1249,7 @@ function ballTick(ball: Ball, delta: number) {
} else if (shouldPierceByColor(vhit, hhit, chit)) {
pierce = true;
} else if (ball.piercedSinceBounce < perks.pierce * 3) {
pierce=true
pierce = true;
ball.piercedSinceBounce++;
}
@ -1264,8 +1267,8 @@ function ballTick(ball: Ball, delta: number) {
}
if (sturdyBounce) {
sounds.wallBeep(x)
return
sounds.wallBeep(x);
return;
}
if (typeof hitBrick !== "undefined") {
const initialBrickColor = bricks[hitBrick];
@ -1338,8 +1341,7 @@ function addToTotalScore(points: number) {
"breakout_71_total_score",
JSON.stringify(getTotalScore() + points),
);
} catch (e) {
}
} catch (e) {}
}
function addToTotalPlayTime(ms: number) {
@ -1351,8 +1353,7 @@ function addToTotalPlayTime(ms: number) {
ms,
),
);
} catch (e) {
}
} catch (e) {}
}
function gameOver(title: string, intro: string) {
@ -1568,7 +1569,8 @@ function explodeBrick(index: number, ball: Ball, isExplosion: boolean) {
const i = getRowColIndex(row + dy, col + dx);
if (bricks[i] && i !== -1) {
// Study bricks resist explisions too
if(bricks[i]!=='black' && perks.sturdy_bricks > Math.random() * 5) continue
if (bricks[i] !== "black" && perks.sturdy_bricks > Math.random() * 5)
continue;
explodeBrick(i, ball, true);
}
}
@ -1820,15 +1822,13 @@ function render() {
);
}
// Coins
ctx.globalAlpha = 1;
coins.forEach((coin) => {
if (!coin.destroyed) {
ctx.globalCompositeOperation = (coin.color === 'gold' || level.color ? "source-over" : "screen");
ctx.globalCompositeOperation =
coin.color === "gold" || level.color ? "source-over" : "screen";
drawCoin(
ctx,
coin.color,
@ -1841,7 +1841,6 @@ function render() {
}
});
// Black shadow around balls
if (!isSettingOn("basic")) {
ctx.globalCompositeOperation = "source-over";
@ -1854,7 +1853,6 @@ function render() {
ctx.globalCompositeOperation = "source-over";
renderAllBricks();
ctx.globalCompositeOperation = "screen";
flashes = flashes.filter(
(f) => levelTime - f.time < f.duration && !f.destroyed,
@ -1874,14 +1872,17 @@ function render() {
}
});
if (perks.extra_life) {
ctx.globalAlpha = 1;
ctx.globalCompositeOperation = "source-over";
ctx.fillStyle = puckColor;
for (let i = 0; i < perks.extra_life; i++) {
ctx.fillRect(offsetXRoundedDown, gameZoneHeight - puckHeight / 2 + 2 * i, gameZoneWidthRoundedUp, 1);
ctx.fillRect(
offsetXRoundedDown,
gameZoneHeight - puckHeight / 2 + 2 * i,
gameZoneWidthRoundedUp,
1,
);
}
}
@ -1942,7 +1943,6 @@ function render() {
false,
);
}
}
// Borders
const hasCombo = combo > baseCombo();
@ -2293,7 +2293,6 @@ function roundRect(
ctx.closePath();
}
function drawIMG(
ctx: CanvasRenderingContext2D,
img: HTMLImageElement,
@ -2356,7 +2355,6 @@ function drawText(
);
}
let levelTime = 0;
// Limits skip last to one use per level
let level_skip_last_uses = 0;
@ -2535,8 +2533,7 @@ async function openScorePanel() {
{
text: "Resume",
help: "Return to your run",
value: () => {
},
value: () => {},
},
{
text: "Restart",
@ -2564,8 +2561,7 @@ async function openSettingsPanel() {
{
text: "Resume",
help: "Return to your run",
value() {
},
value() {},
},
{
text: "Starting perk",
@ -2957,7 +2953,7 @@ function startRecordingGame() {
captureTrack =
captureStream.getVideoTracks()[0] as CanvasCaptureMediaStreamTrack;
const track = getAudioRecordingTrack()
const track = getAudioRecordingTrack();
if (track) {
captureStream.addTrack(track.stream.getAudioTracks()[0]);
}

View file

@ -4,14 +4,13 @@ import _rawLevelsList from "./levels.json";
import _appVersion from "./version.json";
import { rawUpgrades } from "./rawUpgrades";
import _backgrounds from "./backgrounds.json";
const backgrounds = _backgrounds as string[]
const backgrounds = _backgrounds as string[];
const palette = _palette as Palette;
const rawLevelsList = _rawLevelsList as RawLevel[];
export const appVersion = _appVersion as string;
let levelIconHTMLCanvas = document.createElement("canvas");
const levelIconHTMLCanvasCtx = levelIconHTMLCanvas.getContext("2d", {
antialias: false,
@ -65,7 +64,7 @@ export const allLevels = rawLevelsList
.slice(0, level.size * level.size);
const icon = levelIconHTML(bricks, level.size, level.name, level.color);
icons[level.name] = icon;
let svg = level.svg!==null && backgrounds[level.svg]
let svg = level.svg !== null && backgrounds[level.svg];
if (!level.color && !svg) {
svg = backgrounds[hashCode(level.name) % backgrounds.length];
@ -94,12 +93,11 @@ export const upgrades = rawUpgrades.map((u) => ({
icon: icons["icon:" + u.id],
})) as Upgrade[];
function hashCode(string: string) {
let hash = 0;
for (let i = 0; i < string.length; i++) {
let code = string.charCodeAt(i);
hash = ((hash<<5)-hash)+code;
hash = (hash << 5) - hash + code;
hash = hash & hash; // Convert to 32bit integer
}
return Math.abs(hash);

View file

@ -1,4 +1,9 @@
import {gameZoneWidthRoundedUp, isSettingOn, offsetX, offsetXRoundedDown} from "./game";
import {
gameZoneWidthRoundedUp,
isSettingOn,
offsetX,
offsetXRoundedDown,
} from "./game";
export const sounds = {
wallBeep: (pan: number) => {
@ -31,7 +36,7 @@ export const sounds = {
},
lifeLost(pan: number) {
if (!isSettingOn("sound")) return;
createShatteredGlassSound(pixelsToPan(pan))
createShatteredGlassSound(pixelsToPan(pan));
},
coinCatch(pan: number) {
@ -41,11 +46,12 @@ export const sounds = {
};
// How to play the code on the leftconst context = new window.AudioContext();
let audioContext: AudioContext, audioRecordingTrack: MediaStreamAudioDestinationNode;
let audioContext: AudioContext,
audioRecordingTrack: MediaStreamAudioDestinationNode;
export function getAudioContext() {
if (!audioContext) {
if (!isSettingOn('sound')) return null
if (!isSettingOn("sound")) return null;
audioContext = new (window.AudioContext || window.webkitAudioContext)();
audioRecordingTrack = audioContext.createMediaStreamDestination();
}
@ -53,8 +59,8 @@ export function getAudioContext() {
}
export function getAudioRecordingTrack() {
getAudioContext()
return audioRecordingTrack
getAudioContext();
return audioRecordingTrack;
}
function createSingleBounceSound(
@ -65,7 +71,7 @@ function createSingleBounceSound(
type: OscillatorType = "sine",
) {
const context = getAudioContext();
if (!context) return
if (!context) return;
const oscillator = createOscillator(context, baseFreq, type);
// Create a gain node to control the volume
@ -96,7 +102,6 @@ function createSingleBounceSound(
let noiseBuffer: AudioBuffer;
function getNoiseBuffer(context: AudioContext) {
if (!noiseBuffer) {
const bufferSize = context.sampleRate * 2; // 2 seconds
noiseBuffer = context.createBuffer(1, bufferSize, context.sampleRate);
@ -107,15 +112,14 @@ function getNoiseBuffer(context:AudioContext) {
output[i] = Math.random() * 2 - 1;
}
}
return noiseBuffer
return noiseBuffer;
}
function createExplosionSound(pan = 0.5) {
const context = getAudioContext();
if (!context) return
if (!context) return;
// Create an audio buffer
// Create a noise source
const noiseSource = context.createBufferSource();
noiseSource.buffer = getNoiseBuffer(context);
@ -154,7 +158,10 @@ function createExplosionSound(pan = 0.5) {
}
function pixelsToPan(pan: number) {
return Math.max(0, Math.min(1, (pan - offsetXRoundedDown) / gameZoneWidthRoundedUp));
return Math.max(
0,
Math.min(1, (pan - offsetXRoundedDown) / gameZoneWidthRoundedUp),
);
}
let lastComboPlayed = NaN,
@ -184,26 +191,27 @@ function playShepard(delta: number, pan: number, volume: number) {
function createShatteredGlassSound(pan: number) {
const context = getAudioContext();
if (!context) return
if (!context) return;
const oscillators = [
createOscillator(context, 3000, 'square'),
createOscillator(context, 4500, 'square'),
createOscillator(context, 6000, 'square')
createOscillator(context, 3000, "square"),
createOscillator(context, 4500, "square"),
createOscillator(context, 6000, "square"),
];
const gainNode = context.createGain();
const noiseSource = context.createBufferSource();
noiseSource.buffer = getNoiseBuffer(context);
oscillators.forEach(oscillator => oscillator.connect(gainNode));
oscillators.forEach((oscillator) => oscillator.connect(gainNode));
noiseSource.connect(gainNode);
gainNode.gain.setValueAtTime(0.2, context.currentTime);
oscillators.forEach(oscillator => oscillator.start());
oscillators.forEach((oscillator) => oscillator.start());
noiseSource.start();
oscillators.forEach(oscillator => oscillator.stop(context.currentTime + 0.2));
oscillators.forEach((oscillator) =>
oscillator.stop(context.currentTime + 0.2),
);
noiseSource.stop(context.currentTime + 0.2);
gainNode.gain.exponentialRampToValueAtTime(0.001, context.currentTime + 0.2);
// Create a stereo panner node for left-right panning
const panner = context.createStereoPanner();
panner.pan.setValueAtTime(pan * 2 - 1, context.currentTime);
@ -215,9 +223,13 @@ function createShatteredGlassSound(pan:number) {
}
// Helper function to create an oscillator with a specific frequency
function createOscillator(context:AudioContext, frequency:number, type:OscillatorType) {
function createOscillator(
context: AudioContext,
frequency: number,
type: OscillatorType,
) {
const oscillator = context.createOscillator();
oscillator.type =type
oscillator.type = type;
oscillator.frequency.setValueAtTime(frequency, context.currentTime);
return oscillator;
}

View file

@ -1 +1 @@
"29028296"
"29030872"