mirror of
https://gitlab.com/lecarore/breakout71.git
synced 2025-04-20 12:15:06 -04:00
Build and deploy of version 29030872
This commit is contained in:
parent
e14e958686
commit
57cb73128f
10 changed files with 2827 additions and 6487 deletions
|
@ -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
3854
dist/index.html
vendored
File diff suppressed because one or more lines are too long
96
src/game.ts
96
src/game.ts
|
@ -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]);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -1 +1 @@
|
|||
"29028296"
|
||||
"29030872"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue