This commit is contained in:
Renan LE CARO 2025-05-03 08:20:00 +02:00
parent 70182b0129
commit ca1c75a5a1
28 changed files with 992 additions and 1073 deletions

View file

@ -1,184 +1,200 @@
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 { GameState, PerkId } from "./types";
import {
escapeAttribute,
getPossibleUpgrades,
levelsListHTMl,
max_levels,
upgradeLevelAndMaxDisplay,
catchRateBest,
catchRateGood,
levelTimeBest,
levelTimeGood,
missesBest,
missesGood,
choicePerGold,
choicePerSilver,
upPerGold,
upPerSilver,
} from "./pure_functions";
import { t } from "./i18n/i18n";
import { icons } from "./loadGameData";
import { requiredAsyncAlert } from "./asyncAlert";
import {
escapeAttribute,
getPossibleUpgrades,
levelsListHTMl,
max_levels,
pickedUpgradesHTMl,
upgradeLevelAndMaxDisplay,
} from "./game_utils";
import {getNearestUnlockHTML} from "./openScorePanel";
import { getFirstUnlockable, getNearestUnlockHTML } from "./openScorePanel";
import { isOptionOn } from "./options";
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 = [];
let medals = [];
let upgradePoints = 1;
let extraChoices = 0;
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)}">
let hasMedals = 0;
function challengeResult(
name: String,
description: String,
medal: "gold" | "silver" | "no",
) {
let choices = 0,
up = 0;
if (medal === "gold") {
choices += choicePerGold;
up += upPerGold;
} else if (medal === "silver") {
choices += choicePerSilver;
up += upPerSilver;
}
if (medal !== "no") {
hasMedals++;
}
extraChoices += choices;
upgradePoints += up;
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]}
${
up || choices
? t("level_up.challenges.gain", { up, choices })
: t("level_up.challenges.no_gain")
}
</p>
</div>`);
}
}
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.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),
caught: gameState.levelCoughtCoins,
total: gameState.levelSpawnedCoins,
}),
t("level_up.challenges.catchRateGood.description", {
silver: catchRateGood,
gold: catchRateBest,
}),
(catchRate > catchRateBest / 100 && "gold") ||
(catchRate > catchRateGood / 100 && "silver") ||
"no",
);
challengeResult(
gameState.levelMisses
? t("level_up.challenges.levelMisses.name", {
value: gameState.levelMisses,
})
: t("level_up.challenges.levelMisses.none"),
t("level_up.challenges.levelMisses.description", {
silver: missesGood,
gold: missesBest,
}),
(gameState.levelMisses < missesBest && "gold") ||
(gameState.levelMisses < missesGood && "silver") ||
"no",
);
if (hasMedals == 0) {
medals.length = 0;
} else if (hasMedals == 1) {
medals.unshift(t("level_up.challenges.earned_medal", { count: hasMedals }));
} else {
medals.unshift(
t("level_up.challenges.earned_medal_plural", { count: hasMedals }),
);
}
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",
);
const unlockable = getFirstUnlockable(gameState);
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",
);
let offered = 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)
.slice(0, 3 + extraChoices + gameState.perks.one_more_choice);
gameState.upgrade_points += upgradesWon;
offered.forEach((u) => {
dontOfferTooSoon(gameState, u.id);
});
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);
while (true) {
let unlockRelatedUpgradesOffered = 0;
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
.filter((u) => fromStart.includes(u.id))
.concat(list.filter((u) => !fromStart.includes(u.id)));
list.forEach((u) => {
dontOfferTooSoon(gameState, u.id);
});
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])
}))
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)
}))
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;
const upgradesActions = offered.map((u) => {
let className = "";
if (isOptionOn("level_unlocks_hints")) {
if (unlockable?.forbidden?.includes(u.id)) {
unlockRelatedUpgradesOffered++;
className += " forbidden";
}
if (unlockable?.required?.includes(u.id)) {
unlockRelatedUpgradesOffered++;
className += " required";
}
}
return {
value: u.id,
disabled: gameState.perks[u.id] >= u.max + gameState.perks.limitless,
text:
u.name +
(gameState.perks[u.id]
? upgradeLevelAndMaxDisplay(u, gameState)
: ""),
icon: icons["icon:" + u.id],
help: u.help(gameState.perks[u.id] || 1),
tooltip: u.fullHelp(gameState.perks[u.id] || 1),
className,
};
});
const upgradeId = await requiredAsyncAlert<PerkId>({
title: t("level_up.title", {
level: gameState.currentLevel,
max: max_levels(gameState),
}),
content: [
t("level_up.upgrade_perks", {
coins: gameState.levelCoughtCoins,
count: upgradePoints,
}),
...upgradesActions,
levelsListHTMl(gameState, gameState.currentLevel),
unlockRelatedUpgradesOffered ? getNearestUnlockHTML(gameState) : "",
...medals,
pickedUpgradesHTMl(gameState),
`<div id="level-recording-container"></div>`,
],
});
upgradePoints--;
gameState.perks[upgradeId]++;
gameState.runStatistics.upgrades_picked++;
if (!upgradePoints) {
return;
}
}
}
export function dontOfferTooSoon(gameState: GameState, id: PerkId) {
gameState.lastOffered[id] = Math.round(Date.now() / 1000);
gameState.lastOffered[id] = Math.round(Date.now() / 1000);
}