diff --git a/Readme.md b/Readme.md
index 2896eb5..0084314 100644
--- a/Readme.md
+++ b/Readme.md
@@ -14,6 +14,8 @@ Break colourful bricks, catch bouncing coins and select powerful upgrades !
# Changelog
## To do
+- loosing ball is ok if in win timeout period
+-
## Done
- delayed start on mobile
diff --git a/dist/index.html b/dist/index.html
index 065c5f0..0cae984 100644
--- a/dist/index.html
+++ b/dist/index.html
@@ -607,12 +607,15 @@ h2.histogram-title strong {
}
.toast.big {
- opacity: .9;
+ opacity: .8;
+ text-shadow: 2px 0 #000, -2px 0 #000, 0 2px #000, 0 -2px #000, 1px 1px #000, -1px -1px #000, 1px -1px #000, -1px 1px #000;
background: none;
border: none;
font-size: 60px;
+ font-weight: bold;
top: 50vh;
left: 50vw;
+ transform: translate(-50%, -50%);
}
.gridEdit > div > span, .palette > span {
@@ -1011,8 +1014,11 @@ function startPlayCountDown() {
(0, _toast.toast)("GO", "big");
play();
}, 3000));
+ timers.push(setTimeout(()=>(0, _toast.clearToasts)(), 3500));
}
function stopPlayCountDown() {
+ if (!timers.length) return;
+ (0, _toast.clearToasts)();
timers.forEach((id)=>clearTimeout(id));
timers.length = 0;
}
@@ -2949,18 +2955,23 @@ async function askForPersistentStorage() {
var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js");
parcelHelpers.defineInteropFlag(exports);
parcelHelpers.export(exports, "toast", ()=>toast);
+parcelHelpers.export(exports, "clearToasts", ()=>clearToasts);
let div = document.createElement("div");
div.classList = "hidden toast";
document.body.appendChild(div);
let timeout;
function toast(html, className = "") {
+ clearToasts();
div.classList = "toast visible " + className;
div.innerHTML = html;
- if (timeout) clearTimeout(timeout);
- timeout = setTimeout(()=>{
+ timeout = setTimeout(clearToasts, 1500);
+}
+function clearToasts() {
+ if (timeout) {
+ clearTimeout(timeout);
timeout = undefined;
- div.classList = "hidden toast";
- }, 1500);
+ }
+ div.classList = "hidden toast";
}
},{"@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3"}],"gkKU3":[function(require,module,exports,__globalThis) {
@@ -3512,11 +3523,15 @@ parcelHelpers.export(exports, "getCornerOffset", ()=>getCornerOffset);
parcelHelpers.export(exports, "isInWebView", ()=>isInWebView);
parcelHelpers.export(exports, "hoursSpentPlaying", ()=>hoursSpentPlaying);
parcelHelpers.export(exports, "escapeAttribute", ()=>escapeAttribute);
+parcelHelpers.export(exports, "canvasCenterX", ()=>canvasCenterX);
+parcelHelpers.export(exports, "zoneLeftBorderX", ()=>zoneLeftBorderX);
+parcelHelpers.export(exports, "zoneRightBorderX", ()=>zoneRightBorderX);
var _loadGameData = require("./loadGameData");
var _i18N = require("./i18n/i18n");
var _pureFunctions = require("./pure_functions");
var _settings = require("./settings");
var _options = require("./options");
+var _render = require("./render");
function describeLevel(level) {
let bricks = 0, colors = new Set(), bombs = 0;
level.bricks.forEach((color)=>{
@@ -3755,55 +3770,643 @@ function hoursSpentPlaying() {
function escapeAttribute(str) {
return str.replace(/&/gi, "&").replace(/gameCanvas);
+parcelHelpers.export(exports, "ctx", ()=>ctx);
+parcelHelpers.export(exports, "bombSVG", ()=>bombSVG);
+parcelHelpers.export(exports, "background", ()=>background);
+parcelHelpers.export(exports, "backgroundCanvas", ()=>backgroundCanvas);
+parcelHelpers.export(exports, "haloCanvas", ()=>haloCanvas);
+parcelHelpers.export(exports, "getHaloScale", ()=>getHaloScale);
+parcelHelpers.export(exports, "render", ()=>render);
+parcelHelpers.export(exports, "renderAllBricks", ()=>renderAllBricks);
+parcelHelpers.export(exports, "drawPuck", ()=>drawPuck);
+parcelHelpers.export(exports, "drawBall", ()=>drawBall);
+parcelHelpers.export(exports, "drawCoin", ()=>drawCoin);
+parcelHelpers.export(exports, "drawFuzzyBall", ()=>drawFuzzyBall);
+parcelHelpers.export(exports, "drawBrick", ()=>drawBrick);
+parcelHelpers.export(exports, "roundRect", ()=>roundRect);
+parcelHelpers.export(exports, "drawIMG", ()=>drawIMG);
+parcelHelpers.export(exports, "drawText", ()=>drawText);
+parcelHelpers.export(exports, "scoreDisplay", ()=>scoreDisplay);
+parcelHelpers.export(exports, "getDashOffset", ()=>getDashOffset);
+var _gameStateMutators = require("./gameStateMutators");
+var _gameUtils = require("./game_utils");
+var _i18N = require("./i18n/i18n");
+var _game = require("./game");
+var _options = require("./options");
+var _pureFunctions = require("./pure_functions");
+const gameCanvas = document.getElementById("game");
+const ctx = gameCanvas.getContext("2d", {
+ alpha: false
+});
+const bombSVG = document.createElement("img");
+bombSVG.src = "data:image/svg+xml;base64," + btoa(``);
+bombSVG.onload = ()=>(0, _game.gameState).needsRender = true;
+const background = document.createElement("img");
+background.onload = ()=>(0, _game.gameState).needsRender = true;
+const backgroundCanvas = document.createElement("canvas");
+const haloCanvas = document.createElement("canvas");
+const haloCanvasCtx = haloCanvas.getContext("2d", {
+ alpha: false
+});
+function getHaloScale() {
+ return 16 * ((0, _options.isOptionOn)("precise_lighting") ? 1 : 2);
}
-
-},{"b04459cc43e56e8c":"17ciJ","@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3"}],"17ciJ":[function(require,module,exports,__globalThis) {
-module.exports = require("9c7c7951fd7c4db6").getBundleURL('arAGi') + "sw-b71.41cdff1b.js";
-
-},{"9c7c7951fd7c4db6":"lgJ39"}],"lgJ39":[function(require,module,exports,__globalThis) {
-"use strict";
-var bundleURL = {};
-function getBundleURLCached(id) {
- var value = bundleURL[id];
- if (!value) {
- value = getBundleURL();
- bundleURL[id] = value;
+let framesCounter = 0;
+function render(gameState) {
+ framesCounter++;
+ (0, _game.startWork)("render:init");
+ const level = (0, _gameUtils.currentLevelInfo)(gameState);
+ const hasCombo = gameState.combo > (0, _gameStateMutators.baseCombo)(gameState);
+ const { width, height } = gameCanvas;
+ if (!width || !height) return;
+ if (gameState.currentLevel || gameState.levelTime) menuLabel.innerText = (0, _i18N.t)("play.current_lvl", {
+ level: gameState.currentLevel + 1,
+ max: (0, _gameUtils.max_levels)(gameState)
+ });
+ else menuLabel.innerText = (0, _i18N.t)("play.menu_label");
+ const catchRate = gameState.levelSpawnedCoins ? gameState.levelCoughtCoins / (gameState.levelSpawnedCoins || 1) : // gameState.levelSpawnedCoins
+ 1;
+ (0, _game.startWork)("render:scoreDisplay");
+ scoreDisplay.innerHTML = ((0, _options.isOptionOn)("show_fps") || gameState.startParams.computer_controlled ? `
+
+ ${0, _game.lastMeasuredFPS} FPS
+ /
+ ` : "") + ((0, _options.isOptionOn)("show_stats") ? `
+ (0, _pureFunctions.catchRateGood) / 100 && "good" || ""}" data-tooltip="${(0, _i18N.t)("play.stats.coins_catch_rate")}">
+ ${Math.floor(catchRate * 100)}%
+ /
+
+ ${Math.ceil(gameState.levelTime / 1000)}s
+ /
+
+ ${gameState.levelMisses} M
+ /
+ ` : "") + `$${gameState.score}`;
+ scoreDisplay.classList[gameState.startParams.computer_controlled ? "add" : "remove"]("computer_controlled");
+ scoreDisplay.classList[gameState.lastScoreIncrease > gameState.levelTime - 500 ? "add" : "remove"]("active");
+ // Clear
+ if (!(0, _options.isOptionOn)("basic") && level.svg && level.color === "#000000") {
+ const skipN = (0, _options.isOptionOn)("probabilistic_lighting") && (0, _gameStateMutators.liveCount)(gameState.coins) > 150 ? 3 : 0;
+ const shouldSkip = (index)=>skipN ? (framesCounter + index) % (skipN + 1) !== 0 : false;
+ const haloScale = getHaloScale();
+ (0, _game.startWork)("render:halo:clear");
+ haloCanvasCtx.globalCompositeOperation = "source-over";
+ haloCanvasCtx.globalAlpha = skipN ? 0.1 : 0.99;
+ haloCanvasCtx.fillStyle = level.color;
+ haloCanvasCtx.fillRect(0, 0, width / haloScale, height / haloScale);
+ const brightness = (0, _options.isOptionOn)("extra_bright") ? 3 : 1;
+ haloCanvasCtx.globalCompositeOperation = "lighten";
+ haloCanvasCtx.globalAlpha = 0.1 + 5 / ((0, _gameStateMutators.liveCount)(gameState.coins) + 10);
+ (0, _game.startWork)("render:halo:coins");
+ (0, _gameStateMutators.forEachLiveOne)(gameState.coins, (coin, index)=>{
+ if (shouldSkip(index)) return;
+ const color = (0, _gameUtils.getCoinRenderColor)(gameState, coin);
+ drawFuzzyBall(haloCanvasCtx, color, gameState.coinSize * 2 * brightness / haloScale, coin.x / haloScale, coin.y / haloScale);
+ });
+ (0, _game.startWork)("render:halo:balls");
+ gameState.balls.forEach((ball, index)=>{
+ if (shouldSkip(index)) return;
+ haloCanvasCtx.globalAlpha = 0.3 * (1 - (0, _pureFunctions.ballTransparency)(ball, gameState));
+ drawFuzzyBall(haloCanvasCtx, gameState.ballsColor, gameState.ballSize * 2 * brightness / haloScale, ball.x / haloScale, ball.y / haloScale);
+ });
+ (0, _game.startWork)("render:halo:bricks");
+ haloCanvasCtx.globalAlpha = 0.05;
+ gameState.bricks.forEach((color, index)=>{
+ if (!color) return;
+ if (shouldSkip(index)) return;
+ const x = (0, _gameUtils.brickCenterX)(gameState, index), y = (0, _gameUtils.brickCenterY)(gameState, index);
+ drawFuzzyBall(haloCanvasCtx, color == "black" ? "#666666" : color, // Perf could really go down there because of the size of the halo
+ Math.min(200, gameState.brickWidth * 1.5 * brightness) / haloScale, x / haloScale, y / haloScale);
+ });
+ (0, _game.startWork)("render:halo:particles");
+ haloCanvasCtx.globalCompositeOperation = "screen";
+ (0, _gameStateMutators.forEachLiveOne)(gameState.particles, (flash, index)=>{
+ if (shouldSkip(index)) return;
+ const { x, y, time, color, size, duration } = flash;
+ const elapsed = gameState.levelTime - time;
+ haloCanvasCtx.globalAlpha = 0.1 * Math.min(1, 2 - elapsed / duration * 2);
+ drawFuzzyBall(haloCanvasCtx, color, size * 3 * brightness / haloScale, x / haloScale, y / haloScale);
+ });
+ (0, _game.startWork)("render:halo:scale_up");
+ ctx.globalAlpha = 1;
+ ctx.globalCompositeOperation = "source-over";
+ ctx.imageSmoothingQuality = "high";
+ ctx.imageSmoothingEnabled = (0, _options.isOptionOn)("smooth_lighting") || false;
+ ctx.drawImage(haloCanvas, 0, 0, width, height);
+ ctx.imageSmoothingEnabled = false;
+ (0, _game.startWork)("render:halo:pattern");
+ ctx.globalAlpha = 1;
+ ctx.globalCompositeOperation = "multiply";
+ if (level.svg && background.width && background.complete) {
+ if (backgroundCanvas.title !== level.name) {
+ backgroundCanvas.title = level.name;
+ backgroundCanvas.width = gameState.canvasWidth;
+ backgroundCanvas.height = gameState.canvasHeight;
+ const bgctx = backgroundCanvas.getContext("2d");
+ bgctx.globalCompositeOperation = "source-over";
+ bgctx.fillStyle = level.color || "#000";
+ bgctx.fillRect(0, 0, gameState.canvasWidth, gameState.canvasHeight);
+ 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 = "#FFFFFF";
+ 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.globalCompositeOperation = "screen";
+ bgctx.fillStyle = pattern;
+ bgctx.fillRect(0, 0, width, height);
+ }
+ }
+ }
+ ctx.globalCompositeOperation = "darken";
+ ctx.drawImage(backgroundCanvas, 0, 0);
+ } else {
+ // Background not loaded yes
+ ctx.fillStyle = "#000";
+ ctx.fillRect(0, 0, width, height);
+ }
+ } else {
+ (0, _game.startWork)("render:halo-basic");
+ ctx.globalAlpha = 1;
+ ctx.globalCompositeOperation = "source-over";
+ ctx.fillStyle = level.color || "#000";
+ ctx.fillRect(0, 0, width, height);
+ (0, _gameStateMutators.forEachLiveOne)(gameState.particles, (flash)=>{
+ const { x, y, time, color, size, duration } = flash;
+ const elapsed = gameState.levelTime - time;
+ ctx.globalAlpha = Math.min(1, 2 - elapsed / duration * 2);
+ drawBall(ctx, color, size, x, y);
+ });
}
- return value;
-}
-function getBundleURL() {
- try {
- throw new Error();
- } catch (err) {
- var matches = ('' + err.stack).match(/(https?|file|ftp|(chrome|moz|safari-web)-extension):\/\/[^)\n]+/g);
- if (matches) // The first two stack frames will be this function and getBundleURLCached.
- // Use the 3rd one, which will be a runtime in the original bundle.
- return getBaseURL(matches[2]);
+ (0, _game.startWork)("render:explosionshake");
+ ctx.globalAlpha = 1;
+ ctx.globalCompositeOperation = "source-over";
+ const lastExplosionDelay = gameState.levelTime - gameState.lastExplosion + 5;
+ const shaked = lastExplosionDelay < 200 && !(0, _options.isOptionOn)("basic") && // Otherwise, if you pause after an explosion, moving the mouses shakes the picture
+ gameState.running;
+ if (shaked) {
+ const amplitude = (gameState.perks.bigger_explosions + 1) * 50 / lastExplosionDelay;
+ ctx.translate(Math.sin(Date.now()) * amplitude, Math.sin(Date.now() + 36) * amplitude);
}
- return '/';
+ (0, _game.startWork)("render:coins");
+ // Coins
+ ctx.globalAlpha = 1;
+ (0, _gameStateMutators.forEachLiveOne)(gameState.coins, (coin)=>{
+ const color = (0, _gameUtils.getCoinRenderColor)(gameState, coin);
+ const hollow = gameState.perks.metamorphosis && !coin.metamorphosisPoints;
+ ctx.globalCompositeOperation = "source-over";
+ drawCoin(ctx, hollow ? "transparent" : color, coin.size, coin.x, coin.y, // Red border around coins with asceticism
+ hasCombo && gameState.perks.asceticism && "#FF0000" || // Gold coins
+ // (color === "#ffd300" && "#ffd300") ||
+ hollow && color || gameState.level.color, coin.a);
+ });
+ (0, _game.startWork)("render:ball shade");
+ // Black shadow around balls
+ ctx.globalCompositeOperation = "source-over";
+ gameState.balls.forEach((ball)=>{
+ ctx.globalAlpha = Math.min(0.8, (0, _gameStateMutators.liveCount)(gameState.coins) / 20) * (1 - (0, _pureFunctions.ballTransparency)(ball, gameState));
+ drawBall(ctx, level.color || "#000", gameState.ballSize * 6, ball.x, ball.y);
+ });
+ (0, _game.startWork)("render:bricks");
+ ctx.globalCompositeOperation = "source-over";
+ renderAllBricks();
+ (0, _game.startWork)("render:lights");
+ ctx.globalCompositeOperation = "screen";
+ (0, _gameStateMutators.forEachLiveOne)(gameState.lights, (flash)=>{
+ 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(gameState, ctx, color, x, y, -1, gameState.perks.clairvoyant >= 2);
+ });
+ (0, _game.startWork)("render:texts");
+ ctx.globalCompositeOperation = "screen";
+ (0, _gameStateMutators.forEachLiveOne)(gameState.texts, (flash)=>{
+ const { x, y, time, color, size, duration } = flash;
+ const elapsed = gameState.levelTime - time;
+ ctx.globalAlpha = Math.max(0, Math.min(1, 2 - elapsed / duration * 2));
+ ctx.globalCompositeOperation = "source-over";
+ drawText(ctx, flash.text, color, size, x, y - elapsed / 10);
+ });
+ (0, _game.startWork)("render:particles");
+ (0, _gameStateMutators.forEachLiveOne)(gameState.particles, (particle)=>{
+ const { x, y, time, color, size, duration } = particle;
+ const elapsed = gameState.levelTime - time;
+ ctx.globalAlpha = Math.max(0, Math.min(1, 2 - elapsed / duration * 2));
+ ctx.globalCompositeOperation = "screen";
+ drawBall(ctx, color, size, x, y);
+ });
+ //
+ (0, _game.startWork)("render:extra_life");
+ if (gameState.perks.extra_life) {
+ ctx.globalAlpha = gameState.balls.length > 1 ? 0.2 : 1;
+ ctx.globalCompositeOperation = "source-over";
+ ctx.fillStyle = gameState.puckColor;
+ for(let i = 0; i < gameState.perks.extra_life; i++)ctx.fillRect(gameState.offsetXRoundedDown, gameState.gameZoneHeight - gameState.puckHeight / 2 + 2 * i, gameState.gameZoneWidthRoundedUp, 1);
+ }
+ (0, _game.startWork)("render:balls");
+ ctx.globalAlpha = 1;
+ ctx.globalCompositeOperation = "source-over";
+ gameState.balls.forEach((ball)=>{
+ const drawingColor = gameState.ballsColor;
+ const ballAlpha = 1 - (0, _pureFunctions.ballTransparency)(ball, gameState);
+ ctx.globalAlpha = ballAlpha;
+ // The white border around is to distinguish colored balls from coins/bg
+ drawBall(ctx, drawingColor, gameState.ballSize, ball.x, ball.y, gameState.puckColor);
+ if ((0, _gameUtils.telekinesisEffectRate)(gameState, ball) || (0, _gameUtils.yoyoEffectRate)(gameState, ball)) {
+ ctx.beginPath();
+ ctx.moveTo(gameState.puckPosition, gameState.gameZoneHeight);
+ ctx.globalAlpha = (0, _pureFunctions.clamp)(Math.max((0, _gameUtils.telekinesisEffectRate)(gameState, ball), (0, _gameUtils.yoyoEffectRate)(gameState, ball)) * ballAlpha, 0, 1);
+ ctx.strokeStyle = gameState.puckColor;
+ ctx.bezierCurveTo(gameState.puckPosition, gameState.gameZoneHeight, gameState.puckPosition, ball.y, ball.x, ball.y);
+ ctx.stroke();
+ ctx.lineWidth = 2;
+ ctx.setLineDash(emptyArray);
+ }
+ ctx.globalAlpha = 1;
+ if (gameState.perks.clairvoyant && gameState.ballStickToPuck || gameState.perks.steering > 1 && !gameState.ballStickToPuck) {
+ ctx.strokeStyle = gameState.ballsColor;
+ ctx.beginPath();
+ ctx.moveTo(ball.x, ball.y);
+ ctx.lineTo(ball.x + ball.vx * 10, ball.y + ball.vy * 10);
+ ctx.stroke();
+ }
+ });
+ (0, _game.startWork)("render:puck");
+ ctx.globalAlpha = (0, _gameUtils.isMovingWhilePassiveIncome)(gameState) ? 0.2 : 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);
+ (0, _game.startWork)("render:combotext");
+ const spawns = (0, _pureFunctions.coinsBoostedCombo)(gameState);
+ if (spawns > 1 && !(0, _gameUtils.isMovingWhilePassiveIncome)(gameState)) {
+ ctx.globalCompositeOperation = "source-over";
+ ctx.globalAlpha = 1;
+ const comboText = spawns.toString();
+ const comboTextWidth = comboText.length * gameState.puckHeight / 1.8;
+ const totalWidth = comboTextWidth + gameState.coinSize * 2;
+ const left = gameState.puckPosition - totalWidth / 2;
+ ctx.globalAlpha = gameState.combo > (0, _gameStateMutators.baseCombo)(gameState) ? 1 : 0.3;
+ if (totalWidth < gameState.puckWidth) {
+ drawText(ctx, comboText, "#000", gameState.puckHeight, left + gameState.coinSize * 1.5, gameState.gameZoneHeight - gameState.puckHeight / 2, true);
+ ctx.globalAlpha = 1;
+ drawCoin(ctx, "#ffd300", gameState.coinSize, left + gameState.coinSize / 2, gameState.gameZoneHeight - gameState.puckHeight / 2, "#ffd300", 0);
+ } else drawText(ctx, comboTextWidth > gameState.puckWidth ? gameState.combo.toString() : comboText, "#000", comboTextWidth > gameState.puckWidth ? 12 : 20, gameState.puckPosition, gameState.gameZoneHeight - gameState.puckHeight / 2, false);
+ }
+ (0, _game.startWork)("render:borders");
+ // Borders
+ ctx.globalCompositeOperation = "source-over";
+ ctx.globalAlpha = 1;
+ let redLeftSide = hasCombo && (gameState.perks.left_is_lava || gameState.perks.trampoline);
+ let redRightSide = hasCombo && (gameState.perks.right_is_lava || gameState.perks.trampoline);
+ let redTop = hasCombo && (gameState.perks.top_is_lava || gameState.perks.trampoline);
+ if (gameState.offsetXRoundedDown) {
+ // draw outside of gaming area to avoid capturing borders in recordings
+ if (gameState.perks.left_is_lava < 2) drawStraightLine(ctx, gameState, redLeftSide && "#FF0000" || "#FFFFFF", (0, _gameUtils.zoneLeftBorderX)(gameState), 0, (0, _gameUtils.zoneLeftBorderX)(gameState), height, 1);
+ if (gameState.perks.right_is_lava < 2) drawStraightLine(ctx, gameState, redRightSide && "#FF0000" || "#FFFFFF", (0, _gameUtils.zoneRightBorderX)(gameState), 0, (0, _gameUtils.zoneRightBorderX)(gameState), height, 1);
+ } else {
+ if (gameState.perks.left_is_lava < 2) drawStraightLine(ctx, gameState, redLeftSide && "#FF0000" || "", 0, 0, 0, height, 1);
+ if (gameState.perks.right_is_lava < 2) drawStraightLine(ctx, gameState, redRightSide && "#FF0000" || "", width - 1, 0, width - 1, height, 1);
+ }
+ if (redTop && gameState.perks.top_is_lava < 2) drawStraightLine(ctx, gameState, "#FF0000", (0, _gameUtils.zoneLeftBorderX)(gameState), 1, (0, _gameUtils.zoneRightBorderX)(gameState), 1, 1);
+ (0, _game.startWork)("render:bottom_line");
+ ctx.globalAlpha = 1;
+ const corner = (0, _gameUtils.getCornerOffset)(gameState);
+ const bottomLineIsRed = hasCombo && gameState.perks.compound_interest;
+ drawStraightLine(ctx, gameState, bottomLineIsRed && "#FF0000" || (0, _options.isOptionOn)("mobile-mode") && "#FFFFFF" || corner && "#FFFFFF" || "", gameState.offsetXRoundedDown - corner, gameState.gameZoneHeight - 1, width - gameState.offsetXRoundedDown + corner, gameState.gameZoneHeight - 1, bottomLineIsRed ? 1 : 0.5);
+ (0, _game.startWork)("render:contrast");
+ if (!(0, _options.isOptionOn)("basic") && (0, _options.isOptionOn)("contrast") && level.svg && level.color === "#000000") {
+ ctx.imageSmoothingEnabled = (0, _options.isOptionOn)("smooth_lighting") || false;
+ if ((0, _options.isOptionOn)("probabilistic_lighting")) {
+ ctx.globalAlpha = 1;
+ ctx.globalCompositeOperation = "soft-light";
+ } else {
+ haloCanvasCtx.fillStyle = "#FFFFFF";
+ haloCanvasCtx.globalAlpha = 0.25;
+ haloCanvasCtx.globalCompositeOperation = "screen";
+ haloCanvasCtx.fillRect(0, 0, haloCanvas.width, haloCanvas.height);
+ ctx.globalAlpha = 1;
+ ctx.globalCompositeOperation = "overlay";
+ }
+ ctx.drawImage(haloCanvas, 0, 0, width, height);
+ ctx.imageSmoothingEnabled = false;
+ }
+ (0, _game.startWork)("render:text_under_puck");
+ ctx.globalCompositeOperation = "source-over";
+ ctx.globalAlpha = 1;
+ if ((0, _options.isOptionOn)("mobile-mode") && gameState.startParams.computer_controlled) drawText(ctx, "breakout.lecaro.me?autoplay", gameState.puckColor, gameState.puckHeight, gameState.canvasWidth / 2, gameState.gameZoneHeight + (gameState.canvasHeight - gameState.gameZoneHeight) / 2);
+ if ((0, _options.isOptionOn)("mobile-mode") && !gameState.running) drawText(ctx, (0, _i18N.t)("play.mobile_press_to_play"), gameState.puckColor, gameState.puckHeight, gameState.canvasWidth / 2, gameState.gameZoneHeight + (gameState.canvasHeight - gameState.gameZoneHeight) / 2);
+ (0, _game.startWork)("render:askForWakeLock");
+ askForWakeLock(gameState);
+ (0, _game.startWork)("render:resetTransform");
+ if (shaked) ctx.resetTransform();
}
-function getBaseURL(url) {
- return ('' + url).replace(/^((?:https?|file|ftp|(chrome|moz|safari-web)-extension):\/\/.+)\/[^/]+$/, '$1') + '/';
+function drawStraightLine(ctx, gameState, mode, x1, y1, x2, y2, alpha = 1) {
+ ctx.globalAlpha = alpha;
+ if (!mode) return;
+ x1 = Math.round(x1);
+ y1 = Math.round(y1);
+ x2 = Math.round(x2);
+ y2 = Math.round(y2);
+ if (mode == "#FF0000") {
+ ctx.strokeStyle = "red";
+ ctx.lineDashOffset = getDashOffset(gameState);
+ ctx.lineWidth = 2;
+ ctx.setLineDash(redBorderDash);
+ ctx.beginPath();
+ ctx.moveTo(x1, y1);
+ ctx.lineTo(x2, y2);
+ ctx.stroke();
+ ctx.setLineDash(emptyArray);
+ ctx.lineWidth = 1;
+ } else {
+ ctx.fillStyle = mode;
+ ctx.fillRect(Math.min(x1, x2), Math.min(y1, y2), Math.max(1, Math.abs(x1 - x2)), Math.max(1, Math.abs(y1 - y2)));
+ }
+ mode;
+ ctx.globalAlpha = 1;
}
-// TODO: Replace uses with `new URL(url).origin` when ie11 is no longer supported.
-function getOrigin(url) {
- var matches = ('' + url).match(/(https?|file|ftp|(chrome|moz|safari-web)-extension):\/\/[^/]+/);
- if (!matches) throw new Error('Origin not found');
- return matches[0];
+let cachedBricksRender = document.createElement("canvas");
+let cachedBricksRenderKey = "";
+function renderAllBricks() {
+ ctx.globalAlpha = 1;
+ const hasCombo = (0, _game.gameState).combo > (0, _gameStateMutators.baseCombo)((0, _game.gameState));
+ const redBorderOnBricksWithWrongColor = hasCombo && (0, _game.gameState).perks.picky_eater && (0, _gameUtils.isPickyEatingPossible)((0, _game.gameState));
+ const redRowReach = (0, _gameUtils.reachRedRowIndex)((0, _game.gameState));
+ const { clairvoyant } = (0, _game.gameState).perks;
+ let offset = getDashOffset((0, _game.gameState));
+ if (!(redBorderOnBricksWithWrongColor || redRowReach !== -1 || (0, _game.gameState).perks.zen)) offset = 0;
+ const clairVoyance = clairvoyant && (0, _game.gameState).brickHP.reduce((a, b)=>a + b, 0);
+ const newKey = (0, _game.gameState).gameZoneWidth + "_" + (0, _game.gameState).bricks.join("_") + bombSVG.complete + "_" + redRowReach + "_" + redBorderOnBricksWithWrongColor + "_" + (0, _game.gameState).ballsColor + "_" + (0, _game.gameState).perks.pierce_color + "_" + clairVoyance + "_" + offset;
+ if (newKey !== cachedBricksRenderKey) {
+ cachedBricksRenderKey = newKey;
+ cachedBricksRender.width = (0, _game.gameState).gameZoneWidth;
+ cachedBricksRender.height = (0, _game.gameState).gameZoneWidth + 1;
+ const canctx = cachedBricksRender.getContext("2d");
+ canctx.clearRect(0, 0, (0, _game.gameState).gameZoneWidth, (0, _game.gameState).gameZoneWidth);
+ canctx.resetTransform();
+ canctx.translate(-(0, _game.gameState).offsetX, 0);
+ // Bricks
+ (0, _game.gameState).bricks.forEach((color, index)=>{
+ const x = (0, _gameUtils.brickCenterX)((0, _game.gameState), index), y = (0, _gameUtils.brickCenterY)((0, _game.gameState), index);
+ if (!color) return;
+ let redBecauseOfReach = redRowReach === Math.floor(index / (0, _game.gameState).level.size);
+ let redBorder = (0, _game.gameState).ballsColor !== color && color !== "black" && redBorderOnBricksWithWrongColor || hasCombo && (0, _game.gameState).perks.zen && color === "black" || redBecauseOfReach;
+ canctx.globalCompositeOperation = "source-over";
+ drawBrick((0, _game.gameState), canctx, color, x, y, redBorder ? offset : -1, clairvoyant >= 2);
+ if ((0, _game.gameState).brickHP[index] > 1 && clairvoyant) {
+ canctx.globalCompositeOperation = "source-over";
+ drawText(canctx, (0, _game.gameState).brickHP[index].toString(), clairvoyant >= 2 ? color : (0, _game.gameState).level.color, (0, _game.gameState).puckHeight, x, y);
+ }
+ if (color === "black") {
+ canctx.globalCompositeOperation = "source-over";
+ drawIMG(canctx, bombSVG, (0, _game.gameState).brickWidth, x, y);
+ }
+ });
+ }
+ ctx.drawImage(cachedBricksRender, (0, _game.gameState).offsetX, 0);
+}
+let cachedGraphics = {};
+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;
+ can.height = puckHeight * 2;
+ const canctx = can.getContext("2d");
+ canctx.fillStyle = color;
+ canctx.beginPath();
+ canctx.moveTo(0, puckHeight * 2);
+ if (concave_puck) {
+ canctx.lineTo(0, 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);
+ canctx.bezierCurveTo(0, puckHeight * 0.75, puckWidth, puckHeight * 0.75, puckWidth, puckHeight * 1.25);
+ canctx.lineTo(puckWidth, puckHeight * 2);
+ }
+ canctx.fill();
+ if (redBorderOffset !== -1) {
+ canctx.strokeStyle = "#FF0000";
+ canctx.lineWidth = 4;
+ canctx.setLineDash(redBorderDash);
+ canctx.lineDashOffset = redBorderOffset;
+ canctx.stroke();
+ }
+ cachedGraphics[key] = can;
+ }
+ ctx.drawImage(cachedGraphics[key], Math.round((0, _game.gameState).puckPosition - puckWidth / 2), (0, _game.gameState).gameZoneHeight - puckHeight * 2 + yOffset);
+}
+function drawBall(ctx, color, width, x, y, borderColor = "") {
+ const key = "ball" + color + "_" + width + "_" + borderColor;
+ const size = Math.round(width);
+ if (!cachedGraphics[key]) {
+ const can = document.createElement("canvas");
+ can.width = size;
+ can.height = size;
+ const canctx = can.getContext("2d");
+ canctx.beginPath();
+ canctx.arc(size / 2, size / 2, Math.round(size / 2) - 1, 0, 2 * Math.PI);
+ canctx.fillStyle = color;
+ canctx.fill();
+ if (borderColor) {
+ canctx.lineWidth = 2;
+ canctx.strokeStyle = borderColor;
+ canctx.stroke();
+ }
+ cachedGraphics[key] = can;
+ }
+ ctx.drawImage(cachedGraphics[key], Math.round(x - size / 2), Math.round(y - size / 2));
+}
+const angles = 32;
+function drawCoin(ctx, color, size, x, y, borderColor, rawAngle) {
+ const angle = (Math.round(rawAngle / Math.PI * 2 * angles) % angles + angles) % angles;
+ const key = "coin with halo_" + color + "_" + size + "_" + borderColor + "_" + (color === "#ffd300" ? angle : "whatever");
+ if (!cachedGraphics[key]) {
+ const can = document.createElement("canvas");
+ can.width = size;
+ can.height = size;
+ const canctx = can.getContext("2d");
+ // coin
+ canctx.beginPath();
+ canctx.arc(size / 2, size / 2, size / 2, 0, 2 * Math.PI);
+ canctx.fillStyle = color;
+ canctx.fill();
+ canctx.strokeStyle = borderColor;
+ if (borderColor == "#FF0000") {
+ canctx.lineWidth = 2;
+ canctx.setLineDash(redBorderDash);
+ }
+ if (color === "transparent") canctx.lineWidth = 2;
+ canctx.stroke();
+ if (color === "#ffd300") {
+ // Fill in
+ canctx.beginPath();
+ canctx.arc(size / 2, size / 2, size / 2 * 0.6, 0, 2 * Math.PI);
+ canctx.fillStyle = "rgba(255,255,255,0.5)";
+ canctx.fill();
+ canctx.translate(size / 2, size / 2);
+ canctx.rotate(angle / 16);
+ canctx.translate(-size / 2, -size / 2);
+ canctx.globalCompositeOperation = "multiply";
+ drawText(canctx, "$", color, size - 2, size / 2, size / 2 + 1);
+ drawText(canctx, "$", color, size - 2, size / 2, size / 2 + 1);
+ }
+ cachedGraphics[key] = can;
+ }
+ ctx.drawImage(cachedGraphics[key], Math.round(x - size / 2), Math.round(y - size / 2));
+}
+function drawFuzzyBall(ctx, color, width, x, y) {
+ width = Math.max(width, 2);
+ const key = "fuzzy-circle" + color + "_" + width;
+ if (!color?.startsWith("#")) debugger;
+ const size = Math.round(width * 3);
+ if (!size || isNaN(size)) {
+ debugger;
+ return;
+ }
+ if (!cachedGraphics[key]) {
+ const can = document.createElement("canvas");
+ can.width = size;
+ can.height = size;
+ const canctx = can.getContext("2d");
+ const gradient = canctx.createRadialGradient(size / 2, size / 2, 0, size / 2, size / 2, size / 2);
+ gradient.addColorStop(0, color);
+ gradient.addColorStop(0.3, color + "88");
+ gradient.addColorStop(0.6, color + "22");
+ gradient.addColorStop(1, "transparent");
+ canctx.fillStyle = gradient;
+ canctx.fillRect(0, 0, size, size);
+ cachedGraphics[key] = can;
+ }
+ ctx.drawImage(cachedGraphics[key], Math.round(x - size / 2), Math.round(y - size / 2));
+}
+function drawBrick(gameState, ctx, color, x, y, offset = 0, borderOnly) {
+ const tlx = Math.ceil(x - gameState.brickWidth / 2);
+ const tly = Math.ceil(y - gameState.brickWidth / 2);
+ const brx = Math.ceil(x + gameState.brickWidth / 2) - 1;
+ const bry = Math.ceil(y + gameState.brickWidth / 2) - 1;
+ const width = brx - tlx, height = bry - tly;
+ const key = "brick" + color + "_" + "_" + width + "_" + height + "_" + offset + "_" + borderOnly + "_";
+ if (!cachedGraphics[key]) {
+ const can = document.createElement("canvas");
+ can.width = width;
+ can.height = height;
+ const bord = 4;
+ const cornerRadius = 2;
+ const canctx = can.getContext("2d");
+ canctx.fillStyle = color;
+ canctx.setLineDash(offset !== -1 ? redBorderDash : emptyArray);
+ canctx.lineDashOffset = offset;
+ canctx.strokeStyle = offset !== -1 && "#FF000033" || color;
+ canctx.lineJoin = "round";
+ canctx.lineWidth = bord;
+ roundRect(canctx, bord / 2, bord / 2, width - bord, height - bord, cornerRadius);
+ if (!borderOnly) canctx.fill();
+ canctx.stroke();
+ cachedGraphics[key] = can;
+ }
+ ctx.drawImage(cachedGraphics[key], tlx, tly, width, height);
+// It's not easy to have a 1px gap between bricks without antialiasing
+}
+function roundRect(ctx, x, y, width, height, radius) {
+ ctx.beginPath();
+ ctx.moveTo(x + radius, y);
+ ctx.lineTo(x + width - radius, y);
+ ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
+ ctx.lineTo(x + width, y + height - radius);
+ ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
+ ctx.lineTo(x + radius, y + height);
+ ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
+ ctx.lineTo(x, y + radius);
+ ctx.quadraticCurveTo(x, y, x + radius, y);
+ ctx.closePath();
+}
+function drawIMG(ctx, img, size, x, y) {
+ const key = "svg" + img + "_" + size + "_" + img.complete;
+ if (!cachedGraphics[key]) {
+ const can = document.createElement("canvas");
+ can.width = size;
+ can.height = size;
+ const canctx = can.getContext("2d");
+ const ratio = size / Math.max(img.width, img.height);
+ const w = img.width * ratio;
+ const h = img.height * ratio;
+ canctx.drawImage(img, (size - w) / 2, (size - h) / 2, w, h);
+ cachedGraphics[key] = can;
+ }
+ ctx.drawImage(cachedGraphics[key], Math.round(x - size / 2), Math.round(y - size / 2));
+}
+function drawText(ctx, text, color, fontSize, x, y, left = false) {
+ const key = "text" + text + "_" + color + "_" + fontSize + "_" + left;
+ if (!cachedGraphics[key]) {
+ const can = document.createElement("canvas");
+ can.width = fontSize * text.length;
+ can.height = fontSize;
+ const canctx = can.getContext("2d");
+ canctx.fillStyle = color;
+ canctx.textAlign = left ? "left" : "center";
+ canctx.textBaseline = "middle";
+ canctx.font = fontSize + "px monospace";
+ canctx.fillText(text, left ? 0 : can.width / 2, can.height / 2, can.width);
+ cachedGraphics[key] = can;
+ }
+ ctx.drawImage(cachedGraphics[key], left ? x : Math.round(x - cachedGraphics[key].width / 2), Math.round(y - cachedGraphics[key].height / 2));
+}
+const scoreDisplay = document.getElementById("score");
+const menuLabel = document.getElementById("menuLabel");
+const emptyArray = [];
+const redBorderDash = [
+ 5,
+ 5
+];
+function getDashOffset(gameState) {
+ if ((0, _options.isOptionOn)("basic")) return 0;
+ return Math.floor(gameState.levelTime % 500 / 500 * 10) % 10;
+}
+let wakeLock = null, wakeLockPending = false;
+function askForWakeLock(gameState) {
+ if (gameState.startParams.computer_controlled && !wakeLock && !wakeLockPending) {
+ wakeLockPending = true;
+ try {
+ navigator.wakeLock.request("screen").then((lock)=>{
+ wakeLock = lock;
+ wakeLockPending = false;
+ lock.addEventListener("release", ()=>{
+ // the wake lock has been released
+ wakeLock = null;
+ });
+ });
+ } catch (e) {
+ console.warn("askForWakeLock error", e);
+ }
+ }
}
-exports.getBundleURL = getBundleURLCached;
-exports.getBaseURL = getBaseURL;
-exports.getOrigin = getOrigin;
-},{}],"9ZeQl":[function(require,module,exports,__globalThis) {
+},{"./gameStateMutators":"9ZeQl","./game_utils":"cEeac","./i18n/i18n":"eNPRm","./game":"edeGs","./options":"d5NoS","./pure_functions":"6pQh7","@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3"}],"9ZeQl":[function(require,module,exports,__globalThis) {
var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js");
parcelHelpers.defineInteropFlag(exports);
parcelHelpers.export(exports, "setMousePos", ()=>setMousePos);
@@ -4384,7 +4987,7 @@ frames = 1) {
spawnParticlesExplosion(gameState, 3, coin.x, coin.y, "#6262EA");
spawnParticlesImplosion(gameState, 3, coin.previousX, coin.previousY, "#6262EA");
}
- if (gameState.perks.wrap_right > 1 && hitBorder % 2 && coin.previousX > gameState.offsetX + gameState.gameZoneWidth / 2) {
+ if (gameState.perks.wrap_right > 1 && hitBorder % 2 && coin.previousX > (0, _gameUtils.canvasCenterX)(gameState)) {
schedulGameSound(gameState, "plouf", coin.x, 1);
coin.x = gameState.offsetX + gameState.coinSize;
if (coin.vx < 0) coin.vx *= -1;
@@ -4477,7 +5080,7 @@ frames = 1) {
}
}));
if (gameState.perks.wind) {
- const windD = (gameState.puckPosition - (gameState.offsetX + gameState.gameZoneWidth / 2)) / gameState.gameZoneWidth * 2 * gameState.perks.wind;
+ const windD = (gameState.puckPosition - (0, _gameUtils.canvasCenterX)(gameState)) / gameState.gameZoneWidth * 2 * gameState.perks.wind;
for(let i = 0; i < gameState.perks.wind; i++)if (Math.random() * Math.abs(windD) > 0.5) makeParticle(gameState, gameState.offsetXRoundedDown + Math.random() * gameState.gameZoneWidthRoundedUp, Math.random() * gameState.gameZoneHeight, windD * 8, 0, rainbowColor(), true, gameState.coinSize / 2, 150);
}
forEachLiveOne(gameState.particles, (flash, index)=>{
@@ -4507,8 +5110,8 @@ frames = 1) {
makeParticle(gameState, gameState.puckPosition + gameState.puckWidth * pos, gameState.gameZoneHeight - gameState.puckHeight, pos * 10, -5, "#FF0000", true, gameState.coinSize / 2, 100 * (Math.random() + 1));
}
}
- if (gameState.perks.wrap_left && gameState.perks.left_is_lava < 2 && Math.random() * frames > 0.1) makeParticle(gameState, gameState.offsetXRoundedDown, Math.random() * gameState.gameZoneHeight, 5, (Math.random() - 0.5) * 10, "#6262EA", true, gameState.coinSize / 2, 100 * (Math.random() + 1));
- if (gameState.perks.wrap_right && gameState.perks.right_is_lava < 2 && Math.random() * frames > 0.1) makeParticle(gameState, gameState.offsetXRoundedDown + gameState.gameZoneWidth, Math.random() * gameState.gameZoneHeight, -5, (Math.random() - 0.5) * 10, "#6262EA", true, gameState.coinSize / 2, 100 * (Math.random() + 1));
+ if (gameState.perks.wrap_left && gameState.perks.left_is_lava < 2 && Math.random() * frames > 0.1) makeParticle(gameState, (0, _gameUtils.zoneLeftBorderX)(gameState), Math.random() * gameState.gameZoneHeight, 5, (Math.random() - 0.5) * 10, "#6262EA", true, gameState.coinSize / 2, 100 * (Math.random() + 1));
+ if (gameState.perks.wrap_right && gameState.perks.right_is_lava < 2 && Math.random() * frames > 0.1) makeParticle(gameState, (0, _gameUtils.zoneRightBorderX)(gameState), Math.random() * gameState.gameZoneHeight, -5, (Math.random() - 0.5) * 10, "#6262EA", true, gameState.coinSize / 2, 100 * (Math.random() + 1));
// Respawn what's needed, show particles
forEachLiveOne(gameState.respawns, (r, ri)=>{
if (gameState.bricks[r.index]) destroy(gameState.respawns, ri);
@@ -4572,15 +5175,13 @@ function ballTick(gameState, ball, frames) {
}, gameState.perks.puck_repulse_ball + 1, false);
if (gameState.perks.steering) {
const delta = gameState.puckPosition - gameState.lastPuckPosition;
- const angle = Math.atan2(ball.vy, ball.vx) + delta / gameState.gameZoneWidth * Math.PI / 2 * gameState.perks.steering * frames / 2;
- const d = Math.sqrt(ball.vy * ball.vy + ball.vx * ball.vx);
- ball.vy = Math.sin(angle) * d;
- ball.vx = Math.cos(angle) * d;
- console.log({
- delta,
- angle,
- d
- });
+ if (Math.abs(delta) > 1) {
+ const angle = Math.atan2(ball.vy, ball.vx) + delta / gameState.gameZoneWidth * Math.PI / 2 * gameState.perks.steering * frames / 2;
+ const d = Math.sqrt(ball.vy * ball.vy + ball.vx * ball.vx);
+ ball.vy = Math.sin(angle) * d;
+ ball.vx = Math.cos(angle) * d;
+ if (Math.random() < frames && !(0, _options.isOptionOn)('basic')) makeParticle(gameState, ball.x, ball.y, -ball.vx / 10, -ball.vy / 10, '#6262EA', true, 8, 500);
+ }
}
// Bounces
const borderHitCode = bordersHitCheck(gameState, ball, gameState.ballSize / 2, frames);
@@ -4882,633 +5483,7 @@ function zenTick(gameState) {
}
}
-},{"./game_utils":"cEeac","./i18n/i18n":"eNPRm","./settings":"5blfu","./render":"9AS2t","./gameOver":"caCAf","./game":"edeGs","./recording":"godmD","./options":"d5NoS","./pure_functions":"6pQh7","./addToTotalScore":"ka4dG","./getLevelBackground":"7OIPf","./openUpgradesPicker":"2fQt0","@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);
-parcelHelpers.export(exports, "ctx", ()=>ctx);
-parcelHelpers.export(exports, "bombSVG", ()=>bombSVG);
-parcelHelpers.export(exports, "background", ()=>background);
-parcelHelpers.export(exports, "backgroundCanvas", ()=>backgroundCanvas);
-parcelHelpers.export(exports, "haloCanvas", ()=>haloCanvas);
-parcelHelpers.export(exports, "getHaloScale", ()=>getHaloScale);
-parcelHelpers.export(exports, "render", ()=>render);
-parcelHelpers.export(exports, "renderAllBricks", ()=>renderAllBricks);
-parcelHelpers.export(exports, "drawPuck", ()=>drawPuck);
-parcelHelpers.export(exports, "drawBall", ()=>drawBall);
-parcelHelpers.export(exports, "drawCoin", ()=>drawCoin);
-parcelHelpers.export(exports, "drawFuzzyBall", ()=>drawFuzzyBall);
-parcelHelpers.export(exports, "drawBrick", ()=>drawBrick);
-parcelHelpers.export(exports, "roundRect", ()=>roundRect);
-parcelHelpers.export(exports, "drawIMG", ()=>drawIMG);
-parcelHelpers.export(exports, "drawText", ()=>drawText);
-parcelHelpers.export(exports, "scoreDisplay", ()=>scoreDisplay);
-parcelHelpers.export(exports, "getDashOffset", ()=>getDashOffset);
-var _gameStateMutators = require("./gameStateMutators");
-var _gameUtils = require("./game_utils");
-var _i18N = require("./i18n/i18n");
-var _game = require("./game");
-var _options = require("./options");
-var _pureFunctions = require("./pure_functions");
-const gameCanvas = document.getElementById("game");
-const ctx = gameCanvas.getContext("2d", {
- alpha: false
-});
-const bombSVG = document.createElement("img");
-bombSVG.src = "data:image/svg+xml;base64," + btoa(``);
-bombSVG.onload = ()=>(0, _game.gameState).needsRender = true;
-const background = document.createElement("img");
-background.onload = ()=>(0, _game.gameState).needsRender = true;
-const backgroundCanvas = document.createElement("canvas");
-const haloCanvas = document.createElement("canvas");
-const haloCanvasCtx = haloCanvas.getContext("2d", {
- alpha: false
-});
-function getHaloScale() {
- return 16 * ((0, _options.isOptionOn)("precise_lighting") ? 1 : 2);
-}
-let framesCounter = 0;
-function render(gameState) {
- framesCounter++;
- (0, _game.startWork)("render:init");
- const level = (0, _gameUtils.currentLevelInfo)(gameState);
- const hasCombo = gameState.combo > (0, _gameStateMutators.baseCombo)(gameState);
- const { width, height } = gameCanvas;
- if (!width || !height) return;
- if (gameState.currentLevel || gameState.levelTime) menuLabel.innerText = (0, _i18N.t)("play.current_lvl", {
- level: gameState.currentLevel + 1,
- max: (0, _gameUtils.max_levels)(gameState)
- });
- else menuLabel.innerText = (0, _i18N.t)("play.menu_label");
- const catchRate = gameState.levelSpawnedCoins ? gameState.levelCoughtCoins / (gameState.levelSpawnedCoins || 1) : // gameState.levelSpawnedCoins
- 1;
- (0, _game.startWork)("render:scoreDisplay");
- scoreDisplay.innerHTML = ((0, _options.isOptionOn)("show_fps") || gameState.startParams.computer_controlled ? `
-
- ${0, _game.lastMeasuredFPS} FPS
- /
- ` : "") + ((0, _options.isOptionOn)("show_stats") ? `
- (0, _pureFunctions.catchRateGood) / 100 && "good" || ""}" data-tooltip="${(0, _i18N.t)("play.stats.coins_catch_rate")}">
- ${Math.floor(catchRate * 100)}%
- /
-
- ${Math.ceil(gameState.levelTime / 1000)}s
- /
-
- ${gameState.levelMisses} M
- /
- ` : "") + `$${gameState.score}`;
- scoreDisplay.classList[gameState.startParams.computer_controlled ? "add" : "remove"]("computer_controlled");
- scoreDisplay.classList[gameState.lastScoreIncrease > gameState.levelTime - 500 ? "add" : "remove"]("active");
- // Clear
- if (!(0, _options.isOptionOn)("basic") && level.svg && level.color === "#000000") {
- const skipN = (0, _options.isOptionOn)("probabilistic_lighting") && (0, _gameStateMutators.liveCount)(gameState.coins) > 150 ? 3 : 0;
- const shouldSkip = (index)=>skipN ? (framesCounter + index) % (skipN + 1) !== 0 : false;
- const haloScale = getHaloScale();
- (0, _game.startWork)("render:halo:clear");
- haloCanvasCtx.globalCompositeOperation = "source-over";
- haloCanvasCtx.globalAlpha = skipN ? 0.1 : 0.99;
- haloCanvasCtx.fillStyle = level.color;
- haloCanvasCtx.fillRect(0, 0, width / haloScale, height / haloScale);
- const brightness = (0, _options.isOptionOn)("extra_bright") ? 3 : 1;
- haloCanvasCtx.globalCompositeOperation = "lighten";
- haloCanvasCtx.globalAlpha = 0.1 + 5 / ((0, _gameStateMutators.liveCount)(gameState.coins) + 10);
- (0, _game.startWork)("render:halo:coins");
- (0, _gameStateMutators.forEachLiveOne)(gameState.coins, (coin, index)=>{
- if (shouldSkip(index)) return;
- const color = (0, _gameUtils.getCoinRenderColor)(gameState, coin);
- drawFuzzyBall(haloCanvasCtx, color, gameState.coinSize * 2 * brightness / haloScale, coin.x / haloScale, coin.y / haloScale);
- });
- (0, _game.startWork)("render:halo:balls");
- gameState.balls.forEach((ball, index)=>{
- if (shouldSkip(index)) return;
- haloCanvasCtx.globalAlpha = 0.3 * (1 - (0, _pureFunctions.ballTransparency)(ball, gameState));
- drawFuzzyBall(haloCanvasCtx, gameState.ballsColor, gameState.ballSize * 2 * brightness / haloScale, ball.x / haloScale, ball.y / haloScale);
- });
- (0, _game.startWork)("render:halo:bricks");
- haloCanvasCtx.globalAlpha = 0.05;
- gameState.bricks.forEach((color, index)=>{
- if (!color) return;
- if (shouldSkip(index)) return;
- const x = (0, _gameUtils.brickCenterX)(gameState, index), y = (0, _gameUtils.brickCenterY)(gameState, index);
- drawFuzzyBall(haloCanvasCtx, color == "black" ? "#666666" : color, // Perf could really go down there because of the size of the halo
- Math.min(200, gameState.brickWidth * 1.5 * brightness) / haloScale, x / haloScale, y / haloScale);
- });
- (0, _game.startWork)("render:halo:particles");
- haloCanvasCtx.globalCompositeOperation = "screen";
- (0, _gameStateMutators.forEachLiveOne)(gameState.particles, (flash, index)=>{
- if (shouldSkip(index)) return;
- const { x, y, time, color, size, duration } = flash;
- const elapsed = gameState.levelTime - time;
- haloCanvasCtx.globalAlpha = 0.1 * Math.min(1, 2 - elapsed / duration * 2);
- drawFuzzyBall(haloCanvasCtx, color, size * 3 * brightness / haloScale, x / haloScale, y / haloScale);
- });
- (0, _game.startWork)("render:halo:scale_up");
- ctx.globalAlpha = 1;
- ctx.globalCompositeOperation = "source-over";
- ctx.imageSmoothingQuality = "high";
- ctx.imageSmoothingEnabled = (0, _options.isOptionOn)("smooth_lighting") || false;
- ctx.drawImage(haloCanvas, 0, 0, width, height);
- ctx.imageSmoothingEnabled = false;
- (0, _game.startWork)("render:halo:pattern");
- ctx.globalAlpha = 1;
- ctx.globalCompositeOperation = "multiply";
- if (level.svg && background.width && background.complete) {
- if (backgroundCanvas.title !== level.name) {
- backgroundCanvas.title = level.name;
- backgroundCanvas.width = gameState.canvasWidth;
- backgroundCanvas.height = gameState.canvasHeight;
- const bgctx = backgroundCanvas.getContext("2d");
- bgctx.globalCompositeOperation = "source-over";
- bgctx.fillStyle = level.color || "#000";
- bgctx.fillRect(0, 0, gameState.canvasWidth, gameState.canvasHeight);
- 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 = "#FFFFFF";
- 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.globalCompositeOperation = "screen";
- bgctx.fillStyle = pattern;
- bgctx.fillRect(0, 0, width, height);
- }
- }
- }
- ctx.globalCompositeOperation = "darken";
- ctx.drawImage(backgroundCanvas, 0, 0);
- } else {
- // Background not loaded yes
- ctx.fillStyle = "#000";
- ctx.fillRect(0, 0, width, height);
- }
- } else {
- (0, _game.startWork)("render:halo-basic");
- ctx.globalAlpha = 1;
- ctx.globalCompositeOperation = "source-over";
- ctx.fillStyle = level.color || "#000";
- ctx.fillRect(0, 0, width, height);
- (0, _gameStateMutators.forEachLiveOne)(gameState.particles, (flash)=>{
- const { x, y, time, color, size, duration } = flash;
- const elapsed = gameState.levelTime - time;
- ctx.globalAlpha = Math.min(1, 2 - elapsed / duration * 2);
- drawBall(ctx, color, size, x, y);
- });
- }
- (0, _game.startWork)("render:explosionshake");
- ctx.globalAlpha = 1;
- ctx.globalCompositeOperation = "source-over";
- const lastExplosionDelay = gameState.levelTime - gameState.lastExplosion + 5;
- const shaked = lastExplosionDelay < 200 && !(0, _options.isOptionOn)("basic") && // Otherwise, if you pause after an explosion, moving the mouses shakes the picture
- gameState.running;
- if (shaked) {
- const amplitude = (gameState.perks.bigger_explosions + 1) * 50 / lastExplosionDelay;
- ctx.translate(Math.sin(Date.now()) * amplitude, Math.sin(Date.now() + 36) * amplitude);
- }
- (0, _game.startWork)("render:coins");
- // Coins
- ctx.globalAlpha = 1;
- (0, _gameStateMutators.forEachLiveOne)(gameState.coins, (coin)=>{
- const color = (0, _gameUtils.getCoinRenderColor)(gameState, coin);
- const hollow = gameState.perks.metamorphosis && !coin.metamorphosisPoints;
- ctx.globalCompositeOperation = "source-over";
- drawCoin(ctx, hollow ? "transparent" : color, coin.size, coin.x, coin.y, // Red border around coins with asceticism
- hasCombo && gameState.perks.asceticism && "#FF0000" || // Gold coins
- // (color === "#ffd300" && "#ffd300") ||
- hollow && color || gameState.level.color, coin.a);
- });
- (0, _game.startWork)("render:ball shade");
- // Black shadow around balls
- ctx.globalCompositeOperation = "source-over";
- gameState.balls.forEach((ball)=>{
- ctx.globalAlpha = Math.min(0.8, (0, _gameStateMutators.liveCount)(gameState.coins) / 20) * (1 - (0, _pureFunctions.ballTransparency)(ball, gameState));
- drawBall(ctx, level.color || "#000", gameState.ballSize * 6, ball.x, ball.y);
- });
- (0, _game.startWork)("render:bricks");
- ctx.globalCompositeOperation = "source-over";
- renderAllBricks();
- (0, _game.startWork)("render:lights");
- ctx.globalCompositeOperation = "screen";
- (0, _gameStateMutators.forEachLiveOne)(gameState.lights, (flash)=>{
- 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(gameState, ctx, color, x, y, -1, gameState.perks.clairvoyant >= 2);
- });
- (0, _game.startWork)("render:texts");
- ctx.globalCompositeOperation = "screen";
- (0, _gameStateMutators.forEachLiveOne)(gameState.texts, (flash)=>{
- const { x, y, time, color, size, duration } = flash;
- const elapsed = gameState.levelTime - time;
- ctx.globalAlpha = Math.max(0, Math.min(1, 2 - elapsed / duration * 2));
- ctx.globalCompositeOperation = "source-over";
- drawText(ctx, flash.text, color, size, x, y - elapsed / 10);
- });
- (0, _game.startWork)("render:particles");
- (0, _gameStateMutators.forEachLiveOne)(gameState.particles, (particle)=>{
- const { x, y, time, color, size, duration } = particle;
- const elapsed = gameState.levelTime - time;
- ctx.globalAlpha = Math.max(0, Math.min(1, 2 - elapsed / duration * 2));
- ctx.globalCompositeOperation = "screen";
- drawBall(ctx, color, size, x, y);
- });
- //
- (0, _game.startWork)("render:extra_life");
- if (gameState.perks.extra_life) {
- ctx.globalAlpha = gameState.balls.length > 1 ? 0.2 : 1;
- ctx.globalCompositeOperation = "source-over";
- ctx.fillStyle = gameState.puckColor;
- for(let i = 0; i < gameState.perks.extra_life; i++)ctx.fillRect(gameState.offsetXRoundedDown, gameState.gameZoneHeight - gameState.puckHeight / 2 + 2 * i, gameState.gameZoneWidthRoundedUp, 1);
- }
- (0, _game.startWork)("render:balls");
- ctx.globalAlpha = 1;
- ctx.globalCompositeOperation = "source-over";
- gameState.balls.forEach((ball)=>{
- const drawingColor = gameState.ballsColor;
- const ballAlpha = 1 - (0, _pureFunctions.ballTransparency)(ball, gameState);
- ctx.globalAlpha = ballAlpha;
- // The white border around is to distinguish colored balls from coins/bg
- drawBall(ctx, drawingColor, gameState.ballSize, ball.x, ball.y, gameState.puckColor);
- if ((0, _gameUtils.telekinesisEffectRate)(gameState, ball) || (0, _gameUtils.yoyoEffectRate)(gameState, ball)) {
- ctx.beginPath();
- ctx.moveTo(gameState.puckPosition, gameState.gameZoneHeight);
- ctx.globalAlpha = (0, _pureFunctions.clamp)(Math.max((0, _gameUtils.telekinesisEffectRate)(gameState, ball), (0, _gameUtils.yoyoEffectRate)(gameState, ball)) * ballAlpha, 0, 1);
- ctx.strokeStyle = gameState.puckColor;
- ctx.bezierCurveTo(gameState.puckPosition, gameState.gameZoneHeight, gameState.puckPosition, ball.y, ball.x, ball.y);
- ctx.stroke();
- ctx.lineWidth = 2;
- ctx.setLineDash(emptyArray);
- }
- ctx.globalAlpha = 1;
- if (gameState.perks.clairvoyant && gameState.ballStickToPuck || gameState.perks.steering > 1 && !gameState.ballStickToPuck) {
- ctx.strokeStyle = gameState.ballsColor;
- ctx.beginPath();
- ctx.moveTo(ball.x, ball.y);
- ctx.lineTo(ball.x + ball.vx * 10, ball.y + ball.vy * 10);
- ctx.stroke();
- }
- });
- (0, _game.startWork)("render:puck");
- ctx.globalAlpha = (0, _gameUtils.isMovingWhilePassiveIncome)(gameState) ? 0.2 : 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);
- (0, _game.startWork)("render:combotext");
- const spawns = (0, _pureFunctions.coinsBoostedCombo)(gameState);
- if (spawns > 1 && !(0, _gameUtils.isMovingWhilePassiveIncome)(gameState)) {
- ctx.globalCompositeOperation = "source-over";
- ctx.globalAlpha = 1;
- const comboText = spawns.toString();
- const comboTextWidth = comboText.length * gameState.puckHeight / 1.8;
- const totalWidth = comboTextWidth + gameState.coinSize * 2;
- const left = gameState.puckPosition - totalWidth / 2;
- ctx.globalAlpha = gameState.combo > (0, _gameStateMutators.baseCombo)(gameState) ? 1 : 0.3;
- if (totalWidth < gameState.puckWidth) {
- drawText(ctx, comboText, "#000", gameState.puckHeight, left + gameState.coinSize * 1.5, gameState.gameZoneHeight - gameState.puckHeight / 2, true);
- ctx.globalAlpha = 1;
- drawCoin(ctx, "#ffd300", gameState.coinSize, left + gameState.coinSize / 2, gameState.gameZoneHeight - gameState.puckHeight / 2, "#ffd300", 0);
- } else drawText(ctx, comboTextWidth > gameState.puckWidth ? gameState.combo.toString() : comboText, "#000", comboTextWidth > gameState.puckWidth ? 12 : 20, gameState.puckPosition, gameState.gameZoneHeight - gameState.puckHeight / 2, false);
- }
- (0, _game.startWork)("render:borders");
- // Borders
- ctx.globalCompositeOperation = "source-over";
- ctx.globalAlpha = 1;
- let redLeftSide = hasCombo && (gameState.perks.left_is_lava || gameState.perks.trampoline);
- let redRightSide = hasCombo && (gameState.perks.right_is_lava || gameState.perks.trampoline);
- let redTop = hasCombo && (gameState.perks.top_is_lava || gameState.perks.trampoline);
- if (gameState.offsetXRoundedDown) {
- // draw outside of gaming area to avoid capturing borders in recordings
- if (gameState.perks.left_is_lava < 2) drawStraightLine(ctx, gameState, redLeftSide && "#FF0000" || "#FFFFFF", gameState.offsetXRoundedDown - 1, 0, gameState.offsetXRoundedDown - 1, height, 1);
- if (gameState.perks.right_is_lava < 2) drawStraightLine(ctx, gameState, redRightSide && "#FF0000" || "#FFFFFF", width - gameState.offsetXRoundedDown + 1, 0, width - gameState.offsetXRoundedDown + 1, height, 1);
- } else {
- if (gameState.perks.left_is_lava < 2) drawStraightLine(ctx, gameState, redLeftSide && "#FF0000" || "", 0, 0, 0, height, 1);
- if (gameState.perks.right_is_lava < 2) drawStraightLine(ctx, gameState, redRightSide && "#FF0000" || "", width - 1, 0, width - 1, height, 1);
- }
- if (redTop && gameState.perks.top_is_lava < 2) drawStraightLine(ctx, gameState, "#FF0000", gameState.offsetXRoundedDown, 1, width - gameState.offsetXRoundedDown, 1, 1);
- (0, _game.startWork)("render:bottom_line");
- ctx.globalAlpha = 1;
- const corner = (0, _gameUtils.getCornerOffset)(gameState);
- const bottomLineIsRed = hasCombo && gameState.perks.compound_interest;
- drawStraightLine(ctx, gameState, bottomLineIsRed && "#FF0000" || (0, _options.isOptionOn)("mobile-mode") && "#FFFFFF" || corner && "#FFFFFF" || "", gameState.offsetXRoundedDown - corner, gameState.gameZoneHeight - 1, width - gameState.offsetXRoundedDown + corner, gameState.gameZoneHeight - 1, bottomLineIsRed ? 1 : 0.5);
- (0, _game.startWork)("render:contrast");
- if (!(0, _options.isOptionOn)("basic") && (0, _options.isOptionOn)("contrast") && level.svg && level.color === "#000000") {
- ctx.imageSmoothingEnabled = (0, _options.isOptionOn)("smooth_lighting") || false;
- if ((0, _options.isOptionOn)("probabilistic_lighting")) {
- ctx.globalAlpha = 1;
- ctx.globalCompositeOperation = "soft-light";
- } else {
- haloCanvasCtx.fillStyle = "#FFFFFF";
- haloCanvasCtx.globalAlpha = 0.25;
- haloCanvasCtx.globalCompositeOperation = "screen";
- haloCanvasCtx.fillRect(0, 0, haloCanvas.width, haloCanvas.height);
- ctx.globalAlpha = 1;
- ctx.globalCompositeOperation = "overlay";
- }
- ctx.drawImage(haloCanvas, 0, 0, width, height);
- ctx.imageSmoothingEnabled = false;
- }
- (0, _game.startWork)("render:text_under_puck");
- ctx.globalCompositeOperation = "source-over";
- ctx.globalAlpha = 1;
- if ((0, _options.isOptionOn)("mobile-mode") && gameState.startParams.computer_controlled) drawText(ctx, "breakout.lecaro.me?autoplay", gameState.puckColor, gameState.puckHeight, gameState.canvasWidth / 2, gameState.gameZoneHeight + (gameState.canvasHeight - gameState.gameZoneHeight) / 2);
- if ((0, _options.isOptionOn)("mobile-mode") && !gameState.running) drawText(ctx, (0, _i18N.t)("play.mobile_press_to_play"), gameState.puckColor, gameState.puckHeight, gameState.canvasWidth / 2, gameState.gameZoneHeight + (gameState.canvasHeight - gameState.gameZoneHeight) / 2);
- (0, _game.startWork)("render:askForWakeLock");
- askForWakeLock(gameState);
- (0, _game.startWork)("render:resetTransform");
- if (shaked) ctx.resetTransform();
-}
-function drawStraightLine(ctx, gameState, mode, x1, y1, x2, y2, alpha = 1) {
- ctx.globalAlpha = alpha;
- if (!mode) return;
- x1 = Math.round(x1);
- y1 = Math.round(y1);
- x2 = Math.round(x2);
- y2 = Math.round(y2);
- if (mode == "#FF0000") {
- ctx.strokeStyle = "red";
- ctx.lineDashOffset = getDashOffset(gameState);
- ctx.lineWidth = 2;
- ctx.setLineDash(redBorderDash);
- ctx.beginPath();
- ctx.moveTo(x1, y1);
- ctx.lineTo(x2, y2);
- ctx.stroke();
- ctx.setLineDash(emptyArray);
- ctx.lineWidth = 1;
- } else {
- ctx.fillStyle = mode;
- ctx.fillRect(Math.min(x1, x2), Math.min(y1, y2), Math.max(1, Math.abs(x1 - x2)), Math.max(1, Math.abs(y1 - y2)));
- }
- mode;
- ctx.globalAlpha = 1;
-}
-let cachedBricksRender = document.createElement("canvas");
-let cachedBricksRenderKey = "";
-function renderAllBricks() {
- ctx.globalAlpha = 1;
- const hasCombo = (0, _game.gameState).combo > (0, _gameStateMutators.baseCombo)((0, _game.gameState));
- const redBorderOnBricksWithWrongColor = hasCombo && (0, _game.gameState).perks.picky_eater && (0, _gameUtils.isPickyEatingPossible)((0, _game.gameState));
- const redRowReach = (0, _gameUtils.reachRedRowIndex)((0, _game.gameState));
- const { clairvoyant } = (0, _game.gameState).perks;
- let offset = getDashOffset((0, _game.gameState));
- if (!(redBorderOnBricksWithWrongColor || redRowReach !== -1 || (0, _game.gameState).perks.zen)) offset = 0;
- const clairVoyance = clairvoyant && (0, _game.gameState).brickHP.reduce((a, b)=>a + b, 0);
- const newKey = (0, _game.gameState).gameZoneWidth + "_" + (0, _game.gameState).bricks.join("_") + bombSVG.complete + "_" + redRowReach + "_" + redBorderOnBricksWithWrongColor + "_" + (0, _game.gameState).ballsColor + "_" + (0, _game.gameState).perks.pierce_color + "_" + clairVoyance + "_" + offset;
- if (newKey !== cachedBricksRenderKey) {
- cachedBricksRenderKey = newKey;
- cachedBricksRender.width = (0, _game.gameState).gameZoneWidth;
- cachedBricksRender.height = (0, _game.gameState).gameZoneWidth + 1;
- const canctx = cachedBricksRender.getContext("2d");
- canctx.clearRect(0, 0, (0, _game.gameState).gameZoneWidth, (0, _game.gameState).gameZoneWidth);
- canctx.resetTransform();
- canctx.translate(-(0, _game.gameState).offsetX, 0);
- // Bricks
- (0, _game.gameState).bricks.forEach((color, index)=>{
- const x = (0, _gameUtils.brickCenterX)((0, _game.gameState), index), y = (0, _gameUtils.brickCenterY)((0, _game.gameState), index);
- if (!color) return;
- let redBecauseOfReach = redRowReach === Math.floor(index / (0, _game.gameState).level.size);
- let redBorder = (0, _game.gameState).ballsColor !== color && color !== "black" && redBorderOnBricksWithWrongColor || hasCombo && (0, _game.gameState).perks.zen && color === "black" || redBecauseOfReach;
- canctx.globalCompositeOperation = "source-over";
- drawBrick((0, _game.gameState), canctx, color, x, y, redBorder ? offset : -1, clairvoyant >= 2);
- if ((0, _game.gameState).brickHP[index] > 1 && clairvoyant) {
- canctx.globalCompositeOperation = "source-over";
- drawText(canctx, (0, _game.gameState).brickHP[index].toString(), clairvoyant >= 2 ? color : (0, _game.gameState).level.color, (0, _game.gameState).puckHeight, x, y);
- }
- if (color === "black") {
- canctx.globalCompositeOperation = "source-over";
- drawIMG(canctx, bombSVG, (0, _game.gameState).brickWidth, x, y);
- }
- });
- }
- ctx.drawImage(cachedBricksRender, (0, _game.gameState).offsetX, 0);
-}
-let cachedGraphics = {};
-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;
- can.height = puckHeight * 2;
- const canctx = can.getContext("2d");
- canctx.fillStyle = color;
- canctx.beginPath();
- canctx.moveTo(0, puckHeight * 2);
- if (concave_puck) {
- canctx.lineTo(0, 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);
- canctx.bezierCurveTo(0, puckHeight * 0.75, puckWidth, puckHeight * 0.75, puckWidth, puckHeight * 1.25);
- canctx.lineTo(puckWidth, puckHeight * 2);
- }
- canctx.fill();
- if (redBorderOffset !== -1) {
- canctx.strokeStyle = "#FF0000";
- canctx.lineWidth = 4;
- canctx.setLineDash(redBorderDash);
- canctx.lineDashOffset = redBorderOffset;
- canctx.stroke();
- }
- cachedGraphics[key] = can;
- }
- ctx.drawImage(cachedGraphics[key], Math.round((0, _game.gameState).puckPosition - puckWidth / 2), (0, _game.gameState).gameZoneHeight - puckHeight * 2 + yOffset);
-}
-function drawBall(ctx, color, width, x, y, borderColor = "") {
- const key = "ball" + color + "_" + width + "_" + borderColor;
- const size = Math.round(width);
- if (!cachedGraphics[key]) {
- const can = document.createElement("canvas");
- can.width = size;
- can.height = size;
- const canctx = can.getContext("2d");
- canctx.beginPath();
- canctx.arc(size / 2, size / 2, Math.round(size / 2) - 1, 0, 2 * Math.PI);
- canctx.fillStyle = color;
- canctx.fill();
- if (borderColor) {
- canctx.lineWidth = 2;
- canctx.strokeStyle = borderColor;
- canctx.stroke();
- }
- cachedGraphics[key] = can;
- }
- ctx.drawImage(cachedGraphics[key], Math.round(x - size / 2), Math.round(y - size / 2));
-}
-const angles = 32;
-function drawCoin(ctx, color, size, x, y, borderColor, rawAngle) {
- const angle = (Math.round(rawAngle / Math.PI * 2 * angles) % angles + angles) % angles;
- const key = "coin with halo_" + color + "_" + size + "_" + borderColor + "_" + (color === "#ffd300" ? angle : "whatever");
- if (!cachedGraphics[key]) {
- const can = document.createElement("canvas");
- can.width = size;
- can.height = size;
- const canctx = can.getContext("2d");
- // coin
- canctx.beginPath();
- canctx.arc(size / 2, size / 2, size / 2, 0, 2 * Math.PI);
- canctx.fillStyle = color;
- canctx.fill();
- canctx.strokeStyle = borderColor;
- if (borderColor == "#FF0000") {
- canctx.lineWidth = 2;
- canctx.setLineDash(redBorderDash);
- }
- if (color === "transparent") canctx.lineWidth = 2;
- canctx.stroke();
- if (color === "#ffd300") {
- // Fill in
- canctx.beginPath();
- canctx.arc(size / 2, size / 2, size / 2 * 0.6, 0, 2 * Math.PI);
- canctx.fillStyle = "rgba(255,255,255,0.5)";
- canctx.fill();
- canctx.translate(size / 2, size / 2);
- canctx.rotate(angle / 16);
- canctx.translate(-size / 2, -size / 2);
- canctx.globalCompositeOperation = "multiply";
- drawText(canctx, "$", color, size - 2, size / 2, size / 2 + 1);
- drawText(canctx, "$", color, size - 2, size / 2, size / 2 + 1);
- }
- cachedGraphics[key] = can;
- }
- ctx.drawImage(cachedGraphics[key], Math.round(x - size / 2), Math.round(y - size / 2));
-}
-function drawFuzzyBall(ctx, color, width, x, y) {
- width = Math.max(width, 2);
- const key = "fuzzy-circle" + color + "_" + width;
- if (!color?.startsWith("#")) debugger;
- const size = Math.round(width * 3);
- if (!size || isNaN(size)) {
- debugger;
- return;
- }
- if (!cachedGraphics[key]) {
- const can = document.createElement("canvas");
- can.width = size;
- can.height = size;
- const canctx = can.getContext("2d");
- const gradient = canctx.createRadialGradient(size / 2, size / 2, 0, size / 2, size / 2, size / 2);
- gradient.addColorStop(0, color);
- gradient.addColorStop(0.3, color + "88");
- gradient.addColorStop(0.6, color + "22");
- gradient.addColorStop(1, "transparent");
- canctx.fillStyle = gradient;
- canctx.fillRect(0, 0, size, size);
- cachedGraphics[key] = can;
- }
- ctx.drawImage(cachedGraphics[key], Math.round(x - size / 2), Math.round(y - size / 2));
-}
-function drawBrick(gameState, ctx, color, x, y, offset = 0, borderOnly) {
- const tlx = Math.ceil(x - gameState.brickWidth / 2);
- const tly = Math.ceil(y - gameState.brickWidth / 2);
- const brx = Math.ceil(x + gameState.brickWidth / 2) - 1;
- const bry = Math.ceil(y + gameState.brickWidth / 2) - 1;
- const width = brx - tlx, height = bry - tly;
- const key = "brick" + color + "_" + "_" + width + "_" + height + "_" + offset + "_" + borderOnly + "_";
- if (!cachedGraphics[key]) {
- const can = document.createElement("canvas");
- can.width = width;
- can.height = height;
- const bord = 4;
- const cornerRadius = 2;
- const canctx = can.getContext("2d");
- canctx.fillStyle = color;
- canctx.setLineDash(offset !== -1 ? redBorderDash : emptyArray);
- canctx.lineDashOffset = offset;
- canctx.strokeStyle = offset !== -1 && "#FF000033" || color;
- canctx.lineJoin = "round";
- canctx.lineWidth = bord;
- roundRect(canctx, bord / 2, bord / 2, width - bord, height - bord, cornerRadius);
- if (!borderOnly) canctx.fill();
- canctx.stroke();
- cachedGraphics[key] = can;
- }
- ctx.drawImage(cachedGraphics[key], tlx, tly, width, height);
-// It's not easy to have a 1px gap between bricks without antialiasing
-}
-function roundRect(ctx, x, y, width, height, radius) {
- ctx.beginPath();
- ctx.moveTo(x + radius, y);
- ctx.lineTo(x + width - radius, y);
- ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
- ctx.lineTo(x + width, y + height - radius);
- ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
- ctx.lineTo(x + radius, y + height);
- ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
- ctx.lineTo(x, y + radius);
- ctx.quadraticCurveTo(x, y, x + radius, y);
- ctx.closePath();
-}
-function drawIMG(ctx, img, size, x, y) {
- const key = "svg" + img + "_" + size + "_" + img.complete;
- if (!cachedGraphics[key]) {
- const can = document.createElement("canvas");
- can.width = size;
- can.height = size;
- const canctx = can.getContext("2d");
- const ratio = size / Math.max(img.width, img.height);
- const w = img.width * ratio;
- const h = img.height * ratio;
- canctx.drawImage(img, (size - w) / 2, (size - h) / 2, w, h);
- cachedGraphics[key] = can;
- }
- ctx.drawImage(cachedGraphics[key], Math.round(x - size / 2), Math.round(y - size / 2));
-}
-function drawText(ctx, text, color, fontSize, x, y, left = false) {
- const key = "text" + text + "_" + color + "_" + fontSize + "_" + left;
- if (!cachedGraphics[key]) {
- const can = document.createElement("canvas");
- can.width = fontSize * text.length;
- can.height = fontSize;
- const canctx = can.getContext("2d");
- canctx.fillStyle = color;
- canctx.textAlign = left ? "left" : "center";
- canctx.textBaseline = "middle";
- canctx.font = fontSize + "px monospace";
- canctx.fillText(text, left ? 0 : can.width / 2, can.height / 2, can.width);
- cachedGraphics[key] = can;
- }
- ctx.drawImage(cachedGraphics[key], left ? x : Math.round(x - cachedGraphics[key].width / 2), Math.round(y - cachedGraphics[key].height / 2));
-}
-const scoreDisplay = document.getElementById("score");
-const menuLabel = document.getElementById("menuLabel");
-const emptyArray = [];
-const redBorderDash = [
- 5,
- 5
-];
-function getDashOffset(gameState) {
- if ((0, _options.isOptionOn)("basic")) return 0;
- return Math.floor(gameState.levelTime % 500 / 500 * 10) % 10;
-}
-let wakeLock = null, wakeLockPending = false;
-function askForWakeLock(gameState) {
- if (gameState.startParams.computer_controlled && !wakeLock && !wakeLockPending) {
- wakeLockPending = true;
- try {
- navigator.wakeLock.request("screen").then((lock)=>{
- wakeLock = lock;
- wakeLockPending = false;
- lock.addEventListener("release", ()=>{
- // the wake lock has been released
- wakeLock = null;
- });
- });
- } catch (e) {
- console.warn("askForWakeLock error", e);
- }
- }
-}
-
-},{"./gameStateMutators":"9ZeQl","./game_utils":"cEeac","./i18n/i18n":"eNPRm","./game":"edeGs","./options":"d5NoS","./pure_functions":"6pQh7","@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3"}],"caCAf":[function(require,module,exports,__globalThis) {
+},{"./game_utils":"cEeac","./i18n/i18n":"eNPRm","./settings":"5blfu","./render":"9AS2t","./gameOver":"caCAf","./game":"edeGs","./recording":"godmD","./options":"d5NoS","./pure_functions":"6pQh7","./addToTotalScore":"ka4dG","./getLevelBackground":"7OIPf","./openUpgradesPicker":"2fQt0","@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3"}],"caCAf":[function(require,module,exports,__globalThis) {
var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js");
parcelHelpers.defineInteropFlag(exports);
parcelHelpers.export(exports, "addToTotalPlayTime", ()=>addToTotalPlayTime);
@@ -6722,7 +6697,54 @@ function getNearestUnlockHTML(gameState) {
`;
}
-},{"./asyncAlert":"rSqLY","./i18n/i18n":"eNPRm","./game_utils":"cEeac","./gameOver":"caCAf","./game":"edeGs","./loadGameData":"l1B4x","./pure_functions":"6pQh7","./settings":"5blfu","./get_level_unlock_condition":"a0fq0","./options":"d5NoS","@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3"}],"aQN6X":[function(require,module,exports,__globalThis) {
+},{"./asyncAlert":"rSqLY","./i18n/i18n":"eNPRm","./game_utils":"cEeac","./gameOver":"caCAf","./game":"edeGs","./loadGameData":"l1B4x","./pure_functions":"6pQh7","./settings":"5blfu","./get_level_unlock_condition":"a0fq0","./options":"d5NoS","@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3"}],"2n0gK":[function(require,module,exports,__globalThis) {
+var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js");
+parcelHelpers.defineInteropFlag(exports);
+if ("serviceWorker" in navigator && window.location.href.endsWith("/index.html?isPWA=true")) {
+ // @ts-ignore
+ const url = new URL(require("b04459cc43e56e8c"));
+ navigator.serviceWorker.register(url);
+}
+
+},{"b04459cc43e56e8c":"17ciJ","@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3"}],"17ciJ":[function(require,module,exports,__globalThis) {
+module.exports = require("9c7c7951fd7c4db6").getBundleURL('arAGi') + "sw-b71.41cdff1b.js";
+
+},{"9c7c7951fd7c4db6":"lgJ39"}],"lgJ39":[function(require,module,exports,__globalThis) {
+"use strict";
+var bundleURL = {};
+function getBundleURLCached(id) {
+ var value = bundleURL[id];
+ if (!value) {
+ value = getBundleURL();
+ bundleURL[id] = value;
+ }
+ return value;
+}
+function getBundleURL() {
+ try {
+ throw new Error();
+ } catch (err) {
+ var matches = ('' + err.stack).match(/(https?|file|ftp|(chrome|moz|safari-web)-extension):\/\/[^)\n]+/g);
+ if (matches) // The first two stack frames will be this function and getBundleURLCached.
+ // Use the 3rd one, which will be a runtime in the original bundle.
+ return getBaseURL(matches[2]);
+ }
+ return '/';
+}
+function getBaseURL(url) {
+ return ('' + url).replace(/^((?:https?|file|ftp|(chrome|moz|safari-web)-extension):\/\/.+)\/[^/]+$/, '$1') + '/';
+}
+// TODO: Replace uses with `new URL(url).origin` when ie11 is no longer supported.
+function getOrigin(url) {
+ var matches = ('' + url).match(/(https?|file|ftp|(chrome|moz|safari-web)-extension):\/\/[^/]+/);
+ if (!matches) throw new Error('Origin not found');
+ return matches[0];
+}
+exports.getBundleURL = getBundleURLCached;
+exports.getBaseURL = getBaseURL;
+exports.getOrigin = getOrigin;
+
+},{}],"aQN6X":[function(require,module,exports,__globalThis) {
var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js");
parcelHelpers.defineInteropFlag(exports);
parcelHelpers.export(exports, "getRunLevels", ()=>getRunLevels);
diff --git a/src/game.less b/src/game.less
index 7e90213..5b855f7 100644
--- a/src/game.less
+++ b/src/game.less
@@ -8,7 +8,6 @@
box-sizing: border-box;
}
-@purple: #6262ea;
body {
margin: 0;
@@ -648,8 +647,12 @@ h2.histogram-title strong {
left: 50vw;
top: 50vh;
font-size: 60px;
- opacity: 0.9;
+ font-weight: bold;
+ opacity: 0.8;
background: none;
+ text-shadow: 2px 0 #000, -2px 0 #000, 0 2px #000, 0 -2px #000,
+ 1px 1px #000, -1px -1px #000, 1px -1px #000, -1px 1px #000;
+ transform: translate(-50%,-50%);
}
}
@@ -709,7 +712,7 @@ h2.histogram-title strong {
position: relative;
> div {
- background: @purple;
+ background: @palette_b;
position: absolute;
inset: 0;
transform-origin: top left;
@@ -729,7 +732,7 @@ h2.histogram-title strong {
content: "";
position: absolute;
inset: 0;
- background: linear-gradient(-45deg, @purple, transparent);
+ background: linear-gradient(-45deg, @palette_b, transparent);
mix-blend-mode: screen;
opacity: 0.3;
}
@@ -739,3 +742,25 @@ h2.histogram-title strong {
opacity: 0.8;
color: #8a8a8a;
}
+
+@palette_B:black;
+@palette_W:#FFFFFF;
+@palette_g:#231f20;
+@palette_y:#FFD300;
+@palette_b:#6262EA;
+@palette_t:#5DA3EA;
+@palette_s:#E67070;
+@palette_r:#e32119;
+@palette_R:#ab0c0c;
+@palette_c:#59EEA3;
+@palette_G:#A1F051;
+@palette_v:#A664E8;
+@palette_p:#E869E8;
+@palette_a:#5BECEC;
+@palette_C:#53EE53;
+@palette_S:#F44848;
+@palette_P:#E66BA8;
+@palette_O:#F29E4A;
+@palette_k:#618227;
+@palette_e:#e1c8b4;
+@palette_l:#9b9fa;
\ No newline at end of file
diff --git a/src/game.ts b/src/game.ts
index ef81827..178b31f 100644
--- a/src/game.ts
+++ b/src/game.ts
@@ -82,7 +82,7 @@ import { monitorLevelsUnlocks } from "./monitorLevelsUnlocks";
import { levelEditorMenuEntry } from "./levelEditor";
import { categories } from "./upgrades";
import { reasonLevelIsLocked } from "./get_level_unlock_condition";
-import { toast } from "./toast";
+import {clearToasts, toast} from "./toast";
export async function play() {
if (await applyFullScreenChoice()) return;
@@ -263,8 +263,12 @@ function startPlayCountDown() {
play();
}, 3000),
);
+ timers.push(setTimeout(() => clearToasts(), 3500));
+
}
function stopPlayCountDown() {
+ if(!timers.length) return
+ clearToasts()
timers.forEach((id) => clearTimeout(id));
timers.length = 0;
}
diff --git a/src/gameStateMutators.ts b/src/gameStateMutators.ts
index 5bfef54..a5498df 100644
--- a/src/gameStateMutators.ts
+++ b/src/gameStateMutators.ts
@@ -12,7 +12,7 @@ import {
import {
brickCenterX,
- brickCenterY,
+ brickCenterY, canvasCenterX,
currentLevelInfo,
distance2,
distanceBetween,
@@ -27,7 +27,7 @@ import {
reachRedRowIndex,
shouldPierceByColor,
telekinesisEffectRate,
- yoyoEffectRate,
+ yoyoEffectRate, zoneLeftBorderX, zoneRightBorderX,
} from "./game_utils";
import { t } from "./i18n/i18n";
@@ -1184,7 +1184,7 @@ export function gameStateTick(
if (
gameState.perks.wrap_right > 1 &&
hitBorder % 2 &&
- coin.previousX > gameState.offsetX + gameState.gameZoneWidth / 2
+ coin.previousX > canvasCenterX(gameState)
) {
schedulGameSound(gameState, "plouf", coin.x, 1);
coin.x = gameState.offsetX + gameState.coinSize;
@@ -1395,7 +1395,7 @@ export function gameStateTick(
if (gameState.perks.wind) {
const windD =
((gameState.puckPosition -
- (gameState.offsetX + gameState.gameZoneWidth / 2)) /
+ canvasCenterX(gameState)) /
gameState.gameZoneWidth) *
2 *
gameState.perks.wind;
@@ -1529,7 +1529,7 @@ export function gameStateTick(
) {
makeParticle(
gameState,
- gameState.offsetXRoundedDown,
+ zoneLeftBorderX(gameState),
Math.random() * gameState.gameZoneHeight,
5,
(Math.random() - 0.5) * 10,
@@ -1546,7 +1546,7 @@ export function gameStateTick(
) {
makeParticle(
gameState,
- gameState.offsetXRoundedDown + gameState.gameZoneWidth,
+ zoneRightBorderX(gameState),
Math.random() * gameState.gameZoneHeight,
-5,
(Math.random() - 0.5) * 10,
@@ -1694,20 +1694,21 @@ export function ballTick(gameState: GameState, ball: Ball, frames: number) {
if (gameState.perks.steering) {
const delta = gameState.puckPosition - gameState.lastPuckPosition;
- const angle =
- Math.atan2(ball.vy, ball.vx) +
- ((((delta / gameState.gameZoneWidth) * Math.PI) / 2) *
- gameState.perks.steering *
- frames) /
- 2;
- const d = Math.sqrt(ball.vy * ball.vy + ball.vx * ball.vx);
- ball.vy = Math.sin(angle) * d;
- ball.vx = Math.cos(angle) * d;
- console.log({
- delta,
- angle,
- d,
- });
+ if(Math.abs(delta)>1) {
+ const angle =
+ Math.atan2(ball.vy, ball.vx) +
+ ((((delta / gameState.gameZoneWidth) * Math.PI) / 2) *
+ gameState.perks.steering *
+ frames) /
+ 2;
+ const d = Math.sqrt(ball.vy * ball.vy + ball.vx * ball.vx);
+ ball.vy = Math.sin(angle) * d;
+ ball.vx = Math.cos(angle) * d;
+ if (Math.random() < frames && !isOptionOn('basic')) {
+ makeParticle(gameState, ball.x, ball.y, -ball.vx / 10, -ball.vy / 10,
+ '#6262EA', true, 8, 500)
+ }
+ }
}
// Bounces
diff --git a/src/game_utils.ts b/src/game_utils.ts
index 34d606b..21c7178 100644
--- a/src/game_utils.ts
+++ b/src/game_utils.ts
@@ -12,6 +12,7 @@ import { t } from "./i18n/i18n";
import { clamp } from "./pure_functions";
import { getSettingValue, getTotalScore } from "./settings";
import { isOptionOn } from "./options";
+import {gameCanvas} from "./render";
export function describeLevel(level: Level) {
let bricks = 0,
@@ -345,3 +346,14 @@ export function escapeAttribute(str: String) {
.replace(/"/gi, """)
.replace(/'/gi, "'");
}
+
+export function canvasCenterX(gameState:GameState){
+ return gameState.canvasWidth/2
+}
+export function zoneLeftBorderX(gameState:GameState){
+
+ return gameState.offsetXRoundedDown - 1
+}
+export function zoneRightBorderX(gameState:GameState){
+ return gameCanvas.width - gameState.offsetXRoundedDown + 1
+}
\ No newline at end of file
diff --git a/src/render.ts b/src/render.ts
index ac3f091..b784d4b 100644
--- a/src/render.ts
+++ b/src/render.ts
@@ -10,7 +10,7 @@ import {
max_levels,
reachRedRowIndex,
telekinesisEffectRate,
- yoyoEffectRate,
+ yoyoEffectRate, zoneLeftBorderX, zoneRightBorderX,
} from "./game_utils";
import { colorString, GameState } from "./types";
import { t } from "./i18n/i18n";
@@ -517,9 +517,9 @@ export function render(gameState: GameState) {
ctx,
gameState,
(redLeftSide && "#FF0000") || "#FFFFFF",
- gameState.offsetXRoundedDown - 1,
+ zoneLeftBorderX(gameState),
0,
- gameState.offsetXRoundedDown - 1,
+ zoneLeftBorderX(gameState),
height,
1,
);
@@ -528,9 +528,9 @@ export function render(gameState: GameState) {
ctx,
gameState,
(redRightSide && "#FF0000") || "#FFFFFF",
- width - gameState.offsetXRoundedDown + 1,
+ zoneRightBorderX(gameState),
0,
- width - gameState.offsetXRoundedDown + 1,
+ zoneRightBorderX(gameState),
height,
1,
);
@@ -565,9 +565,9 @@ export function render(gameState: GameState) {
ctx,
gameState,
"#FF0000",
- gameState.offsetXRoundedDown,
+ zoneLeftBorderX(gameState),
1,
- width - gameState.offsetXRoundedDown,
+ zoneRightBorderX(gameState),
1,
1,
);
diff --git a/src/toast.ts b/src/toast.ts
index 06ab73b..05866bb 100644
--- a/src/toast.ts
+++ b/src/toast.ts
@@ -3,13 +3,16 @@ div.classList = "hidden toast";
document.body.appendChild(div);
let timeout: NodeJS.Timeout | undefined;
export function toast(html: string, className = "") {
+ clearToasts()
div.classList = "toast visible " + className;
div.innerHTML = html;
+ timeout = setTimeout(clearToasts, 1500);
+}
+
+export function clearToasts(){
if (timeout) {
clearTimeout(timeout);
+ timeout = undefined
}
- timeout = setTimeout(() => {
- timeout = undefined;
- div.classList = "hidden toast";
- }, 1500);
-}
+ div.classList = "hidden toast";
+}
\ No newline at end of file