mirror of
https://gitlab.com/lecarore/breakout71.git
synced 2025-04-21 12:36:15 -04:00
Added statistics (the last ones weren't actually recording anything)
This commit is contained in:
parent
d952139eeb
commit
c2e1924e52
3 changed files with 325 additions and 179 deletions
|
@ -63,7 +63,8 @@ There's also an easy mode for kids (slower ball) and a color-blind mode (no colo
|
||||||
- puck bounce predictions rendered with particles or lines (requires big refactor)
|
- puck bounce predictions rendered with particles or lines (requires big refactor)
|
||||||
|
|
||||||
## Engine ideas
|
## Engine ideas
|
||||||
|
- few puck bounces = more choices / upgrades
|
||||||
|
- disable zooming (for ios double tap)
|
||||||
- particles when bouncing on sides / top
|
- particles when bouncing on sides / top
|
||||||
- show total score on end screen (score added to total)
|
- show total score on end screen (score added to total)
|
||||||
- show stats on end screen compared to other runs
|
- show stats on end screen compared to other runs
|
||||||
|
|
|
@ -55,7 +55,7 @@ function resetCombo(x, y) {
|
||||||
}
|
}
|
||||||
const lost = Math.max(0, prev - combo);
|
const lost = Math.max(0, prev - combo);
|
||||||
if (lost) {
|
if (lost) {
|
||||||
incrementRunStatistics('combo_resets', 1)
|
|
||||||
for (let i = 0; i < lost && i < 8; i++) {
|
for (let i = 0; i < lost && i < 8; i++) {
|
||||||
setTimeout(() => sounds.comboDecrease(), i * 100);
|
setTimeout(() => sounds.comboDecrease(), i * 100);
|
||||||
}
|
}
|
||||||
|
@ -132,14 +132,13 @@ background.addEventListener("load", () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const fitSize = () => {
|
const fitSize = () => {
|
||||||
const {width, height} = canvas.getBoundingClientRect();
|
const {width, height} = canvas.getBoundingClientRect();
|
||||||
canvas.width = width;
|
canvas.width = width;
|
||||||
canvas.height = height;
|
canvas.height = height;
|
||||||
ctx.fillStyle=currentLevelInfo()?.color||'black'
|
ctx.fillStyle = currentLevelInfo()?.color || 'black'
|
||||||
ctx.globalAlpha=1
|
ctx.globalAlpha = 1
|
||||||
ctx.fillRect(0,0,width,height)
|
ctx.fillRect(0, 0, width, height)
|
||||||
backgroundCanvas.width = width;
|
backgroundCanvas.width = width;
|
||||||
backgroundCanvas.height = height;
|
backgroundCanvas.height = height;
|
||||||
|
|
||||||
|
@ -244,7 +243,8 @@ function addToScore(coin) {
|
||||||
lastPlayedCoinGrab = Date.now()
|
lastPlayedCoinGrab = Date.now()
|
||||||
sounds.coinCatch(coin.x)
|
sounds.coinCatch(coin.x)
|
||||||
}
|
}
|
||||||
incrementRunStatistics('caught_coins', coin.points)
|
runStatistics.score+=coin.points
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,7 +269,8 @@ function resetBalls() {
|
||||||
sparks: 0,
|
sparks: 0,
|
||||||
piercedSinceBounce: 0,
|
piercedSinceBounce: 0,
|
||||||
hitSinceBounce: 0,
|
hitSinceBounce: 0,
|
||||||
hitItem:[],
|
hitItem: [],
|
||||||
|
sapperUses: 0,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -289,7 +290,7 @@ function putBallsAtPuck() {
|
||||||
vy: -baseSpeed,
|
vy: -baseSpeed,
|
||||||
sx: 0,
|
sx: 0,
|
||||||
sy: 0,
|
sy: 0,
|
||||||
hitItem:[],
|
hitItem: [],
|
||||||
hitSinceBounce: 0,
|
hitSinceBounce: 0,
|
||||||
piercedSinceBounce: 0,
|
piercedSinceBounce: 0,
|
||||||
// piercedSinceBounce: 0,
|
// piercedSinceBounce: 0,
|
||||||
|
@ -377,6 +378,7 @@ async function openUpgradesPicker() {
|
||||||
textAfterButtons
|
textAfterButtons
|
||||||
});
|
});
|
||||||
cb();
|
cb();
|
||||||
|
runStatistics.upgrades_picked++
|
||||||
}
|
}
|
||||||
resetCombo();
|
resetCombo();
|
||||||
resetBalls();
|
resetBalls();
|
||||||
|
@ -394,6 +396,7 @@ function setLevel(l) {
|
||||||
levelStartScore = score;
|
levelStartScore = score;
|
||||||
levelSpawnedCoins = 0;
|
levelSpawnedCoins = 0;
|
||||||
levelMisses = 0;
|
levelMisses = 0;
|
||||||
|
runStatistics.levelsPlayed++
|
||||||
|
|
||||||
resetCombo();
|
resetCombo();
|
||||||
recomputeTargetBaseSpeed();
|
recomputeTargetBaseSpeed();
|
||||||
|
@ -404,8 +407,6 @@ function setLevel(l) {
|
||||||
gridSize = lvl.size;
|
gridSize = lvl.size;
|
||||||
fitSize();
|
fitSize();
|
||||||
}
|
}
|
||||||
incrementRunStatistics('lvl_size_' + lvl.size, 1)
|
|
||||||
incrementRunStatistics('lvl_name_' + lvl.name, 1)
|
|
||||||
coins = [];
|
coins = [];
|
||||||
bricks = [...lvl.bricks];
|
bricks = [...lvl.bricks];
|
||||||
flashes = [];
|
flashes = [];
|
||||||
|
@ -445,7 +446,8 @@ const upgrades = [
|
||||||
"id": "extra_life",
|
"id": "extra_life",
|
||||||
"name": "+1 life",
|
"name": "+1 life",
|
||||||
"max": 7,
|
"max": 7,
|
||||||
"help": "Survive dropping the ball once."
|
"help": "Survive dropping the ball",
|
||||||
|
extraLevelsHelp: `One more life just in case`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"threshold": 0,
|
"threshold": 0,
|
||||||
|
@ -462,28 +464,32 @@ const upgrades = [
|
||||||
"giftable": true,
|
"giftable": true,
|
||||||
"name": "+3 base combo",
|
"name": "+3 base combo",
|
||||||
"max": 7,
|
"max": 7,
|
||||||
"help": "Your combo starts 3 points higher."
|
"help": "Your combo starts at 4",
|
||||||
|
extraLevelsHelp: `Combo starts 3 points higher`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"threshold": 0,
|
"threshold": 0,
|
||||||
"id": "slow_down",
|
"id": "slow_down",
|
||||||
"name": "Slower ball",
|
"name": "Slower ball",
|
||||||
"max": 2,
|
"max": 2,
|
||||||
"help": "Slows down the ball."
|
"help": "Slows down the ball",
|
||||||
|
extraLevelsHelp: `Make it even slower`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"threshold": 0,
|
"threshold": 0,
|
||||||
"id": "bigger_puck",
|
"id": "bigger_puck",
|
||||||
"name": "Bigger puck",
|
"name": "Bigger puck",
|
||||||
"max": 2,
|
"max": 2,
|
||||||
"help": "Catches more coins."
|
"help": "Catches more coins",
|
||||||
|
extraLevelsHelp: `Even bigger puck`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"threshold": 0,
|
"threshold": 0,
|
||||||
"id": "viscosity",
|
"id": "viscosity",
|
||||||
"name": "Viscosity",
|
"name": "Viscosity",
|
||||||
"max": 3,
|
"max": 3,
|
||||||
"help": "Slower coins fall.",
|
"help": "Slower coins fall",
|
||||||
|
extraLevelsHelp: `Even slower fall`,
|
||||||
tryout: {
|
tryout: {
|
||||||
perks: {viscosity: 3, base_combo: 3},
|
perks: {viscosity: 3, base_combo: 3},
|
||||||
level: 'Waves'
|
level: 'Waves'
|
||||||
|
@ -510,7 +516,8 @@ const upgrades = [
|
||||||
"id": "skip_last",
|
"id": "skip_last",
|
||||||
"name": "Easy Cleanup",
|
"name": "Easy Cleanup",
|
||||||
"max": 7,
|
"max": 7,
|
||||||
"help": "The last brick will self-destruct."
|
"help": "The last brick will self-destruct",
|
||||||
|
extraLevelsHelp: `Level clears one brick earlier`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"threshold": 500,
|
"threshold": 500,
|
||||||
|
@ -518,40 +525,44 @@ const upgrades = [
|
||||||
"giftable": true,
|
"giftable": true,
|
||||||
"name": "Puck controls ball",
|
"name": "Puck controls ball",
|
||||||
"max": 2,
|
"max": 2,
|
||||||
"help": "Control the ball's trajectory."
|
"help": "Control the ball's trajectory",
|
||||||
|
extraLevelsHelp: `Stronger effect on the ball`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"threshold": 1000,
|
"threshold": 1000,
|
||||||
"id": "coin_magnet",
|
"id": "coin_magnet",
|
||||||
"name": "Coins magnet",
|
"name": "Coins magnet",
|
||||||
"max": 3,
|
"max": 3,
|
||||||
"help": "Puck attracts coins.",
|
"help": "Puck attracts coins",
|
||||||
tryout: {
|
tryout: {
|
||||||
perks: {coin_magnet: 3, base_combo: 3}
|
perks: {coin_magnet: 3, base_combo: 3}
|
||||||
}
|
}, extraLevelsHelp: `Stronger effect on the coins`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"threshold": 1500,
|
"threshold": 1500,
|
||||||
"id": "multiball",
|
"id": "multiball",
|
||||||
"giftable": true,
|
"giftable": true,
|
||||||
"name": "+1 ball",
|
"name": "+1 ball",
|
||||||
"max": 3,
|
"max": 6,
|
||||||
"help": "Start with one more balls.",
|
"help": "Start with two balls",
|
||||||
|
extraLevelsHelp: `One more ball`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"threshold": 2000,
|
"threshold": 2000,
|
||||||
"id": "smaller_puck",
|
"id": "smaller_puck",
|
||||||
"name": "Smaller puck",
|
"name": "Smaller puck",
|
||||||
"max": 2,
|
"max": 2,
|
||||||
"help": "Gives you more control."
|
"help": "Gives you more control",
|
||||||
|
extraLevelsHelp: `Even smaller puck`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"threshold": 3000,
|
"threshold": 3000,
|
||||||
"id": "pierce",
|
"id": "pierce",
|
||||||
"giftable": true,
|
"giftable": true,
|
||||||
"name": "Heavy ball",
|
"name": "Piercing",
|
||||||
"max": 3,
|
"max": 3,
|
||||||
"help": "Ball pierces bricks."
|
"help": "Ball pierces 3 bricks",
|
||||||
|
extraLevelsHelp: `Pierce 3 more bricks`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"threshold": 4000,
|
"threshold": 4000,
|
||||||
|
@ -560,7 +571,7 @@ const upgrades = [
|
||||||
"name": "Picky eater",
|
"name": "Picky eater",
|
||||||
"color_blind_exclude": true,
|
"color_blind_exclude": true,
|
||||||
"max": 1,
|
"max": 1,
|
||||||
"help": "Break bricks color by color.",
|
"help": "Break bricks color by color",
|
||||||
|
|
||||||
tryout: {
|
tryout: {
|
||||||
perks: {picky_eater: 1},
|
perks: {picky_eater: 1},
|
||||||
|
@ -573,7 +584,7 @@ const upgrades = [
|
||||||
"name": "Stain",
|
"name": "Stain",
|
||||||
"color_blind_exclude": true,
|
"color_blind_exclude": true,
|
||||||
"max": 1,
|
"max": 1,
|
||||||
"help": "Coins color the bricks they touch.",
|
"help": "Coins color the bricks they touch",
|
||||||
tryout: {
|
tryout: {
|
||||||
perks: {metamorphosis: 3},
|
perks: {metamorphosis: 3},
|
||||||
level: 'Lines'
|
level: 'Lines'
|
||||||
|
@ -585,7 +596,8 @@ const upgrades = [
|
||||||
"giftable": true,
|
"giftable": true,
|
||||||
"name": "Compound interest",
|
"name": "Compound interest",
|
||||||
"max": 3,
|
"max": 3,
|
||||||
"help": "Avoid missing coins with your puck."
|
"help": "Avoid missing coins with your puck",
|
||||||
|
extraLevelsHelp: `Combo grows faster but missed coins hurt it more`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"threshold": 7000,
|
"threshold": 7000,
|
||||||
|
@ -593,23 +605,24 @@ const upgrades = [
|
||||||
"giftable": true,
|
"giftable": true,
|
||||||
"name": "Hot start",
|
"name": "Hot start",
|
||||||
"max": 3,
|
"max": 3,
|
||||||
"help": "Clear the level quickly."
|
"help": "Clear the level quickly",
|
||||||
|
extraLevelsHelp: `Combo starts higher but shrinks faster`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"threshold": 9000,
|
"threshold": 9000,
|
||||||
"id": "sapper",
|
"id": "sapper",
|
||||||
"giftable": true,
|
"giftable": true,
|
||||||
"name": "Sapper",
|
"name": "Sapper",
|
||||||
"max": 1,
|
"max": 7,
|
||||||
"help": "Bricks become bombs."
|
"help": "1st brick hit becomes bomb",
|
||||||
|
extraLevelsHelp: `1 more brick replaced by a bomb`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"threshold": 11000,
|
"threshold": 11000,
|
||||||
"id": "bigger_explosions",
|
"id": "bigger_explosions",
|
||||||
"name": "Kaboom",
|
"name": "Kaboom",
|
||||||
"max": 1,
|
"max": 1,
|
||||||
"help": "Bigger explosions.",
|
"help": "Bigger explosions",
|
||||||
|
|
||||||
tryout: {
|
tryout: {
|
||||||
perks: {bigger_explosions: 1},
|
perks: {bigger_explosions: 1},
|
||||||
level: 'Ship'
|
level: 'Ship'
|
||||||
|
@ -620,7 +633,8 @@ const upgrades = [
|
||||||
"id": "extra_levels",
|
"id": "extra_levels",
|
||||||
"name": "+1 level",
|
"name": "+1 level",
|
||||||
"max": 3,
|
"max": 3,
|
||||||
"help": "Play one more level before winning."
|
"help": "Play 8 levels instead of 7",
|
||||||
|
extraLevelsHelp: `1 more brick replaced by a bomb`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"threshold": 15000,
|
"threshold": 15000,
|
||||||
|
@ -628,14 +642,15 @@ const upgrades = [
|
||||||
"name": "Color pierce",
|
"name": "Color pierce",
|
||||||
"color_blind_exclude": true,
|
"color_blind_exclude": true,
|
||||||
"max": 1,
|
"max": 1,
|
||||||
"help": "Ball breaks same color bricks."
|
"help": "Ball breaks same color bricks"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"threshold": 18000,
|
"threshold": 18000,
|
||||||
"id": "soft_reset",
|
"id": "soft_reset",
|
||||||
"name": "Soft reset",
|
"name": "Soft reset",
|
||||||
"max": 2,
|
"max": 2,
|
||||||
"help": "Combo grows slower but resets less"
|
"help": "Combo grows slower but resets less",
|
||||||
|
extraLevelsHelp: `Even slower combo growth but softer reset`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"threshold": 21000,
|
"threshold": 21000,
|
||||||
|
@ -644,6 +659,7 @@ const upgrades = [
|
||||||
requires: 'multiball',
|
requires: 'multiball',
|
||||||
"max": 3,
|
"max": 3,
|
||||||
"help": "Balls repulse balls.",
|
"help": "Balls repulse balls.",
|
||||||
|
extraLevelsHelp: 'Stronger repulsion force ',
|
||||||
tryout: {
|
tryout: {
|
||||||
perks: {ball_repulse_ball: 1, multiball: 2},
|
perks: {ball_repulse_ball: 1, multiball: 2},
|
||||||
}
|
}
|
||||||
|
@ -654,7 +670,7 @@ const upgrades = [
|
||||||
requires: 'multiball',
|
requires: 'multiball',
|
||||||
"name": "Gravity",
|
"name": "Gravity",
|
||||||
"max": 3,
|
"max": 3,
|
||||||
"help": "Balls attract balls.",
|
"help": "Balls attract balls.", extraLevelsHelp: 'Stronger attraction force ',
|
||||||
tryout: {
|
tryout: {
|
||||||
perks: {ball_attract_ball: 1, multiball: 2},
|
perks: {ball_attract_ball: 1, multiball: 2},
|
||||||
}
|
}
|
||||||
|
@ -663,6 +679,7 @@ const upgrades = [
|
||||||
"threshold": 30000,
|
"threshold": 30000,
|
||||||
"id": "puck_repulse_ball",
|
"id": "puck_repulse_ball",
|
||||||
"name": "Soft landing",
|
"name": "Soft landing",
|
||||||
|
extraLevelsHelp: 'Stronger repulsion force ',
|
||||||
"max": 3,
|
"max": 3,
|
||||||
"help": "Puck repulses balls.",
|
"help": "Puck repulses balls.",
|
||||||
},
|
},
|
||||||
|
@ -671,7 +688,7 @@ const upgrades = [
|
||||||
"id": "wind",
|
"id": "wind",
|
||||||
"name": "Wind",
|
"name": "Wind",
|
||||||
"max": 3,
|
"max": 3,
|
||||||
"help": "Puck position creates wind.",
|
"help": "Puck position creates wind.", extraLevelsHelp: 'Stronger wind force ',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"threshold": 40000,
|
"threshold": 40000,
|
||||||
|
@ -679,13 +696,14 @@ const upgrades = [
|
||||||
"name": "Sturdy bricks",
|
"name": "Sturdy bricks",
|
||||||
"max": 4,
|
"max": 4,
|
||||||
"help": "Bricks sometimes resist hits but drop more coins.",
|
"help": "Bricks sometimes resist hits but drop more coins.",
|
||||||
|
extraLevelsHelp: 'Bricks resist more and drop more coins ',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"threshold": 45000,
|
"threshold": 45000,
|
||||||
"id": "respawn",
|
"id": "respawn",
|
||||||
"name": "Respawn",
|
"name": "Respawn",
|
||||||
"max": 4,
|
"max": 4,
|
||||||
"help": "The first brick hit will respawn.",
|
"help": "The first brick hit will respawn.", extraLevelsHelp: 'More bricks can respawn ',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -759,7 +777,6 @@ function pickRandomUpgrades(count) {
|
||||||
.sort((a, b) => a.id > b.id ? 1 : -1)
|
.sort((a, b) => a.id > b.id ? 1 : -1)
|
||||||
|
|
||||||
list.forEach(u => {
|
list.forEach(u => {
|
||||||
incrementRunStatistics('offered_upgrade.' + u.id, 1)
|
|
||||||
dontOfferTooSoon(u.id)
|
dontOfferTooSoon(u.id)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -768,10 +785,9 @@ function pickRandomUpgrades(count) {
|
||||||
icon: u.icon,
|
icon: u.icon,
|
||||||
value: () => {
|
value: () => {
|
||||||
perks[u.id]++;
|
perks[u.id]++;
|
||||||
incrementRunStatistics('picked_upgrade.' + u.id, 1)
|
|
||||||
scoreStory.push("Picked upgrade : " + u.name);
|
scoreStory.push("Picked upgrade : " + u.name);
|
||||||
},
|
},
|
||||||
help: u.help,
|
help: (perks[u.id] && u.extraLevelsHelp) || u.help,
|
||||||
// max: u.max,
|
// max: u.max,
|
||||||
// checked: perks[u.id]
|
// checked: perks[u.id]
|
||||||
}))
|
}))
|
||||||
|
@ -797,7 +813,6 @@ function restart() {
|
||||||
}
|
}
|
||||||
const randomGift = reset_perks();
|
const randomGift = reset_perks();
|
||||||
|
|
||||||
incrementRunStatistics('starting_upgrade.' + randomGift, 1)
|
|
||||||
dontOfferTooSoon(randomGift)
|
dontOfferTooSoon(randomGift)
|
||||||
|
|
||||||
setLevel(0);
|
setLevel(0);
|
||||||
|
@ -983,6 +998,9 @@ function tick() {
|
||||||
if (running) {
|
if (running) {
|
||||||
|
|
||||||
levelTime += currentTick - lastTick;
|
levelTime += currentTick - lastTick;
|
||||||
|
runStatistics.runTime += currentTick - lastTick
|
||||||
|
runStatistics.max_combo = Math.max(runStatistics.max_combo, combo)
|
||||||
|
|
||||||
// How many time to compute
|
// How many time to compute
|
||||||
let delta = Math.min(4, (currentTick - lastTick) / (1000 / 60));
|
let delta = Math.min(4, (currentTick - lastTick) / (1000 / 60));
|
||||||
delta *= running ? 1 : 0
|
delta *= running ? 1 : 0
|
||||||
|
@ -1006,7 +1024,6 @@ function tick() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (!remainingBricks && !coins.length) {
|
if (!remainingBricks && !coins.length) {
|
||||||
incrementRunStatistics('level_time', levelTime)
|
|
||||||
|
|
||||||
if (currentLevel + 1 < max_levels()) {
|
if (currentLevel + 1 < max_levels()) {
|
||||||
setLevel(currentLevel + 1);
|
setLevel(currentLevel + 1);
|
||||||
|
@ -1185,13 +1202,13 @@ function ballTick(ball, delta) {
|
||||||
resetCombo(ball.x, ball.y);
|
resetCombo(ball.x, ball.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(perks.respawn){
|
if (perks.respawn) {
|
||||||
ball.hitItem.slice(0,-1).slice(0,perks.respawn)
|
ball.hitItem.slice(0, -1).slice(0, perks.respawn)
|
||||||
.forEach(({index,color})=>bricks[index]=bricks[index]||color)
|
.forEach(({index, color}) => bricks[index] = bricks[index] || color)
|
||||||
}
|
}
|
||||||
ball.hitItem=[]
|
ball.hitItem = []
|
||||||
if (!ball.hitSinceBounce) {
|
if (!ball.hitSinceBounce) {
|
||||||
incrementRunStatistics('miss')
|
runStatistics.misses++
|
||||||
levelMisses++;
|
levelMisses++;
|
||||||
const loss = resetCombo(ball.x, ball.y)
|
const loss = resetCombo(ball.x, ball.y)
|
||||||
if (ball.bouncesList?.length) {
|
if (ball.bouncesList?.length) {
|
||||||
|
@ -1224,8 +1241,9 @@ function ballTick(ball, delta) {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
incrementRunStatistics('puck_bounces')
|
runStatistics.puck_bounces++
|
||||||
ball.hitSinceBounce = 0;
|
ball.hitSinceBounce = 0;
|
||||||
|
ball.sapperUses = 0;
|
||||||
ball.piercedSinceBounce = 0;
|
ball.piercedSinceBounce = 0;
|
||||||
ball.bouncesList = [{
|
ball.bouncesList = [{
|
||||||
x: ball.previousx,
|
x: ball.previousx,
|
||||||
|
@ -1235,6 +1253,7 @@ function ballTick(ball, delta) {
|
||||||
|
|
||||||
if (ball.y > gameZoneHeight + ballSize / 2 && running) {
|
if (ball.y > gameZoneHeight + ballSize / 2 && running) {
|
||||||
ball.destroyed = true;
|
ball.destroyed = true;
|
||||||
|
runStatistics.balls_lost++
|
||||||
if (!balls.find((b) => !b.destroyed)) {
|
if (!balls.find((b) => !b.destroyed)) {
|
||||||
if (perks.extra_life) {
|
if (perks.extra_life) {
|
||||||
perks.extra_life--;
|
perks.extra_life--;
|
||||||
|
@ -1262,10 +1281,11 @@ function ballTick(ball, delta) {
|
||||||
|
|
||||||
explodeBrick(hitBrick, ball, false);
|
explodeBrick(hitBrick, ball, false);
|
||||||
|
|
||||||
if (perks.sapper && initialBrickColor !== "black" &&
|
if (ball.sapperUses < perks.sapper && initialBrickColor !== "black" &&
|
||||||
// don't replace a brick that bounced with sturdy_bricks
|
// don't replace a brick that bounced with sturdy_bricks
|
||||||
!bricks[hitBrick]) {
|
!bricks[hitBrick]) {
|
||||||
bricks[hitBrick] = "black";
|
bricks[hitBrick] = "black";
|
||||||
|
ball.sapperUses++
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1289,26 +1309,8 @@ function ballTick(ball, delta) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let runStatistics = {};
|
let runStatistics = {};
|
||||||
|
|
||||||
function resetRunStatistics() {
|
|
||||||
runStatistics = {
|
|
||||||
started: Date.now(),
|
|
||||||
ended: null,
|
|
||||||
hadOverrides,
|
|
||||||
width: window.innerWidth,
|
|
||||||
height: window.innerHeight,
|
|
||||||
easy: isSettingOn('easy'),
|
|
||||||
color_blind: isSettingOn('color_blind'),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function incrementRunStatistics(key, amount = 1) {
|
|
||||||
runStatistics[key + '_total'] = (runStatistics[key + '_total'] || 0) + amount
|
|
||||||
runStatistics[key + '_lvl_' + currentLevel] = (runStatistics[key + '_lvl_' + currentLevel] || 0) + amount
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTotalScore() {
|
function getTotalScore() {
|
||||||
try {
|
try {
|
||||||
|
@ -1326,12 +1328,13 @@ function addToTotalScore(points) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function gameOver(title, intro) {
|
function gameOver(title, intro) {
|
||||||
if (!running) return;
|
if (!running) return;
|
||||||
pause()
|
pause()
|
||||||
stopRecording()
|
stopRecording()
|
||||||
|
|
||||||
runStatistics.ended = Date.now()
|
runStatistics.max_level = currentLevel+1
|
||||||
|
|
||||||
const {stats} = getLevelStats();
|
const {stats} = getLevelStats();
|
||||||
|
|
||||||
|
@ -1341,17 +1344,6 @@ function gameOver(title, intro) {
|
||||||
} else {
|
} else {
|
||||||
scoreStory.push(`You dropped the ball and finished your run early. `);
|
scoreStory.push(`You dropped the ball and finished your run early. `);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
// Stores only last 100 runs
|
|
||||||
const runsHistory = JSON.parse(localStorage.getItem('breakout_71_history') || '[]').slice(0, 99).concat([runStatistics])
|
|
||||||
|
|
||||||
// Generate some histogram
|
|
||||||
|
|
||||||
localStorage.setItem('breakout_71_history', '<pre>' + JSON.stringify(runsHistory, null, 2) + '</pre>')
|
|
||||||
} catch {
|
|
||||||
}
|
|
||||||
|
|
||||||
let animationDelay = -300
|
let animationDelay = -300
|
||||||
const getDelay = () => {
|
const getDelay = () => {
|
||||||
animationDelay += 800
|
animationDelay += 800
|
||||||
|
@ -1396,6 +1388,80 @@ function gameOver(title, intro) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let runStats = ''
|
||||||
|
if (!hadOverrides) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Stores only top 100 runs
|
||||||
|
let runsHistory = JSON.parse(localStorage.getItem('breakout_71_runs_history') || '[]');
|
||||||
|
runsHistory.sort((a,b)=>a.score-b.score).reverse()
|
||||||
|
runsHistory=runsHistory.slice(0, 100)
|
||||||
|
console.log(runsHistory.map(r=>r.score))
|
||||||
|
runsHistory.push(runStatistics)
|
||||||
|
|
||||||
|
// Generate some histogram
|
||||||
|
localStorage.setItem('breakout_71_runs_history', JSON.stringify(runsHistory, null, 2))
|
||||||
|
|
||||||
|
const makeHistogram = (title, getter, unit) => {
|
||||||
|
let values = runsHistory.map(h => getter(h) || 0)
|
||||||
|
const min = Math.min(...values)
|
||||||
|
const max = Math.max(...values)
|
||||||
|
// No point
|
||||||
|
if(min===max) return ''
|
||||||
|
// One bin per unique value, max 10
|
||||||
|
const binsCount = Math.min(values.length,10)
|
||||||
|
if(binsCount<3) return ''
|
||||||
|
const bins = []
|
||||||
|
const binsTotal = []
|
||||||
|
for(let i=0;i<binsCount;i++){
|
||||||
|
bins.push(0)
|
||||||
|
binsTotal.push(0)
|
||||||
|
}
|
||||||
|
const binSize = (max - min) / bins.length
|
||||||
|
const binIndexOf = v => Math.min(bins.length - 1, Math.floor((v - min) / binSize))
|
||||||
|
values.forEach(v => {
|
||||||
|
if(isNaN(v)) return
|
||||||
|
const index=binIndexOf(v)
|
||||||
|
bins[index]++
|
||||||
|
binsTotal[index]+=v
|
||||||
|
})
|
||||||
|
if(bins.filter(b=>b).length<3) return ''
|
||||||
|
const maxBin = Math.max(...bins)
|
||||||
|
const lastValue = values[values.length - 1]
|
||||||
|
const activeBin = binIndexOf(lastValue)
|
||||||
|
return `<h2 class="histogram-title">${title} : <strong>${lastValue}${unit}</strong></h2><div class="histogram">
|
||||||
|
${bins.map((v, vi) => `<span class="${vi === activeBin ? 'active' : ''}"><span style="height:${v / maxBin * 80}px" title="${v} run${v>1 ? 's':''} between ${
|
||||||
|
Math.floor(min + vi * binSize)} and ${Math.floor(min + (vi + 1) * binSize)}${unit}"
|
||||||
|
><span>${
|
||||||
|
(!v && ' ') || (vi==activeBin && lastValue+unit) || (Math.round(binsTotal[vi]/v)+unit)
|
||||||
|
}</span></span></span>`).join('')}
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
runStats += makeHistogram('Total score', r => r.score, '')
|
||||||
|
runStats += makeHistogram('Catch rate', r => Math.round(r.score / r.coins_spawned * 100), '%')
|
||||||
|
runStats += makeHistogram('Bricks broken', r => r.bricks_broken, '')
|
||||||
|
runStats += makeHistogram('Bricks broken per minute', r =>Math.round(r.bricks_broken/r.runTime*1000*60), ' bpm')
|
||||||
|
runStats += makeHistogram('Hit rate', r => Math.round((1-r.misses / r.puck_bounces) * 100), '%')
|
||||||
|
runStats += makeHistogram('Duration per level', r => Math.round(r.runTime/1000/r.levelsPlayed), 's')
|
||||||
|
runStats += makeHistogram('Level reached', r => r.levelsPlayed, '')
|
||||||
|
runStats += makeHistogram('Upgrades applied', r => r.upgrades_picked, '')
|
||||||
|
runStats += makeHistogram('Balls lost', r => r.balls_lost, '')
|
||||||
|
runStats += makeHistogram('Average combo', r => Math.round(r.coins_spawned /r.bricks_broken) , '')
|
||||||
|
runStats += makeHistogram('Max combo', r => r.max_combo , '')
|
||||||
|
|
||||||
|
if(runStats){
|
||||||
|
runStats= `<p>Find below your run statistics compared to past runs.</p>`+ runStats
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Avoid the sad sound right as we restart a new games
|
// Avoid the sad sound right as we restart a new games
|
||||||
combo = 1
|
combo = 1
|
||||||
asyncAlert({
|
asyncAlert({
|
||||||
|
@ -1403,19 +1469,35 @@ function gameOver(title, intro) {
|
||||||
<p>${intro}</p>
|
<p>${intro}</p>
|
||||||
${unlocksInfo}
|
${unlocksInfo}
|
||||||
`, textAfterButtons: `
|
`, textAfterButtons: `
|
||||||
|
${runStats}
|
||||||
<div id="level-recording-container"></div>
|
<div id="level-recording-container"></div>
|
||||||
${scoreStory.map((t) => "<p>" + t + "</p>").join("")}
|
${scoreStory.map((t) => "<p>" + t + "</p>").join("")}
|
||||||
`
|
`
|
||||||
}).then(() => restart());
|
}).then(() => restart());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resetRunStatistics() {
|
||||||
|
runStatistics = {
|
||||||
|
started: Date.now(),
|
||||||
|
levelsPlayed: 0,
|
||||||
|
runTime: 0,
|
||||||
|
coins_spawned: 0,
|
||||||
|
score: 0,
|
||||||
|
bricks_broken:0,
|
||||||
|
misses:0,
|
||||||
|
balls_lost:0,
|
||||||
|
puck_bounces:0,
|
||||||
|
upgrades_picked:1,
|
||||||
|
max_combo:1
|
||||||
|
}
|
||||||
|
}
|
||||||
function explodeBrick(index, ball, isExplosion) {
|
function explodeBrick(index, ball, isExplosion) {
|
||||||
const color = bricks[index];
|
const color = bricks[index];
|
||||||
|
|
||||||
if (color === 'black') {
|
if (color === 'black') {
|
||||||
delete bricks[index];
|
delete bricks[index];
|
||||||
const x = brickCenterX(index), y = brickCenterY(index);
|
const x = brickCenterX(index), y = brickCenterY(index);
|
||||||
|
|
||||||
incrementRunStatistics('explosion', 1)
|
|
||||||
sounds.explode(ball.x);
|
sounds.explode(ball.x);
|
||||||
const {col, row} = getRowCol(index);
|
const {col, row} = getRowCol(index);
|
||||||
const size = 1 + perks.bigger_explosions;
|
const size = 1 + perks.bigger_explosions;
|
||||||
|
@ -1443,16 +1525,16 @@ function explodeBrick(index, ball, isExplosion) {
|
||||||
});
|
});
|
||||||
spawnExplosion(7 * (1 + perks.bigger_explosions), x, y, 'white', 150, coinSize,);
|
spawnExplosion(7 * (1 + perks.bigger_explosions), x, y, 'white', 150, coinSize,);
|
||||||
ball.hitSinceBounce++;
|
ball.hitSinceBounce++;
|
||||||
|
runStatistics.bricks_broken++
|
||||||
} else if (color) {
|
} else if (color) {
|
||||||
// Even if it bounces we don't want to count that as a miss
|
// Even if it bounces we don't want to count that as a miss
|
||||||
ball.hitSinceBounce++;
|
ball.hitSinceBounce++;
|
||||||
|
|
||||||
if(perks.sturdy_bricks && perks.sturdy_bricks*2>Math.random()*10){
|
if (perks.sturdy_bricks && perks.sturdy_bricks * 2 > Math.random() * 10) {
|
||||||
// Resist
|
// Resist
|
||||||
sounds.coinBounce(ball.x, 1)
|
sounds.coinBounce(ball.x, 1)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Flashing is take care of by the tick loop
|
// Flashing is take care of by the tick loop
|
||||||
const x = brickCenterX(index), y = brickCenterY(index);
|
const x = brickCenterX(index), y = brickCenterY(index);
|
||||||
|
|
||||||
|
@ -1460,16 +1542,18 @@ function explodeBrick(index, ball, isExplosion) {
|
||||||
|
|
||||||
levelSpawnedCoins += combo;
|
levelSpawnedCoins += combo;
|
||||||
|
|
||||||
incrementRunStatistics('spawned_coins', combo)
|
runStatistics.coins_spawned+=combo
|
||||||
|
runStatistics.bricks_broken++
|
||||||
|
|
||||||
|
|
||||||
coins = coins.filter((c) => !c.destroyed);
|
coins = coins.filter((c) => !c.destroyed);
|
||||||
let coinsToSpawn=combo
|
let coinsToSpawn = combo
|
||||||
if(perks.sturdy_bricks){
|
if (perks.sturdy_bricks) {
|
||||||
// +10% per level
|
// +10% per level
|
||||||
coinsToSpawn+=Math.ceil((10+perks.sturdy_bricks) / 10 * coinsToSpawn)
|
coinsToSpawn += Math.ceil((10 + perks.sturdy_bricks) / 10 * coinsToSpawn)
|
||||||
}
|
}
|
||||||
|
|
||||||
while (coinsToSpawn-- ) {
|
while (coinsToSpawn--) {
|
||||||
// Avoids saturating the canvas with coins
|
// Avoids saturating the canvas with coins
|
||||||
if (coins.length > MAX_COINS * (isSettingOn("basic") ? 0.5 : 1)) {
|
if (coins.length > MAX_COINS * (isSettingOn("basic") ? 0.5 : 1)) {
|
||||||
// Just pick a random one
|
// Just pick a random one
|
||||||
|
@ -1518,7 +1602,7 @@ function explodeBrick(index, ball, isExplosion) {
|
||||||
spawnExplosion(5 + combo, x, y, color, 100, coinSize / 2);
|
spawnExplosion(5 + combo, x, y, color, 100, coinSize / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!bricks[index] ){
|
if (!bricks[index]) {
|
||||||
ball.hitItem?.push({
|
ball.hitItem?.push({
|
||||||
index,
|
index,
|
||||||
color
|
color
|
||||||
|
@ -2375,7 +2459,7 @@ document.getElementById("menu").addEventListener("click", (e) => {
|
||||||
const options = {
|
const options = {
|
||||||
sound: {
|
sound: {
|
||||||
default: true, name: `Game sounds`, help: `Can slow down some phones.`,
|
default: true, name: `Game sounds`, help: `Can slow down some phones.`,
|
||||||
disabled:()=>false
|
disabled: () => false
|
||||||
}, "mobile-mode": {
|
}, "mobile-mode": {
|
||||||
default: window.innerHeight > window.innerWidth,
|
default: window.innerHeight > window.innerWidth,
|
||||||
name: `Mobile mode`,
|
name: `Mobile mode`,
|
||||||
|
@ -2383,30 +2467,30 @@ const options = {
|
||||||
afterChange() {
|
afterChange() {
|
||||||
fitSize();
|
fitSize();
|
||||||
},
|
},
|
||||||
disabled:()=>false
|
disabled: () => false
|
||||||
},
|
},
|
||||||
basic: {
|
basic: {
|
||||||
default: false, name: `Fast mode`, help: `Simpler graphics for older devices.`,
|
default: false, name: `Fast mode`, help: `Simpler graphics for older devices.`,
|
||||||
disabled:()=>false
|
disabled: () => false
|
||||||
},
|
},
|
||||||
"easy": {
|
"easy": {
|
||||||
default: false, name: `Easy mode`, help: `Slower ball as starting perk.`, restart: true,
|
default: false, name: `Easy mode`, help: `Slower ball as starting perk.`, restart: true,
|
||||||
disabled:()=>false
|
disabled: () => false
|
||||||
}, "color_blind": {
|
}, "color_blind": {
|
||||||
default: false, name: `Color blind mode`, help: `Removes mechanics about colors.`, restart: true,
|
default: false, name: `Color blind mode`, help: `Removes mechanics about colors.`, restart: true,
|
||||||
disabled:()=>false
|
disabled: () => false
|
||||||
},
|
},
|
||||||
// Could not get the sharing to work without loading androidx and all the modern android things so for now i'll just disable sharing in the android app
|
// Could not get the sharing to work without loading androidx and all the modern android things so for now i'll just disable sharing in the android app
|
||||||
"record": {
|
"record": {
|
||||||
default: false, name: `Record gameplay videos`, help: `Get a video of each level.`,
|
default: false, name: `Record gameplay videos`, help: `Get a video of each level.`,
|
||||||
disabled(){
|
disabled() {
|
||||||
return window.location.search.includes('isInWebView=true')
|
return window.location.search.includes('isInWebView=true')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
gif: {
|
gif: {
|
||||||
default: false, name: `Make a gif too`, help: `3x heavier, 2x smaller, 7s max`,
|
default: false, name: `Make a gif too`, help: `3x heavier, 2x smaller, 7s max`,
|
||||||
disabled(){
|
disabled() {
|
||||||
return window.location.protocol === "file:" ||! isSettingOn('record')
|
return window.location.protocol === "file:" || !isSettingOn('record')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -2417,9 +2501,9 @@ async function openSettingsPanel() {
|
||||||
|
|
||||||
const optionsList = [];
|
const optionsList = [];
|
||||||
for (const key in options) {
|
for (const key in options) {
|
||||||
if (options[key] )
|
if (options[key])
|
||||||
optionsList.push({
|
optionsList.push({
|
||||||
disabled:options[key].disabled(),
|
disabled: options[key].disabled(),
|
||||||
checked: isSettingOn(key) ? 1 : 0,
|
checked: isSettingOn(key) ? 1 : 0,
|
||||||
max: 1, text: options[key].name, help: options[key].help, value: () => {
|
max: 1, text: options[key].name, help: options[key].help, value: () => {
|
||||||
toggleSetting(key)
|
toggleSetting(key)
|
||||||
|
@ -2496,29 +2580,29 @@ Click an item above to start a test run with it.
|
||||||
|
|
||||||
...optionsList,
|
...optionsList,
|
||||||
|
|
||||||
(document.fullscreenEnabled || document.webkitFullscreenEnabled) &&(document.fullscreenElement!==null ?{
|
(document.fullscreenEnabled || document.webkitFullscreenEnabled) && (document.fullscreenElement !== null ? {
|
||||||
text: "Exit Fullscreen",
|
text: "Exit Fullscreen",
|
||||||
help: "Might not work on some machines",
|
help: "Might not work on some machines",
|
||||||
value() {
|
value() {
|
||||||
if (document.exitFullscreen) {
|
if (document.exitFullscreen) {
|
||||||
document.exitFullscreen();
|
document.exitFullscreen();
|
||||||
} else if (document.webkitCancelFullScreen) {
|
} else if (document.webkitCancelFullScreen) {
|
||||||
document.webkitCancelFullScreen();
|
document.webkitCancelFullScreen();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
} :
|
||||||
}:
|
{
|
||||||
{
|
text: "Fullscreen",
|
||||||
text: "Fullscreen",
|
help: "Might not work on some machines",
|
||||||
help: "Might not work on some machines",
|
value() {
|
||||||
value() {
|
const docel = document.documentElement
|
||||||
const docel = document.documentElement
|
if (docel.requestFullscreen) {
|
||||||
if (docel.requestFullscreen) {
|
docel.requestFullscreen();
|
||||||
docel.requestFullscreen();
|
} else if (docel.webkitRequestFullscreen) {
|
||||||
} else if (docel.webkitRequestFullscreen) {
|
docel.webkitRequestFullscreen();
|
||||||
docel.webkitRequestFullscreen();
|
}
|
||||||
}
|
}
|
||||||
}
|
}),
|
||||||
}),
|
|
||||||
{
|
{
|
||||||
text: 'Reset Game',
|
text: 'Reset Game',
|
||||||
help: "Erase high score and statistics",
|
help: "Erase high score and statistics",
|
||||||
|
@ -2590,8 +2674,22 @@ function repulse(a, b, power, impactsBToo) {
|
||||||
a.vx -= dx * fact
|
a.vx -= dx * fact
|
||||||
a.vy -= dy * fact
|
a.vy -= dy * fact
|
||||||
|
|
||||||
const speed = 10
|
const speed = 10
|
||||||
const rand = 2
|
const rand = 2
|
||||||
|
flashes.push({
|
||||||
|
type: "particle",
|
||||||
|
duration: 100,
|
||||||
|
time: levelTime,
|
||||||
|
size: coinSize / 2,
|
||||||
|
color: rainbowColor(),
|
||||||
|
ethereal: true,
|
||||||
|
x: a.x,
|
||||||
|
y: a.y,
|
||||||
|
vx: -dx * speed + a.vx + (Math.random() - 0.5) * rand,
|
||||||
|
vy: -dy * speed + a.vy + (Math.random() - 0.5) * rand,
|
||||||
|
})
|
||||||
|
if (impactsBToo) {
|
||||||
|
|
||||||
flashes.push({
|
flashes.push({
|
||||||
type: "particle",
|
type: "particle",
|
||||||
duration: 100,
|
duration: 100,
|
||||||
|
@ -2599,26 +2697,12 @@ function repulse(a, b, power, impactsBToo) {
|
||||||
size: coinSize / 2,
|
size: coinSize / 2,
|
||||||
color: rainbowColor(),
|
color: rainbowColor(),
|
||||||
ethereal: true,
|
ethereal: true,
|
||||||
x: a.x,
|
x: b.x,
|
||||||
y: a.y,
|
y: b.y,
|
||||||
vx: -dx * speed + a.vx + (Math.random() - 0.5) * rand,
|
vx: dx * speed + b.vx + (Math.random() - 0.5) * rand,
|
||||||
vy: -dy * speed + a.vy + (Math.random() - 0.5) * rand,
|
vy: dy * speed + b.vy + (Math.random() - 0.5) * rand,
|
||||||
})
|
})
|
||||||
if (impactsBToo) {
|
}
|
||||||
|
|
||||||
flashes.push({
|
|
||||||
type: "particle",
|
|
||||||
duration: 100,
|
|
||||||
time: levelTime,
|
|
||||||
size: coinSize / 2,
|
|
||||||
color: rainbowColor(),
|
|
||||||
ethereal: true,
|
|
||||||
x: b.x,
|
|
||||||
y: b.y,
|
|
||||||
vx: dx * speed + b.vx + (Math.random() - 0.5) * rand,
|
|
||||||
vy: dy * speed + b.vy + (Math.random() - 0.5) * rand,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2638,32 +2722,32 @@ function attract(a, b, power) {
|
||||||
a.vx -= dx * fact
|
a.vx -= dx * fact
|
||||||
a.vy -= dy * fact
|
a.vy -= dy * fact
|
||||||
|
|
||||||
const speed = 10
|
const speed = 10
|
||||||
const rand = 2
|
const rand = 2
|
||||||
flashes.push({
|
flashes.push({
|
||||||
type: "particle",
|
type: "particle",
|
||||||
duration: 100,
|
duration: 100,
|
||||||
time: levelTime,
|
time: levelTime,
|
||||||
size: coinSize / 2,
|
size: coinSize / 2,
|
||||||
color: rainbowColor(),
|
color: rainbowColor(),
|
||||||
ethereal: true,
|
ethereal: true,
|
||||||
x: a.x,
|
x: a.x,
|
||||||
y: a.y,
|
y: a.y,
|
||||||
vx: dx * speed + a.vx + (Math.random() - 0.5) * rand,
|
vx: dx * speed + a.vx + (Math.random() - 0.5) * rand,
|
||||||
vy: dy * speed + a.vy + (Math.random() - 0.5) * rand,
|
vy: dy * speed + a.vy + (Math.random() - 0.5) * rand,
|
||||||
})
|
})
|
||||||
flashes.push({
|
flashes.push({
|
||||||
type: "particle",
|
type: "particle",
|
||||||
duration: 100,
|
duration: 100,
|
||||||
time: levelTime,
|
time: levelTime,
|
||||||
size: coinSize / 2,
|
size: coinSize / 2,
|
||||||
color: rainbowColor(),
|
color: rainbowColor(),
|
||||||
ethereal: true,
|
ethereal: true,
|
||||||
x: b.x,
|
x: b.x,
|
||||||
y: b.y,
|
y: b.y,
|
||||||
vx: -dx * speed + b.vx + (Math.random() - 0.5) * rand,
|
vx: -dx * speed + b.vx + (Math.random() - 0.5) * rand,
|
||||||
vy: -dy * speed + b.vy + (Math.random() - 0.5) * rand,
|
vy: -dy * speed + b.vy + (Math.random() - 0.5) * rand,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
let levelIconHTMLCanvas = document.createElement('canvas')
|
let levelIconHTMLCanvas = document.createElement('canvas')
|
||||||
|
@ -2732,7 +2816,7 @@ function drawMainCanvasOnSmallCanvas() {
|
||||||
let nthGifFrame = 0, gifFrameReduction = 2
|
let nthGifFrame = 0, gifFrameReduction = 2
|
||||||
|
|
||||||
function recordGifFrame() {
|
function recordGifFrame() {
|
||||||
if(nthGifFrame/60>7) return
|
if (nthGifFrame / 60 > 7) return
|
||||||
gifCtx.globalCompositeOperation = 'screen'
|
gifCtx.globalCompositeOperation = 'screen'
|
||||||
gifCtx.globalAlpha = 1 / gifFrameReduction
|
gifCtx.globalAlpha = 1 / gifFrameReduction
|
||||||
gifCtx?.drawImage(canvas, offsetXRoundedDown, 0, gameZoneWidthRoundedUp, gameZoneHeight, 0, 0, gifCanvas.width, gifCanvas.height)
|
gifCtx?.drawImage(canvas, offsetXRoundedDown, 0, gameZoneWidthRoundedUp, gameZoneHeight, 0, 0, gifCanvas.width, gifCanvas.height)
|
||||||
|
@ -2782,8 +2866,8 @@ function startRecordingGame() {
|
||||||
height: gifCanvas.height,
|
height: gifCanvas.height,
|
||||||
dither: false,
|
dither: false,
|
||||||
});
|
});
|
||||||
}else{
|
} else {
|
||||||
levelGif=null
|
levelGif = null
|
||||||
}
|
}
|
||||||
|
|
||||||
// drawMainCanvasOnSmallCanvas()
|
// drawMainCanvasOnSmallCanvas()
|
||||||
|
@ -2862,12 +2946,10 @@ function resumeRecording() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function stopRecording() {
|
function stopRecording() {
|
||||||
|
|
||||||
if (!isSettingOn('record')) {
|
if (!isSettingOn('record')) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!mediaRecorder) return;
|
if (!mediaRecorder) return;
|
||||||
|
|
||||||
mediaRecorder?.stop()
|
mediaRecorder?.stop()
|
||||||
levelGif?.render()
|
levelGif?.render()
|
||||||
mediaRecorder = null
|
mediaRecorder = null
|
||||||
|
@ -2879,6 +2961,11 @@ function captureFileName(ext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
fitSize()
|
fitSize()
|
||||||
restart()
|
restart()
|
||||||
tick();
|
tick();
|
|
@ -84,13 +84,13 @@ body.black_puck #menu {
|
||||||
|
|
||||||
.popup > div {
|
.popup > div {
|
||||||
margin: auto;
|
margin: auto;
|
||||||
padding: 20px;
|
padding: 20px 10px;
|
||||||
/*border: 1px solid white;*/
|
/*border: 1px solid white;*/
|
||||||
transform-origin: center;
|
transform-origin: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
width: 90%;
|
width: 100%;
|
||||||
max-width: 450px;
|
max-width: 450px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,10 +249,11 @@ body.black_puck #menu {
|
||||||
margin: 40px;
|
margin: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#level-recording-container img,#level-recording-container video{
|
#level-recording-container img, #level-recording-container video {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
#level-recording-container a {
|
#level-recording-container a {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
@ -274,4 +275,61 @@ body.black_puck #menu {
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.histogram {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: stretch;
|
||||||
|
margin: 10px 0 40px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.histogram > span {
|
||||||
|
/* Hover zone */
|
||||||
|
flex-grow: 1;
|
||||||
|
width: 10px;
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
.histogram > span.active > span{
|
||||||
|
background: #4049ca;
|
||||||
|
}
|
||||||
|
.histogram > span > span{
|
||||||
|
/*Visible bar*/
|
||||||
|
background: #1c1c2f;
|
||||||
|
width: 100%;
|
||||||
|
display: block;
|
||||||
|
border-radius: 5px;
|
||||||
|
min-height: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.histogram > span > span> span {
|
||||||
|
/*label */
|
||||||
|
position: absolute;
|
||||||
|
bottom: -20px;
|
||||||
|
pointer-events: none;
|
||||||
|
white-space: nowrap;
|
||||||
|
transform-origin: bottom left;
|
||||||
|
font-size: 13px;
|
||||||
|
text-align: center;
|
||||||
|
display: block;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%,0);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.histogram > span:not(:hover):not(.active) > span> span {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2.histogram-title {
|
||||||
|
color: #3b3f75;
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2.histogram-title strong {
|
||||||
|
color: #4049ca;
|
||||||
}
|
}
|
Loading…
Add table
Add a link
Reference in a new issue