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

View file

@ -13,23 +13,20 @@ Break colourful bricks, catch bouncing coins and select powerful upgrades !
# Changelog
## To do
- instead of bouncing the ball,loosing a life pauses the game (with coins still in the air)
- +1 upgrade per gold medal, but they are all applied to the selected perk
## Done
- rewoked perks choices to limit perk fatigue, avoid wall of texts, allow users to skip
- removed rerolls
- offer to pick 1 upgrade out of 3 choices
- playing well adds 1 upgrade and 1 choice
- playing even better adds 1 choice
- "more choices" perk adds 1 choice
- you can skip the upgrades and they'll be saved for later as extra lives
- you can pick an upgrade multiple time to level it up
- when you earn multiple upgrade points, they all need to be put on the same perk
- wait for bricks to respawn before leveling up
- creative mode : removed tooltips for perks as they were getting in the way on mobile
- unlocked upgrades and levels : split item description (with tooltip) and "try" button
- unlocked level: removed progress bars as there's no real progress
- bigger "level X of Y cleared"
- continue to level X/Y as button
- lives = upgrade points
- clarify challenges
- removed the "sides bounce" challenge
- clarify challenges but only show them when you pass one of them
- removed the "sides bounce" challenge, bouncing on sides shouldn't be punished
- upgrades list now uses numbers instead of bars, looks better with limitless
- somehow score clicks didn't register while the game was playing, that's solved
- Fix : click tooltip to open on mobile, click anywhere to close
@ -396,6 +393,7 @@ Break colourful bricks, catch bouncing coins and select powerful upgrades !
## Easy perk ideas
- chill : no perks gain, no level limit,+20 base combo
- when the ball teleport, probability that it's duplicated instead
- combo resets on teleport
- combo resets when hitting puck without a teleport
@ -501,12 +499,6 @@ Break colourful bricks, catch bouncing coins and select powerful upgrades !
## UX / gameplay
- make menu and score button more "button like" when you just installed the game.
- chill game mode, to just relax your mind :
- no 7 levels limit
- no upgrades offered at the end of the level
- get a random perk
- every 7 level it's replaced by another random perk
- every 7 levels, +10 base combo and +1 piece
- avoid showing a +1 and -1 at the same time when a combo increase is reset
- explain to iOS users how to add the app to home screen to get fullscreen
- delayed start on mobile to let users place the puck where they want

View file

@ -29,8 +29,8 @@ android {
applicationId = "me.lecaro.breakout"
minSdk = 21
targetSdk = 34
versionCode = 29100389
versionName = "29100389"
versionCode = 29103645
versionName = "29103645"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary = true

File diff suppressed because one or more lines are too long

351
dist/index.html vendored

File diff suppressed because one or more lines are too long

View file

@ -1,5 +1,5 @@
// The version of the cache.
const VERSION = "29100389";
const VERSION = "29103645";
// The name of the cache
const CACHE_NAME = `breakout-71-${VERSION}`;

View file

@ -1,6 +1,7 @@
import { t } from "./i18n/i18n";
import { isOptionOn } from "./options";
import { hideAnyTooltip } from "./tooltip";
export let alertsOpen = 0,
closeModal: null | (() => void) = null;
@ -100,58 +101,7 @@ export async function asyncAlert<t>({
addto.className = "actions";
popup.appendChild(addto);
}
const buttonWrap = document.createElement("div");
addto.appendChild(buttonWrap);
const {
text,
value,
help,
disabled,
className = "",
icon = "",
tooltip,
} = entry;
const button = document.createElement("button");
button.innerHTML = `
${icon}
<div>
<strong>${text}</strong>
<em>${help || ""}</em>
</div>`;
if (disabled) {
button.setAttribute("disabled", "disabled");
} else {
button.addEventListener("click", (e) => {
e.preventDefault();
e.stopPropagation();
closeWithResult(value);
// Focus "same" button if it's still there
lastClickedItemIndex = index;
});
}
button.className =
className +
(lastClickedItemIndex === index ? " needs-focus" : "") +
" choice-button";
buttonWrap.appendChild(button);
if (tooltip) {
if (isOptionOn("mobile-mode")) {
const helpBtn = document.createElement("button");
helpBtn.innerText = "?";
helpBtn.setAttribute("data-help-content", tooltip);
buttonWrap.appendChild(helpBtn);
} else {
button.setAttribute("data-tooltip", tooltip);
}
}
addButton(entry, index, addto, closeWithResult);
});
popup.addEventListener(
@ -191,3 +141,96 @@ function updateAlertsOpen(delta: number) {
}
document.body.classList[alertsOpen ? "add" : "remove"]("has-alert-open");
}
function addButton<t>(
entry: AsyncAlertAction<t>,
index: number,
addto: HTMLElement,
closeWithResult: (r: t) => void,
) {
const {
text = "",
value = null,
help = "",
disabled = false,
className = "",
icon = "",
tooltip = "",
actionLabel = "",
} = entry;
const buttonWrap = document.createElement("div");
addto.appendChild(buttonWrap);
if (actionLabel) {
buttonWrap.className = className;
buttonWrap.innerHTML = icon;
const description = document.createElement("p");
description.innerHTML = `
<strong>${text}</strong>
${help}
`;
description.setAttribute("data-tooltip", tooltip);
description.setAttribute("data-help-content", tooltip);
buttonWrap.appendChild(description);
const button = document.createElement("button");
button.textContent = actionLabel;
button.disabled = disabled;
if (!disabled)
button.addEventListener("click", (e) => {
e.preventDefault();
e.stopPropagation();
closeWithResult(value);
// Focus "same" button if it's still there
lastClickedItemIndex = index;
});
button.className = lastClickedItemIndex === index ? " needs-focus" : "";
buttonWrap.appendChild(button);
return;
}
const button = document.createElement("button");
button.innerHTML = `
${icon}
<div>
<strong>${text}</strong>
<em>${help || ""}</em>
</div>`;
if (disabled) {
button.setAttribute("disabled", "disabled");
} else {
button.addEventListener("click", (e) => {
e.preventDefault();
e.stopPropagation();
closeWithResult(value);
// Focus "same" button if it's still there
lastClickedItemIndex = index;
});
}
button.className =
className +
(lastClickedItemIndex === index ? " needs-focus" : "") +
" choice-button";
buttonWrap.appendChild(button);
if (tooltip) {
button.setAttribute("data-tooltip", tooltip);
// if (!isOptionOn("mobile-mode")) {
// // const helpBtn = document.createElement("button");
// // helpBtn.innerText = "?";
// // helpBtn.setAttribute("data-help-content", tooltip);
// // buttonWrap.appendChild(helpBtn);
// // } else {
// button.setAttribute("data-tooltip", tooltip);
// }
}
}

View file

