mirror of
https://gitlab.com/lecarore/breakout71.git
synced 2025-05-04 10:49:13 -04:00
wip
This commit is contained in:
parent
b5fafa5f3c
commit
70182b0129
14 changed files with 344 additions and 475 deletions
26
Readme.md
26
Readme.md
|
@ -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)
|
||||
|
||||
## 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).
|
||||
|
||||
- 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
|
||||
- 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
|
||||
|
|
188
dist/index.html
vendored
188
dist/index.html
vendored
File diff suppressed because one or more lines are too long
158
src/game.less
158
src/game.less
|
@ -1,10 +1,9 @@
|
|||
* {
|
||||
font-family:
|
||||
Courier New,
|
||||
Courier,
|
||||
Lucida Sans Typewriter,
|
||||
Lucida Typewriter,
|
||||
monospace;
|
||||
font-family: Courier New,
|
||||
Courier,
|
||||
Lucida Sans Typewriter,
|
||||
Lucida Typewriter,
|
||||
monospace;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
|
@ -422,63 +421,6 @@ h2.histogram-title strong {
|
|||
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 {
|
||||
opacity: 1;
|
||||
|
@ -511,27 +453,84 @@ h2.histogram-title strong {
|
|||
align-self: flex-start;
|
||||
padding: 5px;
|
||||
font-weight: bold;
|
||||
transition:
|
||||
transform 0.2s,
|
||||
box-shadow 0.2s;
|
||||
box-shadow:
|
||||
0 1px 0 black inset,
|
||||
0 2px #5da3ea,
|
||||
0 4px white;
|
||||
transition: transform 0.2s,
|
||||
box-shadow 0.2s;
|
||||
box-shadow: 0 1px 0 black inset,
|
||||
0 2px #5da3ea,
|
||||
0 4px white;
|
||||
|
||||
&:hover {
|
||||
background: #5da3ea;
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: translate(0, 4px);
|
||||
box-shadow:
|
||||
0 1px 0 black inset,
|
||||
0 0px #5da3ea,
|
||||
0 0px white;
|
||||
box-shadow: 0 1px 0 black inset,
|
||||
0 0px #5da3ea,
|
||||
0 0px white;
|
||||
}
|
||||
|
||||
transition:
|
||||
transform 0.2s,
|
||||
box-shadow 0.2s;
|
||||
transition: transform 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;
|
||||
padding-right: 10px;
|
||||
pointer-events: none;
|
||||
transition:
|
||||
opacity 200ms,
|
||||
transform 200ms;
|
||||
transition: opacity 200ms,
|
||||
transform 200ms;
|
||||
z-index: 7;
|
||||
|
||||
&.hidden {
|
||||
|
|
|
@ -113,7 +113,7 @@ export function upgradeLevelAndMaxDisplay(
|
|||
) {
|
||||
const lvl = gameState.perks[upgrade.id];
|
||||
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) {
|
||||
|
|
|
@ -76,8 +76,6 @@
|
|||
"level_up.challenges.levelMisses.name": "",
|
||||
"level_up.challenges.levelTime.description": "",
|
||||
"level_up.challenges.levelTime.name": "",
|
||||
"level_up.challenges.levelWallBounces.description": "",
|
||||
"level_up.challenges.levelWallBounces.name": "",
|
||||
"level_up.go": "",
|
||||
"level_up.go_with_upgrades": "",
|
||||
"level_up.gold": "",
|
||||
|
|
|
@ -2790,81 +2790,6 @@
|
|||
</concept_node>
|
||||
</children>
|
||||
</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>
|
||||
</folder_node>
|
||||
<concept_node>
|
||||
|
|
|
@ -76,8 +76,6 @@
|
|||
"level_up.challenges.levelMisses.name": "",
|
||||
"level_up.challenges.levelTime.description": "",
|
||||
"level_up.challenges.levelTime.name": "",
|
||||
"level_up.challenges.levelWallBounces.description": "",
|
||||
"level_up.challenges.levelWallBounces.name": "",
|
||||
"level_up.go": "",
|
||||
"level_up.go_with_upgrades": "",
|
||||
"level_up.gold": "",
|
||||
|
|
|
@ -68,26 +68,24 @@
|
|||
"lab.reset": "Reset",
|
||||
"lab.select_level": "Select a level to play on",
|
||||
"lab.unlocks_at": "Unlocks at total score {{score}}",
|
||||
"level_up.add_perks": "Add perks to your build",
|
||||
"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.add_perks": "Add perks",
|
||||
"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.intro": "Play well to earn extra upgrades and choices",
|
||||
"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.intro": "Challenges",
|
||||
"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.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.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_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.no": "You did not meet the reward condition.",
|
||||
"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.upgrade": "Upgrade",
|
||||
"level_up.upgrade_perks": "Your current perks",
|
||||
"level_up.upgrade_perks": "Upgrade your perks",
|
||||
"main_menu.basic": "",
|
||||
"main_menu.basic_help": "",
|
||||
"main_menu.colorful_coins": "",
|
||||
|
|
|
@ -76,8 +76,6 @@
|
|||
"level_up.challenges.levelMisses.name": "",
|
||||
"level_up.challenges.levelTime.description": "",
|
||||
"level_up.challenges.levelTime.name": "",
|
||||
"level_up.challenges.levelWallBounces.description": "",
|
||||
"level_up.challenges.levelWallBounces.name": "",
|
||||
"level_up.go": "",
|
||||
"level_up.go_with_upgrades": "",
|
||||
"level_up.gold": "",
|
||||
|
|
|
@ -76,8 +76,6 @@
|
|||
"level_up.challenges.levelMisses.name": "",
|
||||
"level_up.challenges.levelTime.description": "",
|
||||
"level_up.challenges.levelTime.name": "",
|
||||
"level_up.challenges.levelWallBounces.description": "",
|
||||
"level_up.challenges.levelWallBounces.name": "",
|
||||
"level_up.go": "",
|
||||
"level_up.go_with_upgrades": "",
|
||||
"level_up.gold": "",
|
||||
|
|
|
@ -76,8 +76,6 @@
|
|||
"level_up.challenges.levelMisses.name": "",
|
||||
"level_up.challenges.levelTime.description": "",
|
||||
"level_up.challenges.levelTime.name": "",
|
||||
"level_up.challenges.levelWallBounces.description": "",
|
||||
"level_up.challenges.levelWallBounces.name": "",
|
||||
"level_up.go": "",
|
||||
"level_up.go_with_upgrades": "",
|
||||
"level_up.gold": "",
|
||||
|
|
|
@ -76,8 +76,6 @@
|
|||
"level_up.challenges.levelMisses.name": "",
|
||||
"level_up.challenges.levelTime.description": "",
|
||||
"level_up.challenges.levelTime.name": "",
|
||||
"level_up.challenges.levelWallBounces.description": "",
|
||||
"level_up.challenges.levelWallBounces.name": "",
|
||||
"level_up.go": "",
|
||||
"level_up.go_with_upgrades": "",
|
||||
"level_up.gold": "",
|
||||
|
|
|
@ -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 {
|
||||
catchRateBest,
|
||||
catchRateGood,
|
||||
levelTimeBest,
|
||||
levelTimeGood,
|
||||
missesBest,
|
||||
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,
|
||||
escapeAttribute,
|
||||
getPossibleUpgrades,
|
||||
levelsListHTMl,
|
||||
max_levels,
|
||||
upgradeLevelAndMaxDisplay,
|
||||
} from "./game_utils";
|
||||
import { getNearestUnlockHTML } from "./openScorePanel";
|
||||
import {getNearestUnlockHTML} from "./openScorePanel";
|
||||
|
||||
export async function openUpgradesPicker(gameState: GameState) {
|
||||
const catchRate =
|
||||
gameState.levelCoughtCoins / (gameState.levelSpawnedCoins || 1);
|
||||
const catchRate =
|
||||
gameState.levelCoughtCoins / (gameState.levelSpawnedCoins || 1);
|
||||
|
||||
let choices = 3;
|
||||
let upgradesWon = 1;
|
||||
let medals = [];
|
||||
function challengeResult(
|
||||
name: String,
|
||||
description: String,
|
||||
medal: "gold" | "silver" | "no",
|
||||
) {
|
||||
if (medal === "gold") {
|
||||
choices++;
|
||||
upgradesWon++;
|
||||
}
|
||||
if (medal === "silver") {
|
||||
upgradesWon++;
|
||||
}
|
||||
medals.push(`<div class="upgrade" data-tooltip="${escapeAttribute(description)}">
|
||||
let choices = 3;
|
||||
let upgradesWon = 1;
|
||||
let medals = [];
|
||||
|
||||
function challengeResult(
|
||||
name: String,
|
||||
description: String,
|
||||
medal: "gold" | "silver" | "no",
|
||||
) {
|
||||
if (medal === "gold") {
|
||||
choices++;
|
||||
choices++;
|
||||
upgradesWon++;
|
||||
}
|
||||
if (medal === "silver") {
|
||||
choices++;
|
||||
upgradesWon++;
|
||||
}
|
||||
medals.push(`<div class="upgrade" data-tooltip="${escapeAttribute(description)}">
|
||||
${icons["icon:" + medal + "_medal"]}
|
||||
<p>
|
||||
<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>
|
||||
</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(
|
||||
t("level_up.challenges.levelTime.name", {
|
||||
value: Math.ceil(gameState.levelTime / 1000),
|
||||
}),
|
||||
t("level_up.challenges.levelTime.description", {
|
||||
silver: levelTimeGood,
|
||||
gold: levelTimeBest,
|
||||
}),
|
||||
(gameState.levelTime < levelTimeBest * 1000 && "gold") ||
|
||||
(gameState.levelTime < levelTimeGood * 1000 && "silver") ||
|
||||
"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],
|
||||
challengeResult(
|
||||
t("level_up.challenges.levelTime.name", {
|
||||
value: Math.ceil(gameState.levelTime / 1000),
|
||||
}),
|
||||
t("level_up.challenges.levelTime.description", {
|
||||
silver: levelTimeGood,
|
||||
gold: levelTimeBest,
|
||||
}),
|
||||
(gameState.levelTime < levelTimeBest * 1000 && "gold") ||
|
||||
(gameState.levelTime < levelTimeGood * 1000 && "silver") ||
|
||||
"no",
|
||||
);
|
||||
|
||||
list = list
|
||||
.filter((u) => fromStart.includes(u.id))
|
||||
.concat(list.filter((u) => !fromStart.includes(u.id)));
|
||||
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",
|
||||
);
|
||||
|
||||
list.forEach((u) => {
|
||||
dontOfferTooSoon(gameState, u.id);
|
||||
});
|
||||
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",
|
||||
);
|
||||
|
||||
const actions = list.map((u) => {
|
||||
const max = u.max + gameState.perks.limitless;
|
||||
const lvl = gameState.perks[u.id];
|
||||
gameState.upgrade_points += upgradesWon;
|
||||
|
||||
const button =
|
||||
!gameState.upgrade_points || gameState.perks[u.id] >= max
|
||||
? ""
|
||||
: ` <button data-resolve-to="${u.id}">${
|
||||
lvl ? t("level_up.upgrade") : t("level_up.pick")
|
||||
}</button>`;
|
||||
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 lvlInfo = lvl ? upgradeLevelAndMaxDisplay(u, gameState) : "";
|
||||
const help = u.help(Math.max(1, lvl));
|
||||
const fromStart = upgrades
|
||||
.map((u) => u.id)
|
||||
.filter((id) => gameState.perks[id]);
|
||||
|
||||
return {
|
||||
u,
|
||||
button,
|
||||
html: `<div class="upgrade choice ${
|
||||
(!lvl && gameState.upgrade_points && " ") ||
|
||||
(lvl && "used") ||
|
||||
"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>`,
|
||||
};
|
||||
});
|
||||
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],
|
||||
);
|
||||
|
||||
const forcePick =
|
||||
gameState.upgrade_points > 0 && !!actions.find((a) => a.button !== "");
|
||||
list = list
|
||||
.filter((u) => fromStart.includes(u.id))
|
||||
.concat(list.filter((u) => !fromStart.includes(u.id)));
|
||||
|
||||
const upgradeId = await requiredAsyncAlert<PerkId | null>({
|
||||
title: t("level_up.title", {
|
||||
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,
|
||||
},
|
||||
list.forEach((u) => {
|
||||
dontOfferTooSoon(gameState, u.id);
|
||||
});
|
||||
|
||||
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) {
|
||||
gameState.perks[upgradeId]++;
|
||||
gameState.runStatistics.upgrades_picked++;
|
||||
gameState.upgrade_points--;
|
||||
} else {
|
||||
return;
|
||||
const forcePick = !![...upgradesActions, ...addPerkActions].find(a => !a.disabled)
|
||||
|
||||
const upgradeId = await requiredAsyncAlert<PerkId | null>({
|
||||
title: t("level_up.title", {
|
||||
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"),
|
||||
...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) {
|
||||
gameState.lastOffered[id] = Math.round(Date.now() / 1000);
|
||||
gameState.lastOffered[id] = Math.round(Date.now() / 1000);
|
||||
}
|
||||
|
|
|
@ -97,9 +97,6 @@ export function render(gameState: GameState) {
|
|||
</span><span> / </span>
|
||||
<span class="${(gameState.levelTime < levelTimeBest * 1000 && "great") || (gameState.levelTime < levelTimeGood * 1000 && "good") || ""}" data-tooltip="${t("play.stats.levelTime")}">
|
||||
${Math.ceil(gameState.levelTime / 1000)}s
|
||||
</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")}">
|
||||
${gameState.levelMisses} M
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue