This commit is contained in:
Renan LE CARO 2025-04-29 16:10:24 +02:00
parent 08a61d6967
commit d17eba50e5
24 changed files with 711 additions and 1028 deletions

View file

@ -25,7 +25,8 @@ Break colourful bricks, catch bouncing coins and select powerful upgrades !
- you can pick an upgrade multiple time to level it up
- missed challenges show as greyed out choices (with unlock condition).
- bigger "level X or Y cleared", continue to level X/Y as button
- lives = upgrade points
- instead of bouncing the ball,loosing a life pauses the game (with coins still in the air)
## Done

524
dist/index.html vendored

File diff suppressed because one or more lines are too long

View file

@ -102,6 +102,9 @@ export async function asyncAlert<t>({
popup.appendChild(addto);
}
const buttonWrap = document.createElement("div")
addto.appendChild(buttonWrap)
const {
text,
value,
@ -134,16 +137,16 @@ ${icon}
}
button.className =
className + (lastClickedItemIndex === index ? " needs-focus" : "");
className + (lastClickedItemIndex === index ? " needs-focus" : "")+' choice-button';
addto.appendChild(button);
buttonWrap.appendChild(button);
if (tooltip) {
if (isOptionOn("mobile-mode")) {
const helpBtn = document.createElement("button");
helpBtn.innerText = "?";
helpBtn.setAttribute("data-help-content", tooltip);
button.appendChild(helpBtn);
buttonWrap.appendChild(helpBtn);
} else {
button.setAttribute("data-tooltip", tooltip);
}

View file

@ -149,12 +149,6 @@
"bricks": "__________t__W_tt_WWW_t__W_ttt______",
"credit": ""
},
{
"name": "icon:extra_life",
"size": 9,
"bricks": "___________GG_WG___GGWWGGW_GGWWGGWWGGWWGGWWGG_WGGWWGG___GWWGG_____WGG_______G____",
"credit": ""
},
{
"name": "icon:forgiving",
"size": 8,

View file

@ -55,12 +55,25 @@ canvas:not(#game) {
&:hover,
&:focus {
background: white;
color: black;
background: rgba(255, 255, 255, 0.2);
color: white;
cursor: pointer;
}
text-shadow: 0 0 4px var(--level-background);
&.button-look {
// Look more like buttons for begginer to understand they are clickable
text-shadow: none;
background: #000000d6;
border: 1px solid #fff;
border-radius: 5px;
margin: 5px;
padding: 0 6px;
line-height: 20px;
box-shadow: 0 2px #fff;
}
}
#score {
@ -142,59 +155,68 @@ body:not(.has-alert-open) #popup {
flex-direction: column;
align-items: stretch;
> button {
font: inherit;
background: rgba(0, 0, 0, 0.8);
color: white;
padding: 10px;
cursor: pointer;
border: 1px solid white;
text-align: left;
> div {
display: flex;
gap: 10px;
margin-top: -1px;
&:not([disabled]):hover,
&:not([disabled]):focus {
border-color: #f1d33b;
position: relative;
z-index: 1;
}
&[disabled] {
opacity: 0.5;
filter: saturate(0);
//pointer-events: none;
cursor: not-allowed;
}
& > div {
& > button.choice-button {
flex-grow: 1;
}
font: inherit;
background: rgba(0, 0, 0, 0.8);
color: white;
padding: 10px;
cursor: pointer;
border: 1px solid white;
text-align: left;
display: flex;
gap: 10px;
margin-top: -1px;
& > div > em {
display: block;
opacity: 0.8;
}
&:not([disabled]):hover,
&:not([disabled]):focus {
border-color: #f1d33b;
position: relative;
z-index: 1;
}
&.grey-out-unless-hovered {
&:not(:hover) {
opacity: 0.6;
&[disabled]:not(.no-border) {
opacity: 0.5;
filter: saturate(0);
cursor: not-allowed;
}
img {
filter: saturate(0);
&.no-border{
border-color: transparent;
}
& > div {
flex-grow: 1;
}
& > div > em {
display: block;
opacity: 0.8;
}
&.grey-out-unless-hovered {
&:not(:hover) {
opacity: 0.6;
img {
filter: saturate(0);
}
}
&[disabled] {
opacity: 0.2;
}
}
&[disabled] {
opacity: 0.2;
}
}
button[data-help-content] {
> button[data-help-content] {
border-radius: 4px;
outline: none;
align-self: flex-start;
align-self: center;
width: 30px;
height: 30px;
background: #5da3ea;
@ -205,6 +227,7 @@ body:not(.has-alert-open) #popup {
flex-shrink: 0;
font-weight: bold;
font-size: 22px;
margin: 5px;
}
}
}
@ -412,6 +435,7 @@ h2.histogram-title strong {
top: -3px;
font-weight: bold;
border: 1px solid #FFF;
margin-left: 5px;
> span {
display: inline-block;
@ -419,14 +443,14 @@ h2.histogram-title strong {
&:first-child {
padding: 3px 6px 0 2px;
padding: 3px 6px 0 2px;
color: #000;
background: #FFF;
}
&:last-child {
padding: 3px 3px 0 2px;
padding: 3px 3px 0 2px;
color: #FFF;
background: #000;
@ -442,9 +466,21 @@ h2.histogram-title strong {
transform: skewX(-10deg)
}
}
}
&.capped {
> span:first-child {
color: #FFF;
background: #000;
}
> span:last-child::before {
width: 1px;
background: white;
}
}
}
&.used {

View file

@ -1,38 +1,18 @@
import {
allLevels,
allLevelsAndIcons,
appVersion,
icons,
upgrades,
} from "./loadGameData";
import {
Ball,
Coin,
GameState,
LightFlash,
OptionId,
ParticleFlash,
PerkId,
PerksMap,
RunParams,
TextFlash,
} from "./types";
import { getAudioContext, playPendingSounds } from "./sounds";
import {allLevels, allLevelsAndIcons, appVersion, icons, upgrades,} from "./loadGameData";
import {Ball, Coin, GameState, LightFlash, OptionId, ParticleFlash, PerksMap, RunParams, TextFlash,} from "./types";
import {getAudioContext, playPendingSounds} from "./sounds";
import {
currentLevelInfo,
describeLevel,
getRowColIndex,
highScoreText,
hoursSpentPlaying,
levelsListHTMl,
max_levels,
pickedUpgradesHTMl,
sample,
sumOfValues,
} from "./game_utils";
import "./PWA/sw_loader";
import { getCurrentLang, languages, t } from "./i18n/i18n";
import {getCurrentLang, languages, t} from "./i18n/i18n";
import {
commitSettingsChangesToLocalStorage,
cycleMaxCoins,
@ -47,58 +27,28 @@ import {
gameStateTick,
liveCount,
normalizeGameState,
pickRandomUpgrades,
setLevel,
setMousePos,
} from "./gameStateMutators";
import {
backgroundCanvas,
gameCanvas,
getHaloScale,
haloCanvas,
render,
scoreDisplay,
} from "./render";
import {
pauseRecording,
recordOneFrame,
resumeRecording,
startRecordingGame,
} from "./recording";
import { newGameState } from "./newGameState";
import {
alertsOpen,
asyncAlert,
AsyncAlertAction,
closeModal,
requiredAsyncAlert,
} from "./asyncAlert";
import { isOptionOn, options, toggleOption } from "./options";
import {
catchRateBest,
catchRateGood,
clamp,
levelTimeBest,
levelTimeGood,
miniMarkDown,
missesBest,
missesGood,
wallBouncedBest,
wallBouncedGood,
} from "./pure_functions";
import { helpMenuEntry } from "./help";
import { creativeMode } from "./creative";
import { hideAnyTooltip, setupTooltips } from "./tooltip";
import { startingPerkMenuButton } from "./startingPerks";
import {backgroundCanvas, gameCanvas, getHaloScale, haloCanvas, render, scoreDisplay,} from "./render";
import {pauseRecording, recordOneFrame, resumeRecording, startRecordingGame,} from "./recording";
import {newGameState} from "./newGameState";
import {alertsOpen, asyncAlert, AsyncAlertAction, closeModal,} from "./asyncAlert";
import {isOptionOn, options, toggleOption} from "./options";
import {clamp, miniMarkDown,} from "./pure_functions";
import {helpMenuEntry} from "./help";
import {creativeMode} from "./creative";
import {hideAnyTooltip, setupTooltips} from "./tooltip";
import {startingPerkMenuButton} from "./startingPerks";
import "./migrations";
import { getHistory } from "./gameOver";
import { generateSaveFileContent } from "./generateSaveFileContent";
import { runHistoryViewerMenuEntry } from "./runHistoryViewer";
import { getNearestUnlockHTML, openScorePanel } from "./openScorePanel";
import { monitorLevelsUnlocks } from "./monitorLevelsUnlocks";
import { levelEditorMenuEntry } from "./levelEditor";
import { categories } from "./upgrades";
import { reasonLevelIsLocked } from "./get_level_unlock_condition";
import {getHistory} from "./gameOver";
import {generateSaveFileContent} from "./generateSaveFileContent";
import {runHistoryViewerMenuEntry} from "./runHistoryViewer";
import {openScorePanel} from "./openScorePanel";
import {monitorLevelsUnlocks} from "./monitorLevelsUnlocks";
import {levelEditorMenuEntry} from "./levelEditor";
import {categories} from "./upgrades";
import {reasonLevelIsLocked} from "./get_level_unlock_condition";
export async function play() {
if (await applyFullScreenChoice()) return;
@ -131,8 +81,6 @@ export function pause(playerAskedForPause: boolean) {
pauseRecording();
gameState.pauseTimeout = null;
// document.body.className = gameState.running ? " running " : " paused ";
scoreDisplay.className = "";
gameState.needsRender = true;
};
@ -249,122 +197,6 @@ setInterval(() => {
fitSize(gameState);
}, 1000);
export async function openUpgradesPicker(gameState: GameState) {
const catchRate =
gameState.levelCoughtCoins / (gameState.levelSpawnedCoins || 1);
let repeats = 1;
let timeGain = "",
catchGain = "",
wallHitsGain = "",
missesGain = "";
if (gameState.levelWallBounces < wallBouncedBest) {
repeats++;
gameState.rerolls++;
wallHitsGain = t("level_up.plus_one_upgrade_and_reroll");
} else if (gameState.levelWallBounces < wallBouncedGood) {
repeats++;
wallHitsGain = t("level_up.plus_one_upgrade");
}
if (gameState.levelTime < levelTimeBest * 1000) {
repeats++;
gameState.rerolls++;
timeGain = t("level_up.plus_one_upgrade_and_reroll");
} else if (gameState.levelTime < levelTimeGood * 1000) {
repeats++;
timeGain = t("level_up.plus_one_upgrade");
}
if (catchRate > catchRateBest / 100) {
repeats++;
gameState.rerolls++;
catchGain = t("level_up.plus_one_upgrade_and_reroll");
} else if (catchRate > catchRateGood / 100) {
repeats++;
catchGain = t("level_up.plus_one_upgrade");
}
if (gameState.levelMisses < missesBest) {
repeats++;
gameState.rerolls++;
missesGain = t("level_up.plus_one_upgrade_and_reroll");
} else if (gameState.levelMisses < missesGood) {
repeats++;
missesGain = t("level_up.plus_one_upgrade");
}
while (repeats--) {
const actions: Array<{
text: string;
icon: string;
value: PerkId | "reroll";
help: string;
className: string;
tooltip: string;
}> = pickRandomUpgrades(gameState, 3 + gameState.perks.one_more_choice);
if (!actions.length) break;
if (gameState.rerolls)
actions.push({
text: t("level_up.reroll", { count: gameState.rerolls }),
help: t("level_up.reroll_help"),
value: "reroll" as const,
icon: icons["icon:reroll"],
});
const compliment =
(timeGain &&
catchGain &&
missesGain &&
wallHitsGain &&
t("level_up.compliment_perfect")) ||
((timeGain || catchGain || missesGain || wallHitsGain) &&
t("level_up.compliment_good")) ||
t("level_up.compliment_advice");
const upgradeId = await requiredAsyncAlert<PerkId | "reroll">({
title:
t("level_up.pick_upgrade_title") +
(repeats ? " (" + (repeats + 1) + ")" : ""),
content: [
`<p>${t("level_up.before_buttons", {
score: gameState.levelCoughtCoins,
catchGain,
levelSpawnedCoins: gameState.levelSpawnedCoins,
time: Math.round(gameState.levelTime / 1000),
timeGain,
levelMisses: gameState.levelMisses,
missesGain,
levelWallBounces: gameState.levelWallBounces,
wallHitsGain,
compliment,
})}
</p>
<p>${t("level_up.after_buttons", {
level: gameState.currentLevel + 1,
max: max_levels(gameState),
})} </p>
<p>${levelsListHTMl(gameState, gameState.currentLevel + 1)}</p>
`,
...actions,
pickedUpgradesHTMl(gameState),
getNearestUnlockHTML(gameState),
`<div id="level-recording-container"></div>`,
],
});
if (upgradeId === "reroll") {
repeats++;
gameState.rerolls--;
} else {
gameState.perks[upgradeId]++;
gameState.runStatistics.upgrades_picked++;
}
}
}
gameCanvas.addEventListener("mouseup", (e) => {
if (e.button !== 0) return;
if (gameState.running) {
@ -543,21 +375,33 @@ document.addEventListener("visibilitychange", () => {
pause(true);
}
});
if(getSettingValue('score-opened',0 )<3){
scoreDisplay.classList.add('button-look')
}
const menuDisplay = document.getElementById("menu") as HTMLButtonElement;
if(getSettingValue('menu-opened',0 )<3){
menuDisplay.classList.add('button-look')
}
function scoreOpen(e){
e.preventDefault();
if (!alertsOpen) {
setSettingValue('score-opened',getSettingValue('score-opened',0 )+1 )
openScorePanel(gameState);
}
}
scoreDisplay.addEventListener("click",scoreOpen);
scoreDisplay.addEventListener("mousedown", scoreOpen);
(document.getElementById("menu") as HTMLButtonElement).addEventListener(
menuDisplay.addEventListener(
"click",
(e) => {
e.preventDefault();
if (!alertsOpen) {
setSettingValue('menu-opened',getSettingValue('menu-opened',0 )+1 )
openMainMenu();
}
},

View file

@ -6,7 +6,6 @@ import {
GameState,
LightFlash,
ParticleFlash,
PerkId,
ReusableArray,
TextFlash,
} from "./types";
@ -21,7 +20,6 @@ import {
getCoinRenderColor,
getCornerOffset,
getMajorityValue,
getPossibleUpgrades,
getRowColIndex,
isMovingWhilePassiveIncome,
isPickyEatingPossible,
@ -31,32 +29,18 @@ import {
telekinesisEffectRate,
yoyoEffectRate,
} from "./game_utils";
import { t } from "./i18n/i18n";
import { icons } from "./loadGameData";
import {t} from "./i18n/i18n";
import { getCurrentMaxCoins, getCurrentMaxParticles } from "./settings";
import { background } from "./render";
import { gameOver } from "./gameOver";
import {
brickIndex,
fitSize,
gameState,
hasBrick,
hitsSomething,
openUpgradesPicker,
pause,
startComputerControlledGame,
} from "./game";
import { stopRecording } from "./recording";
import { isOptionOn } from "./options";
import {
ballTransparency,
clamp,
coinsBoostedCombo,
comboKeepingRate,
} from "./pure_functions";
import { addToTotalScore } from "./addToTotalScore";
import { hashCode } from "./getLevelBackground";
import {getCurrentMaxCoins, getCurrentMaxParticles} from "./settings";
import {background} from "./render";
import {gameOver} from "./gameOver";
import {brickIndex, fitSize, gameState, hasBrick, hitsSomething, pause, startComputerControlledGame,} from "./game";
import {stopRecording} from "./recording";
import {isOptionOn} from "./options";
import {ballTransparency, clamp, coinsBoostedCombo, comboKeepingRate,} from "./pure_functions";
import {addToTotalScore} from "./addToTotalScore";
import {hashCode} from "./getLevelBackground";
import {openUpgradesPicker} from "./openUpgradesPicker";
export function setMousePos(gameState: GameState, x: number) {
if (gameState.startParams.computer_controlled) return;
@ -556,41 +540,6 @@ export function explodeBrick(
}
}
export function dontOfferTooSoon(gameState: GameState, id: PerkId) {
gameState.lastOffered[id] = Math.round(Date.now() / 1000);
}
export function pickRandomUpgrades(gameState: GameState, count: number) {
let list = 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, count)
.sort((a, b) => (a.id > b.id ? 1 : -1));
list.forEach((u) => {
dontOfferTooSoon(gameState, u.id);
});
return list.map((u) => ({
text:
u.name +
(gameState.perks[u.id]
? t("level_up.upgrade_perk_to_level", {
level: gameState.perks[u.id] + 1,
})
: ""),
icon: icons["icon:" + u.id],
value: u.id as PerkId,
help: u.help(gameState.perks[u.id] + 1),
className: "upgrade ",
tooltip: u.fullHelp(gameState.perks[u.id] + 1),
}));
}
export function schedulGameSound(
gameState: GameState,
sound: keyof GameState["aboutToPlaySound"],
@ -654,12 +603,13 @@ export async function setLevel(gameState: GameState, l: number) {
gameState.upgradesOfferedFor = l;
stopRecording();
gameState.currentLevel = l;
gameState.level = gameState.runLevels[l % gameState.runLevels.length];
if (l > 0) {
await openUpgradesPicker(gameState);
}
gameState.currentLevel = l;
gameState.level = gameState.runLevels[l % gameState.runLevels.length];
gameState.levelTime = 0;
gameState.winAt = 0;
@ -667,6 +617,7 @@ export async function setLevel(gameState: GameState, l: number) {
gameState.lastPuckMove = 0;
gameState.lastZenComboIncrease = 0;
gameState.autoCleanUses = 0;
gameState.lastTickDown = gameState.levelTime;
gameState.levelStartScore = gameState.score;
gameState.levelSpawnedCoins = 0;
@ -1005,8 +956,8 @@ export function gameStateTick(
}
}
if (
remainingBricks <= gameState.perks.skip_last &&
if (( window.location.search.includes("skipplaying") ||
remainingBricks <= gameState.perks.skip_last) &&
!gameState.autoCleanUses
) {
gameState.bricks.forEach((type, index) => {
@ -1819,7 +1770,7 @@ export function ballTick(gameState: GameState, ball: Ball, frames: number) {
ball.vy > 0 &&
(ballIsUnderPuck ||
(gameState.balls.length < 2 &&
gameState.perks.extra_life &&
gameState.extra_lives &&
ball.y > ylimit + gameState.puckHeight / 2))
) {
if (ballIsUnderPuck) {
@ -2038,9 +1989,9 @@ export function ballTick(gameState: GameState, ball: Ball, frames: number) {
}
function justLostALife(gameState: GameState, ball: Ball, x: number, y: number) {
gameState.perks.extra_life -= 1;
if (gameState.perks.extra_life < 0) {
gameState.perks.extra_life = 0;
gameState.extra_lives -= 1;
if (gameState.extra_lives < 0) {
gameState.extra_lives = 0;
} else if (gameState.perks.sacrifice) {
gameState.combo *= gameState.perks.sacrifice;
gameState.bricks.forEach(

View file

@ -1,9 +1,9 @@
import { Ball, Coin, GameState, Level, PerkId, PerksMap } from "./types";
import { icons, upgrades } from "./loadGameData";
import { t } from "./i18n/i18n";
import { clamp } from "./pure_functions";
import { getSettingValue, getTotalScore } from "./settings";
import { isOptionOn } from "./options";
import {Ball, Coin, GameState, Level, PerkId, PerksMap, Upgrade} from "./types";
import {icons, upgrades} from "./loadGameData";
import {t} from "./i18n/i18n";
import {clamp} from "./pure_functions";
import {getSettingValue, getTotalScore} from "./settings";
import {isOptionOn} from "./options";
export function describeLevel(level: Level) {
let bricks = 0,
@ -99,6 +99,12 @@ export function max_levels(gameState: GameState) {
return 7 + gameState.perks.extra_levels;
}
export function upgradeLevelAndMaxDisplay(upgrade: Upgrade, gameState: GameState) {
const lvl = gameState.perks[upgrade.id];
const max = upgrade.max + gameState.perks.limitless
return `<span class="level ${lvl < max ? 'can-upgrade' : 'capped'}"><span>${lvl}</span><span>${max}</span></span>`
}
export function pickedUpgradesHTMl(gameState: GameState) {
const upgradesList = getPossibleUpgrades(gameState)
.filter((u) => gameState.perks[u.id])
@ -114,7 +120,7 @@ export function pickedUpgradesHTMl(gameState: GameState) {
${icons["icon:" + u.id]}
<p>
<strong>${u.name}</strong>
<span class="level"><span>${gameState.perks[u.id]}</span><span>${newMax}</span></span>
${upgradeLevelAndMaxDisplay(u, gameState)}
${u.help(Math.max(1, gameState.perks[u.id]))}
</p>
</div>

View file

@ -16,7 +16,6 @@ function isExcluded(id: PerkId) {
if (!excluded) {
excluded = new Set([
"extra_levels",
"extra_life",
"one_more_choice",
"shunt",
"slow_down",

View file

@ -68,17 +68,13 @@
"lab.reset": "إعادة ضبط",
"lab.select_level": "حدد المستوى للعب عليه",
"lab.unlocks_at": "يتم فتحه عند إجمالي النتيجة {{score}}",
"level_up.after_buttons": "لقد انتهيت للتو من المستوى {{level}}/{{max}}.",
"level_up.before_buttons": "لقد اصطدت {{score}} عملة {{catchGain}} من أصل {{levelSpawnedCoins}} في {{time}} ثانية {{timeGain}}.\n\nأخطأت {{levelMisses}} مرات {{missesGain}} واصطدمت بالجدران أو السقف {{levelWallBounces}} مرات{{wallHitsGain}}.\n{{compliment}}",
"level_up.compliment_advice": "حاول التقاط جميع العملات المعدنية، ولا تفوت الطوب أبدًا، ولا تصطدم أبدًا بالجدران/السقف أو قم بتجاوز المستوى في أقل من 30 ثانية للحصول على ترقيات إضافية.",
"level_up.compliment_good": "أحسنت !",
"level_up.compliment_perfect": "رائع، استمر في ذلك!",
"level_up.pick_upgrade_title": "اختر ترقية",
"level_up.plus_one_upgrade": "(+1 ترقية)",
"level_up.plus_one_upgrade_and_reroll": "(+1 ترقية و+1 إعادة رمي)",
"level_up.reroll": "إعادة الرمي ({{count}})",
"level_up.reroll_help": "تقديم خيارات جديدة",
"level_up.upgrade_perk_to_level": "المستوى {{level}}",
"level_up.go": "",
"level_up.instructions": "",
"level_up.maxed_upgrade": "",
"level_up.no_points": "",
"level_up.pick_upgrade": "",
"level_up.title": "لقد انتهيت للتو من المستوى {{level}}/{{max}}.",
"level_up.upgrade_perk_to_level": "",
"main_menu.basic": "",
"main_menu.basic_help": "",
"main_menu.colorful_coins": "",
@ -161,8 +157,8 @@
"play.stats.levelTime": "وقت المستوى",
"play.stats.levelWallBounces": "ارتدادات الحائط",
"score_panel.close_to_unlock": "فتح المستوى التالي:",
"score_panel.extra_lives_count": "",
"score_panel.get_upgrades_to_unlock": "احصل على {{missingUpgrades}} واحصل على {{points}} نقطة إضافية لفتح المستوى \"{{level}}\"",
"score_panel.rerolls_count": "لقد جمعت {{rerolls}} إعادة تسجيل",
"score_panel.score_to_unlock": "احصل على {{points}} نقطة إضافية لفتح المستوى \"{{level}}\"",
"score_panel.title": "{{score}} نقطة في المستوى {{level}}/{{max}} ",
"score_panel.upcoming_levels": "المستويات القادمة :",
@ -305,9 +301,6 @@
"upgrades.extra_levels.name": "5 دقائق إضافية",
"upgrades.extra_levels.tooltip": "العب {{count}} مستوى بدلاً من 7",
"upgrades.extra_levels.verbose_description": "يمكن أن تستمر اللعبة الافتراضية لسبعة مستويات كحد أقصى، تنتهي بعدها.\n\nيتيح لك كل مستوى من هذه الميزة الانتقال إلى مستوى أعلى. غالبًا ما تكون المستويات الأخيرة هي التي تحقق فيها أعلى النقاط، لذا قد يكون الفرق كبيرًا.",
"upgrades.extra_life.name": "حياة إضافية",
"upgrades.extra_life.tooltip": "سترتد الكرة مرة واحدة على الخط السفلي قبل أن تضيع.",
"upgrades.extra_life.verbose_description": "عادةً، لديك كرة واحدة، وتنتهي اللعبة بمجرد إسقاطها.\n\nتضيف هذه الميزة شريطًا أبيض أسفل الشاشة يحفظ الكرة مرة واحدة، ثم ينكسر أثناء ذلك.\n\nستخسر مستوى واحدًا من هذه الميزة في كل مرة ترتد فيها كرة أسفل الشاشة.",
"upgrades.forgiving.name": "غفور",
"upgrades.forgiving.tooltip": "يؤدي فقدان الفواصل إلى تقليل المجموعة تدريجيًا بدلاً من تقليلها دفعة واحدة.",
"upgrades.forgiving.verbose_description": "أول خطأ في كل مستوى مجاني، ثم 10% من المجموعة، ثم 20% ..",

View file

@ -2493,7 +2493,42 @@
<name>level_up</name>
<children>
<concept_node>
<name>after_buttons</name>
<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>instructions</name>
<description/>
<comment/>
<translations>
@ -2528,7 +2563,7 @@
</translations>
</concept_node>
<concept_node>
<name>before_buttons</name>
<name>maxed_upgrade</name>
<description/>
<comment/>
<translations>
@ -2538,11 +2573,11 @@
</translation>
<translation>
<language>de-DE</language>
<approved>true</approved>
<approved>false</approved>
</translation>
<translation>
<language>en-US</language>
<approved>true</approved>
<approved>false</approved>
</translation>
<translation>
<language>es-CL</language>
@ -2550,7 +2585,7 @@
</translation>
<translation>
<language>fr-FR</language>
<approved>true</approved>
<approved>false</approved>
</translation>
<translation>
<language>ru-RU</language>
@ -2563,7 +2598,7 @@
</translations>
</concept_node>
<concept_node>
<name>compliment_advice</name>
<name>no_points</name>
<description/>
<comment/>
<translations>
@ -2573,11 +2608,11 @@
</translation>
<translation>
<language>de-DE</language>
<approved>true</approved>
<approved>false</approved>
</translation>
<translation>
<language>en-US</language>
<approved>true</approved>
<approved>false</approved>
</translation>
<translation>
<language>es-CL</language>
@ -2585,7 +2620,7 @@
</translation>
<translation>
<language>fr-FR</language>
<approved>true</approved>
<approved>false</approved>
</translation>
<translation>
<language>ru-RU</language>
@ -2598,7 +2633,7 @@
</translations>
</concept_node>
<concept_node>
<name>compliment_good</name>
<name>pick_upgrade</name>
<description/>
<comment/>
<translations>
@ -2608,11 +2643,11 @@
</translation>
<translation>
<language>de-DE</language>
<approved>true</approved>
<approved>false</approved>
</translation>
<translation>
<language>en-US</language>
<approved>true</approved>
<approved>false</approved>
</translation>
<translation>
<language>es-CL</language>
@ -2620,7 +2655,7 @@
</translation>
<translation>
<language>fr-FR</language>
<approved>true</approved>
<approved>false</approved>
</translation>
<translation>
<language>ru-RU</language>
@ -2633,182 +2668,7 @@
</translations>
</concept_node>
<concept_node>
<name>compliment_perfect</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>pick_upgrade_title</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>plus_one_upgrade</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>plus_one_upgrade_and_reroll</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>reroll</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>reroll_help</name>
<name>title</name>
<description/>
<comment/>
<translations>
@ -5768,7 +5628,7 @@
</translations>
</concept_node>
<concept_node>
<name>get_upgrades_to_unlock</name>
<name>extra_lives_count</name>
<description/>
<comment/>
<translations>
@ -5778,11 +5638,11 @@
</translation>
<translation>
<language>de-DE</language>
<approved>true</approved>
<approved>false</approved>
</translation>
<translation>
<language>en-US</language>
<approved>true</approved>
<approved>false</approved>
</translation>
<translation>
<language>es-CL</language>
@ -5790,7 +5650,7 @@
</translation>
<translation>
<language>fr-FR</language>
<approved>true</approved>
<approved>false</approved>
</translation>
<translation>
<language>ru-RU</language>
@ -5803,7 +5663,7 @@
</translations>
</concept_node>
<concept_node>
<name>rerolls_count</name>
<name>get_upgrades_to_unlock</name>
<description/>
<comment/>
<translations>
@ -10927,116 +10787,6 @@
</concept_node>
</children>
</folder_node>
<folder_node>
<name>extra_life</name>
<children>
<concept_node>
<name>name</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>tooltip</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>verbose_description</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>
</children>
</folder_node>
<folder_node>
<name>forgiving</name>
<children>

View file

@ -68,17 +68,13 @@
"lab.reset": "Zurücksetzen",
"lab.select_level": "Wähle ein Level zum Spielen",
"lab.unlocks_at": "Wird bei Gesamtpunktzahl {{score}} freigeschaltet",
"level_up.after_buttons": "Du hast gerade Level {{level}}/{{max}} beendet.",
"level_up.before_buttons": "Du hast {{score}} Münzen {{catchGain}} aus {{levelSpawnedCoins}} in {{time}} Sekunden {{timeGain}} gefangen.\n\nDu hast {{levelMisses}} Mal danebengeschossen {{missesGain}} und {{levelWallBounces}} Mal die Wände oder die Decke getroffen {{wallHitsGain}}.\n\n{{compliment}}",
"level_up.compliment_advice": "Versuche, alle Münzen zu fangen, verpasse nie die Steine, stoße nie an die Wände/Decke oder schaffe das Level unter 30 Sekunden, um zusätzliche Upgrades zu erhalten.",
"level_up.compliment_good": "Gut gemacht!",
"level_up.compliment_perfect": "Beeindruckend, machen Sie weiter so!",
"level_up.pick_upgrade_title": "Wählen Sie ein Upgrade",
"level_up.plus_one_upgrade": "(+1 Upgrade)",
"level_up.plus_one_upgrade_and_reroll": "(+1 Upgrade und +1 Neuwurf)",
"level_up.reroll": "Neu würfeln ({{count}})",
"level_up.reroll_help": "Bietet neue Auswahl",
"level_up.upgrade_perk_to_level": " lvl {{level}}",
"level_up.go": "",
"level_up.instructions": "",
"level_up.maxed_upgrade": "",
"level_up.no_points": "",
"level_up.pick_upgrade": "",
"level_up.title": "Du hast gerade Level {{level}}/{{max}} beendet.",
"level_up.upgrade_perk_to_level": "",
"main_menu.basic": "",
"main_menu.basic_help": "",
"main_menu.colorful_coins": "",
@ -161,8 +157,8 @@
"play.stats.levelTime": "Zeit pro Level",
"play.stats.levelWallBounces": "Wandaufpralle",
"score_panel.close_to_unlock": "Nächstes Level freischalten:",
"score_panel.extra_lives_count": "",
"score_panel.get_upgrades_to_unlock": "Hole {{missingUpgrades}} und erziele {{points}} mehr Punkte, um Level \"{{level}}\" freizuschalten.",
"score_panel.rerolls_count": "Du hast {{rerolls}} Upgrades neugewürfelt",
"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": "Kommende Level:",
@ -305,9 +301,6 @@
"upgrades.extra_levels.name": "5 min mehr",
"upgrades.extra_levels.tooltip": "Spielen Sie {{count}} Stufen statt 7",
"upgrades.extra_levels.verbose_description": "Das Standardspiel kann maximal 7 Stufen dauern, danach ist das Spiel vorbei.\n\nMit jeder Stufe dieses Vorteils können Sie eine Stufe höher gehen. Die letzten Level sind oft diejenigen, in denen man die meisten Punkte macht, so dass der Unterschied dramatisch sein kann.",
"upgrades.extra_life.name": "Extraleben",
"upgrades.extra_life.tooltip": "Der Ball prallt einmal auf der unteren Linie auf, bevor er verloren ist.",
"upgrades.extra_life.verbose_description": "Normalerweise hat man nur einen Ball, und das Spiel ist vorbei, sobald man ihn fallen lässt.\n\nDieser Vorteil fügt eine weiße Leiste am unteren Rand des Bildschirms hinzu, die einen Ball einmal speichert und dabei zerbricht.\n\nJedes Mal, wenn ein Ball am unteren Rand des Bildschirms aufprallt, verlierst du eine Stufe dieses Vorteils.",
"upgrades.forgiving.name": "Verzeihen",
"upgrades.forgiving.tooltip": "Durch fehlende Pausen wird die Kombo schrittweise reduziert, anstatt auf einmal.",
"upgrades.forgiving.verbose_description": "Der erste Fehlschuss pro Level ist kostenlos, dann 10% der Combo, dann 20% ...",

View file

@ -68,17 +68,13 @@
"lab.reset": "Reset",
"lab.select_level": "Select a level to play on",
"lab.unlocks_at": "Unlocks at total score {{score}}",
"level_up.after_buttons": "You just finished level {{level}}/{{max}}.",
"level_up.before_buttons": "You caught {{score}} coins {{catchGain}} out of {{levelSpawnedCoins}} in {{time}} seconds {{timeGain}}.\n\nYou missed {{levelMisses}} times {{missesGain}} and hit the walls or ceiling {{levelWallBounces}} times{{wallHitsGain}}.\n\n{{compliment}}",
"level_up.compliment_advice": "Try to catch all coins, never miss the bricks, never hit the walls/ceiling or clear the level under 30s to gain additional upgrades.",
"level_up.compliment_good": "Well done !",
"level_up.compliment_perfect": "Impressive, keep it up !",
"level_up.pick_upgrade_title": "Pick an upgrade",
"level_up.plus_one_upgrade": "(+1 upgrade)",
"level_up.plus_one_upgrade_and_reroll": "(+1 upgrade and +1 re-roll)",
"level_up.reroll": "Re-roll ({{count}})",
"level_up.reroll_help": "Offer new choices",
"level_up.upgrade_perk_to_level": " lvl {{level}}",
"level_up.go": "Continue to level \"{{name}}\"",
"level_up.instructions": "You can upgrade perks below using your {{count}} extra lives. ",
"level_up.maxed_upgrade": "\"{{name}}\" is at max level",
"level_up.no_points": "You've spent all your extra lives.",
"level_up.pick_upgrade": "Get \"{{name}}\"",
"level_up.title": "You just finished level {{level}}/{{max}}.",
"level_up.upgrade_perk_to_level": "Upgrade \"{{name}}\" to level {{level}}",
"main_menu.basic": "",
"main_menu.basic_help": "",
"main_menu.colorful_coins": "",
@ -161,12 +157,12 @@
"play.stats.levelTime": "Level time",
"play.stats.levelWallBounces": "Wall bounces",
"score_panel.close_to_unlock": "Next level unlock :",
"score_panel.extra_lives_count": "You have accumulated {{count}} upgrade points.",
"score_panel.get_upgrades_to_unlock": "Get {{missingUpgrades}} and score {{points}} more points to unlock level \"{{level}}\"",
"score_panel.rerolls_count": "You have accumulated {{rerolls}} rerolls",
"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.upgrades_picked": "Upgrades picked in this game run : ",
"score_panel.upgrades_picked": "Upgrades picked : ",
"settings.autoplay": "Auto play",
"settings.autoplay_help": "Start a session with random upgrades and a computer controlled paddle",
"settings.basic": "Basic graphics",
@ -298,16 +294,13 @@
"upgrades.corner_shot.verbose_description": "Helps with aiming in the corners. Further levels let you go further out. ",
"upgrades.double_or_nothing.name": "Double or nothing",
"upgrades.double_or_nothing.tooltip": "Combo climbs {{multiplier}} times faster, but you'll loose {{percent}}% of your score at each reset.",
"upgrades.double_or_nothing.verbose_description": "",
"upgrades.double_or_nothing.verbose_description": "The combo reset only counts when your combo was above the minimum",
"upgrades.etherealcoins.name": "Coins, in Space",
"upgrades.etherealcoins.tooltip": "Coins are no longer affected by gravity",
"upgrades.etherealcoins.verbose_description": "The coins will maintain their speed even after several bounces, and will no longer be affected by gravity.",
"upgrades.extra_levels.name": "5 min more",
"upgrades.extra_levels.tooltip": "Play {{count}} levels instead of 7",
"upgrades.extra_levels.verbose_description": "The default game can last a max of 7 levels, after which the game is over. \n\nEach level of this perk lets you go one level higher. The last levels are often the ones where you make the most score, so the difference can be dramatic.",
"upgrades.extra_life.name": "Extra Life",
"upgrades.extra_life.tooltip": "Saves your ball once",
"upgrades.extra_life.verbose_description": "Normally, you have one ball, and the game is over as soon as you drop it.\n\nThis perk adds a white bar at the bottom of the screen that will save a ball once, and break in the process. \n\nYou'll lose one level of that perk every time a ball bounces at the bottom of the screen.\n\nIf you have multiple balls, only the last one will be saved by the extra life. ",
"upgrades.forgiving.name": "Forgiving",
"upgrades.forgiving.tooltip": "Keep most of your combo when missing",
"upgrades.forgiving.verbose_description": "The first miss per level is free, then 10% of the combo, then 20% .. ",

View file

@ -68,17 +68,13 @@
"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.after_buttons": "Acabas de completar el nivel {{level}}/{{max}}.",
"level_up.before_buttons": "Has cogido {{score}} monedas {{catchGain}} en {{levelSpawnedCoins}} en {{time}} segundos {{timeGain}}.\n\nHas fallado los ladrillos {{levelMisses}} veces {{missesGain}} y tocado los bordes del área de juego {{levelWallBounces}} veces {{wallHitsGain}}.\n\n{{compliment}}",
"level_up.compliment_advice": "Intenta coger todas las piezas, no fallar nunca con los ladrillos, no tocar nunca las paredes o terminar el nivel en menos de 30 segundos para conseguir opciones extra y mejoras.",
"level_up.compliment_good": "¡Bien hecho!",
"level_up.compliment_perfect": "Impresionante, ¡seguid así!",
"level_up.pick_upgrade_title": "Elija una mejora",
"level_up.plus_one_upgrade": "(+1 mejora)",
"level_up.plus_one_upgrade_and_reroll": "(+1 mejora y +1 relanzamiento)",
"level_up.reroll": "Relanzamiento ({{count}})",
"level_up.reroll_help": "Nuevas opciones",
"level_up.upgrade_perk_to_level": " nivel {{level}}",
"level_up.go": "",
"level_up.instructions": "",
"level_up.maxed_upgrade": "",
"level_up.no_points": "",
"level_up.pick_upgrade": "",
"level_up.title": "Acabas de completar el nivel {{level}}/{{max}}.",
"level_up.upgrade_perk_to_level": "",
"main_menu.basic": "Gráficos simplificados",
"main_menu.basic_help": "Mejor rendimiento.",
"main_menu.colorful_coins": "Piezas de color",
@ -161,8 +157,8 @@
"play.stats.levelTime": "Duración del nivel",
"play.stats.levelWallBounces": "Rebota en las paredes",
"score_panel.close_to_unlock": "Siguiente nivel desbloqueado:",
"score_panel.extra_lives_count": "",
"score_panel.get_upgrades_to_unlock": "Consigue {{missingUpgrades}} y coge {{points}} monedas extra para desbloquear el nivel \"{{level}}\".",
"score_panel.rerolls_count": "Has acumulado {{rerolls}} rerolls",
"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": "Niveles de partido :",
@ -305,9 +301,6 @@
"upgrades.extra_levels.name": "5 minutos más",
"upgrades.extra_levels.tooltip": "Juega {{count}} niveles en lugar de 7",
"upgrades.extra_levels.verbose_description": "El juego suele durar 7 niveles, tras los cuales se acaba la partida y la puntuación que hayas alcanzado es tu puntuación del juego.\n\nElegir esta mejora te permite prolongar el juego un nivel. Los últimos niveles suelen ser en los que más puntos consigues, así que la diferencia puede ser espectacular.",
"upgrades.extra_life.name": "Segunda oportunidad",
"upgrades.extra_life.tooltip": "La pelota rebota una vez antes de perderse.",
"upgrades.extra_life.verbose_description": "Normalmente, sólo tienes una bola por ronda, y la ronda termina en cuanto la sueltas.\n\nEsta habilidad añade una barra blanca en la parte inferior de la pantalla que guardará una bola una vez, y se romperá en el proceso.\n\nPuedes coger varias vidas por adelantado, que se usan cada vez que una bola está a punto de perderse.",
"upgrades.forgiving.name": "Errar es humano",
"upgrades.forgiving.tooltip": "Perder los ladrillos significa perder una porción progresivamente mayor del combo",
"upgrades.forgiving.verbose_description": " El primer ladrillo perdido por nivel no cuesta nada, el siguiente 10%, 20% y así sucesivamente.",

View file

@ -68,17 +68,13 @@
"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.after_buttons": "Vous venez de terminer le niveau {{level}}/{{max}}.",
"level_up.before_buttons": "Vous avez attrapé {{score}} pièces {{catchGain}} sur {{levelSpawnedCoins}} en {{time}} secondes {{timeGain}}.\n\nVous avez raté les briques {{levelMisses}} fois {{missesGain}} et touché les bords de la zone de jeu {{levelWallBounces}} fois {{wallHitsGain}}.\n\n{{compliment}}",
"level_up.compliment_advice": "Essayez d'attraper toutes les pièces, de ne jamais rater les briques, de ne pas toucher les murs ou de terminer le niveau en moins de 30 secondes pour obtenir des choix supplémentaires et des améliorations.",
"level_up.compliment_good": "Bravo !",
"level_up.compliment_perfect": "Impressionnant, continuez comme ça !",
"level_up.pick_upgrade_title": "Choisir une amélioration",
"level_up.plus_one_upgrade": "(+1 upgrade)",
"level_up.plus_one_upgrade_and_reroll": "(+1 amélioration et +1 relance)",
"level_up.reroll": "Relancer ({{count}})",
"level_up.reroll_help": "Nouveaux choix",
"level_up.upgrade_perk_to_level": " niveau {{level}}",
"level_up.go": "",
"level_up.instructions": "",
"level_up.maxed_upgrade": "",
"level_up.no_points": "",
"level_up.pick_upgrade": "",
"level_up.title": "Vous venez de terminer le niveau {{level}}/{{max}}.",
"level_up.upgrade_perk_to_level": "",
"main_menu.basic": "",
"main_menu.basic_help": "",
"main_menu.colorful_coins": "",
@ -161,12 +157,12 @@
"play.stats.levelTime": "Durée du niveau",
"play.stats.levelWallBounces": "Rebonds sur les murs",
"score_panel.close_to_unlock": "Prochain niveau débloqué : ",
"score_panel.extra_lives_count": "",
"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": "Vous avez accumulé {{rerolls}} rerolls",
"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": "Niveaux de la parties : ",
"score_panel.upgrades_picked": "Améliorations choisies pendant la partie :",
"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",
"settings.basic": "Graphismes simplifiés",
@ -305,9 +301,6 @@
"upgrades.extra_levels.name": "Encore 5 minutes",
"upgrades.extra_levels.tooltip": "Jouer {{count}} niveaux au lieu de 7",
"upgrades.extra_levels.verbose_description": "La partie dure normalement 7 niveaux, après quoi le jeu est terminé et le score que vous avez atteint est votre score de partie.\n\nChoisir cette amélioration vous permet de prolonger la partie d'un niveau. Les derniers niveaux sont souvent ceux où vous faites le plus de points, la différence peut donc être spectaculaire.",
"upgrades.extra_life.name": "Seconde chance",
"upgrades.extra_life.tooltip": "La balle rebondit une fois avant d'être perdue.",
"upgrades.extra_life.verbose_description": "Normalement, vous n'avez qu'une seule balle par manche, et la manche est terminée dès que vous la laissez tomber.\n\nCette compétence ajoute une barre blanche en bas de l'écran qui sauvera une balle une fois, et se brisera au cours du processus.\n\nVous pouvez prendre plusieurs vies d'avances, elle seront utilisées à chaque fois qu'une balle est sur le point d'être perdue. ",
"upgrades.forgiving.name": "L'erreur est humaine",
"upgrades.forgiving.tooltip": "Rater les briques fait perdre un portion progressivement plu importante du combo",
"upgrades.forgiving.verbose_description": " La première brique ratée par niveau ne coûte rien, la suivante 10%, 20%, etc.",

View file

@ -68,17 +68,13 @@
"lab.reset": "Перезагрузить",
"lab.select_level": "Выберите уровень для игры",
"lab.unlocks_at": "Открывается при общем количестве очков {{score}}",
"level_up.after_buttons": "Вы только что закончили уровень {{level}}/{{max}}.",
"level_up.before_buttons": "Вы поймали {{score}} монет {{catchGain}} из {{levelSpawnedCoins}} за {{time}} секунды {{timeGain}}.\n\nВы промахнулись {{levelMisses}} раз {{missesGain}} и ударились о стены или потолок {{levelWallBounces}} раз{{wallHitsGain}}.\n\n{{compliment}}",
"level_up.compliment_advice": "Постарайтесь поймать все монеты, не промахнуться мимо кирпичей, не удариться о стены/потолок или пройти уровень за 30 секунд, чтобы получить дополнительные улучшения.",
"level_up.compliment_good": "Молодцы!",
"level_up.compliment_perfect": "Впечатляет, продолжайте в том же духе!",
"level_up.pick_upgrade_title": "Выберите обновление",
"level_up.plus_one_upgrade": "(+1 обновление)",
"level_up.plus_one_upgrade_and_reroll": "(+1 улучшение и +1 повторный бросок)",
"level_up.reroll": "Повторный бросок ({{count}})",
"level_up.reroll_help": "Предложите новые варианты",
"level_up.upgrade_perk_to_level": " lvl {{level}}",
"level_up.go": "",
"level_up.instructions": "",
"level_up.maxed_upgrade": "",
"level_up.no_points": "",
"level_up.pick_upgrade": "",
"level_up.title": "Вы только что закончили уровень {{level}}/{{max}}.",
"level_up.upgrade_perk_to_level": "",
"main_menu.basic": "",
"main_menu.basic_help": "",
"main_menu.colorful_coins": "",
@ -161,8 +157,8 @@
"play.stats.levelTime": "Время уровня",
"play.stats.levelWallBounces": "Отскоки от стены",
"score_panel.close_to_unlock": "Разблокировка следующего уровня:",
"score_panel.extra_lives_count": "",
"score_panel.get_upgrades_to_unlock": "Наберите {{missingUpgrades}} и наберите {{points}} больше очков, чтобы разблокировать уровень \"{{level}}\"",
"score_panel.rerolls_count": "Вы накопили {{rerolls}} повторных бросков",
"score_panel.score_to_unlock": "Наберите {{points}} больше очков, чтобы разблокировать уровень \"{{level}}\"",
"score_panel.title": "{{score}} очков на уровне {{level}}/{{max}} ",
"score_panel.upcoming_levels": "Предстоящие уровни :",
@ -305,9 +301,6 @@
"upgrades.extra_levels.name": "Еще 5 минут",
"upgrades.extra_levels.tooltip": "Играйте {{count}} уровней вместо 7",
"upgrades.extra_levels.verbose_description": "По умолчанию игра может длиться максимум 7 уровней, после чего игра заканчивается.\n\nКаждый уровень этой привилегии позволяет вам подняться на один уровень выше. Последние уровни часто являются теми, где вы набираете больше всего очков, так что разница может быть значительной.",
"upgrades.extra_life.name": "Экстра жизнь",
"upgrades.extra_life.tooltip": "Мяч отскочит один раз на нижнюю линию, после чего будет потерян.",
"upgrades.extra_life.verbose_description": "Обычно у вас один шар, и игра заканчивается, как только вы его бросите.\n\nЭто преимущество добавляет белую полоску в нижней части экрана, которая сохранит шарик один раз и разобьется в процессе.\n\nВы будете терять один уровень этого перка каждый раз, когда мяч будет отскакивать в нижнюю часть экрана.",
"upgrades.forgiving.name": "Прощение",
"upgrades.forgiving.tooltip": "Пропущенные паузы уменьшают комбо постепенно, а не все сразу.",
"upgrades.forgiving.verbose_description": "Первый промах за уровень - бесплатно, затем 10% от комбо, затем 20%.",

View file

@ -68,17 +68,13 @@
"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.after_buttons": " {{level}}/{{max}}seviyesini yeni bitirdiniz.",
"level_up.before_buttons": " {{time}} saniyede {{levelSpawnedCoins}} üzerinden {{score}} jeton {{catchGain}} yakaladın {{timeGain}}.\n {{levelMisses}} kez {{missesGain}} ıskaladın ve {{levelWallBounces}} kez duvarlara veya tavana çarptın{{wallHitsGain}}.\n{{compliment}}",
"level_up.compliment_advice": "Tüm paraları toplamaya çalışın, tuğlaları asla kaçırmayın, duvarlara/tavana çarpmayın veya ek yükseltmeler kazanmak için 30 saniyenin altındaki seviyeyi temizlemeyin.",
"level_up.compliment_good": "Tebrikler !",
"level_up.compliment_perfect": "Çok etkileyici, böyle devam edin!",
"level_up.pick_upgrade_title": "Bir yükseltme seçin",
"level_up.plus_one_upgrade": "(+1 yükseltme)",
"level_up.plus_one_upgrade_and_reroll": "(+1 yükseltme ve +1 yeniden atma)",
"level_up.reroll": "Tekrar at ({{count}})",
"level_up.reroll_help": "Yeni seçenekler sunun",
"level_up.upgrade_perk_to_level": "Seviye {{level}}",
"level_up.go": "",
"level_up.instructions": "",
"level_up.maxed_upgrade": "",
"level_up.no_points": "",
"level_up.pick_upgrade": "",
"level_up.title": " {{level}}/{{max}}seviyesini yeni bitirdiniz.",
"level_up.upgrade_perk_to_level": "",
"main_menu.basic": "",
"main_menu.basic_help": "",
"main_menu.colorful_coins": "",
@ -161,8 +157,8 @@
"play.stats.levelTime": "Seviye zamanı",
"play.stats.levelWallBounces": "Duvar sıçramaları",
"score_panel.close_to_unlock": "Sonraki seviyenin kilidini aç:",
"score_panel.extra_lives_count": "",
"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": " {{rerolls}} yeniden atma biriktirdiniz",
"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": "Yaklaşan seviyeler :",
@ -305,9 +301,6 @@
"upgrades.extra_levels.name": "5 dakika daha",
"upgrades.extra_levels.tooltip": "7 yerine {{count}} seviye oyna",
"upgrades.extra_levels.verbose_description": "Varsayılan oyun en fazla 7 seviye sürebilir, ardından oyun biter. \n\nBu avantajın her seviyesi bir seviye daha yukarı çıkmanızı sağlar. Son seviyeler genellikle en fazla puanı aldığınız seviyelerdir, bu yüzden fark dramatik olabilir.",
"upgrades.extra_life.name": "Ekstra Hayat",
"upgrades.extra_life.tooltip": "Top, kaybedilmeden önce alt çizgide bir kez sekecektir.",
"upgrades.extra_life.verbose_description": "Normalde bir topunuz vardır ve oyun onu bıraktığınız anda biter.\n\nBu yetenek, ekranın altına bir kez topu kurtaracak ve bu süreçte kırılacak beyaz bir çubuk ekler. \n\nEkranın altında bir top her zıpladığında bu yeteneğin bir seviyesini kaybedersiniz.",
"upgrades.forgiving.name": "Bağışlayıcı",
"upgrades.forgiving.tooltip": "Molaları kaçırmak, komboyu bir kerede azaltmak yerine kademeli olarak azaltır.",
"upgrades.forgiving.verbose_description": "Her seviyede ilk ıskalama bedava, sonra kombonun %10'u, sonra %20'si...",

View file

@ -8,7 +8,7 @@ import {
makeEmptyPerksMap,
sumOfValues,
} from "./game_utils";
import { dontOfferTooSoon, resetBalls } from "./gameStateMutators";
import { resetBalls } from "./gameStateMutators";
import { isOptionOn } from "./options";
import { getHistory } from "./gameOver";
import { getSettingValue, getTotalScore } from "./settings";
@ -17,6 +17,7 @@ import {
isLevelLocked,
reasonLevelIsLocked,
} from "./get_level_unlock_condition";
import {dontOfferTooSoon} from "./openUpgradesPicker";
export function getRunLevels(
params: RunParams,
@ -143,7 +144,7 @@ export function newGameState(params: RunParams): GameState {
needsRender: true,
autoCleanUses: 0,
...defaultSounds(),
rerolls: 0,
extra_lives: 0,
creative:
params?.computer_controlled ||
sumOfValues(params.perks) > 1 ||

View file

@ -1,17 +1,13 @@
import { GameState } 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 { firstWhere } from "./pure_functions";
import { getSettingValue, getTotalScore } from "./settings";
import {
getLevelUnlockCondition,
reasonLevelIsLocked,
upgradeName,
} from "./get_level_unlock_condition";
import {GameState} 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 {firstWhere} from "./pure_functions";
import {getSettingValue, getTotalScore} from "./settings";
import {getLevelUnlockCondition, reasonLevelIsLocked, upgradeName,} from "./get_level_unlock_condition";
export async function openScorePanel(gameState: GameState) {
pause(true);
@ -28,8 +24,8 @@ export async function openScorePanel(gameState: GameState) {
pickedUpgradesHTMl(gameState),
levelsListHTMl(gameState, gameState.currentLevel),
getNearestUnlockHTML(gameState),
gameState.rerolls
? t("score_panel.rerolls_count", { rerolls: gameState.rerolls })
gameState.extra_lives
? t("score_panel.extra_lives_count", { count: gameState.extra_lives })
: "",
],
allowClose: true,
@ -100,3 +96,4 @@ export function getNearestUnlockHTML(gameState: GameState) {
`;
}

142
src/openUpgradesPicker.ts Normal file
View file

@ -0,0 +1,142 @@
import {GameState, PerkId} from "./types";
import {
catchRateBest,
catchRateGood,
levelTimeBest,
levelTimeGood,
missesBest,
missesGood,
wallBouncedBest,
wallBouncedGood
} from "./pure_functions";
import {t} from "./i18n/i18n";
import {icons, upgrades} from "./loadGameData";
import {asyncAlert} from "./asyncAlert";
import {
getPossibleUpgrades,
levelsListHTMl,
max_levels,
pickedUpgradesHTMl,
upgradeLevelAndMaxDisplay
} from "./game_utils";
import {getNearestUnlockHTML} from "./openScorePanel";
export async function openUpgradesPicker(gameState: GameState) {
const catchRate =
gameState.levelCoughtCoins / (gameState.levelSpawnedCoins || 1);
gameState.extra_lives++
let choices = 3 + gameState.perks.one_more_choice
if (gameState.levelWallBounces < wallBouncedGood) {
choices++;
gameState.extra_lives++;
if (gameState.levelWallBounces < wallBouncedBest) {
choices++;
}
}
if (gameState.levelTime < levelTimeGood * 1000) {
choices++;
gameState.extra_lives++;
if (gameState.levelTime < levelTimeBest * 1000) {
choices++;
}
}
if (catchRate > catchRateGood / 100) {
choices++;
gameState.extra_lives++;
if (catchRate > catchRateBest / 100) {
choices++;
}
}
if (gameState.levelMisses < missesGood) {
choices++;
gameState.extra_lives++;
if (gameState.levelMisses < missesBest) {
choices++;
}
}
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)
.slice(0, choices)
.map(u=>u.id)
offered.forEach((id) => {
dontOfferTooSoon(gameState, id);
});
let list = upgrades.filter(u=>offered.includes(u.id)||gameState.perks[u.id])
while (true) {
let actions: Array<{
text: string;
icon: string;
value: PerkId | null;
help: string;
className: string;
tooltip: string;
}> = list.map((u) => {
const disabled= !gameState.extra_lives || gameState.perks[u.id] >= u.max + gameState.perks.limitless
const lvl= gameState.perks[u.id]
return ({
text: u.name + (lvl ? upgradeLevelAndMaxDisplay(u, gameState):''),
help: u.help(Math.max(1, lvl)),
tooltip: u.fullHelp(Math.max(1, lvl)),
icon: icons["icon:" + u.id],
value: u.id as PerkId,
className: "upgrade " + (disabled && lvl ? 'no-border ':' ') + ( lvl ? '':'grey-out-unless-hovered ') ,
disabled:disabled ,
})
})
actions = [
...actions.filter(a=>gameState.perks[a.value]),
...actions.filter(a=>!gameState.perks[a.value]),
]
const upgradeId = await asyncAlert<PerkId | null>({
title:
t("level_up.title", {
level: gameState.currentLevel ,
max: max_levels(gameState),
}),
content: [
{
text: t('level_up.go',{name:gameState.level.name}),
icon: icons[gameState.level.name],
value:null,
},
// pickedUpgradesHTMl(gameState),
gameState.extra_lives ? `<p>${t("level_up.instructions", {
count:gameState.extra_lives
})}</p>` :`<p>${t("level_up.no_points")}</p>` ,
...actions,
levelsListHTMl(gameState, gameState.currentLevel ),
getNearestUnlockHTML(gameState),
`<div id="level-recording-container"></div>`,
],
});
if (upgradeId ) {
gameState.perks[upgradeId]++;
gameState.runStatistics.upgrades_picked++;
gameState.extra_lives--
}else{
return
}
}
}
export function dontOfferTooSoon(gameState: GameState, id: PerkId) {
gameState.lastOffered[id] = Math.round(Date.now() / 1000);
}

View file

@ -108,10 +108,9 @@ export function render(gameState: GameState) {
: "") +
`<span class="score" data-tooltip="${t("play.score_tooltip")}">$${gameState.score}</span>`;
scoreDisplay.className =
(gameState.startParams.computer_controlled && "computer_controlled") ||
(gameState.lastScoreIncrease > gameState.levelTime - 500 && "active") ||
"";
scoreDisplay.classList[gameState.startParams.computer_controlled ? 'add':'remove']('computer_controlled');
scoreDisplay.classList[gameState.lastScoreIncrease > gameState.levelTime - 500 ? 'add':'remove']('active');
// Clear
if (!isOptionOn("basic") && level.svg && level.color === "#000000") {
const skipN =
@ -363,13 +362,13 @@ export function render(gameState: GameState) {
ctx.globalCompositeOperation = "screen";
drawBall(ctx, color, size, x, y);
});
//
startWork("render:extra_life");
if (gameState.perks.extra_life) {
if (gameState.extra_lives) {
ctx.globalAlpha = gameState.balls.length > 1 ? 0.2 : 1;
ctx.globalCompositeOperation = "source-over";
ctx.fillStyle = gameState.puckColor;
for (let i = 0; i < gameState.perks.extra_life; i++) {
for (let i = 0; i < gameState.extra_lives; i++) {
ctx.fillRect(
gameState.offsetXRoundedDown,
gameState.gameZoneHeight - gameState.puckHeight / 2 + 2 * i,

View file

@ -3,7 +3,6 @@ import { isOptionOn } from "./options";
const tooltip = document.getElementById("tooltip") as HTMLDivElement;
export function setupTooltips() {
return
if (isOptionOn("mobile-mode")) {
setupMobileTooltips(tooltip);
} else {

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 };
};
rerolls: number;
extra_lives: number;
creative: boolean;
startParams: RunParams;
};

View file

@ -44,18 +44,6 @@ export const rawUpgrades = [
t("upgrades.bigger_puck.verbose_description", { lvl }),
},
{
category: categories.beginner,
requires: "",
threshold: 0,
gift: false,
id: "extra_life",
max: 7,
name: t("upgrades.extra_life.name"),
help: (lvl: number) => t("upgrades.extra_life.tooltip"),
fullHelp: (lvl: number) =>
t("upgrades.extra_life.verbose_description", { lvl }),
},
{
category: categories.beginner,
requires: "",
@ -548,7 +536,7 @@ export const rawUpgrades = [
},
{
category: categories.advanced,
requires: "extra_life",
requires: "",
threshold: 110000,
gift: false,
id: "sacrifice",