@ -9,7 +9,12 @@ import {
restart,
} from "./game";
import { asyncAlert, requiredAsyncAlert } from "./asyncAlert";
import { describeLevel, highScoreText, sumOfValues } from "./game_utils";
import {
describeLevel,
highScoreText,
levelAndMaxBadge,
sumOfValues,
} from "./game_utils";
import { getHistory } from "./gameOver";
import { noCreative } from "./upgrades";
import { levelIconHTML } from "./levelIcon";
@ -92,15 +97,16 @@ export async function openCreativeModePerksPicker() {
.map((u) => ({
icon: icons["icon:" + u.id],
text: u.name,
help:
(creativeModePerks[u.id] || 0) +
"/" +
(u.max + (creativeModePerks.limitless || 0)),
help: levelAndMaxBadge(
creativeModePerks[u.id] || 0,
u.max + (creativeModePerks.limitless || 0),
),
value: u,
className:
" upgrade " +
(creativeModePerks[u.id] ? " highlight" : " not-highlighed"),
tooltip: u.help(creativeModePerks[u.id] || 1),
// tooltip: u.help(creativeModePerks[u.id] || 1),
// actionLabel: (creativeModePerks[u.id] || 0) + ' / '+( u.max + (creativeModePerks.limitless || 0))
})),
t("lab.select_level"),
...levelOptions,

View file

@ -332,7 +332,7 @@
{
"name": "icon:reroll",
"size": 8,
"bricks": "__llllll_llBlBlelllllleBWWWWWeeeWBWBWeBeWWWWWeeeWBWBWBe_WWWWWe__",
"bricks": "___WWWWe__WgWWee_WWWWegellllleeelglglegellgllee_lglgle__lllll___",
"credit": ""
},
{

View file

@ -1 +1 @@
"29100389"
"29103645"

View file

@ -1,9 +1,10 @@
* {
font-family: Courier New,
Courier,
Lucida Sans Typewriter,
Lucida Typewriter,
monospace;
font-family:
Courier New,
Courier,
Lucida Sans Typewriter,
Lucida Typewriter,
monospace;
box-sizing: border-box;
}
@ -155,6 +156,7 @@ body:not(.has-alert-open) #popup {
align-items: stretch;
> div {
position: relative;
display: flex;
& > button.choice-button {
@ -209,6 +211,14 @@ body:not(.has-alert-open) #popup {
opacity: 0.2;
}
}
&.forbidden {
background: linear-gradient(45deg, darkred, transparent);
}
&.required {
background: linear-gradient(45deg, gold, transparent);
}
}
> button[data-help-content] {
@ -234,6 +244,7 @@ body:not(.has-alert-open) #popup {
&.actionsAsGrid.large > div > section {
grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
grid-gap: 20px;
}
&.actionsAsGrid > div {
@ -413,7 +424,6 @@ h2.histogram-title strong {
p {
flex-grow: 1;
color: rgba(255, 255, 255, 0.6);
margin: 0 20px;
}
@ -421,7 +431,6 @@ h2.histogram-title strong {
color: white;
}
&.used {
opacity: 1;
}
@ -453,11 +462,13 @@ h2.histogram-title strong {
align-self: flex-start;
padding: 5px;
font-weight: bold;
transition: transform 0.2s,
box-shadow 0.2s;
box-shadow: 0 1px 0 black inset,
0 2px #5da3ea,
0 4px white;
transition:
transform 0.2s,
box-shadow 0.2s;
box-shadow:
0 1px 0 black inset,
0 2px #5da3ea,
0 4px white;
&:hover {
background: #5da3ea;
@ -465,13 +476,25 @@ h2.histogram-title strong {
&:active {
transform: translate(0, 4px);
box-shadow: 0 1px 0 black inset,
0 0px #5da3ea,
0 0px white;
box-shadow:
0 1px 0 black inset,
0 0px #5da3ea,
0 0px white;
}
transition: transform 0.2s,
box-shadow 0.2s;
transition:
transform 0.2s,
box-shadow 0.2s;
&[disabled] {
pointer-events: none;
background: #222;
box-shadow: 0 1px 0 black inset;
transform: translate(0, 4px);
color: #666;
text-shadow: 0 0 2px black;
border-color: #666;
}
}
}
@ -593,24 +616,6 @@ h2.histogram-title strong {
}
}
.progress-inline {
position: absolute;
display: block;
background: grey;
left: 62px;
right: 2px;
height: 7px;
bottom: 2px;
border-radius: 2px;
span {
position: absolute;
inset: 1px;
transform-origin: top left;
background: white;
}
}
.toast {
position: fixed;
left: 0;
@ -624,8 +629,9 @@ h2.histogram-title strong {
border-radius: 2px;
padding-right: 10px;
pointer-events: none;
transition: opacity 200ms,
transform 200ms;
transition:
opacity 200ms,
transform 200ms;
z-index: 7;
&.hidden {

View file

@ -798,6 +798,8 @@ async function openUnlockedUpgradesList() {
: help(1),
tooltip: ts < threshold ? "" : fullHelp(1) + " [id:" + id + "]",
threshold,
className: "upgrade choice " + (ts > threshold ? "used" : ""),
actionLabel: t("unlocks.use"),
}))
.sort((a, b) => a.threshold - b.threshold);
@ -833,7 +835,7 @@ async function openUnlockedUpgradesList() {
}
async function openUnlockedLevelsList() {
const hintField = isOptionOn("mobile-mode") ? "help" : "tooltip";
// const hintField = isOptionOn("mobile-mode") ? "help" : "tooltip";
const unlockedBefore = new Set(
getSettingValue("breakout_71_unlocked_levels", []),
@ -842,16 +844,16 @@ async function openUnlockedLevelsList() {
const lockedBecause = unlockedBefore.has(l.name)
? null
: reasonLevelIsLocked(li, l.name, getHistory(), true);
const percentUnlocked = lockedBecause?.reached
? `<span class="progress-inline"><span style="transform: scale(${Math.floor((lockedBecause.reached / lockedBecause.minScore) * 100) / 100},1)"></span></span>`
: "";
return {
text: l.name + percentUnlocked,
text: l.name,
disabled: !!lockedBecause,
value: { level: l } as RunParams,
icon: icons[l.name],
[hintField]: lockedBecause?.text || describeLevel(l),
help: lockedBecause?.text || describeLevel(l),
className: "upgrade choice " + (!lockedBecause ? "used" : ""),
tooltip: l.credit,
actionLabel: t("unlocks.try"),
};
});

View file

@ -995,7 +995,10 @@ export function gameStateTick(
// Delayed win when coins are still flying
(gameState.winAt && gameState.levelTime > gameState.winAt) ||
// instant win condition
(gameState.levelTime && !remainingBricks && !liveCount(gameState.coins))
(gameState.levelTime &&
!remainingBricks &&
!hasPendingBricks &&
!liveCount(gameState.coins))
) {
if (gameState.startParams.computer_controlled) {
startComputerControlledGame(gameState.startParams.stress);
@ -1967,8 +1970,7 @@ export function ballTick(gameState: GameState, ball: Ball, frames: number) {
) {
const remainingPierce = ball.piercePoints;
const remainingSapper = ball.sapperUses < gameState.perks.sapper;
const willMiss =
isOptionOn("red_miss") && ball.vy > 0 && !ball.hitSinceBounce;
const willMiss = ball.vy > 0 && !ball.hitSinceBounce;
const extraCombo = gameState.combo - 1;
if (

View file

@ -113,6 +113,10 @@ export function upgradeLevelAndMaxDisplay(
) {
const lvl = gameState.perks[upgrade.id];
const max = upgrade.max + gameState.perks.limitless;
return levelAndMaxBadge(lvl, max);
}
export function levelAndMaxBadge(lvl: number, max: number) {
return ` <span class="level ${lvl < max ? "can-upgrade" : "capped"}"><span>${lvl}</span><span>${max}</span></span>`;
}
@ -123,15 +127,19 @@ export function pickedUpgradesHTMl(gameState: GameState) {
const newMax = Math.max(0, u.max + gameState.perks.limitless);
const state = (gameState.perks[u.id] && 1) || (!newMax && 2) || 3;
const tooltip = escapeAttribute(u.fullHelp(gameState.perks[u.id] || 1));
return {
state,
html: `
<div class="upgrade ${["??", "used", "banned", "free"][state]}">
${icons["icon:" + u.id]}
<p>
<p data-tooltip="${tooltip}"
data-help-content="${tooltip}"
>
<strong>${u.name}</strong>
${upgradeLevelAndMaxDisplay(u, gameState)}
${u.help(Math.max(1, gameState.perks[u.id]))}
${u.help(gameState.perks[u.id] || 1)}
</p>
</div>
`,

View file

@ -68,23 +68,18 @@
"lab.reset": "إعادة ضبط",
"lab.select_level": "حدد المستوى للعب عليه",
"lab.unlocks_at": "يتم فتحه عند إجمالي النتيجة {{score}}",
"level_up.add_perks": "",
"level_up.challenges.catchRateGood.description": "",
"level_up.challenges.catchRateGood.name": "",
"level_up.challenges.intro": "",
"level_up.challenges.earned_medal": "",
"level_up.challenges.earned_medal_plural": "",
"level_up.challenges.gain": "",
"level_up.challenges.levelMisses.description": "",
"level_up.challenges.levelMisses.name": "",
"level_up.challenges.levelMisses.none": "",
"level_up.challenges.levelTime.description": "",
"level_up.challenges.levelTime.name": "",
"level_up.go": "",
"level_up.go_with_upgrades": "",
"level_up.gold": "",
"level_up.maxed_upgrade": "",
"level_up.no": "",
"level_up.pick": "",
"level_up.silver": "",
"level_up.challenges.no_gain": "",
"level_up.title": "لقد انتهيت للتو من المستوى {{level}}/{{max}}.",
"level_up.upgrade": "",
"level_up.upgrade_perks": "",
"main_menu.basic": "",
"main_menu.basic_help": "",
@ -169,11 +164,11 @@
"play.stats.levelWallBounces": "ارتدادات الحائط",
"score_panel.close_to_unlock": "فتح المستوى التالي:",
"score_panel.get_upgrades_to_unlock": "احصل على {{missingUpgrades}} واحصل على {{points}} نقطة إضافية لفتح المستوى \"{{level}}\"",
"score_panel.rerolls_count": "المستويات القادمة :",
"score_panel.score_to_unlock": "احصل على {{points}} نقطة إضافية لفتح المستوى \"{{level}}\"",
"score_panel.title": "{{score}} نقطة في المستوى {{level}}/{{max}} ",
"score_panel.upcoming_levels": "",
"score_panel.upgrade_point_count": "",
"score_panel.upgrade_points_count": "المستويات القادمة :",
"score_panel.upgrades_picked": "الترقيات التي تم اختيارها في هذه اللعبة:",
"settings.autoplay": "التشغيل التلقائي",
"settings.autoplay_help": "ابدأ جلسة مع ترقيات عشوائية ومجداف يتم التحكم فيه بواسطة الكمبيوتر",
@ -181,8 +176,6 @@
"settings.basic_help": "أداء أفضل.",
"settings.colorful_coins": "عملات معدنية ملونة",
"settings.colorful_coins_help": "تظهر العملات المعدنية دائمًا بلون الطوب",
"settings.comboIncreaseTexts": "إظهار +X باللون الذهبي",
"settings.comboIncreaseTexts_help": "عندما تزيد المجموعة",
"settings.contrast": "تباين عالي",
"settings.contrast_help": "تقديم أكثر ألوانًا وظلامًا",
"settings.donation_reminder": "ذكّرني بالتبرع",
@ -197,6 +190,8 @@
"settings.kid_help": "ابدأ الألعاب المستقبلية بـ \"الكرة الأبطأ\".",
"settings.language": "لغة",
"settings.language_help": "اختر لغة اللعبة",
"settings.level_unlocks_hints": "",
"settings.level_unlocks_hints_help": "",
"settings.load_save_file": "تحميل ملف الحفظ",
"settings.load_save_file_help": "حدد ملف الحفظ على جهازك",
"settings.max_coins": " {{max}} عملات معدنية على الشاشة كحد أقصى",
@ -212,8 +207,6 @@
"settings.record": "تسجيل مقاطع فيديو للعبة",
"settings.record_download": "تنزيل الفيديو ({{size}} ميجابايت)",
"settings.record_help": "احصل على فيديو لكل مستوى.",
"settings.red_miss": "تحذير ملكة جمال",
"settings.red_miss_help": "إظهار الجسيمات الحمراء حول الكرات التي تهبط دون إصابة.",
"settings.reset": "إعادة تعيين اللعبة",
"settings.reset_cancel": "لا",
"settings.reset_confirm": "نعم",
@ -226,7 +219,7 @@
"settings.show_fps": "عداد FPS",
"settings.show_fps_help": "مراقبة أداء التطبيق",
"settings.show_stats": "عرض الإحصائيات في الوقت الحقيقي",
"settings.show_stats_help": "العملات المعدنية، الوقت، الارتدادات، الأخطاء",
"settings.show_stats_help": "",
"settings.smooth_lighting": "إضاءة سلسة",
"settings.smooth_lighting_help": "طمس تأثيرات الإضاءة الخلفية لجعلها تبدو أقل مربعًا. يزيد هذا من التأخير.",
"settings.sounds": "أصوات اللعبة",
@ -255,7 +248,9 @@
"unlocks.minTotalScore": "تجميع إجمالي قدره{{score}}دولار",
"unlocks.reached": "أفضل نتيجة حصلت عليها كانت {{reached}}.",
"unlocks.title_upgrades": "لقد قمت بفتح {{unlocked}} ترقيات من أصل {{out_of}}",
"unlocks.try": "",
"unlocks.upgrades": "الترقيات المفتوحة",
"unlocks.use": "",
"upgrades.addiction.name": "مدمن",
"upgrades.addiction.tooltip": "+{{lvl}} مجموعة / لبنة، يتم إعادة تعيين المجموعة لمدة {{delay}}ثانية بعد كسر لبنة.",
"upgrades.addiction.verbose_description": "يبدأ العد التنازلي بعد كسر أول لبنة من كل مستوى، ويتوقف عند تدمير جميع الطوب.",

View file

@ -2492,41 +2492,6 @@
<folder_node>
<name>level_up</name>
<children>
<concept_node>
<name>add_perks</name>
<description/>
<comment/>
<translations>
<translation>
<language>ar-LB</language>
<approved>false</approved>
</translation>
<translation>
<language>de-DE</language>
<approved>false</approved>
</translation>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-CL</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>false</approved>
</translation>
<translation>
<language>ru-RU</language>
<approved>false</approved>
</translation>
<translation>
<language>tr-TR</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<folder_node>
<name>challenges</name>
<children>
@ -2606,7 +2571,77 @@
</children>
</folder_node>
<concept_node>
<name>intro</name>
<name>earned_medal</name>
<description/>
<comment/>
<translations>
<translation>
<language>ar-LB</language>
<approved>false</approved>
</translation>
<translation>
<language>de-DE</language>
<approved>false</approved>
</translation>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-CL</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>false</approved>
</translation>
<translation>
<language>ru-RU</language>
<approved>false</approved>
</translation>
<translation>
<language>tr-TR</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>earned_medal_plural</name>
<description/>
<comment/>
<translations>
<translation>
<language>ar-LB</language>
<approved>false</approved>
</translation>
<translation>
<language>de-DE</language>
<approved>false</approved>
</translation>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-CL</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>false</approved>
</translation>
<translation>
<language>ru-RU</language>
<approved>false</approved>
</translation>
<translation>
<language>tr-TR</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>gain</name>
<description/>
<comment/>
<translations>
@ -2713,6 +2748,41 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>none</name>
<description/>
<comment/>
<translations>
<translation>
<language>ar-LB</language>
<approved>false</approved>
</translation>
<translation>
<language>de-DE</language>
<approved>false</approved>
</translation>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-CL</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>false</approved>
</translation>
<translation>
<language>ru-RU</language>
<approved>false</approved>
</translation>
<translation>
<language>tr-TR</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
</children>
</folder_node>
<folder_node>
@ -2790,253 +2860,43 @@
</concept_node>
</children>
</folder_node>
<concept_node>
<name>no_gain</name>
<description/>
<comment/>
<translations>
<translation>
<language>ar-LB</language>
<approved>false</approved>
</translation>
<translation>
<language>de-DE</language>
<approved>false</approved>
</translation>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-CL</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>false</approved>
</translation>
<translation>
<language>ru-RU</language>
<approved>false</approved>
</translation>
<translation>
<language>tr-TR</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
</children>
</folder_node>
<concept_node>
<name>go</name>
<description/>
<comment/>
<translations>
<translation>
<language>ar-LB</language>
<approved>false</approved>
</translation>
<translation>
<language>de-DE</language>
<approved>false</approved>
</translation>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-CL</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>false</approved>
</translation>
<translation>
<language>ru-RU</language>
<approved>false</approved>
</translation>
<translation>
<language>tr-TR</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>go_with_upgrades</name>
<description/>
<comment/>
<translations>
<translation>
<language>ar-LB</language>
<approved>false</approved>
</translation>
<translation>
<language>de-DE</language>
<approved>false</approved>
</translation>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-CL</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>false</approved>
</translation>
<translation>
<language>ru-RU</language>
<approved>false</approved>
</translation>
<translation>
<language>tr-TR</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>gold</name>
<description/>
<comment/>
<translations>
<translation>
<language>ar-LB</language>
<approved>false</approved>
</translation>
<translation>
<language>de-DE</language>
<approved>false</approved>
</translation>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-CL</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>false</approved>
</translation>
<translation>
<language>ru-RU</language>
<approved>false</approved>
</translation>
<translation>
<language>tr-TR</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>maxed_upgrade</name>
<description/>
<comment/>
<translations>
<translation>
<language>ar-LB</language>
<approved>false</approved>
</translation>
<translation>
<language>de-DE</language>
<approved>false</approved>
</translation>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-CL</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>false</approved>
</translation>
<translation>
<language>ru-RU</language>
<approved>false</approved>
</translation>
<translation>
<language>tr-TR</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>no</name>
<description/>
<comment/>
<translations>
<translation>
<language>ar-LB</language>
<approved>false</approved>
</translation>
<translation>
<language>de-DE</language>
<approved>false</approved>
</translation>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-CL</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>false</approved>
</translation>
<translation>
<language>ru-RU</language>
<approved>false</approved>
</translation>
<translation>
<language>tr-TR</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>pick</name>
<description/>
<comment/>
<translations>
<translation>
<language>ar-LB</language>
<approved>false</approved>
</translation>
<translation>
<language>de-DE</language>
<approved>false</approved>
</translation>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-CL</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>false</approved>
</translation>
<translation>
<language>ru-RU</language>
<approved>false</approved>
</translation>
<translation>
<language>tr-TR</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>silver</name>
<description/>
<comment/>
<translations>
<translation>
<language>ar-LB</language>
<approved>false</approved>
</translation>
<translation>
<language>de-DE</language>
<approved>false</approved>
</translation>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-CL</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>false</approved>
</translation>
<translation>
<language>ru-RU</language>
<approved>false</approved>
</translation>
<translation>
<language>tr-TR</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>title</name>
<description/>
@ -3072,41 +2932,6 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>upgrade</name>
<description/>
<comment/>
<translations>
<translation>
<language>ar-LB</language>
<approved>false</approved>
</translation>
<translation>
<language>de-DE</language>
<approved>false</approved>
</translation>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-CL</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>false</approved>
</translation>
<translation>
<language>ru-RU</language>
<approved>false</approved>
</translation>
<translation>
<language>tr-TR</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>upgrade_perks</name>
<description/>
@ -6067,6 +5892,41 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>rerolls_count</name>
<description/>
<comment/>
<translations>
<translation>
<language>ar-LB</language>
<approved>false</approved>
</translation>
<translation>
<language>de-DE</language>
<approved>false</approved>
</translation>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-CL</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>false</approved>
</translation>
<translation>
<language>ru-RU</language>
<approved>false</approved>
</translation>
<translation>
<language>tr-TR</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>score_to_unlock</name>
<description/>
@ -6207,41 +6067,6 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>upgrade_points_count</name>
<description/>
<comment/>
<translations>
<translation>
<language>ar-LB</language>
<approved>false</approved>
</translation>
<translation>
<language>de-DE</language>
<approved>true</approved>
</translation>
<translation>
<language>en-US</language>
<approved>true</approved>
</translation>
<translation>
<language>es-CL</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>true</approved>
</translation>
<translation>
<language>ru-RU</language>
<approved>false</approved>
</translation>
<translation>
<language>tr-TR</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>upgrades_picked</name>
<description/>
@ -6492,76 +6317,6 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>comboIncreaseTexts</name>
<description/>
<comment/>
<translations>
<translation>
<language>ar-LB</language>
<approved>false</approved>
</translation>
<translation>
<language>de-DE</language>
<approved>true</approved>
</translation>
<translation>
<language>en-US</language>
<approved>true</approved>
</translation>
<translation>
<language>es-CL</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>true</approved>
</translation>
<translation>
<language>ru-RU</language>
<approved>false</approved>
</translation>
<translation>
<language>tr-TR</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>comboIncreaseTexts_help</name>
<description/>
<comment/>
<translations>
<translation>
<language>ar-LB</language>
<approved>false</approved>
</translation>
<translation>
<language>de-DE</language>
<approved>true</approved>
</translation>
<translation>
<language>en-US</language>
<approved>true</approved>
</translation>
<translation>
<language>es-CL</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>true</approved>
</translation>
<translation>
<language>ru-RU</language>
<approved>false</approved>
</translation>
<translation>
<language>tr-TR</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>contrast</name>
<description/>
@ -7052,6 +6807,76 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>level_unlocks_hints</name>
<description/>
<comment/>
<translations>
<translation>
<language>ar-LB</language>
<approved>false</approved>
</translation>
<translation>
<language>de-DE</language>
<approved>false</approved>
</translation>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-CL</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>false</approved>
</translation>
<translation>
<language>ru-RU</language>
<approved>false</approved>
</translation>
<translation>
<language>tr-TR</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>level_unlocks_hints_help</name>
<description/>
<comment/>
<translations>
<translation>
<language>ar-LB</language>
<approved>false</approved>
</translation>
<translation>
<language>de-DE</language>
<approved>false</approved>
</translation>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-CL</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>false</approved>
</translation>
<translation>
<language>ru-RU</language>
<approved>false</approved>
</translation>
<translation>
<language>tr-TR</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>load_save_file</name>
<description/>
@ -7577,76 +7402,6 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>red_miss</name>
<description/>
<comment/>
<translations>
<translation>
<language>ar-LB</language>
<approved>false</approved>
</translation>
<translation>
<language>de-DE</language>
<approved>true</approved>
</translation>
<translation>
<language>en-US</language>
<approved>true</approved>
</translation>
<translation>
<language>es-CL</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>true</approved>
</translation>
<translation>
<language>ru-RU</language>
<approved>false</approved>
</translation>
<translation>
<language>tr-TR</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>red_miss_help</name>
<description/>
<comment/>
<translations>
<translation>
<language>ar-LB</language>
<approved>false</approved>
</translation>
<translation>
<language>de-DE</language>
<approved>true</approved>
</translation>
<translation>
<language>en-US</language>
<approved>true</approved>
</translation>
<translation>
<language>es-CL</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>true</approved>
</translation>
<translation>
<language>ru-RU</language>
<approved>false</approved>
</translation>
<translation>
<language>tr-TR</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>reset</name>
<description/>
@ -9097,6 +8852,41 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>try</name>
<description/>
<comment>This needs to be kept very short</comment>
<translations>
<translation>
<language>ar-LB</language>
<approved>false</approved>
</translation>
<translation>
<language>de-DE</language>
<approved>false</approved>
</translation>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-CL</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>false</approved>
</translation>
<translation>
<language>ru-RU</language>
<approved>false</approved>
</translation>
<translation>
<language>tr-TR</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>upgrades</name>
<description/>
@ -9132,6 +8922,41 @@
</translation>
</translations>
</concept_node>
<concept_node>
<name>use</name>
<description/>
<comment/>
<translations>
<translation>
<language>ar-LB</language>
<approved>false</approved>
</translation>
<translation>
<language>de-DE</language>
<approved>false</approved>
</translation>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>es-CL</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>false</approved>
</translation>
<translation>
<language>ru-RU</language>
<approved>false</approved>
</translation>
<translation>
<language>tr-TR</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
</children>
</folder_node>
<folder_node>

View file

@ -68,23 +68,18 @@
"lab.reset": "Zurücksetzen",
"lab.select_level": "Wähle ein Level zum Spielen",
"lab.unlocks_at": "Wird bei Gesamtpunktzahl {{score}} freigeschaltet",
"level_up.add_perks": "",
"level_up.challenges.catchRateGood.description": "",
"level_up.challenges.catchRateGood.name": "",
"level_up.challenges.intro": "",
"level_up.challenges.earned_medal": "",
"level_up.challenges.earned_medal_plural": "",
"level_up.challenges.gain": "",
"level_up.challenges.levelMisses.description": "",
"level_up.challenges.levelMisses.name": "",
"level_up.challenges.levelMisses.none": "",
"level_up.challenges.levelTime.description": "",
"level_up.challenges.levelTime.name": "",
"level_up.go": "",
"level_up.go_with_upgrades": "",
"level_up.gold": "",
"level_up.maxed_upgrade": "",
"level_up.no": "",
"level_up.pick": "",
"level_up.silver": "",
"level_up.challenges.no_gain": "",
"level_up.title": "Du hast gerade Level {{level}}/{{max}} beendet.",
"level_up.upgrade": "",
"level_up.upgrade_perks": "",
"main_menu.basic": "",
"main_menu.basic_help": "",
@ -169,11 +164,11 @@
"play.stats.levelWallBounces": "Wandaufpralle",
"score_panel.close_to_unlock": "Nächstes Level freischalten:",
"score_panel.get_upgrades_to_unlock": "Hole {{missingUpgrades}} und erziele {{points}} mehr Punkte, um Level \"{{level}}\" freizuschalten.",
"score_panel.rerolls_count": "Kommende Level:",
"score_panel.score_to_unlock": "Erziele {{points}} mehr Punkte, um Level \"{{level}}\" freizuschalten.",
"score_panel.title": "{{score}} Punkte in Level {{level}}/{{max}} ",
"score_panel.upcoming_levels": "",
"score_panel.upgrade_point_count": "",
"score_panel.upgrade_points_count": "Kommende Level:",
"score_panel.upgrades_picked": "In diesem Spiel freigeschaltete Upgrades:",
"settings.autoplay": "Automatisch spielen",
"settings.autoplay_help": "Starte eine Sitzung mit zufälligen Upgrades und einem computergesteuerten Paddel",
@ -181,8 +176,6 @@
"settings.basic_help": "Bessere Leistung",
"settings.colorful_coins": "Bunte Münzen",
"settings.colorful_coins_help": "Münzen spawnen immer in der Farbe des Steins",
"settings.comboIncreaseTexts": "+X golden anzeigen",
"settings.comboIncreaseTexts_help": "Wenn die Combo zunimmt",
"settings.contrast": "Hoher Kontrast",
"settings.contrast_help": "Buntes und dunkles Rendering",
"settings.donation_reminder": "Erinnere mich an eine Spende",
@ -197,6 +190,8 @@
"settings.kid_help": "Beginne künftige Spiele mit \"langsameren Ball\".",
"settings.language": "Sprache",
"settings.language_help": "Wähle die Sprache des Spiels",
"settings.level_unlocks_hints": "",
"settings.level_unlocks_hints_help": "",
"settings.load_save_file": "Speicherdatei laden",
"settings.load_save_file_help": "Wähle eine Speicherdatei auf Ihrem Gerät",
"settings.max_coins": " {{max}} Münzen auf dem Bildschirm maximal",
@ -212,8 +207,6 @@
"settings.record": "Spielvideos aufnehmen",
"settings.record_download": "Video herunterladen ({{size}} MB)",
"settings.record_help": "Bekomme ein Video von jedem Level.",
"settings.red_miss": "Verstecke Warnung",
"settings.red_miss_help": "Zeige rote Partikel um Bälle, die ohne Treffer zu Boden gehen.",
"settings.reset": "Spiel zurücksetzen",
"settings.reset_cancel": "Nein",
"settings.reset_confirm": "Ja",
@ -226,7 +219,7 @@
"settings.show_fps": "FPS-Zähler",
"settings.show_fps_help": "Überwache die Leistung der Anwendung",
"settings.show_stats": "Echtzeit-Statistiken anzeigen",
"settings.show_stats_help": "Münzen, Zeit, Sprünge, Verfehlungen",
"settings.show_stats_help": "Münzen, Zeit, Verfehlungen",
"settings.smooth_lighting": "Sanfte Beleuchtung",
"settings.smooth_lighting_help": "Verwische die Lichteffekte im Hintergrund, damit sie weniger quadratisch aussehen. Erhöht die Verzögerung.",
"settings.sounds": "Spiel-Geräusche",
@ -255,7 +248,9 @@
"unlocks.minTotalScore": "Summiert ein Punktstand von ${{score}}",
"unlocks.reached": "Ihr bester Punktstand war {{reached}}.",
"unlocks.title_upgrades": "Du hast {{unlocked}} Upgrades von {{out_of}} freigeschaltet.",
"unlocks.try": "",
"unlocks.upgrades": "Freigeschaltete Upgrades",
"unlocks.use": "",
"upgrades.addiction.name": "Sucht",
"upgrades.addiction.tooltip": "+{{lvl}} Combo / Stein, Combo wird {{delay}}s nach Zerbrechen eines Steins zurückgesetzt.",
"upgrades.addiction.verbose_description": "Der Countdown beginnt erst nach dem Zerbrechen des ersten Steins eines jeden Levels. Er stoppt, sobald alle Ziegel zerstört sind.",

View file

@ -68,24 +68,19 @@
"lab.reset": "Reset",
"lab.select_level": "Select a level to play on",
"lab.unlocks_at": "Unlocks at total score {{score}}",
"level_up.add_perks": "Add perks",
"level_up.challenges.catchRateGood.description": "Catch {{gold}}% of coins for gold, {{silver}}% for silver. Last level you caught {{caught}} coins out of {{total}}",
"level_up.challenges.catchRateGood.name": "{{value}}% coins caught ",
"level_up.challenges.intro": "Challenges",
"level_up.challenges.catchRateGood.description": "Catch {{gold}}% of coins for gold, {{silver}}% for silver.",
"level_up.challenges.catchRateGood.name": "{{caught}} coins caught out of {{total}}",
"level_up.challenges.earned_medal": "You earned a medal",
"level_up.challenges.earned_medal_plural": "You earned {{count}} medals",
"level_up.challenges.gain": "+{{choices}} choices and +{{up}} upgrades",
"level_up.challenges.levelMisses.description": "You'll get a gold medal if you miss less than {{gold}} times, silver under {{silver}}. ",
"level_up.challenges.levelMisses.name": "{{value}} missed shots",
"level_up.challenges.levelMisses.none": "No missed shots",
"level_up.challenges.levelTime.description": "You'll get a gold medal under {{gold}}s and a silver medal under {{silver}}s. ",
"level_up.challenges.levelTime.name": "{{value}}s play time",
"level_up.go": "Continue to level \"{{name}}\"",
"level_up.go_with_upgrades": "Spend your {{count}} upgrade points first",
"level_up.gold": "You gained two choices and upgrade point.",
"level_up.maxed_upgrade": "\"{{name}}\" is at max level",
"level_up.no": "You did not meet the reward condition.",
"level_up.pick": "Pick",
"level_up.silver": "You gained one choice and upgrade point.",
"level_up.challenges.no_gain": "No gain",
"level_up.title": "Level {{level}}/{{max}} cleared",
"level_up.upgrade": "Upgrade",
"level_up.upgrade_perks": "Upgrade your perks",
"level_up.upgrade_perks": "You caught {{coins}} coins. Pick {{count}} upgrade(s) below. ",
"main_menu.basic": "",
"main_menu.basic_help": "",
"main_menu.colorful_coins": "",
@ -169,20 +164,18 @@
"play.stats.levelWallBounces": "Wall bounces",
"score_panel.close_to_unlock": "Next level unlock :",
"score_panel.get_upgrades_to_unlock": "Get {{missingUpgrades}} and score {{points}} more points to unlock level \"{{level}}\"",
"score_panel.rerolls_count": "Upcoming levels :",
"score_panel.score_to_unlock": "Score {{points}} more points to unlock level \"{{level}}\"",
"score_panel.title": "{{score}} points at level {{level}}/{{max}} ",
"score_panel.upcoming_levels": "Upcoming levels",
"score_panel.upgrade_point_count": "You have accumulated {{count}} upgrade points.",
"score_panel.upgrade_points_count": "Upcoming levels :",
"score_panel.upgrades_picked": "Upgrades picked : ",
"score_panel.upgrades_picked": "Your current build :",
"settings.autoplay": "Auto play",
"settings.autoplay_help": "Start a session with random upgrades and a computer controlled paddle",
"settings.basic": "Basic graphics",
"settings.basic_help": "Better performance.",
"settings.colorful_coins": "Colorful coins",
"settings.colorful_coins_help": "Coins always spawn of the color of the brick",
"settings.comboIncreaseTexts": "Show +X in gold",
"settings.comboIncreaseTexts_help": "When the combo increase",
"settings.contrast": "High Contrast",
"settings.contrast_help": "More colorful and dark rendering",
"settings.donation_reminder": "Remind me to donate",
@ -197,6 +190,8 @@
"settings.kid_help": "Start future games with \"slower ball\".",
"settings.language": "Language",
"settings.language_help": "Choose the game's language",
"settings.level_unlocks_hints": "Levels unlock hints",
"settings.level_unlocks_hints_help": "Help you pick the right perks to unlock levels",
"settings.load_save_file": "Load save file",
"settings.load_save_file_help": "Select a save file on your device",
"settings.max_coins": " {{max}} coins on screen maximum",
@ -212,8 +207,6 @@
"settings.record": "Record gameplay videos",
"settings.record_download": "Download video ({{size}} MB)",
"settings.record_help": "Get a video of each level.",
"settings.red_miss": "Miss warning",
"settings.red_miss_help": "Show red particles around balls going down without a hit.",
"settings.reset": "Reset Game",
"settings.reset_cancel": "No",
"settings.reset_confirm": "Yes",
@ -226,7 +219,7 @@
"settings.show_fps": "FPS counter",
"settings.show_fps_help": "Monitor the app's performance",
"settings.show_stats": "Show real time stats",
"settings.show_stats_help": "Coins, time, bounces, misses",
"settings.show_stats_help": "Coins, time, misses",
"settings.smooth_lighting": "Smooth lighting",
"settings.smooth_lighting_help": "Blur the background light effects to make them look less square. Increases lag.",
"settings.sounds": "Game sounds",
@ -255,7 +248,9 @@
"unlocks.minTotalScore": "Accumulate a total of ${{score}}",
"unlocks.reached": "Your best score was {{reached}}.",
"unlocks.title_upgrades": "{{unlocked}} / {{out_of}} upgrades unlocked",
"unlocks.try": "Try",
"unlocks.upgrades": "Unlocked upgrades",
"unlocks.use": "Use",
"upgrades.addiction.name": "Addiction",
"upgrades.addiction.tooltip": "More coins if you break bricks without pause",
"upgrades.addiction.verbose_description": "+{{lvl}} combo / brick broken, combo resets {{delay}}s after breaking a brick. The countdown only starts after breaking the first brick of each level. It stops as soon as all bricks are destroyed.",

View file

@ -68,23 +68,18 @@
"lab.reset": "Reiniciar",
"lab.select_level": "Selecciona un nivel para jugar",
"lab.unlocks_at": "Desbloqueado a partir de una puntuación total de {{score}}.",
"level_up.add_perks": "",
"level_up.challenges.catchRateGood.description": "",
"level_up.challenges.catchRateGood.name": "",
"level_up.challenges.intro": "",
"level_up.challenges.earned_medal": "",
"level_up.challenges.earned_medal_plural": "",
"level_up.challenges.gain": "",
"level_up.challenges.levelMisses.description": "",
"level_up.challenges.levelMisses.name": "",
"level_up.challenges.levelMisses.none": "",
"level_up.challenges.levelTime.description": "",
"level_up.challenges.levelTime.name": "",
"level_up.go": "",
"level_up.go_with_upgrades": "",
"level_up.gold": "",
"level_up.maxed_upgrade": "",
"level_up.no": "",
"level_up.pick": "",
"level_up.silver": "",
"level_up.challenges.no_gain": "",
"level_up.title": "Acabas de completar el nivel {{level}}/{{max}}.",
"level_up.upgrade": "",
"level_up.upgrade_perks": "",
"main_menu.basic": "Gráficos simplificados",
"main_menu.basic_help": "Mejor rendimiento.",
@ -169,11 +164,11 @@
"play.stats.levelWallBounces": "Rebota en las paredes",
"score_panel.close_to_unlock": "Siguiente nivel desbloqueado:",
"score_panel.get_upgrades_to_unlock": "Consigue {{missingUpgrades}} y coge {{points}} monedas extra para desbloquear el nivel \"{{level}}\".",
"score_panel.rerolls_count": "Niveles de partido :",
"score_panel.score_to_unlock": "Coge {{points}} monedas más para desbloquear el nivel \"{{level}}\".",
"score_panel.title": "{{score}} puntos en {{level}}/{{max}} nivel",
"score_panel.upcoming_levels": "",
"score_panel.upgrade_point_count": "",
"score_panel.upgrade_points_count": "Niveles de partido :",
"score_panel.upgrades_picked": "Mejoras elegidas durante el juego :",
"settings.autoplay": "Auto-reproducción",
"settings.autoplay_help": "Comienza una sesión con mejoras aleatorias y una paleta controlada por computadora.",
@ -181,8 +176,6 @@
"settings.basic_help": "Mejor rendimiento.",
"settings.colorful_coins": "Monedas de colores",
"settings.colorful_coins_help": "Las monedas siempre aparecen del color del ladrillo.",
"settings.comboIncreaseTexts": "Mostrar +X en oro",
"settings.comboIncreaseTexts_help": "Cuando el combo aumenta",
"settings.contrast": "Alto contraste",
"settings.contrast_help": "Representación más colorida y oscura.",
"settings.donation_reminder": "Recuérdame que debo donar",
@ -197,6 +190,8 @@
"settings.kid_help": "Comience los juegos futuros con \"pelota más lenta\".",
"settings.language": "Idioma",
"settings.language_help": "Elige el idioma del juego",
"settings.level_unlocks_hints": "",
"settings.level_unlocks_hints_help": "",
"settings.load_save_file": "Cargar archivo guardado",
"settings.load_save_file_help": "Seleccione un archivo guardado en su dispositivo",
"settings.max_coins": " {{max}} monedas en pantalla máximo",
@ -212,8 +207,6 @@
"settings.record": "Grabar vídeos de juego",
"settings.record_download": "Descargar vídeo ({{size}} MB)",
"settings.record_help": "Obtenga un vídeo de cada nivel.",
"settings.red_miss": "Advertencia de pérdida",
"settings.red_miss_help": "Muestra partículas rojas alrededor de las bolas que caen sin impacto.",
"settings.reset": "Reiniciar Juego",
"settings.reset_cancel": "No",
"settings.reset_confirm": "Sí",
@ -226,7 +219,7 @@
"settings.show_fps": "Contador de FPS",
"settings.show_fps_help": "Supervisar el rendimiento de la aplicación",
"settings.show_stats": "Mostrar estadísticas en tiempo real",
"settings.show_stats_help": "Monedas, tiempo, rebotes, fallos.",
"settings.show_stats_help": "Monedas, tiempo, fallos.",
"settings.smooth_lighting": "Iluminación suave",
"settings.smooth_lighting_help": "Desenfoca los efectos de luz de fondo para que se vean menos cuadrados. Aumenta el retardo.",
"settings.sounds": "Sonidos del juego",
@ -255,7 +248,9 @@
"unlocks.minTotalScore": "Acumula un total de ${{score}}",
"unlocks.reached": "Tu mejor puntuación hasta ahora es {{reached}}.",
"unlocks.title_upgrades": "Has desbloqueado {{unlocked}} mejoras en {{out_of}}.",
"unlocks.try": "",
"unlocks.upgrades": "Mejoras desbloqueadas",
"unlocks.use": "",
"upgrades.addiction.name": "Adicción",
"upgrades.addiction.tooltip": "+{{lvl}} combo / ladrillo, combo perdido después de {{delay}}s sin romper ningún ladrillo",
"upgrades.addiction.verbose_description": "La cuenta atrás sólo comienza una vez que se ha destruido el primer ladrillo del nivel, y se detiene en cuanto no hay más ladrillos.",

View file

@ -68,23 +68,18 @@
"lab.reset": "Réinitialiser",
"lab.select_level": "Sélectionnez un niveau sur lequel jouer",
"lab.unlocks_at": "Déverrouillé à partir d'un score total de {{score}}",
"level_up.add_perks": "",
"level_up.challenges.catchRateGood.description": "",
"level_up.challenges.catchRateGood.name": "",
"level_up.challenges.intro": "",
"level_up.challenges.earned_medal": "",
"level_up.challenges.earned_medal_plural": "",
"level_up.challenges.gain": "",
"level_up.challenges.levelMisses.description": "",
"level_up.challenges.levelMisses.name": "",
"level_up.challenges.levelMisses.none": "",
"level_up.challenges.levelTime.description": "",
"level_up.challenges.levelTime.name": "",
"level_up.go": "",
"level_up.go_with_upgrades": "",
"level_up.gold": "",
"level_up.maxed_upgrade": "",
"level_up.no": "",
"level_up.pick": "",
"level_up.silver": "",
"level_up.challenges.no_gain": "",
"level_up.title": "Vous venez de terminer le niveau {{level}}/{{max}}.",
"level_up.upgrade": "",
"level_up.upgrade_perks": "",
"main_menu.basic": "",
"main_menu.basic_help": "",
@ -169,11 +164,11 @@
"play.stats.levelWallBounces": "Rebonds sur les murs",
"score_panel.close_to_unlock": "Prochain niveau débloqué : ",
"score_panel.get_upgrades_to_unlock": "Obtenez {{missingUpgrades}} et attrapez {{points}} pièces supplémentaires pour débloquer le niveau « {{level}} »",
"score_panel.rerolls_count": "Niveaux de la parties : ",
"score_panel.score_to_unlock": "Attrapez {{points}} pièces supplémentaires pour débloquer le niveau « {{level}} »",
"score_panel.title": "{{score}} points au niveau {{level}}/{{max}} ",
"score_panel.upcoming_levels": "",
"score_panel.upgrade_point_count": "",
"score_panel.upgrade_points_count": "Niveaux de la parties : ",
"score_panel.upgrades_picked": "Améliorations choisies :",
"settings.autoplay": "Lecture automatique",
"settings.autoplay_help": "Démarrez une session avec des mises à niveau aléatoires et une pagaie contrôlée par ordinateur",
@ -181,8 +176,6 @@
"settings.basic_help": "Meilleures performances.",
"settings.colorful_coins": "Pièces colorées",
"settings.colorful_coins_help": "Les pièces apparaissent toujours de la couleur de la brique",
"settings.comboIncreaseTexts": "Afficher un +X doré",
"settings.comboIncreaseTexts_help": "Quand le combo augmente",
"settings.contrast": "Contraste élevé",
"settings.contrast_help": "Affichage plus contrasté et coloré",
"settings.donation_reminder": "Me rappeler de donner",
@ -197,6 +190,8 @@
"settings.kid_help": "Balle plus lente",
"settings.language": "Langue",
"settings.language_help": "Changer la langue d'affichage",
"settings.level_unlocks_hints": "",
"settings.level_unlocks_hints_help": "",
"settings.load_save_file": "Charger une sauvegarde",
"settings.load_save_file_help": "Depuis un fichier ",
"settings.max_coins": "{{max}} pièces affichées maximum",
@ -212,8 +207,6 @@
"settings.record": "Enregistrer des vidéos de jeu",
"settings.record_download": "Télécharger la vidéo ({{size}} MB)",
"settings.record_help": "Obtenez une vidéo de chaque niveau.",
"settings.red_miss": "Balles ratées",
"settings.red_miss_help": "Afficher des particules rouges autours des balles qui redescendent sans avoir touché une brique.",
"settings.reset": "Réinitialiser le jeu",
"settings.reset_cancel": "Non",
"settings.reset_confirm": "Oui",
@ -226,7 +219,7 @@
"settings.show_fps": "Compteur de FPS",
"settings.show_fps_help": "Surveiller la performance du jeu",
"settings.show_stats": "Statistiques en temps réel",
"settings.show_stats_help": "Pièces, temps, rebonds, ratés",
"settings.show_stats_help": "Pièces, temps, ratés",
"settings.smooth_lighting": "Éclairage doux",
"settings.smooth_lighting_help": "Floutez les effets de lumière d'arrière-plan pour les rendre moins carrés. Augmente le décalage.",
"settings.sounds": "Sons du jeu",
@ -255,7 +248,9 @@
"unlocks.minTotalScore": "Accumuler un total de ${{score}}",
"unlocks.reached": "Votre meilleur score pour l'instant est {{reached}}.",
"unlocks.title_upgrades": "Vous avez débloqué {{unlocked}} améliorations sur {{out_of}}",
"unlocks.try": "",
"unlocks.upgrades": "Mises à niveau débloquées",
"unlocks.use": "",
"upgrades.addiction.name": "Addiction",
"upgrades.addiction.tooltip": "+{{lvl}} combo / brique cassée, combo perdu après {{delay}}s sans casser de briques",
"upgrades.addiction.verbose_description": "Le décompte ne commence qu'à parti de la destruction de la première brique du niveau, et s'arrête dès qu'il n'y a plus de briques. ",

View file

@ -68,23 +68,18 @@
"lab.reset": "Перезагрузить",
"lab.select_level": "Выберите уровень для игры",
"lab.unlocks_at": "Открывается при общем количестве очков {{score}}",
"level_up.add_perks": "",
"level_up.challenges.catchRateGood.description": "",
"level_up.challenges.catchRateGood.name": "",
"level_up.challenges.intro": "",
"level_up.challenges.earned_medal": "",
"level_up.challenges.earned_medal_plural": "",
"level_up.challenges.gain": "",
"level_up.challenges.levelMisses.description": "",
"level_up.challenges.levelMisses.name": "",
"level_up.challenges.levelMisses.none": "",
"level_up.challenges.levelTime.description": "",
"level_up.challenges.levelTime.name": "",
"level_up.go": "",
"level_up.go_with_upgrades": "",
"level_up.gold": "",
"level_up.maxed_upgrade": "",
"level_up.no": "",
"level_up.pick": "",
"level_up.silver": "",
"level_up.challenges.no_gain": "",
"level_up.title": "Вы только что закончили уровень {{level}}/{{max}}.",
"level_up.upgrade": "",
"level_up.upgrade_perks": "",
"main_menu.basic": "",
"main_menu.basic_help": "",
@ -169,11 +164,11 @@
"play.stats.levelWallBounces": "Отскоки от стены",
"score_panel.close_to_unlock": "Разблокировка следующего уровня:",
"score_panel.get_upgrades_to_unlock": "Наберите {{missingUpgrades}} и наберите {{points}} больше очков, чтобы разблокировать уровень \"{{level}}\"",
"score_panel.rerolls_count": "Предстоящие уровни :",
"score_panel.score_to_unlock": "Наберите {{points}} больше очков, чтобы разблокировать уровень \"{{level}}\"",
"score_panel.title": "{{score}} очков на уровне {{level}}/{{max}} ",
"score_panel.upcoming_levels": "",
"score_panel.upgrade_point_count": "",
"score_panel.upgrade_points_count": "Предстоящие уровни :",
"score_panel.upgrades_picked": "Обновления, выбранные в этой игре, запускаются :",
"settings.autoplay": "Автоматическое воспроизведение",
"settings.autoplay_help": "Начните сеанс со случайными улучшениями и компьютерным веслом",
@ -181,8 +176,6 @@
"settings.basic_help": "Улучшенная производительность.",
"settings.colorful_coins": "Разноцветные монеты",
"settings.colorful_coins_help": "Монеты всегда спаунятся того же цвета, что и кирпичи",
"settings.comboIncreaseTexts": "Показать +X в золотом цвете",
"settings.comboIncreaseTexts_help": "Когда комбо увеличивается",
"settings.contrast": "Высокая контрастность",
"settings.contrast_help": "Более красочная и темная визуализация",
"settings.donation_reminder": "Напомните мне о пожертвовании",
@ -197,6 +190,8 @@
"settings.kid_help": "Начинайте будущие игры с \"медленного мяча\".",
"settings.language": "Язык",
"settings.language_help": "Выберите язык игры",
"settings.level_unlocks_hints": "",
"settings.level_unlocks_hints_help": "",
"settings.load_save_file": "Загрузить файл сохранения",
"settings.load_save_file_help": "Выберите файл сохранения на вашем устройстве",
"settings.max_coins": " {{max}} монет на экране максимум",
@ -212,8 +207,6 @@
"settings.record": "Запись видеороликов игрового процесса",
"settings.record_download": "Скачать видео ({{size}} МБ)",
"settings.record_help": "Получите видеозапись каждого уровня.",
"settings.red_miss": "Мисс предупреждение",
"settings.red_miss_help": "Покажите красные частицы вокруг мячей, падающих без попадания.",
"settings.reset": "Перезагрузка игры",
"settings.reset_cancel": "Нет",
"settings.reset_confirm": "Да",
@ -226,7 +219,7 @@
"settings.show_fps": "Счетчик FPS",
"settings.show_fps_help": "Контролируйте работу приложения",
"settings.show_stats": "Показывайте статистику в реальном времени",
"settings.show_stats_help": "Монеты, время, отскоки, промахи",
"settings.show_stats_help": "Монеты, время, промахи",
"settings.smooth_lighting": "Плавное освещение",
"settings.smooth_lighting_help": "Размыть фоновые световые эффекты, чтобы они выглядели менее квадратными. Увеличивает задержку.",
"settings.sounds": "Звуки игры",
@ -255,7 +248,9 @@
"unlocks.minTotalScore": "Накопите в общей сложности ${{score}}",
"unlocks.reached": "Ваш лучший результат - {{reached}}.",
"unlocks.title_upgrades": "Вы разблокировали {{unlocked}} обновлений из {{out_of}}",
"unlocks.try": "",
"unlocks.upgrades": "Разблокированные улучшения",
"unlocks.use": "",
"upgrades.addiction.name": "Наркомания",
"upgrades.addiction.tooltip": "+{{lvl}} комбо / кирпич, комбо сбрасывается на {{delay}}с после разрушения кирпича.",
"upgrades.addiction.verbose_description": "Отсчет начинается только после разрушения первого кирпича на каждом уровне. Он остановится, как только все кирпичи будут уничтожены.",

View file

@ -68,23 +68,18 @@
"lab.reset": "Sıfırla",
"lab.select_level": "Oynamak için bir seviye seçin",
"lab.unlocks_at": "Toplam puan {{score}}olduğunda açılır",
"level_up.add_perks": "",
"level_up.challenges.catchRateGood.description": "",
"level_up.challenges.catchRateGood.name": "",
"level_up.challenges.intro": "",
"level_up.challenges.earned_medal": "",
"level_up.challenges.earned_medal_plural": "",
"level_up.challenges.gain": "",
"level_up.challenges.levelMisses.description": "",
"level_up.challenges.levelMisses.name": "",
"level_up.challenges.levelMisses.none": "",
"level_up.challenges.levelTime.description": "",
"level_up.challenges.levelTime.name": "",
"level_up.go": "",
"level_up.go_with_upgrades": "",
"level_up.gold": "",
"level_up.maxed_upgrade": "",
"level_up.no": "",
"level_up.pick": "",
"level_up.silver": "",
"level_up.challenges.no_gain": "",
"level_up.title": " {{level}}/{{max}}seviyesini yeni bitirdiniz.",
"level_up.upgrade": "",
"level_up.upgrade_perks": "",
"main_menu.basic": "",
"main_menu.basic_help": "",
@ -169,11 +164,11 @@
"play.stats.levelWallBounces": "Duvar sıçramaları",
"score_panel.close_to_unlock": "Sonraki seviyenin kilidini aç:",
"score_panel.get_upgrades_to_unlock": " {{missingUpgrades}} alın ve \"{{level}}\" seviyesinin kilidini açmak için {{points}} puan daha kazanın",
"score_panel.rerolls_count": "Yaklaşan seviyeler :",
"score_panel.score_to_unlock": "\"{{level}}\" seviyesini açmak için {{points}} puan daha kazanın",
"score_panel.title": " {{level}}/{{max}} seviyesinde{{score}} puan",
"score_panel.upcoming_levels": "",
"score_panel.upgrade_point_count": "",
"score_panel.upgrade_points_count": "Yaklaşan seviyeler :",
"score_panel.upgrades_picked": "Bu oyun çalışmasında seçilen yükseltmeler:",
"settings.autoplay": "Otomatik oynatma",
"settings.autoplay_help": "Rastgele yükseltmeler ve bilgisayar kontrollü bir kürekle bir oturum başlatın",
@ -181,8 +176,6 @@
"settings.basic_help": "Daha iyi performans.",
"settings.colorful_coins": "Renkli madeni paralar",
"settings.colorful_coins_help": "Madeni paralar her zaman tuğlanın renginde ortaya çıkar",
"settings.comboIncreaseTexts": "Altında +X göster",
"settings.comboIncreaseTexts_help": "Kombo arttığında",
"settings.contrast": "Yüksek Kontrast",
"settings.contrast_help": "Daha renkli ve koyu görüntü oluşturma",
"settings.donation_reminder": "Bana bağış yapmayı hatırlat",
@ -197,6 +190,8 @@
"settings.kid_help": "Gelecek oyunlara \"daha yavaş top\" ile başlayın.",
"settings.language": "Dil",
"settings.language_help": "Oyunun dilini seçin",
"settings.level_unlocks_hints": "",
"settings.level_unlocks_hints_help": "",
"settings.load_save_file": "Kayıt dosyasını yükle",
"settings.load_save_file_help": "Cihazınızda bir kayıt dosyası seçin",
"settings.max_coins": "Ekranda maksimum {{max}} jeton var",
@ -212,8 +207,6 @@
"settings.record": "Oyun videolarını kaydedin",
"settings.record_download": "Videoyu indir ({{size}} MB)",
"settings.record_help": "Her seviyenin videosunu edinin.",
"settings.red_miss": "Uyarıyı kaçırdım",
"settings.red_miss_help": "Vuruş yapmadan aşağı inen topların etrafında kırmızı parçacıklar göster.",
"settings.reset": "Oyunu Sıfırla",
"settings.reset_cancel": "HAYIR",
"settings.reset_confirm": "Evet",
@ -226,7 +219,7 @@
"settings.show_fps": "FPS Sayacı",
"settings.show_fps_help": "Uygulamanın performansını izleyin",
"settings.show_stats": "Gerçek zamanlı istatistikleri göster",
"settings.show_stats_help": "Paralar, zaman, sekmeler, ıskalar",
"settings.show_stats_help": "Paralar, zaman, ıskalar",
"settings.smooth_lighting": "Pürüzsüz aydınlatma",
"settings.smooth_lighting_help": "Arka plan ışık efektlerini daha az kare görünmeleri için bulanıklaştırın. Gecikmeyi artırır.",
"settings.sounds": "Oyun sesleri",
@ -255,7 +248,9 @@
"unlocks.minTotalScore": "Toplam ${{score}}biriktirin",
"unlocks.reached": "En iyi skorunuz {{reached}}idi.",
"unlocks.title_upgrades": " {{out_of}}yükseltmeden {{unlocked}} tanesinin kilidini açtınız",
"unlocks.try": "",
"unlocks.upgrades": "Kilidi açılmış yükseltmeler",
"unlocks.use": "",
"upgrades.addiction.name": "Bağımlılık",
"upgrades.addiction.tooltip": "+{{lvl}} kombo / tuğla, kombo bir tuğlayı kırdıktan sonra {{delay}}saniye içinde sıfırlanır.",
"upgrades.addiction.verbose_description": "Geri sayım yalnızca her seviyenin ilk tuğlası kırıldıktan sonra başlar. Tüm tuğlalar yok edildiğinde durur.",

View file

@ -144,7 +144,7 @@ export function newGameState(params: RunParams): GameState {
needsRender: true,
autoCleanUses: 0,
...defaultSounds(),
upgrade_points: 0,
rerolls: 0,
creative:
params?.computer_controlled ||
sumOfValues(params.perks) > 1 ||

View file

@ -1,10 +1,10 @@
import { GameState } from "./types";
import { GameState, PerkId } from "./types";
import { asyncAlert } from "./asyncAlert";
import { t } from "./i18n/i18n";
import { levelsListHTMl, max_levels, pickedUpgradesHTMl } from "./game_utils";
import { getCreativeModeWarning, getHistory } from "./gameOver";
import { pause } from "./game";
import { allLevels, icons } from "./loadGameData";
import { allLevels, icons, upgrades } from "./loadGameData";
import { firstWhere } from "./pure_functions";
import { getSettingValue, getTotalScore } from "./settings";
import {
@ -12,6 +12,7 @@ import {
reasonLevelIsLocked,
upgradeName,
} from "./get_level_unlock_condition";
import { isOptionOn } from "./options";
export async function openScorePanel(gameState: GameState) {
pause(true);
@ -28,9 +29,9 @@ export async function openScorePanel(gameState: GameState) {
pickedUpgradesHTMl(gameState),
levelsListHTMl(gameState, gameState.currentLevel),
getNearestUnlockHTML(gameState),
gameState.upgrade_points
gameState.rerolls
? t("score_panel.upgrade_point_count", {
count: gameState.upgrade_points,
count: gameState.rerolls,
})
: "",
],
@ -38,10 +39,10 @@ export async function openScorePanel(gameState: GameState) {
});
}
export function getNearestUnlockHTML(gameState: GameState) {
if (gameState.creative) return "";
export function getFirstUnlockable(gameState: GameState) {
if (gameState.creative) return undefined;
const unlocked = new Set(getSettingValue("breakout_71_unlocked_levels", []));
const firstUnlockable = firstWhere(allLevels, (l, li) => {
return firstWhere(allLevels, (l, li) => {
if (unlocked.has(l.name)) return;
const reason = reasonLevelIsLocked(li, l.name, getHistory(), false);
if (!reason) return;
@ -50,14 +51,18 @@ export function getNearestUnlockHTML(gameState: GameState) {
li,
l.name,
);
const missing = required.filter((id) => !gameState?.perks?.[id]);
const missing: PerkId[] = required.filter((id) => !gameState?.perks?.[id]);
// we can't have a forbidden perk
if (forbidden.find((id) => gameState?.perks?.[id])) {
return;
}
// All required upgrades need to be unlocked
if (missing.find((u) => u.threshold > getTotalScore())) {
if (
missing.find(
(id) => upgrades.find((u) => u.id === id)!.threshold > getTotalScore(),
)
) {
return;
}
@ -71,6 +76,11 @@ export function getNearestUnlockHTML(gameState: GameState) {
reason,
};
});
}
export function getNearestUnlockHTML(gameState: GameState) {
if (!isOptionOn("level_unlocks_hints")) return "";
const firstUnlockable = getFirstUnlockable(gameState);
if (!firstUnlockable) return "";
let missingPoints = Math.max(0, firstUnlockable.minScore - gameState.score);

View file

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

View file

@ -3,7 +3,7 @@ import { t } from "./i18n/i18n";
import { OptionDef, OptionId } from "./types";
import { getSettingValue, setSettingValue } from "./settings";
import { hoursSpentPlaying } from "./game_utils";
import {getHighScore, hoursSpentPlaying} from "./game_utils";
export const options = {
sound: {
@ -90,10 +90,10 @@ export const options = {
name: t("settings.donation_reminder"),
help: t("settings.donation_reminder_help"),
},
red_miss: {
default: true,
name: t("settings.red_miss"),
help: t("settings.red_miss_help"),
level_unlocks_hints: {
default: getHighScore()>1000,
name: t("settings.level_unlocks_hints"),
help: t("settings.level_unlocks_hints_help"),
},
} as const satisfies { [k: string]: OptionDef };

View file

@ -97,12 +97,16 @@ export function firstWhere<Input, Output>(
export const wallBouncedBest = 2,
wallBouncedGood = 7,
levelTimeBest = 10,
levelTimeBest = 25,
levelTimeGood = 45,
catchRateBest = 99,
catchRateBest = 98,
catchRateGood = 90,
missesBest = 1,
missesGood = 6;
missesGood = 6,
choicePerSilver = 1,
choicePerGold = 2,
upPerSilver = 1,
upPerGold = 1;
export const MAX_LEVEL_SIZE = 21;
export const MIN_LEVEL_SIZE = 2;

2
src/types.d.ts vendored
View file

@ -280,7 +280,7 @@ export type GameState = {
plouf: { vol: number; x: number };
colorChange: { vol: number; x: number };
};
upgrade_points: number;
rerolls: number;
creative: boolean;
startParams: RunParams;
};

View file

@ -856,8 +856,7 @@ export const rawUpgrades = [
name: t("upgrades.extra_levels.name"),
help: (lvl: number) =>
t("upgrades.extra_levels.tooltip", { count: lvl + 7 }),
fullHelp: (lvl: number) =>
t("upgrades.extra_levels.verbose_description", { lvl }),
fullHelp: (lvl: number) => t("upgrades.extra_levels.verbose_description"),
},
{
category: categories.combo_boost,