mirror of
https://gitlab.com/lecarore/breakout71.git
synced 2025-04-21 12:36:15 -04:00
Draft of looping mode, shine bricks when hit but not broken
This commit is contained in:
parent
59ef24c865
commit
46f87556e1
20 changed files with 2639 additions and 3031 deletions
49
Readme.md
49
Readme.md
|
@ -13,8 +13,9 @@ Break colourful bricks, catch bouncing coins and select powerful upgrades !
|
||||||
- [GitLab](https://gitlab.com/lecarore/breakout71)
|
- [GitLab](https://gitlab.com/lecarore/breakout71)
|
||||||
- [HackerNews thread](https://news.ycombinator.com/item?id=43183131)
|
- [HackerNews thread](https://news.ycombinator.com/item?id=43183131)
|
||||||
|
|
||||||
|
|
||||||
# Todo
|
# Todo
|
||||||
- bring back detailed help of perks as "intel"
|
|
||||||
- people assume unbounded allows for wrap around
|
- people assume unbounded allows for wrap around
|
||||||
- coin magnet and viscosity : only one level ~2.5
|
- coin magnet and viscosity : only one level ~2.5
|
||||||
- Boost Ascetism : give +2 or even +3 combo per brick destroyed
|
- Boost Ascetism : give +2 or even +3 combo per brick destroyed
|
||||||
|
@ -22,17 +23,24 @@ Break colourful bricks, catch bouncing coins and select powerful upgrades !
|
||||||
- show -N points in red when combo resets
|
- show -N points in red when combo resets
|
||||||
- reach : this is too punishing now, maybe only reset if you hit the lowest populate row of the level, if it's not a full width row
|
- reach : this is too punishing now, maybe only reset if you hit the lowest populate row of the level, if it's not a full width row
|
||||||
- respawn: N% of bricks respawn after N seconds
|
- respawn: N% of bricks respawn after N seconds
|
||||||
|
- [jaceys] Counters for coins lost, misses, and boundary bounces, as well as a timer.
|
||||||
|
- [jaceys] Move the restart button out of the menu, so that it is more easily accessible
|
||||||
|
- [jaceys] A visual indication of whether a ball has hit a brick this serve
|
||||||
|
- [obigre] Offer to level ups perks separately
|
||||||
|
- bring back detailed help of perks as "intel"
|
||||||
|
- https://weblate.org/fr/
|
||||||
|
|
||||||
|
|
||||||
# Premium: infinite mode
|
# Premium: allow looping
|
||||||
|
|
||||||
Allow players to loop the game, adding one hasard per loop, making it harder and harder to exploit each strategy.
|
Allow players to loop the game :
|
||||||
The high score are separated from the main mode. The scores are added for unlock. You no longer get upgrades after the first 7 levels.
|
- [x] keep your score
|
||||||
The score you make in each level is instead multiplied by the number of "upgrades" and "choices" you would have had.
|
- [x] keep 1 perk
|
||||||
|
- [x] add one hasard
|
||||||
The score is your "fuel", and lets you pick the next level from a list. Each level has a cost, preview, and one or two downgrades.
|
- [ ] add one HP to all bricks
|
||||||
Each downgrade acts as a score multiplier.
|
- [ ] advertise looping in normal game over screen
|
||||||
Your goal is no longer to score higher, but to go farther
|
- [ ] save score at the end of first loop, in addition to the final one ?
|
||||||
|
- [ ] check that stats like max level are correct
|
||||||
|
|
||||||
|
|
||||||
# System requirements
|
# System requirements
|
||||||
|
@ -164,7 +172,18 @@ There's also an easy mode for kids (slower ball).
|
||||||
|
|
||||||
# extra levels
|
# extra levels
|
||||||
|
|
||||||
- Good games : FTL, Nova drift, Noita, Enter the gungeon, Zero Sivert, Factorio, Swarm
|
- Good games :
|
||||||
|
- FTL
|
||||||
|
- Nova drift
|
||||||
|
- Noita
|
||||||
|
- Enter the gungeon
|
||||||
|
- Zero Sivert
|
||||||
|
- Factorio
|
||||||
|
- Swarm
|
||||||
|
- Nuclear throne
|
||||||
|
- Brigador
|
||||||
|
|
||||||
|
|
||||||
- letters and an associated word or name
|
- letters and an associated word or name
|
||||||
- famous characters and movies
|
- famous characters and movies
|
||||||
- fruits
|
- fruits
|
||||||
|
@ -224,3 +243,13 @@ https://prohama.com/dog-21-pattern/
|
||||||
|
|
||||||
I wanted an APK to start in fullscreen and be able to list it on fdroid and the play store. I started with an empty view and went to work trimming it down, with the help of that tutorial
|
I wanted an APK to start in fullscreen and be able to list it on fdroid and the play store. I started with an empty view and went to work trimming it down, with the help of that tutorial
|
||||||
https://github.com/fractalwrench/ApkGolf/blob/master/blog/BLOG_POST.md
|
https://github.com/fractalwrench/ApkGolf/blob/master/blog/BLOG_POST.md
|
||||||
|
|
||||||
|
|
||||||
|
# Other noteworthy games in the breakout genre
|
||||||
|
|
||||||
|
LBreakoutHD : https://sourceforge.net/p/lgames/code/HEAD/tree/trunk/lbreakouthd/
|
||||||
|
|
||||||
|
Wizorb https://store.steampowered.com/app/207420/Wizorb/
|
||||||
|
|
||||||
|
Rollers of the realm : narratif, chaque balle est un aventurier
|
||||||
|
https://store.steampowered.com/app/262470/Rollers_of_the_Realm/
|
1015
dist/index.html
vendored
1015
dist/index.html
vendored
File diff suppressed because one or more lines are too long
159
src/adventure.ts
159
src/adventure.ts
|
@ -1,159 +0,0 @@
|
||||||
import { GameState, Level, PerkId } from "./types";
|
|
||||||
import { pickedUpgradesHTMl, sample, sumOfValues } from "./game_utils";
|
|
||||||
import { allLevels, icons, upgrades } from "./loadGameData";
|
|
||||||
import { t } from "./i18n/i18n";
|
|
||||||
import { requiredAsyncAlert } from "./asyncAlert";
|
|
||||||
import { debuffs } from "./debuffs";
|
|
||||||
|
|
||||||
type AdventureModeButton = {
|
|
||||||
text: string;
|
|
||||||
icon: string;
|
|
||||||
help?: string;
|
|
||||||
value: AdventureModeSelection;
|
|
||||||
};
|
|
||||||
type AdventureModeSelection = {
|
|
||||||
cost: number;
|
|
||||||
level?: Level;
|
|
||||||
perk?: PerkId;
|
|
||||||
discard?: PerkId;
|
|
||||||
};
|
|
||||||
const MAX_LVL = 3;
|
|
||||||
export async function openAdventureRunUpgradesPicker(gameState: GameState) {
|
|
||||||
// Just add random debuff for now
|
|
||||||
const debuffToApply = sample(
|
|
||||||
debuffs.filter((d) => gameState.debuffs[d.id] < d.max),
|
|
||||||
);
|
|
||||||
if (debuffToApply) {
|
|
||||||
gameState.debuffs[debuffToApply.id]++;
|
|
||||||
}
|
|
||||||
|
|
||||||
let levelChoiceCount = 1;
|
|
||||||
|
|
||||||
const catchRate =
|
|
||||||
(gameState.score - gameState.levelStartScore) /
|
|
||||||
(gameState.levelSpawnedCoins || 1);
|
|
||||||
|
|
||||||
if (gameState.levelWallBounces == 0) {
|
|
||||||
levelChoiceCount++;
|
|
||||||
}
|
|
||||||
if (gameState.levelTime < 30 * 1000) {
|
|
||||||
levelChoiceCount++;
|
|
||||||
}
|
|
||||||
if (catchRate === 1) {
|
|
||||||
levelChoiceCount++;
|
|
||||||
}
|
|
||||||
if (gameState.levelMisses === 0) {
|
|
||||||
levelChoiceCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
let perkChoices = 2 + gameState.perks.one_more_choice + levelChoiceCount;
|
|
||||||
|
|
||||||
const priceMultiplier =
|
|
||||||
1 +
|
|
||||||
Math.ceil(
|
|
||||||
gameState.currentLevel * Math.pow(1.05, gameState.currentLevel) * 10,
|
|
||||||
);
|
|
||||||
|
|
||||||
const levelChoices: AdventureModeButton[] = [...allLevels]
|
|
||||||
.sort(() => Math.random() - 0.5)
|
|
||||||
.slice(0, MAX_LVL)
|
|
||||||
.sort((a, b) => a.bricksCount - b.bricksCount)
|
|
||||||
.slice(0, Math.min(MAX_LVL, levelChoiceCount))
|
|
||||||
.map((level, levelIndex) => ({
|
|
||||||
text: t("premium.pick_level", {
|
|
||||||
name: level.name,
|
|
||||||
cost: priceMultiplier * levelIndex,
|
|
||||||
}),
|
|
||||||
icon: icons[level.name],
|
|
||||||
help:
|
|
||||||
level.size +
|
|
||||||
"x" +
|
|
||||||
level.size +
|
|
||||||
" with " +
|
|
||||||
level.bricksCount +
|
|
||||||
" bricks",
|
|
||||||
value: {
|
|
||||||
level,
|
|
||||||
cost: priceMultiplier * levelIndex,
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
const perksChoices = upgrades
|
|
||||||
.filter((u) => u.adventure)
|
|
||||||
.filter((u) => !u?.requires || gameState.perks[u?.requires])
|
|
||||||
.filter((u) => gameState.perks[u.id] < u.max)
|
|
||||||
.sort(() => Math.random() - 0.5)
|
|
||||||
.slice(0, perkChoices);
|
|
||||||
|
|
||||||
const discardChoices: AdventureModeButton[] =
|
|
||||||
sumOfValues(gameState.perks) > 5
|
|
||||||
? upgrades
|
|
||||||
.filter((u) => u.adventure)
|
|
||||||
.filter((u) => gameState.perks[u.id])
|
|
||||||
.sort(() => Math.random() - 0.5)
|
|
||||||
.slice(0, 3)
|
|
||||||
.map((u, ui) => {
|
|
||||||
return {
|
|
||||||
icon: `<span class="red-icon">${u.icon}</span>`,
|
|
||||||
text: t("premium.discard", { name: u.name }),
|
|
||||||
help: t("premium.discard_help"),
|
|
||||||
value: { discard: u.id, cost: 0 },
|
|
||||||
};
|
|
||||||
})
|
|
||||||
: [];
|
|
||||||
|
|
||||||
let used = new Set();
|
|
||||||
let choice: AdventureModeSelection | null = null;
|
|
||||||
while (
|
|
||||||
(choice = await requiredAsyncAlert({
|
|
||||||
title: t("premium.next_step_title"),
|
|
||||||
content: [
|
|
||||||
`
|
|
||||||
<p>${t("premium.choose_next_step", { score: gameState.score })}</p>
|
|
||||||
${pickedUpgradesHTMl(gameState)}
|
|
||||||
`,
|
|
||||||
...perksChoices.map((u, ui) => {
|
|
||||||
const lvl = gameState.perks[u.id];
|
|
||||||
const cost =
|
|
||||||
(priceMultiplier + sumOfValues(gameState.perks) + lvl) * (ui + 1);
|
|
||||||
return {
|
|
||||||
icon: u.icon,
|
|
||||||
text:
|
|
||||||
lvl == 0
|
|
||||||
? t("premium.pick_perk", { name: u.name, cost })
|
|
||||||
: t("premium.upgrade_perk_to_level", {
|
|
||||||
name: u.name,
|
|
||||||
cost,
|
|
||||||
lvl: lvl + 1,
|
|
||||||
}),
|
|
||||||
help: u.help(lvl + 1),
|
|
||||||
value: { perk: u.id, cost },
|
|
||||||
disabled: gameState.score < cost || used.has(u.id),
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
discardChoices.length ? "You can discard some perks" : "",
|
|
||||||
...discardChoices,
|
|
||||||
`Click a level below to continue`,
|
|
||||||
...levelChoices.map((p) => ({
|
|
||||||
...p,
|
|
||||||
disabled: gameState.score < p.value.cost,
|
|
||||||
})),
|
|
||||||
],
|
|
||||||
}))
|
|
||||||
) {
|
|
||||||
gameState.score -= choice.cost;
|
|
||||||
if (choice.perk) {
|
|
||||||
used.add(choice.perk);
|
|
||||||
gameState.perks[choice.perk]++;
|
|
||||||
}
|
|
||||||
if (choice.discard) {
|
|
||||||
used.add(choice.discard);
|
|
||||||
gameState.perks[choice.discard] = 0;
|
|
||||||
}
|
|
||||||
if (choice.level) {
|
|
||||||
gameState.runLevels[gameState.currentLevel + 1] = choice.level;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -860,7 +860,7 @@
|
||||||
{
|
{
|
||||||
"name": "icon:unbounded",
|
"name": "icon:unbounded",
|
||||||
"size": 9,
|
"size": 9,
|
||||||
"bricks": "y_WWWWW_y__________yttttt____ttttt__y____W__y______________y_y____________WWW___y",
|
"bricks": "rrWWWWWrrrr_____rrrrtttttrrrrttt__rrrr____yrrrr_____rrrr___y_yWrr_____rrrrWWW__ry",
|
||||||
"svg": null,
|
"svg": null,
|
||||||
"color": ""
|
"color": ""
|
||||||
},
|
},
|
||||||
|
@ -1032,13 +1032,6 @@
|
||||||
"svg": null,
|
"svg": null,
|
||||||
"color": ""
|
"color": ""
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "icon:adventure_mode",
|
|
||||||
"size": 11,
|
|
||||||
"bricks": "__________________________________ttt___bbb_bttttbbbbbbbb__tbt__bbbbbbbbttttb_bbb___ttt__________________________________",
|
|
||||||
"svg": null,
|
|
||||||
"color": ""
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "icon:7_levels_run",
|
"name": "icon:7_levels_run",
|
||||||
"size": 7,
|
"size": 7,
|
||||||
|
|
|
@ -1,56 +1,47 @@
|
||||||
import { t } from "./i18n/i18n";
|
import { t } from "./i18n/i18n";
|
||||||
|
import {Debuff} from "./types";
|
||||||
|
|
||||||
export const debuffs = [
|
export const debuffs = [
|
||||||
{
|
{
|
||||||
id: "negative_coins",
|
id: "negative_coins",
|
||||||
max: 20,
|
max: 20,
|
||||||
name: t("debuffs.negative_coins.name"),
|
name: (lvl: number) => t("debuffs.negative_coins.help",{lvl}),
|
||||||
help: (lvl: number) => t("debuffs.negative_coins.help", { lvl }),
|
help: (lvl: number) => t("debuffs.negative_coins.help", { lvl }),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "negative_bricks",
|
id: "more_bombs",
|
||||||
max: 20,
|
max: 20,
|
||||||
name: t("debuffs.negative_bricks.name"),
|
name: (lvl: number) => t("debuffs.more_bombs.help", { lvl }),
|
||||||
help: (lvl: number) => t("debuffs.negative_bricks.help", { lvl }),
|
help: (lvl: number) => t("debuffs.more_bombs.help", { lvl }),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "banned",
|
||||||
|
max: 50,
|
||||||
|
name: (lvl: number,banned:string) => t("debuffs.banned.description",{lvl,banned}),
|
||||||
|
help: (lvl: number,perk:string) => t("debuffs.banned.help", { lvl,perk }),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "interference",
|
||||||
|
max: 20,
|
||||||
|
name: (lvl: number) => t("debuffs.interference.help", { lvl }),
|
||||||
|
help: (lvl: number) => t("debuffs.interference.help", { lvl }),
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
] as const as Debuff[];
|
||||||
id: "void_coins_on_touch",
|
|
||||||
max: 1,
|
|
||||||
name: t("debuffs.void_coins_on_touch.name"),
|
|
||||||
help: (lvl: number) => t("debuffs.void_coins_on_touch.help", { lvl }),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "void_brick_on_touch",
|
|
||||||
max: 1,
|
|
||||||
name: t("debuffs.void_brick_on_touch.name"),
|
|
||||||
help: (lvl: number) => t("debuffs.void_brick_on_touch.help", { lvl }),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: "downward_wind",
|
|
||||||
max: 20,
|
|
||||||
name: t("debuffs.downward_wind.name"),
|
|
||||||
help: (lvl: number) => t("debuffs.downward_wind.help", { lvl }),
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
id: "side_wind",
|
|
||||||
max: 20,
|
|
||||||
name: t("debuffs.side_wind.name"),
|
|
||||||
help: (lvl: number) => t("debuffs.side_wind.help", { lvl }),
|
|
||||||
},
|
|
||||||
] as const;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Possible challenges :
|
Possible challenges :
|
||||||
|
|
||||||
- add a force field for 10s that negates hots start
|
- interference : telekinesis works backward for lvl/2 seconds every 5 seconds (show timer ?)
|
||||||
- other perks can be randomly turned off
|
- exclusion : one of your current perks (except the kept one) is banned
|
||||||
- ball keeps accelerating until unplayable
|
- fireworks : some bricks are explosive, you're not told which ones
|
||||||
|
-
|
||||||
|
|
||||||
- graphical effects like trail, contrast, blur to make it harder to see what's going on
|
- graphical effects like trail, contrast, blur to make it harder to see what's going on
|
||||||
- ball creates a draft behind itself that blows coins in odd patterns
|
- ball creates a draft behind itself that blows coins in odd patterns
|
||||||
- bricks are invisible
|
- bricks are invisible
|
||||||
|
- downward wind
|
||||||
|
- side wind
|
||||||
- add red anti-coins that apply downgrades
|
- add red anti-coins that apply downgrades
|
||||||
- destroy your combo
|
- destroy your combo
|
||||||
- hurt your score
|
- hurt your score
|
||||||
|
|
59
src/game.ts
59
src/game.ts
|
@ -13,7 +13,8 @@ import {
|
||||||
} from "./types";
|
} from "./types";
|
||||||
import { getAudioContext, playPendingSounds } from "./sounds";
|
import { getAudioContext, playPendingSounds } from "./sounds";
|
||||||
import {
|
import {
|
||||||
currentLevelInfo,
|
bannedUpgradesHTMl,
|
||||||
|
currentLevelInfo, debuffsHTMl,
|
||||||
getRowColIndex,
|
getRowColIndex,
|
||||||
levelsListHTMl,
|
levelsListHTMl,
|
||||||
max_levels,
|
max_levels,
|
||||||
|
@ -446,25 +447,25 @@ document.addEventListener("visibilitychange", () => {
|
||||||
async function openScorePanel() {
|
async function openScorePanel() {
|
||||||
pause(true);
|
pause(true);
|
||||||
const cb = await asyncAlert({
|
const cb = await asyncAlert({
|
||||||
title: gameState.isAdventureMode
|
title:
|
||||||
? t("score_panel.title_adventure", {
|
gameState.loop ?
|
||||||
|
t("score_panel.title_looped", {
|
||||||
|
loop:gameState.loop,
|
||||||
score: gameState.score,
|
score: gameState.score,
|
||||||
level: gameState.currentLevel + 1,
|
level: gameState.currentLevel + 1,
|
||||||
max: max_levels(gameState),
|
max: max_levels(gameState),
|
||||||
})
|
}):
|
||||||
: t("score_panel.title", {
|
t("score_panel.title", {
|
||||||
score: gameState.score,
|
score: gameState.score,
|
||||||
level: gameState.currentLevel + 1,
|
level: gameState.currentLevel + 1,
|
||||||
max: max_levels(gameState),
|
max: max_levels(gameState),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
content: [
|
content: [
|
||||||
`
|
gameState.isCreativeModeRun ? `<p>${t("score_panel.test_run")}</p>` : "",
|
||||||
${gameState.isCreativeModeRun ? `<p>${t("score_panel.test_run")}</p>` : ""}
|
pickedUpgradesHTMl(gameState),
|
||||||
${pickedUpgradesHTMl(gameState)}
|
levelsListHTMl(gameState),
|
||||||
|
debuffsHTMl(gameState),
|
||||||
<p>${levelsListHTMl(gameState)}</p>
|
|
||||||
`,
|
|
||||||
],
|
],
|
||||||
allowClose: true,
|
allowClose: true,
|
||||||
});
|
});
|
||||||
|
@ -1007,32 +1008,24 @@ restart(
|
||||||
(window.location.search.includes("stressTest") && {
|
(window.location.search.includes("stressTest") && {
|
||||||
level: "Bird",
|
level: "Bird",
|
||||||
perks: {
|
perks: {
|
||||||
sapper: 10,
|
// sapper: 1,
|
||||||
bigger_explosions: 1,
|
// bigger_explosions: 20,
|
||||||
unbounded: 1,
|
// // unbounded: 1,
|
||||||
pierce_color: 1,
|
// // pierce_color: 1,
|
||||||
pierce: 20,
|
// pierce: 1,
|
||||||
multiball: 6,
|
|
||||||
base_combo: 7,
|
// multiball: 6,
|
||||||
|
// base_combo: 7,
|
||||||
telekinesis: 2,
|
telekinesis: 2,
|
||||||
yoyo: 2,
|
yoyo: 2,
|
||||||
metamorphosis: 1,
|
// metamorphosis: 1,
|
||||||
implosions: 1,
|
// implosions: 1,
|
||||||
|
// sturdy_bricks:5
|
||||||
},
|
},
|
||||||
|
debuffs:{
|
||||||
|
|
||||||
|
}
|
||||||
}) ||
|
}) ||
|
||||||
(window.location.search.includes("adventure") && {
|
|
||||||
adventure: true,
|
|
||||||
perks: {
|
|
||||||
// pierce:15
|
|
||||||
},
|
|
||||||
debuffs: {
|
|
||||||
// side_wind:20
|
|
||||||
// negative_bricks:3,
|
|
||||||
// negative_coins:5,
|
|
||||||
// void_coins_on_touch: 1,
|
|
||||||
// void_brick_on_touch: 1,
|
|
||||||
},
|
|
||||||
}) ||
|
|
||||||
{},
|
{},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -136,8 +136,7 @@ export function gameOver(title: string, intro: string) {
|
||||||
],
|
],
|
||||||
}).then(() =>
|
}).then(() =>
|
||||||
restart({
|
restart({
|
||||||
levelToAvoid: currentLevelInfo(gameState).name,
|
levelToAvoid: currentLevelInfo(gameState).name
|
||||||
adventure: gameState.isAdventureMode,
|
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,7 @@
|
||||||
import { Ball, GameState, PerkId, PerksMap } from "./types";
|
import { Ball, GameState, PerkId, PerksMap } from "./types";
|
||||||
import { icons, upgrades } from "./loadGameData";
|
import { icons, upgrades } from "./loadGameData";
|
||||||
import { t } from "./i18n/i18n";
|
import { t } from "./i18n/i18n";
|
||||||
|
import {debuffs} from "./debuffs";
|
||||||
|
|
||||||
export function getMajorityValue(arr: string[]): string {
|
export function getMajorityValue(arr: string[]): string {
|
||||||
const count: { [k: string]: number } = {};
|
const count: { [k: string]: number } = {};
|
||||||
|
@ -54,6 +55,8 @@ export function getPossibleUpgrades(gameState: GameState) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function max_levels(gameState: GameState) {
|
export function max_levels(gameState: GameState) {
|
||||||
|
// TODO
|
||||||
|
return 2
|
||||||
return 7 + gameState.perks.extra_levels;
|
return 7 + gameState.perks.extra_levels;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,14 +64,25 @@ export function pickedUpgradesHTMl(gameState: GameState) {
|
||||||
let list = "";
|
let list = "";
|
||||||
for (let u of upgrades) {
|
for (let u of upgrades) {
|
||||||
for (let i = 0; i < gameState.perks[u.id]; i++)
|
for (let i = 0; i < gameState.perks[u.id]; i++)
|
||||||
list += `<span title="${u.name}">${icons["icon:" + u.id]}</span>`;
|
list += `<span title="${u.name} : ${u.help(gameState.perks[u.id])}">${icons["icon:" + u.id]}</span>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!list) return "";
|
if (!list) return "";
|
||||||
return ` <p>${t("score_panel.upgrades_picked")}</p> <p>${list}</p>`;
|
return ` <p>${t("score_panel.upgrades_picked")}</p> <p>${list}</p>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function debuffsHTMl(gameState: GameState):string {
|
||||||
|
const banned = upgrades.filter(u=>gameState.bannedPerks[u.id]).map(u=>u.name).join(', ')
|
||||||
|
|
||||||
|
|
||||||
|
let list = debuffs.filter(d=>gameState.debuffs[d.id]).map(d=>d.name(gameState.debuffs[d.id], banned)).join(', ');
|
||||||
|
|
||||||
|
if (!list) return "";
|
||||||
|
return `<p>${t("score_panel.bebuffs_list")} : ${list}</p>`;
|
||||||
|
}
|
||||||
|
|
||||||
export function levelsListHTMl(gameState: GameState) {
|
export function levelsListHTMl(gameState: GameState) {
|
||||||
if (gameState.isAdventureMode) return "";
|
|
||||||
if (!gameState.perks.clairvoyant) return "";
|
if (!gameState.perks.clairvoyant) return "";
|
||||||
let list = "";
|
let list = "";
|
||||||
for (let i = 0; i < max_levels(gameState); i++) {
|
for (let i = 0; i < max_levels(gameState); i++) {
|
||||||
|
|
|
@ -88,10 +88,10 @@
|
||||||
<name>debuffs</name>
|
<name>debuffs</name>
|
||||||
<children>
|
<children>
|
||||||
<folder_node>
|
<folder_node>
|
||||||
<name>downward_wind</name>
|
<name>banned</name>
|
||||||
<children>
|
<children>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>help</name>
|
<name>description</name>
|
||||||
<description/>
|
<description/>
|
||||||
<comment/>
|
<comment/>
|
||||||
<translations>
|
<translations>
|
||||||
|
@ -106,7 +106,7 @@
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>name</name>
|
<name>help</name>
|
||||||
<description/>
|
<description/>
|
||||||
<comment/>
|
<comment/>
|
||||||
<translations>
|
<translations>
|
||||||
|
@ -114,6 +114,26 @@
|
||||||
<language>en-US</language>
|
<language>en-US</language>
|
||||||
<approved>true</approved>
|
<approved>true</approved>
|
||||||
</translation>
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-FR</language>
|
||||||
|
<approved>true</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
|
</children>
|
||||||
|
</folder_node>
|
||||||
|
<folder_node>
|
||||||
|
<name>interference</name>
|
||||||
|
<children>
|
||||||
|
<concept_node>
|
||||||
|
<name>help</name>
|
||||||
|
<description/>
|
||||||
|
<comment/>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
<translation>
|
<translation>
|
||||||
<language>fr-FR</language>
|
<language>fr-FR</language>
|
||||||
<approved>false</approved>
|
<approved>false</approved>
|
||||||
|
@ -123,7 +143,7 @@
|
||||||
</children>
|
</children>
|
||||||
</folder_node>
|
</folder_node>
|
||||||
<folder_node>
|
<folder_node>
|
||||||
<name>negative_bricks</name>
|
<name>more_bombs</name>
|
||||||
<children>
|
<children>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>help</name>
|
<name>help</name>
|
||||||
|
@ -140,21 +160,6 @@
|
||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
<concept_node>
|
|
||||||
<name>name</name>
|
|
||||||
<description/>
|
|
||||||
<comment/>
|
|
||||||
<translations>
|
|
||||||
<translation>
|
|
||||||
<language>en-US</language>
|
|
||||||
<approved>true</approved>
|
|
||||||
</translation>
|
|
||||||
<translation>
|
|
||||||
<language>fr-FR</language>
|
|
||||||
<approved>false</approved>
|
|
||||||
</translation>
|
|
||||||
</translations>
|
|
||||||
</concept_node>
|
|
||||||
</children>
|
</children>
|
||||||
</folder_node>
|
</folder_node>
|
||||||
<folder_node>
|
<folder_node>
|
||||||
|
@ -175,126 +180,6 @@
|
||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
<concept_node>
|
|
||||||
<name>name</name>
|
|
||||||
<description/>
|
|
||||||
<comment/>
|
|
||||||
<translations>
|
|
||||||
<translation>
|
|
||||||
<language>en-US</language>
|
|
||||||
<approved>true</approved>
|
|
||||||
</translation>
|
|
||||||
<translation>
|
|
||||||
<language>fr-FR</language>
|
|
||||||
<approved>false</approved>
|
|
||||||
</translation>
|
|
||||||
</translations>
|
|
||||||
</concept_node>
|
|
||||||
</children>
|
|
||||||
</folder_node>
|
|
||||||
<folder_node>
|
|
||||||
<name>side_wind</name>
|
|
||||||
<children>
|
|
||||||
<concept_node>
|
|
||||||
<name>help</name>
|
|
||||||
<description/>
|
|
||||||
<comment/>
|
|
||||||
<translations>
|
|
||||||
<translation>
|
|
||||||
<language>en-US</language>
|
|
||||||
<approved>true</approved>
|
|
||||||
</translation>
|
|
||||||
<translation>
|
|
||||||
<language>fr-FR</language>
|
|
||||||
<approved>false</approved>
|
|
||||||
</translation>
|
|
||||||
</translations>
|
|
||||||
</concept_node>
|
|
||||||
<concept_node>
|
|
||||||
<name>name</name>
|
|
||||||
<description/>
|
|
||||||
<comment/>
|
|
||||||
<translations>
|
|
||||||
<translation>
|
|
||||||
<language>en-US</language>
|
|
||||||
<approved>true</approved>
|
|
||||||
</translation>
|
|
||||||
<translation>
|
|
||||||
<language>fr-FR</language>
|
|
||||||
<approved>false</approved>
|
|
||||||
</translation>
|
|
||||||
</translations>
|
|
||||||
</concept_node>
|
|
||||||
</children>
|
|
||||||
</folder_node>
|
|
||||||
<folder_node>
|
|
||||||
<name>void_brick_on_touch</name>
|
|
||||||
<children>
|
|
||||||
<concept_node>
|
|
||||||
<name>help</name>
|
|
||||||
<description/>
|
|
||||||
<comment/>
|
|
||||||
<translations>
|
|
||||||
<translation>
|
|
||||||
<language>en-US</language>
|
|
||||||
<approved>true</approved>
|
|
||||||
</translation>
|
|
||||||
<translation>
|
|
||||||
<language>fr-FR</language>
|
|
||||||
<approved>false</approved>
|
|
||||||
</translation>
|
|
||||||
</translations>
|
|
||||||
</concept_node>
|
|
||||||
<concept_node>
|
|
||||||
<name>name</name>
|
|
||||||
<description/>
|
|
||||||
<comment/>
|
|
||||||
<translations>
|
|
||||||
<translation>
|
|
||||||
<language>en-US</language>
|
|
||||||
<approved>true</approved>
|
|
||||||
</translation>
|
|
||||||
<translation>
|
|
||||||
<language>fr-FR</language>
|
|
||||||
<approved>false</approved>
|
|
||||||
</translation>
|
|
||||||
</translations>
|
|
||||||
</concept_node>
|
|
||||||
</children>
|
|
||||||
</folder_node>
|
|
||||||
<folder_node>
|
|
||||||
<name>void_coins_on_touch</name>
|
|
||||||
<children>
|
|
||||||
<concept_node>
|
|
||||||
<name>help</name>
|
|
||||||
<description/>
|
|
||||||
<comment/>
|
|
||||||
<translations>
|
|
||||||
<translation>
|
|
||||||
<language>en-US</language>
|
|
||||||
<approved>true</approved>
|
|
||||||
</translation>
|
|
||||||
<translation>
|
|
||||||
<language>fr-FR</language>
|
|
||||||
<approved>false</approved>
|
|
||||||
</translation>
|
|
||||||
</translations>
|
|
||||||
</concept_node>
|
|
||||||
<concept_node>
|
|
||||||
<name>name</name>
|
|
||||||
<description/>
|
|
||||||
<comment/>
|
|
||||||
<translations>
|
|
||||||
<translation>
|
|
||||||
<language>en-US</language>
|
|
||||||
<approved>true</approved>
|
|
||||||
</translation>
|
|
||||||
<translation>
|
|
||||||
<language>fr-FR</language>
|
|
||||||
<approved>false</approved>
|
|
||||||
</translation>
|
|
||||||
</translations>
|
|
||||||
</concept_node>
|
|
||||||
</children>
|
</children>
|
||||||
</folder_node>
|
</folder_node>
|
||||||
</children>
|
</children>
|
||||||
|
@ -849,6 +734,41 @@
|
||||||
</concept_node>
|
</concept_node>
|
||||||
</children>
|
</children>
|
||||||
</folder_node>
|
</folder_node>
|
||||||
|
<folder_node>
|
||||||
|
<name>loop</name>
|
||||||
|
<children>
|
||||||
|
<concept_node>
|
||||||
|
<name>instructions</name>
|
||||||
|
<description/>
|
||||||
|
<comment/>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-FR</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>title</name>
|
||||||
|
<description/>
|
||||||
|
<comment/>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-FR</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
|
</children>
|
||||||
|
</folder_node>
|
||||||
<folder_node>
|
<folder_node>
|
||||||
<name>main_menu</name>
|
<name>main_menu</name>
|
||||||
<children>
|
<children>
|
||||||
|
@ -882,6 +802,36 @@
|
||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>colorful_coins</name>
|
||||||
|
<description/>
|
||||||
|
<comment/>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-FR</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>colorful_coins_help</name>
|
||||||
|
<description/>
|
||||||
|
<comment/>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-FR</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>download_save_file</name>
|
<name>download_save_file</name>
|
||||||
<description/>
|
<description/>
|
||||||
|
@ -1497,6 +1447,36 @@
|
||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>show_stats</name>
|
||||||
|
<description/>
|
||||||
|
<comment/>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-FR</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>show_stats_help</name>
|
||||||
|
<description/>
|
||||||
|
<comment/>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-FR</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>sounds</name>
|
<name>sounds</name>
|
||||||
<description/>
|
<description/>
|
||||||
|
@ -1608,7 +1588,7 @@
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>current_lvl_adventure</name>
|
<name>current_lvl_loop</name>
|
||||||
<description/>
|
<description/>
|
||||||
<comment/>
|
<comment/>
|
||||||
<translations>
|
<translations>
|
||||||
|
@ -1672,36 +1652,6 @@
|
||||||
<folder_node>
|
<folder_node>
|
||||||
<name>premium</name>
|
<name>premium</name>
|
||||||
<children>
|
<children>
|
||||||
<concept_node>
|
|
||||||
<name>adventure_mode</name>
|
|
||||||
<description/>
|
|
||||||
<comment/>
|
|
||||||
<translations>
|
|
||||||
<translation>
|
|
||||||
<language>en-US</language>
|
|
||||||
<approved>true</approved>
|
|
||||||
</translation>
|
|
||||||
<translation>
|
|
||||||
<language>fr-FR</language>
|
|
||||||
<approved>true</approved>
|
|
||||||
</translation>
|
|
||||||
</translations>
|
|
||||||
</concept_node>
|
|
||||||
<concept_node>
|
|
||||||
<name>adventure_mode_help</name>
|
|
||||||
<description/>
|
|
||||||
<comment/>
|
|
||||||
<translations>
|
|
||||||
<translation>
|
|
||||||
<language>en-US</language>
|
|
||||||
<approved>true</approved>
|
|
||||||
</translation>
|
|
||||||
<translation>
|
|
||||||
<language>fr-FR</language>
|
|
||||||
<approved>true</approved>
|
|
||||||
</translation>
|
|
||||||
</translations>
|
|
||||||
</concept_node>
|
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>back</name>
|
<name>back</name>
|
||||||
<description/>
|
<description/>
|
||||||
|
@ -1777,51 +1727,6 @@
|
||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
<concept_node>
|
|
||||||
<name>choose_next_step</name>
|
|
||||||
<description/>
|
|
||||||
<comment/>
|
|
||||||
<translations>
|
|
||||||
<translation>
|
|
||||||
<language>en-US</language>
|
|
||||||
<approved>true</approved>
|
|
||||||
</translation>
|
|
||||||
<translation>
|
|
||||||
<language>fr-FR</language>
|
|
||||||
<approved>false</approved>
|
|
||||||
</translation>
|
|
||||||
</translations>
|
|
||||||
</concept_node>
|
|
||||||
<concept_node>
|
|
||||||
<name>discard</name>
|
|
||||||
<description/>
|
|
||||||
<comment/>
|
|
||||||
<translations>
|
|
||||||
<translation>
|
|
||||||
<language>en-US</language>
|
|
||||||
<approved>true</approved>
|
|
||||||
</translation>
|
|
||||||
<translation>
|
|
||||||
<language>fr-FR</language>
|
|
||||||
<approved>false</approved>
|
|
||||||
</translation>
|
|
||||||
</translations>
|
|
||||||
</concept_node>
|
|
||||||
<concept_node>
|
|
||||||
<name>discard_help</name>
|
|
||||||
<description/>
|
|
||||||
<comment/>
|
|
||||||
<translations>
|
|
||||||
<translation>
|
|
||||||
<language>en-US</language>
|
|
||||||
<approved>true</approved>
|
|
||||||
</translation>
|
|
||||||
<translation>
|
|
||||||
<language>fr-FR</language>
|
|
||||||
<approved>false</approved>
|
|
||||||
</translation>
|
|
||||||
</translations>
|
|
||||||
</concept_node>
|
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>enter</name>
|
<name>enter</name>
|
||||||
<description/>
|
<description/>
|
||||||
|
@ -1883,13 +1788,13 @@
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>next_step_title</name>
|
<name>per_hours</name>
|
||||||
<description/>
|
<description/>
|
||||||
<comment/>
|
<comment/>
|
||||||
<translations>
|
<translations>
|
||||||
<translation>
|
<translation>
|
||||||
<language>en-US</language>
|
<language>en-US</language>
|
||||||
<approved>true</approved>
|
<approved>false</approved>
|
||||||
</translation>
|
</translation>
|
||||||
<translation>
|
<translation>
|
||||||
<language>fr-FR</language>
|
<language>fr-FR</language>
|
||||||
|
@ -1898,13 +1803,13 @@
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>pick_level</name>
|
<name>per_hours_help</name>
|
||||||
<description/>
|
<description/>
|
||||||
<comment/>
|
<comment/>
|
||||||
<translations>
|
<translations>
|
||||||
<translation>
|
<translation>
|
||||||
<language>en-US</language>
|
<language>en-US</language>
|
||||||
<approved>true</approved>
|
<approved>false</approved>
|
||||||
</translation>
|
</translation>
|
||||||
<translation>
|
<translation>
|
||||||
<language>fr-FR</language>
|
<language>fr-FR</language>
|
||||||
|
@ -1913,13 +1818,13 @@
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>pick_perk</name>
|
<name>thanks</name>
|
||||||
<description/>
|
<description/>
|
||||||
<comment/>
|
<comment/>
|
||||||
<translations>
|
<translations>
|
||||||
<translation>
|
<translation>
|
||||||
<language>en-US</language>
|
<language>en-US</language>
|
||||||
<approved>true</approved>
|
<approved>false</approved>
|
||||||
</translation>
|
</translation>
|
||||||
<translation>
|
<translation>
|
||||||
<language>fr-FR</language>
|
<language>fr-FR</language>
|
||||||
|
@ -1928,28 +1833,13 @@
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>short_help</name>
|
<name>thanks_help</name>
|
||||||
<description/>
|
<description/>
|
||||||
<comment/>
|
<comment/>
|
||||||
<translations>
|
<translations>
|
||||||
<translation>
|
<translation>
|
||||||
<language>en-US</language>
|
<language>en-US</language>
|
||||||
<approved>true</approved>
|
<approved>false</approved>
|
||||||
</translation>
|
|
||||||
<translation>
|
|
||||||
<language>fr-FR</language>
|
|
||||||
<approved>true</approved>
|
|
||||||
</translation>
|
|
||||||
</translations>
|
|
||||||
</concept_node>
|
|
||||||
<concept_node>
|
|
||||||
<name>upgrade_perk_to_level</name>
|
|
||||||
<description/>
|
|
||||||
<comment/>
|
|
||||||
<translations>
|
|
||||||
<translation>
|
|
||||||
<language>en-US</language>
|
|
||||||
<approved>true</approved>
|
|
||||||
</translation>
|
</translation>
|
||||||
<translation>
|
<translation>
|
||||||
<language>fr-FR</language>
|
<language>fr-FR</language>
|
||||||
|
@ -1958,13 +1848,13 @@
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>your_upgrades</name>
|
<name>title</name>
|
||||||
<description/>
|
<description/>
|
||||||
<comment/>
|
<comment/>
|
||||||
<translations>
|
<translations>
|
||||||
<translation>
|
<translation>
|
||||||
<language>en-US</language>
|
<language>en-US</language>
|
||||||
<approved>true</approved>
|
<approved>false</approved>
|
||||||
</translation>
|
</translation>
|
||||||
<translation>
|
<translation>
|
||||||
<language>fr-FR</language>
|
<language>fr-FR</language>
|
||||||
|
@ -2058,62 +1948,17 @@
|
||||||
<name>score_panel</name>
|
<name>score_panel</name>
|
||||||
<children>
|
<children>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>restart</name>
|
<name>bebuffs_list</name>
|
||||||
<description/>
|
<description/>
|
||||||
<comment/>
|
<comment/>
|
||||||
<translations>
|
<translations>
|
||||||
<translation>
|
<translation>
|
||||||
<language>en-US</language>
|
<language>en-US</language>
|
||||||
<approved>true</approved>
|
<approved>false</approved>
|
||||||
</translation>
|
</translation>
|
||||||
<translation>
|
<translation>
|
||||||
<language>fr-FR</language>
|
<language>fr-FR</language>
|
||||||
<approved>true</approved>
|
<approved>false</approved>
|
||||||
</translation>
|
|
||||||
</translations>
|
|
||||||
</concept_node>
|
|
||||||
<concept_node>
|
|
||||||
<name>restart_help</name>
|
|
||||||
<description/>
|
|
||||||
<comment/>
|
|
||||||
<translations>
|
|
||||||
<translation>
|
|
||||||
<language>en-US</language>
|
|
||||||
<approved>true</approved>
|
|
||||||
</translation>
|
|
||||||
<translation>
|
|
||||||
<language>fr-FR</language>
|
|
||||||
<approved>true</approved>
|
|
||||||
</translation>
|
|
||||||
</translations>
|
|
||||||
</concept_node>
|
|
||||||
<concept_node>
|
|
||||||
<name>resume</name>
|
|
||||||
<description/>
|
|
||||||
<comment/>
|
|
||||||
<translations>
|
|
||||||
<translation>
|
|
||||||
<language>en-US</language>
|
|
||||||
<approved>true</approved>
|
|
||||||
</translation>
|
|
||||||
<translation>
|
|
||||||
<language>fr-FR</language>
|
|
||||||
<approved>true</approved>
|
|
||||||
</translation>
|
|
||||||
</translations>
|
|
||||||
</concept_node>
|
|
||||||
<concept_node>
|
|
||||||
<name>resume_help</name>
|
|
||||||
<description/>
|
|
||||||
<comment/>
|
|
||||||
<translations>
|
|
||||||
<translation>
|
|
||||||
<language>en-US</language>
|
|
||||||
<approved>true</approved>
|
|
||||||
</translation>
|
|
||||||
<translation>
|
|
||||||
<language>fr-FR</language>
|
|
||||||
<approved>true</approved>
|
|
||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
@ -2148,7 +1993,7 @@
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>title_adventure</name>
|
<name>title_looped</name>
|
||||||
<description/>
|
<description/>
|
||||||
<comment/>
|
<comment/>
|
||||||
<translations>
|
<translations>
|
||||||
|
|
|
@ -3,18 +3,11 @@
|
||||||
"confirmRestart.text": "You're about to start a new run, is that really what you wanted ?",
|
"confirmRestart.text": "You're about to start a new run, is that really what you wanted ?",
|
||||||
"confirmRestart.title": "Start a new run ?",
|
"confirmRestart.title": "Start a new run ?",
|
||||||
"confirmRestart.yes": "Restart game",
|
"confirmRestart.yes": "Restart game",
|
||||||
"debuffs.downward_wind.help": "A strong wind sends the ball back down for the first {{lvl}}s of each level.",
|
"debuffs.banned.description": "{{lvl}} banned perk(s) : {{banned}}",
|
||||||
"debuffs.downward_wind.name": "Downward wind",
|
"debuffs.banned.help": "{{perk}} is banned for this run",
|
||||||
"debuffs.negative_bricks.help": "{{lvl}} bricks on each level are replaced by negative bricks that break the combo",
|
"debuffs.interference.help": "Telekinesis and yo-yo stop working for {{lvl}}s every 7s",
|
||||||
"debuffs.negative_bricks.name": "Void bricks",
|
"debuffs.more_bombs.help": "{{lvl}} bricks replaced by bombs",
|
||||||
"debuffs.negative_coins.help": "{{lvl}}% of coins spawn empty and break the combo if caught",
|
"debuffs.negative_coins.help": "{{lvl}}% of coins spawn void and break the combo if caught",
|
||||||
"debuffs.negative_coins.name": "Void coins",
|
|
||||||
"debuffs.side_wind.help": "A strong wind sends the ball and coins to one of the sides",
|
|
||||||
"debuffs.side_wind.name": "Side wind",
|
|
||||||
"debuffs.void_brick_on_touch.help": "Bricks touched by void coins become void",
|
|
||||||
"debuffs.void_brick_on_touch.name": "Bricks become void",
|
|
||||||
"debuffs.void_coins_on_touch.help": "Coins that touch void bricks become void",
|
|
||||||
"debuffs.void_coins_on_touch.name": "Coins become void",
|
|
||||||
"gameOver.cumulative_total": "Your total cumulative score went from {{startTs}} to {{endTs}}.",
|
"gameOver.cumulative_total": "Your total cumulative score went from {{startTs}} to {{endTs}}.",
|
||||||
"gameOver.lost.summary": "You dropped the ball after catching {{score}} coins.",
|
"gameOver.lost.summary": "You dropped the ball after catching {{score}} coins.",
|
||||||
"gameOver.lost.title": "Game Over",
|
"gameOver.lost.title": "Game Over",
|
||||||
|
@ -50,8 +43,12 @@
|
||||||
"level_up.unlocked_level": " (Level)",
|
"level_up.unlocked_level": " (Level)",
|
||||||
"level_up.unlocked_perk": " (Perk)",
|
"level_up.unlocked_perk": " (Perk)",
|
||||||
"level_up.upgrade_perk_to_level": " lvl {{level}}",
|
"level_up.upgrade_perk_to_level": " lvl {{level}}",
|
||||||
|
"loop.instructions": "All your perks will be erased except one that you can pick below. Each option comes with an additional hazard will appear on all levels going forward. ",
|
||||||
|
"loop.title": "Starting loop {{loop}}",
|
||||||
"main_menu.basic": "Basic graphics",
|
"main_menu.basic": "Basic graphics",
|
||||||
"main_menu.basic_help": "Better performance.",
|
"main_menu.basic_help": "Better performance.",
|
||||||
|
"main_menu.colorful_coins": "Colorful coins",
|
||||||
|
"main_menu.colorful_coins_help": "Coins always spawn of the color of the brick",
|
||||||
"main_menu.download_save_file": "Download score and stats",
|
"main_menu.download_save_file": "Download score and stats",
|
||||||
"main_menu.download_save_file_help": "Get a save file",
|
"main_menu.download_save_file_help": "Get a save file",
|
||||||
"main_menu.footer_html": "<p> \n<span>Made in France by <a href=\"https://lecaro.me\">Renan LE CARO</a>.</span> \n<a href=\"https://paypal.me/renanlecaro\" target=\"_blank\">Donate</a>\n<a href=\"https://discord.gg/DZSPqyJkwP\" target=\"_blank\">Discord</a>\n<a href=\"https://f-droid.org/en/packages/me.lecaro.breakout/\" target=\"_blank\">F-Droid</a>\n<a href=\"https://play.google.com/store/apps/details?id=me.lecaro.breakout\" target=\"_blank\">Google Play</a>\n<a href=\"https://renanlecaro.itch.io/breakout71\" target=\"_blank\">itch.io</a> \n<a href=\"https://gitlab.com/lecarore/breakout71\" target=\"_blank\">Gitlab</a>\n<a href=\"https://breakout.lecaro.me/\" target=\"_blank\">Web version</a>\n<a href=\"https://news.ycombinator.com/item?id=43183131\" target=\"_blank\">HackerNews</a>\n<a href=\"https://breakout.lecaro.me/privacy.html\" target=\"_blank\">Privacy Policy</a>\n<span>v.{{appVersion}}</span>\n</p>\n",
|
"main_menu.footer_html": "<p> \n<span>Made in France by <a href=\"https://lecaro.me\">Renan LE CARO</a>.</span> \n<a href=\"https://paypal.me/renanlecaro\" target=\"_blank\">Donate</a>\n<a href=\"https://discord.gg/DZSPqyJkwP\" target=\"_blank\">Discord</a>\n<a href=\"https://f-droid.org/en/packages/me.lecaro.breakout/\" target=\"_blank\">F-Droid</a>\n<a href=\"https://play.google.com/store/apps/details?id=me.lecaro.breakout\" target=\"_blank\">Google Play</a>\n<a href=\"https://renanlecaro.itch.io/breakout71\" target=\"_blank\">itch.io</a> \n<a href=\"https://gitlab.com/lecarore/breakout71\" target=\"_blank\">Gitlab</a>\n<a href=\"https://breakout.lecaro.me/\" target=\"_blank\">Web version</a>\n<a href=\"https://news.ycombinator.com/item?id=43183131\" target=\"_blank\">HackerNews</a>\n<a href=\"https://breakout.lecaro.me/privacy.html\" target=\"_blank\">Privacy Policy</a>\n<span>v.{{appVersion}}</span>\n</p>\n",
|
||||||
|
@ -93,49 +90,42 @@
|
||||||
"main_menu.settings_title": "Settings",
|
"main_menu.settings_title": "Settings",
|
||||||
"main_menu.show_fps": "FPS counter",
|
"main_menu.show_fps": "FPS counter",
|
||||||
"main_menu.show_fps_help": "Monitor the app's performance",
|
"main_menu.show_fps_help": "Monitor the app's performance",
|
||||||
|
"main_menu.show_stats": "Show real time stats",
|
||||||
|
"main_menu.show_stats_help": "Coins, time, bounces, misses",
|
||||||
"main_menu.sounds": "Game sounds",
|
"main_menu.sounds": "Game sounds",
|
||||||
"main_menu.sounds_help": "Can slow down some phones.",
|
"main_menu.sounds_help": "Can slow down some phones.",
|
||||||
"main_menu.title": "Breakout 71",
|
"main_menu.title": "Breakout 71",
|
||||||
"main_menu.unlocks": "Unlocked content",
|
"main_menu.unlocks": "Unlocked content",
|
||||||
"main_menu.unlocks_help": "Try perks and levels you unlocked",
|
"main_menu.unlocks_help": "Try perks and levels you unlocked",
|
||||||
"play.close_modale_window_tooltip": "close ",
|
"play.close_modale_window_tooltip": "close ",
|
||||||
"play.current_lvl": "L{{level}}/{{max}}",
|
"play.current_lvl": "Level {{level}}/{{max}}",
|
||||||
"play.current_lvl_adventure": "L {{level}}",
|
"play.current_lvl_loop": "Level {{level}}/{{max}} loop {{loop}}",
|
||||||
"play.menu_label": "menu",
|
"play.menu_label": "menu",
|
||||||
"play.missed_ball": "miss",
|
"play.missed_ball": "miss",
|
||||||
"play.mobile_press_to_play": "Press and hold here to play",
|
"play.mobile_press_to_play": "Press and hold here to play",
|
||||||
"premium.adventure_mode": "Infinite mode",
|
|
||||||
"premium.adventure_mode_help": "Start a new game in infinite mode",
|
|
||||||
"premium.back": "Back",
|
"premium.back": "Back",
|
||||||
"premium.back_help": "Return to main menu",
|
"premium.back_help": "Return to main menu",
|
||||||
"premium.buy": "Buy a license key",
|
"premium.buy": "Buy a license key",
|
||||||
"premium.buy_disabled_help": "Coming soon",
|
"premium.buy_disabled_help": "Coming soon",
|
||||||
"premium.buy_help": "You'll be taken to a stripe form to pay and will receive the license by email. Come back to enter it here after.",
|
"premium.buy_help": "You'll be taken to a stripe form to pay and will receive the license by email. Come back to enter it here after.",
|
||||||
"premium.choose_next_step": "You have ${{score}}. Click any upgrades you want to buy.",
|
|
||||||
"premium.discard": "Discard perk {{name}}",
|
|
||||||
"premium.discard_help": "Will make other perks cheaper",
|
|
||||||
"premium.enter": "Enter license key",
|
"premium.enter": "Enter license key",
|
||||||
"premium.enter_help": "Paste the license in the window that opens",
|
"premium.enter_help": "Paste the license in the window that opens",
|
||||||
"premium.help": "Buy a license for Breakout 71 to unlock infinite mode and support development. It costs 4.99€ and lasts forever. You can use it on multiple devices, but please don't share it online. ",
|
"premium.help": "Buy a license for Breakout 71 to unlock looping and support development. It costs 4.99€ and lasts forever. You can use it on multiple devices, but please don't share it online. ",
|
||||||
"premium.help_google": "While I do plan to offer premium licenses through google play, I haven't gotten around it yet, so there's no buy link here. If you already have a license key, you can enter it below. ",
|
"premium.help_google": "While I do plan to offer premium licenses through google play, I haven't gotten around it yet, so there's no buy link here. If you already have a license key, you can enter it below. ",
|
||||||
"premium.next_step_title": "Buy upgrades and continue to next level",
|
"premium.per_hours": "You've played for {{hours}} hours",
|
||||||
"premium.pick_level": "Go to level \"{{name}}\" for ${{cost}}",
|
"premium.per_hours_help": "Donate 4.99€ to get premium",
|
||||||
"premium.pick_perk": "Get {{name}} for ${{cost}}",
|
"premium.thanks": "You are premium, thanks ! ",
|
||||||
"premium.short_help": "Play as long as possible",
|
"premium.thanks_help": "Copy your license key",
|
||||||
"premium.upgrade_perk_to_level": "Upgrade {{name}} to {{lvl}} for ${{cost}}",
|
"premium.title": "Unlock looping with premium ",
|
||||||
"premium.your_upgrades": "Your upgrades so far : ",
|
|
||||||
"sandbox.help": "Test any perk combination",
|
"sandbox.help": "Test any perk combination",
|
||||||
"sandbox.instructions": "Select perks below and press \"start run\" to try them out in a test run. Scores and stats are not recorded.",
|
"sandbox.instructions": "Select perks below and press \"start run\" to try them out in a test run. Scores and stats are not recorded.",
|
||||||
"sandbox.start": "Start test run",
|
"sandbox.start": "Start test run",
|
||||||
"sandbox.title": "Sandbox mode",
|
"sandbox.title": "Sandbox mode",
|
||||||
"sandbox.unlocks_at": "Unlocks at total score {{score}}",
|
"sandbox.unlocks_at": "Unlocks at total score {{score}}",
|
||||||
"score_panel.restart": "Restart",
|
"score_panel.bebuffs_list": "Debuffs :",
|
||||||
"score_panel.restart_help": "Start a brand new run",
|
|
||||||
"score_panel.resume": "Resume",
|
|
||||||
"score_panel.resume_help": "Return to your run",
|
|
||||||
"score_panel.test_run": "This is a test run, score is not recorded permanently",
|
"score_panel.test_run": "This is a test run, score is not recorded permanently",
|
||||||
"score_panel.title": "{{score}} points at level {{level}}/{{max}} ",
|
"score_panel.title": "{{score}} points at level {{level}}/{{max}} ",
|
||||||
"score_panel.title_adventure": "{{score}} points at level {{level}} of your adventure",
|
"score_panel.title_looped": "{{score}} points at level {{level}}/{{max}} of loop {{loop}}",
|
||||||
"score_panel.upcoming_levels": "Upcoming levels :",
|
"score_panel.upcoming_levels": "Upcoming levels :",
|
||||||
"score_panel.upgrades_picked": "Upgrades picked so far : ",
|
"score_panel.upgrades_picked": "Upgrades picked so far : ",
|
||||||
"unlocks.greyed_out_help": "The greyed out ones can be unlocked by increasing your total score. The total score increases every time you score in game.",
|
"unlocks.greyed_out_help": "The greyed out ones can be unlocked by increasing your total score. The total score increases every time you score in game.",
|
||||||
|
|
|
@ -3,18 +3,11 @@
|
||||||
"confirmRestart.text": "Vous êtes sur le point de commencer une nouvelle partie, est-ce vraiment ce que vous vouliez ?",
|
"confirmRestart.text": "Vous êtes sur le point de commencer une nouvelle partie, est-ce vraiment ce que vous vouliez ?",
|
||||||
"confirmRestart.title": "Démarrer une nouvelle partie ?",
|
"confirmRestart.title": "Démarrer une nouvelle partie ?",
|
||||||
"confirmRestart.yes": "Commencer une nouvelle partie",
|
"confirmRestart.yes": "Commencer une nouvelle partie",
|
||||||
"debuffs.downward_wind.help": "Un vent fort renvoie la balle vers le bas pendant les {{lvl}}premières secondes de chaque niveau.",
|
"debuffs.banned.description": "{{lvl}} amélioration(s) bannie(s) : {{banned}}",
|
||||||
"debuffs.downward_wind.name": "Vent descendant",
|
"debuffs.banned.help": "",
|
||||||
"debuffs.negative_bricks.help": "{{lvl}} briques à chaque niveau sont remplacées par des briques négatives qui brisent le combo",
|
"debuffs.interference.help": "",
|
||||||
"debuffs.negative_bricks.name": "Briques vides",
|
"debuffs.more_bombs.help": "",
|
||||||
"debuffs.negative_coins.help": " ",
|
"debuffs.negative_coins.help": "",
|
||||||
"debuffs.negative_coins.name": "Pièces du vide",
|
|
||||||
"debuffs.side_wind.help": "Un vent fort envoie la balle et les pièces vers l'un des côtés",
|
|
||||||
"debuffs.side_wind.name": "Vent latéral",
|
|
||||||
"debuffs.void_brick_on_touch.help": "Les briques touchées par des pièces vides deviennent vides",
|
|
||||||
"debuffs.void_brick_on_touch.name": "Les briques deviennent vides",
|
|
||||||
"debuffs.void_coins_on_touch.help": "Les pièces qui touchent des briques vides deviennent nulles",
|
|
||||||
"debuffs.void_coins_on_touch.name": "Les pièces deviennent nulles",
|
|
||||||
"gameOver.cumulative_total": "Votre score total cumulé est passé de {{startTs}} à {{endTs}}.",
|
"gameOver.cumulative_total": "Votre score total cumulé est passé de {{startTs}} à {{endTs}}.",
|
||||||
"gameOver.lost.summary": "Vous avez fait tomber la balle après avoir attrapé {{score}} pièces.",
|
"gameOver.lost.summary": "Vous avez fait tomber la balle après avoir attrapé {{score}} pièces.",
|
||||||
"gameOver.lost.title": "Balle perdue",
|
"gameOver.lost.title": "Balle perdue",
|
||||||
|
@ -50,8 +43,12 @@
|
||||||
"level_up.unlocked_level": " (Niveau)",
|
"level_up.unlocked_level": " (Niveau)",
|
||||||
"level_up.unlocked_perk": " (Amélioration)",
|
"level_up.unlocked_perk": " (Amélioration)",
|
||||||
"level_up.upgrade_perk_to_level": " niveau {{level}}",
|
"level_up.upgrade_perk_to_level": " niveau {{level}}",
|
||||||
|
"loop.instructions": "",
|
||||||
|
"loop.title": "",
|
||||||
"main_menu.basic": "Graphismes simplifiés",
|
"main_menu.basic": "Graphismes simplifiés",
|
||||||
"main_menu.basic_help": "Meilleures performances.",
|
"main_menu.basic_help": "Meilleures performances.",
|
||||||
|
"main_menu.colorful_coins": "",
|
||||||
|
"main_menu.colorful_coins_help": "",
|
||||||
"main_menu.download_save_file": "Sauvegarder mes progrès",
|
"main_menu.download_save_file": "Sauvegarder mes progrès",
|
||||||
"main_menu.download_save_file_help": "Obtenir un fichier de sauvegarde",
|
"main_menu.download_save_file_help": "Obtenir un fichier de sauvegarde",
|
||||||
"main_menu.footer_html": " <p> \n<span>Programmé en France par <a href=\"https://lecaro.me\">Renan LE CARO</a>.</span>\n<a href=\"https://paypal.me/renanlecaro\" target=\"_blank\">Donner</a>\n<a href=\"https://discord.gg/DZSPqyJkwP\" target=\"_blank\">Discord</a>\n<a href=\"https://f-droid.org/en/packages/me.lecaro.breakout/\" target=\"_blank\">F-Droid</a>\n<a href=\"https://play.google.com/store/apps/details?id=me.lecaro.breakout\" target=\"_blank\">Google Play</a>\n<a href=\"https://renanlecaro.itch.io/breakout71\" target=\"_blank\">itch.io</a>\n<a href=\"https://gitlab.com/lecarore/breakout71\" target=\"_blank\">Gitlab</a>\n<a href=\"https://breakout.lecaro.me/\" target=\"_blank\">Version web</a>\n<a href=\"https://news.ycombinator.com/item?id=43183131\" target=\"_blank\">HackerNews</a>\n<a href=\"https://breakout.lecaro.me/privacy.html\" target=\"_blank\">Politique de confidentialité</a> \n<span>v.{{appVersion}}</span>\n</p>",
|
"main_menu.footer_html": " <p> \n<span>Programmé en France par <a href=\"https://lecaro.me\">Renan LE CARO</a>.</span>\n<a href=\"https://paypal.me/renanlecaro\" target=\"_blank\">Donner</a>\n<a href=\"https://discord.gg/DZSPqyJkwP\" target=\"_blank\">Discord</a>\n<a href=\"https://f-droid.org/en/packages/me.lecaro.breakout/\" target=\"_blank\">F-Droid</a>\n<a href=\"https://play.google.com/store/apps/details?id=me.lecaro.breakout\" target=\"_blank\">Google Play</a>\n<a href=\"https://renanlecaro.itch.io/breakout71\" target=\"_blank\">itch.io</a>\n<a href=\"https://gitlab.com/lecarore/breakout71\" target=\"_blank\">Gitlab</a>\n<a href=\"https://breakout.lecaro.me/\" target=\"_blank\">Version web</a>\n<a href=\"https://news.ycombinator.com/item?id=43183131\" target=\"_blank\">HackerNews</a>\n<a href=\"https://breakout.lecaro.me/privacy.html\" target=\"_blank\">Politique de confidentialité</a> \n<span>v.{{appVersion}}</span>\n</p>",
|
||||||
|
@ -93,6 +90,8 @@
|
||||||
"main_menu.settings_title": "Paramètre",
|
"main_menu.settings_title": "Paramètre",
|
||||||
"main_menu.show_fps": "Compteur de FPS",
|
"main_menu.show_fps": "Compteur de FPS",
|
||||||
"main_menu.show_fps_help": "Surveiller la perf du jeu",
|
"main_menu.show_fps_help": "Surveiller la perf du jeu",
|
||||||
|
"main_menu.show_stats": "",
|
||||||
|
"main_menu.show_stats_help": "",
|
||||||
"main_menu.sounds": "Sons du jeu",
|
"main_menu.sounds": "Sons du jeu",
|
||||||
"main_menu.sounds_help": "Ralentis certains téléphones.",
|
"main_menu.sounds_help": "Ralentis certains téléphones.",
|
||||||
"main_menu.title": "Breakout 71",
|
"main_menu.title": "Breakout 71",
|
||||||
|
@ -100,42 +99,33 @@
|
||||||
"main_menu.unlocks_help": "Essayez les éléments débloqués",
|
"main_menu.unlocks_help": "Essayez les éléments débloqués",
|
||||||
"play.close_modale_window_tooltip": "Fermer",
|
"play.close_modale_window_tooltip": "Fermer",
|
||||||
"play.current_lvl": "Niveau {{level}}/{{max}}",
|
"play.current_lvl": "Niveau {{level}}/{{max}}",
|
||||||
"play.current_lvl_adventure": "Niveau {{level}}",
|
"play.current_lvl_loop": "Niveau {{level}}/{{max}} boucle {{loop}}",
|
||||||
"play.menu_label": "Menu",
|
"play.menu_label": "Menu",
|
||||||
"play.missed_ball": "raté",
|
"play.missed_ball": "raté",
|
||||||
"play.mobile_press_to_play": "Gardez le doigt ici pour jouer",
|
"play.mobile_press_to_play": "Gardez le doigt ici pour jouer",
|
||||||
"premium.adventure_mode": "Mode sans fin",
|
|
||||||
"premium.adventure_mode_help": "Démarrer une nouvelle partie sans fin",
|
|
||||||
"premium.back": "Retour",
|
"premium.back": "Retour",
|
||||||
"premium.back_help": "Retour au menu principal",
|
"premium.back_help": "Retour au menu principal",
|
||||||
"premium.buy": "Acheter une clé de licence",
|
"premium.buy": "Acheter une clé de licence",
|
||||||
"premium.buy_disabled_help": "À venir",
|
"premium.buy_disabled_help": "À venir",
|
||||||
"premium.buy_help": "Vous serez redirigé vers un formulaire pour payer et recevrez la licence par e-mail. Revenez ensuite pour la saisir ici.",
|
"premium.buy_help": "Vous serez redirigé vers un formulaire pour payer et recevrez la licence par e-mail. Revenez ensuite pour la saisir ici.",
|
||||||
"premium.choose_next_step": "Vous disposez de{{score}}$. Cliquez sur les améliorations que vous souhaitez acheter.",
|
|
||||||
"premium.discard": "Abandonner l'avantage {{name}}",
|
|
||||||
"premium.discard_help": "Cela rendra d'autres avantages moins chers",
|
|
||||||
"premium.enter": "Entrez la clé de licence",
|
"premium.enter": "Entrez la clé de licence",
|
||||||
"premium.enter_help": "Collez la licence dans la fenêtre qui s'ouvre",
|
"premium.enter_help": "Collez la licence dans la fenêtre qui s'ouvre",
|
||||||
"premium.help": "Achetez une licence pour Breakout 71 pour débloquer le mode infini et soutenir le développement. Elle coûte 4,99 € et est illimitée dans le temps. Vous pouvez l'utiliser sur plusieurs appareils, mais ne la partagez pas en ligne.",
|
"premium.help": "Achetez une licence pour Breakout 71 pour débloquer le bouclage du jeu et soutenir le développement. Elle coûte 4,99 € et est illimitée dans le temps. Vous pouvez l'utiliser sur plusieurs appareils, mais ne la partagez pas en ligne.",
|
||||||
"premium.help_google": "Bien que je prévoie de proposer des licences premium via Google Play, je n'ai pas encore eu l'occasion de le faire ; il n'y a donc pas de lien d'achat ici. Si vous possédez déjà une clé de licence, vous pouvez la saisir ci-dessous.",
|
"premium.help_google": "Bien que je prévoie de proposer des licences premium via Google Play, je n'ai pas encore eu l'occasion de le faire ; il n'y a donc pas de lien d'achat ici. Si vous possédez déjà une clé de licence, vous pouvez la saisir ci-dessous.",
|
||||||
"premium.next_step_title": "Achetez des améliorations et passez au niveau suivant",
|
"premium.per_hours": "Vous avez passé {{hours}} heures à jouer",
|
||||||
"premium.pick_level": "Accédez au niveau « {{name}} » pour $ {{cost}}",
|
"premium.per_hours_help": "Donnez 4.99€ pour être premium",
|
||||||
"premium.pick_perk": "Obtenez {{name}}pour{{cost}}$",
|
"premium.thanks": "",
|
||||||
"premium.short_help": "Jouez le plus longtemps possible",
|
"premium.thanks_help": "",
|
||||||
"premium.upgrade_perk_to_level": "Passez de {{name}} à {{lvl}} pour{{cost}}$",
|
"premium.title": "",
|
||||||
"premium.your_upgrades": "Vos mises à jour jusqu'à présent :",
|
|
||||||
"sandbox.help": "Tester n'importe quelle combinaison d'améliorations",
|
"sandbox.help": "Tester n'importe quelle combinaison d'améliorations",
|
||||||
"sandbox.instructions": "Sélectionnez les amélioration ci-dessous et appuyez sur \"Démarrer la partie de test\" pour les tester. Les scores et les statistiques ne seront pas enregistrés.",
|
"sandbox.instructions": "Sélectionnez les amélioration ci-dessous et appuyez sur \"Démarrer la partie de test\" pour les tester. Les scores et les statistiques ne seront pas enregistrés.",
|
||||||
"sandbox.start": "Démarrer la partie de test",
|
"sandbox.start": "Démarrer la partie de test",
|
||||||
"sandbox.title": "Mode bac à sable",
|
"sandbox.title": "Mode bac à sable",
|
||||||
"sandbox.unlocks_at": "Déverrouillé à partir d'un score total de {{score}}",
|
"sandbox.unlocks_at": "Déverrouillé à partir d'un score total de {{score}}",
|
||||||
"score_panel.restart": "Redémarrer",
|
"score_panel.bebuffs_list": "Handicapes : ",
|
||||||
"score_panel.restart_help": "Commencer une nouvelle partie",
|
|
||||||
"score_panel.resume": "Continuer la partie",
|
|
||||||
"score_panel.resume_help": "Fermer cette fenêtre pour retourner au jeu",
|
|
||||||
"score_panel.test_run": "Il s'agit d'une partie d'essai, le score n'est pas enregistré.",
|
"score_panel.test_run": "Il s'agit d'une partie d'essai, le score n'est pas enregistré.",
|
||||||
"score_panel.title": "{{score}} points au niveau {{level}}/{{max}} ",
|
"score_panel.title": "{{score}} points au niveau {{level}}/{{max}} ",
|
||||||
"score_panel.title_adventure": "{{score}} points au niveau {{level}} de l'aventure",
|
"score_panel.title_looped": "{{score}} points au niveau {{level}}/{{max}} ",
|
||||||
"score_panel.upcoming_levels": "Niveaux de la parties : ",
|
"score_panel.upcoming_levels": "Niveaux de la parties : ",
|
||||||
"score_panel.upgrades_picked": "Améliorations choisies jusqu'à présent :",
|
"score_panel.upgrades_picked": "Améliorations choisies jusqu'à présent :",
|
||||||
"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.",
|
||||||
|
|
|
@ -45,7 +45,5 @@ export const allLevels = rawLevelsList
|
||||||
|
|
||||||
export const upgrades = rawUpgrades.map((u) => ({
|
export const upgrades = rawUpgrades.map((u) => ({
|
||||||
...u,
|
...u,
|
||||||
icon: icons["icon:" + u.id],
|
icon: icons["icon:" + u.id]
|
||||||
adventure: "adventure" in u ? u.adventure : true,
|
|
||||||
normal: "normal" in u ? u.normal : true,
|
|
||||||
})) as Upgrade[];
|
})) as Upgrade[];
|
||||||
|
|
|
@ -11,10 +11,10 @@ import { dontOfferTooSoon, resetBalls } from "./gameStateMutators";
|
||||||
import { isOptionOn } from "./options";
|
import { isOptionOn } from "./options";
|
||||||
import { debuffs } from "./debuffs";
|
import { debuffs } from "./debuffs";
|
||||||
|
|
||||||
export function newGameState(params: RunParams): GameState {
|
|
||||||
const totalScoreAtRunStart = getTotalScore();
|
export function getRunLevels(totalScoreAtRunStart:number, params: RunParams){
|
||||||
const firstLevel = params?.level
|
const firstLevel =
|
||||||
? allLevels.filter((l) => l.name === params?.level)
|
params?.level ? allLevels.filter((l) => l.name === params?.level)
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
const restInRandomOrder = allLevels
|
const restInRandomOrder = allLevels
|
||||||
|
@ -23,9 +23,15 @@ export function newGameState(params: RunParams): GameState {
|
||||||
.filter((l) => l.name !== params?.levelToAvoid)
|
.filter((l) => l.name !== params?.levelToAvoid)
|
||||||
.sort(() => Math.random() - 0.5);
|
.sort(() => Math.random() - 0.5);
|
||||||
|
|
||||||
const runLevels = firstLevel.concat(
|
return firstLevel.concat(
|
||||||
restInRandomOrder.slice(0, 7 + 3).sort((a, b) => a.sortKey - b.sortKey),
|
restInRandomOrder.slice(0, 7 + 3).sort((a, b) => a.sortKey - b.sortKey),
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function newGameState(params: RunParams): GameState {
|
||||||
|
const totalScoreAtRunStart = getTotalScore();
|
||||||
|
|
||||||
|
const runLevels =getRunLevels(totalScoreAtRunStart, params)
|
||||||
|
|
||||||
const perks = { ...makeEmptyPerksMap(upgrades), ...(params?.perks || {}) };
|
const perks = { ...makeEmptyPerksMap(upgrades), ...(params?.perks || {}) };
|
||||||
|
|
||||||
|
@ -35,6 +41,7 @@ export function newGameState(params: RunParams): GameState {
|
||||||
currentLevel: 0,
|
currentLevel: 0,
|
||||||
upgradesOfferedFor: -1,
|
upgradesOfferedFor: -1,
|
||||||
perks,
|
perks,
|
||||||
|
bannedPerks:makeEmptyPerksMap(upgrades),
|
||||||
debuffs: { ...emptyDebuffsMap(), ...(params?.debuffs || {}) },
|
debuffs: { ...emptyDebuffsMap(), ...(params?.debuffs || {}) },
|
||||||
puckWidth: 200,
|
puckWidth: 200,
|
||||||
baseSpeed: 12,
|
baseSpeed: 12,
|
||||||
|
@ -102,13 +109,12 @@ export function newGameState(params: RunParams): GameState {
|
||||||
needsRender: true,
|
needsRender: true,
|
||||||
autoCleanUses: 0,
|
autoCleanUses: 0,
|
||||||
...defaultSounds(),
|
...defaultSounds(),
|
||||||
|
|
||||||
isAdventureMode: !!params?.adventure,
|
|
||||||
rerolls: 0,
|
rerolls: 0,
|
||||||
|
loop:0
|
||||||
};
|
};
|
||||||
resetBalls(gameState);
|
resetBalls(gameState);
|
||||||
|
|
||||||
if (!sumOfValues(gameState.perks) && !params?.adventure) {
|
if (!sumOfValues(gameState.perks)) {
|
||||||
const giftable = getPossibleUpgrades(gameState).filter((u) => u.giftable);
|
const giftable = getPossibleUpgrades(gameState).filter((u) => u.giftable);
|
||||||
const randomGift =
|
const randomGift =
|
||||||
(isOptionOn("easy") && "slow_down") ||
|
(isOptionOn("easy") && "slow_down") ||
|
||||||
|
|
|
@ -19,11 +19,21 @@ export const options = {
|
||||||
name: t("main_menu.basic"),
|
name: t("main_menu.basic"),
|
||||||
help: t("main_menu.basic_help"),
|
help: t("main_menu.basic_help"),
|
||||||
},
|
},
|
||||||
|
colorful_coins: {
|
||||||
|
default: false,
|
||||||
|
name: t("main_menu.colorful_coins"),
|
||||||
|
help: t("main_menu.colorful_coins_help"),
|
||||||
|
},
|
||||||
show_fps: {
|
show_fps: {
|
||||||
default: false,
|
default: false,
|
||||||
name: t("main_menu.show_fps"),
|
name: t("main_menu.show_fps"),
|
||||||
help: t("main_menu.show_fps_help"),
|
help: t("main_menu.show_fps_help"),
|
||||||
},
|
},
|
||||||
|
show_stats: {
|
||||||
|
default: false,
|
||||||
|
name: t("main_menu.show_stats"),
|
||||||
|
help: t("main_menu.show_stats_help"),
|
||||||
|
},
|
||||||
pointerLock: {
|
pointerLock: {
|
||||||
default: false,
|
default: false,
|
||||||
name: t("main_menu.pointer_lock"),
|
name: t("main_menu.pointer_lock"),
|
||||||
|
|
258
src/premium.ts
258
src/premium.ts
|
@ -1,9 +1,9 @@
|
||||||
import { GameState } from "./types";
|
import {GameState} from "./types";
|
||||||
import { icons } from "./loadGameData";
|
import {icons} from "./loadGameData";
|
||||||
import { t } from "./i18n/i18n";
|
import {t} from "./i18n/i18n";
|
||||||
import { getSettingValue, setSettingValue } from "./settings";
|
import {getSettingValue, setSettingValue} from "./settings";
|
||||||
import { asyncAlert } from "./asyncAlert";
|
import {asyncAlert} from "./asyncAlert";
|
||||||
import { confirmRestart, openMainMenu, restart } from "./game";
|
import {openMainMenu} from "./game";
|
||||||
|
|
||||||
const publicKeyString = `-----BEGIN PUBLIC KEY-----
|
const publicKeyString = `-----BEGIN PUBLIC KEY-----
|
||||||
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAyGQJgs6gxa0Fd86TuZ2q
|
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAyGQJgs6gxa0Fd86TuZ2q
|
||||||
|
@ -21,49 +21,49 @@ dZpWPx3O+rZbRQb0gHcvN4+n2Y7fWAeC9mxVZtADqvVr/GTumMbLj7DdhWtt1Ogu
|
||||||
-----END PUBLIC KEY-----`;
|
-----END PUBLIC KEY-----`;
|
||||||
|
|
||||||
function pemToArrayBuffer(pem: string) {
|
function pemToArrayBuffer(pem: string) {
|
||||||
const b64 = pem
|
const b64 = pem
|
||||||
.replace(/-----BEGIN PUBLIC KEY-----/, "")
|
.replace(/-----BEGIN PUBLIC KEY-----/, "")
|
||||||
.replace(/-----END PUBLIC KEY-----/, "")
|
.replace(/-----END PUBLIC KEY-----/, "")
|
||||||
.replace(/\s+/g, "");
|
.replace(/\s+/g, "");
|
||||||
const binaryDerString = atob(b64);
|
const binaryDerString = atob(b64);
|
||||||
const binaryDer = new Uint8Array(binaryDerString.length);
|
const binaryDer = new Uint8Array(binaryDerString.length);
|
||||||
for (let i = 0; i < binaryDerString.length; i++) {
|
for (let i = 0; i < binaryDerString.length; i++) {
|
||||||
binaryDer[i] = binaryDerString.charCodeAt(i);
|
binaryDer[i] = binaryDerString.charCodeAt(i);
|
||||||
}
|
}
|
||||||
return binaryDer.buffer;
|
return binaryDer.buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getPriceId(key: string, pem: string) {
|
async function getPriceId(key: string, pem: string) {
|
||||||
// Split the key into its components
|
// Split the key into its components
|
||||||
const [priceId, timestamp, signature] = key.split(":");
|
const [priceId, timestamp, signature] = key.split(":");
|
||||||
const data = `${priceId}:${timestamp}`;
|
const data = `${priceId}:${timestamp}`;
|
||||||
|
|
||||||
const publicKeyBuffer = pemToArrayBuffer(pem);
|
const publicKeyBuffer = pemToArrayBuffer(pem);
|
||||||
|
|
||||||
const publicKey = await crypto.subtle.importKey(
|
const publicKey = await crypto.subtle.importKey(
|
||||||
"spki",
|
"spki",
|
||||||
publicKeyBuffer,
|
publicKeyBuffer,
|
||||||
{
|
{
|
||||||
name: "RSA-PSS",
|
name: "RSA-PSS",
|
||||||
hash: "SHA-256",
|
hash: "SHA-256",
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
["verify"],
|
["verify"],
|
||||||
);
|
);
|
||||||
|
|
||||||
// Verify the signature using ECDSA
|
// Verify the signature using ECDSA
|
||||||
const isValid = await crypto.subtle.verify(
|
const isValid = await crypto.subtle.verify(
|
||||||
{
|
{
|
||||||
name: "RSA-PSS",
|
name: "RSA-PSS",
|
||||||
saltLength: 32,
|
saltLength: 32,
|
||||||
},
|
},
|
||||||
publicKey,
|
publicKey,
|
||||||
new Uint8Array(Array.from(atob(signature), (c) => c.charCodeAt(0))),
|
new Uint8Array(Array.from(atob(signature), (c) => c.charCodeAt(0))),
|
||||||
new TextEncoder().encode(data),
|
new TextEncoder().encode(data),
|
||||||
);
|
);
|
||||||
if (!isValid) throw new Error("Invalid key signature");
|
if (!isValid) throw new Error("Invalid key signature");
|
||||||
|
|
||||||
return priceId;
|
return priceId;
|
||||||
}
|
}
|
||||||
|
|
||||||
let premium = false;
|
let premium = false;
|
||||||
|
@ -71,94 +71,116 @@ const gamePriceId = "price_1R6YaEGRf74lr2EkSo2GPvuO";
|
||||||
checkKey(getSettingValue("license", "")).then();
|
checkKey(getSettingValue("license", "")).then();
|
||||||
|
|
||||||
async function checkKey(key: string) {
|
async function checkKey(key: string) {
|
||||||
if (!key) return "No key";
|
if (!key) return "No key";
|
||||||
try {
|
try {
|
||||||
if (gamePriceId !== (await getPriceId(key, publicKeyString))) {
|
if (gamePriceId !== (await getPriceId(key, publicKeyString))) {
|
||||||
return "Wrong product";
|
return "Wrong product";
|
||||||
|
}
|
||||||
|
premium = true;
|
||||||
|
return "";
|
||||||
|
} catch (e) {
|
||||||
|
return "Could not upgrade : " + e.message;
|
||||||
}
|
}
|
||||||
premium = true;
|
|
||||||
return "";
|
|
||||||
} catch (e) {
|
|
||||||
return "Could not upgrade : " + e.message;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isPremium() {
|
export function isPremium() {
|
||||||
return premium;
|
return premium;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function premiumMenuEntry(gameState: GameState) {
|
export function premiumMenuEntry(gameState: GameState) {
|
||||||
if (isPremium()) {
|
if (isPremium()) {
|
||||||
return {
|
return {
|
||||||
icon: icons["icon:adventure_mode"],
|
icon: icons["icon:premium_active"],
|
||||||
text: t("premium.adventure_mode"),
|
text: t("premium.thanks"),
|
||||||
help: t("premium.adventure_mode_help"),
|
help: t("premium.thanks_help"),
|
||||||
value: async () => {
|
value: async () => {
|
||||||
if (await confirmRestart(gameState)) {
|
navigator.clipboard.writeText(getSettingValue('license', ''))
|
||||||
restart({
|
openMainMenu()
|
||||||
adventure: true,
|
},
|
||||||
});
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let text = t("premium.title")
|
||||||
|
let help = t("premium.buy")
|
||||||
|
try {
|
||||||
|
const timePlayed = localStorage.getItem('breakout_71_total_play_time')
|
||||||
|
if (timePlayed && !isGooglePlayInstall) {
|
||||||
|
const hours = parseFloat(timePlayed) / 1000 / 60 / 60
|
||||||
|
const pricePerHours = 4.99 / hours
|
||||||
|
const args = {
|
||||||
|
hours: Math.floor(hours),
|
||||||
|
pricePerHours: pricePerHours.toFixed(2)
|
||||||
|
}
|
||||||
|
if (pricePerHours > 0 && pricePerHours < 0.5) {
|
||||||
|
text = t("premium.per_hours", args)
|
||||||
|
help = t("premium.per_hours_help", args)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log({args})
|
||||||
}
|
}
|
||||||
},
|
} catch (e) {
|
||||||
|
console.warn(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
icon: icons["icon:premium"],
|
||||||
|
text,
|
||||||
|
help,
|
||||||
|
value: () => openPremiumMenu(""),
|
||||||
};
|
};
|
||||||
}
|
|
||||||
return {
|
|
||||||
icon: icons["icon:premium"],
|
|
||||||
text: t("premium.adventure_mode"),
|
|
||||||
help: t("premium.short_help"),
|
|
||||||
value: () => openPremiumMenu(""),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function openPremiumMenu(text) {
|
const isGooglePlayInstall =
|
||||||
const isGooglePlayInstall =
|
|
||||||
new URLSearchParams(location.search).get("source") ===
|
new URLSearchParams(location.search).get("source") ===
|
||||||
"com.android.vending";
|
"com.android.vending";
|
||||||
|
|
||||||
const cb = await asyncAlert({
|
async function openPremiumMenu(text) {
|
||||||
title: t("premium.adventure_mode"),
|
|
||||||
content: [
|
|
||||||
text ||
|
const cb = await asyncAlert({
|
||||||
(isGooglePlayInstall && t("premium.help_google")) ||
|
title: t("premium.title"),
|
||||||
t("premium.help"),
|
content: [
|
||||||
{
|
text ||
|
||||||
text: t("premium.buy"),
|
(isGooglePlayInstall && t("premium.help_google")) ||
|
||||||
disabled: isGooglePlayInstall,
|
t("premium.help"),
|
||||||
help: isGooglePlayInstall
|
{
|
||||||
? t("premium.buy_disabled_help")
|
text: t("premium.buy"),
|
||||||
: t("premium.buy_help"),
|
disabled: isGooglePlayInstall,
|
||||||
value() {
|
help: isGooglePlayInstall
|
||||||
window.open(
|
? t("premium.buy_disabled_help")
|
||||||
"https://licenses.lecaro.me/buy/price_1R6YaEGRf74lr2EkSo2GPvuO",
|
: t("premium.buy_help"),
|
||||||
"_blank",
|
value() {
|
||||||
);
|
window.open(
|
||||||
},
|
"https://licenses.lecaro.me/buy/price_1R6YaEGRf74lr2EkSo2GPvuO",
|
||||||
},
|
"_blank",
|
||||||
{
|
);
|
||||||
text: t("premium.enter"),
|
},
|
||||||
help: t("premium.enter_help"),
|
},
|
||||||
async value() {
|
{
|
||||||
const value = (prompt("Please paste your license key") || "").replace(
|
text: t("premium.enter"),
|
||||||
/\s+/g,
|
help: t("premium.enter_help"),
|
||||||
"",
|
async value() {
|
||||||
);
|
const value = (prompt("Please paste your license key") || "").replace(
|
||||||
const problem = await checkKey(value);
|
/\s+/g,
|
||||||
if (problem) {
|
"",
|
||||||
openPremiumMenu(problem).then();
|
);
|
||||||
} else {
|
const problem = await checkKey(value);
|
||||||
setSettingValue("license", value);
|
if (problem) {
|
||||||
openMainMenu().then();
|
openPremiumMenu(problem).then();
|
||||||
}
|
} else {
|
||||||
},
|
setSettingValue("license", value);
|
||||||
},
|
openMainMenu().then();
|
||||||
{
|
}
|
||||||
text: t("premium.back"),
|
},
|
||||||
help: t("premium.back_help"),
|
},
|
||||||
value() {
|
{
|
||||||
openMainMenu().then();
|
text: t("premium.back"),
|
||||||
},
|
help: t("premium.back_help"),
|
||||||
},
|
value() {
|
||||||
],
|
openMainMenu().then();
|
||||||
});
|
},
|
||||||
if (cb) cb();
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
if (cb) cb();
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,7 +54,7 @@ export function drawMainCanvasOnSmallCanvas(gameState: GameState) {
|
||||||
recordCanvasCtx.fillText(
|
recordCanvasCtx.fillText(
|
||||||
"Level " +
|
"Level " +
|
||||||
(gameState.currentLevel + 1) +
|
(gameState.currentLevel + 1) +
|
||||||
(gameState.isAdventureMode ? "" : "/" + max_levels(gameState)),
|
"/" + max_levels(gameState),
|
||||||
12,
|
12,
|
||||||
12,
|
12,
|
||||||
);
|
);
|
||||||
|
|
|
@ -36,11 +36,11 @@ export function render(gameState: GameState) {
|
||||||
if (!width || !height) return;
|
if (!width || !height) return;
|
||||||
|
|
||||||
if (gameState.currentLevel || gameState.levelTime) {
|
if (gameState.currentLevel || gameState.levelTime) {
|
||||||
menuLabel.innerText = gameState.isAdventureMode
|
menuLabel.innerText = gameState.loop? t("play.current_lvl_loop", {
|
||||||
? t("play.current_lvl_adventure", {
|
|
||||||
level: gameState.currentLevel + 1,
|
level: gameState.currentLevel + 1,
|
||||||
})
|
max: max_levels(gameState),
|
||||||
: t("play.current_lvl", {
|
loop:gameState.loop
|
||||||
|
}) : t("play.current_lvl", {
|
||||||
level: gameState.currentLevel + 1,
|
level: gameState.currentLevel + 1,
|
||||||
max: max_levels(gameState),
|
max: max_levels(gameState),
|
||||||
});
|
});
|
||||||
|
@ -88,12 +88,7 @@ export function render(gameState: GameState) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
ctx.globalAlpha = 1;
|
ctx.globalAlpha = 1;
|
||||||
forEachLiveOne(gameState.lights, (flash) => {
|
|
||||||
const { x, y, time, color, size, duration } = flash;
|
|
||||||
const elapsed = gameState.levelTime - time;
|
|
||||||
ctx.globalAlpha = Math.min(1, 2 - (elapsed / duration) * 2);
|
|
||||||
drawFuzzyBall(ctx, color, size, x, y);
|
|
||||||
});
|
|
||||||
forEachLiveOne(gameState.particles, (flash) => {
|
forEachLiveOne(gameState.particles, (flash) => {
|
||||||
const { x, y, time, color, size, duration } = flash;
|
const { x, y, time, color, size, duration } = flash;
|
||||||
const elapsed = gameState.levelTime - time;
|
const elapsed = gameState.levelTime - time;
|
||||||
|
@ -166,8 +161,10 @@ export function render(gameState: GameState) {
|
||||||
// Coins
|
// Coins
|
||||||
ctx.globalAlpha = 1;
|
ctx.globalAlpha = 1;
|
||||||
forEachLiveOne(gameState.coins, (coin) => {
|
forEachLiveOne(gameState.coins, (coin) => {
|
||||||
ctx.globalCompositeOperation =
|
|
||||||
coin.color === "gold" || level.color ? "source-over" : "screen";
|
ctx.globalCompositeOperation ='source-over'
|
||||||
|
// ctx.globalCompositeOperation =
|
||||||
|
// coin.color === "gold" || level.color ? "source-over" : "screen";
|
||||||
drawCoin(
|
drawCoin(
|
||||||
ctx,
|
ctx,
|
||||||
coin.color,
|
coin.color,
|
||||||
|
@ -200,6 +197,15 @@ export function render(gameState: GameState) {
|
||||||
ctx.globalCompositeOperation = "source-over";
|
ctx.globalCompositeOperation = "source-over";
|
||||||
renderAllBricks();
|
renderAllBricks();
|
||||||
|
|
||||||
|
|
||||||
|
ctx.globalCompositeOperation = "screen";
|
||||||
|
forEachLiveOne(gameState.lights, (flash) => {
|
||||||
|
const { x, y, time, color, size, duration } = flash;
|
||||||
|
const elapsed = gameState.levelTime - time;
|
||||||
|
ctx.globalAlpha = Math.min(1, 2 - (elapsed / duration) * 2) * 0.5;
|
||||||
|
drawBrick(ctx, color, x,y, -1)
|
||||||
|
});
|
||||||
|
|
||||||
ctx.globalCompositeOperation = "screen";
|
ctx.globalCompositeOperation = "screen";
|
||||||
forEachLiveOne(gameState.texts, (flash) => {
|
forEachLiveOne(gameState.texts, (flash) => {
|
||||||
const { x, y, time, color, size, duration } = flash;
|
const { x, y, time, color, size, duration } = flash;
|
||||||
|
@ -495,8 +501,7 @@ export function renderAllBricks() {
|
||||||
redBorderOnBricksWithWrongColor ||
|
redBorderOnBricksWithWrongColor ||
|
||||||
redColorOnAllBricks ||
|
redColorOnAllBricks ||
|
||||||
gameState.perks.reach ||
|
gameState.perks.reach ||
|
||||||
gameState.perks.zen ||
|
gameState.perks.zen
|
||||||
gameState.debuffs.negative_bricks
|
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
offset = 0;
|
offset = 0;
|
||||||
|
|
14
src/types.d.ts
vendored
14
src/types.d.ts
vendored
|
@ -27,10 +27,6 @@ export type Palette = { [k: string]: string };
|
||||||
export type Upgrade = {
|
export type Upgrade = {
|
||||||
threshold: number;
|
threshold: number;
|
||||||
giftable: boolean;
|
giftable: boolean;
|
||||||
// Offered in adventure mode
|
|
||||||
adventure: boolean;
|
|
||||||
// offered in normal mode
|
|
||||||
normal: boolean;
|
|
||||||
id: PerkId;
|
id: PerkId;
|
||||||
name: string;
|
name: string;
|
||||||
icon: string;
|
icon: string;
|
||||||
|
@ -158,6 +154,12 @@ export type PerksMap = {
|
||||||
[k in PerkId]: number;
|
[k in PerkId]: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type Debuff={
|
||||||
|
id: DebuffId;
|
||||||
|
max:number;
|
||||||
|
name:(lvl: number,banned:string)=>string;
|
||||||
|
help:(lvl: number,perk:string)=>string;
|
||||||
|
}
|
||||||
export type DebuffId = (typeof debuffs)[number]["id"];
|
export type DebuffId = (typeof debuffs)[number]["id"];
|
||||||
|
|
||||||
export type DebuffsMap = {
|
export type DebuffsMap = {
|
||||||
|
@ -208,6 +210,7 @@ export type GameState = {
|
||||||
puckWidth: number;
|
puckWidth: number;
|
||||||
// perks the user currently has
|
// perks the user currently has
|
||||||
perks: PerksMap;
|
perks: PerksMap;
|
||||||
|
bannedPerks: PerksMap;
|
||||||
debuffs: DebuffsMap;
|
debuffs: DebuffsMap;
|
||||||
// Base speed of the ball in pixels/tick
|
// Base speed of the ball in pixels/tick
|
||||||
baseSpeed: number;
|
baseSpeed: number;
|
||||||
|
@ -281,15 +284,14 @@ export type GameState = {
|
||||||
colorChange: { vol: number; x: number };
|
colorChange: { vol: number; x: number };
|
||||||
void: { vol: number; x: number };
|
void: { vol: number; x: number };
|
||||||
};
|
};
|
||||||
isAdventureMode: boolean;
|
|
||||||
rerolls: number;
|
rerolls: number;
|
||||||
|
loop: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type RunParams = {
|
export type RunParams = {
|
||||||
level?: string;
|
level?: string;
|
||||||
levelToAvoid?: string;
|
levelToAvoid?: string;
|
||||||
perks?: Partial<PerksMap>;
|
perks?: Partial<PerksMap>;
|
||||||
adventure?: boolean;
|
|
||||||
debuffs?: boolean;
|
debuffs?: boolean;
|
||||||
};
|
};
|
||||||
export type OptionDef = {
|
export type OptionDef = {
|
||||||
|
|
112
src/upgrades.ts
112
src/upgrades.ts
|
@ -3,7 +3,7 @@ import { t } from "./i18n/i18n";
|
||||||
export const rawUpgrades = [
|
export const rawUpgrades = [
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 0,
|
threshold: 0,
|
||||||
giftable: false,
|
giftable: false,
|
||||||
id: "extra_life",
|
id: "extra_life",
|
||||||
|
@ -17,7 +17,7 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 0,
|
threshold: 0,
|
||||||
id: "streak_shots",
|
id: "streak_shots",
|
||||||
giftable: true,
|
giftable: true,
|
||||||
|
@ -29,7 +29,7 @@ export const rawUpgrades = [
|
||||||
|
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 0,
|
threshold: 0,
|
||||||
id: "base_combo",
|
id: "base_combo",
|
||||||
giftable: true,
|
giftable: true,
|
||||||
|
@ -41,7 +41,7 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 0,
|
threshold: 0,
|
||||||
giftable: false,
|
giftable: false,
|
||||||
id: "slow_down",
|
id: "slow_down",
|
||||||
|
@ -52,7 +52,7 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 0,
|
threshold: 0,
|
||||||
giftable: false,
|
giftable: false,
|
||||||
id: "bigger_puck",
|
id: "bigger_puck",
|
||||||
|
@ -63,7 +63,7 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 0,
|
threshold: 0,
|
||||||
giftable: false,
|
giftable: false,
|
||||||
id: "viscosity",
|
id: "viscosity",
|
||||||
|
@ -75,7 +75,7 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 0,
|
threshold: 0,
|
||||||
id: "left_is_lava",
|
id: "left_is_lava",
|
||||||
giftable: true,
|
giftable: true,
|
||||||
|
@ -87,7 +87,7 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 0,
|
threshold: 0,
|
||||||
id: "right_is_lava",
|
id: "right_is_lava",
|
||||||
giftable: true,
|
giftable: true,
|
||||||
|
@ -98,7 +98,7 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 0,
|
threshold: 0,
|
||||||
id: "top_is_lava",
|
id: "top_is_lava",
|
||||||
giftable: true,
|
giftable: true,
|
||||||
|
@ -109,7 +109,7 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 0,
|
threshold: 0,
|
||||||
giftable: false,
|
giftable: false,
|
||||||
id: "skip_last",
|
id: "skip_last",
|
||||||
|
@ -123,10 +123,10 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 500,
|
threshold: 500,
|
||||||
id: "telekinesis",
|
id: "telekinesis",
|
||||||
giftable: true,
|
giftable: false,
|
||||||
max: 2,
|
max: 2,
|
||||||
name: t("upgrades.telekinesis.name"),
|
name: t("upgrades.telekinesis.name"),
|
||||||
help: (lvl: number) =>
|
help: (lvl: number) =>
|
||||||
|
@ -137,7 +137,7 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 1000,
|
threshold: 1000,
|
||||||
giftable: false,
|
giftable: false,
|
||||||
id: "coin_magnet",
|
id: "coin_magnet",
|
||||||
|
@ -151,10 +151,10 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 1500,
|
threshold: 1500,
|
||||||
id: "multiball",
|
id: "multiball",
|
||||||
giftable: true,
|
giftable: false,
|
||||||
max: 6,
|
max: 6,
|
||||||
name: t("upgrades.multiball.name"),
|
name: t("upgrades.multiball.name"),
|
||||||
help: (lvl: number) => t("upgrades.multiball.help", { count: lvl + 1 }),
|
help: (lvl: number) => t("upgrades.multiball.help", { count: lvl + 1 }),
|
||||||
|
@ -162,7 +162,7 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 2000,
|
threshold: 2000,
|
||||||
giftable: false,
|
giftable: false,
|
||||||
id: "smaller_puck",
|
id: "smaller_puck",
|
||||||
|
@ -176,10 +176,10 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 3000,
|
threshold: 3000,
|
||||||
id: "pierce",
|
id: "pierce",
|
||||||
giftable: true,
|
giftable: false,
|
||||||
max: 3,
|
max: 3,
|
||||||
name: t("upgrades.pierce.name"),
|
name: t("upgrades.pierce.name"),
|
||||||
help: (lvl: number) => t("upgrades.pierce.help", { count: 3 * lvl }),
|
help: (lvl: number) => t("upgrades.pierce.help", { count: 3 * lvl }),
|
||||||
|
@ -187,7 +187,7 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 4000,
|
threshold: 4000,
|
||||||
id: "picky_eater",
|
id: "picky_eater",
|
||||||
giftable: true,
|
giftable: true,
|
||||||
|
@ -198,7 +198,7 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 5000,
|
threshold: 5000,
|
||||||
giftable: false,
|
giftable: false,
|
||||||
id: "metamorphosis",
|
id: "metamorphosis",
|
||||||
|
@ -209,7 +209,7 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 6000,
|
threshold: 6000,
|
||||||
id: "compound_interest",
|
id: "compound_interest",
|
||||||
giftable: true,
|
giftable: true,
|
||||||
|
@ -220,7 +220,7 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 7000,
|
threshold: 7000,
|
||||||
id: "hot_start",
|
id: "hot_start",
|
||||||
giftable: true,
|
giftable: true,
|
||||||
|
@ -235,10 +235,10 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 9000,
|
threshold: 9000,
|
||||||
id: "sapper",
|
id: "sapper",
|
||||||
giftable: true,
|
giftable: false,
|
||||||
max: 7,
|
max: 7,
|
||||||
name: t("upgrades.sapper.name"),
|
name: t("upgrades.sapper.name"),
|
||||||
help: (lvl: number) =>
|
help: (lvl: number) =>
|
||||||
|
@ -249,7 +249,7 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 11000,
|
threshold: 11000,
|
||||||
id: "bigger_explosions",
|
id: "bigger_explosions",
|
||||||
giftable: false,
|
giftable: false,
|
||||||
|
@ -260,7 +260,7 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 13000,
|
threshold: 13000,
|
||||||
giftable: false,
|
giftable: false,
|
||||||
adventure: false,
|
adventure: false,
|
||||||
|
@ -272,7 +272,7 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 15000,
|
threshold: 15000,
|
||||||
giftable: false,
|
giftable: false,
|
||||||
id: "pierce_color",
|
id: "pierce_color",
|
||||||
|
@ -283,7 +283,7 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 18000,
|
threshold: 18000,
|
||||||
giftable: false,
|
giftable: false,
|
||||||
id: "soft_reset",
|
id: "soft_reset",
|
||||||
|
@ -320,7 +320,7 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 30000,
|
threshold: 30000,
|
||||||
giftable: false,
|
giftable: false,
|
||||||
id: "puck_repulse_ball",
|
id: "puck_repulse_ball",
|
||||||
|
@ -334,7 +334,7 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 35000,
|
threshold: 35000,
|
||||||
giftable: false,
|
giftable: false,
|
||||||
id: "wind",
|
id: "wind",
|
||||||
|
@ -346,7 +346,7 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 40000,
|
threshold: 40000,
|
||||||
giftable: false,
|
giftable: false,
|
||||||
id: "sturdy_bricks",
|
id: "sturdy_bricks",
|
||||||
|
@ -360,7 +360,7 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 45000,
|
threshold: 45000,
|
||||||
giftable: false,
|
giftable: false,
|
||||||
id: "respawn",
|
id: "respawn",
|
||||||
|
@ -372,7 +372,7 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 50000,
|
threshold: 50000,
|
||||||
giftable: false,
|
giftable: false,
|
||||||
id: "one_more_choice",
|
id: "one_more_choice",
|
||||||
|
@ -383,7 +383,7 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 55000,
|
threshold: 55000,
|
||||||
giftable: false,
|
giftable: false,
|
||||||
id: "instant_upgrade",
|
id: "instant_upgrade",
|
||||||
|
@ -395,7 +395,7 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 60000,
|
threshold: 60000,
|
||||||
giftable: false,
|
giftable: false,
|
||||||
id: "concave_puck",
|
id: "concave_puck",
|
||||||
|
@ -406,7 +406,7 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 65000,
|
threshold: 65000,
|
||||||
giftable: false,
|
giftable: false,
|
||||||
id: "helium",
|
id: "helium",
|
||||||
|
@ -417,9 +417,9 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 70000,
|
threshold: 70000,
|
||||||
giftable: false,
|
giftable: true,
|
||||||
id: "asceticism",
|
id: "asceticism",
|
||||||
max: 1,
|
max: 1,
|
||||||
name: t("upgrades.asceticism.name"),
|
name: t("upgrades.asceticism.name"),
|
||||||
|
@ -428,7 +428,7 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 75000,
|
threshold: 75000,
|
||||||
giftable: false,
|
giftable: false,
|
||||||
id: "unbounded",
|
id: "unbounded",
|
||||||
|
@ -439,7 +439,7 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 80000,
|
threshold: 80000,
|
||||||
giftable: false,
|
giftable: false,
|
||||||
id: "shunt",
|
id: "shunt",
|
||||||
|
@ -450,7 +450,7 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 85000,
|
threshold: 85000,
|
||||||
giftable: false,
|
giftable: false,
|
||||||
id: "yoyo",
|
id: "yoyo",
|
||||||
|
@ -461,9 +461,9 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 90000,
|
threshold: 90000,
|
||||||
giftable: false,
|
giftable: true,
|
||||||
id: "nbricks",
|
id: "nbricks",
|
||||||
max: 3,
|
max: 3,
|
||||||
name: t("upgrades.nbricks.name"),
|
name: t("upgrades.nbricks.name"),
|
||||||
|
@ -472,7 +472,7 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 95000,
|
threshold: 95000,
|
||||||
giftable: false,
|
giftable: false,
|
||||||
id: "etherealcoins",
|
id: "etherealcoins",
|
||||||
|
@ -493,9 +493,9 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 105000,
|
threshold: 105000,
|
||||||
giftable: false,
|
giftable: true,
|
||||||
id: "zen",
|
id: "zen",
|
||||||
max: 1,
|
max: 1,
|
||||||
name: t("upgrades.zen.name"),
|
name: t("upgrades.zen.name"),
|
||||||
|
@ -516,7 +516,7 @@ export const rawUpgrades = [
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
threshold: 115000,
|
threshold: 115000,
|
||||||
giftable: false,
|
giftable: true,
|
||||||
id: "trampoline",
|
id: "trampoline",
|
||||||
max: 1,
|
max: 1,
|
||||||
name: t("upgrades.trampoline.name"),
|
name: t("upgrades.trampoline.name"),
|
||||||
|
@ -525,7 +525,7 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 120000,
|
threshold: 120000,
|
||||||
giftable: false,
|
giftable: false,
|
||||||
id: "ghost_coins",
|
id: "ghost_coins",
|
||||||
|
@ -546,7 +546,7 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 130000,
|
threshold: 130000,
|
||||||
giftable: false,
|
giftable: false,
|
||||||
id: "ball_attracts_coins",
|
id: "ball_attracts_coins",
|
||||||
|
@ -557,8 +557,9 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 135000,
|
threshold: 135000,
|
||||||
|
// a bit too hard when starting up
|
||||||
giftable: false,
|
giftable: false,
|
||||||
id: "reach",
|
id: "reach",
|
||||||
max: 1,
|
max: 1,
|
||||||
|
@ -568,9 +569,9 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 140000,
|
threshold: 140000,
|
||||||
giftable: false,
|
giftable: true,
|
||||||
id: "passive_income",
|
id: "passive_income",
|
||||||
max: 4,
|
max: 4,
|
||||||
name: t("upgrades.passive_income.name"),
|
name: t("upgrades.passive_income.name"),
|
||||||
|
@ -580,7 +581,7 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 145000,
|
threshold: 145000,
|
||||||
giftable: false,
|
giftable: false,
|
||||||
id: "clairvoyant",
|
id: "clairvoyant",
|
||||||
|
@ -592,7 +593,7 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 150000,
|
threshold: 150000,
|
||||||
giftable: true,
|
giftable: true,
|
||||||
id: "side_kick",
|
id: "side_kick",
|
||||||
|
@ -603,7 +604,7 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 155000,
|
threshold: 155000,
|
||||||
giftable: false,
|
giftable: false,
|
||||||
id: "implosions",
|
id: "implosions",
|
||||||
|
@ -614,7 +615,6 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
rejects: "",
|
|
||||||
threshold: 160000,
|
threshold: 160000,
|
||||||
giftable: false,
|
giftable: false,
|
||||||
id: "corner_shot",
|
id: "corner_shot",
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue