Stats display

This commit is contained in:
Renan LE CARO 2025-03-29 20:45:54 +01:00
parent 23798c4e58
commit a328520191
8 changed files with 288 additions and 122 deletions

View file

@ -27,27 +27,23 @@ At the end of the last level of each run, they can start a new loop. They'll be
to level 1, with only one of their perks, leveled up. All the other perks they used in the run
will be banned from the pool. The perk they decide to keep will gain one level, even if it was
already maxed out.
# Todo before next release
- b71 white border around dark coins
- [jaceys] Counters for coins lost, misses, and boundary bounces, as well as a timer.
# next
- 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] 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
- 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
# Todo before next release
# 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
- 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
@ -66,6 +62,8 @@ already maxed out.
- soft reset : same math as shunt
- smaller puck : now the puck can get as small as a ball
- 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
- Made the "combo lost" text last 500ms instead of the pointless 150ms

167
dist/index.html vendored
View file

@ -68,6 +68,18 @@ body {
transition: color 10ms;
}
#score span {
color: #333;
}
#score span.great {
color: #90ee90;
}
#score span.good {
color: #fff;
}
#menu {
left: 0;
}
@ -354,6 +366,7 @@ h2.histogram-title strong {
<button id="menu"><span id="menuLabel">menu</span></button>
<button id="score"></button>
<div id="FPSDisplay"></div>
<div id="statsdisplay"></div>
<canvas id="game"></canvas>
<div id="popup">
<button id="close-modale"></button>
@ -502,10 +515,10 @@ h2.histogram-title strong {
this[globalName] = mainExports;
}
}
})({"gVqJ6":[function(require,module,exports,__globalThis) {
require("5e6e73082bdd189e")(require("e4e486661babe6bc").getBundleURL('bgzJG') + "editor.1350aee5.js");
})({"kddl8":[function(require,module,exports,__globalThis) {
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";
var cacheLoader = require("ca2a84f7fa4a3bb0");
module.exports = cacheLoader(function(bundle) {
@ -602,7 +615,7 @@ exports.getBundleURL = getBundleURLCached;
exports.getBaseURL = getBaseURL;
exports.getOrigin = getOrigin;
},{}],"67XFf":[function(require,module,exports,__globalThis) {
},{}],"j3Ih9":[function(require,module,exports,__globalThis) {
var _gameTs = require("./game.ts");
},{"./game.ts":"edeGs"}],"edeGs":[function(require,module,exports,__globalThis) {
@ -611,7 +624,7 @@ parcelHelpers.defineInteropFlag(exports);
parcelHelpers.export(exports, "play", ()=>play);
parcelHelpers.export(exports, "pause", ()=>pause);
parcelHelpers.export(exports, "fitSize", ()=>fitSize);
parcelHelpers.export(exports, "openShortRunUpgradesPicker", ()=>openShortRunUpgradesPicker);
parcelHelpers.export(exports, "openUpgradesPicker", ()=>openUpgradesPicker);
parcelHelpers.export(exports, "brickIndex", ()=>brickIndex);
parcelHelpers.export(exports, "hasBrick", ()=>hasBrick);
parcelHelpers.export(exports, "hitsSomething", ()=>hitsSomething);
@ -715,7 +728,7 @@ setInterval(()=>{
const { width, height } = (0, _render.gameCanvas).getBoundingClientRect();
if (width !== gameState.canvasWidth || height !== gameState.canvasHeight) fitSize();
}, 1000);
async function openShortRunUpgradesPicker(gameState) {
async function openUpgradesPicker(gameState) {
const catchRate = (gameState.score - gameState.levelStartScore) / (gameState.levelSpawnedCoins || 1);
let repeats = 1;
let timeGain = "", catchGain = "", wallHitsGain = "", missesGain = "";
@ -1331,16 +1344,21 @@ function restart(params) {
restart(window.location.search.includes("stressTest") && {
level: "Bird",
perks: {
pierce: 1,
sapper: 1,
implosions: 3,
streak_shots: 1
shocks: 10,
multiball: 6,
telekinesis: 2,
ghost_coins: 1,
pierce: 4,
clairvoyant: 3,
bigger_explosions: 2,
sapper: 2,
unbounded: 1
},
levelsPerLoop: 2
} || {});
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");
parcelHelpers.defineInteropFlag(exports);
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");
parcelHelpers.defineInteropFlag(exports);
parcelHelpers.export(exports, "getCurrentLang", ()=>getCurrentLang);
@ -2579,10 +2597,10 @@ if ("serviceWorker" in navigator && window.location.href.endsWith("/index.html?i
navigator.serviceWorker.register(url);
}
},{"b04459cc43e56e8c":"jblPb","@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3"}],"jblPb":[function(require,module,exports,__globalThis) {
module.exports = require("a15a43021d40e52d").getBundleURL('bgzJG') + "sw-b71.41cdff1b.js";
},{"b04459cc43e56e8c":"pb85M","@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3"}],"pb85M":[function(require,module,exports,__globalThis) {
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");
parcelHelpers.defineInteropFlag(exports);
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);
}
}
function explosionAt(gameState, index, x, y, ball) {
const size = 1 + gameState.perks.bigger_explosions + Math.max(1, gameState.perks.implosions) - 1;
function explosionAt(gameState, index, x, y, ball, extraSize = 0) {
const size = 1 + gameState.perks.bigger_explosions + Math.max(0, gameState.perks.implosions - 1) + extraSize;
schedulGameSound(gameState, "explode", ball.x, 1);
if (index !== -1) {
const col = index % gameState.gridSize;
@ -2771,7 +2789,7 @@ function explodeBrick(gameState, index, ball, isExplosion) {
// resetCombo(gameState, x, y);
// }
setBrick(gameState, index, "");
explosionAt(gameState, index, x, y, ball);
explosionAt(gameState, index, x, y, ball, 0);
} else if (color) {
// Even if it bounces we don't want to count that as a miss
// Flashing is take care of by the tick loop
@ -2924,7 +2942,7 @@ async function setLevel(gameState, l) {
gameState.upgradesOfferedFor = l;
(0, _game.pause)(false);
(0, _recording.stopRecording)();
if (l > 0) await (0, _game.openShortRunUpgradesPicker)(gameState);
if (l > 0) await (0, _game.openUpgradesPicker)(gameState);
gameState.currentLevel = l;
gameState.level = gameState.runLevels[l];
gameState.levelTime = 0;
@ -2934,6 +2952,7 @@ async function setLevel(gameState, l) {
gameState.lastTickDown = gameState.levelTime;
gameState.levelStartScore = gameState.score;
gameState.levelSpawnedCoins = 0;
gameState.levelLostCoins = 0;
gameState.levelMisses = 0;
gameState.runStatistics.levelsPlayed++;
// Reset combo silently
@ -2946,7 +2965,7 @@ async function setLevel(gameState, l) {
gameState.gridSize = lvl.size;
(0, _game.fitSize)();
}
empty(gameState.coins);
gameState.levelLostCoins += empty(gameState.coins);
empty(gameState.particles);
empty(gameState.lights);
empty(gameState.texts);
@ -3012,7 +3031,13 @@ function coinBrickHitCheck(gameState, coin) {
const vhit = (0, _game.hitsSomething)(previousX, y, radius);
const hhit = (0, _game.hitsSomething)(x, previousY, radius);
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") {
coin.y = coin.previousY;
coin.vy *= -1;
@ -3102,7 +3127,7 @@ frames = 1) {
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;
});
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.vx *= ratio;
if (coin.vx > 7 * gameState.baseSpeed) coin.vx = 7 * gameState.baseSpeed;
@ -3123,10 +3148,14 @@ frames = 1) {
addToScore(gameState, coin);
destroy(gameState.coins, coinIndex);
} else if (coin.y > gameState.canvasHeight + coinRadius) {
gameState.levelLostCoins += coin.points;
destroy(gameState.coins, coinIndex);
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
destroy(gameState.coins, coinIndex);
} 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
gameState.levelLostCoins += coin.points;
destroy(gameState.coins, coinIndex);
}
const hitBrick = coinBrickHitCheck(gameState, coin);
if (gameState.perks.metamorphosis && typeof hitBrick !== "undefined") {
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.vy += (0, _gameUtils.clamp)(b.y - y, -limit, limit) + (Math.random() - 0.5) * limit / 3;
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) {
@ -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 (ballIsUnderPuck) {
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.vy = speed * Math.sin(angle);
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) {
let weight = 0.8 + Math.random() * 0.2 + Math.min(2, points * 0.01);
weight *= 5 / (5 + gameState.perks.etherealcoins);
append(gameState.coins, (p)=>{
p.x = x;
p.y = y;
@ -3451,9 +3481,16 @@ function liveCount(where) {
return where.total;
}
function empty(where) {
let destroyed = 0;
where.total = 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) {
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");
parcelHelpers.defineInteropFlag(exports);
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">
<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>`);
bombSVG.onload = ()=>(0, _game.gameState).needsRender = true;
const background = document.createElement("img");
const backgroundCanvas = document.createElement("canvas");
function render(gameState) {
@ -3510,7 +3548,21 @@ function render(gameState) {
max: (0, _gameUtils.max_levels)(gameState)
});
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" : "";
// Clear
if (!(0, _options.isOptionOn)("basic") && !level.color && level.svg) {
@ -3556,10 +3608,23 @@ function render(gameState) {
const bgctx = backgroundCanvas.getContext("2d");
bgctx.fillStyle = level.color || "#000";
bgctx.fillRect(0, 0, gameState.canvasWidth, gameState.canvasHeight);
const pattern = ctx.createPattern(background, "repeat");
if (pattern) {
bgctx.fillStyle = pattern;
bgctx.fillRect(0, 0, width, height);
if (gameState.perks.clairvoyant >= 3) {
const pageSource = document.body.innerHTML.replace(/\s+/gi, '');
const lineWidth = Math.ceil(gameState.canvasWidth / 15);
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);
@ -3596,7 +3661,7 @@ function render(gameState) {
ctx.globalCompositeOperation = "source-over";
// ctx.globalCompositeOperation =
// 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
if (!(0, _options.isOptionOn)("basic")) {
@ -3613,7 +3678,7 @@ function render(gameState) {
const { x, y, time, color, size, duration } = flash;
const elapsed = gameState.levelTime - time;
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";
(0, _gameStateMutators.forEachLiveOne)(gameState.texts, (flash)=>{
@ -3663,7 +3728,7 @@ function render(gameState) {
// The puck
ctx.globalAlpha = 1;
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) {
ctx.globalCompositeOperation = "source-over";
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 redBorder = (0, _game.gameState).ballsColor !== color && color !== "black" && redBorderOnBricksWithWrongColor || hasCombo && (0, _game.gameState).perks.zen && color === "black" || redBecauseOfReach || redColorOnAllBricks;
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) {
canctx.globalCompositeOperation = "destination-out";
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);
}
let cachedGraphics = {};
function drawPuck(ctx, color, puckWidth, puckHeight, yOffset = 0, flipped, redBorderOffset) {
const key = "puck" + color + "_" + puckWidth + "_" + puckHeight + "_" + flipped + "_" + redBorderOffset;
function drawPuck(ctx, color, puckWidth, puckHeight, yOffset = 0, concave_puck, redBorderOffset) {
const key = "puck" + color + "_" + puckWidth + "_" + puckHeight + "_" + concave_puck + "_" + redBorderOffset;
if (!cachedGraphics[key]) {
const can = document.createElement("canvas");
can.width = puckWidth;
@ -3767,9 +3832,9 @@ function drawPuck(ctx, color, puckWidth, puckHeight, yOffset = 0, flipped, redBo
canctx.fillStyle = color;
canctx.beginPath();
canctx.moveTo(0, puckHeight * 2);
if (flipped) {
if (concave_puck) {
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);
} else {
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.fillStyle = color;
canctx.fill();
if (color === "gold" || borderColor === "red") {
canctx.strokeStyle = borderColor;
if (borderColor == "red") {
canctx.lineWidth = 2;
canctx.setLineDash(redBorderDash);
}
canctx.stroke();
canctx.strokeStyle = borderColor;
if (borderColor == "red") {
canctx.lineWidth = 2;
canctx.setLineDash(redBorderDash);
}
canctx.stroke();
if (color === "gold") {
// Fill in
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));
}
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 tly = Math.ceil(y - (0, _game.gameState).brickWidth / 2);
const brx = Math.ceil(x + (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 key = "brick" + color + "_" + "_" + width + "_" + height + "_" + offset;
const key = "brick" + color + "_" + "_" + width + "_" + height + "_" + offset + '_' + borderOnly;
if (!cachedGraphics[key]) {
const can = document.createElement("canvas");
can.width = width;
@ -3887,7 +3950,7 @@ function drawBrick(ctx, color, x, y, offset = 0) {
canctx.lineJoin = "round";
canctx.lineWidth = bord;
roundRect(canctx, bord / 2, bord / 2, width - bord, height - bord, cornerRadius);
canctx.fill();
if (!borderOnly) canctx.fill();
canctx.stroke();
cachedGraphics[key] = can;
}
@ -4645,7 +4708,7 @@ function newGameState(params) {
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>
</body>

View file

@ -61,6 +61,17 @@ body {
color: gold;
transition: color 0.01s;
}
span{
color: #333;
&.great{
color: lightgreen;
}
&.good{
color: white;
}
}
}
#menu {
@ -352,3 +363,22 @@ h2.histogram-title strong {
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);
//
//}

View file

@ -160,11 +160,14 @@ setInterval(() => {
fitSize();
}, 1000);
export async function openShortRunUpgradesPicker(gameState: GameState) {
export async function openUpgradesPicker(gameState: GameState) {
const catchRate =
(gameState.score - gameState.levelStartScore) /
(gameState.levelSpawnedCoins || 1);
let repeats = 1;
let timeGain = "",
@ -983,10 +986,16 @@ restart(
(window.location.search.includes("stressTest") && {
level: "Bird",
perks: {
pierce: 1,
sapper: 1,
implosions: 3,
streak_shots:1
shocks:10,
multiball:6,
telekinesis:2,
ghost_coins:1,
pierce:4,
clairvoyant:3,
bigger_explosions:2,
sapper:2,
unbounded:1
},
levelsPerLoop: 2,
}) ||

View file

@ -36,7 +36,7 @@ import {icons, upgrades} from "./loadGameData";
import {addToTotalScore, getCurrentMaxCoins, getCurrentMaxParticles,} from "./settings";
import {background} from "./render";
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 {isOptionOn} from "./options";
import {isPremium} from "./premium";
@ -124,13 +124,13 @@ export function normalizeGameState(gameState: GameState) {
(gameState.gameZoneWidth / 12) *
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 +
gameState.gameZoneWidthRoundedUp -
gameState.puckWidth / 2 + gameState.puckWidth * corner;
let maxX = gameState.offsetXRoundedDown +
gameState.gameZoneWidthRoundedUp -
gameState.puckWidth / 2 + gameState.puckWidth * corner;
gameState.puckPosition = clamp(gameState.puckPosition, minX, maxX);
@ -179,7 +179,7 @@ export function resetCombo(
}
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;
@ -198,7 +198,7 @@ export function decreaseCombo(
if (lost) {
schedulGameSound(gameState, "comboDecrease", x, 1);
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,
y: number,
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);
if (index !== -1) {
const col = index % gameState.gridSize;
@ -292,7 +295,7 @@ export function explosionAt(
if (gameState.perks.implosions) {
spawnImplosion(
gameState,
7 *size ,
7 * size,
x,
y,
"white",
@ -300,7 +303,7 @@ export function explosionAt(
} else {
spawnExplosion(
gameState,
7 *size,
7 * size,
x,
y,
"white",
@ -332,7 +335,7 @@ export function explodeBrick(
// resetCombo(gameState, x, y);
// }
setBrick(gameState, index, "");
explosionAt(gameState, index, x, y, ball);
explosionAt(gameState, index, x, y, ball, 0);
} else if (color) {
// 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);
stopRecording();
if (l > 0) {
await openShortRunUpgradesPicker(gameState);
await openUpgradesPicker(gameState);
}
gameState.currentLevel = l;
@ -629,6 +632,7 @@ export async function setLevel(gameState: GameState, l: number) {
gameState.lastTickDown = gameState.levelTime;
gameState.levelStartScore = gameState.score;
gameState.levelSpawnedCoins = 0;
gameState.levelLostCoins = 0;
gameState.levelMisses = 0;
gameState.runStatistics.levelsPlayed++;
@ -650,7 +654,7 @@ export async function setLevel(gameState: GameState, l: number) {
gameState.gridSize = lvl.size;
fitSize();
}
empty(gameState.coins);
gameState.levelLostCoins += empty(gameState.coins);
empty(gameState.particles);
empty(gameState.lights);
empty(gameState.texts);
@ -798,7 +802,16 @@ export function coinBrickHitCheck(gameState: GameState, coin: Coin) {
typeof hhit == "undefined" &&
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") {
coin.y = coin.previousY;
coin.vy *= -1;
@ -988,7 +1001,7 @@ export function gameStateTick(
(gameState.perks.viscosity *
0.03 +
0.005) *
frames;
frames / (1 + gameState.perks.etherealcoins);
coin.vy *= ratio;
coin.vx *= ratio;
@ -1040,6 +1053,7 @@ export function gameStateTick(
destroy(gameState.coins, coinIndex);
} else if (coin.y > gameState.canvasHeight + coinRadius) {
gameState.levelLostCoins+=coin.points
destroy(gameState.coins, coinIndex);
if (gameState.perks.compound_interest) {
resetCombo(gameState, coin.x, coin.y);
@ -1052,6 +1066,7 @@ export function gameStateTick(
)
) {
// Out of bound on sides
gameState.levelLostCoins+=coin.points
destroy(gameState.coins, coinIndex);
}
@ -1126,7 +1141,7 @@ export function gameStateTick(
((Math.random() - 0.5) * limit) / 3;
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) => {
if (gameState.bricks[r.index]) {
destroy(gameState.respawns, ri)
} else if (gameState.levelTime>r.time) {
} else if (gameState.levelTime > r.time) {
setBrick(gameState, r.index, r.color)
destroy(gameState.respawns, ri)
} else if (!isOptionOn("basic")) {
@ -1435,7 +1450,7 @@ export function ballTick(gameState: GameState, ball: Ball, delta: number) {
const angle = Math.atan2(
-gameState.puckWidth / 2,
(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.vy = speed * Math.sin(angle);
@ -1657,7 +1672,8 @@ function makeCoin(
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: Partial<Coin>) => {
p.x = x;
@ -1718,7 +1734,7 @@ function makeText(
p.y = y;
p.color = color;
p.size = size;
p.duration = clamp(duration,400,2000);
p.duration = clamp(duration, 400, 2000);
p.text = text;
});
}
@ -1776,9 +1792,16 @@ export function liveCount<T>(where: ReusableArray<T>) {
}
export function empty<T>(where: ReusableArray<T>) {
let destroyed=0
where.total = 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>(

View file

@ -23,6 +23,7 @@
<button id="menu"><span id="menuLabel">menu</span></button>
<button id="score"></button>
<div id="FPSDisplay"></div>
<div id="statsdisplay"></div>
<canvas id="game"></canvas>
<div id="popup">
<button id="close-modale"></button>

View file

@ -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">
<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>`);
bombSVG.onload = () => gameState.needsRender = true
export const background = document.createElement("img");
export const backgroundCanvas = document.createElement("canvas");
@ -49,7 +50,25 @@ export function render(gameState: GameState) {
} else {
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 =
gameState.lastScoreIncrease > gameState.levelTime - 500 ? "active" : "";
@ -117,10 +136,30 @@ export function render(gameState: GameState) {
) as CanvasRenderingContext2D;
bgctx.fillStyle = level.color || "#000";
bgctx.fillRect(0, 0, gameState.canvasWidth, gameState.canvasHeight);
const pattern = ctx.createPattern(background, "repeat");
if (pattern) {
bgctx.fillStyle = pattern;
bgctx.fillRect(0, 0, width, height);
if (gameState.perks.clairvoyant >= 3) {
const pageSource = document.body.innerHTML.replace(/\s+/gi, '')
const lineWidth = Math.ceil(gameState.canvasWidth / 15)
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.y,
(hasCombo && gameState.perks.asceticism && "red") ||
(!coin.points && "red") ||
level.color ||
"black",
(coin.color==='gold' && 'gold')||
gameState.puckColor,
coin.a,
);
});
@ -205,7 +243,7 @@ export function render(gameState: GameState) {
const {x, y, time, color, size, duration} = flash;
const elapsed = gameState.levelTime - time;
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";
@ -290,11 +328,11 @@ export function render(gameState: GameState) {
drawPuck(
ctx,
gameState.puckColor,
gameState.puckColor,
gameState.puckWidth,
gameState.puckHeight,
0,
!!gameState.perks.concave_puck,
gameState.perks.concave_puck,
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(
ctx,
gameState,
@ -422,7 +460,7 @@ export function render(gameState: GameState) {
1,
);
ctx.globalAlpha=1
ctx.globalAlpha = 1
drawStraightLine(
ctx,
gameState,
@ -448,6 +486,7 @@ export function render(gameState: GameState) {
);
}
if (shaked) {
ctx.resetTransform();
}
@ -559,7 +598,7 @@ export function renderAllBricks() {
countBricksAbove(gameState, index) &&
!countBricksBelow(gameState, index);
let redBorder = (gameState.ballsColor !== color &&
let redBorder = (gameState.ballsColor !== color &&
color !== "black" &&
redBorderOnBricksWithWrongColor) ||
(hasCombo && gameState.perks.zen && color === "black") ||
@ -567,7 +606,8 @@ export function renderAllBricks() {
redColorOnAllBricks;
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) {
canctx.globalCompositeOperation = "destination-out";
drawText(
@ -598,7 +638,7 @@ export function drawPuck(
puckWidth: number,
puckHeight: number,
yOffset = 0,
flipped: boolean,
concave_puck: number,
redBorderOffset: number,
) {
const key =
@ -609,7 +649,7 @@ export function drawPuck(
"_" +
puckHeight +
"_" +
flipped +
concave_puck +
"_" +
redBorderOffset;
@ -623,13 +663,13 @@ export function drawPuck(
canctx.beginPath();
canctx.moveTo(0, puckHeight * 2);
if (flipped) {
if (concave_puck) {
canctx.lineTo(0, puckHeight * 0.75);
canctx.bezierCurveTo(
puckWidth / 2,
puckHeight,
puckHeight * (2 + concave_puck) / 3,
puckWidth / 2,
puckHeight * 1,
puckHeight * (2 + concave_puck) / 3,
puckWidth,
puckHeight * 0.75,
);
@ -741,14 +781,12 @@ export function drawCoin(
canctx.fillStyle = color;
canctx.fill();
if (color === "gold" || borderColor === "red") {
canctx.strokeStyle = borderColor;
if (borderColor == "red") {
canctx.lineWidth = 2;
canctx.setLineDash(redBorderDash);
}
canctx.stroke();
canctx.strokeStyle = borderColor;
if (borderColor == "red") {
canctx.lineWidth = 2;
canctx.setLineDash(redBorderDash);
}
canctx.stroke();
if (color === "gold") {
// Fill in
@ -817,6 +855,7 @@ export function drawBrick(
x: number,
y: number,
offset: number = 0,
borderOnly: boolean
) {
const tlx = Math.ceil(x - gameState.brickWidth / 2);
const tly = Math.ceil(y - gameState.brickWidth / 2);
@ -825,7 +864,7 @@ export function drawBrick(
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]) {
const can = document.createElement("canvas");
@ -850,7 +889,9 @@ export function drawBrick(
height - bord,
cornerRadius,
);
canctx.fill();
if (!borderOnly) {
canctx.fill();
}
canctx.stroke();
cachedGraphics[key] = can;

1
src/types.d.ts vendored
View file

@ -244,6 +244,7 @@ export type GameState = {
levelStartScore: number;
levelMisses: number;
levelSpawnedCoins: number;
levelLostCoins: number;
// MAX_COINS: number;
// MAX_PARTICLES: number;