mirror of
https://gitlab.com/lecarore/breakout71.git
synced 2025-04-20 12:15:06 -04:00
Build
This commit is contained in:
parent
a4e24fd397
commit
27a2cd686e
16 changed files with 3396 additions and 3332 deletions
|
@ -11,8 +11,8 @@ android {
|
||||||
applicationId = "me.lecaro.breakout"
|
applicationId = "me.lecaro.breakout"
|
||||||
minSdk = 21
|
minSdk = 21
|
||||||
targetSdk = 34
|
targetSdk = 34
|
||||||
versionCode = 29053158
|
versionCode = 29054664
|
||||||
versionName = "29053158"
|
versionName = "29054664"
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
vectorDrawables {
|
vectorDrawables {
|
||||||
useSupportLibrary = true
|
useSupportLibrary = true
|
||||||
|
|
File diff suppressed because one or more lines are too long
36
dist/index.html
vendored
36
dist/index.html
vendored
|
@ -899,10 +899,10 @@ async function openScorePanel() {
|
||||||
gameState.isCreativeModeRun ? `<p>${(0, _i18N.t)("score_panel.test_run")}</p>` : "",
|
gameState.isCreativeModeRun ? `<p>${(0, _i18N.t)("score_panel.test_run")}</p>` : "",
|
||||||
(0, _gameUtils.pickedUpgradesHTMl)(gameState),
|
(0, _gameUtils.pickedUpgradesHTMl)(gameState),
|
||||||
(0, _gameUtils.levelsListHTMl)(gameState),
|
(0, _gameUtils.levelsListHTMl)(gameState),
|
||||||
gameState.rerolls ? (0, _i18N.t)('score_panel.rerolls_count', {
|
gameState.rerolls ? (0, _i18N.t)("score_panel.rerolls_count", {
|
||||||
rerolls: gameState.rerolls
|
rerolls: gameState.rerolls
|
||||||
}) : '',
|
}) : "",
|
||||||
banned && (0, _i18N.t)('score_panel.banned', {
|
banned && (0, _i18N.t)("score_panel.banned", {
|
||||||
banned
|
banned
|
||||||
})
|
})
|
||||||
],
|
],
|
||||||
|
@ -1293,7 +1293,7 @@ function setKeyPressed(key, on) {
|
||||||
}
|
}
|
||||||
document.addEventListener("keydown", (e)=>{
|
document.addEventListener("keydown", (e)=>{
|
||||||
if (e.key.toLowerCase() === "f" && !e.ctrlKey && !e.metaKey) {
|
if (e.key.toLowerCase() === "f" && !e.ctrlKey && !e.metaKey) {
|
||||||
(0, _options.toggleOption)('fullscreen');
|
(0, _options.toggleOption)("fullscreen");
|
||||||
applyFullScreenChoice();
|
applyFullScreenChoice();
|
||||||
} else if (e.key in pressed) setKeyPressed(e.key, 1);
|
} else if (e.key in pressed) setKeyPressed(e.key, 1);
|
||||||
if (e.key === " " && !(0, _asyncAlert.alertsOpen)) {
|
if (e.key === " " && !(0, _asyncAlert.alertsOpen)) {
|
||||||
|
@ -1385,7 +1385,7 @@ const upgrades = (0, _upgrades.rawUpgrades).map((u)=>({
|
||||||
}));
|
}));
|
||||||
|
|
||||||
},{"./data/palette.json":"ktRBU","./data/levels.json":"8JSUc","./data/version.json":"iyP6E","./upgrades":"1u3Dx","./getLevelBackground":"7OIPf","./levelIcon":"6rQoT","@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3"}],"iyP6E":[function(require,module,exports,__globalThis) {
|
},{"./data/palette.json":"ktRBU","./data/levels.json":"8JSUc","./data/version.json":"iyP6E","./upgrades":"1u3Dx","./getLevelBackground":"7OIPf","./levelIcon":"6rQoT","@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3"}],"iyP6E":[function(require,module,exports,__globalThis) {
|
||||||
module.exports = JSON.parse("\"29053158\"");
|
module.exports = JSON.parse("\"29054664\"");
|
||||||
|
|
||||||
},{}],"1u3Dx":[function(require,module,exports,__globalThis) {
|
},{}],"1u3Dx":[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");
|
||||||
|
@ -2901,7 +2901,7 @@ async function gotoNextLoop(gameState) {
|
||||||
content: [
|
content: [
|
||||||
(0, _i18N.t)("loop.instructions"),
|
(0, _i18N.t)("loop.instructions"),
|
||||||
comboText,
|
comboText,
|
||||||
...userPerks.filter((u)=>u.id !== 'instant_upgrade').map((u)=>{
|
...userPerks.filter((u)=>u.id !== "instant_upgrade").map((u)=>{
|
||||||
return {
|
return {
|
||||||
text: u.name + (0, _i18N.t)("level_up.upgrade_perk_to_level", {
|
text: u.name + (0, _i18N.t)("level_up.upgrade_perk_to_level", {
|
||||||
level: gameState.perks[u.id] + 1
|
level: gameState.perks[u.id] + 1
|
||||||
|
@ -3538,24 +3538,24 @@ 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;
|
||||||
scoreDisplay.innerHTML = ((0, _options.isOptionOn)("show_fps") ? `
|
scoreDisplay.innerHTML = ((0, _options.isOptionOn)("show_fps") ? `
|
||||||
<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>
|
||||||
|
|
||||||
` : '') + ((0, _options.isOptionOn)('show_stats') ? `
|
` : "") + ((0, _options.isOptionOn)("show_stats") ? `
|
||||||
<span class="${catchRate == 1 && 'great' || catchRate > 0.9 && 'good' || ''}">
|
<span class="${catchRate == 1 && "great" || catchRate > 0.9 && "good" || ""}">
|
||||||
${Math.floor(catchRate * 100)}%
|
${Math.floor(catchRate * 100)}%
|
||||||
</span><span> / </span>
|
</span><span> / </span>
|
||||||
<span class="${gameState.levelWallBounces == 0 && 'great' || gameState.levelWallBounces < 5 && 'good' || ''}">
|
<span class="${gameState.levelWallBounces == 0 && "great" || gameState.levelWallBounces < 5 && "good" || ""}">
|
||||||
${gameState.levelWallBounces} B
|
${gameState.levelWallBounces} B
|
||||||
</span><span> / </span>
|
</span><span> / </span>
|
||||||
<span class="${gameState.levelTime < 30000 && 'great' || gameState.levelTime < 60000 && 'good' || ''}">
|
<span class="${gameState.levelTime < 30000 && "great" || gameState.levelTime < 60000 && "good" || ""}">
|
||||||
${Math.ceil(gameState.levelTime / 1000)}s
|
${Math.ceil(gameState.levelTime / 1000)}s
|
||||||
</span><span> / </span>
|
</span><span> / </span>
|
||||||
<span class="${gameState.levelMisses == 0 && 'great' || gameState.levelMisses <= 3 && 'good' || ''}">
|
<span class="${gameState.levelMisses == 0 && "great" || gameState.levelMisses <= 3 && "good" || ""}">
|
||||||
${gameState.levelMisses} M
|
${gameState.levelMisses} M
|
||||||
</span><span> / </span>
|
</span><span> / </span>
|
||||||
` : '') + `$${gameState.score}`;
|
` : "") + `$${gameState.score}`;
|
||||||
scoreDisplay.className = gameState.lastScoreIncrease > gameState.levelTime - 500 ? "active" : "";
|
scoreDisplay.className = gameState.lastScoreIncrease > gameState.levelTime - 500 ? "active" : "";
|
||||||
// Clear
|
// Clear
|
||||||
if (!(0, _options.isOptionOn)("basic") && !level.color && level.svg) {
|
if (!(0, _options.isOptionOn)("basic") && !level.color && level.svg) {
|
||||||
|
@ -3602,14 +3602,14 @@ function render(gameState) {
|
||||||
bgctx.fillStyle = level.color || "#000";
|
bgctx.fillStyle = level.color || "#000";
|
||||||
bgctx.fillRect(0, 0, gameState.canvasWidth, gameState.canvasHeight);
|
bgctx.fillRect(0, 0, gameState.canvasWidth, gameState.canvasHeight);
|
||||||
if (gameState.perks.clairvoyant >= 3) {
|
if (gameState.perks.clairvoyant >= 3) {
|
||||||
const pageSource = document.body.innerHTML.replace(/\s+/gi, '');
|
const pageSource = document.body.innerHTML.replace(/\s+/gi, "");
|
||||||
const lineWidth = Math.ceil(gameState.canvasWidth / 15);
|
const lineWidth = Math.ceil(gameState.canvasWidth / 15);
|
||||||
const lines = Math.ceil(gameState.canvasHeight / 20);
|
const lines = Math.ceil(gameState.canvasHeight / 20);
|
||||||
const chars = lineWidth * lines;
|
const chars = lineWidth * lines;
|
||||||
let start = Math.ceil(Math.random() * (pageSource.length - chars));
|
let start = Math.ceil(Math.random() * (pageSource.length - chars));
|
||||||
for(let i = 0; i < lines; i++){
|
for(let i = 0; i < lines; i++){
|
||||||
bgctx.fillStyle = 'white';
|
bgctx.fillStyle = "white";
|
||||||
bgctx.font = '20px Courier';
|
bgctx.font = "20px Courier";
|
||||||
bgctx.fillText(pageSource.slice(start + i * lineWidth, start + (i + 1) * lineWidth), 0, i * 20, gameState.canvasWidth);
|
bgctx.fillText(pageSource.slice(start + i * lineWidth, start + (i + 1) * lineWidth), 0, i * 20, gameState.canvasWidth);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -3654,7 +3654,7 @@ function render(gameState) {
|
||||||
ctx.globalCompositeOperation = "source-over";
|
ctx.globalCompositeOperation = "source-over";
|
||||||
// ctx.globalCompositeOperation =
|
// ctx.globalCompositeOperation =
|
||||||
// coin.color === "gold" || level.color ? "source-over" : "screen";
|
// coin.color === "gold" || level.color ? "source-over" : "screen";
|
||||||
drawCoin(ctx, coin.color, coin.size, coin.x, coin.y, hasCombo && gameState.perks.asceticism && "red" || coin.color === 'gold' && 'gold' || gameState.puckColor, coin.a);
|
drawCoin(ctx, coin.color, coin.size, coin.x, coin.y, hasCombo && gameState.perks.asceticism && "red" || coin.color === "gold" && "gold" || gameState.puckColor, coin.a);
|
||||||
});
|
});
|
||||||
// Black shadow around balls
|
// Black shadow around balls
|
||||||
if (!(0, _options.isOptionOn)("basic")) {
|
if (!(0, _options.isOptionOn)("basic")) {
|
||||||
|
@ -3928,7 +3928,7 @@ function drawBrick(ctx, color, x, y, offset = 0, borderOnly) {
|
||||||
const brx = Math.ceil(x + (0, _game.gameState).brickWidth / 2) - 1;
|
const brx = Math.ceil(x + (0, _game.gameState).brickWidth / 2) - 1;
|
||||||
const bry = Math.ceil(y + (0, _game.gameState).brickWidth / 2) - 1;
|
const bry = Math.ceil(y + (0, _game.gameState).brickWidth / 2) - 1;
|
||||||
const width = brx - tlx, height = bry - tly;
|
const width = brx - tlx, height = bry - tly;
|
||||||
const key = "brick" + color + "_" + "_" + width + "_" + height + "_" + offset + '_' + borderOnly;
|
const key = "brick" + color + "_" + "_" + width + "_" + height + "_" + offset + "_" + borderOnly;
|
||||||
if (!cachedGraphics[key]) {
|
if (!cachedGraphics[key]) {
|
||||||
const can = document.createElement("canvas");
|
const can = document.createElement("canvas");
|
||||||
can.width = width;
|
can.width = width;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// The version of the cache.
|
// The version of the cache.
|
||||||
const VERSION = "29053158";
|
const VERSION = "29054664";
|
||||||
|
|
||||||
// The name of the cache
|
// The name of the cache
|
||||||
const CACHE_NAME = `breakout-71-${VERSION}`;
|
const CACHE_NAME = `breakout-71-${VERSION}`;
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
"29053158"
|
"29054664"
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
* {
|
* {
|
||||||
font-family: Courier New,
|
font-family:
|
||||||
|
Courier New,
|
||||||
Courier,
|
Courier,
|
||||||
Lucida Sans Typewriter,
|
Lucida Sans Typewriter,
|
||||||
Lucida Typewriter,
|
Lucida Typewriter,
|
||||||
|
|
81
src/game.ts
81
src/game.ts
|
@ -12,7 +12,13 @@ import {
|
||||||
Upgrade,
|
Upgrade,
|
||||||
} from "./types";
|
} from "./types";
|
||||||
import { getAudioContext, playPendingSounds } from "./sounds";
|
import { getAudioContext, playPendingSounds } from "./sounds";
|
||||||
import {currentLevelInfo, getRowColIndex, levelsListHTMl, max_levels, pickedUpgradesHTMl,} from "./game_utils";
|
import {
|
||||||
|
currentLevelInfo,
|
||||||
|
getRowColIndex,
|
||||||
|
levelsListHTMl,
|
||||||
|
max_levels,
|
||||||
|
pickedUpgradesHTMl,
|
||||||
|
} from "./game_utils";
|
||||||
|
|
||||||
import "./PWA/sw_loader";
|
import "./PWA/sw_loader";
|
||||||
import { getCurrentLang, t } from "./i18n/i18n";
|
import { getCurrentLang, t } from "./i18n/i18n";
|
||||||
|
@ -34,10 +40,27 @@ import {
|
||||||
setLevel,
|
setLevel,
|
||||||
setMousePos,
|
setMousePos,
|
||||||
} from "./gameStateMutators";
|
} from "./gameStateMutators";
|
||||||
import {backgroundCanvas, ctx, gameCanvas, render, scoreDisplay,} from "./render";
|
import {
|
||||||
import {pauseRecording, recordOneFrame, resumeRecording, startRecordingGame,} from "./recording";
|
backgroundCanvas,
|
||||||
|
ctx,
|
||||||
|
gameCanvas,
|
||||||
|
render,
|
||||||
|
scoreDisplay,
|
||||||
|
} from "./render";
|
||||||
|
import {
|
||||||
|
pauseRecording,
|
||||||
|
recordOneFrame,
|
||||||
|
resumeRecording,
|
||||||
|
startRecordingGame,
|
||||||
|
} from "./recording";
|
||||||
import { newGameState } from "./newGameState";
|
import { newGameState } from "./newGameState";
|
||||||
import {alertsOpen, asyncAlert, AsyncAlertAction, closeModal, requiredAsyncAlert,} from "./asyncAlert";
|
import {
|
||||||
|
alertsOpen,
|
||||||
|
asyncAlert,
|
||||||
|
AsyncAlertAction,
|
||||||
|
closeModal,
|
||||||
|
requiredAsyncAlert,
|
||||||
|
} from "./asyncAlert";
|
||||||
import { isOptionOn, options, toggleOption } from "./options";
|
import { isOptionOn, options, toggleOption } from "./options";
|
||||||
import { hashCode } from "./getLevelBackground";
|
import { hashCode } from "./getLevelBackground";
|
||||||
import { premiumMenuEntry } from "./premium";
|
import { premiumMenuEntry } from "./premium";
|
||||||
|
@ -161,13 +184,10 @@ setInterval(() => {
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
export async function openUpgradesPicker(gameState: GameState) {
|
export async function openUpgradesPicker(gameState: GameState) {
|
||||||
|
|
||||||
const catchRate =
|
const catchRate =
|
||||||
(gameState.score - gameState.levelStartScore) /
|
(gameState.score - gameState.levelStartScore) /
|
||||||
(gameState.levelSpawnedCoins || 1);
|
(gameState.levelSpawnedCoins || 1);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
let repeats = 1;
|
let repeats = 1;
|
||||||
|
|
||||||
let timeGain = "",
|
let timeGain = "",
|
||||||
|
@ -389,7 +409,7 @@ let FPSCounter = 0;
|
||||||
export let lastMeasuredFPS = 60;
|
export let lastMeasuredFPS = 60;
|
||||||
|
|
||||||
setInterval(() => {
|
setInterval(() => {
|
||||||
lastMeasuredFPS = FPSCounter
|
lastMeasuredFPS = FPSCounter;
|
||||||
FPSCounter = 0;
|
FPSCounter = 0;
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
||||||
|
@ -420,7 +440,6 @@ async function openScorePanel() {
|
||||||
.map((u) => u.name)
|
.map((u) => u.name)
|
||||||
.join(", ");
|
.join(", ");
|
||||||
|
|
||||||
|
|
||||||
const cb = await asyncAlert({
|
const cb = await asyncAlert({
|
||||||
title: gameState.loop
|
title: gameState.loop
|
||||||
? t("score_panel.title_looped", {
|
? t("score_panel.title_looped", {
|
||||||
|
@ -439,9 +458,10 @@ async function openScorePanel() {
|
||||||
gameState.isCreativeModeRun ? `<p>${t("score_panel.test_run")}</p>` : "",
|
gameState.isCreativeModeRun ? `<p>${t("score_panel.test_run")}</p>` : "",
|
||||||
pickedUpgradesHTMl(gameState),
|
pickedUpgradesHTMl(gameState),
|
||||||
levelsListHTMl(gameState),
|
levelsListHTMl(gameState),
|
||||||
gameState.rerolls ?
|
gameState.rerolls
|
||||||
t('score_panel.rerolls_count', {rerolls: gameState.rerolls}) : '',
|
? t("score_panel.rerolls_count", { rerolls: gameState.rerolls })
|
||||||
banned && t('score_panel.banned', {banned})
|
: "",
|
||||||
|
banned && t("score_panel.banned", { banned }),
|
||||||
],
|
],
|
||||||
allowClose: true,
|
allowClose: true,
|
||||||
});
|
});
|
||||||
|
@ -564,7 +584,7 @@ async function openSettingsMenu() {
|
||||||
value: () => {
|
value: () => {
|
||||||
toggleOption(key);
|
toggleOption(key);
|
||||||
fitSize();
|
fitSize();
|
||||||
applyFullScreenChoice()
|
applyFullScreenChoice();
|
||||||
openSettingsMenu();
|
openSettingsMenu();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -786,40 +806,36 @@ async function openSettingsMenu() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function applyFullScreenChoice(): boolean {
|
function applyFullScreenChoice(): boolean {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!(document.fullscreenEnabled || document.webkitFullscreenEnabled)) {
|
if (!(document.fullscreenEnabled || document.webkitFullscreenEnabled)) {
|
||||||
return false
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (document.fullscreenElement !== null && !isOptionOn("fullscreen")) {
|
if (document.fullscreenElement !== null && !isOptionOn("fullscreen")) {
|
||||||
if (document.exitFullscreen) {
|
if (document.exitFullscreen) {
|
||||||
document.exitFullscreen();
|
document.exitFullscreen();
|
||||||
return true
|
return true;
|
||||||
} else if (document.webkitCancelFullScreen) {
|
} else if (document.webkitCancelFullScreen) {
|
||||||
document.webkitCancelFullScreen();
|
document.webkitCancelFullScreen();
|
||||||
return true
|
return true;
|
||||||
}
|
}
|
||||||
} else if (isOptionOn("fullscreen") && !document.fullscreenElement) {
|
} else if (isOptionOn("fullscreen") && !document.fullscreenElement) {
|
||||||
const docel = document.documentElement;
|
const docel = document.documentElement;
|
||||||
if (docel.requestFullscreen) {
|
if (docel.requestFullscreen) {
|
||||||
docel.requestFullscreen();
|
docel.requestFullscreen();
|
||||||
return true
|
return true;
|
||||||
} else if (docel.webkitRequestFullscreen) {
|
} else if (docel.webkitRequestFullscreen) {
|
||||||
docel.webkitRequestFullscreen();
|
docel.webkitRequestFullscreen();
|
||||||
return true
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn(e);
|
console.warn(e);
|
||||||
|
|
||||||
}
|
}
|
||||||
return false
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async function openUnlocksList() {
|
async function openUnlocksList() {
|
||||||
const ts = getTotalScore();
|
const ts = getTotalScore();
|
||||||
const upgradeActions = upgrades
|
const upgradeActions = upgrades
|
||||||
|
@ -831,7 +847,7 @@ async function openUnlocksList() {
|
||||||
disabled: ts < threshold,
|
disabled: ts < threshold,
|
||||||
value: { perks: { [id]: 1 } } as RunParams,
|
value: { perks: { [id]: 1 } } as RunParams,
|
||||||
icon,
|
icon,
|
||||||
}))
|
}));
|
||||||
|
|
||||||
const levelActions = allLevels
|
const levelActions = allLevels
|
||||||
.sort((a, b) => a.threshold - b.threshold)
|
.sort((a, b) => a.threshold - b.threshold)
|
||||||
|
@ -849,11 +865,12 @@ async function openUnlocksList() {
|
||||||
value: { level: l.name } as RunParams,
|
value: { level: l.name } as RunParams,
|
||||||
icon: icons[l.name],
|
icon: icons[l.name],
|
||||||
};
|
};
|
||||||
})
|
});
|
||||||
|
|
||||||
const percentUnlock = Math.round(
|
const percentUnlock = Math.round(
|
||||||
([...upgradeActions, ...levelActions].filter((a) => !a.disabled).length / (upgradeActions.length +
|
([...upgradeActions, ...levelActions].filter((a) => !a.disabled).length /
|
||||||
levelActions.length)) * 100,
|
(upgradeActions.length + levelActions.length)) *
|
||||||
|
100,
|
||||||
);
|
);
|
||||||
const tryOn = await asyncAlert<RunParams>({
|
const tryOn = await asyncAlert<RunParams>({
|
||||||
title: t("unlocks.title", { percentUnlock }),
|
title: t("unlocks.title", { percentUnlock }),
|
||||||
|
@ -863,7 +880,6 @@ async function openUnlocksList() {
|
||||||
...upgradeActions,
|
...upgradeActions,
|
||||||
t("unlocks.level"),
|
t("unlocks.level"),
|
||||||
...levelActions,
|
...levelActions,
|
||||||
|
|
||||||
],
|
],
|
||||||
allowClose: true,
|
allowClose: true,
|
||||||
actionsAsGrid: true,
|
actionsAsGrid: true,
|
||||||
|
@ -894,7 +910,6 @@ export async function confirmRestart(gameState) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const pressed: { [k: string]: number } = {
|
const pressed: { [k: string]: number } = {
|
||||||
ArrowLeft: 0,
|
ArrowLeft: 0,
|
||||||
ArrowRight: 0,
|
ArrowRight: 0,
|
||||||
|
@ -912,8 +927,8 @@ export function setKeyPressed(key: string, on: 0 | 1) {
|
||||||
|
|
||||||
document.addEventListener("keydown", (e) => {
|
document.addEventListener("keydown", (e) => {
|
||||||
if (e.key.toLowerCase() === "f" && !e.ctrlKey && !e.metaKey) {
|
if (e.key.toLowerCase() === "f" && !e.ctrlKey && !e.metaKey) {
|
||||||
toggleOption('fullscreen');
|
toggleOption("fullscreen");
|
||||||
applyFullScreenChoice()
|
applyFullScreenChoice();
|
||||||
} else if (e.key in pressed) {
|
} else if (e.key in pressed) {
|
||||||
setKeyPressed(e.key, 1);
|
setKeyPressed(e.key, 1);
|
||||||
}
|
}
|
||||||
|
@ -970,7 +985,6 @@ export function restart(params: RunParams) {
|
||||||
setLevel(gameState, 0);
|
setLevel(gameState, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
restart(
|
restart(
|
||||||
(window.location.search.includes("stressTest") && {
|
(window.location.search.includes("stressTest") && {
|
||||||
level: "Bird",
|
level: "Bird",
|
||||||
|
@ -983,8 +997,7 @@ restart(
|
||||||
clairvoyant: 3,
|
clairvoyant: 3,
|
||||||
bigger_explosions: 2,
|
bigger_explosions: 2,
|
||||||
sapper: 2,
|
sapper: 2,
|
||||||
unbounded:1
|
unbounded: 1,
|
||||||
|
|
||||||
},
|
},
|
||||||
levelsPerLoop: 2,
|
levelsPerLoop: 2,
|
||||||
}) ||
|
}) ||
|
||||||
|
|
|
@ -31,10 +31,22 @@ import {
|
||||||
import { t } from "./i18n/i18n";
|
import { t } from "./i18n/i18n";
|
||||||
import { icons, upgrades } from "./loadGameData";
|
import { icons, upgrades } from "./loadGameData";
|
||||||
|
|
||||||
import {addToTotalScore, getCurrentMaxCoins, getCurrentMaxParticles,} from "./settings";
|
import {
|
||||||
|
addToTotalScore,
|
||||||
|
getCurrentMaxCoins,
|
||||||
|
getCurrentMaxParticles,
|
||||||
|
} from "./settings";
|
||||||
import { background } from "./render";
|
import { background } from "./render";
|
||||||
import { gameOver } from "./gameOver";
|
import { gameOver } from "./gameOver";
|
||||||
import {brickIndex, fitSize, gameState, hasBrick, hitsSomething, openUpgradesPicker, pause,} from "./game";
|
import {
|
||||||
|
brickIndex,
|
||||||
|
fitSize,
|
||||||
|
gameState,
|
||||||
|
hasBrick,
|
||||||
|
hitsSomething,
|
||||||
|
openUpgradesPicker,
|
||||||
|
pause,
|
||||||
|
} from "./game";
|
||||||
import { stopRecording } from "./recording";
|
import { stopRecording } from "./recording";
|
||||||
import { isOptionOn } from "./options";
|
import { isOptionOn } from "./options";
|
||||||
import { isPremium } from "./premium";
|
import { isPremium } from "./premium";
|
||||||
|
@ -119,18 +131,27 @@ export function normalizeGameState(gameState: GameState) {
|
||||||
gameState.perks.slow_down * 2,
|
gameState.perks.slow_down * 2,
|
||||||
);
|
);
|
||||||
|
|
||||||
gameState.puckWidth = Math.max(gameState.ballSize,
|
gameState.puckWidth = Math.max(
|
||||||
|
gameState.ballSize,
|
||||||
(gameState.gameZoneWidth / 12) *
|
(gameState.gameZoneWidth / 12) *
|
||||||
Math.min(12, 3 - gameState.perks.smaller_puck + gameState.perks.bigger_puck));
|
Math.min(
|
||||||
|
12,
|
||||||
|
3 - gameState.perks.smaller_puck + gameState.perks.bigger_puck,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
const corner = gameState.levelTime ? gameState.perks.corner_shot : 0
|
const corner = gameState.levelTime ? gameState.perks.corner_shot : 0;
|
||||||
|
|
||||||
let minX = gameState.offsetXRoundedDown + gameState.puckWidth / 2 - gameState.puckWidth * corner
|
let minX =
|
||||||
|
gameState.offsetXRoundedDown +
|
||||||
|
gameState.puckWidth / 2 -
|
||||||
|
gameState.puckWidth * corner;
|
||||||
|
|
||||||
let maxX = gameState.offsetXRoundedDown +
|
let maxX =
|
||||||
|
gameState.offsetXRoundedDown +
|
||||||
gameState.gameZoneWidthRoundedUp -
|
gameState.gameZoneWidthRoundedUp -
|
||||||
gameState.puckWidth / 2 + gameState.puckWidth * corner;
|
gameState.puckWidth / 2 +
|
||||||
|
gameState.puckWidth * corner;
|
||||||
|
|
||||||
gameState.puckPosition = clamp(gameState.puckPosition, minX, maxX);
|
gameState.puckPosition = clamp(gameState.puckPosition, minX, maxX);
|
||||||
|
|
||||||
|
@ -165,7 +186,7 @@ export function resetCombo(
|
||||||
|
|
||||||
if (prev > gameState.combo && gameState.perks.soft_reset) {
|
if (prev > gameState.combo && gameState.perks.soft_reset) {
|
||||||
gameState.combo += Math.floor(
|
gameState.combo += Math.floor(
|
||||||
(prev - gameState.combo) * comboKeepingRate(gameState.perks.soft_reset)
|
(prev - gameState.combo) * comboKeepingRate(gameState.perks.soft_reset),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const lost = Math.max(0, prev - gameState.combo);
|
const lost = Math.max(0, prev - gameState.combo);
|
||||||
|
@ -177,8 +198,15 @@ export function resetCombo(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (typeof x !== "undefined" && typeof y !== "undefined") {
|
if (typeof x !== "undefined" && typeof y !== "undefined") {
|
||||||
|
makeText(
|
||||||
makeText(gameState, x, y, "red", "-" + lost, 20, 500 + clamp(lost, 0, 500));
|
gameState,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
"red",
|
||||||
|
"-" + lost,
|
||||||
|
20,
|
||||||
|
500 + clamp(lost, 0, 500),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return lost;
|
return lost;
|
||||||
|
@ -255,11 +283,13 @@ export function explosionAt(
|
||||||
x: number,
|
x: number,
|
||||||
y: number,
|
y: number,
|
||||||
ball: Ball,
|
ball: Ball,
|
||||||
extraSize: number = 0
|
extraSize: number = 0,
|
||||||
) {
|
) {
|
||||||
const size = 1 + gameState.perks.bigger_explosions +
|
const size =
|
||||||
Math.max(0, gameState.perks.implosions - 1) + extraSize
|
1 +
|
||||||
;
|
gameState.perks.bigger_explosions +
|
||||||
|
Math.max(0, gameState.perks.implosions - 1) +
|
||||||
|
extraSize;
|
||||||
schedulGameSound(gameState, "explode", ball.x, 1);
|
schedulGameSound(gameState, "explode", ball.x, 1);
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
const col = index % gameState.gridSize;
|
const col = index % gameState.gridSize;
|
||||||
|
@ -292,21 +322,9 @@ export function explosionAt(
|
||||||
|
|
||||||
// makeLight(gameState, x, y, "white", gameState.brickWidth * 2, 150);
|
// makeLight(gameState, x, y, "white", gameState.brickWidth * 2, 150);
|
||||||
if (gameState.perks.implosions) {
|
if (gameState.perks.implosions) {
|
||||||
spawnImplosion(
|
spawnImplosion(gameState, 7 * size, x, y, "white");
|
||||||
gameState,
|
|
||||||
7 * size,
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
"white",
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
spawnExplosion(
|
spawnExplosion(gameState, 7 * size, x, y, "white");
|
||||||
gameState,
|
|
||||||
7 * size,
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
"white",
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
gameState.runStatistics.bricks_broken++;
|
gameState.runStatistics.bricks_broken++;
|
||||||
|
@ -467,13 +485,17 @@ export function explodeBrick(
|
||||||
spawnExplosion(gameState, 5 + Math.min(gameState.combo, 30), x, y, color);
|
spawnExplosion(gameState, 5 + Math.min(gameState.combo, 30), x, y, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gameState.perks.respawn && color !== "black" && !gameState.bricks[index]) {
|
if (
|
||||||
|
gameState.perks.respawn &&
|
||||||
|
color !== "black" &&
|
||||||
|
!gameState.bricks[index]
|
||||||
|
) {
|
||||||
if (Math.random() < comboKeepingRate(gameState.perks.respawn)) {
|
if (Math.random() < comboKeepingRate(gameState.perks.respawn)) {
|
||||||
append(gameState.respawns, b => {
|
append(gameState.respawns, (b) => {
|
||||||
b.color = color
|
b.color = color;
|
||||||
b.index = index
|
b.index = index;
|
||||||
b.time = gameState.levelTime + 3 * 1000 / gameState.perks.respawn
|
b.time = gameState.levelTime + (3 * 1000) / gameState.perks.respawn;
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -579,7 +601,7 @@ export async function gotoNextLoop(gameState: GameState) {
|
||||||
t("loop.instructions"),
|
t("loop.instructions"),
|
||||||
comboText,
|
comboText,
|
||||||
...userPerks
|
...userPerks
|
||||||
.filter(u => u.id !== 'instant_upgrade')
|
.filter((u) => u.id !== "instant_upgrade")
|
||||||
.map((u) => {
|
.map((u) => {
|
||||||
return {
|
return {
|
||||||
text:
|
text:
|
||||||
|
@ -595,11 +617,11 @@ export async function gotoNextLoop(gameState: GameState) {
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
userPerks.forEach(u => {
|
userPerks.forEach((u) => {
|
||||||
if (u.id !== keep) {
|
if (u.id !== keep) {
|
||||||
gameState.bannedPerks[u.id] = 1
|
gameState.bannedPerks[u.id] = 1;
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
Object.assign(gameState.perks, makeEmptyPerksMap(upgrades), {
|
Object.assign(gameState.perks, makeEmptyPerksMap(upgrades), {
|
||||||
[keep]: gameState.perks[keep],
|
[keep]: gameState.perks[keep],
|
||||||
|
@ -642,7 +664,8 @@ export async function setLevel(gameState: GameState, l: number) {
|
||||||
gameState.combo += Math.round(
|
gameState.combo += Math.round(
|
||||||
Math.max(
|
Math.max(
|
||||||
0,
|
0,
|
||||||
(finalCombo - gameState.combo) * comboKeepingRate(gameState.perks.shunt),
|
(finalCombo - gameState.combo) *
|
||||||
|
comboKeepingRate(gameState.perks.shunt),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -675,8 +698,7 @@ function setBrick(gameState: GameState, index: number, color: string) {
|
||||||
gameState.bricks[index] = color || "";
|
gameState.bricks[index] = color || "";
|
||||||
gameState.brickHP[index] =
|
gameState.brickHP[index] =
|
||||||
(color === "black" && 1) ||
|
(color === "black" && 1) ||
|
||||||
(color &&
|
(color && 1 + gameState.perks.sturdy_bricks) ||
|
||||||
1 + gameState.perks.sturdy_bricks) ||
|
|
||||||
0;
|
0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -805,11 +827,9 @@ export function coinBrickHitCheck(gameState: GameState, coin: Coin) {
|
||||||
if (gameState.perks.ghost_coins) {
|
if (gameState.perks.ghost_coins) {
|
||||||
// slow down
|
// slow down
|
||||||
if (typeof (vhit ?? hhit ?? chit) !== "undefined") {
|
if (typeof (vhit ?? hhit ?? chit) !== "undefined") {
|
||||||
|
|
||||||
coin.vy *= 1 - 0.2 / gameState.perks.ghost_coins;
|
coin.vy *= 1 - 0.2 / gameState.perks.ghost_coins;
|
||||||
coin.vx *= 1 - 0.2 / gameState.perks.ghost_coins;
|
coin.vx *= 1 - 0.2 / gameState.perks.ghost_coins;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (typeof vhit !== "undefined" || typeof chit !== "undefined") {
|
if (typeof vhit !== "undefined" || typeof chit !== "undefined") {
|
||||||
coin.y = coin.previousY;
|
coin.y = coin.previousY;
|
||||||
|
@ -934,7 +954,7 @@ export function gameStateTick(
|
||||||
gameState.autoCleanUses++;
|
gameState.autoCleanUses++;
|
||||||
}
|
}
|
||||||
|
|
||||||
const hasPendingBricks = liveCount(gameState.respawns)
|
const hasPendingBricks = liveCount(gameState.respawns);
|
||||||
|
|
||||||
if (gameState.running && !remainingBricks && !hasPendingBricks) {
|
if (gameState.running && !remainingBricks && !hasPendingBricks) {
|
||||||
if (!gameState.winAt) {
|
if (!gameState.winAt) {
|
||||||
|
@ -997,10 +1017,8 @@ export function gameStateTick(
|
||||||
|
|
||||||
const ratio =
|
const ratio =
|
||||||
1 -
|
1 -
|
||||||
(gameState.perks.viscosity *
|
((gameState.perks.viscosity * 0.03 + 0.005) * frames) /
|
||||||
0.03 +
|
(1 + gameState.perks.etherealcoins);
|
||||||
0.005) *
|
|
||||||
frames / (1 + gameState.perks.etherealcoins);
|
|
||||||
|
|
||||||
coin.vy *= ratio;
|
coin.vy *= ratio;
|
||||||
coin.vx *= ratio;
|
coin.vx *= ratio;
|
||||||
|
@ -1018,7 +1036,8 @@ export function gameStateTick(
|
||||||
gameState.perks.helium > 0 &&
|
gameState.perks.helium > 0 &&
|
||||||
Math.abs(coin.x - gameState.puckPosition) * 2 >
|
Math.abs(coin.x - gameState.puckPosition) * 2 >
|
||||||
gameState.puckWidth + coin.size;
|
gameState.puckWidth + coin.size;
|
||||||
coin.vy += frames * coin.weight * 0.8 * (flip ? -gameState.perks.helium : 1);
|
coin.vy +=
|
||||||
|
frames * coin.weight * 0.8 * (flip ? -gameState.perks.helium : 1);
|
||||||
if (flip && !isOptionOn("basic") && Math.random() < 0.1) {
|
if (flip && !isOptionOn("basic") && Math.random() < 0.1) {
|
||||||
makeParticle(
|
makeParticle(
|
||||||
gameState,
|
gameState,
|
||||||
|
@ -1034,7 +1053,6 @@ export function gameStateTick(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
|
@ -1047,12 +1065,11 @@ export function gameStateTick(
|
||||||
// a bit of margin to be nice , negative in case it's a negative coin
|
// 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);
|
||||||
|
|
||||||
destroy(gameState.coins, coinIndex);
|
destroy(gameState.coins, coinIndex);
|
||||||
} else if (coin.y > gameState.canvasHeight + coinRadius) {
|
} else if (coin.y > gameState.canvasHeight + coinRadius) {
|
||||||
gameState.levelLostCoins+=coin.points
|
gameState.levelLostCoins += coin.points;
|
||||||
destroy(gameState.coins, coinIndex);
|
destroy(gameState.coins, coinIndex);
|
||||||
if (gameState.perks.compound_interest) {
|
if (gameState.perks.compound_interest) {
|
||||||
resetCombo(gameState, coin.x, coin.y);
|
resetCombo(gameState, coin.x, coin.y);
|
||||||
|
@ -1060,12 +1077,11 @@ export function gameStateTick(
|
||||||
} else if (
|
} else if (
|
||||||
gameState.perks.unbounded &&
|
gameState.perks.unbounded &&
|
||||||
(coin.x < -gameState.gameZoneWidth / 2 ||
|
(coin.x < -gameState.gameZoneWidth / 2 ||
|
||||||
coin.x > gameState.canvasWidth + gameState.gameZoneWidth / 2
|
coin.x > gameState.canvasWidth + gameState.gameZoneWidth / 2 ||
|
||||||
|| coin.y < -gameState.gameZoneWidth
|
coin.y < -gameState.gameZoneWidth)
|
||||||
)
|
|
||||||
) {
|
) {
|
||||||
// Out of bound on sides
|
// Out of bound on sides
|
||||||
gameState.levelLostCoins+=coin.points
|
gameState.levelLostCoins += coin.points;
|
||||||
destroy(gameState.coins, coinIndex);
|
destroy(gameState.coins, coinIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1140,7 +1156,14 @@ export function gameStateTick(
|
||||||
((Math.random() - 0.5) * limit) / 3;
|
((Math.random() - 0.5) * limit) / 3;
|
||||||
|
|
||||||
let index = brickIndex(x, y);
|
let index = brickIndex(x, y);
|
||||||
explosionAt(gameState, index, x, y, a, Math.max(0, gameState.perks.shocks - 1));
|
explosionAt(
|
||||||
|
gameState,
|
||||||
|
index,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
a,
|
||||||
|
Math.max(0, gameState.perks.shocks - 1),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
@ -1276,10 +1299,10 @@ export function gameStateTick(
|
||||||
// Respawn what's needed, show particles
|
// Respawn what's needed, show particles
|
||||||
forEachLiveOne(gameState.respawns, (r, ri) => {
|
forEachLiveOne(gameState.respawns, (r, ri) => {
|
||||||
if (gameState.bricks[r.index]) {
|
if (gameState.bricks[r.index]) {
|
||||||
destroy(gameState.respawns, ri)
|
destroy(gameState.respawns, ri);
|
||||||
} else if (gameState.levelTime > r.time) {
|
} else if (gameState.levelTime > r.time) {
|
||||||
setBrick(gameState, r.index, r.color)
|
setBrick(gameState, r.index, r.color);
|
||||||
destroy(gameState.respawns, ri)
|
destroy(gameState.respawns, ri);
|
||||||
} else if (!isOptionOn("basic")) {
|
} else if (!isOptionOn("basic")) {
|
||||||
const { index, color } = r;
|
const { index, color } = r;
|
||||||
const vertical = Math.random() > 0.5;
|
const vertical = Math.random() > 0.5;
|
||||||
|
@ -1298,8 +1321,7 @@ export function gameStateTick(
|
||||||
250,
|
250,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
|
|
||||||
forEachLiveOne(gameState.particles, (p, pi) => {
|
forEachLiveOne(gameState.particles, (p, pi) => {
|
||||||
if (gameState.levelTime > p.time + p.duration) {
|
if (gameState.levelTime > p.time + p.duration) {
|
||||||
|
@ -1334,14 +1356,12 @@ export function ballTick(gameState: GameState, ball: Ball, delta: number) {
|
||||||
ball.vx +=
|
ball.vx +=
|
||||||
((gameState.puckPosition - ball.x) / 1000) *
|
((gameState.puckPosition - ball.x) / 1000) *
|
||||||
delta *
|
delta *
|
||||||
gameState.perks.telekinesis
|
gameState.perks.telekinesis;
|
||||||
}
|
}
|
||||||
if (isYoyoActive(gameState, ball)) {
|
if (isYoyoActive(gameState, ball)) {
|
||||||
speedLimitDampener += 3;
|
speedLimitDampener += 3;
|
||||||
ball.vx +=
|
ball.vx +=
|
||||||
((gameState.puckPosition - ball.x) / 1000) *
|
((gameState.puckPosition - ball.x) / 1000) * delta * gameState.perks.yoyo;
|
||||||
delta *
|
|
||||||
gameState.perks.yoyo
|
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
ball.vx * ball.vx + ball.vy * ball.vy <
|
ball.vx * ball.vx + ball.vy * ball.vy <
|
||||||
|
@ -1390,7 +1410,6 @@ export function ballTick(gameState: GameState, ball: Ball, delta: number) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const borderHitCode = bordersHitCheck(
|
const borderHitCode = bordersHitCheck(
|
||||||
gameState,
|
gameState,
|
||||||
ball,
|
ball,
|
||||||
|
@ -1449,7 +1468,9 @@ export function ballTick(gameState: GameState, ball: Ball, delta: number) {
|
||||||
const angle = Math.atan2(
|
const angle = Math.atan2(
|
||||||
-gameState.puckWidth / 2,
|
-gameState.puckWidth / 2,
|
||||||
(ball.x - gameState.puckPosition) *
|
(ball.x - gameState.puckPosition) *
|
||||||
(gameState.perks.concave_puck ? -1 / (1 + gameState.perks.concave_puck) : 1),
|
(gameState.perks.concave_puck
|
||||||
|
? -1 / (1 + gameState.perks.concave_puck)
|
||||||
|
: 1),
|
||||||
);
|
);
|
||||||
ball.vx = speed * Math.cos(angle);
|
ball.vx = speed * Math.cos(angle);
|
||||||
ball.vy = speed * Math.sin(angle);
|
ball.vy = speed * Math.sin(angle);
|
||||||
|
@ -1471,7 +1492,6 @@ export function ballTick(gameState: GameState, ball: Ball, delta: number) {
|
||||||
resetCombo(gameState, ball.x, ball.y);
|
resetCombo(gameState, ball.x, ball.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!ball.hitSinceBounce && gameState.bricks.find((i) => i)) {
|
if (!ball.hitSinceBounce && gameState.bricks.find((i) => i)) {
|
||||||
gameState.runStatistics.misses++;
|
gameState.runStatistics.misses++;
|
||||||
if (gameState.perks.forgiving) {
|
if (gameState.perks.forgiving) {
|
||||||
|
@ -1505,15 +1525,14 @@ export function ballTick(gameState: GameState, ball: Ball, delta: number) {
|
||||||
(gameState.perks.unbounded && ball.x < -gameState.gameZoneWidth / 2) ||
|
(gameState.perks.unbounded && ball.x < -gameState.gameZoneWidth / 2) ||
|
||||||
ball.x > gameState.canvasWidth + gameState.gameZoneWidth / 2;
|
ball.x > gameState.canvasWidth + gameState.gameZoneWidth / 2;
|
||||||
|
|
||||||
const lostInTheSky = (gameState.perks.unbounded > 1 &&
|
const lostInTheSky =
|
||||||
ball.y < -gameState.gameZoneWidth / 2
|
gameState.perks.unbounded > 1 && ball.y < -gameState.gameZoneWidth / 2;
|
||||||
)
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
gameState.running &&
|
gameState.running &&
|
||||||
(ball.y > gameState.gameZoneHeight + gameState.ballSize / 2 || lostOnSides
|
(ball.y > gameState.gameZoneHeight + gameState.ballSize / 2 ||
|
||||||
|| lostInTheSky
|
lostOnSides ||
|
||||||
)
|
lostInTheSky)
|
||||||
) {
|
) {
|
||||||
ball.destroyed = true;
|
ball.destroyed = true;
|
||||||
gameState.runStatistics.balls_lost++;
|
gameState.runStatistics.balls_lost++;
|
||||||
|
@ -1638,7 +1657,7 @@ function justLostALife(gameState: GameState, ball: Ball, x: number, y: number) {
|
||||||
if (gameState.perks.extra_life < 0) {
|
if (gameState.perks.extra_life < 0) {
|
||||||
gameState.perks.extra_life = 0;
|
gameState.perks.extra_life = 0;
|
||||||
} else if (gameState.perks.sacrifice) {
|
} else if (gameState.perks.sacrifice) {
|
||||||
gameState.combo *= gameState.perks.sacrifice
|
gameState.combo *= gameState.perks.sacrifice;
|
||||||
gameState.bricks.forEach(
|
gameState.bricks.forEach(
|
||||||
(color, index) => color && explodeBrick(gameState, index, ball, true),
|
(color, index) => color && explodeBrick(gameState, index, ball, true),
|
||||||
);
|
);
|
||||||
|
@ -1671,8 +1690,8 @@ function makeCoin(
|
||||||
color = "gold",
|
color = "gold",
|
||||||
points = 1,
|
points = 1,
|
||||||
) {
|
) {
|
||||||
let weight = 0.8 + Math.random() * 0.2 + Math.min(2, points * 0.01)
|
let weight = 0.8 + Math.random() * 0.2 + Math.min(2, points * 0.01);
|
||||||
weight *= 5 / (5 + gameState.perks.etherealcoins)
|
weight *= 5 / (5 + gameState.perks.etherealcoins);
|
||||||
|
|
||||||
append(gameState.coins, (p: Partial<Coin>) => {
|
append(gameState.coins, (p: Partial<Coin>) => {
|
||||||
p.x = x;
|
p.x = x;
|
||||||
|
@ -1690,7 +1709,7 @@ function makeCoin(
|
||||||
p.sa = Math.random() - 0.5;
|
p.sa = Math.random() - 0.5;
|
||||||
p.points = points;
|
p.points = points;
|
||||||
p.weight = weight;
|
p.weight = weight;
|
||||||
p.metamorphosisPoints = gameState.perks.metamorphosis
|
p.metamorphosisPoints = gameState.perks.metamorphosis;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1791,16 +1810,16 @@ export function liveCount<T>(where: ReusableArray<T>) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function empty<T>(where: ReusableArray<T>) {
|
export function empty<T>(where: ReusableArray<T>) {
|
||||||
let destroyed=0
|
let destroyed = 0;
|
||||||
where.total = 0;
|
where.total = 0;
|
||||||
where.indexMin = 0;
|
where.indexMin = 0;
|
||||||
where.list.forEach((i) => {
|
where.list.forEach((i) => {
|
||||||
if (!i.destroyed) {
|
if (!i.destroyed) {
|
||||||
i.destroyed = true
|
i.destroyed = true;
|
||||||
destroyed++
|
destroyed++;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return destroyed
|
return destroyed;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function forEachLiveOne<T>(
|
export function forEachLiveOne<T>(
|
||||||
|
|
|
@ -15,9 +15,7 @@ export function sample<T>(arr: T[]): T {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sampleN<T>(arr: T[], n: number): T[] {
|
export function sampleN<T>(arr: T[], n: number): T[] {
|
||||||
|
return [...arr].sort(() => Math.random() - 0.5).slice(0, n);
|
||||||
return [...arr].sort(()=>Math.random()-0.5)
|
|
||||||
.slice(0,n)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sumOfValues(obj: { [key: string]: number } | undefined | null) {
|
export function sumOfValues(obj: { [key: string]: number } | undefined | null) {
|
||||||
|
@ -55,7 +53,10 @@ export function getRowColIndex(gameState: GameState, row: number, col: number) {
|
||||||
|
|
||||||
export function getPossibleUpgrades(gameState: GameState) {
|
export function getPossibleUpgrades(gameState: GameState) {
|
||||||
return upgrades
|
return upgrades
|
||||||
.filter((u) => gameState.totalScoreAtRunStart >= u.threshold || gameState.loop>0)
|
.filter(
|
||||||
|
(u) =>
|
||||||
|
gameState.totalScoreAtRunStart >= u.threshold || gameState.loop > 0,
|
||||||
|
)
|
||||||
.filter((u) => !u?.requires || gameState.perks[u?.requires]);
|
.filter((u) => !u?.requires || gameState.perks[u?.requires]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,4 +187,3 @@ export function countBricksBelow(gameState: GameState, index: number) {
|
||||||
}
|
}
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -129,5 +129,3 @@ export function newGameState(params: RunParams): GameState {
|
||||||
}
|
}
|
||||||
return gameState;
|
return gameState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -115,7 +115,6 @@ export function premiumMenuEntry(gameState: GameState) {
|
||||||
text = t("premium.per_hours", args);
|
text = t("premium.per_hours", args);
|
||||||
help = t("premium.per_hours_help", args);
|
help = t("premium.per_hours_help", args);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn(e);
|
console.warn(e);
|
||||||
|
|
|
@ -3,5 +3,5 @@ export function clamp(value: number, min: number, max: number) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function comboKeepingRate(level: number) {
|
export function comboKeepingRate(level: number) {
|
||||||
return clamp(1 - 1 / (1 + level) * 1.5, 0, 1)
|
return clamp(1 - (1 / (1 + level)) * 1.5, 0, 1);
|
||||||
}
|
}
|
102
src/render.ts
102
src/render.ts
|
@ -1,4 +1,4 @@
|
||||||
import {baseCombo, forEachLiveOne, liveCount,} from "./gameStateMutators";
|
import { baseCombo, forEachLiveOne, liveCount } from "./gameStateMutators";
|
||||||
import {
|
import {
|
||||||
brickCenterX,
|
brickCenterX,
|
||||||
brickCenterY,
|
brickCenterY,
|
||||||
|
@ -24,7 +24,7 @@ bombSVG.src =
|
||||||
btoa(`<svg width="144" height="144" viewBox="0 0 38.101 38.099" xmlns="http://www.w3.org/2000/svg">
|
btoa(`<svg width="144" height="144" viewBox="0 0 38.101 38.099" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path d="m6.1528 26.516c-2.6992-3.4942-2.9332-8.281-.58305-11.981a10.454 10.454 0 017.3701-4.7582c1.962-.27726 4.1646.05953 5.8835.90027l.45013.22017.89782-.87417c.83748-.81464.91169-.87499 1.0992-.90271.40528-.058713.58876.03425 1.1971.6116l.55451.52679 1.0821-1.0821c1.1963-1.1963 1.383-1.3357 2.1039-1.5877.57898-.20223 1.5681-.19816 2.1691.00897 1.4613.50314 2.3673 1.7622 2.3567 3.2773-.0058.95654-.24464 1.5795-.90924 2.3746-.40936.48928-.55533.81057-.57898 1.2737-.02039.41018.1109.77714.42322 1.1792.30172.38816.3694.61323.2797.93044-.12803.45666-.56674.71598-1.0242.60507-.601-.14597-1.3031-1.3088-1.3969-2.3126-.09459-1.0161.19245-1.8682.92392-2.7432.42567-.50885.5643-.82851.5643-1.3031 0-.50151-.14026-.83177-.51211-1.2028-.50966-.50966-1.0968-.64829-1.781-.41996l-.37348.12477-2.1006 2.1006.52597.55696c.45421.48194.5325.58876.57898.78855.09622.41588.07502.45014-.88396 1.4548l-.87173.9125.26339.57979a10.193 10.193 0 01.9231 4.1001c.03996 2.046-.41996 3.8082-1.4442 5.537-.55044.928-1.0185 1.5013-1.8968 2.3241-.83503.78284-1.5526 1.2827-2.4904 1.7361-3.4266 1.657-7.4721 1.3422-10.549-.82035-.73473-.51782-1.7312-1.4621-2.2515-2.1357zm21.869-4.5584c-.0579-.19734-.05871-2.2662 0-2.4545.11906-.39142.57898-.63361 1.0038-.53005.23812.05708.54147.32455.6116.5382.06279.19163.06769 2.1805.0065 2.3811-.12558.40773-.61649.67602-1.0462.57164-.234-.05708-.51615-.30498-.57568-.50722m3.0417-2.6013c-.12313-.6222.37837-1.1049 1.0479-1.0079.18348.0261.25279.08399 1.0071.83911.75838.75838.81301.82362.84074 1.0112.10193.68499-.40365 1.1938-1.034 1.0405-.1949-.0473-.28786-.12558-1.0144-.85216-.7649-.76409-.80241-.81057-.84645-1.0316m.61323-3.0629a.85623.85623 0 01.59284-.99975c.28949-.09214 2.1814-.08318 2.3917.01141.38734.17369.6279.61078.53984.98181-.06035.25606-.35391.57327-.60181.64992-.25279.07747-2.2278.053-2.4097-.03017-.26013-.11906-.46318-.36125-.51374-.61323" fill="#fff" opacity="0.3"/>
|
<path d="m6.1528 26.516c-2.6992-3.4942-2.9332-8.281-.58305-11.981a10.454 10.454 0 017.3701-4.7582c1.962-.27726 4.1646.05953 5.8835.90027l.45013.22017.89782-.87417c.83748-.81464.91169-.87499 1.0992-.90271.40528-.058713.58876.03425 1.1971.6116l.55451.52679 1.0821-1.0821c1.1963-1.1963 1.383-1.3357 2.1039-1.5877.57898-.20223 1.5681-.19816 2.1691.00897 1.4613.50314 2.3673 1.7622 2.3567 3.2773-.0058.95654-.24464 1.5795-.90924 2.3746-.40936.48928-.55533.81057-.57898 1.2737-.02039.41018.1109.77714.42322 1.1792.30172.38816.3694.61323.2797.93044-.12803.45666-.56674.71598-1.0242.60507-.601-.14597-1.3031-1.3088-1.3969-2.3126-.09459-1.0161.19245-1.8682.92392-2.7432.42567-.50885.5643-.82851.5643-1.3031 0-.50151-.14026-.83177-.51211-1.2028-.50966-.50966-1.0968-.64829-1.781-.41996l-.37348.12477-2.1006 2.1006.52597.55696c.45421.48194.5325.58876.57898.78855.09622.41588.07502.45014-.88396 1.4548l-.87173.9125.26339.57979a10.193 10.193 0 01.9231 4.1001c.03996 2.046-.41996 3.8082-1.4442 5.537-.55044.928-1.0185 1.5013-1.8968 2.3241-.83503.78284-1.5526 1.2827-2.4904 1.7361-3.4266 1.657-7.4721 1.3422-10.549-.82035-.73473-.51782-1.7312-1.4621-2.2515-2.1357zm21.869-4.5584c-.0579-.19734-.05871-2.2662 0-2.4545.11906-.39142.57898-.63361 1.0038-.53005.23812.05708.54147.32455.6116.5382.06279.19163.06769 2.1805.0065 2.3811-.12558.40773-.61649.67602-1.0462.57164-.234-.05708-.51615-.30498-.57568-.50722m3.0417-2.6013c-.12313-.6222.37837-1.1049 1.0479-1.0079.18348.0261.25279.08399 1.0071.83911.75838.75838.81301.82362.84074 1.0112.10193.68499-.40365 1.1938-1.034 1.0405-.1949-.0473-.28786-.12558-1.0144-.85216-.7649-.76409-.80241-.81057-.84645-1.0316m.61323-3.0629a.85623.85623 0 01.59284-.99975c.28949-.09214 2.1814-.08318 2.3917.01141.38734.17369.6279.61078.53984.98181-.06035.25606-.35391.57327-.60181.64992-.25279.07747-2.2278.053-2.4097-.03017-.26013-.11906-.46318-.36125-.51374-.61323" fill="#fff" opacity="0.3"/>
|
||||||
</svg>`);
|
</svg>`);
|
||||||
bombSVG.onload = () => gameState.needsRender = true
|
bombSVG.onload = () => (gameState.needsRender = true);
|
||||||
|
|
||||||
export const background = document.createElement("img");
|
export const background = document.createElement("img");
|
||||||
export const backgroundCanvas = document.createElement("canvas");
|
export const backgroundCanvas = document.createElement("canvas");
|
||||||
|
@ -51,34 +51,37 @@ export function render(gameState: GameState) {
|
||||||
menuLabel.innerText = t("play.menu_label");
|
menuLabel.innerText = t("play.menu_label");
|
||||||
}
|
}
|
||||||
|
|
||||||
const catchRate = gameState.levelSpawnedCoins ?
|
const catchRate = gameState.levelSpawnedCoins
|
||||||
(gameState.levelSpawnedCoins - gameState.levelLostCoins)/gameState.levelSpawnedCoins :1
|
? (gameState.levelSpawnedCoins - gameState.levelLostCoins) /
|
||||||
|
gameState.levelSpawnedCoins
|
||||||
|
: 1;
|
||||||
|
|
||||||
scoreDisplay.innerHTML =
|
scoreDisplay.innerHTML =
|
||||||
(isOptionOn("show_fps") ? `
|
(isOptionOn("show_fps")
|
||||||
<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>
|
||||||
|
|
||||||
`:'')+
|
`
|
||||||
|
: "") +
|
||||||
|
(isOptionOn("show_stats")
|
||||||
|
? `
|
||||||
(isOptionOn('show_stats') ? `
|
<span class="${(catchRate == 1 && "great") || (catchRate > 0.9 && "good") || ""}">
|
||||||
<span class="${(catchRate==1 && 'great') || (catchRate>0.9 && 'good')||''}">
|
|
||||||
${Math.floor(catchRate * 100)}%
|
${Math.floor(catchRate * 100)}%
|
||||||
</span><span> / </span>
|
</span><span> / </span>
|
||||||
<span class="${(gameState.levelWallBounces==0 && 'great') || (gameState.levelWallBounces<5 && 'good')||''}">
|
<span class="${(gameState.levelWallBounces == 0 && "great") || (gameState.levelWallBounces < 5 && "good") || ""}">
|
||||||
${gameState.levelWallBounces} B
|
${gameState.levelWallBounces} B
|
||||||
</span><span> / </span>
|
</span><span> / </span>
|
||||||
<span class="${(gameState.levelTime<30000 && 'great') || (gameState.levelTime<60000 && 'good')||''}">
|
<span class="${(gameState.levelTime < 30000 && "great") || (gameState.levelTime < 60000 && "good") || ""}">
|
||||||
${Math.ceil(gameState.levelTime / 1000)}s
|
${Math.ceil(gameState.levelTime / 1000)}s
|
||||||
</span><span> / </span>
|
</span><span> / </span>
|
||||||
<span class="${(gameState.levelMisses==0 && 'great') || (gameState.levelMisses<=3 && 'good')||''}">
|
<span class="${(gameState.levelMisses == 0 && "great") || (gameState.levelMisses <= 3 && "good") || ""}">
|
||||||
${gameState.levelMisses} M
|
${gameState.levelMisses} M
|
||||||
</span><span> / </span>
|
</span><span> / </span>
|
||||||
`: '' )+ `$${gameState.score}`;
|
`
|
||||||
|
: "") +
|
||||||
|
`$${gameState.score}`;
|
||||||
|
|
||||||
scoreDisplay.className =
|
scoreDisplay.className =
|
||||||
gameState.lastScoreIncrease > gameState.levelTime - 500 ? "active" : "";
|
gameState.lastScoreIncrease > gameState.levelTime - 500 ? "active" : "";
|
||||||
|
@ -147,24 +150,25 @@ export function render(gameState: GameState) {
|
||||||
bgctx.fillStyle = level.color || "#000";
|
bgctx.fillStyle = level.color || "#000";
|
||||||
bgctx.fillRect(0, 0, gameState.canvasWidth, gameState.canvasHeight);
|
bgctx.fillRect(0, 0, gameState.canvasWidth, gameState.canvasHeight);
|
||||||
if (gameState.perks.clairvoyant >= 3) {
|
if (gameState.perks.clairvoyant >= 3) {
|
||||||
const pageSource = document.body.innerHTML.replace(/\s+/gi, '')
|
const pageSource = document.body.innerHTML.replace(/\s+/gi, "");
|
||||||
const lineWidth = Math.ceil(gameState.canvasWidth / 15)
|
const lineWidth = Math.ceil(gameState.canvasWidth / 15);
|
||||||
const lines = Math.ceil(gameState.canvasHeight / 20)
|
const lines = Math.ceil(gameState.canvasHeight / 20);
|
||||||
const chars = lineWidth * lines
|
const chars = lineWidth * lines;
|
||||||
let start = Math.ceil(Math.random() * (pageSource.length - chars))
|
let start = Math.ceil(Math.random() * (pageSource.length - chars));
|
||||||
for (let i = 0; i < lines; i++) {
|
for (let i = 0; i < lines; i++) {
|
||||||
bgctx.fillStyle = 'white'
|
bgctx.fillStyle = "white";
|
||||||
bgctx.font = '20px Courier'
|
bgctx.font = "20px Courier";
|
||||||
bgctx.fillText(pageSource.slice(
|
bgctx.fillText(
|
||||||
|
pageSource.slice(
|
||||||
start + i * lineWidth,
|
start + i * lineWidth,
|
||||||
start + (i + 1) * lineWidth),
|
start + (i + 1) * lineWidth,
|
||||||
|
),
|
||||||
0,
|
0,
|
||||||
i * 20,
|
i * 20,
|
||||||
gameState.canvasWidth
|
gameState.canvasWidth,
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
const pattern = ctx.createPattern(background, "repeat");
|
const pattern = ctx.createPattern(background, "repeat");
|
||||||
if (pattern) {
|
if (pattern) {
|
||||||
bgctx.fillStyle = pattern;
|
bgctx.fillStyle = pattern;
|
||||||
|
@ -223,7 +227,7 @@ export function render(gameState: GameState) {
|
||||||
coin.x,
|
coin.x,
|
||||||
coin.y,
|
coin.y,
|
||||||
(hasCombo && gameState.perks.asceticism && "red") ||
|
(hasCombo && gameState.perks.asceticism && "red") ||
|
||||||
(coin.color==='gold' && 'gold')||
|
(coin.color === "gold" && "gold") ||
|
||||||
gameState.puckColor,
|
gameState.puckColor,
|
||||||
coin.a,
|
coin.a,
|
||||||
);
|
);
|
||||||
|
@ -242,7 +246,6 @@ export function render(gameState: GameState) {
|
||||||
ball.y,
|
ball.y,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.globalCompositeOperation = "source-over";
|
ctx.globalCompositeOperation = "source-over";
|
||||||
|
@ -458,7 +461,7 @@ export function render(gameState: GameState) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.globalAlpha = gameState.perks.unbounded > 1 ? 0.1 : 1
|
ctx.globalAlpha = gameState.perks.unbounded > 1 ? 0.1 : 1;
|
||||||
drawStraightLine(
|
drawStraightLine(
|
||||||
ctx,
|
ctx,
|
||||||
gameState,
|
gameState,
|
||||||
|
@ -470,7 +473,7 @@ export function render(gameState: GameState) {
|
||||||
1,
|
1,
|
||||||
);
|
);
|
||||||
|
|
||||||
ctx.globalAlpha = 1
|
ctx.globalAlpha = 1;
|
||||||
drawStraightLine(
|
drawStraightLine(
|
||||||
ctx,
|
ctx,
|
||||||
gameState,
|
gameState,
|
||||||
|
@ -496,7 +499,6 @@ export function render(gameState: GameState) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (shaked) {
|
if (shaked) {
|
||||||
ctx.resetTransform();
|
ctx.resetTransform();
|
||||||
}
|
}
|
||||||
|
@ -608,7 +610,8 @@ export function renderAllBricks() {
|
||||||
countBricksAbove(gameState, index) &&
|
countBricksAbove(gameState, index) &&
|
||||||
!countBricksBelow(gameState, index);
|
!countBricksBelow(gameState, index);
|
||||||
|
|
||||||
let redBorder = (gameState.ballsColor !== color &&
|
let redBorder =
|
||||||
|
(gameState.ballsColor !== color &&
|
||||||
color !== "black" &&
|
color !== "black" &&
|
||||||
redBorderOnBricksWithWrongColor) ||
|
redBorderOnBricksWithWrongColor) ||
|
||||||
(hasCombo && gameState.perks.zen && color === "black") ||
|
(hasCombo && gameState.perks.zen && color === "black") ||
|
||||||
|
@ -616,8 +619,14 @@ export function renderAllBricks() {
|
||||||
redColorOnAllBricks;
|
redColorOnAllBricks;
|
||||||
|
|
||||||
canctx.globalCompositeOperation = "source-over";
|
canctx.globalCompositeOperation = "source-over";
|
||||||
drawBrick(canctx,
|
drawBrick(
|
||||||
color, x, y, redBorder ? offset : -1, gameState.perks.clairvoyant >= 2);
|
canctx,
|
||||||
|
color,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
redBorder ? offset : -1,
|
||||||
|
gameState.perks.clairvoyant >= 2,
|
||||||
|
);
|
||||||
if (gameState.brickHP[index] > 1 && gameState.perks.clairvoyant) {
|
if (gameState.brickHP[index] > 1 && gameState.perks.clairvoyant) {
|
||||||
canctx.globalCompositeOperation = "destination-out";
|
canctx.globalCompositeOperation = "destination-out";
|
||||||
drawText(
|
drawText(
|
||||||
|
@ -677,9 +686,9 @@ export function drawPuck(
|
||||||
canctx.lineTo(0, puckHeight * 0.75);
|
canctx.lineTo(0, puckHeight * 0.75);
|
||||||
canctx.bezierCurveTo(
|
canctx.bezierCurveTo(
|
||||||
puckWidth / 2,
|
puckWidth / 2,
|
||||||
puckHeight * (2 + concave_puck) / 3,
|
(puckHeight * (2 + concave_puck)) / 3,
|
||||||
puckWidth / 2,
|
puckWidth / 2,
|
||||||
puckHeight * (2 + concave_puck) / 3,
|
(puckHeight * (2 + concave_puck)) / 3,
|
||||||
puckWidth,
|
puckWidth,
|
||||||
puckHeight * 0.75,
|
puckHeight * 0.75,
|
||||||
);
|
);
|
||||||
|
@ -865,7 +874,7 @@ export function drawBrick(
|
||||||
x: number,
|
x: number,
|
||||||
y: number,
|
y: number,
|
||||||
offset: number = 0,
|
offset: number = 0,
|
||||||
borderOnly: boolean
|
borderOnly: boolean,
|
||||||
) {
|
) {
|
||||||
const tlx = Math.ceil(x - gameState.brickWidth / 2);
|
const tlx = Math.ceil(x - gameState.brickWidth / 2);
|
||||||
const tly = Math.ceil(y - gameState.brickWidth / 2);
|
const tly = Math.ceil(y - gameState.brickWidth / 2);
|
||||||
|
@ -874,7 +883,18 @@ export function drawBrick(
|
||||||
|
|
||||||
const width = brx - tlx,
|
const width = brx - tlx,
|
||||||
height = bry - tly;
|
height = bry - tly;
|
||||||
const key = "brick" + color + "_" + "_" + width + "_" + height + "_" + offset + '_' + borderOnly;
|
const key =
|
||||||
|
"brick" +
|
||||||
|
color +
|
||||||
|
"_" +
|
||||||
|
"_" +
|
||||||
|
width +
|
||||||
|
"_" +
|
||||||
|
height +
|
||||||
|
"_" +
|
||||||
|
offset +
|
||||||
|
"_" +
|
||||||
|
borderOnly;
|
||||||
|
|
||||||
if (!cachedGraphics[key]) {
|
if (!cachedGraphics[key]) {
|
||||||
const can = document.createElement("canvas");
|
const can = document.createElement("canvas");
|
||||||
|
|
8
src/types.d.ts
vendored
8
src/types.d.ts
vendored
|
@ -238,8 +238,12 @@ export type GameState = {
|
||||||
coins: ReusableArray<Coin>;
|
coins: ReusableArray<Coin>;
|
||||||
|
|
||||||
// Bricks that should respawn destroyed
|
// Bricks that should respawn destroyed
|
||||||
respawns: ReusableArray<{ index: number; color: string ; time:number;
|
respawns: ReusableArray<{
|
||||||
destroyed?: boolean;}>;
|
index: number;
|
||||||
|
color: string;
|
||||||
|
time: number;
|
||||||
|
destroyed?: boolean;
|
||||||
|
}>;
|
||||||
|
|
||||||
levelStartScore: number;
|
levelStartScore: number;
|
||||||
levelMisses: number;
|
levelMisses: number;
|
||||||
|
|
|
@ -289,7 +289,10 @@ export const rawUpgrades = [
|
||||||
id: "soft_reset",
|
id: "soft_reset",
|
||||||
max: 3,
|
max: 3,
|
||||||
name: t("upgrades.soft_reset.name"),
|
name: t("upgrades.soft_reset.name"),
|
||||||
help: (lvl: number) => t("upgrades.soft_reset.help", { percent: Math.round(comboKeepingRate(lvl) * 100)}),
|
help: (lvl: number) =>
|
||||||
|
t("upgrades.soft_reset.help", {
|
||||||
|
percent: Math.round(comboKeepingRate(lvl) * 100),
|
||||||
|
}),
|
||||||
fullHelp: t("upgrades.soft_reset.fullHelp"),
|
fullHelp: t("upgrades.soft_reset.fullHelp"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -368,7 +371,10 @@ export const rawUpgrades = [
|
||||||
max: 4,
|
max: 4,
|
||||||
name: t("upgrades.respawn.name"),
|
name: t("upgrades.respawn.name"),
|
||||||
help: (lvl: number) =>
|
help: (lvl: number) =>
|
||||||
t("upgrades.respawn.help",{percent:Math.floor(100*comboKeepingRate(lvl)),delay:(3/lvl).toFixed(2)}),
|
t("upgrades.respawn.help", {
|
||||||
|
percent: Math.floor(100 * comboKeepingRate(lvl)),
|
||||||
|
delay: (3 / lvl).toFixed(2),
|
||||||
|
}),
|
||||||
fullHelp: t("upgrades.respawn.fullHelp"),
|
fullHelp: t("upgrades.respawn.fullHelp"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -433,9 +439,10 @@ export const rawUpgrades = [
|
||||||
id: "unbounded",
|
id: "unbounded",
|
||||||
max: 1,
|
max: 1,
|
||||||
name: t("upgrades.unbounded.name"),
|
name: t("upgrades.unbounded.name"),
|
||||||
help: (lvl: number) => lvl > 1 ?
|
help: (lvl: number) =>
|
||||||
t("upgrades.unbounded.help_no_ceiling",{lvl}):
|
lvl > 1
|
||||||
t("upgrades.unbounded.help",{lvl}),
|
? t("upgrades.unbounded.help_no_ceiling", { lvl })
|
||||||
|
: t("upgrades.unbounded.help", { lvl }),
|
||||||
fullHelp: t("upgrades.unbounded.fullHelp"),
|
fullHelp: t("upgrades.unbounded.fullHelp"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -446,7 +453,10 @@ export const rawUpgrades = [
|
||||||
id: "shunt",
|
id: "shunt",
|
||||||
max: 3,
|
max: 3,
|
||||||
name: t("upgrades.shunt.name"),
|
name: t("upgrades.shunt.name"),
|
||||||
help: (lvl: number) => t("upgrades.shunt.help", { percent: Math.round(comboKeepingRate(lvl) * 100) }),
|
help: (lvl: number) =>
|
||||||
|
t("upgrades.shunt.help", {
|
||||||
|
percent: Math.round(comboKeepingRate(lvl) * 100),
|
||||||
|
}),
|
||||||
fullHelp: t("upgrades.shunt.fullHelp"),
|
fullHelp: t("upgrades.shunt.fullHelp"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -510,9 +520,9 @@ export const rawUpgrades = [
|
||||||
max: 1,
|
max: 1,
|
||||||
name: t("upgrades.sacrifice.name"),
|
name: t("upgrades.sacrifice.name"),
|
||||||
help: (lvl: number) =>
|
help: (lvl: number) =>
|
||||||
lvl==1 ?
|
lvl == 1
|
||||||
t("upgrades.sacrifice.help_l1"):
|
? t("upgrades.sacrifice.help_l1")
|
||||||
t("upgrades.sacrifice.help_over",{lvl}),
|
: t("upgrades.sacrifice.help_over", { lvl }),
|
||||||
fullHelp: t("upgrades.sacrifice.fullHelp"),
|
fullHelp: t("upgrades.sacrifice.fullHelp"),
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue