Lives just save the ball once and show as a line.

This commit is contained in:
Renan LE CARO 2025-03-12 15:15:30 +01:00
parent 752a5713d7
commit 2b9bb30000
5 changed files with 6626 additions and 2805 deletions

View file

@ -17,6 +17,7 @@ set -x
# the version number is just a unix timestamp in minutes
versionCode=$(($(date +%s) / 60))
# Replace the version code and name in gradle for fdroid and play store
sed -i -e "s/^[[:space:]]*versionCode = .*/ versionCode = $versionCode/" \
-e "s/^[[:space:]]*versionName = .*/ versionName = \"$versionCode\"/" \
@ -27,6 +28,9 @@ echo "\"$versionCode\"" > src/version.json
# remove all exif metadata from pictures, because i think fdroid doesn't like that. odd
find -name '*.jp*g' -o -name '*.png' | xargs exiftool -all=
# expose the git log to the app itself
git log --pretty=format:' %s' > src/git-log.txt
npx prettier --write src/
npm run build

3845
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

124
src/git-log.txt Normal file
View file

@ -0,0 +1,124 @@
Lives just save the ball once and show as a line.
Deduplicated backgrounds to make the game smaller
wip
Ignore build files
Build and deploy of version 29028296
Updated to use typescript strict typing
wip
Build and deploy of version 29022953
Adjustments to creative mode
Creative mode, cleanup loop fix
Farming points with respawn should be fixed, the sapper-generated bombs no longer count as a broken brick
Typed existing game.ts
Tried to use ts to catch bugs, it's pretty useless for now.
wip
Build and deploy of version 29020191
Build and deploy of version 29020186
Build and deploy of version 29020162
Build and deploy of version 29020161
Build and deploy of version 29020161
Build and deploy of version 29020156
Move to parcel
Ingnore parcel files
When trying a perk or level, actually play the run
Automatic deploy 29019801
Try to get the horizontally bouncing balls unstuck, and allow ball to go fast when tethered
Avoid caching angles of coins that look the same, more coin angles
wip
Automatic deploy 29019704
Better handling of large combo
Automatic deploy 29018692
Automatic deploy 29017278
Avoid fullscreen on ctrl+f
Automatic deploy 29017275
Added a palette for bricks color
wip
wip
Automatic deploy 29017191
Automatic deploy 29015398
Added random patterns as background of levels instead of repeating their name
Automatic deploy 29015054
Fix : bg color of dollar level, puck width and coins centering
Automatic deploy 29014379
Automatic deploy 29014360
Removed gif recording, increased webm recording resolution
Feedback
Automatic deploy 29014300
Removed console.log that were triggering GC, and made all balls the same color to avoid another memory leak and simplify code
Automatic deploy 29014045
Typos
Automatic deploy 29013936
Explanation for perks in help, coins speed limit to avoid clipping, adapted coin spawn rate
Automatic deploy 29011564
Track total play time and added meta description
Automatic deploy 29011519
Automatic deploy 29011397
Typo
Automatic deploy 29011331
Pause abuse delay, f for fullscreen toggle, keyboard support
Automatic deploy 29010156
Automatic deploy 29010123
Automatic deploy 29010003
Automatic deploy 29009984
Automatic deploy 29009918
Automatic deploy 29008583
Automatic deploy 29008176
Automatic deploy 29007858
wip
Automatic deploy 29007301
Automatic deploy 29007124
Automatic deploy 29005750
Automatic deploy 29005697
Added statistics (the last ones weren't actually recording anything)
Automatic deploy 29002316
Automatic deploy 29002312
Automatic deploy 29002304
Automatic deploy 29002302
Automatic deploy 29002301
Automatic deploy 29002295
wip
Automatic deploy 29000827
Automatic deploy 29000798
Automatic deploy 29000794
Automatic deploy 28999986
Wind perk
wip
Automatic deploy 28999931
wip
Automatic deploy 28999417
wip
wip
Automatic deploy 28998184
Adjusted neon effect, more screenshake after bigger_explosion perk is chosen
Automatic deploy 28996852
Automatic deploy 28996655
Automatic deploy 28996651
wip
Automatic deploy 28996415
Fixed small issues
wip
wip
wip
wip
wip
Automatic deploy 28994348
wip
wip
wip
Added some fastlane screenshots in repo for fdroid
Automatic deploy 28994244
Automatic deploy 28994243
wip
Automatic deploy 28994240
wip
Automatic deploy 28994204
wip
wip
Automatic deploy 28994174
wip
Automatic deploy 28994149
Automatic deploy 28994147
wip
wip
Added GPL3 license
Initial commit

223
src/sounds.ts Normal file
View file

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