mirror of
https://gitlab.com/lecarore/breakout71.git
synced 2025-04-21 20:46:14 -04:00
Stats display
This commit is contained in:
parent
23798c4e58
commit
a328520191
8 changed files with 288 additions and 122 deletions
22
Readme.md
22
Readme.md
|
@ -28,26 +28,22 @@ to level 1, with only one of their perks, leveled up. All the other perks they u
|
||||||
will be banned from the pool. The perk they decide to keep will gain one level, even if it was
|
will be banned from the pool. The perk they decide to keep will gain one level, even if it was
|
||||||
already maxed out.
|
already maxed out.
|
||||||
|
|
||||||
# Todo before next release
|
# next
|
||||||
- b71 white border around dark coins
|
|
||||||
|
|
||||||
- [jaceys] Counters for coins lost, misses, and boundary bounces, as well as a timer.
|
|
||||||
- wind : move coins based on puck movement not position
|
- wind : move coins based on puck movement not position
|
||||||
- show -N points in red when combo resets
|
|
||||||
- Top down /read: punishing now, maybe only reset if you hit the lowest populate row of the level, if it's not a full width row
|
|
||||||
- [jaceys] Move the restart button out of the menu, so that it is more easily accessible
|
- [jaceys] Move the restart button out of the menu, so that it is more easily accessible
|
||||||
- [jaceys] A visual indication of whether a ball has hit a brick this serve
|
- [jaceys] A visual indication of whether a ball has hit a brick this serve
|
||||||
|
- Top down /reach: punishing now, maybe only reset if you hit the lowest populate row of the level, if it's not a full width row
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Todo before next release
|
||||||
|
|
||||||
- scale concave_puck
|
|
||||||
- scale instant_upgrade
|
|
||||||
- scale etherealcoins (0 grav, maybe then start floting like helium ? maybe less viscosity)
|
|
||||||
- scale shocks (maybe spawn balls during the explosion ? maybe bigger explosions for this)
|
|
||||||
- scale ghost_coins : pass through bricks will less friction ?
|
|
||||||
- scale clairvoyant
|
|
||||||
|
|
||||||
# 29 march 2025
|
# 29 march 2025
|
||||||
|
|
||||||
|
- [jaceys] Counters for coins lost, misses, and boundary bounces, as well as a timer.
|
||||||
|
- added a white border around all coins, to make dark ones visible on dark bg
|
||||||
- Removed all previous loop only hazards
|
- Removed all previous loop only hazards
|
||||||
- Looping now bans all your perks except one. That one can level up beyond the normal max.
|
- Looping now bans all your perks except one. That one can level up beyond the normal max.
|
||||||
- Adjusted many perks to work beyond the max
|
- Adjusted many perks to work beyond the max
|
||||||
|
@ -66,6 +62,8 @@ already maxed out.
|
||||||
- soft reset : same math as shunt
|
- soft reset : same math as shunt
|
||||||
- smaller puck : now the puck can get as small as a ball
|
- smaller puck : now the puck can get as small as a ball
|
||||||
- Unbounded : at level 2+, the top of the level is gone too
|
- Unbounded : at level 2+, the top of the level is gone too
|
||||||
|
- concave_puck : ball bounces straighter and straighter, to the point where you can't move it without another perk
|
||||||
|
- shocks lvl 2+ make bigger explosions
|
||||||
- Make fullscreen an option and turn it back on when playing
|
- Make fullscreen an option and turn it back on when playing
|
||||||
- Made the "combo lost" text last 500ms instead of the pointless 150ms
|
- Made the "combo lost" text last 500ms instead of the pointless 150ms
|
||||||
|
|
||||||
|
|
167
dist/index.html
vendored
167
dist/index.html
vendored
|
@ -68,6 +68,18 @@ body {
|
||||||
transition: color 10ms;
|
transition: color 10ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#score span {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
#score span.great {
|
||||||
|
color: #90ee90;
|
||||||
|
}
|
||||||
|
|
||||||
|
#score span.good {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
#menu {
|
#menu {
|
||||||
left: 0;
|
left: 0;
|
||||||
}
|
}
|
||||||
|
@ -354,6 +366,7 @@ h2.histogram-title strong {
|
||||||
<button id="menu">☰ <span id="menuLabel">menu</span></button>
|
<button id="menu">☰ <span id="menuLabel">menu</span></button>
|
||||||
<button id="score"></button>
|
<button id="score"></button>
|
||||||
<div id="FPSDisplay"></div>
|
<div id="FPSDisplay"></div>
|
||||||
|
<div id="statsdisplay"></div>
|
||||||
<canvas id="game"></canvas>
|
<canvas id="game"></canvas>
|
||||||
<div id="popup">
|
<div id="popup">
|
||||||
<button id="close-modale"></button>
|
<button id="close-modale"></button>
|
||||||
|
@ -502,10 +515,10 @@ h2.histogram-title strong {
|
||||||
this[globalName] = mainExports;
|
this[globalName] = mainExports;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})({"gVqJ6":[function(require,module,exports,__globalThis) {
|
})({"kddl8":[function(require,module,exports,__globalThis) {
|
||||||
require("5e6e73082bdd189e")(require("e4e486661babe6bc").getBundleURL('bgzJG') + "editor.1350aee5.js");
|
require("a47ce90399998ad7")(require("8898ae71f5563c68").getBundleURL('1j15T') + "editor.1350aee5.js");
|
||||||
|
|
||||||
},{"5e6e73082bdd189e":"61B45","e4e486661babe6bc":"lgJ39"}],"61B45":[function(require,module,exports,__globalThis) {
|
},{"a47ce90399998ad7":"61B45","8898ae71f5563c68":"lgJ39"}],"61B45":[function(require,module,exports,__globalThis) {
|
||||||
"use strict";
|
"use strict";
|
||||||
var cacheLoader = require("ca2a84f7fa4a3bb0");
|
var cacheLoader = require("ca2a84f7fa4a3bb0");
|
||||||
module.exports = cacheLoader(function(bundle) {
|
module.exports = cacheLoader(function(bundle) {
|
||||||
|
@ -602,7 +615,7 @@ exports.getBundleURL = getBundleURLCached;
|
||||||
exports.getBaseURL = getBaseURL;
|
exports.getBaseURL = getBaseURL;
|
||||||
exports.getOrigin = getOrigin;
|
exports.getOrigin = getOrigin;
|
||||||
|
|
||||||
},{}],"67XFf":[function(require,module,exports,__globalThis) {
|
},{}],"j3Ih9":[function(require,module,exports,__globalThis) {
|
||||||
var _gameTs = require("./game.ts");
|
var _gameTs = require("./game.ts");
|
||||||
|
|
||||||
},{"./game.ts":"edeGs"}],"edeGs":[function(require,module,exports,__globalThis) {
|
},{"./game.ts":"edeGs"}],"edeGs":[function(require,module,exports,__globalThis) {
|
||||||
|
@ -611,7 +624,7 @@ parcelHelpers.defineInteropFlag(exports);
|
||||||
parcelHelpers.export(exports, "play", ()=>play);
|
parcelHelpers.export(exports, "play", ()=>play);
|
||||||
parcelHelpers.export(exports, "pause", ()=>pause);
|
parcelHelpers.export(exports, "pause", ()=>pause);
|
||||||
parcelHelpers.export(exports, "fitSize", ()=>fitSize);
|
parcelHelpers.export(exports, "fitSize", ()=>fitSize);
|
||||||
parcelHelpers.export(exports, "openShortRunUpgradesPicker", ()=>openShortRunUpgradesPicker);
|
parcelHelpers.export(exports, "openUpgradesPicker", ()=>openUpgradesPicker);
|
||||||
parcelHelpers.export(exports, "brickIndex", ()=>brickIndex);
|
parcelHelpers.export(exports, "brickIndex", ()=>brickIndex);
|
||||||
parcelHelpers.export(exports, "hasBrick", ()=>hasBrick);
|
parcelHelpers.export(exports, "hasBrick", ()=>hasBrick);
|
||||||
parcelHelpers.export(exports, "hitsSomething", ()=>hitsSomething);
|
parcelHelpers.export(exports, "hitsSomething", ()=>hitsSomething);
|
||||||
|
@ -715,7 +728,7 @@ setInterval(()=>{
|
||||||
const { width, height } = (0, _render.gameCanvas).getBoundingClientRect();
|
const { width, height } = (0, _render.gameCanvas).getBoundingClientRect();
|
||||||
if (width !== gameState.canvasWidth || height !== gameState.canvasHeight) fitSize();
|
if (width !== gameState.canvasWidth || height !== gameState.canvasHeight) fitSize();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
async function openShortRunUpgradesPicker(gameState) {
|
async function openUpgradesPicker(gameState) {
|
||||||
const catchRate = (gameState.score - gameState.levelStartScore) / (gameState.levelSpawnedCoins || 1);
|
const catchRate = (gameState.score - gameState.levelStartScore) / (gameState.levelSpawnedCoins || 1);
|
||||||
let repeats = 1;
|
let repeats = 1;
|
||||||
let timeGain = "", catchGain = "", wallHitsGain = "", missesGain = "";
|
let timeGain = "", catchGain = "", wallHitsGain = "", missesGain = "";
|
||||||
|
@ -1331,16 +1344,21 @@ function restart(params) {
|
||||||
restart(window.location.search.includes("stressTest") && {
|
restart(window.location.search.includes("stressTest") && {
|
||||||
level: "Bird",
|
level: "Bird",
|
||||||
perks: {
|
perks: {
|
||||||
pierce: 1,
|
shocks: 10,
|
||||||
sapper: 1,
|
multiball: 6,
|
||||||
implosions: 3,
|
telekinesis: 2,
|
||||||
streak_shots: 1
|
ghost_coins: 1,
|
||||||
|
pierce: 4,
|
||||||
|
clairvoyant: 3,
|
||||||
|
bigger_explosions: 2,
|
||||||
|
sapper: 2,
|
||||||
|
unbounded: 1
|
||||||
},
|
},
|
||||||
levelsPerLoop: 2
|
levelsPerLoop: 2
|
||||||
} || {});
|
} || {});
|
||||||
tick();
|
tick();
|
||||||
|
|
||||||
},{"./loadGameData":"l1B4x","./sounds":"dQKPV","./game_utils":"cEeac","./PWA/sw_loader":"2n0gK","./i18n/i18n":"eNPRm","./settings":"5blfu","./gameStateMutators":"9ZeQl","./render":"9AS2t","./recording":"godmD","./newGameState":"aQN6X","./asyncAlert":"rSqLY","./options":"d5NoS","./getLevelBackground":"7OIPf","@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3","./premium":"4GEPs"}],"l1B4x":[function(require,module,exports,__globalThis) {
|
},{"./loadGameData":"l1B4x","./sounds":"dQKPV","./game_utils":"cEeac","./PWA/sw_loader":"2n0gK","./i18n/i18n":"eNPRm","./settings":"5blfu","./gameStateMutators":"9ZeQl","./render":"9AS2t","./recording":"godmD","./newGameState":"aQN6X","./asyncAlert":"rSqLY","./options":"d5NoS","./getLevelBackground":"7OIPf","./premium":"4GEPs","@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3"}],"l1B4x":[function(require,module,exports,__globalThis) {
|
||||||
var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js");
|
var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js");
|
||||||
parcelHelpers.defineInteropFlag(exports);
|
parcelHelpers.defineInteropFlag(exports);
|
||||||
parcelHelpers.export(exports, "appVersion", ()=>appVersion);
|
parcelHelpers.export(exports, "appVersion", ()=>appVersion);
|
||||||
|
@ -2002,7 +2020,7 @@ const rawUpgrades = [
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
},{"./i18n/i18n":"eNPRm","@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3","./game_utils":"cEeac"}],"eNPRm":[function(require,module,exports,__globalThis) {
|
},{"./i18n/i18n":"eNPRm","./game_utils":"cEeac","@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3"}],"eNPRm":[function(require,module,exports,__globalThis) {
|
||||||
var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js");
|
var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js");
|
||||||
parcelHelpers.defineInteropFlag(exports);
|
parcelHelpers.defineInteropFlag(exports);
|
||||||
parcelHelpers.export(exports, "getCurrentLang", ()=>getCurrentLang);
|
parcelHelpers.export(exports, "getCurrentLang", ()=>getCurrentLang);
|
||||||
|
@ -2579,10 +2597,10 @@ if ("serviceWorker" in navigator && window.location.href.endsWith("/index.html?i
|
||||||
navigator.serviceWorker.register(url);
|
navigator.serviceWorker.register(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
},{"b04459cc43e56e8c":"jblPb","@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3"}],"jblPb":[function(require,module,exports,__globalThis) {
|
},{"b04459cc43e56e8c":"pb85M","@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3"}],"pb85M":[function(require,module,exports,__globalThis) {
|
||||||
module.exports = require("a15a43021d40e52d").getBundleURL('bgzJG') + "sw-b71.41cdff1b.js";
|
module.exports = require("5dcff9a30a9dc436").getBundleURL('1j15T') + "sw-b71.41cdff1b.js";
|
||||||
|
|
||||||
},{"a15a43021d40e52d":"lgJ39"}],"9ZeQl":[function(require,module,exports,__globalThis) {
|
},{"5dcff9a30a9dc436":"lgJ39"}],"9ZeQl":[function(require,module,exports,__globalThis) {
|
||||||
var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js");
|
var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js");
|
||||||
parcelHelpers.defineInteropFlag(exports);
|
parcelHelpers.defineInteropFlag(exports);
|
||||||
parcelHelpers.export(exports, "setMousePos", ()=>setMousePos);
|
parcelHelpers.export(exports, "setMousePos", ()=>setMousePos);
|
||||||
|
@ -2729,8 +2747,8 @@ function spawnImplosion(gameState, count, x, y, color) {
|
||||||
makeParticle(gameState, x - dx * 10, y - dy * 10, dx, dy, color, false);
|
makeParticle(gameState, x - dx * 10, y - dy * 10, dx, dy, color, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function explosionAt(gameState, index, x, y, ball) {
|
function explosionAt(gameState, index, x, y, ball, extraSize = 0) {
|
||||||
const size = 1 + gameState.perks.bigger_explosions + Math.max(1, gameState.perks.implosions) - 1;
|
const size = 1 + gameState.perks.bigger_explosions + Math.max(0, gameState.perks.implosions - 1) + extraSize;
|
||||||
schedulGameSound(gameState, "explode", ball.x, 1);
|
schedulGameSound(gameState, "explode", ball.x, 1);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
const col = index % gameState.gridSize;
|
const col = index % gameState.gridSize;
|
||||||
|
@ -2771,7 +2789,7 @@ function explodeBrick(gameState, index, ball, isExplosion) {
|
||||||
// resetCombo(gameState, x, y);
|
// resetCombo(gameState, x, y);
|
||||||
// }
|
// }
|
||||||
setBrick(gameState, index, "");
|
setBrick(gameState, index, "");
|
||||||
explosionAt(gameState, index, x, y, ball);
|
explosionAt(gameState, index, x, y, ball, 0);
|
||||||
} 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
|
||||||
// Flashing is take care of by the tick loop
|
// Flashing is take care of by the tick loop
|
||||||
|
@ -2924,7 +2942,7 @@ async function setLevel(gameState, l) {
|
||||||
gameState.upgradesOfferedFor = l;
|
gameState.upgradesOfferedFor = l;
|
||||||
(0, _game.pause)(false);
|
(0, _game.pause)(false);
|
||||||
(0, _recording.stopRecording)();
|
(0, _recording.stopRecording)();
|
||||||
if (l > 0) await (0, _game.openShortRunUpgradesPicker)(gameState);
|
if (l > 0) await (0, _game.openUpgradesPicker)(gameState);
|
||||||
gameState.currentLevel = l;
|
gameState.currentLevel = l;
|
||||||
gameState.level = gameState.runLevels[l];
|
gameState.level = gameState.runLevels[l];
|
||||||
gameState.levelTime = 0;
|
gameState.levelTime = 0;
|
||||||
|
@ -2934,6 +2952,7 @@ async function setLevel(gameState, l) {
|
||||||
gameState.lastTickDown = gameState.levelTime;
|
gameState.lastTickDown = gameState.levelTime;
|
||||||
gameState.levelStartScore = gameState.score;
|
gameState.levelStartScore = gameState.score;
|
||||||
gameState.levelSpawnedCoins = 0;
|
gameState.levelSpawnedCoins = 0;
|
||||||
|
gameState.levelLostCoins = 0;
|
||||||
gameState.levelMisses = 0;
|
gameState.levelMisses = 0;
|
||||||
gameState.runStatistics.levelsPlayed++;
|
gameState.runStatistics.levelsPlayed++;
|
||||||
// Reset combo silently
|
// Reset combo silently
|
||||||
|
@ -2946,7 +2965,7 @@ async function setLevel(gameState, l) {
|
||||||
gameState.gridSize = lvl.size;
|
gameState.gridSize = lvl.size;
|
||||||
(0, _game.fitSize)();
|
(0, _game.fitSize)();
|
||||||
}
|
}
|
||||||
empty(gameState.coins);
|
gameState.levelLostCoins += empty(gameState.coins);
|
||||||
empty(gameState.particles);
|
empty(gameState.particles);
|
||||||
empty(gameState.lights);
|
empty(gameState.lights);
|
||||||
empty(gameState.texts);
|
empty(gameState.texts);
|
||||||
|
@ -3012,7 +3031,13 @@ function coinBrickHitCheck(gameState, coin) {
|
||||||
const vhit = (0, _game.hitsSomething)(previousX, y, radius);
|
const vhit = (0, _game.hitsSomething)(previousX, y, radius);
|
||||||
const hhit = (0, _game.hitsSomething)(x, previousY, radius);
|
const hhit = (0, _game.hitsSomething)(x, previousY, radius);
|
||||||
const chit = typeof vhit == "undefined" && typeof hhit == "undefined" && (0, _game.hitsSomething)(x, y, radius) || undefined;
|
const chit = typeof vhit == "undefined" && typeof hhit == "undefined" && (0, _game.hitsSomething)(x, y, radius) || undefined;
|
||||||
if (!gameState.perks.ghost_coins) {
|
if (gameState.perks.ghost_coins) // slow down
|
||||||
|
{
|
||||||
|
if (typeof (vhit ?? hhit ?? chit) !== "undefined") {
|
||||||
|
coin.vy *= 1 - 0.2 / gameState.perks.ghost_coins;
|
||||||
|
coin.vx *= 1 - 0.2 / gameState.perks.ghost_coins;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
if (typeof vhit !== "undefined" || typeof chit !== "undefined") {
|
if (typeof vhit !== "undefined" || typeof chit !== "undefined") {
|
||||||
coin.y = coin.previousY;
|
coin.y = coin.previousY;
|
||||||
coin.vy *= -1;
|
coin.vy *= -1;
|
||||||
|
@ -3102,7 +3127,7 @@ frames = 1) {
|
||||||
coin.vx += (ball.x - coin.x) / d2 * 50 * gameState.perks.ball_attracts_coins;
|
coin.vx += (ball.x - coin.x) / d2 * 50 * gameState.perks.ball_attracts_coins;
|
||||||
coin.vy += (ball.y - coin.y) / d2 * 50 * gameState.perks.ball_attracts_coins;
|
coin.vy += (ball.y - coin.y) / d2 * 50 * gameState.perks.ball_attracts_coins;
|
||||||
});
|
});
|
||||||
const ratio = 1 - (gameState.perks.viscosity * 0.03 + 0.005) * frames;
|
const ratio = 1 - (gameState.perks.viscosity * 0.03 + 0.005) * frames / (1 + gameState.perks.etherealcoins);
|
||||||
coin.vy *= ratio;
|
coin.vy *= ratio;
|
||||||
coin.vx *= ratio;
|
coin.vx *= ratio;
|
||||||
if (coin.vx > 7 * gameState.baseSpeed) coin.vx = 7 * gameState.baseSpeed;
|
if (coin.vx > 7 * gameState.baseSpeed) coin.vx = 7 * gameState.baseSpeed;
|
||||||
|
@ -3123,10 +3148,14 @@ frames = 1) {
|
||||||
addToScore(gameState, coin);
|
addToScore(gameState, coin);
|
||||||
destroy(gameState.coins, coinIndex);
|
destroy(gameState.coins, coinIndex);
|
||||||
} else if (coin.y > gameState.canvasHeight + coinRadius) {
|
} else if (coin.y > gameState.canvasHeight + coinRadius) {
|
||||||
|
gameState.levelLostCoins += coin.points;
|
||||||
destroy(gameState.coins, coinIndex);
|
destroy(gameState.coins, coinIndex);
|
||||||
if (gameState.perks.compound_interest) resetCombo(gameState, coin.x, coin.y);
|
if (gameState.perks.compound_interest) resetCombo(gameState, coin.x, coin.y);
|
||||||
} else if (gameState.perks.unbounded && (coin.x < -gameState.gameZoneWidth / 2 || coin.x > gameState.canvasWidth + gameState.gameZoneWidth / 2 || coin.y < -gameState.gameZoneWidth)) // Out of bound on sides
|
} else if (gameState.perks.unbounded && (coin.x < -gameState.gameZoneWidth / 2 || coin.x > gameState.canvasWidth + gameState.gameZoneWidth / 2 || coin.y < -gameState.gameZoneWidth)) {
|
||||||
destroy(gameState.coins, coinIndex);
|
// Out of bound on sides
|
||||||
|
gameState.levelLostCoins += coin.points;
|
||||||
|
destroy(gameState.coins, coinIndex);
|
||||||
|
}
|
||||||
const hitBrick = coinBrickHitCheck(gameState, coin);
|
const hitBrick = coinBrickHitCheck(gameState, coin);
|
||||||
if (gameState.perks.metamorphosis && typeof hitBrick !== "undefined") {
|
if (gameState.perks.metamorphosis && typeof hitBrick !== "undefined") {
|
||||||
if (gameState.bricks[hitBrick] && coin.color !== gameState.bricks[hitBrick] && gameState.bricks[hitBrick] !== "black" && coin.metamorphosisPoints) {
|
if (gameState.bricks[hitBrick] && coin.color !== gameState.bricks[hitBrick] && gameState.bricks[hitBrick] !== "black" && coin.metamorphosisPoints) {
|
||||||
|
@ -3162,7 +3191,7 @@ frames = 1) {
|
||||||
b.vx += (0, _gameUtils.clamp)(b.x - x, -limit, limit) + (Math.random() - 0.5) * limit / 3;
|
b.vx += (0, _gameUtils.clamp)(b.x - x, -limit, limit) + (Math.random() - 0.5) * limit / 3;
|
||||||
b.vy += (0, _gameUtils.clamp)(b.y - y, -limit, limit) + (Math.random() - 0.5) * limit / 3;
|
b.vy += (0, _gameUtils.clamp)(b.y - y, -limit, limit) + (Math.random() - 0.5) * limit / 3;
|
||||||
let index = (0, _game.brickIndex)(x, y);
|
let index = (0, _game.brickIndex)(x, y);
|
||||||
explosionAt(gameState, index, x, y, a);
|
explosionAt(gameState, index, x, y, a, Math.max(0, gameState.perks.shocks - 1));
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
if (gameState.perks.wind) {
|
if (gameState.perks.wind) {
|
||||||
|
@ -3271,7 +3300,7 @@ function ballTick(gameState, ball, delta) {
|
||||||
if (ball.y > ylimit && ball.vy > 0 && (ballIsUnderPuck || gameState.perks.extra_life && ball.y > ylimit + gameState.puckHeight / 2)) {
|
if (ball.y > ylimit && ball.vy > 0 && (ballIsUnderPuck || gameState.perks.extra_life && ball.y > ylimit + gameState.puckHeight / 2)) {
|
||||||
if (ballIsUnderPuck) {
|
if (ballIsUnderPuck) {
|
||||||
const speed = Math.sqrt(ball.vx * ball.vx + ball.vy * ball.vy);
|
const speed = Math.sqrt(ball.vx * ball.vx + ball.vy * ball.vy);
|
||||||
const angle = Math.atan2(-gameState.puckWidth / 2, (ball.x - gameState.puckPosition) * (gameState.perks.concave_puck ? -0.5 : 1));
|
const angle = Math.atan2(-gameState.puckWidth / 2, (ball.x - gameState.puckPosition) * (gameState.perks.concave_puck ? -1 / (1 + gameState.perks.concave_puck) : 1));
|
||||||
ball.vx = speed * Math.cos(angle);
|
ball.vx = speed * Math.cos(angle);
|
||||||
ball.vy = speed * Math.sin(angle);
|
ball.vy = speed * Math.sin(angle);
|
||||||
schedulGameSound(gameState, "wallBeep", ball.x, 1);
|
schedulGameSound(gameState, "wallBeep", ball.x, 1);
|
||||||
|
@ -3373,6 +3402,7 @@ function justLostALife(gameState, ball, x, y) {
|
||||||
}
|
}
|
||||||
function makeCoin(gameState, x, y, vx, vy, color = "gold", points = 1) {
|
function makeCoin(gameState, x, y, vx, vy, color = "gold", points = 1) {
|
||||||
let weight = 0.8 + Math.random() * 0.2 + Math.min(2, points * 0.01);
|
let weight = 0.8 + Math.random() * 0.2 + Math.min(2, points * 0.01);
|
||||||
|
weight *= 5 / (5 + gameState.perks.etherealcoins);
|
||||||
append(gameState.coins, (p)=>{
|
append(gameState.coins, (p)=>{
|
||||||
p.x = x;
|
p.x = x;
|
||||||
p.y = y;
|
p.y = y;
|
||||||
|
@ -3451,9 +3481,16 @@ function liveCount(where) {
|
||||||
return where.total;
|
return where.total;
|
||||||
}
|
}
|
||||||
function empty(where) {
|
function empty(where) {
|
||||||
|
let destroyed = 0;
|
||||||
where.total = 0;
|
where.total = 0;
|
||||||
where.indexMin = 0;
|
where.indexMin = 0;
|
||||||
where.list.forEach((i)=>i.destroyed = true);
|
where.list.forEach((i)=>{
|
||||||
|
if (!i.destroyed) {
|
||||||
|
i.destroyed = true;
|
||||||
|
destroyed++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return destroyed;
|
||||||
}
|
}
|
||||||
function forEachLiveOne(where, cb) {
|
function forEachLiveOne(where, cb) {
|
||||||
where.list.forEach((item, index)=>{
|
where.list.forEach((item, index)=>{
|
||||||
|
@ -3461,7 +3498,7 @@ function forEachLiveOne(where, cb) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
},{"./game_utils":"cEeac","./i18n/i18n":"eNPRm","./loadGameData":"l1B4x","./settings":"5blfu","./render":"9AS2t","./gameOver":"caCAf","./game":"edeGs","./recording":"godmD","./options":"d5NoS","@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3","./premium":"4GEPs","./newGameState":"aQN6X","./asyncAlert":"rSqLY"}],"9AS2t":[function(require,module,exports,__globalThis) {
|
},{"./game_utils":"cEeac","./i18n/i18n":"eNPRm","./loadGameData":"l1B4x","./settings":"5blfu","./render":"9AS2t","./gameOver":"caCAf","./game":"edeGs","./recording":"godmD","./options":"d5NoS","./premium":"4GEPs","./newGameState":"aQN6X","./asyncAlert":"rSqLY","@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3"}],"9AS2t":[function(require,module,exports,__globalThis) {
|
||||||
var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js");
|
var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js");
|
||||||
parcelHelpers.defineInteropFlag(exports);
|
parcelHelpers.defineInteropFlag(exports);
|
||||||
parcelHelpers.export(exports, "gameCanvas", ()=>gameCanvas);
|
parcelHelpers.export(exports, "gameCanvas", ()=>gameCanvas);
|
||||||
|
@ -3494,6 +3531,7 @@ const bombSVG = document.createElement("img");
|
||||||
bombSVG.src = "data:image/svg+xml;base64," + btoa(`<svg width="144" height="144" viewBox="0 0 38.101 38.099" xmlns="http://www.w3.org/2000/svg">
|
bombSVG.src = "data:image/svg+xml;base64," + btoa(`<svg width="144" height="144" viewBox="0 0 38.101 38.099" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="m6.1528 26.516c-2.6992-3.4942-2.9332-8.281-.58305-11.981a10.454 10.454 0 017.3701-4.7582c1.962-.27726 4.1646.05953 5.8835.90027l.45013.22017.89782-.87417c.83748-.81464.91169-.87499 1.0992-.90271.40528-.058713.58876.03425 1.1971.6116l.55451.52679 1.0821-1.0821c1.1963-1.1963 1.383-1.3357 2.1039-1.5877.57898-.20223 1.5681-.19816 2.1691.00897 1.4613.50314 2.3673 1.7622 2.3567 3.2773-.0058.95654-.24464 1.5795-.90924 2.3746-.40936.48928-.55533.81057-.57898 1.2737-.02039.41018.1109.77714.42322 1.1792.30172.38816.3694.61323.2797.93044-.12803.45666-.56674.71598-1.0242.60507-.601-.14597-1.3031-1.3088-1.3969-2.3126-.09459-1.0161.19245-1.8682.92392-2.7432.42567-.50885.5643-.82851.5643-1.3031 0-.50151-.14026-.83177-.51211-1.2028-.50966-.50966-1.0968-.64829-1.781-.41996l-.37348.12477-2.1006 2.1006.52597.55696c.45421.48194.5325.58876.57898.78855.09622.41588.07502.45014-.88396 1.4548l-.87173.9125.26339.57979a10.193 10.193 0 01.9231 4.1001c.03996 2.046-.41996 3.8082-1.4442 5.537-.55044.928-1.0185 1.5013-1.8968 2.3241-.83503.78284-1.5526 1.2827-2.4904 1.7361-3.4266 1.657-7.4721 1.3422-10.549-.82035-.73473-.51782-1.7312-1.4621-2.2515-2.1357zm21.869-4.5584c-.0579-.19734-.05871-2.2662 0-2.4545.11906-.39142.57898-.63361 1.0038-.53005.23812.05708.54147.32455.6116.5382.06279.19163.06769 2.1805.0065 2.3811-.12558.40773-.61649.67602-1.0462.57164-.234-.05708-.51615-.30498-.57568-.50722m3.0417-2.6013c-.12313-.6222.37837-1.1049 1.0479-1.0079.18348.0261.25279.08399 1.0071.83911.75838.75838.81301.82362.84074 1.0112.10193.68499-.40365 1.1938-1.034 1.0405-.1949-.0473-.28786-.12558-1.0144-.85216-.7649-.76409-.80241-.81057-.84645-1.0316m.61323-3.0629a.85623.85623 0 01.59284-.99975c.28949-.09214 2.1814-.08318 2.3917.01141.38734.17369.6279.61078.53984.98181-.06035.25606-.35391.57327-.60181.64992-.25279.07747-2.2278.053-2.4097-.03017-.26013-.11906-.46318-.36125-.51374-.61323" fill="#fff" opacity="0.3"/>
|
<path d="m6.1528 26.516c-2.6992-3.4942-2.9332-8.281-.58305-11.981a10.454 10.454 0 017.3701-4.7582c1.962-.27726 4.1646.05953 5.8835.90027l.45013.22017.89782-.87417c.83748-.81464.91169-.87499 1.0992-.90271.40528-.058713.58876.03425 1.1971.6116l.55451.52679 1.0821-1.0821c1.1963-1.1963 1.383-1.3357 2.1039-1.5877.57898-.20223 1.5681-.19816 2.1691.00897 1.4613.50314 2.3673 1.7622 2.3567 3.2773-.0058.95654-.24464 1.5795-.90924 2.3746-.40936.48928-.55533.81057-.57898 1.2737-.02039.41018.1109.77714.42322 1.1792.30172.38816.3694.61323.2797.93044-.12803.45666-.56674.71598-1.0242.60507-.601-.14597-1.3031-1.3088-1.3969-2.3126-.09459-1.0161.19245-1.8682.92392-2.7432.42567-.50885.5643-.82851.5643-1.3031 0-.50151-.14026-.83177-.51211-1.2028-.50966-.50966-1.0968-.64829-1.781-.41996l-.37348.12477-2.1006 2.1006.52597.55696c.45421.48194.5325.58876.57898.78855.09622.41588.07502.45014-.88396 1.4548l-.87173.9125.26339.57979a10.193 10.193 0 01.9231 4.1001c.03996 2.046-.41996 3.8082-1.4442 5.537-.55044.928-1.0185 1.5013-1.8968 2.3241-.83503.78284-1.5526 1.2827-2.4904 1.7361-3.4266 1.657-7.4721 1.3422-10.549-.82035-.73473-.51782-1.7312-1.4621-2.2515-2.1357zm21.869-4.5584c-.0579-.19734-.05871-2.2662 0-2.4545.11906-.39142.57898-.63361 1.0038-.53005.23812.05708.54147.32455.6116.5382.06279.19163.06769 2.1805.0065 2.3811-.12558.40773-.61649.67602-1.0462.57164-.234-.05708-.51615-.30498-.57568-.50722m3.0417-2.6013c-.12313-.6222.37837-1.1049 1.0479-1.0079.18348.0261.25279.08399 1.0071.83911.75838.75838.81301.82362.84074 1.0112.10193.68499-.40365 1.1938-1.034 1.0405-.1949-.0473-.28786-.12558-1.0144-.85216-.7649-.76409-.80241-.81057-.84645-1.0316m.61323-3.0629a.85623.85623 0 01.59284-.99975c.28949-.09214 2.1814-.08318 2.3917.01141.38734.17369.6279.61078.53984.98181-.06035.25606-.35391.57327-.60181.64992-.25279.07747-2.2278.053-2.4097-.03017-.26013-.11906-.46318-.36125-.51374-.61323" fill="#fff" opacity="0.3"/>
|
||||||
</svg>`);
|
</svg>`);
|
||||||
|
bombSVG.onload = ()=>(0, _game.gameState).needsRender = true;
|
||||||
const background = document.createElement("img");
|
const background = document.createElement("img");
|
||||||
const backgroundCanvas = document.createElement("canvas");
|
const backgroundCanvas = document.createElement("canvas");
|
||||||
function render(gameState) {
|
function render(gameState) {
|
||||||
|
@ -3510,7 +3548,21 @@ function render(gameState) {
|
||||||
max: (0, _gameUtils.max_levels)(gameState)
|
max: (0, _gameUtils.max_levels)(gameState)
|
||||||
});
|
});
|
||||||
else menuLabel.innerText = (0, _i18N.t)("play.menu_label");
|
else menuLabel.innerText = (0, _i18N.t)("play.menu_label");
|
||||||
scoreDisplay.innerText = `$${gameState.score}`;
|
const catchRate = gameState.levelSpawnedCoins ? (gameState.levelSpawnedCoins - gameState.levelLostCoins) / gameState.levelSpawnedCoins : 1;
|
||||||
|
scoreDisplay.innerHTML = ((0, _options.isOptionOn)('show_stats') ? `
|
||||||
|
<span class="${catchRate == 1 && 'great' || catchRate > 0.9 && 'good' || ''}">
|
||||||
|
${Math.floor(catchRate * 100)}%
|
||||||
|
</span><span> / </span>
|
||||||
|
<span class="${gameState.levelWallBounces == 0 && 'great' || gameState.levelWallBounces < 5 && 'good' || ''}">
|
||||||
|
${gameState.levelWallBounces} B
|
||||||
|
</span><span> / </span>
|
||||||
|
<span class="${gameState.levelTime < 30000 && 'great' || gameState.levelTime < 60000 && 'good' || ''}">
|
||||||
|
${Math.ceil(gameState.levelTime / 1000)}s
|
||||||
|
</span><span> / </span>
|
||||||
|
<span class="${gameState.levelMisses == 0 && 'great' || gameState.levelMisses <= 3 && 'good' || ''}">
|
||||||
|
${gameState.levelMisses} M
|
||||||
|
</span><span> / </span>
|
||||||
|
` : '') + `$${gameState.score}`;
|
||||||
scoreDisplay.className = gameState.lastScoreIncrease > gameState.levelTime - 500 ? "active" : "";
|
scoreDisplay.className = gameState.lastScoreIncrease > gameState.levelTime - 500 ? "active" : "";
|
||||||
// Clear
|
// Clear
|
||||||
if (!(0, _options.isOptionOn)("basic") && !level.color && level.svg) {
|
if (!(0, _options.isOptionOn)("basic") && !level.color && level.svg) {
|
||||||
|
@ -3556,10 +3608,23 @@ function render(gameState) {
|
||||||
const bgctx = backgroundCanvas.getContext("2d");
|
const bgctx = backgroundCanvas.getContext("2d");
|
||||||
bgctx.fillStyle = level.color || "#000";
|
bgctx.fillStyle = level.color || "#000";
|
||||||
bgctx.fillRect(0, 0, gameState.canvasWidth, gameState.canvasHeight);
|
bgctx.fillRect(0, 0, gameState.canvasWidth, gameState.canvasHeight);
|
||||||
const pattern = ctx.createPattern(background, "repeat");
|
if (gameState.perks.clairvoyant >= 3) {
|
||||||
if (pattern) {
|
const pageSource = document.body.innerHTML.replace(/\s+/gi, '');
|
||||||
bgctx.fillStyle = pattern;
|
const lineWidth = Math.ceil(gameState.canvasWidth / 15);
|
||||||
bgctx.fillRect(0, 0, width, height);
|
const lines = Math.ceil(gameState.canvasHeight / 20);
|
||||||
|
const chars = lineWidth * lines;
|
||||||
|
let start = Math.ceil(Math.random() * (pageSource.length - chars));
|
||||||
|
for(let i = 0; i < lines; i++){
|
||||||
|
bgctx.fillStyle = 'white';
|
||||||
|
bgctx.font = '20px Courier';
|
||||||
|
bgctx.fillText(pageSource.slice(start + i * lineWidth, start + (i + 1) * lineWidth), 0, i * 20, gameState.canvasWidth);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const pattern = ctx.createPattern(background, "repeat");
|
||||||
|
if (pattern) {
|
||||||
|
bgctx.fillStyle = pattern;
|
||||||
|
bgctx.fillRect(0, 0, width, height);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx.drawImage(backgroundCanvas, 0, 0);
|
ctx.drawImage(backgroundCanvas, 0, 0);
|
||||||
|
@ -3596,7 +3661,7 @@ function render(gameState) {
|
||||||
ctx.globalCompositeOperation = "source-over";
|
ctx.globalCompositeOperation = "source-over";
|
||||||
// ctx.globalCompositeOperation =
|
// ctx.globalCompositeOperation =
|
||||||
// coin.color === "gold" || level.color ? "source-over" : "screen";
|
// coin.color === "gold" || level.color ? "source-over" : "screen";
|
||||||
drawCoin(ctx, coin.color, coin.size, coin.x, coin.y, hasCombo && gameState.perks.asceticism && "red" || !coin.points && "red" || level.color || "black", coin.a);
|
drawCoin(ctx, coin.color, coin.size, coin.x, coin.y, hasCombo && gameState.perks.asceticism && "red" || coin.color === 'gold' && 'gold' || gameState.puckColor, coin.a);
|
||||||
});
|
});
|
||||||
// Black shadow around balls
|
// Black shadow around balls
|
||||||
if (!(0, _options.isOptionOn)("basic")) {
|
if (!(0, _options.isOptionOn)("basic")) {
|
||||||
|
@ -3613,7 +3678,7 @@ function render(gameState) {
|
||||||
const { x, y, time, color, size, duration } = flash;
|
const { x, y, time, color, size, duration } = flash;
|
||||||
const elapsed = gameState.levelTime - time;
|
const elapsed = gameState.levelTime - time;
|
||||||
ctx.globalAlpha = Math.min(1, 2 - elapsed / duration * 2) * 0.5;
|
ctx.globalAlpha = Math.min(1, 2 - elapsed / duration * 2) * 0.5;
|
||||||
drawBrick(ctx, color, x, y, -1);
|
drawBrick(ctx, color, x, y, -1, gameState.perks.clairvoyant >= 2);
|
||||||
});
|
});
|
||||||
ctx.globalCompositeOperation = "screen";
|
ctx.globalCompositeOperation = "screen";
|
||||||
(0, _gameStateMutators.forEachLiveOne)(gameState.texts, (flash)=>{
|
(0, _gameStateMutators.forEachLiveOne)(gameState.texts, (flash)=>{
|
||||||
|
@ -3663,7 +3728,7 @@ function render(gameState) {
|
||||||
// The puck
|
// The puck
|
||||||
ctx.globalAlpha = 1;
|
ctx.globalAlpha = 1;
|
||||||
ctx.globalCompositeOperation = "source-over";
|
ctx.globalCompositeOperation = "source-over";
|
||||||
drawPuck(ctx, gameState.puckColor, gameState.puckWidth, gameState.puckHeight, 0, !!gameState.perks.concave_puck, gameState.perks.streak_shots && hasCombo ? getDashOffset(gameState) : -1);
|
drawPuck(ctx, gameState.puckColor, gameState.puckWidth, gameState.puckHeight, 0, gameState.perks.concave_puck, gameState.perks.streak_shots && hasCombo ? getDashOffset(gameState) : -1);
|
||||||
if (gameState.combo > 1) {
|
if (gameState.combo > 1) {
|
||||||
ctx.globalCompositeOperation = "source-over";
|
ctx.globalCompositeOperation = "source-over";
|
||||||
const comboText = "x " + gameState.combo;
|
const comboText = "x " + gameState.combo;
|
||||||
|
@ -3743,7 +3808,7 @@ function renderAllBricks() {
|
||||||
let redBecauseOfReach = (0, _game.gameState).perks.reach && (0, _gameUtils.countBricksAbove)((0, _game.gameState), index) && !(0, _gameUtils.countBricksBelow)((0, _game.gameState), index);
|
let redBecauseOfReach = (0, _game.gameState).perks.reach && (0, _gameUtils.countBricksAbove)((0, _game.gameState), index) && !(0, _gameUtils.countBricksBelow)((0, _game.gameState), index);
|
||||||
let redBorder = (0, _game.gameState).ballsColor !== color && color !== "black" && redBorderOnBricksWithWrongColor || hasCombo && (0, _game.gameState).perks.zen && color === "black" || redBecauseOfReach || redColorOnAllBricks;
|
let redBorder = (0, _game.gameState).ballsColor !== color && color !== "black" && redBorderOnBricksWithWrongColor || hasCombo && (0, _game.gameState).perks.zen && color === "black" || redBecauseOfReach || redColorOnAllBricks;
|
||||||
canctx.globalCompositeOperation = "source-over";
|
canctx.globalCompositeOperation = "source-over";
|
||||||
drawBrick(canctx, color, x, y, redBorder ? offset : -1);
|
drawBrick(canctx, color, x, y, redBorder ? offset : -1, (0, _game.gameState).perks.clairvoyant >= 2);
|
||||||
if ((0, _game.gameState).brickHP[index] > 1 && (0, _game.gameState).perks.clairvoyant) {
|
if ((0, _game.gameState).brickHP[index] > 1 && (0, _game.gameState).perks.clairvoyant) {
|
||||||
canctx.globalCompositeOperation = "destination-out";
|
canctx.globalCompositeOperation = "destination-out";
|
||||||
drawText(canctx, (0, _game.gameState).brickHP[index].toString(), "white", (0, _game.gameState).puckHeight, x, y);
|
drawText(canctx, (0, _game.gameState).brickHP[index].toString(), "white", (0, _game.gameState).puckHeight, x, y);
|
||||||
|
@ -3757,8 +3822,8 @@ function renderAllBricks() {
|
||||||
ctx.drawImage(cachedBricksRender, (0, _game.gameState).offsetX, 0);
|
ctx.drawImage(cachedBricksRender, (0, _game.gameState).offsetX, 0);
|
||||||
}
|
}
|
||||||
let cachedGraphics = {};
|
let cachedGraphics = {};
|
||||||
function drawPuck(ctx, color, puckWidth, puckHeight, yOffset = 0, flipped, redBorderOffset) {
|
function drawPuck(ctx, color, puckWidth, puckHeight, yOffset = 0, concave_puck, redBorderOffset) {
|
||||||
const key = "puck" + color + "_" + puckWidth + "_" + puckHeight + "_" + flipped + "_" + redBorderOffset;
|
const key = "puck" + color + "_" + puckWidth + "_" + puckHeight + "_" + concave_puck + "_" + redBorderOffset;
|
||||||
if (!cachedGraphics[key]) {
|
if (!cachedGraphics[key]) {
|
||||||
const can = document.createElement("canvas");
|
const can = document.createElement("canvas");
|
||||||
can.width = puckWidth;
|
can.width = puckWidth;
|
||||||
|
@ -3767,9 +3832,9 @@ function drawPuck(ctx, color, puckWidth, puckHeight, yOffset = 0, flipped, redBo
|
||||||
canctx.fillStyle = color;
|
canctx.fillStyle = color;
|
||||||
canctx.beginPath();
|
canctx.beginPath();
|
||||||
canctx.moveTo(0, puckHeight * 2);
|
canctx.moveTo(0, puckHeight * 2);
|
||||||
if (flipped) {
|
if (concave_puck) {
|
||||||
canctx.lineTo(0, puckHeight * 0.75);
|
canctx.lineTo(0, puckHeight * 0.75);
|
||||||
canctx.bezierCurveTo(puckWidth / 2, puckHeight, puckWidth / 2, puckHeight * 1, puckWidth, puckHeight * 0.75);
|
canctx.bezierCurveTo(puckWidth / 2, puckHeight * (2 + concave_puck) / 3, puckWidth / 2, puckHeight * (2 + concave_puck) / 3, puckWidth, puckHeight * 0.75);
|
||||||
canctx.lineTo(puckWidth, puckHeight * 2);
|
canctx.lineTo(puckWidth, puckHeight * 2);
|
||||||
} else {
|
} else {
|
||||||
canctx.lineTo(0, puckHeight * 1.25);
|
canctx.lineTo(0, puckHeight * 1.25);
|
||||||
|
@ -3823,14 +3888,12 @@ function drawCoin(ctx, color, size, x, y, borderColor, rawAngle) {
|
||||||
canctx.arc(size / 2, size / 2, size / 2, 0, 2 * Math.PI);
|
canctx.arc(size / 2, size / 2, size / 2, 0, 2 * Math.PI);
|
||||||
canctx.fillStyle = color;
|
canctx.fillStyle = color;
|
||||||
canctx.fill();
|
canctx.fill();
|
||||||
if (color === "gold" || borderColor === "red") {
|
canctx.strokeStyle = borderColor;
|
||||||
canctx.strokeStyle = borderColor;
|
if (borderColor == "red") {
|
||||||
if (borderColor == "red") {
|
canctx.lineWidth = 2;
|
||||||
canctx.lineWidth = 2;
|
canctx.setLineDash(redBorderDash);
|
||||||
canctx.setLineDash(redBorderDash);
|
|
||||||
}
|
|
||||||
canctx.stroke();
|
|
||||||
}
|
}
|
||||||
|
canctx.stroke();
|
||||||
if (color === "gold") {
|
if (color === "gold") {
|
||||||
// Fill in
|
// Fill in
|
||||||
canctx.beginPath();
|
canctx.beginPath();
|
||||||
|
@ -3866,13 +3929,13 @@ function drawFuzzyBall(ctx, color, width, x, y) {
|
||||||
}
|
}
|
||||||
ctx.drawImage(cachedGraphics[key], Math.round(x - size / 2), Math.round(y - size / 2));
|
ctx.drawImage(cachedGraphics[key], Math.round(x - size / 2), Math.round(y - size / 2));
|
||||||
}
|
}
|
||||||
function drawBrick(ctx, color, x, y, offset = 0) {
|
function drawBrick(ctx, color, x, y, offset = 0, borderOnly) {
|
||||||
const tlx = Math.ceil(x - (0, _game.gameState).brickWidth / 2);
|
const tlx = Math.ceil(x - (0, _game.gameState).brickWidth / 2);
|
||||||
const tly = Math.ceil(y - (0, _game.gameState).brickWidth / 2);
|
const tly = Math.ceil(y - (0, _game.gameState).brickWidth / 2);
|
||||||
const brx = Math.ceil(x + (0, _game.gameState).brickWidth / 2) - 1;
|
const brx = Math.ceil(x + (0, _game.gameState).brickWidth / 2) - 1;
|
||||||
const bry = Math.ceil(y + (0, _game.gameState).brickWidth / 2) - 1;
|
const bry = Math.ceil(y + (0, _game.gameState).brickWidth / 2) - 1;
|
||||||
const width = brx - tlx, height = bry - tly;
|
const width = brx - tlx, height = bry - tly;
|
||||||
const key = "brick" + color + "_" + "_" + width + "_" + height + "_" + offset;
|
const key = "brick" + color + "_" + "_" + width + "_" + height + "_" + offset + '_' + borderOnly;
|
||||||
if (!cachedGraphics[key]) {
|
if (!cachedGraphics[key]) {
|
||||||
const can = document.createElement("canvas");
|
const can = document.createElement("canvas");
|
||||||
can.width = width;
|
can.width = width;
|
||||||
|
@ -3887,7 +3950,7 @@ function drawBrick(ctx, color, x, y, offset = 0) {
|
||||||
canctx.lineJoin = "round";
|
canctx.lineJoin = "round";
|
||||||
canctx.lineWidth = bord;
|
canctx.lineWidth = bord;
|
||||||
roundRect(canctx, bord / 2, bord / 2, width - bord, height - bord, cornerRadius);
|
roundRect(canctx, bord / 2, bord / 2, width - bord, height - bord, cornerRadius);
|
||||||
canctx.fill();
|
if (!borderOnly) canctx.fill();
|
||||||
canctx.stroke();
|
canctx.stroke();
|
||||||
cachedGraphics[key] = can;
|
cachedGraphics[key] = can;
|
||||||
}
|
}
|
||||||
|
@ -4645,7 +4708,7 @@ function newGameState(params) {
|
||||||
return gameState;
|
return gameState;
|
||||||
}
|
}
|
||||||
|
|
||||||
},{"./settings":"5blfu","./loadGameData":"l1B4x","./game_utils":"cEeac","./gameStateMutators":"9ZeQl","./options":"d5NoS","@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3"}]},["gVqJ6","67XFf"], "67XFf", "parcelRequire94c2")
|
},{"./settings":"5blfu","./loadGameData":"l1B4x","./game_utils":"cEeac","./gameStateMutators":"9ZeQl","./options":"d5NoS","@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3"}]},["kddl8","j3Ih9"], "j3Ih9", "parcelRequire94c2")
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -61,6 +61,17 @@ body {
|
||||||
color: gold;
|
color: gold;
|
||||||
transition: color 0.01s;
|
transition: color 0.01s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
span{
|
||||||
|
color: #333;
|
||||||
|
|
||||||
|
&.great{
|
||||||
|
color: lightgreen;
|
||||||
|
}
|
||||||
|
&.good{
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#menu {
|
#menu {
|
||||||
|
@ -352,3 +363,22 @@ h2.histogram-title strong {
|
||||||
mix-blend-mode: luminosity;
|
mix-blend-mode: luminosity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//#statsdisplay{
|
||||||
|
// z-index: 1;
|
||||||
|
// white-space: nowrap;
|
||||||
|
// line-height: 20px;
|
||||||
|
// pointer-events: none;
|
||||||
|
// user-select: none;
|
||||||
|
// color: white;
|
||||||
|
// position: fixed;
|
||||||
|
// padding: 0;
|
||||||
|
// bottom: -20px;
|
||||||
|
// right: 0;
|
||||||
|
// width: 20px;
|
||||||
|
// overflow: visible;
|
||||||
|
//
|
||||||
|
// transform-origin: top left;
|
||||||
|
// transform: rotate(-90deg);
|
||||||
|
//
|
||||||
|
//}
|
19
src/game.ts
19
src/game.ts
|
@ -160,11 +160,14 @@ setInterval(() => {
|
||||||
fitSize();
|
fitSize();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
export async function openShortRunUpgradesPicker(gameState: GameState) {
|
export async function openUpgradesPicker(gameState: GameState) {
|
||||||
|
|
||||||
const catchRate =
|
const catchRate =
|
||||||
(gameState.score - gameState.levelStartScore) /
|
(gameState.score - gameState.levelStartScore) /
|
||||||
(gameState.levelSpawnedCoins || 1);
|
(gameState.levelSpawnedCoins || 1);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
let repeats = 1;
|
let repeats = 1;
|
||||||
|
|
||||||
let timeGain = "",
|
let timeGain = "",
|
||||||
|
@ -983,10 +986,16 @@ restart(
|
||||||
(window.location.search.includes("stressTest") && {
|
(window.location.search.includes("stressTest") && {
|
||||||
level: "Bird",
|
level: "Bird",
|
||||||
perks: {
|
perks: {
|
||||||
pierce: 1,
|
shocks:10,
|
||||||
sapper: 1,
|
multiball:6,
|
||||||
implosions: 3,
|
telekinesis:2,
|
||||||
streak_shots:1
|
ghost_coins:1,
|
||||||
|
pierce:4,
|
||||||
|
clairvoyant:3,
|
||||||
|
bigger_explosions:2,
|
||||||
|
sapper:2,
|
||||||
|
unbounded:1
|
||||||
|
|
||||||
},
|
},
|
||||||
levelsPerLoop: 2,
|
levelsPerLoop: 2,
|
||||||
}) ||
|
}) ||
|
||||||
|
|
|
@ -36,7 +36,7 @@ import {icons, upgrades} from "./loadGameData";
|
||||||
import {addToTotalScore, getCurrentMaxCoins, getCurrentMaxParticles,} from "./settings";
|
import {addToTotalScore, getCurrentMaxCoins, getCurrentMaxParticles,} from "./settings";
|
||||||
import {background} from "./render";
|
import {background} from "./render";
|
||||||
import {gameOver} from "./gameOver";
|
import {gameOver} from "./gameOver";
|
||||||
import {brickIndex, fitSize, gameState, hasBrick, hitsSomething, openShortRunUpgradesPicker, pause,} from "./game";
|
import {brickIndex, fitSize, gameState, hasBrick, hitsSomething, openUpgradesPicker, pause,} from "./game";
|
||||||
import {stopRecording} from "./recording";
|
import {stopRecording} from "./recording";
|
||||||
import {isOptionOn} from "./options";
|
import {isOptionOn} from "./options";
|
||||||
import {isPremium} from "./premium";
|
import {isPremium} from "./premium";
|
||||||
|
@ -124,13 +124,13 @@ export function normalizeGameState(gameState: GameState) {
|
||||||
(gameState.gameZoneWidth / 12) *
|
(gameState.gameZoneWidth / 12) *
|
||||||
Math.min(12, 3 - gameState.perks.smaller_puck + gameState.perks.bigger_puck));
|
Math.min(12, 3 - gameState.perks.smaller_puck + gameState.perks.bigger_puck));
|
||||||
|
|
||||||
const corner = gameState.levelTime ? gameState.perks.corner_shot : 0
|
const corner = gameState.levelTime ? gameState.perks.corner_shot : 0
|
||||||
|
|
||||||
let minX = gameState.offsetXRoundedDown + gameState.puckWidth / 2 - gameState.puckWidth * corner
|
let minX = gameState.offsetXRoundedDown + gameState.puckWidth / 2 - gameState.puckWidth * corner
|
||||||
|
|
||||||
let maxX =gameState.offsetXRoundedDown +
|
let maxX = gameState.offsetXRoundedDown +
|
||||||
gameState.gameZoneWidthRoundedUp -
|
gameState.gameZoneWidthRoundedUp -
|
||||||
gameState.puckWidth / 2 + gameState.puckWidth * corner;
|
gameState.puckWidth / 2 + gameState.puckWidth * corner;
|
||||||
|
|
||||||
|
|
||||||
gameState.puckPosition = clamp(gameState.puckPosition, minX, maxX);
|
gameState.puckPosition = clamp(gameState.puckPosition, minX, maxX);
|
||||||
|
@ -179,7 +179,7 @@ export function resetCombo(
|
||||||
}
|
}
|
||||||
if (typeof x !== "undefined" && typeof y !== "undefined") {
|
if (typeof x !== "undefined" && typeof y !== "undefined") {
|
||||||
|
|
||||||
makeText(gameState, x, y, "red", "-" + lost, 20, 500+clamp(lost, 0,500));
|
makeText(gameState, x, y, "red", "-" + lost, 20, 500 + clamp(lost, 0, 500));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return lost;
|
return lost;
|
||||||
|
@ -198,7 +198,7 @@ export function decreaseCombo(
|
||||||
if (lost) {
|
if (lost) {
|
||||||
schedulGameSound(gameState, "comboDecrease", x, 1);
|
schedulGameSound(gameState, "comboDecrease", x, 1);
|
||||||
if (typeof x !== "undefined" && typeof y !== "undefined") {
|
if (typeof x !== "undefined" && typeof y !== "undefined") {
|
||||||
makeText(gameState, x, y, "red", "-" + lost, 20, 400+lost);
|
makeText(gameState, x, y, "red", "-" + lost, 20, 400 + lost);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -256,8 +256,11 @@ export function explosionAt(
|
||||||
x: number,
|
x: number,
|
||||||
y: number,
|
y: number,
|
||||||
ball: Ball,
|
ball: Ball,
|
||||||
|
extraSize: number = 0
|
||||||
) {
|
) {
|
||||||
const size = 1 + gameState.perks.bigger_explosions + Math.max(1,gameState.perks.implosions) - 1;
|
const size = 1 + gameState.perks.bigger_explosions +
|
||||||
|
Math.max(0, gameState.perks.implosions - 1) + extraSize
|
||||||
|
;
|
||||||
schedulGameSound(gameState, "explode", ball.x, 1);
|
schedulGameSound(gameState, "explode", ball.x, 1);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
const col = index % gameState.gridSize;
|
const col = index % gameState.gridSize;
|
||||||
|
@ -292,7 +295,7 @@ export function explosionAt(
|
||||||
if (gameState.perks.implosions) {
|
if (gameState.perks.implosions) {
|
||||||
spawnImplosion(
|
spawnImplosion(
|
||||||
gameState,
|
gameState,
|
||||||
7 *size ,
|
7 * size,
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
"white",
|
"white",
|
||||||
|
@ -300,7 +303,7 @@ export function explosionAt(
|
||||||
} else {
|
} else {
|
||||||
spawnExplosion(
|
spawnExplosion(
|
||||||
gameState,
|
gameState,
|
||||||
7 *size,
|
7 * size,
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
"white",
|
"white",
|
||||||
|
@ -332,7 +335,7 @@ export function explodeBrick(
|
||||||
// resetCombo(gameState, x, y);
|
// resetCombo(gameState, x, y);
|
||||||
// }
|
// }
|
||||||
setBrick(gameState, index, "");
|
setBrick(gameState, index, "");
|
||||||
explosionAt(gameState, index, x, y, ball);
|
explosionAt(gameState, index, x, y, ball, 0);
|
||||||
} 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
|
||||||
|
|
||||||
|
@ -616,7 +619,7 @@ export async function setLevel(gameState: GameState, l: number) {
|
||||||
pause(false);
|
pause(false);
|
||||||
stopRecording();
|
stopRecording();
|
||||||
if (l > 0) {
|
if (l > 0) {
|
||||||
await openShortRunUpgradesPicker(gameState);
|
await openUpgradesPicker(gameState);
|
||||||
}
|
}
|
||||||
gameState.currentLevel = l;
|
gameState.currentLevel = l;
|
||||||
|
|
||||||
|
@ -629,6 +632,7 @@ export async function setLevel(gameState: GameState, l: number) {
|
||||||
gameState.lastTickDown = gameState.levelTime;
|
gameState.lastTickDown = gameState.levelTime;
|
||||||
gameState.levelStartScore = gameState.score;
|
gameState.levelStartScore = gameState.score;
|
||||||
gameState.levelSpawnedCoins = 0;
|
gameState.levelSpawnedCoins = 0;
|
||||||
|
gameState.levelLostCoins = 0;
|
||||||
gameState.levelMisses = 0;
|
gameState.levelMisses = 0;
|
||||||
gameState.runStatistics.levelsPlayed++;
|
gameState.runStatistics.levelsPlayed++;
|
||||||
|
|
||||||
|
@ -650,7 +654,7 @@ export async function setLevel(gameState: GameState, l: number) {
|
||||||
gameState.gridSize = lvl.size;
|
gameState.gridSize = lvl.size;
|
||||||
fitSize();
|
fitSize();
|
||||||
}
|
}
|
||||||
empty(gameState.coins);
|
gameState.levelLostCoins += empty(gameState.coins);
|
||||||
empty(gameState.particles);
|
empty(gameState.particles);
|
||||||
empty(gameState.lights);
|
empty(gameState.lights);
|
||||||
empty(gameState.texts);
|
empty(gameState.texts);
|
||||||
|
@ -798,7 +802,16 @@ export function coinBrickHitCheck(gameState: GameState, coin: Coin) {
|
||||||
typeof hhit == "undefined" &&
|
typeof hhit == "undefined" &&
|
||||||
hitsSomething(x, y, radius)) ||
|
hitsSomething(x, y, radius)) ||
|
||||||
undefined;
|
undefined;
|
||||||
if (!gameState.perks.ghost_coins) {
|
|
||||||
|
if (gameState.perks.ghost_coins) {
|
||||||
|
// slow down
|
||||||
|
if (typeof (vhit ?? hhit ?? chit) !== "undefined") {
|
||||||
|
|
||||||
|
coin.vy *= 1 - 0.2 / gameState.perks.ghost_coins;
|
||||||
|
coin.vx *= 1 - 0.2 / gameState.perks.ghost_coins;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
if (typeof vhit !== "undefined" || typeof chit !== "undefined") {
|
if (typeof vhit !== "undefined" || typeof chit !== "undefined") {
|
||||||
coin.y = coin.previousY;
|
coin.y = coin.previousY;
|
||||||
coin.vy *= -1;
|
coin.vy *= -1;
|
||||||
|
@ -988,7 +1001,7 @@ export function gameStateTick(
|
||||||
(gameState.perks.viscosity *
|
(gameState.perks.viscosity *
|
||||||
0.03 +
|
0.03 +
|
||||||
0.005) *
|
0.005) *
|
||||||
frames;
|
frames / (1 + gameState.perks.etherealcoins);
|
||||||
|
|
||||||
coin.vy *= ratio;
|
coin.vy *= ratio;
|
||||||
coin.vx *= ratio;
|
coin.vx *= ratio;
|
||||||
|
@ -1040,6 +1053,7 @@ export function gameStateTick(
|
||||||
|
|
||||||
destroy(gameState.coins, coinIndex);
|
destroy(gameState.coins, coinIndex);
|
||||||
} else if (coin.y > gameState.canvasHeight + coinRadius) {
|
} else if (coin.y > gameState.canvasHeight + coinRadius) {
|
||||||
|
gameState.levelLostCoins+=coin.points
|
||||||
destroy(gameState.coins, coinIndex);
|
destroy(gameState.coins, coinIndex);
|
||||||
if (gameState.perks.compound_interest) {
|
if (gameState.perks.compound_interest) {
|
||||||
resetCombo(gameState, coin.x, coin.y);
|
resetCombo(gameState, coin.x, coin.y);
|
||||||
|
@ -1052,6 +1066,7 @@ export function gameStateTick(
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
// Out of bound on sides
|
// Out of bound on sides
|
||||||
|
gameState.levelLostCoins+=coin.points
|
||||||
destroy(gameState.coins, coinIndex);
|
destroy(gameState.coins, coinIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1126,7 +1141,7 @@ export function gameStateTick(
|
||||||
((Math.random() - 0.5) * limit) / 3;
|
((Math.random() - 0.5) * limit) / 3;
|
||||||
|
|
||||||
let index = brickIndex(x, y);
|
let index = brickIndex(x, y);
|
||||||
explosionAt(gameState, index, x, y, a);
|
explosionAt(gameState, index, x, y, a, Math.max(0, gameState.perks.shocks - 1));
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
@ -1263,7 +1278,7 @@ export function gameStateTick(
|
||||||
forEachLiveOne(gameState.respawns, (r, ri) => {
|
forEachLiveOne(gameState.respawns, (r, ri) => {
|
||||||
if (gameState.bricks[r.index]) {
|
if (gameState.bricks[r.index]) {
|
||||||
destroy(gameState.respawns, ri)
|
destroy(gameState.respawns, ri)
|
||||||
} else if (gameState.levelTime>r.time) {
|
} else if (gameState.levelTime > r.time) {
|
||||||
setBrick(gameState, r.index, r.color)
|
setBrick(gameState, r.index, r.color)
|
||||||
destroy(gameState.respawns, ri)
|
destroy(gameState.respawns, ri)
|
||||||
} else if (!isOptionOn("basic")) {
|
} else if (!isOptionOn("basic")) {
|
||||||
|
@ -1435,7 +1450,7 @@ export function ballTick(gameState: GameState, ball: Ball, delta: number) {
|
||||||
const angle = Math.atan2(
|
const angle = Math.atan2(
|
||||||
-gameState.puckWidth / 2,
|
-gameState.puckWidth / 2,
|
||||||
(ball.x - gameState.puckPosition) *
|
(ball.x - gameState.puckPosition) *
|
||||||
(gameState.perks.concave_puck ? -0.5 : 1),
|
(gameState.perks.concave_puck ? -1 / (1 + gameState.perks.concave_puck) : 1),
|
||||||
);
|
);
|
||||||
ball.vx = speed * Math.cos(angle);
|
ball.vx = speed * Math.cos(angle);
|
||||||
ball.vy = speed * Math.sin(angle);
|
ball.vy = speed * Math.sin(angle);
|
||||||
|
@ -1657,7 +1672,8 @@ function makeCoin(
|
||||||
color = "gold",
|
color = "gold",
|
||||||
points = 1,
|
points = 1,
|
||||||
) {
|
) {
|
||||||
let weight = 0.8 + Math.random() * 0.2 + Math.min(2, points * 0.01);
|
let weight = 0.8 + Math.random() * 0.2 + Math.min(2, points * 0.01)
|
||||||
|
weight *= 5 / (5 + gameState.perks.etherealcoins)
|
||||||
|
|
||||||
append(gameState.coins, (p: Partial<Coin>) => {
|
append(gameState.coins, (p: Partial<Coin>) => {
|
||||||
p.x = x;
|
p.x = x;
|
||||||
|
@ -1718,7 +1734,7 @@ function makeText(
|
||||||
p.y = y;
|
p.y = y;
|
||||||
p.color = color;
|
p.color = color;
|
||||||
p.size = size;
|
p.size = size;
|
||||||
p.duration = clamp(duration,400,2000);
|
p.duration = clamp(duration, 400, 2000);
|
||||||
p.text = text;
|
p.text = text;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1776,9 +1792,16 @@ export function liveCount<T>(where: ReusableArray<T>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function empty<T>(where: ReusableArray<T>) {
|
export function empty<T>(where: ReusableArray<T>) {
|
||||||
|
let destroyed=0
|
||||||
where.total = 0;
|
where.total = 0;
|
||||||
where.indexMin = 0;
|
where.indexMin = 0;
|
||||||
where.list.forEach((i) => (i.destroyed = true));
|
where.list.forEach((i) => {
|
||||||
|
if(!i.destroyed) {
|
||||||
|
i.destroyed = true
|
||||||
|
destroyed++
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return destroyed
|
||||||
}
|
}
|
||||||
|
|
||||||
export function forEachLiveOne<T>(
|
export function forEachLiveOne<T>(
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
<button id="menu">☰ <span id="menuLabel">menu</span></button>
|
<button id="menu">☰ <span id="menuLabel">menu</span></button>
|
||||||
<button id="score"></button>
|
<button id="score"></button>
|
||||||
<div id="FPSDisplay"></div>
|
<div id="FPSDisplay"></div>
|
||||||
|
<div id="statsdisplay"></div>
|
||||||
<canvas id="game"></canvas>
|
<canvas id="game"></canvas>
|
||||||
<div id="popup">
|
<div id="popup">
|
||||||
<button id="close-modale"></button>
|
<button id="close-modale"></button>
|
||||||
|
|
|
@ -24,6 +24,7 @@ bombSVG.src =
|
||||||
btoa(`<svg width="144" height="144" viewBox="0 0 38.101 38.099" xmlns="http://www.w3.org/2000/svg">
|
btoa(`<svg width="144" height="144" viewBox="0 0 38.101 38.099" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="m6.1528 26.516c-2.6992-3.4942-2.9332-8.281-.58305-11.981a10.454 10.454 0 017.3701-4.7582c1.962-.27726 4.1646.05953 5.8835.90027l.45013.22017.89782-.87417c.83748-.81464.91169-.87499 1.0992-.90271.40528-.058713.58876.03425 1.1971.6116l.55451.52679 1.0821-1.0821c1.1963-1.1963 1.383-1.3357 2.1039-1.5877.57898-.20223 1.5681-.19816 2.1691.00897 1.4613.50314 2.3673 1.7622 2.3567 3.2773-.0058.95654-.24464 1.5795-.90924 2.3746-.40936.48928-.55533.81057-.57898 1.2737-.02039.41018.1109.77714.42322 1.1792.30172.38816.3694.61323.2797.93044-.12803.45666-.56674.71598-1.0242.60507-.601-.14597-1.3031-1.3088-1.3969-2.3126-.09459-1.0161.19245-1.8682.92392-2.7432.42567-.50885.5643-.82851.5643-1.3031 0-.50151-.14026-.83177-.51211-1.2028-.50966-.50966-1.0968-.64829-1.781-.41996l-.37348.12477-2.1006 2.1006.52597.55696c.45421.48194.5325.58876.57898.78855.09622.41588.07502.45014-.88396 1.4548l-.87173.9125.26339.57979a10.193 10.193 0 01.9231 4.1001c.03996 2.046-.41996 3.8082-1.4442 5.537-.55044.928-1.0185 1.5013-1.8968 2.3241-.83503.78284-1.5526 1.2827-2.4904 1.7361-3.4266 1.657-7.4721 1.3422-10.549-.82035-.73473-.51782-1.7312-1.4621-2.2515-2.1357zm21.869-4.5584c-.0579-.19734-.05871-2.2662 0-2.4545.11906-.39142.57898-.63361 1.0038-.53005.23812.05708.54147.32455.6116.5382.06279.19163.06769 2.1805.0065 2.3811-.12558.40773-.61649.67602-1.0462.57164-.234-.05708-.51615-.30498-.57568-.50722m3.0417-2.6013c-.12313-.6222.37837-1.1049 1.0479-1.0079.18348.0261.25279.08399 1.0071.83911.75838.75838.81301.82362.84074 1.0112.10193.68499-.40365 1.1938-1.034 1.0405-.1949-.0473-.28786-.12558-1.0144-.85216-.7649-.76409-.80241-.81057-.84645-1.0316m.61323-3.0629a.85623.85623 0 01.59284-.99975c.28949-.09214 2.1814-.08318 2.3917.01141.38734.17369.6279.61078.53984.98181-.06035.25606-.35391.57327-.60181.64992-.25279.07747-2.2278.053-2.4097-.03017-.26013-.11906-.46318-.36125-.51374-.61323" fill="#fff" opacity="0.3"/>
|
<path d="m6.1528 26.516c-2.6992-3.4942-2.9332-8.281-.58305-11.981a10.454 10.454 0 017.3701-4.7582c1.962-.27726 4.1646.05953 5.8835.90027l.45013.22017.89782-.87417c.83748-.81464.91169-.87499 1.0992-.90271.40528-.058713.58876.03425 1.1971.6116l.55451.52679 1.0821-1.0821c1.1963-1.1963 1.383-1.3357 2.1039-1.5877.57898-.20223 1.5681-.19816 2.1691.00897 1.4613.50314 2.3673 1.7622 2.3567 3.2773-.0058.95654-.24464 1.5795-.90924 2.3746-.40936.48928-.55533.81057-.57898 1.2737-.02039.41018.1109.77714.42322 1.1792.30172.38816.3694.61323.2797.93044-.12803.45666-.56674.71598-1.0242.60507-.601-.14597-1.3031-1.3088-1.3969-2.3126-.09459-1.0161.19245-1.8682.92392-2.7432.42567-.50885.5643-.82851.5643-1.3031 0-.50151-.14026-.83177-.51211-1.2028-.50966-.50966-1.0968-.64829-1.781-.41996l-.37348.12477-2.1006 2.1006.52597.55696c.45421.48194.5325.58876.57898.78855.09622.41588.07502.45014-.88396 1.4548l-.87173.9125.26339.57979a10.193 10.193 0 01.9231 4.1001c.03996 2.046-.41996 3.8082-1.4442 5.537-.55044.928-1.0185 1.5013-1.8968 2.3241-.83503.78284-1.5526 1.2827-2.4904 1.7361-3.4266 1.657-7.4721 1.3422-10.549-.82035-.73473-.51782-1.7312-1.4621-2.2515-2.1357zm21.869-4.5584c-.0579-.19734-.05871-2.2662 0-2.4545.11906-.39142.57898-.63361 1.0038-.53005.23812.05708.54147.32455.6116.5382.06279.19163.06769 2.1805.0065 2.3811-.12558.40773-.61649.67602-1.0462.57164-.234-.05708-.51615-.30498-.57568-.50722m3.0417-2.6013c-.12313-.6222.37837-1.1049 1.0479-1.0079.18348.0261.25279.08399 1.0071.83911.75838.75838.81301.82362.84074 1.0112.10193.68499-.40365 1.1938-1.034 1.0405-.1949-.0473-.28786-.12558-1.0144-.85216-.7649-.76409-.80241-.81057-.84645-1.0316m.61323-3.0629a.85623.85623 0 01.59284-.99975c.28949-.09214 2.1814-.08318 2.3917.01141.38734.17369.6279.61078.53984.98181-.06035.25606-.35391.57327-.60181.64992-.25279.07747-2.2278.053-2.4097-.03017-.26013-.11906-.46318-.36125-.51374-.61323" fill="#fff" opacity="0.3"/>
|
||||||
</svg>`);
|
</svg>`);
|
||||||
|
bombSVG.onload = () => gameState.needsRender = true
|
||||||
|
|
||||||
export const background = document.createElement("img");
|
export const background = document.createElement("img");
|
||||||
export const backgroundCanvas = document.createElement("canvas");
|
export const backgroundCanvas = document.createElement("canvas");
|
||||||
|
@ -49,7 +50,25 @@ export function render(gameState: GameState) {
|
||||||
} else {
|
} else {
|
||||||
menuLabel.innerText = t("play.menu_label");
|
menuLabel.innerText = t("play.menu_label");
|
||||||
}
|
}
|
||||||
scoreDisplay.innerText = `$${gameState.score}`;
|
|
||||||
|
const catchRate = gameState.levelSpawnedCoins ?
|
||||||
|
(gameState.levelSpawnedCoins - gameState.levelLostCoins)/gameState.levelSpawnedCoins :1
|
||||||
|
|
||||||
|
scoreDisplay.innerHTML= (isOptionOn('show_stats') ? `
|
||||||
|
<span class="${(catchRate==1 && 'great') || (catchRate>0.9 && 'good')||''}">
|
||||||
|
${Math.floor(catchRate*100)}%
|
||||||
|
</span><span> / </span>
|
||||||
|
<span class="${(gameState.levelWallBounces==0 && 'great') || (gameState.levelWallBounces<5 && 'good')||''}">
|
||||||
|
${gameState.levelWallBounces} B
|
||||||
|
</span><span> / </span>
|
||||||
|
<span class="${(gameState.levelTime<30000 && 'great') || (gameState.levelTime<60000 && 'good')||''}">
|
||||||
|
${Math.ceil(gameState.levelTime/1000)}s
|
||||||
|
</span><span> / </span>
|
||||||
|
<span class="${(gameState.levelMisses==0 && 'great') || (gameState.levelMisses<=3 && 'good')||''}">
|
||||||
|
${gameState.levelMisses} M
|
||||||
|
</span><span> / </span>
|
||||||
|
`: '' )+ `$${gameState.score}`;
|
||||||
|
|
||||||
|
|
||||||
scoreDisplay.className =
|
scoreDisplay.className =
|
||||||
gameState.lastScoreIncrease > gameState.levelTime - 500 ? "active" : "";
|
gameState.lastScoreIncrease > gameState.levelTime - 500 ? "active" : "";
|
||||||
|
@ -117,10 +136,30 @@ export function render(gameState: GameState) {
|
||||||
) as CanvasRenderingContext2D;
|
) as CanvasRenderingContext2D;
|
||||||
bgctx.fillStyle = level.color || "#000";
|
bgctx.fillStyle = level.color || "#000";
|
||||||
bgctx.fillRect(0, 0, gameState.canvasWidth, gameState.canvasHeight);
|
bgctx.fillRect(0, 0, gameState.canvasWidth, gameState.canvasHeight);
|
||||||
const pattern = ctx.createPattern(background, "repeat");
|
if (gameState.perks.clairvoyant >= 3) {
|
||||||
if (pattern) {
|
const pageSource = document.body.innerHTML.replace(/\s+/gi, '')
|
||||||
bgctx.fillStyle = pattern;
|
const lineWidth = Math.ceil(gameState.canvasWidth / 15)
|
||||||
bgctx.fillRect(0, 0, width, height);
|
const lines = Math.ceil(gameState.canvasHeight / 20)
|
||||||
|
const chars = lineWidth * lines
|
||||||
|
let start = Math.ceil(Math.random() * (pageSource.length - chars))
|
||||||
|
for (let i = 0; i < lines; i++) {
|
||||||
|
bgctx.fillStyle = 'white'
|
||||||
|
bgctx.font = '20px Courier'
|
||||||
|
bgctx.fillText(pageSource.slice(
|
||||||
|
start + i * lineWidth,
|
||||||
|
start + (i + 1) * lineWidth),
|
||||||
|
0,
|
||||||
|
i * 20,
|
||||||
|
gameState.canvasWidth
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
const pattern = ctx.createPattern(background, "repeat");
|
||||||
|
if (pattern) {
|
||||||
|
bgctx.fillStyle = pattern;
|
||||||
|
bgctx.fillRect(0, 0, width, height);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,9 +213,8 @@ export function render(gameState: GameState) {
|
||||||
coin.x,
|
coin.x,
|
||||||
coin.y,
|
coin.y,
|
||||||
(hasCombo && gameState.perks.asceticism && "red") ||
|
(hasCombo && gameState.perks.asceticism && "red") ||
|
||||||
(!coin.points && "red") ||
|
(coin.color==='gold' && 'gold')||
|
||||||
level.color ||
|
gameState.puckColor,
|
||||||
"black",
|
|
||||||
coin.a,
|
coin.a,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -205,7 +243,7 @@ export function render(gameState: GameState) {
|
||||||
const {x, y, time, color, size, duration} = flash;
|
const {x, y, time, color, size, duration} = flash;
|
||||||
const elapsed = gameState.levelTime - time;
|
const elapsed = gameState.levelTime - time;
|
||||||
ctx.globalAlpha = Math.min(1, 2 - (elapsed / duration) * 2) * 0.5;
|
ctx.globalAlpha = Math.min(1, 2 - (elapsed / duration) * 2) * 0.5;
|
||||||
drawBrick(ctx, color, x, y, -1);
|
drawBrick(ctx, color, x, y, -1, gameState.perks.clairvoyant >= 2);
|
||||||
});
|
});
|
||||||
|
|
||||||
ctx.globalCompositeOperation = "screen";
|
ctx.globalCompositeOperation = "screen";
|
||||||
|
@ -290,11 +328,11 @@ export function render(gameState: GameState) {
|
||||||
|
|
||||||
drawPuck(
|
drawPuck(
|
||||||
ctx,
|
ctx,
|
||||||
gameState.puckColor,
|
gameState.puckColor,
|
||||||
gameState.puckWidth,
|
gameState.puckWidth,
|
||||||
gameState.puckHeight,
|
gameState.puckHeight,
|
||||||
0,
|
0,
|
||||||
!!gameState.perks.concave_puck,
|
gameState.perks.concave_puck,
|
||||||
gameState.perks.streak_shots && hasCombo ? getDashOffset(gameState) : -1,
|
gameState.perks.streak_shots && hasCombo ? getDashOffset(gameState) : -1,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -410,7 +448,7 @@ export function render(gameState: GameState) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.globalAlpha= gameState.perks.unbounded >1 ? 0.1 : 1
|
ctx.globalAlpha = gameState.perks.unbounded > 1 ? 0.1 : 1
|
||||||
drawStraightLine(
|
drawStraightLine(
|
||||||
ctx,
|
ctx,
|
||||||
gameState,
|
gameState,
|
||||||
|
@ -422,7 +460,7 @@ export function render(gameState: GameState) {
|
||||||
1,
|
1,
|
||||||
);
|
);
|
||||||
|
|
||||||
ctx.globalAlpha=1
|
ctx.globalAlpha = 1
|
||||||
drawStraightLine(
|
drawStraightLine(
|
||||||
ctx,
|
ctx,
|
||||||
gameState,
|
gameState,
|
||||||
|
@ -448,6 +486,7 @@ export function render(gameState: GameState) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (shaked) {
|
if (shaked) {
|
||||||
ctx.resetTransform();
|
ctx.resetTransform();
|
||||||
}
|
}
|
||||||
|
@ -559,7 +598,7 @@ export function renderAllBricks() {
|
||||||
countBricksAbove(gameState, index) &&
|
countBricksAbove(gameState, index) &&
|
||||||
!countBricksBelow(gameState, index);
|
!countBricksBelow(gameState, index);
|
||||||
|
|
||||||
let redBorder = (gameState.ballsColor !== color &&
|
let redBorder = (gameState.ballsColor !== color &&
|
||||||
color !== "black" &&
|
color !== "black" &&
|
||||||
redBorderOnBricksWithWrongColor) ||
|
redBorderOnBricksWithWrongColor) ||
|
||||||
(hasCombo && gameState.perks.zen && color === "black") ||
|
(hasCombo && gameState.perks.zen && color === "black") ||
|
||||||
|
@ -567,7 +606,8 @@ export function renderAllBricks() {
|
||||||
redColorOnAllBricks;
|
redColorOnAllBricks;
|
||||||
|
|
||||||
canctx.globalCompositeOperation = "source-over";
|
canctx.globalCompositeOperation = "source-over";
|
||||||
drawBrick(canctx, color, x, y, redBorder ? offset : -1);
|
drawBrick(canctx,
|
||||||
|
color, x, y, redBorder ? offset : -1, gameState.perks.clairvoyant >= 2);
|
||||||
if (gameState.brickHP[index] > 1 && gameState.perks.clairvoyant) {
|
if (gameState.brickHP[index] > 1 && gameState.perks.clairvoyant) {
|
||||||
canctx.globalCompositeOperation = "destination-out";
|
canctx.globalCompositeOperation = "destination-out";
|
||||||
drawText(
|
drawText(
|
||||||
|
@ -598,7 +638,7 @@ export function drawPuck(
|
||||||
puckWidth: number,
|
puckWidth: number,
|
||||||
puckHeight: number,
|
puckHeight: number,
|
||||||
yOffset = 0,
|
yOffset = 0,
|
||||||
flipped: boolean,
|
concave_puck: number,
|
||||||
redBorderOffset: number,
|
redBorderOffset: number,
|
||||||
) {
|
) {
|
||||||
const key =
|
const key =
|
||||||
|
@ -609,7 +649,7 @@ export function drawPuck(
|
||||||
"_" +
|
"_" +
|
||||||
puckHeight +
|
puckHeight +
|
||||||
"_" +
|
"_" +
|
||||||
flipped +
|
concave_puck +
|
||||||
"_" +
|
"_" +
|
||||||
redBorderOffset;
|
redBorderOffset;
|
||||||
|
|
||||||
|
@ -623,13 +663,13 @@ export function drawPuck(
|
||||||
canctx.beginPath();
|
canctx.beginPath();
|
||||||
canctx.moveTo(0, puckHeight * 2);
|
canctx.moveTo(0, puckHeight * 2);
|
||||||
|
|
||||||
if (flipped) {
|
if (concave_puck) {
|
||||||
canctx.lineTo(0, puckHeight * 0.75);
|
canctx.lineTo(0, puckHeight * 0.75);
|
||||||
canctx.bezierCurveTo(
|
canctx.bezierCurveTo(
|
||||||
puckWidth / 2,
|
puckWidth / 2,
|
||||||
puckHeight,
|
puckHeight * (2 + concave_puck) / 3,
|
||||||
puckWidth / 2,
|
puckWidth / 2,
|
||||||
puckHeight * 1,
|
puckHeight * (2 + concave_puck) / 3,
|
||||||
puckWidth,
|
puckWidth,
|
||||||
puckHeight * 0.75,
|
puckHeight * 0.75,
|
||||||
);
|
);
|
||||||
|
@ -741,14 +781,12 @@ export function drawCoin(
|
||||||
canctx.fillStyle = color;
|
canctx.fillStyle = color;
|
||||||
canctx.fill();
|
canctx.fill();
|
||||||
|
|
||||||
if (color === "gold" || borderColor === "red") {
|
canctx.strokeStyle = borderColor;
|
||||||
canctx.strokeStyle = borderColor;
|
if (borderColor == "red") {
|
||||||
if (borderColor == "red") {
|
canctx.lineWidth = 2;
|
||||||
canctx.lineWidth = 2;
|
canctx.setLineDash(redBorderDash);
|
||||||
canctx.setLineDash(redBorderDash);
|
|
||||||
}
|
|
||||||
canctx.stroke();
|
|
||||||
}
|
}
|
||||||
|
canctx.stroke();
|
||||||
|
|
||||||
if (color === "gold") {
|
if (color === "gold") {
|
||||||
// Fill in
|
// Fill in
|
||||||
|
@ -817,6 +855,7 @@ export function drawBrick(
|
||||||
x: number,
|
x: number,
|
||||||
y: number,
|
y: number,
|
||||||
offset: number = 0,
|
offset: number = 0,
|
||||||
|
borderOnly: boolean
|
||||||
) {
|
) {
|
||||||
const tlx = Math.ceil(x - gameState.brickWidth / 2);
|
const tlx = Math.ceil(x - gameState.brickWidth / 2);
|
||||||
const tly = Math.ceil(y - gameState.brickWidth / 2);
|
const tly = Math.ceil(y - gameState.brickWidth / 2);
|
||||||
|
@ -825,7 +864,7 @@ export function drawBrick(
|
||||||
|
|
||||||
const width = brx - tlx,
|
const width = brx - tlx,
|
||||||
height = bry - tly;
|
height = bry - tly;
|
||||||
const key = "brick" + color + "_" + "_" + width + "_" + height + "_" + offset;
|
const key = "brick" + color + "_" + "_" + width + "_" + height + "_" + offset + '_' + borderOnly;
|
||||||
|
|
||||||
if (!cachedGraphics[key]) {
|
if (!cachedGraphics[key]) {
|
||||||
const can = document.createElement("canvas");
|
const can = document.createElement("canvas");
|
||||||
|
@ -850,7 +889,9 @@ export function drawBrick(
|
||||||
height - bord,
|
height - bord,
|
||||||
cornerRadius,
|
cornerRadius,
|
||||||
);
|
);
|
||||||
canctx.fill();
|
if (!borderOnly) {
|
||||||
|
canctx.fill();
|
||||||
|
}
|
||||||
canctx.stroke();
|
canctx.stroke();
|
||||||
|
|
||||||
cachedGraphics[key] = can;
|
cachedGraphics[key] = can;
|
||||||
|
|
1
src/types.d.ts
vendored
1
src/types.d.ts
vendored
|
@ -244,6 +244,7 @@ export type GameState = {
|
||||||
levelStartScore: number;
|
levelStartScore: number;
|
||||||
levelMisses: number;
|
levelMisses: number;
|
||||||
levelSpawnedCoins: number;
|
levelSpawnedCoins: number;
|
||||||
|
levelLostCoins: number;
|
||||||
|
|
||||||
// MAX_COINS: number;
|
// MAX_COINS: number;
|
||||||
// MAX_PARTICLES: number;
|
// MAX_PARTICLES: number;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue