This commit is contained in:
Renan LE CARO 2025-05-02 09:56:37 +02:00
parent b5fafa5f3c
commit 70182b0129
14 changed files with 344 additions and 475 deletions

View file

@ -16,20 +16,20 @@ Break colourful bricks, catch bouncing coins and select powerful upgrades !
- instead of bouncing the ball,loosing a life pauses the game (with coins still in the air) - instead of bouncing the ball,loosing a life pauses the game (with coins still in the air)
## Done ## Done
- rewoked perks choices to limit perk fatigue, avoid wall of texts, allow users to skip
- removed rerolls
- offer to pick 1 upgrade out of 3 choices
- playing well adds 1 upgrade and 1 choice
- playing even better adds 1 choice
- "more choices" perk adds 1 choice
- you can skip the upgrades and they'll be saved for later as extra lives
- you can pick an upgrade multiple time to level it up
- bigger "level X of Y cleared"
- continue to level X/Y as button
- lives = upgrade points
- clarify challenges
- missed challenges show as greyed out choices (with unlock condition).
- rewoked perks choices to limit perk fatigue, avoid wall of texts, allow users to skip
- removed rerolls
- offer to pick 1 upgrade out of 3 choices
- playing well adds 1 upgrade and 1 choice
- playing even better adds 1 choice
- "more choices" perk adds 1 choice
- you can skip the upgrades and they'll be saved for later as extra lives
- you can pick an upgrade multiple time to level it up
- bigger "level X of Y cleared"
- continue to level X/Y as button
- lives = upgrade points
- clarify challenges
- removed the "sides bounce" challenge
- upgrades list now uses numbers instead of bars, looks better with limitless - upgrades list now uses numbers instead of bars, looks better with limitless
- somehow score clicks didn't register while the game was playing, that's solved - somehow score clicks didn't register while the game was playing, that's solved
- Fix : click tooltip to open on mobile, click anywhere to close - Fix : click tooltip to open on mobile, click anywhere to close

188
dist/index.html vendored

File diff suppressed because one or more lines are too long

View file

