This commit is contained in:
Renan LE CARO 2025-04-23 15:10:21 +02:00
parent 1d71af35c9
commit cb90fef3a9
16 changed files with 1340 additions and 755 deletions

299
dist/index.html vendored

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load diff

View file

@ -80,7 +80,7 @@ import {
catchRateGood, catchRateGood,
clamp, clamp,
levelTimeBest, levelTimeBest,
levelTimeGood, levelTimeGood, miniMarkDown,
missesBest, missesBest,
missesGood, missesGood,
wallBouncedBest, wallBouncedBest,
@ -97,6 +97,7 @@ import { runHistoryViewerMenuEntry } from "./runHistoryViewer";
import { getNearestUnlockHTML, openScorePanel } from "./openScorePanel"; import { getNearestUnlockHTML, openScorePanel } from "./openScorePanel";
import { monitorLevelsUnlocks } from "./monitorLevelsUnlocks"; import { monitorLevelsUnlocks } from "./monitorLevelsUnlocks";
import { levelEditorMenuEntry } from "./levelEditor"; import { levelEditorMenuEntry } from "./levelEditor";
import {categories} from "./upgrades";
export async function play() { export async function play() {
if (await applyFullScreenChoice()) return; if (await applyFullScreenChoice()) return;
@ -909,10 +910,9 @@ async function applyFullScreenChoice() {
async function openUnlockedUpgradesList() { async function openUnlockedUpgradesList() {
const ts = getTotalScore(); const ts = getTotalScore();
const hintField = isOptionOn("mobile-mode") ? "help" : "tooltip";
const upgradeActions = upgrades const upgradeActions = upgrades
.sort((a, b) => a.threshold - b.threshold) .sort((a, b) => a.threshold - b.threshold)
.map(({ name, id, threshold, icon, help }) => ({ .map(({ name, id, threshold, icon, help ,category, fullHelp}) => ({
text: name, text: name,
disabled: ts < threshold, disabled: ts < threshold,
value: { value: {
@ -920,10 +920,12 @@ async function openUnlockedUpgradesList() {
level: allLevelsAndIcons.find((l) => l.name === "icon:" + id), level: allLevelsAndIcons.find((l) => l.name === "icon:" + id),
} as RunParams, } as RunParams,
icon, icon,
[hintField]: category,
help:
ts < threshold ts < threshold
? t("unlocks.minTotalScore", { score: threshold }) ? t("unlocks.minTotalScore", { score: threshold })
: help(1), : help(1),
tooltip:ts < threshold ? '': fullHelp,
})); }));
const tryOn = await asyncAlert<RunParams>({ const tryOn = await asyncAlert<RunParams>({
@ -932,12 +934,21 @@ async function openUnlockedUpgradesList() {
out_of: upgradeActions.length, out_of: upgradeActions.length,
}), }),
content: [ content: [
`<p>${t("unlocks.intro", { ts })} t("unlocks.intro", { ts }),
${upgradeActions.find((u) => u.disabled) ? t("unlocks.greyed_out_help") : ""}</p> `, upgradeActions.find((u) => u.disabled) ? t("unlocks.greyed_out_help") : "",
...upgradeActions, miniMarkDown(t("unlocks.category.beginner")),
...upgradeActions.filter(u=>u.category==categories.beginner),
miniMarkDown(t("unlocks.category.combo")),
...upgradeActions.filter(u=>u.category==categories.combo),
miniMarkDown(t("unlocks.category.combo_boost")),
...upgradeActions.filter(u=>u.category==categories.combo_boost),
miniMarkDown(t("unlocks.category.simple")),
...upgradeActions.filter(u=>u.category==categories.simple),
miniMarkDown(t("unlocks.category.advanced")),
...upgradeActions.filter(u=>u.category==categories.advanced),
], ],
allowClose: true, allowClose: true,
className: "actionsAsGrid large", // className: "actionsAsGrid large",
}); });
if (tryOn) { if (tryOn) {
if (await confirmRestart(gameState)) { if (await confirmRestart(gameState)) {

View file

@ -323,9 +323,8 @@ export function getLevelUnlockCondition(levelIndex: number) {
minScore = Math.max(-1000 + 100 * levelIndex, 0); minScore = Math.max(-1000 + 100 * levelIndex, 0);
if (levelIndex > 20) { if (levelIndex > 20) {
const possibletargets = rawUpgrades const possibletargets = [...rawUpgrades]
.slice(0, Math.floor(levelIndex / 2)) .slice(0, Math.floor(levelIndex / 2))
.map((u) => u)
.filter((u) => !isExcluded(u.id)) .filter((u) => !isExcluded(u.id))
.sort( .sort(
(a, b) => hashCode(levelIndex + a.id) - hashCode(levelIndex + b.id), (a, b) => hashCode(levelIndex + a.id) - hashCode(levelIndex + b.id),

View file

@ -227,6 +227,11 @@
"starting_perks.random": "لقد تم إزالة جميع المزايا، وسيكون الاختيار عشوائيًا.", "starting_perks.random": "لقد تم إزالة جميع المزايا، وسيكون الاختيار عشوائيًا.",
"starting_perks.title": "امتيازات البداية", "starting_perks.title": "امتيازات البداية",
"starting_perks.unchecked": "لا يتم تقديم الامتيازات المذكورة أدناه كامتيازات ابتدائية، ولكن يمكنك النقر عليها لإضافتها إلى المجموعة.", "starting_perks.unchecked": "لا يتم تقديم الامتيازات المذكورة أدناه كامتيازات ابتدائية، ولكن يمكنك النقر عليها لإضافتها إلى المجموعة.",
"unlocks.category.advanced": "",
"unlocks.category.beginner": "",
"unlocks.category.combo": "",
"unlocks.category.combo_boost": "",
"unlocks.category.simple": "",
"unlocks.greyed_out_help": "يمكن فتح الترقيات غير المفعّلة بزيادة مجموع نقاطك. يزداد مجموع النقاط مع كل نقطة تُسجّلها في اللعبة.", "unlocks.greyed_out_help": "يمكن فتح الترقيات غير المفعّلة بزيادة مجموع نقاطك. يزداد مجموع النقاط مع كل نقطة تُسجّلها في اللعبة.",
"unlocks.intro": "", "unlocks.intro": "",
"unlocks.just_unlocked": "تم فتح المستوى", "unlocks.just_unlocked": "تم فتح المستوى",

View file

@ -8092,6 +8092,186 @@
<folder_node> <folder_node>
<name>unlocks</name> <name>unlocks</name>
<children> <children>
<folder_node>
<name>category</name>
<children>
<concept_node>
<name>advanced</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>beginner</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>combo</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>combo_boost</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>simple</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>
<concept_node> <concept_node>
<name>greyed_out_help</name> <name>greyed_out_help</name>
<description/> <description/>

View file

@ -227,6 +227,11 @@
"starting_perks.random": "Alle Vorteile wurden entfernt, die Auswahl erfolgt nach dem Zufallsprinzip.", "starting_perks.random": "Alle Vorteile wurden entfernt, die Auswahl erfolgt nach dem Zufallsprinzip.",
"starting_perks.title": "Startvorteile", "starting_perks.title": "Startvorteile",
"starting_perks.unchecked": "Die folgenden Vergünstigungen werden nicht als Startvergünstigungen angeboten, aber du kannst sie durch Anklicken zum Pool hinzufügen.", "starting_perks.unchecked": "Die folgenden Vergünstigungen werden nicht als Startvergünstigungen angeboten, aber du kannst sie durch Anklicken zum Pool hinzufügen.",
"unlocks.category.advanced": "",
"unlocks.category.beginner": "",
"unlocks.category.combo": "",
"unlocks.category.combo_boost": "",
"unlocks.category.simple": "",
"unlocks.greyed_out_help": "Die ausgegrauten Upgrades können freigeschaltet werden, indem du Ihre Gesamtpunktzahl erhöhst. Die Gesamtpunktzahl erhöht sich jedes Mal, wenn du im Spiel punktest.", "unlocks.greyed_out_help": "Die ausgegrauten Upgrades können freigeschaltet werden, indem du Ihre Gesamtpunktzahl erhöhst. Die Gesamtpunktzahl erhöht sich jedes Mal, wenn du im Spiel punktest.",
"unlocks.intro": "Deine Gesamtpunktzahl ist {{ts}}. Nachfolgend findest du alle Upgrades und Levels, die das Spiel zu bieten hat. Klicke auf ein Upgrade oder eine Stufe, um ein Testspiel damit zu starten.", "unlocks.intro": "Deine Gesamtpunktzahl ist {{ts}}. Nachfolgend findest du alle Upgrades und Levels, die das Spiel zu bieten hat. Klicke auf ein Upgrade oder eine Stufe, um ein Testspiel damit zu starten.",
"unlocks.just_unlocked": "Level freigeschaltet", "unlocks.just_unlocked": "Level freigeschaltet",

View file

@ -227,6 +227,11 @@
"starting_perks.random": "All benefits have been removed, the choice will be random.", "starting_perks.random": "All benefits have been removed, the choice will be random.",
"starting_perks.title": "Starting perks", "starting_perks.title": "Starting perks",
"starting_perks.unchecked": "The perks below are not offered as starting perks, but you can click to add them to the pool. ", "starting_perks.unchecked": "The perks below are not offered as starting perks, but you can click to add them to the pool. ",
"unlocks.category.advanced": "## Advanced upgrades\n\nThose are typically not very useful by themselves, but will can become very powerful when combined with the right combo upgrade. ",
"unlocks.category.beginner": "## Beginner friendly upgrades\n\nThose upgrades are very helpful for beginners, they help you play longer and miss the ball less.\n",
"unlocks.category.combo": "## Combo upgrades\n\nThose upgrades help increase your combo progressively, but also add a combo reset condition. Taking one is a good idea, taking more increases the risk and reward.",
"unlocks.category.combo_boost": "## Combo boosters\n\nThose upgrades increase the combo or combo multiplier without adding a reset condition. ",
"unlocks.category.simple": "## Simple upgrades\n\nThose upgrades will help you get a higher score by themselves. \n",
"unlocks.greyed_out_help": "The grayed out upgrades can be unlocked by increasing your total score. The total score increases every time you score in game.", "unlocks.greyed_out_help": "The grayed out upgrades can be unlocked by increasing your total score. The total score increases every time you score in game.",
"unlocks.intro": "Your total score is {{ts}}.Click an upgrade below to start a test game with it.", "unlocks.intro": "Your total score is {{ts}}.Click an upgrade below to start a test game with it.",
"unlocks.just_unlocked": "Level unlocked", "unlocks.just_unlocked": "Level unlocked",

View file

@ -227,6 +227,11 @@
"starting_perks.random": "Se han eliminado todos los beneficios, la elección será aleatoria.", "starting_perks.random": "Se han eliminado todos los beneficios, la elección será aleatoria.",
"starting_perks.title": "Beneficios iniciales", "starting_perks.title": "Beneficios iniciales",
"starting_perks.unchecked": "Los beneficios a continuación no se ofrecen como beneficios iniciales, pero puedes hacer clic para agregarlos al grupo.", "starting_perks.unchecked": "Los beneficios a continuación no se ofrecen como beneficios iniciales, pero puedes hacer clic para agregarlos al grupo.",
"unlocks.category.advanced": "",
"unlocks.category.beginner": "",
"unlocks.category.combo": "",
"unlocks.category.combo_boost": "",
"unlocks.category.simple": "",
"unlocks.greyed_out_help": "Los objetos en gris pueden desbloquearse aumentando tu puntuación total. La puntuación total aumenta cada vez que consigues puntos en el juego.", "unlocks.greyed_out_help": "Los objetos en gris pueden desbloquearse aumentando tu puntuación total. La puntuación total aumenta cada vez que consigues puntos en el juego.",
"unlocks.intro": "", "unlocks.intro": "",
"unlocks.just_unlocked": "Nivel desbloqueado", "unlocks.just_unlocked": "Nivel desbloqueado",

View file

@ -227,6 +227,11 @@
"starting_perks.random": "Tous les avantages ont été retirés, le choix sera aléatoire.", "starting_perks.random": "Tous les avantages ont été retirés, le choix sera aléatoire.",
"starting_perks.title": "Avantages de départ", "starting_perks.title": "Avantages de départ",
"starting_perks.unchecked": "Les avantages ci-dessous ne sont pas proposés comme avantages de départ, mais vous pouvez cliquer pour les ajouter aux avantages de départ possibles.", "starting_perks.unchecked": "Les avantages ci-dessous ne sont pas proposés comme avantages de départ, mais vous pouvez cliquer pour les ajouter aux avantages de départ possibles.",
"unlocks.category.advanced": "",
"unlocks.category.beginner": "",
"unlocks.category.combo": "",
"unlocks.category.combo_boost": "",
"unlocks.category.simple": "",
"unlocks.greyed_out_help": "Les éléments grisées peuvent être débloquées en augmentant votre score total. Le score total augmente à chaque fois que vous marquez des points dans le jeu.", "unlocks.greyed_out_help": "Les éléments grisées peuvent être débloquées en augmentant votre score total. Le score total augmente à chaque fois que vous marquez des points dans le jeu.",
"unlocks.intro": "", "unlocks.intro": "",
"unlocks.just_unlocked": "Niveau débloqué", "unlocks.just_unlocked": "Niveau débloqué",

View file

@ -227,6 +227,11 @@
"starting_perks.random": "Все преимущества были убраны, выбор будет случайным.", "starting_perks.random": "Все преимущества были убраны, выбор будет случайным.",
"starting_perks.title": "Стартовые привилегии", "starting_perks.title": "Стартовые привилегии",
"starting_perks.unchecked": "Приведенные ниже привилегии не предлагаются в качестве стартовых, но вы можете нажать на них, чтобы добавить в пул.", "starting_perks.unchecked": "Приведенные ниже привилегии не предлагаются в качестве стартовых, но вы можете нажать на них, чтобы добавить в пул.",
"unlocks.category.advanced": "",
"unlocks.category.beginner": "",
"unlocks.category.combo": "",
"unlocks.category.combo_boost": "",
"unlocks.category.simple": "",
"unlocks.greyed_out_help": "Выделенные серым апгрейды можно разблокировать, увеличив общее количество очков. Общий счет увеличивается каждый раз, когда вы набираете очки в игре.", "unlocks.greyed_out_help": "Выделенные серым апгрейды можно разблокировать, увеличив общее количество очков. Общий счет увеличивается каждый раз, когда вы набираете очки в игре.",
"unlocks.intro": "", "unlocks.intro": "",
"unlocks.just_unlocked": "Уровень разблокирован", "unlocks.just_unlocked": "Уровень разблокирован",

View file

@ -227,6 +227,11 @@
"starting_perks.random": "Tüm avantajlar kaldırıldı, seçim rastgele olacak.", "starting_perks.random": "Tüm avantajlar kaldırıldı, seçim rastgele olacak.",
"starting_perks.title": "Başlangıç avantajları", "starting_perks.title": "Başlangıç avantajları",
"starting_perks.unchecked": "Aşağıdaki avantajlar başlangıç avantajı olarak sunulmamaktadır, ancak havuza eklemek için tıklayabilirsiniz.", "starting_perks.unchecked": "Aşağıdaki avantajlar başlangıç avantajı olarak sunulmamaktadır, ancak havuza eklemek için tıklayabilirsiniz.",
"unlocks.category.advanced": "",
"unlocks.category.beginner": "",
"unlocks.category.combo": "",
"unlocks.category.combo_boost": "",
"unlocks.category.simple": "",
"unlocks.greyed_out_help": "Grileştirilmiş yükseltmeler toplam puanınızı artırarak açılabilir. Toplam puan, oyunda her puan aldığınızda artar.", "unlocks.greyed_out_help": "Grileştirilmiş yükseltmeler toplam puanınızı artırarak açılabilir. Toplam puan, oyunda her puan aldığınızda artar.",
"unlocks.intro": "", "unlocks.intro": "",
"unlocks.just_unlocked": "Seviye kilidi açıldı", "unlocks.just_unlocked": "Seviye kilidi açıldı",

View file

@ -28,7 +28,7 @@ function App() {
...cleaned.filter(l=>l.name.match('icon:')).sort((a,b)=>a.name>b.name ? 1:-1), ...cleaned.filter(l=>l.name.match('icon:')).sort((a,b)=>a.name>b.name ? 1:-1),
...cleaned.filter(l=>!l.name.match('icon:')) ...cleaned.filter(l=>!l.name.match('icon:'))
] ]
setLevels(sorted as RawLevel[]) setLevels(sorted as RawLevel[])
allLevels = sorted; allLevels = sorted;
}); });
}, []); }, []);

View file

@ -110,7 +110,7 @@ export function automaticBackgroundColor(bricks: string[]) {
return bricks.filter((b) => b === "g").length > return bricks.filter((b) => b === "g").length >
bricks.filter((b) => b !== "_").length * 0.05 bricks.filter((b) => b !== "_").length * 0.05
? "#115988" ? "#115988"
: ""; : "#000000";
} }
export function levelCodeToRawLevel(code: string) { export function levelCodeToRawLevel(code: string) {

1
src/types.d.ts vendored
View file

@ -28,6 +28,7 @@ export type Upgrade = {
gift: boolean; gift: boolean;
id: PerkId; id: PerkId;
name: string; name: string;
category: string;
icon: string; icon: string;
max: number; max: number;
help: (lvl: number) => string; help: (lvl: number) => string;

View file

@ -10,8 +10,28 @@ export const noCreative: PerkId[] = [
"one_more_choice", "one_more_choice",
]; ];
export const categories={
beginner:1,
combo:2,
combo_boost:2.5,
simple:3,
advanced:4,
}
export const rawUpgrades = [ export const rawUpgrades = [
{ {
category: categories.beginner,
requires: "",
threshold: 0,
gift: false,
id: "slow_down",
max: 2,
name: t("upgrades.slow_down.name"),
help: (lvl: number) => t("upgrades.slow_down.tooltip", { lvl }),
fullHelp: t("upgrades.slow_down.verbose_description"),
},
{
category: categories.beginner,
requires: "", requires: "",
threshold: 0, threshold: 0,
gift: false, gift: false,
@ -25,6 +45,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.extra_life.verbose_description"), fullHelp: t("upgrades.extra_life.verbose_description"),
}, },
{ {
category: categories.beginner,
requires: "", requires: "",
threshold: 60000, threshold: 60000,
gift: false, gift: false,
@ -35,16 +56,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.concave_puck.verbose_description"), fullHelp: t("upgrades.concave_puck.verbose_description"),
}, },
{ {
requires: "", category: categories.beginner,
threshold: 0,
gift: false,
id: "slow_down",
max: 2,
name: t("upgrades.slow_down.name"),
help: (lvl: number) => t("upgrades.slow_down.tooltip", { lvl }),
fullHelp: t("upgrades.slow_down.verbose_description"),
},
{
requires: "", requires: "",
threshold: 500, threshold: 500,
id: "telekinesis", id: "telekinesis",
@ -58,6 +70,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.telekinesis.verbose_description"), fullHelp: t("upgrades.telekinesis.verbose_description"),
}, },
{ {
category: categories.beginner,
requires: "", requires: "",
threshold: 85000, threshold: 85000,
gift: false, gift: false,
@ -68,6 +81,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.yoyo.verbose_description"), fullHelp: t("upgrades.yoyo.verbose_description"),
}, },
{ {
category: categories.beginner,
requires: "", requires: "",
threshold: 0, threshold: 0,
gift: false, gift: false,
@ -79,6 +93,18 @@ export const rawUpgrades = [
}, },
{ {
category: categories.beginner,
requires: "",
threshold: 50000,
gift: false,
id: "one_more_choice",
max: 3,
name: t("upgrades.one_more_choice.name"),
help: (lvl: number) => t("upgrades.one_more_choice.tooltip", { lvl }),
fullHelp: t("upgrades.one_more_choice.verbose_description"),
},
{
category: categories.beginner,
requires: "", requires: "",
threshold: 50, threshold: 50,
gift: false, gift: false,
@ -92,6 +118,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.skip_last.verbose_description"), fullHelp: t("upgrades.skip_last.verbose_description"),
}, },
{ {
category: categories.combo,
requires: "", requires: "",
threshold: 100, threshold: 100,
id: "streak_shots", id: "streak_shots",
@ -103,6 +130,7 @@ export const rawUpgrades = [
}, },
{ {
category: categories.combo,
requires: "", requires: "",
threshold: 200, threshold: 200,
id: "left_is_lava", id: "left_is_lava",
@ -113,6 +141,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.left_is_lava.verbose_description"), fullHelp: t("upgrades.left_is_lava.verbose_description"),
}, },
{ {
category: categories.combo,
requires: "", requires: "",
threshold: 300, threshold: 300,
id: "right_is_lava", id: "right_is_lava",
@ -123,6 +152,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.right_is_lava.verbose_description"), fullHelp: t("upgrades.right_is_lava.verbose_description"),
}, },
{ {
category: categories.combo,
requires: "", requires: "",
threshold: 400, threshold: 400,
id: "top_is_lava", id: "top_is_lava",
@ -133,6 +163,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.top_is_lava.verbose_description"), fullHelp: t("upgrades.top_is_lava.verbose_description"),
}, },
{ {
category: categories.combo,
requires: "", requires: "",
threshold: 4000, threshold: 4000,
id: "hot_start", id: "hot_start",
@ -148,6 +179,7 @@ export const rawUpgrades = [
}, },
{ {
category: categories.combo,
requires: "", requires: "",
threshold: 2000, threshold: 2000,
id: "picky_eater", id: "picky_eater",
@ -159,6 +191,7 @@ export const rawUpgrades = [
}, },
{ {
category: categories.combo,
requires: "", requires: "",
threshold: 3000, threshold: 3000,
id: "compound_interest", id: "compound_interest",
@ -169,6 +202,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.compound_interest.verbose_description"), fullHelp: t("upgrades.compound_interest.verbose_description"),
}, },
{ {
category: categories.combo,
requires: "", requires: "",
threshold: 150000, threshold: 150000,
gift: true, gift: true,
@ -180,27 +214,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.side_kick.verbose_description"), fullHelp: t("upgrades.side_kick.verbose_description"),
}, },
{ {
requires: "", category: categories.combo,
threshold: 135000,
// a bit too hard when starting up
gift: false,
id: "reach",
max: 1,
name: t("upgrades.reach.name"),
help: (lvl: number) => t("upgrades.reach.tooltip", { lvl }),
fullHelp: t("upgrades.reach.verbose_description"),
},
{
requires: "multiball",
threshold: 245000,
gift: false,
id: "happy_family",
max: 1,
name: t("upgrades.happy_family.name"),
help: () => t("upgrades.happy_family.tooltip"),
fullHelp: t("upgrades.happy_family.verbose_description"),
},
{
requires: "", requires: "",
threshold: 150000, threshold: 150000,
gift: true, gift: true,
@ -212,6 +226,30 @@ export const rawUpgrades = [
fullHelp: t("upgrades.side_flip.verbose_description"), fullHelp: t("upgrades.side_flip.verbose_description"),
}, },
{ {
category: categories.combo,
requires: "",
threshold: 135000,
// a bit too hard when starting up
gift: false,
id: "reach",
max: 1,
name: t("upgrades.reach.name"),
help: (lvl: number) => t("upgrades.reach.tooltip", { lvl }),
fullHelp: t("upgrades.reach.verbose_description"),
},
{
category: categories.combo,
requires: "multiball",
threshold: 245000,
gift: false,
id: "happy_family",
max: 1,
name: t("upgrades.happy_family.name"),
help: () => t("upgrades.happy_family.tooltip"),
fullHelp: t("upgrades.happy_family.verbose_description"),
},
{
category: categories.combo,
requires: "", requires: "",
threshold: 165000, threshold: 165000,
gift: false, gift: false,
@ -223,6 +261,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.addiction.verbose_description"), fullHelp: t("upgrades.addiction.verbose_description"),
}, },
{ {
category: categories.combo,
requires: "", requires: "",
threshold: 90000, threshold: 90000,
gift: true, gift: true,
@ -233,6 +272,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.nbricks.verbose_description"), fullHelp: t("upgrades.nbricks.verbose_description"),
}, },
{ {
category: categories.combo,
requires: "", requires: "",
threshold: 230000, threshold: 230000,
gift: false, gift: false,
@ -244,6 +284,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.three_cushion.verbose_description"), fullHelp: t("upgrades.three_cushion.verbose_description"),
}, },
{ {
category: categories.combo,
requires: "", requires: "",
threshold: 115000, threshold: 115000,
gift: true, gift: true,
@ -255,10 +296,10 @@ export const rawUpgrades = [
}, },
{ {
category: categories.combo,
requires: "", requires: "",
threshold: 105000, threshold: 105000,
gift: true, gift: true,
id: "zen", id: "zen",
max: 1, max: 1,
name: t("upgrades.zen.name"), name: t("upgrades.zen.name"),
@ -266,6 +307,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.zen.verbose_description"), fullHelp: t("upgrades.zen.verbose_description"),
}, },
{ {
category: categories.combo,
requires: "", requires: "",
threshold: 70000, threshold: 70000,
gift: true, gift: true,
@ -278,6 +320,7 @@ export const rawUpgrades = [
// Regular // Regular
{ {
category: categories.simple,
requires: "", requires: "",
threshold: 15000, threshold: 15000,
gift: false, gift: false,
@ -288,6 +331,29 @@ export const rawUpgrades = [
fullHelp: t("upgrades.pierce_color.verbose_description"), fullHelp: t("upgrades.pierce_color.verbose_description"),
}, },
{ {
category: categories.simple,
requires: "",
threshold: 1500,
id: "pierce",
gift: false,
max: 3,
name: t("upgrades.pierce.name"),
help: (lvl: number) => t("upgrades.pierce.tooltip", { count: 3 * lvl }),
fullHelp: t("upgrades.pierce.verbose_description"),
},
{
category: categories.simple,
requires: "",
threshold: 800,
id: "multiball",
gift: true,
max: 6,
name: t("upgrades.multiball.name"),
help: (lvl: number) => t("upgrades.multiball.tooltip", { count: lvl + 1 }),
fullHelp: t("upgrades.multiball.verbose_description"),
},
{
category: categories.advanced,
requires: "multiball", requires: "multiball",
threshold: 21000, threshold: 21000,
gift: false, gift: false,
@ -301,6 +367,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.ball_repulse_ball.verbose_description"), fullHelp: t("upgrades.ball_repulse_ball.verbose_description"),
}, },
{ {
category: categories.advanced,
requires: "multiball", requires: "multiball",
threshold: 25000, threshold: 25000,
gift: false, gift: false,
@ -314,6 +381,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.ball_attract_ball.verbose_description"), fullHelp: t("upgrades.ball_attract_ball.verbose_description"),
}, },
{ {
category: categories.advanced,
requires: "", requires: "",
threshold: 30000, threshold: 30000,
gift: false, gift: false,
@ -327,6 +395,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.puck_repulse_ball.verbose_description"), fullHelp: t("upgrades.puck_repulse_ball.verbose_description"),
}, },
{ {
category: categories.advanced,
requires: "", requires: "",
threshold: 35000, threshold: 35000,
gift: false, gift: false,
@ -338,6 +407,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.wind.verbose_description"), fullHelp: t("upgrades.wind.verbose_description"),
}, },
{ {
category: categories.advanced,
requires: "", requires: "",
threshold: 65000, threshold: 65000,
gift: false, gift: false,
@ -348,6 +418,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.helium.verbose_description"), fullHelp: t("upgrades.helium.verbose_description"),
}, },
{ {
category: categories.advanced,
requires: "", requires: "",
threshold: 200000, threshold: 200000,
gift: false, gift: false,
@ -358,6 +429,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.bricks_attract_coins.verbose_description"), fullHelp: t("upgrades.bricks_attract_coins.verbose_description"),
}, },
{ {
category: categories.advanced,
requires: "", requires: "",
threshold: 240000, threshold: 240000,
gift: false, gift: false,
@ -368,6 +440,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.wrap_left.verbose_description"), fullHelp: t("upgrades.wrap_left.verbose_description"),
}, },
{ {
category: categories.advanced,
requires: "", requires: "",
threshold: 245000, threshold: 245000,
gift: false, gift: false,
@ -379,6 +452,7 @@ export const rawUpgrades = [
}, },
{ {
category: categories.simple,
requires: "", requires: "",
threshold: 45000, threshold: 45000,
gift: false, gift: false,
@ -393,16 +467,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.respawn.verbose_description"), fullHelp: t("upgrades.respawn.verbose_description"),
}, },
{ {
requires: "", category: categories.advanced,
threshold: 50000,
gift: false,
id: "one_more_choice",
max: 3,
name: t("upgrades.one_more_choice.name"),
help: (lvl: number) => t("upgrades.one_more_choice.tooltip", { lvl }),
fullHelp: t("upgrades.one_more_choice.verbose_description"),
},
{
requires: "", requires: "",
threshold: 55000, threshold: 55000,
gift: false, gift: false,
@ -418,6 +483,7 @@ export const rawUpgrades = [
}, },
{ {
category: categories.advanced,
requires: "", requires: "",
threshold: 75000, threshold: 75000,
gift: false, gift: false,
@ -429,6 +495,7 @@ export const rawUpgrades = [
}, },
{ {
category: categories.advanced,
requires: "", requires: "",
threshold: 95000, threshold: 95000,
gift: false, gift: false,
@ -439,6 +506,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.etherealcoins.verbose_description"), fullHelp: t("upgrades.etherealcoins.verbose_description"),
}, },
{ {
category: categories.advanced,
requires: "multiball", requires: "multiball",
threshold: 100000, threshold: 100000,
gift: false, gift: false,
@ -449,6 +517,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.shocks.verbose_description"), fullHelp: t("upgrades.shocks.verbose_description"),
}, },
{ {
category: categories.advanced,
requires: "extra_life", requires: "extra_life",
threshold: 110000, threshold: 110000,
gift: false, gift: false,
@ -463,6 +532,7 @@ export const rawUpgrades = [
}, },
{ {
category: categories.advanced,
requires: "", requires: "",
threshold: 120000, threshold: 120000,
gift: false, gift: false,
@ -473,6 +543,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.ghost_coins.verbose_description"), fullHelp: t("upgrades.ghost_coins.verbose_description"),
}, },
{ {
category: categories.combo_boost,
requires: "", requires: "",
threshold: 125000, threshold: 125000,
gift: false, gift: false,
@ -483,6 +554,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.forgiving.verbose_description"), fullHelp: t("upgrades.forgiving.verbose_description"),
}, },
{ {
category: categories.simple,
requires: "", requires: "",
threshold: 130000, threshold: 130000,
gift: false, gift: false,
@ -493,6 +565,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.ball_attracts_coins.verbose_description"), fullHelp: t("upgrades.ball_attracts_coins.verbose_description"),
}, },
{ {
category: categories.simple,
requires: "", requires: "",
threshold: 145000, threshold: 145000,
gift: false, gift: false,
@ -504,6 +577,7 @@ export const rawUpgrades = [
}, },
{ {
category: categories.advanced,
requires: "", requires: "",
threshold: 155000, threshold: 155000,
gift: false, gift: false,
@ -514,6 +588,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.implosions.verbose_description"), fullHelp: t("upgrades.implosions.verbose_description"),
}, },
{ {
category: categories.simple,
requires: "", requires: "",
threshold: 160000, threshold: 160000,
gift: false, gift: false,
@ -524,6 +599,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.corner_shot.verbose_description"), fullHelp: t("upgrades.corner_shot.verbose_description"),
}, },
{ {
category: categories.advanced,
requires: "", requires: "",
threshold: 175000, threshold: 175000,
gift: false, gift: false,
@ -534,6 +610,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.limitless.verbose_description"), fullHelp: t("upgrades.limitless.verbose_description"),
}, },
{ {
category: categories.advanced,
requires: "", requires: "",
threshold: 185000, threshold: 185000,
gift: false, gift: false,
@ -544,6 +621,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.trickledown.verbose_description"), fullHelp: t("upgrades.trickledown.verbose_description"),
}, },
{ {
category: categories.combo_boost,
requires: "", requires: "",
threshold: 190000, threshold: 190000,
gift: false, gift: false,
@ -555,6 +633,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.transparency.verbose_description"), fullHelp: t("upgrades.transparency.verbose_description"),
}, },
{ {
category: categories.simple,
requires: "", requires: "",
threshold: 195000, threshold: 195000,
gift: false, gift: false,
@ -565,6 +644,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.superhot.verbose_description"), fullHelp: t("upgrades.superhot.verbose_description"),
}, },
{ {
category: categories.advanced,
requires: "", requires: "",
threshold: 205000, threshold: 205000,
gift: false, gift: false,
@ -575,6 +655,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.rainbow.verbose_description"), fullHelp: t("upgrades.rainbow.verbose_description"),
}, },
{ {
category: categories.advanced,
requires: "metamorphosis", requires: "metamorphosis",
threshold: 210000, threshold: 210000,
gift: false, gift: false,
@ -585,6 +666,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.hypnosis.verbose_description"), fullHelp: t("upgrades.hypnosis.verbose_description"),
}, },
{ {
category: categories.simple,
requires: "", requires: "",
threshold: 215000, threshold: 215000,
gift: false, gift: false,
@ -596,6 +678,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.bricks_attract_ball.verbose_description"), fullHelp: t("upgrades.bricks_attract_ball.verbose_description"),
}, },
{ {
category: categories.simple,
requires: "", requires: "",
threshold: 220000, threshold: 220000,
gift: false, gift: false,
@ -606,6 +689,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.buoy.verbose_description"), fullHelp: t("upgrades.buoy.verbose_description"),
}, },
{ {
category: categories.advanced,
requires: "", requires: "",
threshold: 225000, threshold: 225000,
gift: false, gift: false,
@ -617,6 +701,7 @@ export const rawUpgrades = [
}, },
{ {
category: categories.advanced,
requires: "", requires: "",
threshold: 235000, threshold: 235000,
gift: false, gift: false,
@ -627,6 +712,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.sticky_coins.verbose_description"), fullHelp: t("upgrades.sticky_coins.verbose_description"),
}, },
{ {
category: categories.combo_boost,
requires: "", requires: "",
threshold: 0, threshold: 0,
id: "base_combo", id: "base_combo",
@ -638,6 +724,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.base_combo.verbose_description"), fullHelp: t("upgrades.base_combo.verbose_description"),
}, },
{ {
category: categories.simple,
requires: "", requires: "",
threshold: 0, threshold: 0,
gift: false, gift: false,
@ -648,6 +735,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.viscosity.verbose_description"), fullHelp: t("upgrades.viscosity.verbose_description"),
}, },
{ {
category: categories.simple,
requires: "", requires: "",
threshold: 700, threshold: 700,
gift: false, gift: false,
@ -661,16 +749,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.coin_magnet.verbose_description"), fullHelp: t("upgrades.coin_magnet.verbose_description"),
}, },
{ {
requires: "", category: categories.advanced,
threshold: 800,
id: "multiball",
gift: true,
max: 6,
name: t("upgrades.multiball.name"),
help: (lvl: number) => t("upgrades.multiball.tooltip", { count: lvl + 1 }),
fullHelp: t("upgrades.multiball.verbose_description"),
},
{
requires: "", requires: "",
threshold: 1000, threshold: 1000,
gift: false, gift: false,
@ -682,16 +761,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.smaller_puck.verbose_description"), fullHelp: t("upgrades.smaller_puck.verbose_description"),
}, },
{ {
requires: "", category: categories.advanced,
threshold: 1500,
id: "pierce",
gift: false,
max: 3,
name: t("upgrades.pierce.name"),
help: (lvl: number) => t("upgrades.pierce.tooltip", { count: 3 * lvl }),
fullHelp: t("upgrades.pierce.verbose_description"),
},
{
requires: "", requires: "",
threshold: 2500, threshold: 2500,
gift: false, gift: false,
@ -702,6 +772,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.metamorphosis.verbose_description"), fullHelp: t("upgrades.metamorphosis.verbose_description"),
}, },
{ {
category: categories.simple,
requires: "", requires: "",
threshold: 6000, threshold: 6000,
id: "sapper", id: "sapper",
@ -715,6 +786,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.sapper.verbose_description"), fullHelp: t("upgrades.sapper.verbose_description"),
}, },
{ {
category: categories.simple,
requires: "", requires: "",
threshold: 9000, threshold: 9000,
id: "bigger_explosions", id: "bigger_explosions",
@ -725,6 +797,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.bigger_explosions.verbose_description"), fullHelp: t("upgrades.bigger_explosions.verbose_description"),
}, },
{ {
category: categories.simple,
requires: "", requires: "",
threshold: 13000, threshold: 13000,
gift: false, gift: false,
@ -736,8 +809,8 @@ export const rawUpgrades = [
t("upgrades.extra_levels.tooltip", { count: lvl + 7 }), t("upgrades.extra_levels.tooltip", { count: lvl + 7 }),
fullHelp: t("upgrades.extra_levels.verbose_description"), fullHelp: t("upgrades.extra_levels.verbose_description"),
}, },
// Combo boosters
{ {
category: categories.combo_boost,
requires: "", requires: "",
threshold: 170000, threshold: 170000,
gift: false, gift: false,
@ -748,6 +821,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.fountain_toss.verbose_description"), fullHelp: t("upgrades.fountain_toss.verbose_description"),
}, },
{ {
category: categories.combo_boost,
requires: "", requires: "",
threshold: 180000, threshold: 180000,
gift: false, gift: false,
@ -758,6 +832,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.minefield.verbose_description"), fullHelp: t("upgrades.minefield.verbose_description"),
}, },
{ {
category: categories.combo_boost,
requires: "", requires: "",
threshold: 18000, threshold: 18000,
gift: false, gift: false,
@ -771,6 +846,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.soft_reset.verbose_description"), fullHelp: t("upgrades.soft_reset.verbose_description"),
}, },
{ {
category: categories.combo_boost,
requires: "", requires: "",
threshold: 80000, threshold: 80000,
gift: false, gift: false,
@ -784,6 +860,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.shunt.verbose_description"), fullHelp: t("upgrades.shunt.verbose_description"),
}, },
{ {
category: categories.combo_boost,
requires: "", requires: "",
threshold: 140000, threshold: 140000,
gift: true, gift: true,
@ -795,6 +872,7 @@ export const rawUpgrades = [
fullHelp: t("upgrades.passive_income.verbose_description"), fullHelp: t("upgrades.passive_income.verbose_description"),
}, },
{ {
category: categories.combo_boost,
requires: "", requires: "",
threshold: 40000, threshold: 40000,
gift: false, gift: false,
@ -805,4 +883,4 @@ export const rawUpgrades = [
t("upgrades.sturdy_bricks.tooltip", { lvl, percent: lvl * 50 }), t("upgrades.sturdy_bricks.tooltip", { lvl, percent: lvl * 50 }),
fullHelp: t("upgrades.sturdy_bricks.verbose_description"), fullHelp: t("upgrades.sturdy_bricks.verbose_description"),
}, },
] as const; ].sort((a,b)=>(a.category-b.category) || (a.threshold-b.threshold)) as const;