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" applicationId = "me.lecaro.breakout"
minSdk = 21 minSdk = 21
targetSdk = 34 targetSdk = 34
versionCode = 29028296 versionCode = 29030872
versionName = "29028296" versionName = "29030872"
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

3854
dist/index.html vendored

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

View file

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

View file

@ -1,223 +1,235 @@
import {gameZoneWidthRoundedUp, isSettingOn, offsetX, offsetXRoundedDown} from "./game"; import {
gameZoneWidthRoundedUp,
isSettingOn,
offsetX,
offsetXRoundedDown,
} from "./game";
export const sounds = { export const sounds = {
wallBeep: (pan: number) => { wallBeep: (pan: number) => {
if (!isSettingOn("sound")) return; if (!isSettingOn("sound")) return;
createSingleBounceSound(800, pixelsToPan(pan)); createSingleBounceSound(800, pixelsToPan(pan));
}, },
comboIncreaseMaybe: (combo: number, x: number, volume: number) => { comboIncreaseMaybe: (combo: number, x: number, volume: number) => {
if (!isSettingOn("sound")) return; if (!isSettingOn("sound")) return;
let delta = 0; let delta = 0;
if (!isNaN(lastComboPlayed)) { if (!isNaN(lastComboPlayed)) {
if (lastComboPlayed < combo) delta = 1; if (lastComboPlayed < combo) delta = 1;
if (lastComboPlayed > combo) delta = -1; if (lastComboPlayed > combo) delta = -1;
} }
playShepard(delta, pixelsToPan(x), volume); playShepard(delta, pixelsToPan(x), volume);
lastComboPlayed = combo; lastComboPlayed = combo;
}, },
comboDecrease() { comboDecrease() {
if (!isSettingOn("sound")) return; if (!isSettingOn("sound")) return;
playShepard(-1, 0.5, 0.5); playShepard(-1, 0.5, 0.5);
}, },
coinBounce: (pan: number, volume: number) => { coinBounce: (pan: number, volume: number) => {
if (!isSettingOn("sound")) return; if (!isSettingOn("sound")) return;
createSingleBounceSound(1200, pixelsToPan(pan), volume, 0.1, "triangle"); createSingleBounceSound(1200, pixelsToPan(pan), volume, 0.1, "triangle");
}, },
explode: (pan: number) => { explode: (pan: number) => {
if (!isSettingOn("sound")) return; if (!isSettingOn("sound")) return;
createExplosionSound(pixelsToPan(pan)); createExplosionSound(pixelsToPan(pan));
}, },
lifeLost(pan:number){ lifeLost(pan: number) {
if (!isSettingOn("sound")) return; if (!isSettingOn("sound")) return;
createShatteredGlassSound(pixelsToPan(pan)) createShatteredGlassSound(pixelsToPan(pan));
}, },
coinCatch(pan: number) { coinCatch(pan: number) {
if (!isSettingOn("sound")) return; if (!isSettingOn("sound")) return;
createSingleBounceSound(900, pixelsToPan(pan), 0.8, 0.1, "triangle"); createSingleBounceSound(900, pixelsToPan(pan), 0.8, 0.1, "triangle");
}, },
}; };
// How to play the code on the leftconst context = new window.AudioContext(); // 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() { export function getAudioContext() {
if (!audioContext) { if (!audioContext) {
if (!isSettingOn('sound')) return null if (!isSettingOn("sound")) return null;
audioContext = new (window.AudioContext || window.webkitAudioContext)(); audioContext = new (window.AudioContext || window.webkitAudioContext)();
audioRecordingTrack = audioContext.createMediaStreamDestination(); audioRecordingTrack = audioContext.createMediaStreamDestination();
} }
return audioContext; return audioContext;
} }
export function getAudioRecordingTrack() { export function getAudioRecordingTrack() {
getAudioContext() getAudioContext();
return audioRecordingTrack return audioRecordingTrack;
} }
function createSingleBounceSound( function createSingleBounceSound(
baseFreq = 800, baseFreq = 800,
pan = 0.5, pan = 0.5,
volume = 1, volume = 1,
duration = 0.1, duration = 0.1,
type: OscillatorType = "sine", type: OscillatorType = "sine",
) { ) {
const context = getAudioContext(); const context = getAudioContext();
if (!context) return if (!context) return;
const oscillator = createOscillator(context, baseFreq, type); const oscillator = createOscillator(context, baseFreq, type);
// Create a gain node to control the volume // Create a gain node to control the volume
const gainNode = context.createGain(); const gainNode = context.createGain();
oscillator.connect(gainNode); oscillator.connect(gainNode);
// Create a stereo panner node for left-right panning // Create a stereo panner node for left-right panning
const panner = context.createStereoPanner(); const panner = context.createStereoPanner();
panner.pan.setValueAtTime(pan * 2 - 1, context.currentTime); panner.pan.setValueAtTime(pan * 2 - 1, context.currentTime);
gainNode.connect(panner); gainNode.connect(panner);
panner.connect(context.destination); panner.connect(context.destination);
panner.connect(audioRecordingTrack); panner.connect(audioRecordingTrack);
// Set up the gain envelope to simulate the impact and quick decay // Set up the gain envelope to simulate the impact and quick decay
gainNode.gain.setValueAtTime(0.8 * volume, context.currentTime); // Initial impact gainNode.gain.setValueAtTime(0.8 * volume, context.currentTime); // Initial impact
gainNode.gain.exponentialRampToValueAtTime( gainNode.gain.exponentialRampToValueAtTime(
0.001, 0.001,
context.currentTime + duration, context.currentTime + duration,
); // Quick decay ); // Quick decay
// Start the oscillator // Start the oscillator
oscillator.start(context.currentTime); oscillator.start(context.currentTime);
// Stop the oscillator after the decay // Stop the oscillator after the decay
oscillator.stop(context.currentTime + duration); oscillator.stop(context.currentTime + duration);
} }
let noiseBuffer: AudioBuffer; let noiseBuffer: AudioBuffer;
function getNoiseBuffer(context:AudioContext) { function getNoiseBuffer(context: AudioContext) {
if (!noiseBuffer) {
const bufferSize = context.sampleRate * 2; // 2 seconds
noiseBuffer = context.createBuffer(1, bufferSize, context.sampleRate);
const output = noiseBuffer.getChannelData(0);
if (!noiseBuffer) { // Fill the buffer with random noise
const bufferSize = context.sampleRate * 2; // 2 seconds for (let i = 0; i < bufferSize; i++) {
noiseBuffer = context.createBuffer(1, bufferSize, context.sampleRate); output[i] = Math.random() * 2 - 1;
const output = noiseBuffer.getChannelData(0);
// Fill the buffer with random noise
for (let i = 0; i < bufferSize; i++) {
output[i] = Math.random() * 2 - 1;
}
} }
return noiseBuffer }
return noiseBuffer;
} }
function createExplosionSound(pan = 0.5) { function createExplosionSound(pan = 0.5) {
const context = getAudioContext(); const context = getAudioContext();
if (!context) return if (!context) return;
// Create an audio buffer // Create an audio buffer
// Create a noise source
const noiseSource = context.createBufferSource();
noiseSource.buffer = getNoiseBuffer(context);
// Create a noise source // Create a gain node to control the volume
const noiseSource = context.createBufferSource(); const gainNode = context.createGain();
noiseSource.buffer = getNoiseBuffer(context); noiseSource.connect(gainNode);
// Create a gain node to control the volume // Create a filter to shape the explosion sound
const gainNode = context.createGain(); const filter = context.createBiquadFilter();
noiseSource.connect(gainNode); filter.type = "lowpass";
filter.frequency.setValueAtTime(1000, context.currentTime); // Set the initial frequency
gainNode.connect(filter);
// Create a filter to shape the explosion sound // Create a stereo panner node for left-right panning
const filter = context.createBiquadFilter(); const panner = context.createStereoPanner();
filter.type = "lowpass"; panner.pan.setValueAtTime(pan * 2 - 1, context.currentTime); // pan 0 to 1 maps to -1 to 1
filter.frequency.setValueAtTime(1000, context.currentTime); // Set the initial frequency
gainNode.connect(filter);
// Create a stereo panner node for left-right panning // Connect filter to panner and then to the destination (speakers)
const panner = context.createStereoPanner(); filter.connect(panner);
panner.pan.setValueAtTime(pan * 2 - 1, context.currentTime); // pan 0 to 1 maps to -1 to 1 panner.connect(context.destination);
panner.connect(audioRecordingTrack);
// Connect filter to panner and then to the destination (speakers) // Ramp down the gain to simulate the explosion's fade-out
filter.connect(panner); gainNode.gain.setValueAtTime(1, context.currentTime);
panner.connect(context.destination); gainNode.gain.exponentialRampToValueAtTime(0.01, context.currentTime + 1);
panner.connect(audioRecordingTrack);
// Ramp down the gain to simulate the explosion's fade-out // Lower the filter frequency over time to create the "explosive" effect
gainNode.gain.setValueAtTime(1, context.currentTime); filter.frequency.exponentialRampToValueAtTime(60, context.currentTime + 1);
gainNode.gain.exponentialRampToValueAtTime(0.01, context.currentTime + 1);
// Lower the filter frequency over time to create the "explosive" effect // Start the noise source
filter.frequency.exponentialRampToValueAtTime(60, context.currentTime + 1); noiseSource.start(context.currentTime);
// Start the noise source // Stop the noise source after the sound has played
noiseSource.start(context.currentTime); noiseSource.stop(context.currentTime + 1);
// Stop the noise source after the sound has played
noiseSource.stop(context.currentTime + 1);
} }
function pixelsToPan(pan: number) { 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, let lastComboPlayed = NaN,
shepard = 6; shepard = 6;
function playShepard(delta: number, pan: number, volume: number) { function playShepard(delta: number, pan: number, volume: number) {
const shepardMax = 11, const shepardMax = 11,
factor = 1.05945594920268, factor = 1.05945594920268,
baseNote = 392; baseNote = 392;
shepard += delta; shepard += delta;
if (shepard > shepardMax) shepard = 0; if (shepard > shepardMax) shepard = 0;
if (shepard < 0) shepard = shepardMax; if (shepard < 0) shepard = shepardMax;
const play = (note: number) => { const play = (note: number) => {
const freq = baseNote * Math.pow(factor, note); const freq = baseNote * Math.pow(factor, note);
const diff = Math.abs(note - shepardMax * 0.5); const diff = Math.abs(note - shepardMax * 0.5);
const maxDistanceToIdeal = 1.5 * shepardMax; const maxDistanceToIdeal = 1.5 * shepardMax;
const vol = Math.max(0, volume * (1 - diff / maxDistanceToIdeal)); const vol = Math.max(0, volume * (1 - diff / maxDistanceToIdeal));
createSingleBounceSound(freq, pan, vol); createSingleBounceSound(freq, pan, vol);
return freq.toFixed(2) + " at " + Math.floor(vol * 100) + "% diff " + diff; return freq.toFixed(2) + " at " + Math.floor(vol * 100) + "% diff " + diff;
}; };
play(1 + shepardMax + shepard); play(1 + shepardMax + shepard);
play(shepard); play(shepard);
play(-1 - shepardMax + shepard); play(-1 - shepardMax + shepard);
} }
function createShatteredGlassSound(pan:number) { function createShatteredGlassSound(pan: number) {
const context = getAudioContext(); const context = getAudioContext();
if (!context) return if (!context) return;
const oscillators = [ const oscillators = [
createOscillator(context, 3000, 'square'), createOscillator(context, 3000, "square"),
createOscillator(context, 4500, 'square'), createOscillator(context, 4500, "square"),
createOscillator(context, 6000, 'square') createOscillator(context, 6000, "square"),
]; ];
const gainNode = context.createGain(); const gainNode = context.createGain();
const noiseSource = context.createBufferSource(); const noiseSource = context.createBufferSource();
noiseSource.buffer = getNoiseBuffer(context); noiseSource.buffer = getNoiseBuffer(context);
oscillators.forEach(oscillator => oscillator.connect(gainNode)); oscillators.forEach((oscillator) => oscillator.connect(gainNode));
noiseSource.connect(gainNode); noiseSource.connect(gainNode);
gainNode.gain.setValueAtTime(0.2, context.currentTime); gainNode.gain.setValueAtTime(0.2, context.currentTime);
oscillators.forEach(oscillator => oscillator.start()); oscillators.forEach((oscillator) => oscillator.start());
noiseSource.start(); noiseSource.start();
oscillators.forEach(oscillator => oscillator.stop(context.currentTime + 0.2)); oscillators.forEach((oscillator) =>
noiseSource.stop(context.currentTime + 0.2); oscillator.stop(context.currentTime + 0.2),
gainNode.gain.exponentialRampToValueAtTime(0.001, 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);
gainNode.connect(panner);
panner.connect(context.destination);
panner.connect(audioRecordingTrack);
// Create a stereo panner node for left-right panning gainNode.connect(panner);
const panner = context.createStereoPanner();
panner.pan.setValueAtTime(pan * 2 - 1, context.currentTime);
gainNode.connect(panner);
panner.connect(context.destination);
panner.connect(audioRecordingTrack);
gainNode.connect(panner);
} }
// Helper function to create an oscillator with a specific frequency // Helper function to create an oscillator with a specific frequency
function createOscillator(context:AudioContext, frequency:number, type:OscillatorType) { function createOscillator(
const oscillator = context.createOscillator(); context: AudioContext,
oscillator.type =type frequency: number,
oscillator.frequency.setValueAtTime(frequency, context.currentTime); type: OscillatorType,
return oscillator; ) {
const oscillator = context.createOscillator();
oscillator.type = type;
oscillator.frequency.setValueAtTime(frequency, context.currentTime);
return oscillator;
} }

2
src/types.d.ts vendored
View file

@ -6,7 +6,7 @@ export type RawLevel = {
name: string; name: string;
size: number; size: number;
bricks: string; bricks: string;
svg: number|null; svg: number | null;
color: string; color: string;
}; };
export type Level = { export type Level = {

View file

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