@ -1,10 +1,9 @@
* { * {
font-family: font-family: Courier New,
Courier New, Courier,
Courier, Lucida Sans Typewriter,
Lucida Sans Typewriter, Lucida Typewriter,
Lucida Typewriter, monospace;
monospace;
box-sizing: border-box; box-sizing: border-box;
} }
@ -422,63 +421,6 @@ h2.histogram-title strong {
color: white; color: white;
} }
.level {
color: #000;
background: #fff;
border-radius: 3px;
overflow: hidden;
font-size: 12px;
font-style: normal;
line-height: 12px;
display: inline-block;
position: relative;
//top: -3px;
font-weight: bold;
border: 1px solid #fff;
//margin-left: 5px;
> span {
display: inline-block;
position: relative;
&:first-child {
padding: 3px 6px 0 2px;
color: #000;
background: #fff;
}
&:last-child {
padding: 3px 3px 0 2px;
color: #fff;
background: #000;
&:before {
content: "";
display: block;
background: black;
position: absolute;
left: -2px;
top: 0;
bottom: 0;
width: 4px;
transform: skewX(-10deg);
}
}
}
&.capped {
opacity: 0.5;
> span:first-child {
color: #fff;
background: #000;
}
> span:last-child::before {
width: 1px;
background: white;
}
}
}
&.used { &.used {
opacity: 1; opacity: 1;
@ -511,27 +453,84 @@ h2.histogram-title strong {
align-self: flex-start; align-self: flex-start;
padding: 5px; padding: 5px;
font-weight: bold; font-weight: bold;
transition: transition: transform 0.2s,
transform 0.2s, box-shadow 0.2s;
box-shadow 0.2s; box-shadow: 0 1px 0 black inset,
box-shadow: 0 2px #5da3ea,
0 1px 0 black inset, 0 4px white;
0 2px #5da3ea,
0 4px white;
&:hover { &:hover {
background: #5da3ea; background: #5da3ea;
} }
&:active { &:active {
transform: translate(0, 4px); transform: translate(0, 4px);
box-shadow: box-shadow: 0 1px 0 black inset,
0 1px 0 black inset, 0 0px #5da3ea,
0 0px #5da3ea, 0 0px white;
0 0px white;
} }
transition: transition: transform 0.2s,
transform 0.2s, box-shadow 0.2s;
box-shadow 0.2s; }
}
.level {
color: #000;
background: #fff;
border-radius: 3px;
overflow: hidden;
font-size: 12px;
font-style: normal;
line-height: 12px;
display: inline-block;
position: relative;
//top: -3px;
font-weight: bold;
border: 1px solid #fff;
//margin-left: 5px;
> span {
display: inline-block;
position: relative;
&:first-child {
padding: 3px 6px 0 2px;
color: #000;
background: #fff;
}
&:last-child {
padding: 3px 3px 0 2px;
color: #fff;
background: #000;
&:before {
content: "";
display: block;
background: black;
position: absolute;
left: -2px;
top: 0;
bottom: 0;
width: 4px;
transform: skewX(-10deg);
}
}
}
&.capped {
opacity: 0.5;
> span:first-child {
color: #fff;
background: #000;
}
> span:last-child::before {
width: 1px;
background: white;
}
} }
} }
@ -625,9 +624,8 @@ h2.histogram-title strong {
border-radius: 2px; border-radius: 2px;
padding-right: 10px; padding-right: 10px;
pointer-events: none; pointer-events: none;
transition: transition: opacity 200ms,
opacity 200ms, transform 200ms;
transform 200ms;
z-index: 7; z-index: 7;
&.hidden { &.hidden {

View file

@ -113,7 +113,7 @@ export function upgradeLevelAndMaxDisplay(
) { ) {
const lvl = gameState.perks[upgrade.id]; const lvl = gameState.perks[upgrade.id];
const max = upgrade.max + gameState.perks.limitless; const max = upgrade.max + gameState.perks.limitless;
return `<span class="level ${lvl < max ? "can-upgrade" : "capped"}"><span>${lvl}</span><span>${max}</span></span>`; return ` <span class="level ${lvl < max ? "can-upgrade" : "capped"}"><span>${lvl}</span><span>${max}</span></span>`;
} }
export function pickedUpgradesHTMl(gameState: GameState) { export function pickedUpgradesHTMl(gameState: GameState) {

View file

@ -76,8 +76,6 @@
"level_up.challenges.levelMisses.name": "", "level_up.challenges.levelMisses.name": "",
"level_up.challenges.levelTime.description": "", "level_up.challenges.levelTime.description": "",
"level_up.challenges.levelTime.name": "", "level_up.challenges.levelTime.name": "",
"level_up.challenges.levelWallBounces.description": "",
"level_up.challenges.levelWallBounces.name": "",
"level_up.go": "", "level_up.go": "",
"level_up.go_with_upgrades": "", "level_up.go_with_upgrades": "",
"level_up.gold": "", "level_up.gold": "",

View file

@ -2790,81 +2790,6 @@
</concept_node> </concept_node>
</children> </children>
</folder_node> </folder_node>
<folder_node>
<name>levelWallBounces</name>
<children>
<concept_node>
<name>description</name>
<description/>
<comment/>
<translations>
<translation>
<language>ar-LB</language>
<approved>false</approved>
</translation>
<translation>
<language>de-DE</language>
<approved>false</approved>
</translation>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-CL</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>false</approved>
</translation>
<translation>
<language>ru-RU</language>
<approved>false</approved>
</translation>
<translation>
<language>tr-TR</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>name</name>
<description/>
<comment/>
<translations>
<translation>
<language>ar-LB</language>
<approved>false</approved>
</translation>
<translation>
<language>de-DE</language>
<approved>false</approved>
</translation>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-CL</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>false</approved>
</translation>
<translation>
<language>ru-RU</language>
<approved>false</approved>
</translation>
<translation>
<language>tr-TR</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
</children>
</folder_node>
</children> </children>
</folder_node> </folder_node>
<concept_node> <concept_node>

View file

@ -76,8 +76,6 @@
"level_up.challenges.levelMisses.name": "", "level_up.challenges.levelMisses.name": "",
"level_up.challenges.levelTime.description": "", "level_up.challenges.levelTime.description": "",
"level_up.challenges.levelTime.name": "", "level_up.challenges.levelTime.name": "",
"level_up.challenges.levelWallBounces.description": "",
"level_up.challenges.levelWallBounces.name": "",
"level_up.go": "", "level_up.go": "",
"level_up.go_with_upgrades": "", "level_up.go_with_upgrades": "",
"level_up.gold": "", "level_up.gold": "",

View file

@ -68,26 +68,24 @@
"lab.reset": "Reset", "lab.reset": "Reset",
"lab.select_level": "Select a level to play on", "lab.select_level": "Select a level to play on",
"lab.unlocks_at": "Unlocks at total score {{score}}", "lab.unlocks_at": "Unlocks at total score {{score}}",
"level_up.add_perks": "Add perks to your build", "level_up.add_perks": "Add perks",
"level_up.challenges.catchRateGood.description": "You'll get a gold medal (+1 choices, +1 upgrade point) for catching {{gold}}% of coins. You'll get a silver medal (+1 upgrade point) if you catch {{silver}}%. This level you caught {{caught}} coins out of {{total}}", "level_up.challenges.catchRateGood.description": "Catch {{gold}}% of coins for gold, {{silver}}% for silver. Last level you caught {{caught}} coins out of {{total}}",
"level_up.challenges.catchRateGood.name": "{{value}}% coins caught ", "level_up.challenges.catchRateGood.name": "{{value}}% coins caught ",
"level_up.challenges.intro": "Play well to earn extra upgrades and choices", "level_up.challenges.intro": "Challenges",
"level_up.challenges.levelMisses.description": "You'll get a gold medal (+1 choices, +1 upgrade point) if you miss all bricks less than {{gold}} times. You'll get a silver medal (+1 upgrade point) under {{silver}} missed shots. ", "level_up.challenges.levelMisses.description": "You'll get a gold medal if you miss less than {{gold}} times, silver under {{silver}}. ",
"level_up.challenges.levelMisses.name": "{{value}} missed shots", "level_up.challenges.levelMisses.name": "{{value}} missed shots",
"level_up.challenges.levelTime.description": "You'll get a gold medal (+1 choices, +1 upgrade point) if you clear the level under {{gold}}s . You'll get a silver medal (+1 upgrade point) under {{silver}}s. ", "level_up.challenges.levelTime.description": "You'll get a gold medal under {{gold}}s and a silver medal under {{silver}}s. ",
"level_up.challenges.levelTime.name": "{{value}}s play time", "level_up.challenges.levelTime.name": "{{value}}s play time",
"level_up.challenges.levelWallBounces.description": "You'll get a gold medal (+1 choices, +1 upgrade point) if the ball bounces less than {{gold}} times on the wall and ceiling. You'll get a silver medal (+1 upgrade point) if if the ball bounces less than {{silver}} times. ",
"level_up.challenges.levelWallBounces.name": "{{value}} wall bounces",
"level_up.go": "Continue to level \"{{name}}\"", "level_up.go": "Continue to level \"{{name}}\"",
"level_up.go_with_upgrades": "Spend your {{count}} upgrade points first", "level_up.go_with_upgrades": "Spend your {{count}} upgrade points first",
"level_up.gold": "You gained one choice and upgrade point.", "level_up.gold": "You gained two choices and upgrade point.",
"level_up.maxed_upgrade": "\"{{name}}\" is at max level", "level_up.maxed_upgrade": "\"{{name}}\" is at max level",
"level_up.no": "You did not meet the reward condition.", "level_up.no": "You did not meet the reward condition.",
"level_up.pick": "Pick", "level_up.pick": "Pick",
"level_up.silver": "You gained an upgrade point.", "level_up.silver": "You gained one choice and upgrade point.",
"level_up.title": "Level {{level}}/{{max}} cleared", "level_up.title": "Level {{level}}/{{max}} cleared",
"level_up.upgrade": "Upgrade", "level_up.upgrade": "Upgrade",
"level_up.upgrade_perks": "Your current perks", "level_up.upgrade_perks": "Upgrade your perks",
"main_menu.basic": "", "main_menu.basic": "",
"main_menu.basic_help": "", "main_menu.basic_help": "",
"main_menu.colorful_coins": "", "main_menu.colorful_coins": "",

View file

@ -76,8 +76,6 @@
"level_up.challenges.levelMisses.name": "", "level_up.challenges.levelMisses.name": "",
"level_up.challenges.levelTime.description": "", "level_up.challenges.levelTime.description": "",
"level_up.challenges.levelTime.name": "", "level_up.challenges.levelTime.name": "",
"level_up.challenges.levelWallBounces.description": "",
"level_up.challenges.levelWallBounces.name": "",
"level_up.go": "", "level_up.go": "",
"level_up.go_with_upgrades": "", "level_up.go_with_upgrades": "",
"level_up.gold": "", "level_up.gold": "",

View file

@ -76,8 +76,6 @@
"level_up.challenges.levelMisses.name": "", "level_up.challenges.levelMisses.name": "",
"level_up.challenges.levelTime.description": "", "level_up.challenges.levelTime.description": "",
"level_up.challenges.levelTime.name": "", "level_up.challenges.levelTime.name": "",
"level_up.challenges.levelWallBounces.description": "",
"level_up.challenges.levelWallBounces.name": "",
"level_up.go": "", "level_up.go": "",
"level_up.go_with_upgrades": "", "level_up.go_with_upgrades": "",
"level_up.gold": "", "level_up.gold": "",

View file

@ -76,8 +76,6 @@
"level_up.challenges.levelMisses.name": "", "level_up.challenges.levelMisses.name": "",
"level_up.challenges.levelTime.description": "", "level_up.challenges.levelTime.description": "",
"level_up.challenges.levelTime.name": "", "level_up.challenges.levelTime.name": "",
"level_up.challenges.levelWallBounces.description": "",
"level_up.challenges.levelWallBounces.name": "",
"level_up.go": "", "level_up.go": "",
"level_up.go_with_upgrades": "", "level_up.go_with_upgrades": "",
"level_up.gold": "", "level_up.gold": "",

View file

@ -76,8 +76,6 @@
"level_up.challenges.levelMisses.name": "", "level_up.challenges.levelMisses.name": "",
"level_up.challenges.levelTime.description": "", "level_up.challenges.levelTime.description": "",
"level_up.challenges.levelTime.name": "", "level_up.challenges.levelTime.name": "",
"level_up.challenges.levelWallBounces.description": "",
"level_up.challenges.levelWallBounces.name": "",
"level_up.go": "", "level_up.go": "",
"level_up.go_with_upgrades": "", "level_up.go_with_upgrades": "",
"level_up.gold": "", "level_up.gold": "",

View file

@ -1,215 +1,184 @@
import { GameState, PerkId } from "./types"; import {GameState, PerkId} from "./types";
import {catchRateBest, catchRateGood, levelTimeBest, levelTimeGood, missesBest, missesGood,} from "./pure_functions";
import {t} from "./i18n/i18n";
import {icons, upgrades} from "./loadGameData";
import {requiredAsyncAlert} from "./asyncAlert";
import { import {
catchRateBest, escapeAttribute,
catchRateGood, getPossibleUpgrades,
levelTimeBest, levelsListHTMl,
levelTimeGood, max_levels,
missesBest, upgradeLevelAndMaxDisplay,
missesGood,
wallBouncedBest,
wallBouncedGood,
} from "./pure_functions";
import { t } from "./i18n/i18n";
import { icons, upgrades } from "./loadGameData";
import { asyncAlert, requiredAsyncAlert } from "./asyncAlert";
import {
escapeAttribute,
getPossibleUpgrades,
levelsListHTMl,
max_levels,
upgradeLevelAndMaxDisplay,
} from "./game_utils"; } from "./game_utils";
import { getNearestUnlockHTML } from "./openScorePanel"; import {getNearestUnlockHTML} from "./openScorePanel";
export async function openUpgradesPicker(gameState: GameState) { export async function openUpgradesPicker(gameState: GameState) {
const catchRate = const catchRate =
gameState.levelCoughtCoins / (gameState.levelSpawnedCoins || 1); gameState.levelCoughtCoins / (gameState.levelSpawnedCoins || 1);
let choices = 3; let choices = 3;
let upgradesWon = 1; let upgradesWon = 1;
let medals = []; let medals = [];
function challengeResult(
name: String, function challengeResult(
description: String, name: String,
medal: "gold" | "silver" | "no", description: String,
) { medal: "gold" | "silver" | "no",
if (medal === "gold") { ) {
choices++; if (medal === "gold") {
upgradesWon++; choices++;
} choices++;
if (medal === "silver") { upgradesWon++;
upgradesWon++; }
} if (medal === "silver") {
medals.push(`<div class="upgrade" data-tooltip="${escapeAttribute(description)}"> choices++;
upgradesWon++;
}
medals.push(`<div class="upgrade" data-tooltip="${escapeAttribute(description)}">
${icons["icon:" + medal + "_medal"]} ${icons["icon:" + medal + "_medal"]}
<p> <p>
<strong>${name}</strong><br/> <strong>${name}</strong><br/>
${{ gold: t("level_up.gold"), silver: t("level_up.silver"), no: t("level_up.no") }[medal]} ${{gold: t("level_up.gold"), silver: t("level_up.silver"), no: t("level_up.no")}[medal]}
</p> </p>
</div>`); </div>`);
} }
challengeResult(
t("level_up.challenges.levelWallBounces.name", {
value: gameState.levelWallBounces,
}),
t("level_up.challenges.levelWallBounces.description", {
silver: wallBouncedGood,
gold: wallBouncedBest,
}),
(gameState.levelWallBounces < wallBouncedBest && "gold") ||
(gameState.levelWallBounces < wallBouncedGood && "silver") ||
"no",
);
challengeResult( challengeResult(
t("level_up.challenges.levelTime.name", { t("level_up.challenges.levelTime.name", {
value: Math.ceil(gameState.levelTime / 1000), value: Math.ceil(gameState.levelTime / 1000),
}), }),
t("level_up.challenges.levelTime.description", { t("level_up.challenges.levelTime.description", {
silver: levelTimeGood, silver: levelTimeGood,
gold: levelTimeBest, gold: levelTimeBest,
}), }),
(gameState.levelTime < levelTimeBest * 1000 && "gold") || (gameState.levelTime < levelTimeBest * 1000 && "gold") ||
(gameState.levelTime < levelTimeGood * 1000 && "silver") || (gameState.levelTime < levelTimeGood * 1000 && "silver") ||
"no", "no",
);
challengeResult(
t("level_up.challenges.catchRateGood.name", {
value: Math.floor(catchRate * 100),
}),
t("level_up.challenges.catchRateGood.description", {
silver: catchRateGood,
gold: catchRateBest,
caught: gameState.levelCoughtCoins,
total: gameState.levelSpawnedCoins,
}),
(catchRate > catchRateBest / 100 && "gold") ||
(catchRate > catchRateGood / 100 && "silver") ||
"no",
);
challengeResult(
t("level_up.challenges.levelMisses.name", { value: gameState.levelMisses }),
t("level_up.challenges.levelMisses.description", {
silver: missesGood,
gold: missesBest,
}),
(gameState.levelMisses < missesBest && "gold") ||
(gameState.levelMisses < missesGood && "silver") ||
"no",
);
gameState.upgrade_points += upgradesWon;
let offered: PerkId[] = getPossibleUpgrades(gameState)
.map((u) => ({
...u,
score: Math.random() + (gameState.lastOffered[u.id] || 0),
}))
.sort((a, b) => a.score - b.score)
.filter((u) => gameState.perks[u.id] < u.max + gameState.perks.limitless)
.map((u) => u.id);
const fromStart = upgrades
.map((u) => u.id)
.filter((id) => gameState.perks[id]);
while (true) {
const updatedChoices = gameState.perks.one_more_choice + choices;
let list = upgrades.filter(
(u) =>
offered.slice(0, updatedChoices).includes(u.id) ||
gameState.perks[u.id],
); );
list = list challengeResult(
.filter((u) => fromStart.includes(u.id)) t("level_up.challenges.catchRateGood.name", {
.concat(list.filter((u) => !fromStart.includes(u.id))); value: Math.floor(catchRate * 100),
}),
t("level_up.challenges.catchRateGood.description", {
silver: catchRateGood,
gold: catchRateBest,
caught: gameState.levelCoughtCoins,
total: gameState.levelSpawnedCoins,
}),
(catchRate > catchRateBest / 100 && "gold") ||
(catchRate > catchRateGood / 100 && "silver") ||
"no",
);
list.forEach((u) => { challengeResult(
dontOfferTooSoon(gameState, u.id); t("level_up.challenges.levelMisses.name", {value: gameState.levelMisses}),
}); t("level_up.challenges.levelMisses.description", {
silver: missesGood,
gold: missesBest,
}),
(gameState.levelMisses < missesBest && "gold") ||
(gameState.levelMisses < missesGood && "silver") ||
"no",
);
const actions = list.map((u) => { gameState.upgrade_points += upgradesWon;
const max = u.max + gameState.perks.limitless;
const lvl = gameState.perks[u.id];
const button = let offered: PerkId[] = getPossibleUpgrades(gameState)
!gameState.upgrade_points || gameState.perks[u.id] >= max .map((u) => ({
? "" ...u,
: ` <button data-resolve-to="${u.id}">${ score: Math.random() + (gameState.lastOffered[u.id] || 0),
lvl ? t("level_up.upgrade") : t("level_up.pick") }))
}</button>`; .sort((a, b) => a.score - b.score)
.filter((u) => gameState.perks[u.id] < u.max + gameState.perks.limitless)
.map((u) => u.id);
const lvlInfo = lvl ? upgradeLevelAndMaxDisplay(u, gameState) : ""; const fromStart = upgrades
const help = u.help(Math.max(1, lvl)); .map((u) => u.id)
.filter((id) => gameState.perks[id]);
return { while (true) {
u, const updatedChoices = gameState.perks.one_more_choice + choices;
button, let list = upgrades.filter(
html: `<div class="upgrade choice ${ (u) =>
(!lvl && gameState.upgrade_points && " ") || offered.slice(0, updatedChoices).includes(u.id) ||
(lvl && "used") || gameState.perks[u.id],
"greyed-out" );
}" >
${icons["icon:" + u.id]}
<p data-tooltip="${escapeAttribute(lvl ? help : u.fullHelp(Math.max(1, lvl)))}">
<strong>${u.name}</strong> ${lvlInfo}
${lvl ? "" : help}
</p>
${button}
</div>`,
};
});
const forcePick = list = list
gameState.upgrade_points > 0 && !!actions.find((a) => a.button !== ""); .filter((u) => fromStart.includes(u.id))
.concat(list.filter((u) => !fromStart.includes(u.id)));
const upgradeId = await requiredAsyncAlert<PerkId | null>({ list.forEach((u) => {
title: t("level_up.title", { dontOfferTooSoon(gameState, u.id);
level: gameState.currentLevel, });
max: max_levels(gameState),
}),
content: [
{
disabled: forcePick,
text: t("level_up.go", { name: gameState.level.name }),
help: forcePick
? t("level_up.go_with_upgrades", {
count: gameState.upgrade_points,
})
: "",
icon: icons[gameState.level.name],
value: null,
},
t("level_up.upgrade_perks"), const upgradesActions = list.filter(u => gameState.perks[u.id])
.map(u => ({
value: u.id,
text: u.name + upgradeLevelAndMaxDisplay(u, gameState),
icon: icons["icon:" + u.id],
disabled: !gameState.upgrade_points || gameState.perks[u.id] >= u.max + gameState.perks.limitless,
tooltip: u.help(gameState.perks[u.id]) + u.fullHelp(gameState.perks[u.id])
}))
...actions.filter((a) => gameState.perks[a.u.id]).map((a) => a.html), const addPerkActions = list.filter(u => !gameState.perks[u.id])
.map(u => ({
value: u.id,
text: u.name,
icon: icons["icon:" + u.id],
disabled: !gameState.upgrade_points,
help: u.help(1),
tooltip: u.fullHelp(1)
t("level_up.add_perks"), }))
...actions.filter((a) => !gameState.perks[a.u.id]).map((a) => a.html),
t("level_up.challenges.intro"),
...medals,
getNearestUnlockHTML(gameState),
levelsListHTMl(gameState, gameState.currentLevel),
`<div id="level-recording-container"></div>`,
],
});
if (upgradeId) { const forcePick = !![...upgradesActions, ...addPerkActions].find(a => !a.disabled)
gameState.perks[upgradeId]++;
gameState.runStatistics.upgrades_picked++; const upgradeId = await requiredAsyncAlert<PerkId | null>({
gameState.upgrade_points--; title: t("level_up.title", {
} else { level: gameState.currentLevel,
return; max: max_levels(gameState),
}),
content: [
{
disabled: forcePick,
text: t("level_up.go", {name: gameState.level.name}),
help: forcePick
? t("level_up.go_with_upgrades", {
count: gameState.upgrade_points,
})
: "",
icon: icons[gameState.level.name],
value: null,
},
t("level_up.upgrade_perks"),
...upgradesActions,
t("level_up.add_perks"),
...addPerkActions,
t("level_up.challenges.intro"),
...medals,
getNearestUnlockHTML(gameState),
levelsListHTMl(gameState, gameState.currentLevel),
`<div id="level-recording-container"></div>`,
],
});
if (upgradeId) {
gameState.perks[upgradeId]++;
gameState.runStatistics.upgrades_picked++;
gameState.upgrade_points--;
} else {
return;
}
} }
}
} }
export function dontOfferTooSoon(gameState: GameState, id: PerkId) { export function dontOfferTooSoon(gameState: GameState, id: PerkId) {
gameState.lastOffered[id] = Math.round(Date.now() / 1000); gameState.lastOffered[id] = Math.round(Date.now() / 1000);
} }

View file

@ -98,9 +98,6 @@ export function render(gameState: GameState) {
<span class="${(gameState.levelTime < levelTimeBest * 1000 && "great") || (gameState.levelTime < levelTimeGood * 1000 && "good") || ""}" data-tooltip="${t("play.stats.levelTime")}"> <span class="${(gameState.levelTime < levelTimeBest * 1000 && "great") || (gameState.levelTime < levelTimeGood * 1000 && "good") || ""}" data-tooltip="${t("play.stats.levelTime")}">
${Math.ceil(gameState.levelTime / 1000)}s ${Math.ceil(gameState.levelTime / 1000)}s
</span><span> / </span> </span><span> / </span>
<span class="${(gameState.levelWallBounces < wallBouncedBest && "great") || (gameState.levelWallBounces < wallBouncedGood && "good") || ""}" data-tooltip="${t("play.stats.levelWallBounces")}">
${gameState.levelWallBounces} B
</span><span> / </span>
<span class="${(gameState.levelMisses < missesBest && "great") || (gameState.levelMisses < missesGood && "good") || ""}" data-tooltip="${t("play.stats.levelMisses")}"> <span class="${(gameState.levelMisses < missesBest && "great") || (gameState.levelMisses < missesGood && "good") || ""}" data-tooltip="${t("play.stats.levelMisses")}">
${gameState.levelMisses} M ${gameState.levelMisses} M
</span><span> / </span> </span><span> / </span>