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,7 +132,6 @@ background.addEventListener("load", () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const fitSize = () => {
|
const fitSize = () => {
|
||||||
const {width, height} = canvas.getBoundingClientRect();
|
const {width, height} = canvas.getBoundingClientRect();
|
||||||
canvas.width = width;
|
canvas.width = width;
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -270,6 +270,7 @@ function resetBalls() {
|
||||||
piercedSinceBounce: 0,
|
piercedSinceBounce: 0,
|
||||||
hitSinceBounce: 0,
|
hitSinceBounce: 0,
|
||||||
hitItem: [],
|
hitItem: [],
|
||||||
|
sapperUses: 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);
|
||||||
|
@ -1191,7 +1208,7 @@ function ballTick(ball, delta) {
|
||||||
}
|
}
|
||||||
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,6 +1525,7 @@ 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++;
|
||||||
|
@ -1452,7 +1535,6 @@ function explodeBrick(index, ball, isExplosion) {
|
||||||
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,7 +1542,9 @@ 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
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,6 +253,7 @@ body.black_puck #menu {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
#level-recording-container a {
|
#level-recording-container a {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
@ -275,3 +276,60 @@ 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