mirror of
https://gitlab.com/lecarore/breakout71.git
synced 2025-04-20 04:05:06 -04:00
Build 29049575
This commit is contained in:
parent
e3e61b12b8
commit
395968bc52
16 changed files with 1735 additions and 1847 deletions
|
@ -25,10 +25,6 @@ There's also an easy mode for kids (slower ball).
|
||||||
- people assume unbounded allows for wrap around
|
- people assume unbounded allows for wrap around
|
||||||
- popups not scrollable sometimes
|
- popups not scrollable sometimes
|
||||||
- fdroid build
|
- fdroid build
|
||||||
- deal with too many upgrades :
|
|
||||||
- disable some upgrades to remove them from the pool
|
|
||||||
- reroll mechanic
|
|
||||||
- extra option that just adds 10% to score
|
|
||||||
- coin magnet and viscosity : only one level ~2.5
|
- coin magnet and viscosity : only one level ~2.5
|
||||||
- Boost Ascetism : give +2 or even +3 combo per brick destroyed
|
- Boost Ascetism : give +2 or even +3 combo per brick destroyed
|
||||||
- wind : move coins based on puck movement not position
|
- wind : move coins based on puck movement not position
|
||||||
|
|
|
@ -11,8 +11,8 @@ android {
|
||||||
applicationId = "me.lecaro.breakout"
|
applicationId = "me.lecaro.breakout"
|
||||||
minSdk = 21
|
minSdk = 21
|
||||||
targetSdk = 34
|
targetSdk = 34
|
||||||
versionCode = 29048147
|
versionCode = 29049575
|
||||||
versionName = "29048147"
|
versionName = "29049575"
|
||||||
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
149
dist/index.html
vendored
149
dist/index.html
vendored
|
@ -631,7 +631,6 @@ var _newGameState = require("./newGameState");
|
||||||
var _asyncAlert = require("./asyncAlert");
|
var _asyncAlert = require("./asyncAlert");
|
||||||
var _options = require("./options");
|
var _options = require("./options");
|
||||||
var _getLevelBackground = require("./getLevelBackground");
|
var _getLevelBackground = require("./getLevelBackground");
|
||||||
var _premium = require("./premium");
|
|
||||||
function play() {
|
function play() {
|
||||||
if (gameState.running) return;
|
if (gameState.running) return;
|
||||||
gameState.running = true;
|
gameState.running = true;
|
||||||
|
@ -754,8 +753,8 @@ async function openShortRunUpgradesPicker(gameState) {
|
||||||
count: gameState.rerolls
|
count: gameState.rerolls
|
||||||
}),
|
}),
|
||||||
help: (0, _i18N.t)("level_up.reroll_help"),
|
help: (0, _i18N.t)("level_up.reroll_help"),
|
||||||
value: 'reroll',
|
value: "reroll",
|
||||||
icon: (0, _loadGameData.icons)['icon:reroll']
|
icon: (0, _loadGameData.icons)["icon:reroll"]
|
||||||
});
|
});
|
||||||
if (!actions.length) break;
|
if (!actions.length) break;
|
||||||
let textAfterButtons = `
|
let textAfterButtons = `
|
||||||
|
@ -790,7 +789,7 @@ async function openShortRunUpgradesPicker(gameState) {
|
||||||
allowClose: false,
|
allowClose: false,
|
||||||
textAfterButtons
|
textAfterButtons
|
||||||
});
|
});
|
||||||
if (upgradeId === 'reroll') {
|
if (upgradeId === "reroll") {
|
||||||
repeats++;
|
repeats++;
|
||||||
gameState.rerolls--;
|
gameState.rerolls--;
|
||||||
} else {
|
} else {
|
||||||
|
@ -962,14 +961,7 @@ async function openMainMenu() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(0, _premium.premiumMenuEntry)(gameState),
|
// premiumMenuEntry(gameState),
|
||||||
//
|
|
||||||
// {
|
|
||||||
// icon: icons["icon:continue"],
|
|
||||||
// text: t("main_menu.resume"),
|
|
||||||
// help: t("main_menu.resume_help"),
|
|
||||||
// value() {},
|
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
text: (0, _i18N.t)("main_menu.settings_title"),
|
text: (0, _i18N.t)("main_menu.settings_title"),
|
||||||
help: (0, _i18N.t)("main_menu.settings_help"),
|
help: (0, _i18N.t)("main_menu.settings_help"),
|
||||||
|
@ -1343,7 +1335,7 @@ restart(window.location.search.includes("stressTest") ? {
|
||||||
} : {});
|
} : {});
|
||||||
tick();
|
tick();
|
||||||
|
|
||||||
},{"./loadGameData":"l1B4x","./sounds":"dQKPV","./game_utils":"cEeac","./PWA/sw_loader":"2n0gK","./i18n/i18n":"eNPRm","./settings":"5blfu","./gameStateMutators":"9ZeQl","./render":"9AS2t","./recording":"godmD","./newGameState":"aQN6X","./asyncAlert":"rSqLY","./options":"d5NoS","./getLevelBackground":"7OIPf","@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3","./premium":"4GEPs"}],"l1B4x":[function(require,module,exports,__globalThis) {
|
},{"./loadGameData":"l1B4x","./sounds":"dQKPV","./game_utils":"cEeac","./PWA/sw_loader":"2n0gK","./i18n/i18n":"eNPRm","./settings":"5blfu","./gameStateMutators":"9ZeQl","./render":"9AS2t","./recording":"godmD","./newGameState":"aQN6X","./asyncAlert":"rSqLY","./options":"d5NoS","./getLevelBackground":"7OIPf","@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3"}],"l1B4x":[function(require,module,exports,__globalThis) {
|
||||||
var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js");
|
var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js");
|
||||||
parcelHelpers.defineInteropFlag(exports);
|
parcelHelpers.defineInteropFlag(exports);
|
||||||
parcelHelpers.export(exports, "appVersion", ()=>appVersion);
|
parcelHelpers.export(exports, "appVersion", ()=>appVersion);
|
||||||
|
@ -1384,7 +1376,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("\"29048147\"");
|
module.exports = JSON.parse("\"29049575\"");
|
||||||
|
|
||||||
},{}],"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");
|
||||||
|
@ -4385,8 +4377,8 @@ function newGameState(params) {
|
||||||
autoCleanUses: 0,
|
autoCleanUses: 0,
|
||||||
...(0, _gameUtils.defaultSounds)(),
|
...(0, _gameUtils.defaultSounds)(),
|
||||||
isAdventureMode: !!params?.adventure,
|
isAdventureMode: !!params?.adventure,
|
||||||
adventurePath: '',
|
adventurePath: "",
|
||||||
seed: 'Seed' + Math.random(),
|
seed: "Seed" + Math.random(),
|
||||||
rerolls: 0
|
rerolls: 0
|
||||||
};
|
};
|
||||||
(0, _gameStateMutators.resetBalls)(gameState);
|
(0, _gameStateMutators.resetBalls)(gameState);
|
||||||
|
@ -4400,130 +4392,7 @@ function newGameState(params) {
|
||||||
return gameState;
|
return gameState;
|
||||||
}
|
}
|
||||||
|
|
||||||
},{"./settings":"5blfu","./loadGameData":"l1B4x","./game_utils":"cEeac","./gameStateMutators":"9ZeQl","./options":"d5NoS","@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3"}],"4GEPs":[function(require,module,exports,__globalThis) {
|
},{"./settings":"5blfu","./loadGameData":"l1B4x","./game_utils":"cEeac","./gameStateMutators":"9ZeQl","./options":"d5NoS","@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3"}]},["gVqJ6","67XFf"], "67XFf", "parcelRequire94c2")
|
||||||
var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js");
|
|
||||||
parcelHelpers.defineInteropFlag(exports);
|
|
||||||
parcelHelpers.export(exports, "isPremium", ()=>isPremium);
|
|
||||||
parcelHelpers.export(exports, "premiumMenuEntry", ()=>premiumMenuEntry);
|
|
||||||
var _loadGameData = require("./loadGameData");
|
|
||||||
var _i18N = require("./i18n/i18n");
|
|
||||||
var _settings = require("./settings");
|
|
||||||
var _asyncAlert = require("./asyncAlert");
|
|
||||||
var _game = require("./game");
|
|
||||||
const publicKeyString = `-----BEGIN PUBLIC KEY-----
|
|
||||||
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAyGQJgs6gxa0Fd86TuZ2q
|
|
||||||
rGQ5ArSn8ug4VIKezru1QhIEkXeOT1lYXOLEryWaVUwXfOa9sVlKAGJY5y0TarAY
|
|
||||||
NF2m67ME8yzNPIoZWbKXutJ3CSCXNTjAqAxHgz7H+qxbNGZXAXw+ta8+PuZDzcCI
|
|
||||||
LbXT1u3/i0ahhA2Erdpv9XQBazKZt5AKzU31XhEEFh1jXZyk9D4XbatYXtvEwaJx
|
|
||||||
eSWmjSxJ6SJb6oH2mwm8V4E0PxYVIa0yX3cPgGuR0pZPMleOTc6o0T24I2AUQb0d
|
|
||||||
FckdFrr5U8bFIf/nwncMYVVNgt1vh88EuzWLjpc52nLrdOkVQNpiCN2uMgBBXQB7
|
|
||||||
iseIfdkGF0A4DBn8qdieDvaSY8zeRW/nAce4FNBidU1SebNRnIU9f/XpA493lJW+
|
|
||||||
Y/zXQBbmX/uSmeZDP4fjhKZv0Qa0ZeGzZiTdBKKb0BlIg/VYFFsqPytUVVyesO4J
|
|
||||||
RCASTIjXW61E7PQKir5qIXwkQDlzJ+bpZ3PHyAvspRrBaDxIYvEEw14evpuqOgS+
|
|
||||||
v/IlgPe+CWSvZa9xxnQl/aWZrOrD7syu6KKCbgUyXEm+Alp0YT3e6nwjn0qiM/cj
|
|
||||||
dZpWPx3O+rZbRQb0gHcvN4+n2Y7fWAeC9mxVZtADqvVr/GTumMbLj7DdhWtt1Ogu
|
|
||||||
4EcvkQ5SKCL0JC93DyctjOMCAwEAAQ==
|
|
||||||
-----END PUBLIC KEY-----`;
|
|
||||||
function pemToArrayBuffer(pem) {
|
|
||||||
const b64 = pem.replace(/-----BEGIN PUBLIC KEY-----/, '').replace(/-----END PUBLIC KEY-----/, '').replace(/\s+/g, '');
|
|
||||||
const binaryDerString = atob(b64);
|
|
||||||
const binaryDer = new Uint8Array(binaryDerString.length);
|
|
||||||
for(let i = 0; i < binaryDerString.length; i++)binaryDer[i] = binaryDerString.charCodeAt(i);
|
|
||||||
return binaryDer.buffer;
|
|
||||||
}
|
|
||||||
async function getPriceId(key, pem) {
|
|
||||||
// Split the key into its components
|
|
||||||
const [priceId, timestamp, signature] = key.split(':');
|
|
||||||
const data = `${priceId}:${timestamp}`;
|
|
||||||
const publicKeyBuffer = pemToArrayBuffer(pem);
|
|
||||||
const publicKey = await crypto.subtle.importKey('spki', publicKeyBuffer, {
|
|
||||||
name: 'RSA-PSS',
|
|
||||||
hash: 'SHA-256'
|
|
||||||
}, true, [
|
|
||||||
'verify'
|
|
||||||
]);
|
|
||||||
// Verify the signature using ECDSA
|
|
||||||
const isValid = await crypto.subtle.verify({
|
|
||||||
name: 'RSA-PSS',
|
|
||||||
saltLength: 32
|
|
||||||
}, publicKey, new Uint8Array(Array.from(atob(signature), (c)=>c.charCodeAt(0))), new TextEncoder().encode(data));
|
|
||||||
if (!isValid) throw new Error("Invalid key signature");
|
|
||||||
return priceId;
|
|
||||||
}
|
|
||||||
let premium = false;
|
|
||||||
const gamePriceId = 'price_1R6YaEGRf74lr2EkSo2GPvuO';
|
|
||||||
checkKey((0, _settings.getSettingValue)('license', '')).then();
|
|
||||||
async function checkKey(key) {
|
|
||||||
if (!key) return 'No key';
|
|
||||||
try {
|
|
||||||
if (gamePriceId !== await getPriceId(key, publicKeyString)) return 'Wrong product';
|
|
||||||
premium = true;
|
|
||||||
return '';
|
|
||||||
} catch (e) {
|
|
||||||
return 'Could not upgrade : ' + e.message;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function isPremium() {
|
|
||||||
return premium;
|
|
||||||
}
|
|
||||||
function premiumMenuEntry(gameState) {
|
|
||||||
if (isPremium()) return {
|
|
||||||
icon: (0, _loadGameData.icons)["icon:adventure_mode"],
|
|
||||||
text: (0, _i18N.t)("premium.adventure_mode"),
|
|
||||||
help: (0, _i18N.t)("premium.adventure_mode_help"),
|
|
||||||
value: async ()=>{
|
|
||||||
if (await (0, _game.confirmRestart)(gameState)) (0, _game.restart)({
|
|
||||||
adventure: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return {
|
|
||||||
icon: (0, _loadGameData.icons)["icon:premium"],
|
|
||||||
text: (0, _i18N.t)("premium.title"),
|
|
||||||
help: (0, _i18N.t)("premium.short_help"),
|
|
||||||
value: ()=>openPremiumMenu('')
|
|
||||||
};
|
|
||||||
}
|
|
||||||
async function openPremiumMenu(text) {
|
|
||||||
const isGooglePlayInstall = new URLSearchParams(location.search).get('source') === 'com.android.vending';
|
|
||||||
const cb = await (0, _asyncAlert.asyncAlert)({
|
|
||||||
title: (0, _i18N.t)("premium.title"),
|
|
||||||
text: text || isGooglePlayInstall && (0, _i18N.t)("premium.help_google") || (0, _i18N.t)("premium.help"),
|
|
||||||
actions: [
|
|
||||||
{
|
|
||||||
text: (0, _i18N.t)("premium.buy"),
|
|
||||||
disabled: isGooglePlayInstall,
|
|
||||||
help: isGooglePlayInstall ? (0, _i18N.t)("premium.buy_disabled_help") : (0, _i18N.t)("premium.buy_help"),
|
|
||||||
value () {
|
|
||||||
window.open('https://licenses.lecaro.me/buy/price_1R6YaEGRf74lr2EkSo2GPvuO', '_blank');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: (0, _i18N.t)("premium.enter"),
|
|
||||||
help: (0, _i18N.t)("premium.enter_help"),
|
|
||||||
async value () {
|
|
||||||
const value = (prompt('Please paste your license key') || '').replace(/\s+/g, '');
|
|
||||||
const problem = await checkKey(value);
|
|
||||||
if (problem) openPremiumMenu(problem).then();
|
|
||||||
else {
|
|
||||||
(0, _settings.setSettingValue)('license', value);
|
|
||||||
(0, _game.openMainMenu)().then();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: (0, _i18N.t)("premium.back"),
|
|
||||||
help: (0, _i18N.t)("premium.back_help"),
|
|
||||||
value () {
|
|
||||||
(0, _game.openMainMenu)().then();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
if (cb) cb();
|
|
||||||
}
|
|
||||||
|
|
||||||
},{"@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3","./loadGameData":"l1B4x","./i18n/i18n":"eNPRm","./settings":"5blfu","./asyncAlert":"rSqLY","./game":"edeGs"}]},["gVqJ6","67XFf"], "67XFf", "parcelRequire94c2")
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// The version of the cache.
|
// The version of the cache.
|
||||||
const VERSION = "29048147";
|
const VERSION = "29049575";
|
||||||
|
|
||||||
// The name of the cache
|
// The name of the cache
|
||||||
const CACHE_NAME = `breakout-71-${VERSION}`;
|
const CACHE_NAME = `breakout-71-${VERSION}`;
|
||||||
|
|
|
@ -1,30 +1,26 @@
|
||||||
import {GameState} from "./types";
|
import { GameState } from "./types";
|
||||||
|
|
||||||
export async function openAdventureRunUpgradesPicker(gameState: GameState) {
|
export async function openAdventureRunUpgradesPicker(gameState: GameState) {
|
||||||
let options=3
|
let options = 3;
|
||||||
const catchRate =
|
const catchRate =
|
||||||
(gameState.score - gameState.levelStartScore) /
|
(gameState.score - gameState.levelStartScore) /
|
||||||
(gameState.levelSpawnedCoins || 1);
|
(gameState.levelSpawnedCoins || 1);
|
||||||
|
|
||||||
|
|
||||||
if (gameState.levelWallBounces == 0) {
|
if (gameState.levelWallBounces == 0) {
|
||||||
options++;
|
options++;
|
||||||
}
|
}
|
||||||
if (gameState.levelTime < 30 * 1000) {
|
if (gameState.levelTime < 30 * 1000) {
|
||||||
options++
|
options++;
|
||||||
}
|
}
|
||||||
if (catchRate === 1) {
|
if (catchRate === 1) {
|
||||||
options++
|
options++;
|
||||||
}
|
}
|
||||||
if (gameState.levelMisses === 0) {
|
if (gameState.levelMisses === 0) {
|
||||||
options++
|
options++;
|
||||||
}
|
}
|
||||||
|
|
||||||
const choices = []
|
const choices = [];
|
||||||
for( let difficulty=0; difficulty<options;difficulty++){
|
for (let difficulty = 0; difficulty < options; difficulty++) {
|
||||||
choices.push({
|
choices.push({});
|
||||||
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1 +1 @@
|
||||||
"29048147"
|
"29049575"
|
||||||
|
|
105
src/game.ts
105
src/game.ts
|
@ -1,4 +1,4 @@
|
||||||
import {allLevels, appVersion, icons, upgrades} from "./loadGameData";
|
import { allLevels, appVersion, icons, upgrades } from "./loadGameData";
|
||||||
import {
|
import {
|
||||||
Ball,
|
Ball,
|
||||||
Coin,
|
Coin,
|
||||||
|
@ -11,11 +11,17 @@ import {
|
||||||
TextFlash,
|
TextFlash,
|
||||||
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";
|
||||||
import {
|
import {
|
||||||
cycleMaxCoins,
|
cycleMaxCoins,
|
||||||
cycleMaxParticles,
|
cycleMaxParticles,
|
||||||
|
@ -34,13 +40,29 @@ 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,
|
||||||
import {newGameState} from "./newGameState";
|
ctx,
|
||||||
import {alertsOpen, asyncAlert, AsyncAlertAction, closeModal,} from "./asyncAlert";
|
gameCanvas,
|
||||||
import {isOptionOn, options, toggleOption} from "./options";
|
render,
|
||||||
import {hashCode} from "./getLevelBackground";
|
scoreDisplay,
|
||||||
import {premiumMenuEntry} from "./premium";
|
} from "./render";
|
||||||
|
import {
|
||||||
|
pauseRecording,
|
||||||
|
recordOneFrame,
|
||||||
|
resumeRecording,
|
||||||
|
startRecordingGame,
|
||||||
|
} from "./recording";
|
||||||
|
import { newGameState } from "./newGameState";
|
||||||
|
import {
|
||||||
|
alertsOpen,
|
||||||
|
asyncAlert,
|
||||||
|
AsyncAlertAction,
|
||||||
|
closeModal,
|
||||||
|
} from "./asyncAlert";
|
||||||
|
import { isOptionOn, options, toggleOption } from "./options";
|
||||||
|
import { hashCode } from "./getLevelBackground";
|
||||||
|
import { premiumMenuEntry } from "./premium";
|
||||||
|
|
||||||
export function play() {
|
export function play() {
|
||||||
if (gameState.running) return;
|
if (gameState.running) return;
|
||||||
|
@ -174,52 +196,50 @@ export async function openShortRunUpgradesPicker(gameState: GameState) {
|
||||||
|
|
||||||
if (gameState.levelWallBounces == 0) {
|
if (gameState.levelWallBounces == 0) {
|
||||||
repeats++;
|
repeats++;
|
||||||
gameState.rerolls++
|
gameState.rerolls++;
|
||||||
wallHitsGain = t("level_up.plus_one_upgrade");
|
wallHitsGain = t("level_up.plus_one_upgrade");
|
||||||
} else if (gameState.levelWallBounces < 5) {
|
} else if (gameState.levelWallBounces < 5) {
|
||||||
gameState.rerolls++
|
gameState.rerolls++;
|
||||||
wallHitsGain = t("level_up.plus_one_choice");
|
wallHitsGain = t("level_up.plus_one_choice");
|
||||||
}
|
}
|
||||||
if (gameState.levelTime < 30 * 1000) {
|
if (gameState.levelTime < 30 * 1000) {
|
||||||
repeats++;
|
repeats++;
|
||||||
gameState.rerolls++
|
gameState.rerolls++;
|
||||||
timeGain = t("level_up.plus_one_upgrade");
|
timeGain = t("level_up.plus_one_upgrade");
|
||||||
} else if (gameState.levelTime < 60 * 1000) {
|
} else if (gameState.levelTime < 60 * 1000) {
|
||||||
gameState.rerolls++
|
gameState.rerolls++;
|
||||||
timeGain = t("level_up.plus_one_choice");
|
timeGain = t("level_up.plus_one_choice");
|
||||||
}
|
}
|
||||||
if (catchRate === 1) {
|
if (catchRate === 1) {
|
||||||
repeats++;
|
repeats++;
|
||||||
gameState.rerolls++
|
gameState.rerolls++;
|
||||||
catchGain = t("level_up.plus_one_upgrade");
|
catchGain = t("level_up.plus_one_upgrade");
|
||||||
} else if (catchRate > 0.9) {
|
} else if (catchRate > 0.9) {
|
||||||
gameState.rerolls++
|
gameState.rerolls++;
|
||||||
catchGain = t("level_up.plus_one_choice");
|
catchGain = t("level_up.plus_one_choice");
|
||||||
}
|
}
|
||||||
if (gameState.levelMisses === 0) {
|
if (gameState.levelMisses === 0) {
|
||||||
repeats++;
|
repeats++;
|
||||||
gameState.rerolls++
|
gameState.rerolls++;
|
||||||
missesGain = t("level_up.plus_one_upgrade");
|
missesGain = t("level_up.plus_one_upgrade");
|
||||||
} else if (gameState.levelMisses <= 3) {
|
} else if (gameState.levelMisses <= 3) {
|
||||||
gameState.rerolls++
|
gameState.rerolls++;
|
||||||
missesGain = t("level_up.plus_one_choice");
|
missesGain = t("level_up.plus_one_choice");
|
||||||
}
|
}
|
||||||
|
|
||||||
while (repeats--) {
|
while (repeats--) {
|
||||||
const actions = pickRandomUpgrades(
|
const actions = pickRandomUpgrades(
|
||||||
gameState,
|
gameState,
|
||||||
3 +
|
3 + gameState.perks.one_more_choice - gameState.perks.instant_upgrade,
|
||||||
gameState.perks.one_more_choice -
|
|
||||||
gameState.perks.instant_upgrade,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if(gameState.rerolls){
|
if (gameState.rerolls) {
|
||||||
actions.push({
|
actions.push({
|
||||||
text: t("level_up.reroll",{count:gameState.rerolls}),
|
text: t("level_up.reroll", { count: gameState.rerolls }),
|
||||||
help: t("level_up.reroll_help"),
|
help: t("level_up.reroll_help"),
|
||||||
value: 'reroll',
|
value: "reroll",
|
||||||
icon: icons['icon:reroll']
|
icon: icons["icon:reroll"],
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
if (!actions.length) break;
|
if (!actions.length) break;
|
||||||
let textAfterButtons = `
|
let textAfterButtons = `
|
||||||
|
@ -242,7 +262,7 @@ export async function openShortRunUpgradesPicker(gameState: GameState) {
|
||||||
t("level_up.compliment_good")) ||
|
t("level_up.compliment_good")) ||
|
||||||
t("level_up.compliment_advice");
|
t("level_up.compliment_advice");
|
||||||
|
|
||||||
const upgradeId = (await asyncAlert<PerkId|'reroll'>({
|
const upgradeId = (await asyncAlert<PerkId | "reroll">({
|
||||||
title:
|
title:
|
||||||
t("level_up.pick_upgrade_title") +
|
t("level_up.pick_upgrade_title") +
|
||||||
(repeats ? " (" + (repeats + 1) + ")" : ""),
|
(repeats ? " (" + (repeats + 1) + ")" : ""),
|
||||||
|
@ -267,10 +287,10 @@ export async function openShortRunUpgradesPicker(gameState: GameState) {
|
||||||
textAfterButtons,
|
textAfterButtons,
|
||||||
})) as PerkId;
|
})) as PerkId;
|
||||||
|
|
||||||
if(upgradeId==='reroll'){
|
if (upgradeId === "reroll") {
|
||||||
repeats++
|
repeats++;
|
||||||
gameState.rerolls--
|
gameState.rerolls--;
|
||||||
}else{
|
} else {
|
||||||
gameState.perks[upgradeId]++;
|
gameState.perks[upgradeId]++;
|
||||||
if (upgradeId === "instant_upgrade") {
|
if (upgradeId === "instant_upgrade") {
|
||||||
repeats += 2;
|
repeats += 2;
|
||||||
|
@ -280,7 +300,6 @@ export async function openShortRunUpgradesPicker(gameState: GameState) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
gameCanvas.addEventListener("mouseup", (e) => {
|
gameCanvas.addEventListener("mouseup", (e) => {
|
||||||
if (e.button !== 0) return;
|
if (e.button !== 0) return;
|
||||||
if (gameState.running) {
|
if (gameState.running) {
|
||||||
|
@ -450,7 +469,7 @@ export async function openMainMenu() {
|
||||||
|
|
||||||
const creativeModeThreshold = Math.max(...upgrades.map((u) => u.threshold));
|
const creativeModeThreshold = Math.max(...upgrades.map((u) => u.threshold));
|
||||||
const actions: AsyncAlertAction<() => void>[] = [
|
const actions: AsyncAlertAction<() => void>[] = [
|
||||||
{
|
{
|
||||||
icon: icons["icon:7_levels_run"],
|
icon: icons["icon:7_levels_run"],
|
||||||
text: t("main_menu.normal"),
|
text: t("main_menu.normal"),
|
||||||
help: t("main_menu.normal_help"),
|
help: t("main_menu.normal_help"),
|
||||||
|
@ -514,15 +533,7 @@ export async function openMainMenu() {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
premiumMenuEntry(gameState)
|
// premiumMenuEntry(gameState),
|
||||||
,
|
|
||||||
//
|
|
||||||
// {
|
|
||||||
// icon: icons["icon:continue"],
|
|
||||||
// text: t("main_menu.resume"),
|
|
||||||
// help: t("main_menu.resume_help"),
|
|
||||||
// value() {},
|
|
||||||
// },
|
|
||||||
{
|
{
|
||||||
text: t("main_menu.settings_title"),
|
text: t("main_menu.settings_title"),
|
||||||
help: t("main_menu.settings_help"),
|
help: t("main_menu.settings_help"),
|
||||||
|
@ -765,7 +776,11 @@ async function openSettingsMenu() {
|
||||||
],
|
],
|
||||||
allowClose: true,
|
allowClose: true,
|
||||||
});
|
});
|
||||||
if (pick && pick !== getCurrentLang() && (await confirmRestart(gameState))) {
|
if (
|
||||||
|
pick &&
|
||||||
|
pick !== getCurrentLang() &&
|
||||||
|
(await confirmRestart(gameState))
|
||||||
|
) {
|
||||||
setSettingValue("lang", pick);
|
setSettingValue("lang", pick);
|
||||||
window.location.reload();
|
window.location.reload();
|
||||||
}
|
}
|
||||||
|
@ -852,7 +867,7 @@ Click an item above to start a run with it.
|
||||||
</p>`,
|
</p>`,
|
||||||
actions,
|
actions,
|
||||||
allowClose: true,
|
allowClose: true,
|
||||||
actionsAsGrid:true
|
actionsAsGrid: true,
|
||||||
});
|
});
|
||||||
if (tryOn) {
|
if (tryOn) {
|
||||||
if (await confirmRestart(gameState)) {
|
if (await confirmRestart(gameState)) {
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -963,7 +963,7 @@
|
||||||
</translation>
|
</translation>
|
||||||
<translation>
|
<translation>
|
||||||
<language>fr-FR</language>
|
<language>fr-FR</language>
|
||||||
<approved>false</approved>
|
<approved>true</approved>
|
||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
@ -978,7 +978,7 @@
|
||||||
</translation>
|
</translation>
|
||||||
<translation>
|
<translation>
|
||||||
<language>fr-FR</language>
|
<language>fr-FR</language>
|
||||||
<approved>false</approved>
|
<approved>true</approved>
|
||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
@ -1453,7 +1453,7 @@
|
||||||
</translation>
|
</translation>
|
||||||
<translation>
|
<translation>
|
||||||
<language>fr-FR</language>
|
<language>fr-FR</language>
|
||||||
<approved>false</approved>
|
<approved>true</approved>
|
||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
@ -1468,7 +1468,7 @@
|
||||||
</translation>
|
</translation>
|
||||||
<translation>
|
<translation>
|
||||||
<language>fr-FR</language>
|
<language>fr-FR</language>
|
||||||
<approved>false</approved>
|
<approved>true</approved>
|
||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
@ -1483,7 +1483,7 @@
|
||||||
</translation>
|
</translation>
|
||||||
<translation>
|
<translation>
|
||||||
<language>fr-FR</language>
|
<language>fr-FR</language>
|
||||||
<approved>false</approved>
|
<approved>true</approved>
|
||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
@ -1498,7 +1498,7 @@
|
||||||
</translation>
|
</translation>
|
||||||
<translation>
|
<translation>
|
||||||
<language>fr-FR</language>
|
<language>fr-FR</language>
|
||||||
<approved>false</approved>
|
<approved>true</approved>
|
||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
@ -1513,7 +1513,7 @@
|
||||||
</translation>
|
</translation>
|
||||||
<translation>
|
<translation>
|
||||||
<language>fr-FR</language>
|
<language>fr-FR</language>
|
||||||
<approved>false</approved>
|
<approved>true</approved>
|
||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
@ -1528,7 +1528,7 @@
|
||||||
</translation>
|
</translation>
|
||||||
<translation>
|
<translation>
|
||||||
<language>fr-FR</language>
|
<language>fr-FR</language>
|
||||||
<approved>false</approved>
|
<approved>true</approved>
|
||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
@ -1543,7 +1543,7 @@
|
||||||
</translation>
|
</translation>
|
||||||
<translation>
|
<translation>
|
||||||
<language>fr-FR</language>
|
<language>fr-FR</language>
|
||||||
<approved>false</approved>
|
<approved>true</approved>
|
||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
@ -1558,7 +1558,7 @@
|
||||||
</translation>
|
</translation>
|
||||||
<translation>
|
<translation>
|
||||||
<language>fr-FR</language>
|
<language>fr-FR</language>
|
||||||
<approved>false</approved>
|
<approved>true</approved>
|
||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
@ -1573,7 +1573,7 @@
|
||||||
</translation>
|
</translation>
|
||||||
<translation>
|
<translation>
|
||||||
<language>fr-FR</language>
|
<language>fr-FR</language>
|
||||||
<approved>false</approved>
|
<approved>true</approved>
|
||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
@ -1588,7 +1588,7 @@
|
||||||
</translation>
|
</translation>
|
||||||
<translation>
|
<translation>
|
||||||
<language>fr-FR</language>
|
<language>fr-FR</language>
|
||||||
<approved>false</approved>
|
<approved>true</approved>
|
||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
@ -1603,7 +1603,7 @@
|
||||||
</translation>
|
</translation>
|
||||||
<translation>
|
<translation>
|
||||||
<language>fr-FR</language>
|
<language>fr-FR</language>
|
||||||
<approved>false</approved>
|
<approved>true</approved>
|
||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
@ -1618,7 +1618,7 @@
|
||||||
</translation>
|
</translation>
|
||||||
<translation>
|
<translation>
|
||||||
<language>fr-FR</language>
|
<language>fr-FR</language>
|
||||||
<approved>false</approved>
|
<approved>true</approved>
|
||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
@ -1633,7 +1633,7 @@
|
||||||
</translation>
|
</translation>
|
||||||
<translation>
|
<translation>
|
||||||
<language>fr-FR</language>
|
<language>fr-FR</language>
|
||||||
<approved>false</approved>
|
<approved>true</approved>
|
||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
|
|
@ -21,7 +21,7 @@ describe("json data checks", () => {
|
||||||
.split("")
|
.split("")
|
||||||
.filter((b) => b !== "_" && b !== "black")
|
.filter((b) => b !== "_" && b !== "black")
|
||||||
.filter((a, b, c) => c.indexOf(a) === b);
|
.filter((a, b, c) => c.indexOf(a) === b);
|
||||||
return uniqueBricks.length > 5;
|
return uniqueBricks.length > 5 && !l.name.startsWith("icon:");
|
||||||
})
|
})
|
||||||
.map((l) => l.name);
|
.map((l) => l.name);
|
||||||
expect(levelsWithManyBrickColors).toEqual([]);
|
expect(levelsWithManyBrickColors).toEqual([]);
|
||||||
|
|
|
@ -28,7 +28,7 @@ export function newGameState(params: RunParams): GameState {
|
||||||
|
|
||||||
const perks = { ...makeEmptyPerksMap(upgrades), ...(params?.perks || {}) };
|
const perks = { ...makeEmptyPerksMap(upgrades), ...(params?.perks || {}) };
|
||||||
|
|
||||||
const gameState: GameState= {
|
const gameState: GameState = {
|
||||||
runLevels,
|
runLevels,
|
||||||
currentLevel: 0,
|
currentLevel: 0,
|
||||||
upgradesOfferedFor: -1,
|
upgradesOfferedFor: -1,
|
||||||
|
@ -101,10 +101,10 @@ export function newGameState(params: RunParams): GameState {
|
||||||
autoCleanUses: 0,
|
autoCleanUses: 0,
|
||||||
...defaultSounds(),
|
...defaultSounds(),
|
||||||
|
|
||||||
isAdventureMode:!!params?.adventure,
|
isAdventureMode: !!params?.adventure,
|
||||||
adventurePath:'',
|
adventurePath: "",
|
||||||
seed:'Seed'+Math.random(),
|
seed: "Seed" + Math.random(),
|
||||||
rerolls:0
|
rerolls: 0,
|
||||||
};
|
};
|
||||||
resetBalls(gameState);
|
resetBalls(gameState);
|
||||||
|
|
||||||
|
|
246
src/premium.ts
246
src/premium.ts
|
@ -1,9 +1,9 @@
|
||||||
import {GameState} from "./types";
|
import { GameState } from "./types";
|
||||||
import {icons} from "./loadGameData";
|
import { icons } from "./loadGameData";
|
||||||
import {t} from "./i18n/i18n";
|
import { t } from "./i18n/i18n";
|
||||||
import {getSettingValue, setSettingValue} from "./settings";
|
import { getSettingValue, setSettingValue } from "./settings";
|
||||||
import {asyncAlert} from "./asyncAlert";
|
import { asyncAlert } from "./asyncAlert";
|
||||||
import {confirmRestart, openMainMenu, restart} from "./game";
|
import { confirmRestart, openMainMenu, restart } from "./game";
|
||||||
|
|
||||||
const publicKeyString = `-----BEGIN PUBLIC KEY-----
|
const publicKeyString = `-----BEGIN PUBLIC KEY-----
|
||||||
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAyGQJgs6gxa0Fd86TuZ2q
|
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAyGQJgs6gxa0Fd86TuZ2q
|
||||||
|
@ -18,144 +18,148 @@ RCASTIjXW61E7PQKir5qIXwkQDlzJ+bpZ3PHyAvspRrBaDxIYvEEw14evpuqOgS+
|
||||||
v/IlgPe+CWSvZa9xxnQl/aWZrOrD7syu6KKCbgUyXEm+Alp0YT3e6nwjn0qiM/cj
|
v/IlgPe+CWSvZa9xxnQl/aWZrOrD7syu6KKCbgUyXEm+Alp0YT3e6nwjn0qiM/cj
|
||||||
dZpWPx3O+rZbRQb0gHcvN4+n2Y7fWAeC9mxVZtADqvVr/GTumMbLj7DdhWtt1Ogu
|
dZpWPx3O+rZbRQb0gHcvN4+n2Y7fWAeC9mxVZtADqvVr/GTumMbLj7DdhWtt1Ogu
|
||||||
4EcvkQ5SKCL0JC93DyctjOMCAwEAAQ==
|
4EcvkQ5SKCL0JC93DyctjOMCAwEAAQ==
|
||||||
-----END PUBLIC KEY-----`
|
-----END PUBLIC KEY-----`;
|
||||||
|
|
||||||
|
|
||||||
function pemToArrayBuffer(pem: string) {
|
function pemToArrayBuffer(pem: string) {
|
||||||
const b64 = pem
|
const b64 = pem
|
||||||
.replace(/-----BEGIN PUBLIC KEY-----/, '')
|
.replace(/-----BEGIN PUBLIC KEY-----/, "")
|
||||||
.replace(/-----END PUBLIC KEY-----/, '')
|
.replace(/-----END PUBLIC KEY-----/, "")
|
||||||
.replace(/\s+/g, '');
|
.replace(/\s+/g, "");
|
||||||
const binaryDerString = atob(b64);
|
const binaryDerString = atob(b64);
|
||||||
const binaryDer = new Uint8Array(binaryDerString.length);
|
const binaryDer = new Uint8Array(binaryDerString.length);
|
||||||
for (let i = 0; i < binaryDerString.length; i++) {
|
for (let i = 0; i < binaryDerString.length; i++) {
|
||||||
binaryDer[i] = binaryDerString.charCodeAt(i);
|
binaryDer[i] = binaryDerString.charCodeAt(i);
|
||||||
}
|
}
|
||||||
return binaryDer.buffer;
|
return binaryDer.buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getPriceId(key: string, pem: string) {
|
async function getPriceId(key: string, pem: string) {
|
||||||
// Split the key into its components
|
// Split the key into its components
|
||||||
const [priceId, timestamp, signature] = key.split(':');
|
const [priceId, timestamp, signature] = key.split(":");
|
||||||
const data = `${priceId}:${timestamp}`;
|
const data = `${priceId}:${timestamp}`;
|
||||||
|
|
||||||
const publicKeyBuffer = pemToArrayBuffer(pem);
|
const publicKeyBuffer = pemToArrayBuffer(pem);
|
||||||
|
|
||||||
|
const publicKey = await crypto.subtle.importKey(
|
||||||
|
"spki",
|
||||||
|
publicKeyBuffer,
|
||||||
|
{
|
||||||
|
name: "RSA-PSS",
|
||||||
|
hash: "SHA-256",
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
["verify"],
|
||||||
|
);
|
||||||
|
|
||||||
const publicKey = await crypto.subtle.importKey(
|
// Verify the signature using ECDSA
|
||||||
'spki',
|
const isValid = await crypto.subtle.verify(
|
||||||
publicKeyBuffer,
|
{
|
||||||
{
|
name: "RSA-PSS",
|
||||||
name: 'RSA-PSS',
|
saltLength: 32,
|
||||||
hash: 'SHA-256',
|
},
|
||||||
},
|
publicKey,
|
||||||
true,
|
new Uint8Array(Array.from(atob(signature), (c) => c.charCodeAt(0))),
|
||||||
['verify']
|
new TextEncoder().encode(data),
|
||||||
);
|
);
|
||||||
|
if (!isValid) throw new Error("Invalid key signature");
|
||||||
|
|
||||||
|
return priceId;
|
||||||
// Verify the signature using ECDSA
|
|
||||||
const isValid = await crypto.subtle.verify(
|
|
||||||
{
|
|
||||||
name: 'RSA-PSS',
|
|
||||||
saltLength: 32,
|
|
||||||
},
|
|
||||||
publicKey,
|
|
||||||
new Uint8Array(Array.from(atob(signature), c => c.charCodeAt(0))),
|
|
||||||
new TextEncoder().encode(data)
|
|
||||||
);
|
|
||||||
if (!isValid) throw new Error("Invalid key signature")
|
|
||||||
|
|
||||||
return priceId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let premium = false
|
let premium = false;
|
||||||
const gamePriceId = 'price_1R6YaEGRf74lr2EkSo2GPvuO'
|
const gamePriceId = "price_1R6YaEGRf74lr2EkSo2GPvuO";
|
||||||
checkKey(getSettingValue('license', '')).then()
|
checkKey(getSettingValue("license", "")).then();
|
||||||
|
|
||||||
async function checkKey(key: string) {
|
async function checkKey(key: string) {
|
||||||
if (!key) return 'No key'
|
if (!key) return "No key";
|
||||||
try {
|
try {
|
||||||
|
if (gamePriceId !== (await getPriceId(key, publicKeyString))) {
|
||||||
if (gamePriceId !== await getPriceId(key, publicKeyString)) {
|
return "Wrong product";
|
||||||
return 'Wrong product'
|
|
||||||
}
|
|
||||||
premium = true
|
|
||||||
return ''
|
|
||||||
|
|
||||||
} catch (e) {
|
|
||||||
return 'Could not upgrade : ' + e.message
|
|
||||||
}
|
}
|
||||||
|
premium = true;
|
||||||
|
return "";
|
||||||
|
} catch (e) {
|
||||||
|
return "Could not upgrade : " + e.message;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isPremium() {
|
export function isPremium() {
|
||||||
return premium
|
return premium;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function premiumMenuEntry(gameState: GameState) {
|
export function premiumMenuEntry(gameState: GameState) {
|
||||||
if (isPremium()) {
|
if (isPremium()) {
|
||||||
return {
|
|
||||||
icon: icons["icon:adventure_mode"],
|
|
||||||
text: t("premium.adventure_mode"),
|
|
||||||
help: t("premium.adventure_mode_help"),
|
|
||||||
value: async () => {
|
|
||||||
if (await confirmRestart(gameState)) {
|
|
||||||
restart({
|
|
||||||
adventure: true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
icon: icons["icon:premium"],
|
icon: icons["icon:adventure_mode"],
|
||||||
text: t("premium.title"),
|
text: t("premium.adventure_mode"),
|
||||||
help: t("premium.short_help"),
|
help: t("premium.adventure_mode_help"),
|
||||||
value: () => openPremiumMenu(''),
|
value: async () => {
|
||||||
}
|
if (await confirmRestart(gameState)) {
|
||||||
|
restart({
|
||||||
|
adventure: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
icon: icons["icon:premium"],
|
||||||
|
text: t("premium.title"),
|
||||||
|
help: t("premium.short_help"),
|
||||||
|
value: () => openPremiumMenu(""),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function openPremiumMenu(text) {
|
async function openPremiumMenu(text) {
|
||||||
const isGooglePlayInstall = new URLSearchParams(location.search).get('source') === 'com.android.vending'
|
const isGooglePlayInstall =
|
||||||
|
new URLSearchParams(location.search).get("source") ===
|
||||||
const cb = await asyncAlert({
|
"com.android.vending";
|
||||||
title: t("premium.title"),
|
|
||||||
text: text || (isGooglePlayInstall && t("premium.help_google")) || t("premium.help"),
|
|
||||||
actions: [
|
|
||||||
{
|
|
||||||
text: t("premium.buy"),
|
|
||||||
disabled: isGooglePlayInstall,
|
|
||||||
help: isGooglePlayInstall ? t("premium.buy_disabled_help") : t("premium.buy_help"),
|
|
||||||
value() {
|
|
||||||
window.open('https://licenses.lecaro.me/buy/price_1R6YaEGRf74lr2EkSo2GPvuO', '_blank');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: t("premium.enter"),
|
|
||||||
help: t("premium.enter_help"),
|
|
||||||
async value() {
|
|
||||||
const value = (prompt('Please paste your license key') || '').replace(/\s+/g, '')
|
|
||||||
const problem = await checkKey(value)
|
|
||||||
if (problem) {
|
|
||||||
openPremiumMenu(problem).then()
|
|
||||||
} else {
|
|
||||||
setSettingValue('license', value)
|
|
||||||
openMainMenu().then()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: t("premium.back"),
|
|
||||||
help: t("premium.back_help"),
|
|
||||||
value() {
|
|
||||||
openMainMenu().then()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
]
|
|
||||||
})
|
|
||||||
if (cb) cb()
|
|
||||||
|
|
||||||
|
const cb = await asyncAlert({
|
||||||
|
title: t("premium.title"),
|
||||||
|
text:
|
||||||
|
text ||
|
||||||
|
(isGooglePlayInstall && t("premium.help_google")) ||
|
||||||
|
t("premium.help"),
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
text: t("premium.buy"),
|
||||||
|
disabled: isGooglePlayInstall,
|
||||||
|
help: isGooglePlayInstall
|
||||||
|
? t("premium.buy_disabled_help")
|
||||||
|
: t("premium.buy_help"),
|
||||||
|
value() {
|
||||||
|
window.open(
|
||||||
|
"https://licenses.lecaro.me/buy/price_1R6YaEGRf74lr2EkSo2GPvuO",
|
||||||
|
"_blank",
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t("premium.enter"),
|
||||||
|
help: t("premium.enter_help"),
|
||||||
|
async value() {
|
||||||
|
const value = (prompt("Please paste your license key") || "").replace(
|
||||||
|
/\s+/g,
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
const problem = await checkKey(value);
|
||||||
|
if (problem) {
|
||||||
|
openPremiumMenu(problem).then();
|
||||||
|
} else {
|
||||||
|
setSettingValue("license", value);
|
||||||
|
openMainMenu().then();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t("premium.back"),
|
||||||
|
help: t("premium.back_help"),
|
||||||
|
value() {
|
||||||
|
openMainMenu().then();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
if (cb) cb();
|
||||||
}
|
}
|
||||||
|
|
|
@ -538,7 +538,7 @@ export function renderAllBricks() {
|
||||||
let redBecauseOfReach =
|
let redBecauseOfReach =
|
||||||
gameState.perks.reach &&
|
gameState.perks.reach &&
|
||||||
countBricksAbove(gameState, index) &&
|
countBricksAbove(gameState, index) &&
|
||||||
!countBricksBelow(gameState, index) ;
|
!countBricksBelow(gameState, index);
|
||||||
|
|
||||||
let redBorder =
|
let redBorder =
|
||||||
(gameState.ballsColor !== color &&
|
(gameState.ballsColor !== color &&
|
||||||
|
|
10
src/types.d.ts
vendored
10
src/types.d.ts
vendored
|
@ -266,17 +266,17 @@ export type GameState = {
|
||||||
coinCatch: { vol: number; x: number };
|
coinCatch: { vol: number; x: number };
|
||||||
colorChange: { vol: number; x: number };
|
colorChange: { vol: number; x: number };
|
||||||
};
|
};
|
||||||
isAdventureMode:boolean;
|
isAdventureMode: boolean;
|
||||||
adventurePath:string;
|
adventurePath: string;
|
||||||
seed:string;
|
seed: string;
|
||||||
rerolls:number;
|
rerolls: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type RunParams = {
|
export type RunParams = {
|
||||||
level?: string;
|
level?: string;
|
||||||
levelToAvoid?: string;
|
levelToAvoid?: string;
|
||||||
perks?: Partial<PerksMap>;
|
perks?: Partial<PerksMap>;
|
||||||
adventure?:boolean;
|
adventure?: boolean;
|
||||||
};
|
};
|
||||||
export type OptionDef = {
|
export type OptionDef = {
|
||||||
default: boolean;
|
default: boolean;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue