mirror of
https://gitlab.com/lecarore/breakout71.git
synced 2025-04-20 12:15:06 -04:00
Added particle and sound effect when coin drops below the "waterline" of the puck
This commit is contained in:
parent
354a6490e9
commit
06843047d2
9 changed files with 162 additions and 82 deletions
17
Readme.md
17
Readme.md
|
@ -26,19 +26,12 @@ languages, I may add features again.
|
||||||
## To do
|
## To do
|
||||||
|
|
||||||
- redo video presentation
|
- redo video presentation
|
||||||
- chill game mode, to just relax your mind :
|
|
||||||
- no 7 levels limit
|
|
||||||
- no upgrades offered at the end of the level
|
|
||||||
- get a random perk
|
|
||||||
- every 7 level it's replaced by another random perk
|
|
||||||
- every 7 levels, +10 base combo and +1 piece
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Done
|
## Done
|
||||||
|
|
||||||
|
- measured and improve the performance (test here https://breakout.lecaro.me/?stresstest)
|
||||||
- added a few levels
|
- added a few levels
|
||||||
- autoplay mode (with wake lock and computer play)
|
- autoplay mode (with wake lock and computer play https://breakout.lecaro.me/?autoplay )
|
||||||
- slower coins fall once they are past the paddle
|
- slower coins fall once they are past the paddle
|
||||||
- in game level editor
|
- in game level editor
|
||||||
- allow loading newer save in outdated app (for rollback)
|
- allow loading newer save in outdated app (for rollback)
|
||||||
|
@ -373,6 +366,12 @@ languages, I may add features again.
|
||||||
|
|
||||||
## UX / gameplay
|
## UX / gameplay
|
||||||
|
|
||||||
|
- chill game mode, to just relax your mind :
|
||||||
|
- no 7 levels limit
|
||||||
|
- no upgrades offered at the end of the level
|
||||||
|
- get a random perk
|
||||||
|
- every 7 level it's replaced by another random perk
|
||||||
|
- every 7 levels, +10 base combo and +1 piece
|
||||||
- avoid showing a +1 and -1 at the same time when a combo increase is reset
|
- avoid showing a +1 and -1 at the same time when a combo increase is reset
|
||||||
- explain to iOS users how to add the app to home screen to get fullscreen
|
- explain to iOS users how to add the app to home screen to get fullscreen
|
||||||
- delayed start on mobile to let users place the puck where they want
|
- delayed start on mobile to let users place the puck where they want
|
||||||
|
|
95
dist/index.html
vendored
95
dist/index.html
vendored
|
@ -723,6 +723,7 @@ parcelHelpers.export(exports, "hasBrick", ()=>hasBrick);
|
||||||
parcelHelpers.export(exports, "hitsSomething", ()=>hitsSomething);
|
parcelHelpers.export(exports, "hitsSomething", ()=>hitsSomething);
|
||||||
parcelHelpers.export(exports, "tick", ()=>tick);
|
parcelHelpers.export(exports, "tick", ()=>tick);
|
||||||
parcelHelpers.export(exports, "lastMeasuredFPS", ()=>lastMeasuredFPS);
|
parcelHelpers.export(exports, "lastMeasuredFPS", ()=>lastMeasuredFPS);
|
||||||
|
parcelHelpers.export(exports, "startWork", ()=>startWork);
|
||||||
parcelHelpers.export(exports, "creativeModeThreshold", ()=>creativeModeThreshold);
|
parcelHelpers.export(exports, "creativeModeThreshold", ()=>creativeModeThreshold);
|
||||||
parcelHelpers.export(exports, "openMainMenu", ()=>openMainMenu);
|
parcelHelpers.export(exports, "openMainMenu", ()=>openMainMenu);
|
||||||
parcelHelpers.export(exports, "confirmRestart", ()=>confirmRestart);
|
parcelHelpers.export(exports, "confirmRestart", ()=>confirmRestart);
|
||||||
|
@ -979,7 +980,6 @@ function tick() {
|
||||||
gameState.runStatistics.runTime += timeDeltaMs * frames;
|
gameState.runStatistics.runTime += timeDeltaMs * frames;
|
||||||
(0, _gameStateMutators.gameStateTick)(gameState, frames);
|
(0, _gameStateMutators.gameStateTick)(gameState, frames);
|
||||||
}
|
}
|
||||||
startWork('render');
|
|
||||||
if (gameState.running || gameState.needsRender) {
|
if (gameState.running || gameState.needsRender) {
|
||||||
gameState.needsRender = false;
|
gameState.needsRender = false;
|
||||||
(0, _render.render)(gameState);
|
(0, _render.render)(gameState);
|
||||||
|
@ -998,18 +998,20 @@ setInterval(()=>{
|
||||||
lastMeasuredFPS = FPSCounter;
|
lastMeasuredFPS = FPSCounter;
|
||||||
FPSCounter = 0;
|
FPSCounter = 0;
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
const showStats = window.location.search.includes("stress");
|
||||||
let total = {};
|
let total = {};
|
||||||
let lastTick = performance.now();
|
let lastTick = performance.now();
|
||||||
let doing = '';
|
let doing = '';
|
||||||
function startWork(what) {
|
function startWork(what) {
|
||||||
|
if (!showStats) return;
|
||||||
const newNow = performance.now();
|
const newNow = performance.now();
|
||||||
if (doing) total[doing] = (total[doing] || 0) + (newNow - lastTick);
|
if (doing) total[doing] = (total[doing] || 0) + (newNow - lastTick);
|
||||||
lastTick = newNow;
|
lastTick = newNow;
|
||||||
doing = what;
|
doing = what;
|
||||||
}
|
}
|
||||||
setInterval(()=>{
|
if (showStats) setInterval(()=>{
|
||||||
const totalTime = (0, _gameUtils.sumOfValues)(total);
|
const totalTime = (0, _gameUtils.sumOfValues)(total);
|
||||||
console.log((0, _gameStateMutators.liveCount)(gameState.coins) + ' coins\n' + Object.entries(total).sort((a, b)=>b[1] - a[1]).filter((a)=>a[1] > 1).map((t)=>t[0] + ':' + (t[1] / totalTime * 100).toFixed(2) + '% (' + t[1] + 'ms)').join('\n'));
|
console.debug((0, _gameStateMutators.liveCount)(gameState.coins) + ' coins\n' + Object.entries(total).sort((a, b)=>b[1] - a[1]).filter((a)=>a[1] > 1).map((t)=>t[0] + ':' + (t[1] / totalTime * 100).toFixed(2) + '% (' + t[1] + 'ms)').join('\n'));
|
||||||
total = {};
|
total = {};
|
||||||
}, 2000);
|
}, 2000);
|
||||||
setInterval(()=>{
|
setInterval(()=>{
|
||||||
|
@ -1435,30 +1437,31 @@ function restart(params) {
|
||||||
(0, _gameStateMutators.setLevel)(gameState, 0);
|
(0, _gameStateMutators.setLevel)(gameState, 0);
|
||||||
if (params?.computer_controlled) play();
|
if (params?.computer_controlled) play();
|
||||||
}
|
}
|
||||||
if (window.location.search.includes("autoplay")) startComputerControlledGame();
|
if (window.location.search.match(/autoplay|stress/)) {
|
||||||
else if (window.location.search.includes("stress")) {
|
startComputerControlledGame();
|
||||||
if (!(0, _options.isOptionOn)('show_fps')) (0, _options.toggleOption)('show_fps');
|
if (!(0, _options.isOptionOn)('show_fps')) (0, _options.toggleOption)('show_fps');
|
||||||
restart({
|
|
||||||
level: (0, _loadGameData.allLevels).find((l)=>l.name == 'Worms'),
|
|
||||||
perks: {
|
|
||||||
base_combo: 5000,
|
|
||||||
pierce: 20,
|
|
||||||
rainbow: 3,
|
|
||||||
sapper: 2,
|
|
||||||
etherealcoins: 1
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else restart({});
|
} else restart({});
|
||||||
function startComputerControlledGame() {
|
function startComputerControlledGame() {
|
||||||
const perks = {
|
const perks = {
|
||||||
base_combo: 20,
|
base_combo: 20,
|
||||||
pierce: 3
|
pierce: 3
|
||||||
};
|
};
|
||||||
|
if (window.location.search.includes("stress")) Object.assign(perks, {
|
||||||
|
base_combo: 5000,
|
||||||
|
pierce: 20,
|
||||||
|
rainbow: 3,
|
||||||
|
sapper: 2,
|
||||||
|
etherealcoins: 1,
|
||||||
|
bricks_attract_ball: 1,
|
||||||
|
respawn: 3
|
||||||
|
});
|
||||||
|
else {
|
||||||
for(let i = 0; i < 10; i++){
|
for(let i = 0; i < 10; i++){
|
||||||
const u = (0, _gameUtils.sample)((0, _loadGameData.upgrades));
|
const u = (0, _gameUtils.sample)((0, _loadGameData.upgrades));
|
||||||
perks[u.id] ||= Math.floor(Math.random() * u.max) + 1;
|
perks[u.id] ||= Math.floor(Math.random() * u.max) + 1;
|
||||||
}
|
}
|
||||||
perks.superhot = 0;
|
perks.superhot = 0;
|
||||||
|
}
|
||||||
restart({
|
restart({
|
||||||
level: (0, _gameUtils.sample)((0, _loadGameData.allLevels).filter((l)=>l.color === "#000000")),
|
level: (0, _gameUtils.sample)((0, _loadGameData.allLevels).filter((l)=>l.color === "#000000")),
|
||||||
computer_controlled: true,
|
computer_controlled: true,
|
||||||
|
@ -2287,7 +2290,7 @@ const rawUpgrades = [
|
||||||
threshold: 215000,
|
threshold: 215000,
|
||||||
gift: false,
|
gift: false,
|
||||||
id: "bricks_attract_ball",
|
id: "bricks_attract_ball",
|
||||||
max: 3,
|
max: 1,
|
||||||
name: (0, _i18N.t)("upgrades.bricks_attract_ball.name"),
|
name: (0, _i18N.t)("upgrades.bricks_attract_ball.name"),
|
||||||
help: (lvl)=>(0, _i18N.t)("upgrades.bricks_attract_ball.tooltip", {
|
help: (lvl)=>(0, _i18N.t)("upgrades.bricks_attract_ball.tooltip", {
|
||||||
count: lvl * 3
|
count: lvl * 3
|
||||||
|
@ -2421,14 +2424,22 @@ try {
|
||||||
function getSettingValue(key, defaultValue) {
|
function getSettingValue(key, defaultValue) {
|
||||||
return cachedSettings[key] ?? defaultValue;
|
return cachedSettings[key] ?? defaultValue;
|
||||||
}
|
}
|
||||||
|
// We avoid using localstorage synchronously for perf reasons
|
||||||
|
let needsSaving = new Set();
|
||||||
function setSettingValue(key, value) {
|
function setSettingValue(key, value) {
|
||||||
|
if (cachedSettings[key] !== value) {
|
||||||
|
needsSaving.add(key);
|
||||||
cachedSettings[key] = value;
|
cachedSettings[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setInterval(()=>{
|
||||||
try {
|
try {
|
||||||
localStorage.setItem(key, JSON.stringify(value));
|
for (let key of needsSaving)localStorage.setItem(key, JSON.stringify(cachedSettings[key]));
|
||||||
|
needsSaving.clear();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn(e);
|
console.warn(e);
|
||||||
}
|
}
|
||||||
}
|
}, 500);
|
||||||
function getTotalScore() {
|
function getTotalScore() {
|
||||||
return getSettingValue("breakout_71_total_score", 0);
|
return getSettingValue("breakout_71_total_score", 0);
|
||||||
}
|
}
|
||||||
|
@ -2439,7 +2450,7 @@ function getCurrentMaxParticles() {
|
||||||
return getCurrentMaxCoins();
|
return getCurrentMaxCoins();
|
||||||
}
|
}
|
||||||
function cycleMaxCoins() {
|
function cycleMaxCoins() {
|
||||||
setSettingValue("max_coins", (getSettingValue("max_coins", 2) + 1) % 10);
|
setSettingValue("max_coins", (getSettingValue("max_coins", 2) + 1) % 7);
|
||||||
}
|
}
|
||||||
|
|
||||||
},{"@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3"}],"gkKU3":[function(require,module,exports,__globalThis) {
|
},{"@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3"}],"gkKU3":[function(require,module,exports,__globalThis) {
|
||||||
|
@ -2628,7 +2639,7 @@ const sounds = {
|
||||||
},
|
},
|
||||||
plouf: (volume, pan)=>{
|
plouf: (volume, pan)=>{
|
||||||
if (!(0, _options.isOptionOn)("sound")) return;
|
if (!(0, _options.isOptionOn)("sound")) return;
|
||||||
createSingleBounceSound(240, pan, volume * 0.5);
|
createSingleBounceSound(500, pan, volume * 0.5);
|
||||||
// createWaterDropSound(800, pan, volume*0.2, 0.2,'triangle')
|
// createWaterDropSound(800, pan, volume*0.2, 0.2,'triangle')
|
||||||
},
|
},
|
||||||
comboIncreaseMaybe: (volume, pan, combo)=>{
|
comboIncreaseMaybe: (volume, pan, combo)=>{
|
||||||
|
@ -3524,8 +3535,8 @@ function explodeBrick(gameState, index, ball, isExplosion) {
|
||||||
gameState.levelSpawnedCoins += coinsToSpawn;
|
gameState.levelSpawnedCoins += coinsToSpawn;
|
||||||
gameState.runStatistics.coins_spawned += coinsToSpawn;
|
gameState.runStatistics.coins_spawned += coinsToSpawn;
|
||||||
gameState.runStatistics.bricks_broken++;
|
gameState.runStatistics.bricks_broken++;
|
||||||
const maxCoins = (0, _settings.getCurrentMaxCoins)() * ((0, _options.isOptionOn)("basic") ? 0.5 : 1);
|
const maxCoins = (0, _settings.getCurrentMaxCoins)();
|
||||||
const spawnableCoins = liveCount(gameState.coins) > (0, _settings.getCurrentMaxCoins)() ? 1 : Math.floor(maxCoins - liveCount(gameState.coins)) / 3;
|
const spawnableCoins = liveCount(gameState.coins) > (0, _settings.getCurrentMaxCoins)() ? 1 : Math.floor((maxCoins - liveCount(gameState.coins)) / 2);
|
||||||
const pointsPerCoin = Math.max(1, Math.ceil(coinsToSpawn / spawnableCoins));
|
const pointsPerCoin = Math.max(1, Math.ceil(coinsToSpawn / spawnableCoins));
|
||||||
while(coinsToSpawn > 0){
|
while(coinsToSpawn > 0){
|
||||||
const points = Math.min(pointsPerCoin, coinsToSpawn);
|
const points = Math.min(pointsPerCoin, coinsToSpawn);
|
||||||
|
@ -3874,7 +3885,10 @@ frames = 1) {
|
||||||
if (gameState.perks.helium && !(0, _options.isOptionOn)("basic") && Math.random() < 0.1 * frames) makeParticle(gameState, coin.x, coin.y, 0, dvy * 10, gameState.perks.metamorphosis || (0, _options.isOptionOn)("colorful_coins") ? coin.color : "#ffd300", true, 5, 250);
|
if (gameState.perks.helium && !(0, _options.isOptionOn)("basic") && Math.random() < 0.1 * frames) makeParticle(gameState, coin.x, coin.y, 0, dvy * 10, gameState.perks.metamorphosis || (0, _options.isOptionOn)("colorful_coins") ? coin.color : "#ffd300", true, 5, 250);
|
||||||
const speed = (Math.abs(coin.vx) + Math.abs(coin.vy)) * 10;
|
const speed = (Math.abs(coin.vx) + Math.abs(coin.vy)) * 10;
|
||||||
const hitBorder = bordersHitCheck(gameState, coin, coin.size / 2, frames);
|
const hitBorder = bordersHitCheck(gameState, coin, coin.size / 2, frames);
|
||||||
if (coin.previousY < gameState.gameZoneHeight && coin.y > gameState.gameZoneHeight && coin.vy > 0 && speed > 20) schedulGameSound(gameState, "plouf", coin.x, (0, _pureFunctions.clamp)(speed, 20, 100) / 100 * 0.2);
|
if (coin.previousY < gameState.gameZoneHeight && coin.y > gameState.gameZoneHeight && coin.vy > 0 && speed > 20) {
|
||||||
|
schedulGameSound(gameState, "plouf", coin.x, (0, _pureFunctions.clamp)(speed, 20, 100) / 100 * 0.2);
|
||||||
|
if (!(0, _options.isOptionOn)('basic')) makeParticle(gameState, coin.x, gameState.gameZoneHeight, -coin.vx / 5, -coin.vy / 5, coin.color, false);
|
||||||
|
}
|
||||||
if (coin.y > gameState.gameZoneHeight - coinRadius - gameState.puckHeight && coin.y < gameState.gameZoneHeight + gameState.puckHeight + coin.vy && Math.abs(coin.x - gameState.puckPosition) < coinRadius + gameState.puckWidth / 2 + // a bit of margin to be nice , negative in case it's a negative coin
|
if (coin.y > gameState.gameZoneHeight - coinRadius - gameState.puckHeight && coin.y < gameState.gameZoneHeight + gameState.puckHeight + coin.vy && Math.abs(coin.x - gameState.puckPosition) < coinRadius + gameState.puckWidth / 2 + // a bit of margin to be nice , negative in case it's a negative coin
|
||||||
gameState.puckHeight * (coin.points ? 1 : -1)) {
|
gameState.puckHeight * (coin.points ? 1 : -1)) {
|
||||||
addToScore(gameState, coin);
|
addToScore(gameState, coin);
|
||||||
|
@ -4298,6 +4312,7 @@ var _i18N = require("./i18n/i18n");
|
||||||
var _game = require("./game");
|
var _game = require("./game");
|
||||||
var _options = require("./options");
|
var _options = require("./options");
|
||||||
var _pureFunctions = require("./pure_functions");
|
var _pureFunctions = require("./pure_functions");
|
||||||
|
var _settings = require("./settings");
|
||||||
const gameCanvas = document.getElementById("game");
|
const gameCanvas = document.getElementById("game");
|
||||||
const ctx = gameCanvas.getContext("2d", {
|
const ctx = gameCanvas.getContext("2d", {
|
||||||
alpha: false
|
alpha: false
|
||||||
|
@ -4316,6 +4331,7 @@ const haloCanvasCtx = haloCanvas.getContext("2d", {
|
||||||
});
|
});
|
||||||
const haloScale = 16;
|
const haloScale = 16;
|
||||||
function render(gameState) {
|
function render(gameState) {
|
||||||
|
(0, _game.startWork)('render:init');
|
||||||
const level = (0, _gameUtils.currentLevelInfo)(gameState);
|
const level = (0, _gameUtils.currentLevelInfo)(gameState);
|
||||||
const hasCombo = gameState.combo > (0, _gameStateMutators.baseCombo)(gameState);
|
const hasCombo = gameState.combo > (0, _gameStateMutators.baseCombo)(gameState);
|
||||||
const { width, height } = gameCanvas;
|
const { width, height } = gameCanvas;
|
||||||
|
@ -4326,7 +4342,11 @@ function render(gameState) {
|
||||||
});
|
});
|
||||||
else menuLabel.innerText = (0, _i18N.t)("play.menu_label");
|
else menuLabel.innerText = (0, _i18N.t)("play.menu_label");
|
||||||
const catchRate = gameState.levelSpawnedCoins ? (gameState.levelSpawnedCoins - gameState.levelLostCoins) / gameState.levelSpawnedCoins : 1;
|
const catchRate = gameState.levelSpawnedCoins ? (gameState.levelSpawnedCoins - gameState.levelLostCoins) / gameState.levelSpawnedCoins : 1;
|
||||||
|
(0, _game.startWork)('render:scoreDisplay');
|
||||||
scoreDisplay.innerHTML = ((0, _options.isOptionOn)("show_fps") || gameState.computer_controlled ? `
|
scoreDisplay.innerHTML = ((0, _options.isOptionOn)("show_fps") || gameState.computer_controlled ? `
|
||||||
|
<span>
|
||||||
|
${Math.floor((0, _gameStateMutators.liveCount)(gameState.coins) / (0, _settings.getCurrentMaxCoins)() * 100)} %
|
||||||
|
</span><span> / </span>
|
||||||
<span class="${Math.abs((0, _game.lastMeasuredFPS) - 60) < 2 && " " || Math.abs((0, _game.lastMeasuredFPS) - 60) < 10 && "good" || "bad"}">
|
<span class="${Math.abs((0, _game.lastMeasuredFPS) - 60) < 2 && " " || Math.abs((0, _game.lastMeasuredFPS) - 60) < 10 && "good" || "bad"}">
|
||||||
${0, _game.lastMeasuredFPS} FPS
|
${0, _game.lastMeasuredFPS} FPS
|
||||||
</span><span> / </span>
|
</span><span> / </span>
|
||||||
|
@ -4348,6 +4368,7 @@ function render(gameState) {
|
||||||
scoreDisplay.className = gameState.computer_controlled && "computer_controlled" || gameState.lastScoreIncrease > gameState.levelTime - 500 && "active" || "";
|
scoreDisplay.className = gameState.computer_controlled && "computer_controlled" || gameState.lastScoreIncrease > gameState.levelTime - 500 && "active" || "";
|
||||||
// Clear
|
// Clear
|
||||||
if (!(0, _options.isOptionOn)("basic") && level.svg && level.color === "#000000") {
|
if (!(0, _options.isOptionOn)("basic") && level.svg && level.color === "#000000") {
|
||||||
|
(0, _game.startWork)('render:halo:clear');
|
||||||
haloCanvasCtx.globalCompositeOperation = "source-over";
|
haloCanvasCtx.globalCompositeOperation = "source-over";
|
||||||
haloCanvasCtx.globalAlpha = 0.99;
|
haloCanvasCtx.globalAlpha = 0.99;
|
||||||
haloCanvasCtx.fillStyle = level.color;
|
haloCanvasCtx.fillStyle = level.color;
|
||||||
|
@ -4355,14 +4376,17 @@ function render(gameState) {
|
||||||
const brightness = (0, _options.isOptionOn)("extra_bright") ? 3 : 1;
|
const brightness = (0, _options.isOptionOn)("extra_bright") ? 3 : 1;
|
||||||
haloCanvasCtx.globalCompositeOperation = "lighten";
|
haloCanvasCtx.globalCompositeOperation = "lighten";
|
||||||
haloCanvasCtx.globalAlpha = 0.1 + 5 / ((0, _gameStateMutators.liveCount)(gameState.coins) + 10);
|
haloCanvasCtx.globalAlpha = 0.1 + 5 / ((0, _gameStateMutators.liveCount)(gameState.coins) + 10);
|
||||||
|
(0, _game.startWork)('render:halo:coins');
|
||||||
(0, _gameStateMutators.forEachLiveOne)(gameState.coins, (coin)=>{
|
(0, _gameStateMutators.forEachLiveOne)(gameState.coins, (coin)=>{
|
||||||
const color = getCoinRenderColor(gameState, coin);
|
const color = getCoinRenderColor(gameState, coin);
|
||||||
drawFuzzyBall(haloCanvasCtx, color, gameState.coinSize * 2 * brightness / haloScale, coin.x / haloScale, coin.y / haloScale);
|
drawFuzzyBall(haloCanvasCtx, color, gameState.coinSize * 2 * brightness / haloScale, coin.x / haloScale, coin.y / haloScale);
|
||||||
});
|
});
|
||||||
|
(0, _game.startWork)('render:halo:balls');
|
||||||
gameState.balls.forEach((ball)=>{
|
gameState.balls.forEach((ball)=>{
|
||||||
haloCanvasCtx.globalAlpha = 0.3 * (1 - (0, _gameUtils.ballTransparency)(ball, gameState));
|
haloCanvasCtx.globalAlpha = 0.3 * (1 - (0, _gameUtils.ballTransparency)(ball, gameState));
|
||||||
drawFuzzyBall(haloCanvasCtx, gameState.ballsColor, gameState.ballSize * 2 * brightness / haloScale, ball.x / haloScale, ball.y / haloScale);
|
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;
|
haloCanvasCtx.globalAlpha = 0.05;
|
||||||
gameState.bricks.forEach((color, index)=>{
|
gameState.bricks.forEach((color, index)=>{
|
||||||
if (!color) return;
|
if (!color) return;
|
||||||
|
@ -4370,6 +4394,7 @@ function render(gameState) {
|
||||||
drawFuzzyBall(haloCanvasCtx, color == "black" ? "#666666" : color, // Perf could really go down there because of the size of the halo
|
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);
|
Math.min(200, gameState.brickWidth * 1.5 * brightness) / haloScale, x / haloScale, y / haloScale);
|
||||||
});
|
});
|
||||||
|
(0, _game.startWork)('render:halo:particles');
|
||||||
haloCanvasCtx.globalCompositeOperation = "screen";
|
haloCanvasCtx.globalCompositeOperation = "screen";
|
||||||
(0, _gameStateMutators.forEachLiveOne)(gameState.particles, (flash)=>{
|
(0, _gameStateMutators.forEachLiveOne)(gameState.particles, (flash)=>{
|
||||||
const { x, y, time, color, size, duration } = flash;
|
const { x, y, time, color, size, duration } = flash;
|
||||||
|
@ -4383,6 +4408,7 @@ function render(gameState) {
|
||||||
ctx.imageSmoothingQuality = "high";
|
ctx.imageSmoothingQuality = "high";
|
||||||
ctx.drawImage(haloCanvas, 0, 0, width, height);
|
ctx.drawImage(haloCanvas, 0, 0, width, height);
|
||||||
ctx.imageSmoothingEnabled = false;
|
ctx.imageSmoothingEnabled = false;
|
||||||
|
(0, _game.startWork)('render:halo:pattern');
|
||||||
ctx.globalAlpha = 1;
|
ctx.globalAlpha = 1;
|
||||||
ctx.globalCompositeOperation = "multiply";
|
ctx.globalCompositeOperation = "multiply";
|
||||||
if (level.svg && background.width && background.complete) {
|
if (level.svg && background.width && background.complete) {
|
||||||
|
@ -4422,6 +4448,7 @@ function render(gameState) {
|
||||||
ctx.fillRect(0, 0, width, height);
|
ctx.fillRect(0, 0, width, height);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
(0, _game.startWork)('render:halo-basic');
|
||||||
ctx.globalAlpha = 1;
|
ctx.globalAlpha = 1;
|
||||||
ctx.globalCompositeOperation = "source-over";
|
ctx.globalCompositeOperation = "source-over";
|
||||||
ctx.fillStyle = level.color || "#000";
|
ctx.fillStyle = level.color || "#000";
|
||||||
|
@ -4433,6 +4460,7 @@ function render(gameState) {
|
||||||
drawBall(ctx, color, size, x, y);
|
drawBall(ctx, color, size, x, y);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
(0, _game.startWork)('render:explosionshake');
|
||||||
ctx.globalAlpha = 1;
|
ctx.globalAlpha = 1;
|
||||||
ctx.globalCompositeOperation = "source-over";
|
ctx.globalCompositeOperation = "source-over";
|
||||||
const lastExplosionDelay = Date.now() - gameState.lastExplosion + 5;
|
const lastExplosionDelay = Date.now() - gameState.lastExplosion + 5;
|
||||||
|
@ -4441,6 +4469,7 @@ function render(gameState) {
|
||||||
const amplitude = (gameState.perks.bigger_explosions + 1) * 50 / lastExplosionDelay;
|
const amplitude = (gameState.perks.bigger_explosions + 1) * 50 / lastExplosionDelay;
|
||||||
ctx.translate(Math.sin(Date.now()) * amplitude, Math.sin(Date.now() + 36) * amplitude);
|
ctx.translate(Math.sin(Date.now()) * amplitude, Math.sin(Date.now() + 36) * amplitude);
|
||||||
}
|
}
|
||||||
|
(0, _game.startWork)('render:coins');
|
||||||
// Coins
|
// Coins
|
||||||
ctx.globalAlpha = 1;
|
ctx.globalAlpha = 1;
|
||||||
(0, _gameStateMutators.forEachLiveOne)(gameState.coins, (coin)=>{
|
(0, _gameStateMutators.forEachLiveOne)(gameState.coins, (coin)=>{
|
||||||
|
@ -4452,6 +4481,7 @@ function render(gameState) {
|
||||||
// (color === "#ffd300" && "#ffd300") ||
|
// (color === "#ffd300" && "#ffd300") ||
|
||||||
hollow && color || gameState.level.color, coin.a);
|
hollow && color || gameState.level.color, coin.a);
|
||||||
});
|
});
|
||||||
|
(0, _game.startWork)('render:ball shade');
|
||||||
// Black shadow around balls
|
// Black shadow around balls
|
||||||
if (!(0, _options.isOptionOn)("basic")) {
|
if (!(0, _options.isOptionOn)("basic")) {
|
||||||
ctx.globalCompositeOperation = "source-over";
|
ctx.globalCompositeOperation = "source-over";
|
||||||
|
@ -4460,8 +4490,10 @@ function render(gameState) {
|
||||||
drawBall(ctx, level.color || "#000", gameState.ballSize * 6, ball.x, ball.y);
|
drawBall(ctx, level.color || "#000", gameState.ballSize * 6, ball.x, ball.y);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
(0, _game.startWork)('render:bricks');
|
||||||
ctx.globalCompositeOperation = "source-over";
|
ctx.globalCompositeOperation = "source-over";
|
||||||
renderAllBricks();
|
renderAllBricks();
|
||||||
|
(0, _game.startWork)('render:lights');
|
||||||
ctx.globalCompositeOperation = "screen";
|
ctx.globalCompositeOperation = "screen";
|
||||||
(0, _gameStateMutators.forEachLiveOne)(gameState.lights, (flash)=>{
|
(0, _gameStateMutators.forEachLiveOne)(gameState.lights, (flash)=>{
|
||||||
const { x, y, time, color, size, duration } = flash;
|
const { x, y, time, color, size, duration } = flash;
|
||||||
|
@ -4469,6 +4501,7 @@ function render(gameState) {
|
||||||
ctx.globalAlpha = Math.min(1, 2 - elapsed / duration * 2) * 0.5;
|
ctx.globalAlpha = Math.min(1, 2 - elapsed / duration * 2) * 0.5;
|
||||||
drawBrick(gameState, ctx, color, x, y, -1, gameState.perks.clairvoyant >= 2);
|
drawBrick(gameState, ctx, color, x, y, -1, gameState.perks.clairvoyant >= 2);
|
||||||
});
|
});
|
||||||
|
(0, _game.startWork)('render:texts');
|
||||||
ctx.globalCompositeOperation = "screen";
|
ctx.globalCompositeOperation = "screen";
|
||||||
(0, _gameStateMutators.forEachLiveOne)(gameState.texts, (flash)=>{
|
(0, _gameStateMutators.forEachLiveOne)(gameState.texts, (flash)=>{
|
||||||
const { x, y, time, color, size, duration } = flash;
|
const { x, y, time, color, size, duration } = flash;
|
||||||
|
@ -4477,6 +4510,7 @@ function render(gameState) {
|
||||||
ctx.globalCompositeOperation = "source-over";
|
ctx.globalCompositeOperation = "source-over";
|
||||||
drawText(ctx, flash.text, color, size, x, y - elapsed / 10);
|
drawText(ctx, flash.text, color, size, x, y - elapsed / 10);
|
||||||
});
|
});
|
||||||
|
(0, _game.startWork)('render:particles');
|
||||||
(0, _gameStateMutators.forEachLiveOne)(gameState.particles, (particle)=>{
|
(0, _gameStateMutators.forEachLiveOne)(gameState.particles, (particle)=>{
|
||||||
const { x, y, time, color, size, duration } = particle;
|
const { x, y, time, color, size, duration } = particle;
|
||||||
const elapsed = gameState.levelTime - time;
|
const elapsed = gameState.levelTime - time;
|
||||||
|
@ -4484,12 +4518,14 @@ function render(gameState) {
|
||||||
ctx.globalCompositeOperation = "screen";
|
ctx.globalCompositeOperation = "screen";
|
||||||
drawBall(ctx, color, size, x, y);
|
drawBall(ctx, color, size, x, y);
|
||||||
});
|
});
|
||||||
|
(0, _game.startWork)('render:extra_life');
|
||||||
if (gameState.perks.extra_life) {
|
if (gameState.perks.extra_life) {
|
||||||
ctx.globalAlpha = 1;
|
ctx.globalAlpha = 1;
|
||||||
ctx.globalCompositeOperation = "source-over";
|
ctx.globalCompositeOperation = "source-over";
|
||||||
ctx.fillStyle = gameState.puckColor;
|
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);
|
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.globalAlpha = 1;
|
||||||
ctx.globalCompositeOperation = "source-over";
|
ctx.globalCompositeOperation = "source-over";
|
||||||
gameState.balls.forEach((ball)=>{
|
gameState.balls.forEach((ball)=>{
|
||||||
|
@ -4517,10 +4553,11 @@ function render(gameState) {
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// The puck
|
(0, _game.startWork)('render:puck');
|
||||||
ctx.globalAlpha = 1;
|
ctx.globalAlpha = 1;
|
||||||
ctx.globalCompositeOperation = "source-over";
|
ctx.globalCompositeOperation = "source-over";
|
||||||
drawPuck(ctx, gameState.puckColor, gameState.puckWidth, gameState.puckHeight, 0, gameState.perks.concave_puck, gameState.perks.streak_shots && hasCombo ? getDashOffset(gameState) : -1);
|
drawPuck(ctx, gameState.puckColor, gameState.puckWidth, gameState.puckHeight, 0, gameState.perks.concave_puck, gameState.perks.streak_shots && hasCombo ? getDashOffset(gameState) : -1);
|
||||||
|
(0, _game.startWork)('render:combotext');
|
||||||
if (gameState.combo > 1) {
|
if (gameState.combo > 1) {
|
||||||
ctx.globalCompositeOperation = "source-over";
|
ctx.globalCompositeOperation = "source-over";
|
||||||
const comboText = "x " + gameState.combo;
|
const comboText = "x " + gameState.combo;
|
||||||
|
@ -4532,6 +4569,7 @@ function render(gameState) {
|
||||||
drawText(ctx, comboText, "#000", gameState.puckHeight, left + gameState.coinSize * 1.5, gameState.gameZoneHeight - gameState.puckHeight / 2, true);
|
drawText(ctx, comboText, "#000", gameState.puckHeight, left + gameState.coinSize * 1.5, gameState.gameZoneHeight - gameState.puckHeight / 2, true);
|
||||||
} else drawText(ctx, comboTextWidth > gameState.puckWidth ? gameState.combo.toString() : comboText, "#000", comboTextWidth > gameState.puckWidth ? 12 : 20, gameState.puckPosition, gameState.gameZoneHeight - gameState.puckHeight / 2, false);
|
} 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
|
// Borders
|
||||||
ctx.globalCompositeOperation = "source-over";
|
ctx.globalCompositeOperation = "source-over";
|
||||||
ctx.globalAlpha = 1;
|
ctx.globalAlpha = 1;
|
||||||
|
@ -4549,6 +4587,7 @@ function render(gameState) {
|
||||||
if (redTop) drawStraightLine(ctx, gameState, "#FF0000", gameState.offsetXRoundedDown, 1, width - gameState.offsetXRoundedDown, 1, 1);
|
if (redTop) drawStraightLine(ctx, gameState, "#FF0000", gameState.offsetXRoundedDown, 1, width - gameState.offsetXRoundedDown, 1, 1);
|
||||||
ctx.globalAlpha = 1;
|
ctx.globalAlpha = 1;
|
||||||
drawStraightLine(ctx, gameState, hasCombo && gameState.perks.compound_interest && "#FF0000" || (0, _options.isOptionOn)("mobile-mode") && "#FFFFFF" || "", gameState.offsetXRoundedDown, gameState.gameZoneHeight, width - gameState.offsetXRoundedDown, gameState.gameZoneHeight, 1);
|
drawStraightLine(ctx, gameState, hasCombo && gameState.perks.compound_interest && "#FF0000" || (0, _options.isOptionOn)("mobile-mode") && "#FFFFFF" || "", gameState.offsetXRoundedDown, gameState.gameZoneHeight, width - gameState.offsetXRoundedDown, gameState.gameZoneHeight, 1);
|
||||||
|
(0, _game.startWork)('render:contrast');
|
||||||
if (!(0, _options.isOptionOn)("basic") && (0, _options.isOptionOn)("contrast") && level.svg && level.color === "#000000") {
|
if (!(0, _options.isOptionOn)("basic") && (0, _options.isOptionOn)("contrast") && level.svg && level.color === "#000000") {
|
||||||
ctx.imageSmoothingEnabled = true;
|
ctx.imageSmoothingEnabled = true;
|
||||||
// haloCanvasCtx.globalCompositeOperation = 'multiply';
|
// haloCanvasCtx.globalCompositeOperation = 'multiply';
|
||||||
|
@ -4562,9 +4601,11 @@ function render(gameState) {
|
||||||
ctx.drawImage(haloCanvas, 0, 0, width, height);
|
ctx.drawImage(haloCanvas, 0, 0, width, height);
|
||||||
ctx.imageSmoothingEnabled = false;
|
ctx.imageSmoothingEnabled = false;
|
||||||
}
|
}
|
||||||
|
(0, _game.startWork)('render:breakout.lecaro.me?autoplay');
|
||||||
ctx.globalCompositeOperation = "source-over";
|
ctx.globalCompositeOperation = "source-over";
|
||||||
ctx.globalAlpha = 1;
|
ctx.globalAlpha = 1;
|
||||||
if ((0, _options.isOptionOn)("mobile-mode") && gameState.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.computer_controlled) drawText(ctx, "breakout.lecaro.me?autoplay", gameState.puckColor, gameState.puckHeight, gameState.canvasWidth / 2, gameState.gameZoneHeight + (gameState.canvasHeight - gameState.gameZoneHeight) / 2);
|
||||||
|
(0, _game.startWork)('render:mobile_press_to_play');
|
||||||
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);
|
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);
|
||||||
// if(isOptionOn('mobile-mode')) {
|
// if(isOptionOn('mobile-mode')) {
|
||||||
// ctx.globalCompositeOperation = "source-over";
|
// ctx.globalCompositeOperation = "source-over";
|
||||||
|
@ -4573,7 +4614,9 @@ function render(gameState) {
|
||||||
// ctx.fillRect(0,gameState.gameZoneHeight, gameState.canvasWidth, gameState.canvasHeight-gameState.gameZoneHeight)
|
// ctx.fillRect(0,gameState.gameZoneHeight, gameState.canvasWidth, gameState.canvasHeight-gameState.gameZoneHeight)
|
||||||
// }
|
// }
|
||||||
// ctx.globalAlpha=1
|
// ctx.globalAlpha=1
|
||||||
|
(0, _game.startWork)('render:askForWakeLock');
|
||||||
askForWakeLock(gameState);
|
askForWakeLock(gameState);
|
||||||
|
(0, _game.startWork)('render:resetTransform');
|
||||||
if (shaked) ctx.resetTransform();
|
if (shaked) ctx.resetTransform();
|
||||||
}
|
}
|
||||||
function drawStraightLine(ctx, gameState, mode, x1, y1, x2, y2, alpha = 1) {
|
function drawStraightLine(ctx, gameState, mode, x1, y1, x2, y2, alpha = 1) {
|
||||||
|
@ -4860,7 +4903,7 @@ function askForWakeLock(gameState) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
},{"./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) {
|
},{"./gameStateMutators":"9ZeQl","./game_utils":"cEeac","./i18n/i18n":"eNPRm","./game":"edeGs","./options":"d5NoS","./pure_functions":"6pQh7","@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3","./settings":"5blfu"}],"caCAf":[function(require,module,exports,__globalThis) {
|
||||||
var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js");
|
var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js");
|
||||||
parcelHelpers.defineInteropFlag(exports);
|
parcelHelpers.defineInteropFlag(exports);
|
||||||
parcelHelpers.export(exports, "addToTotalPlayTime", ()=>addToTotalPlayTime);
|
parcelHelpers.export(exports, "addToTotalPlayTime", ()=>addToTotalPlayTime);
|
||||||
|
@ -4878,9 +4921,7 @@ var _asyncAlert = require("./asyncAlert");
|
||||||
var _upgrades = require("./upgrades");
|
var _upgrades = require("./upgrades");
|
||||||
var _levelEditor = require("./levelEditor");
|
var _levelEditor = require("./levelEditor");
|
||||||
function addToTotalPlayTime(ms) {
|
function addToTotalPlayTime(ms) {
|
||||||
try {
|
|
||||||
(0, _settings.setSettingValue)('breakout_71_total_play_time', (0, _settings.getSettingValue)('breakout_71_total_play_time', 0) + ms);
|
(0, _settings.setSettingValue)('breakout_71_total_play_time', (0, _settings.getSettingValue)('breakout_71_total_play_time', 0) + ms);
|
||||||
} catch (e) {}
|
|
||||||
}
|
}
|
||||||
function gameOver(title, intro) {
|
function gameOver(title, intro) {
|
||||||
if (!(0, _game.gameState).running) return;
|
if (!(0, _game.gameState).running) return;
|
||||||
|
|
23
src/game.ts
23
src/game.ts
|
@ -452,7 +452,6 @@ startWork('gameStateTick')
|
||||||
gameStateTick(gameState, frames);
|
gameStateTick(gameState, frames);
|
||||||
}
|
}
|
||||||
|
|
||||||
startWork('render')
|
|
||||||
if (gameState.running || gameState.needsRender) {
|
if (gameState.running || gameState.needsRender) {
|
||||||
gameState.needsRender = false;
|
gameState.needsRender = false;
|
||||||
render(gameState);
|
render(gameState);
|
||||||
|
@ -478,10 +477,12 @@ setInterval(() => {
|
||||||
FPSCounter = 0;
|
FPSCounter = 0;
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
|
const showStats= window.location.search.includes("stress")
|
||||||
let total={}
|
let total={}
|
||||||
let lastTick=performance.now();
|
let lastTick=performance.now();
|
||||||
let doing= ''
|
let doing= ''
|
||||||
function startWork(what){
|
export function startWork(what){
|
||||||
|
if(!showStats) return
|
||||||
const newNow=performance.now();
|
const newNow=performance.now();
|
||||||
if(doing) {
|
if(doing) {
|
||||||
total[doing] = (total[doing]||0) + ( newNow-lastTick )
|
total[doing] = (total[doing]||0) + ( newNow-lastTick )
|
||||||
|
@ -489,12 +490,12 @@ function startWork(what){
|
||||||
lastTick=newNow
|
lastTick=newNow
|
||||||
doing=what
|
doing=what
|
||||||
}
|
}
|
||||||
|
if(showStats)
|
||||||
setInterval(()=>{
|
setInterval(()=>{
|
||||||
const totalTime = sumOfValues(total)
|
const totalTime = sumOfValues(total)
|
||||||
console.log(
|
console.debug(
|
||||||
liveCount(gameState.coins) +' coins\n'+
|
liveCount(gameState.coins) +' coins\n'+
|
||||||
Object.entries(total).sort((a,b)=>b[1]-a[1]).filter(a=>a[1]>1).map(t=>t[0]+':'+(t[1]/totalTime*100).toFixed(2)+'% ('+t[1]+'ms)').join('\n'))
|
Object.entries(total).sort((a,b)=>b[1]-a[1]).filter(a=>a[1]>1).map(t=>t[0]+':'+(t[1]/totalTime*100).toFixed(2)+'% ('+t[1]+'ms)').join('\n'))
|
||||||
|
|
||||||
total={}
|
total={}
|
||||||
},2000)
|
},2000)
|
||||||
|
|
||||||
|
@ -1040,27 +1041,29 @@ export function restart(params: RunParams) {
|
||||||
play();
|
play();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (window.location.search.includes("autoplay")) {
|
if (window.location.search.match(/autoplay|stress/) ) {
|
||||||
startComputerControlledGame();
|
startComputerControlledGame();
|
||||||
} else if (window.location.search.includes("stress")) {
|
|
||||||
if(!isOptionOn('show_fps'))
|
if(!isOptionOn('show_fps'))
|
||||||
toggleOption('show_fps')
|
toggleOption('show_fps')
|
||||||
restart({
|
|
||||||
level:allLevels.find(l=>l.name=='Worms'),
|
|
||||||
perks:{base_combo:5000, pierce:20, rainbow:3, sapper:2, etherealcoins:1}
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
restart({});
|
restart({});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function startComputerControlledGame() {
|
export function startComputerControlledGame() {
|
||||||
|
|
||||||
const perks: Partial<PerksMap> = { base_combo: 20, pierce: 3 };
|
const perks: Partial<PerksMap> = { base_combo: 20, pierce: 3 };
|
||||||
|
if(window.location.search.includes("stress")){
|
||||||
|
|
||||||
|
Object.assign(perks,{base_combo:5000, pierce:20, rainbow:3, sapper:2, etherealcoins:1, bricks_attract_ball:1, respawn:3})
|
||||||
|
|
||||||
|
}else{
|
||||||
for (let i = 0; i < 10; i++) {
|
for (let i = 0; i < 10; i++) {
|
||||||
const u = sample(upgrades);
|
const u = sample(upgrades);
|
||||||
|
|
||||||
perks[u.id] ||= Math.floor(Math.random() * u.max) + 1;
|
perks[u.id] ||= Math.floor(Math.random() * u.max) + 1;
|
||||||
}
|
}
|
||||||
perks.superhot = 0;
|
perks.superhot = 0;
|
||||||
|
}
|
||||||
restart({
|
restart({
|
||||||
level: sample(allLevels.filter((l) => l.color === "#000000")),
|
level: sample(allLevels.filter((l) => l.color === "#000000")),
|
||||||
computer_controlled: true,
|
computer_controlled: true,
|
||||||
|
|
|
@ -17,10 +17,7 @@ import { run } from "jest";
|
||||||
import { editRawLevelList } from "./levelEditor";
|
import { editRawLevelList } from "./levelEditor";
|
||||||
|
|
||||||
export function addToTotalPlayTime(ms: number) {
|
export function addToTotalPlayTime(ms: number) {
|
||||||
try {
|
|
||||||
setSettingValue('breakout_71_total_play_time', getSettingValue('breakout_71_total_play_time',0)+ms)
|
setSettingValue('breakout_71_total_play_time', getSettingValue('breakout_71_total_play_time',0)+ms)
|
||||||
|
|
||||||
} catch (e) {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function gameOver(title: string, intro: string) {
|
export function gameOver(title: string, intro: string) {
|
||||||
|
|
|
@ -461,11 +461,11 @@ export function explodeBrick(
|
||||||
gameState.runStatistics.coins_spawned += coinsToSpawn;
|
gameState.runStatistics.coins_spawned += coinsToSpawn;
|
||||||
gameState.runStatistics.bricks_broken++;
|
gameState.runStatistics.bricks_broken++;
|
||||||
|
|
||||||
const maxCoins = getCurrentMaxCoins() * (isOptionOn("basic") ? 0.5 : 1);
|
const maxCoins = getCurrentMaxCoins()
|
||||||
const spawnableCoins =
|
const spawnableCoins =
|
||||||
liveCount(gameState.coins) > getCurrentMaxCoins()
|
liveCount(gameState.coins) > getCurrentMaxCoins()
|
||||||
? 1
|
? 1
|
||||||
: Math.floor(maxCoins - liveCount(gameState.coins)) / 3;
|
: Math.floor((maxCoins - liveCount(gameState.coins)) /2) ;
|
||||||
|
|
||||||
const pointsPerCoin = Math.max(1, Math.ceil(coinsToSpawn / spawnableCoins));
|
const pointsPerCoin = Math.max(1, Math.ceil(coinsToSpawn / spawnableCoins));
|
||||||
|
|
||||||
|
@ -1220,6 +1220,9 @@ export function gameStateTick(
|
||||||
const hitBorder = bordersHitCheck(gameState, coin, coin.size / 2, frames);
|
const hitBorder = bordersHitCheck(gameState, coin, coin.size / 2, frames);
|
||||||
if(coin.previousY<gameState.gameZoneHeight && coin.y>gameState.gameZoneHeight && coin.vy>0 && speed > 20) {
|
if(coin.previousY<gameState.gameZoneHeight && coin.y>gameState.gameZoneHeight && coin.vy>0 && speed > 20) {
|
||||||
schedulGameSound(gameState, "plouf", coin.x, clamp(speed, 20,100)/100*0.2);
|
schedulGameSound(gameState, "plouf", coin.x, clamp(speed, 20,100)/100*0.2);
|
||||||
|
if(!isOptionOn('basic')){
|
||||||
|
makeParticle(gameState, coin.x,gameState.gameZoneHeight, -coin.vx/5, -coin.vy/5, coin.color, false )
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
|
|
@ -13,7 +13,7 @@ import {
|
||||||
} from "./game_utils";
|
} from "./game_utils";
|
||||||
import { Coin, colorString, GameState } from "./types";
|
import { Coin, colorString, GameState } from "./types";
|
||||||
import { t } from "./i18n/i18n";
|
import { t } from "./i18n/i18n";
|
||||||
import { gameState, lastMeasuredFPS } from "./game";
|
import {gameState, lastMeasuredFPS, startWork} from "./game";
|
||||||
import { isOptionOn } from "./options";
|
import { isOptionOn } from "./options";
|
||||||
import {
|
import {
|
||||||
catchRateBest,
|
catchRateBest,
|
||||||
|
@ -25,6 +25,7 @@ import {
|
||||||
wallBouncedBest,
|
wallBouncedBest,
|
||||||
wallBouncedGood,
|
wallBouncedGood,
|
||||||
} from "./pure_functions";
|
} from "./pure_functions";
|
||||||
|
import {getCurrentMaxCoins} from "./settings";
|
||||||
|
|
||||||
export const gameCanvas = document.getElementById("game") as HTMLCanvasElement;
|
export const gameCanvas = document.getElementById("game") as HTMLCanvasElement;
|
||||||
export const ctx = gameCanvas.getContext("2d", {
|
export const ctx = gameCanvas.getContext("2d", {
|
||||||
|
@ -51,6 +52,7 @@ const haloCanvasCtx = haloCanvas.getContext("2d", {
|
||||||
export const haloScale = 16;
|
export const haloScale = 16;
|
||||||
|
|
||||||
export function render(gameState: GameState) {
|
export function render(gameState: GameState) {
|
||||||
|
startWork('render:init')
|
||||||
const level = currentLevelInfo(gameState);
|
const level = currentLevelInfo(gameState);
|
||||||
|
|
||||||
const hasCombo = gameState.combo > baseCombo(gameState);
|
const hasCombo = gameState.combo > baseCombo(gameState);
|
||||||
|
@ -70,10 +72,13 @@ export function render(gameState: GameState) {
|
||||||
? (gameState.levelSpawnedCoins - gameState.levelLostCoins) /
|
? (gameState.levelSpawnedCoins - gameState.levelLostCoins) /
|
||||||
gameState.levelSpawnedCoins
|
gameState.levelSpawnedCoins
|
||||||
: 1;
|
: 1;
|
||||||
|
startWork('render:scoreDisplay')
|
||||||
scoreDisplay.innerHTML =
|
scoreDisplay.innerHTML =
|
||||||
(isOptionOn("show_fps") || gameState.computer_controlled
|
(isOptionOn("show_fps") || gameState.computer_controlled
|
||||||
? `
|
? `
|
||||||
|
<span>
|
||||||
|
${Math.floor(liveCount(gameState.coins) / getCurrentMaxCoins() * 100)} %
|
||||||
|
</span><span> / </span>
|
||||||
<span class="${(Math.abs(lastMeasuredFPS - 60) < 2 && " ") || (Math.abs(lastMeasuredFPS - 60) < 10 && "good") || "bad"}">
|
<span class="${(Math.abs(lastMeasuredFPS - 60) < 2 && " ") || (Math.abs(lastMeasuredFPS - 60) < 10 && "good") || "bad"}">
|
||||||
${lastMeasuredFPS} FPS
|
${lastMeasuredFPS} FPS
|
||||||
</span><span> / </span>
|
</span><span> / </span>
|
||||||
|
@ -102,19 +107,20 @@ export function render(gameState: GameState) {
|
||||||
(gameState.computer_controlled && "computer_controlled") ||
|
(gameState.computer_controlled && "computer_controlled") ||
|
||||||
(gameState.lastScoreIncrease > gameState.levelTime - 500 && "active") ||
|
(gameState.lastScoreIncrease > gameState.levelTime - 500 && "active") ||
|
||||||
"";
|
"";
|
||||||
|
|
||||||
// Clear
|
// Clear
|
||||||
if (!isOptionOn("basic") && level.svg && level.color === "#000000") {
|
if (!isOptionOn("basic") && level.svg && level.color === "#000000") {
|
||||||
|
|
||||||
|
startWork('render:halo:clear')
|
||||||
haloCanvasCtx.globalCompositeOperation = "source-over";
|
haloCanvasCtx.globalCompositeOperation = "source-over";
|
||||||
haloCanvasCtx.globalAlpha = 0.99;
|
haloCanvasCtx.globalAlpha = 0.99;
|
||||||
haloCanvasCtx.fillStyle = level.color;
|
haloCanvasCtx.fillStyle = level.color;
|
||||||
haloCanvasCtx.fillRect(0, 0, width / haloScale, height / haloScale);
|
haloCanvasCtx.fillRect(0, 0, width / haloScale, height / haloScale);
|
||||||
|
|
||||||
const brightness = isOptionOn("extra_bright") ? 3 : 1;
|
const brightness = isOptionOn("extra_bright") ? 3 : 1;
|
||||||
|
|
||||||
haloCanvasCtx.globalCompositeOperation = "lighten";
|
haloCanvasCtx.globalCompositeOperation = "lighten";
|
||||||
haloCanvasCtx.globalAlpha =
|
haloCanvasCtx.globalAlpha =
|
||||||
0.1 + (0.5 * 10) / (liveCount(gameState.coins) + 10);
|
0.1 + (0.5 * 10) / (liveCount(gameState.coins) + 10);
|
||||||
|
startWork('render:halo:coins')
|
||||||
forEachLiveOne(gameState.coins, (coin) => {
|
forEachLiveOne(gameState.coins, (coin) => {
|
||||||
const color = getCoinRenderColor(gameState, coin);
|
const color = getCoinRenderColor(gameState, coin);
|
||||||
drawFuzzyBall(
|
drawFuzzyBall(
|
||||||
|
@ -126,6 +132,7 @@ export function render(gameState: GameState) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
startWork('render:halo:balls')
|
||||||
gameState.balls.forEach((ball) => {
|
gameState.balls.forEach((ball) => {
|
||||||
haloCanvasCtx.globalAlpha = 0.3 * (1 - ballTransparency(ball, gameState));
|
haloCanvasCtx.globalAlpha = 0.3 * (1 - ballTransparency(ball, gameState));
|
||||||
drawFuzzyBall(
|
drawFuzzyBall(
|
||||||
|
@ -136,6 +143,8 @@ export function render(gameState: GameState) {
|
||||||
ball.y / haloScale,
|
ball.y / haloScale,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
startWork('render:halo:bricks')
|
||||||
haloCanvasCtx.globalAlpha = 0.05;
|
haloCanvasCtx.globalAlpha = 0.05;
|
||||||
gameState.bricks.forEach((color, index) => {
|
gameState.bricks.forEach((color, index) => {
|
||||||
if (!color) return;
|
if (!color) return;
|
||||||
|
@ -151,6 +160,7 @@ export function render(gameState: GameState) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
startWork('render:halo:particles')
|
||||||
haloCanvasCtx.globalCompositeOperation = "screen";
|
haloCanvasCtx.globalCompositeOperation = "screen";
|
||||||
forEachLiveOne(gameState.particles, (flash) => {
|
forEachLiveOne(gameState.particles, (flash) => {
|
||||||
const { x, y, time, color, size, duration } = flash;
|
const { x, y, time, color, size, duration } = flash;
|
||||||
|
@ -174,6 +184,7 @@ export function render(gameState: GameState) {
|
||||||
ctx.drawImage(haloCanvas, 0, 0, width, height);
|
ctx.drawImage(haloCanvas, 0, 0, width, height);
|
||||||
ctx.imageSmoothingEnabled = false;
|
ctx.imageSmoothingEnabled = false;
|
||||||
|
|
||||||
|
startWork('render:halo:pattern')
|
||||||
ctx.globalAlpha = 1;
|
ctx.globalAlpha = 1;
|
||||||
ctx.globalCompositeOperation = "multiply";
|
ctx.globalCompositeOperation = "multiply";
|
||||||
if (level.svg && background.width && background.complete) {
|
if (level.svg && background.width && background.complete) {
|
||||||
|
@ -225,6 +236,8 @@ export function render(gameState: GameState) {
|
||||||
ctx.fillRect(0, 0, width, height);
|
ctx.fillRect(0, 0, width, height);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
startWork('render:halo-basic')
|
||||||
ctx.globalAlpha = 1;
|
ctx.globalAlpha = 1;
|
||||||
ctx.globalCompositeOperation = "source-over";
|
ctx.globalCompositeOperation = "source-over";
|
||||||
ctx.fillStyle = level.color || "#000";
|
ctx.fillStyle = level.color || "#000";
|
||||||
|
@ -237,6 +250,7 @@ export function render(gameState: GameState) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
startWork('render:explosionshake')
|
||||||
ctx.globalAlpha = 1;
|
ctx.globalAlpha = 1;
|
||||||
ctx.globalCompositeOperation = "source-over";
|
ctx.globalCompositeOperation = "source-over";
|
||||||
const lastExplosionDelay = Date.now() - gameState.lastExplosion + 5;
|
const lastExplosionDelay = Date.now() - gameState.lastExplosion + 5;
|
||||||
|
@ -249,7 +263,7 @@ export function render(gameState: GameState) {
|
||||||
Math.sin(Date.now() + 36) * amplitude,
|
Math.sin(Date.now() + 36) * amplitude,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
startWork('render:coins')
|
||||||
// Coins
|
// Coins
|
||||||
ctx.globalAlpha = 1;
|
ctx.globalAlpha = 1;
|
||||||
forEachLiveOne(gameState.coins, (coin) => {
|
forEachLiveOne(gameState.coins, (coin) => {
|
||||||
|
@ -272,6 +286,7 @@ export function render(gameState: GameState) {
|
||||||
coin.a,
|
coin.a,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
startWork('render:ball shade')
|
||||||
// Black shadow around balls
|
// Black shadow around balls
|
||||||
if (!isOptionOn("basic")) {
|
if (!isOptionOn("basic")) {
|
||||||
ctx.globalCompositeOperation = "source-over";
|
ctx.globalCompositeOperation = "source-over";
|
||||||
|
@ -289,10 +304,11 @@ export function render(gameState: GameState) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
startWork('render:bricks')
|
||||||
ctx.globalCompositeOperation = "source-over";
|
ctx.globalCompositeOperation = "source-over";
|
||||||
renderAllBricks();
|
renderAllBricks();
|
||||||
|
|
||||||
|
startWork('render:lights')
|
||||||
ctx.globalCompositeOperation = "screen";
|
ctx.globalCompositeOperation = "screen";
|
||||||
forEachLiveOne(gameState.lights, (flash) => {
|
forEachLiveOne(gameState.lights, (flash) => {
|
||||||
const { x, y, time, color, size, duration } = flash;
|
const { x, y, time, color, size, duration } = flash;
|
||||||
|
@ -309,6 +325,7 @@ export function render(gameState: GameState) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
startWork('render:texts')
|
||||||
ctx.globalCompositeOperation = "screen";
|
ctx.globalCompositeOperation = "screen";
|
||||||
forEachLiveOne(gameState.texts, (flash) => {
|
forEachLiveOne(gameState.texts, (flash) => {
|
||||||
const { x, y, time, color, size, duration } = flash;
|
const { x, y, time, color, size, duration } = flash;
|
||||||
|
@ -318,6 +335,7 @@ export function render(gameState: GameState) {
|
||||||
drawText(ctx, flash.text, color, size, x, y - elapsed / 10);
|
drawText(ctx, flash.text, color, size, x, y - elapsed / 10);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
startWork('render:particles')
|
||||||
forEachLiveOne(gameState.particles, (particle) => {
|
forEachLiveOne(gameState.particles, (particle) => {
|
||||||
const { x, y, time, color, size, duration } = particle;
|
const { x, y, time, color, size, duration } = particle;
|
||||||
const elapsed = gameState.levelTime - time;
|
const elapsed = gameState.levelTime - time;
|
||||||
|
@ -325,6 +343,8 @@ export function render(gameState: GameState) {
|
||||||
ctx.globalCompositeOperation = "screen";
|
ctx.globalCompositeOperation = "screen";
|
||||||
drawBall(ctx, color, size, x, y);
|
drawBall(ctx, color, size, x, y);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
startWork('render:extra_life')
|
||||||
if (gameState.perks.extra_life) {
|
if (gameState.perks.extra_life) {
|
||||||
ctx.globalAlpha = 1;
|
ctx.globalAlpha = 1;
|
||||||
ctx.globalCompositeOperation = "source-over";
|
ctx.globalCompositeOperation = "source-over";
|
||||||
|
@ -339,9 +359,9 @@ export function render(gameState: GameState) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
startWork('render:balls')
|
||||||
ctx.globalAlpha = 1;
|
ctx.globalAlpha = 1;
|
||||||
ctx.globalCompositeOperation = "source-over";
|
ctx.globalCompositeOperation = "source-over";
|
||||||
|
|
||||||
gameState.balls.forEach((ball) => {
|
gameState.balls.forEach((ball) => {
|
||||||
const drawingColor = gameState.ballsColor;
|
const drawingColor = gameState.ballsColor;
|
||||||
const ballAlpha = 1 - ballTransparency(ball, gameState);
|
const ballAlpha = 1 - ballTransparency(ball, gameState);
|
||||||
|
@ -390,10 +410,10 @@ export function render(gameState: GameState) {
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// The puck
|
|
||||||
|
startWork('render:puck')
|
||||||
ctx.globalAlpha = 1;
|
ctx.globalAlpha = 1;
|
||||||
ctx.globalCompositeOperation = "source-over";
|
ctx.globalCompositeOperation = "source-over";
|
||||||
|
|
||||||
drawPuck(
|
drawPuck(
|
||||||
ctx,
|
ctx,
|
||||||
gameState.puckColor,
|
gameState.puckColor,
|
||||||
|
@ -404,6 +424,7 @@ export function render(gameState: GameState) {
|
||||||
gameState.perks.streak_shots && hasCombo ? getDashOffset(gameState) : -1,
|
gameState.perks.streak_shots && hasCombo ? getDashOffset(gameState) : -1,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
startWork('render:combotext')
|
||||||
if (gameState.combo > 1) {
|
if (gameState.combo > 1) {
|
||||||
ctx.globalCompositeOperation = "source-over";
|
ctx.globalCompositeOperation = "source-over";
|
||||||
const comboText = "x " + gameState.combo;
|
const comboText = "x " + gameState.combo;
|
||||||
|
@ -443,8 +464,8 @@ export function render(gameState: GameState) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
startWork('render:Borders')
|
||||||
// Borders
|
// Borders
|
||||||
|
|
||||||
ctx.globalCompositeOperation = "source-over";
|
ctx.globalCompositeOperation = "source-over";
|
||||||
ctx.globalAlpha = 1;
|
ctx.globalAlpha = 1;
|
||||||
|
|
||||||
|
@ -527,6 +548,7 @@ export function render(gameState: GameState) {
|
||||||
1,
|
1,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
startWork('render:contrast')
|
||||||
if (
|
if (
|
||||||
!isOptionOn("basic") &&
|
!isOptionOn("basic") &&
|
||||||
isOptionOn("contrast") &&
|
isOptionOn("contrast") &&
|
||||||
|
@ -547,6 +569,7 @@ export function render(gameState: GameState) {
|
||||||
ctx.imageSmoothingEnabled = false;
|
ctx.imageSmoothingEnabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
startWork('render:breakout.lecaro.me?autoplay')
|
||||||
ctx.globalCompositeOperation = "source-over";
|
ctx.globalCompositeOperation = "source-over";
|
||||||
ctx.globalAlpha = 1;
|
ctx.globalAlpha = 1;
|
||||||
|
|
||||||
|
@ -561,6 +584,7 @@ export function render(gameState: GameState) {
|
||||||
(gameState.canvasHeight - gameState.gameZoneHeight) / 2,
|
(gameState.canvasHeight - gameState.gameZoneHeight) / 2,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
startWork('render:mobile_press_to_play')
|
||||||
if (isOptionOn("mobile-mode") && !gameState.running) {
|
if (isOptionOn("mobile-mode") && !gameState.running) {
|
||||||
drawText(
|
drawText(
|
||||||
ctx,
|
ctx,
|
||||||
|
@ -580,8 +604,10 @@ export function render(gameState: GameState) {
|
||||||
// ctx.fillRect(0,gameState.gameZoneHeight, gameState.canvasWidth, gameState.canvasHeight-gameState.gameZoneHeight)
|
// ctx.fillRect(0,gameState.gameZoneHeight, gameState.canvasWidth, gameState.canvasHeight-gameState.gameZoneHeight)
|
||||||
// }
|
// }
|
||||||
// ctx.globalAlpha=1
|
// ctx.globalAlpha=1
|
||||||
|
startWork('render:askForWakeLock')
|
||||||
askForWakeLock(gameState);
|
askForWakeLock(gameState);
|
||||||
|
|
||||||
|
startWork('render:resetTransform')
|
||||||
if (shaked) {
|
if (shaked) {
|
||||||
ctx.resetTransform();
|
ctx.resetTransform();
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,14 +19,25 @@ export function getSettingValue<T>(key: string, defaultValue: T) {
|
||||||
return (cachedSettings[key] as T) ?? defaultValue;
|
return (cachedSettings[key] as T) ?? defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We avoid using localstorage synchronously for perf reasons
|
||||||
|
let needsSaving= new Set()
|
||||||
export function setSettingValue<T>(key: string, value: T) {
|
export function setSettingValue<T>(key: string, value: T) {
|
||||||
|
if(cachedSettings[key] !==value){
|
||||||
|
needsSaving.add(key)
|
||||||
cachedSettings[key] = value;
|
cachedSettings[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setInterval(()=>{
|
||||||
try {
|
try {
|
||||||
localStorage.setItem(key, JSON.stringify(value));
|
for(let key of needsSaving){
|
||||||
|
localStorage.setItem(key, JSON.stringify(cachedSettings[key]));
|
||||||
|
}
|
||||||
|
needsSaving.clear()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn(e);
|
console.warn(e);
|
||||||
}
|
}
|
||||||
}
|
}, 500)
|
||||||
|
|
||||||
|
|
||||||
export function getTotalScore() {
|
export function getTotalScore() {
|
||||||
return getSettingValue("breakout_71_total_score", 0);
|
return getSettingValue("breakout_71_total_score", 0);
|
||||||
|
@ -39,5 +50,5 @@ export function getCurrentMaxParticles() {
|
||||||
return getCurrentMaxCoins()
|
return getCurrentMaxCoins()
|
||||||
}
|
}
|
||||||
export function cycleMaxCoins() {
|
export function cycleMaxCoins() {
|
||||||
setSettingValue("max_coins", (getSettingValue("max_coins", 2) + 1) % 10);
|
setSettingValue("max_coins", (getSettingValue("max_coins", 2) + 1) % 7);
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ export const sounds = {
|
||||||
|
|
||||||
plouf: (volume: number, pan: number) => {
|
plouf: (volume: number, pan: number) => {
|
||||||
if (!isOptionOn("sound")) return;
|
if (!isOptionOn("sound")) return;
|
||||||
createSingleBounceSound(240, pan, volume*0.5);
|
createSingleBounceSound(500, pan, volume*0.5);
|
||||||
// createWaterDropSound(800, pan, volume*0.2, 0.2,'triangle')
|
// createWaterDropSound(800, pan, volume*0.2, 0.2,'triangle')
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -818,7 +818,7 @@ export const rawUpgrades = [
|
||||||
threshold: 215000,
|
threshold: 215000,
|
||||||
gift: false,
|
gift: false,
|
||||||
id: "bricks_attract_ball",
|
id: "bricks_attract_ball",
|
||||||
max: 3,
|
max: 1,
|
||||||
name: t("upgrades.bricks_attract_ball.name"),
|
name: t("upgrades.bricks_attract_ball.name"),
|
||||||
help: (lvl: number) =>
|
help: (lvl: number) =>
|
||||||
t("upgrades.bricks_attract_ball.tooltip", { count: lvl * 3 }),
|
t("upgrades.bricks_attract_ball.tooltip", { count: lvl * 3 }),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue