This commit is contained in:
Renan LE CARO 2025-04-20 21:14:35 +02:00
parent 9716235531
commit 0a1d9dfe2f
18 changed files with 2391 additions and 1981 deletions

View file

@ -22,6 +22,14 @@ Other translation are very welcome, contact me if you'd like to submit one.
# Changelog
## To do
## Done
- level editor : removed the conditions on bricks count, level name and credits to be able to copy the code
- shadow around ball when there are many coins : enabled in basic mode too
- hot start : after reset, if you raise the combo again, only start ticking down after a whole second.
- new perk : ottawa treaty, breaking a brick near a bomb disarms the bomb
- shocks now doesn't add ball speed at level 1
- creative mode UI rework
- compound_interest : combo resets as soon as coin passes the paddle line
- added bombs to implosion and kaboom starter levels
- toast an error if storage is blocked
@ -320,7 +328,7 @@ Other translation are very welcome, contact me if you'd like to submit one.
- cash out : double last level's gains
- snowball : Combo resets every 0.1s . +1 combo for each combo gained Since last reset.
- Chain reaction : +lvl*lvl combo per brick broken by an explosion, combo resets after explosion is over
- catching a coin changes the color of the balls
- coins stained by balls
- fast pause : pause delay divided by {{lvl}} (helps with teleport)
- [colin] Capital - les vies non perdues à la fin du niveau rapportent un bonus de points

143
dist/index.html vendored

File diff suppressed because one or more lines are too long

View file

@ -116,7 +116,7 @@ export async function openCreativeModePerksPicker() {
});
setSettingValue("creativeModePerks", creativeModePerks);
setSettingValue("creativeModeLevel", '')
} else if (choice === "play") {
} else if (choice === "play" || ("bricks" in choice && choice.name==getSettingValue("creativeModeLevel", ''))) {
if (await confirmRestart(gameState)) {
restart({
perks: creativeModePerks,
@ -131,6 +131,8 @@ export async function openCreativeModePerksPicker() {
creativeModePerks[choice.id] =
((creativeModePerks[choice.id] || 0) + 1) %
(choice.max + 1 + (creativeModePerks.limitless || 0));
setSettingValue("creativeModePerks", creativeModePerks);
}
}
}

View file

@ -1341,8 +1341,22 @@
{
"color": "#115988",
"size": 21,
"bricks": "__________________________________________________yy_______________yy__yy__yy___________yy__yy__yy____________yy__yy_yy_________y__yy__yy_yy________yyy_yyy_yy_yy_________yy__yy_yyyyy__________yy_yyyyyyyy___yyy____yyyyygggyyy__yyy______yyygBBBgyy_yyy________ygBBBBBgyyyy_________ygBBBBBgyyy__________yygBBBgyyyy___________yygBgyyyy____________yyyByyyy_____________yyyyByy_______________yyByy_________________r_________________________________",
"bricks": "__________________________________________________yy_______________yy__yy__yy___________yy__yy__yy____________yy__yy_yy_________y__yy__yy_yy________yyy_yyy_yy_yy_________yy__yy_yyyyy__________yy_yyyyyyyy___yyy____yyyyygggyyy__yyy______yyygBBBgyy_yyy________ygBBBBBgyyyy__W______ygBBBBBgyyy__________yygBBBgyyyy___________yygBgyyyy____________yyyByyyy_____________yyyyByy_______________yyByy_________________r_________________________________",
"name": "A Very Dangerous High-Five",
"credit": "Suggested by Noodlemire. A unique shape, fun to bounce the ball between fingers. The palm was initially boring on its own, so I gave it a big bomb. It adds a distinct feeling between the top and bottom halves."
},
{
"name": "icon:buoy",
"size": 7,
"bricks": "___b______b_____bbb__abbbbbaaatttaaaaataaaaaaaaaa",
"svg": null,
"color": ""
},
{
"name": "icon:ottawa_treaty",
"size": 8,
"bricks": "BBtWWtBBBttWWttBtWWtttWtttWWWWtttttWWtttttWWWtttBWWtWttBBBtttWBB",
"svg": null,
"color": ""
}
]

View file

@ -5,7 +5,6 @@ import { gameState, pause, restart } from "./game";
import {
currentLevelInfo,
describeLevel,
findLast,
pickedUpgradesHTMl,
reasonLevelIsLocked,
} from "./game_utils";
@ -98,6 +97,8 @@ export function gameOver(title: string, intro: string) {
help: "",
},
`<div id="level-recording-container"></div>`,
pickedUpgradesHTMl(gameState),
unlocksInfo,
getHistograms(gameState),
],

View file

@ -32,12 +32,12 @@ import {
telekinesisEffectRate,
yoyoEffectRate,
} from "./game_utils";
import { t } from "./i18n/i18n";
import { icons } from "./loadGameData";
import {t} from "./i18n/i18n";
import {icons} from "./loadGameData";
import { getCurrentMaxCoins, getCurrentMaxParticles } from "./settings";
import { background } from "./render";
import { gameOver } from "./gameOver";
import {getCurrentMaxCoins, getCurrentMaxParticles} from "./settings";
import {background} from "./render";
import {gameOver} from "./gameOver";
import {
brickIndex,
fitSize,
@ -48,11 +48,11 @@ import {
pause,
startComputerControlledGame,
} from "./game";
import { stopRecording } from "./recording";
import { isOptionOn } from "./options";
import { clamp, comboKeepingRate } from "./pure_functions";
import { addToTotalScore } from "./addToTotalScore";
import { hashCode } from "./getLevelBackground";
import {stopRecording} from "./recording";
import {isOptionOn} from "./options";
import {clamp, comboKeepingRate} from "./pure_functions";
import {addToTotalScore} from "./addToTotalScore";
import {hashCode} from "./getLevelBackground";
export function setMousePos(gameState: GameState, x: number) {
if (gameState.startParams.computer_controlled) return;
@ -470,7 +470,7 @@ export function explodeBrick(
while (coinsToSpawn > 0) {
const points = Math.min(pointsPerCoin, coinsToSpawn);
if (points < 0 || isNaN(points)) {
console.error({ points });
console.error({points});
debugger;
}
@ -649,9 +649,9 @@ export function addToScore(gameState: GameState, coin: Coin) {
addToTotalScore(gameState, coin.points);
if (gameState.score > gameState.highScore && !gameState.creative) {
gameState.highScore = gameState.score;
try{
try {
localStorage.setItem("breakout-3-hs-short", gameState.score.toString());
}catch (e){
} catch (e) {
}
}
@ -893,7 +893,7 @@ export function attract(gameState: GameState, a: Ball, b: Ball, power: number) {
export function coinBrickHitCheck(gameState: GameState, coin: Coin) {
// Make ball/coin bonce, and return bricks that were hit
const radius = coin.size / 2;
const { x, y, previousX, previousY } = coin;
const {x, y, previousX, previousY} = coin;
const vhit = hitsSomething(previousX, y, radius);
const hhit = hitsSomething(x, previousY, radius);
@ -1015,7 +1015,6 @@ export function gameStateTick(
}
gameState.balls = gameState.balls.filter((ball) => !ball.destroyed);
const remainingBricks = gameState.bricks.filter(
(b) => b && b !== "black",
).length;
@ -1025,9 +1024,12 @@ export function gameStateTick(
gameState.lastBrickBroken = 0;
}
if (
gameState.levelTime > gameState.lastTickDown + 1000 &&
gameState.perks.hot_start
if(gameState.perks.hot_start){
if(gameState.combo===baseCombo(gameState)){
// Give 1s of time between catching a coin and tick down
gameState.lastTickDown=gameState.levelTime
}else if (
gameState.levelTime > gameState.lastTickDown + 1000
) {
gameState.lastTickDown = gameState.levelTime;
decreaseCombo(
@ -1037,6 +1039,8 @@ export function gameStateTick(
gameState.gameZoneHeight - 2 * gameState.puckHeight,
);
}
}
if (
remainingBricks <= gameState.perks.skip_last &&
@ -1075,7 +1079,7 @@ export function gameStateTick(
} else {
gameOver(
t("gameOver.win.title"),
t("gameOver.win.summary", { score: gameState.score }),
t("gameOver.win.summary", {score: gameState.score}),
);
}
} else if (gameState.running || gameState.levelTime) {
@ -1170,6 +1174,12 @@ export function gameStateTick(
coin.vy *= ratio;
coin.vx *= ratio;
}
if (coin.y > gameState.gameZoneHeight && coin.floatingTime < gameState.perks.buoy * 30) {
coin.floatingTime += frames
coin.vy -= 1.5
}
if (coin.vx > 7 * gameState.baseSpeed) coin.vx = 7 * gameState.baseSpeed;
if (coin.vx < -7 * gameState.baseSpeed)
coin.vx = -7 * gameState.baseSpeed;
@ -1207,9 +1217,7 @@ export function gameStateTick(
coin.y,
0,
dvy * 10,
gameState.perks.metamorphosis || isOptionOn("colorful_coins")
? coin.color
: "#ffd300",
getCoinRenderColor(gameState, coin),
true,
5,
250,
@ -1222,7 +1230,8 @@ export function gameStateTick(
coin.previousY < gameState.gameZoneHeight &&
coin.y > gameState.gameZoneHeight &&
coin.vy > 0 &&
speed > 20
speed > 20 &&
!coin.floatingTime
) {
schedulGameSound(
gameState,
@ -1320,22 +1329,26 @@ export function gameStateTick(
if (gameState.perks.shocks) {
gameState.balls.forEach((a, ai) =>
gameState.balls.forEach((b, bi) => {
if (
ai < bi &&
!a.destroyed &&
!b.destroyed &&
distance2(a, b) < gameState.ballSize * gameState.ballSize
) {
// switch speeds
let tempVx = a.vx;
let tempVy = a.vy;
a.vx = b.vx;
a.vy = b.vy;
b.vx = tempVx;
b.vy = tempVy;
// Compute center
let x = (a.x + b.x) / 2;
let y = (a.y + b.y) / 2;
const limit = gameState.baseSpeed;
// space out the balls with extra speed
if (gameState.perks.shocks > 1) {
const limit = gameState.baseSpeed * gameState.perks.shocks / 2;
a.vx +=
clamp(a.x - x, -limit, limit) +
((Math.random() - 0.5) * limit) / 3;
@ -1348,7 +1361,7 @@ export function gameStateTick(
b.vy +=
clamp(b.y - y, -limit, limit) +
((Math.random() - 0.5) * limit) / 3;
}
let index = brickIndex(x, y);
explosionAt(
gameState,
@ -1498,7 +1511,7 @@ export function gameStateTick(
setBrick(gameState, r.index, r.color);
destroy(gameState.respawns, ri);
} else {
const { index, color } = r;
const {index, color} = r;
const vertical = Math.random() > 0.5;
const dx = Math.random() > 0.5 ? 1 : -1;
const dy = Math.random() > 0.5 ? 1 : -1;
@ -1744,14 +1757,14 @@ export function ballTick(gameState: GameState, ball: Ball, frames: number) {
} else {
gameOver(
t("gameOver.lost.title"),
t("gameOver.lost.summary", { score: gameState.score }),
t("gameOver.lost.summary", {score: gameState.score}),
);
}
}
}
const radius = gameState.ballSize / 2;
// Make ball/coin bonce, and return bricks that were hit
const { x, y, previousX, previousY } = ball;
const {x, y, previousX, previousY} = ball;
const vhit = hitsSomething(previousX, y, radius);
const hhit = hitsSomething(x, previousY, radius);
@ -1812,6 +1825,7 @@ export function ballTick(gameState: GameState, ball: Ball, frames: number) {
if (!gameState.brickHP[hitBrick]) {
ball.brokenSinceBounce++;
applyOttawaTreatyPerk(gameState, hitBrick,ball)
explodeBrick(gameState, hitBrick, ball, false);
if (
ball.sapperUses < gameState.perks.sapper &&
@ -1939,6 +1953,7 @@ function makeCoin(
p.points = points;
p.weight = weight;
p.metamorphosisPoints = gameState.perks.metamorphosis;
p.floatingTime = 0
});
}
@ -2020,7 +2035,7 @@ export function append<T>(
makeItem(where.list[where.indexMin]);
where.indexMin++;
} else {
const p = { destroyed: false };
const p = {destroyed: false};
makeItem(p);
where.list.push(p);
}
@ -2112,3 +2127,33 @@ function goToNearestBrick(
);
}
}
function applyOttawaTreatyPerk(gameState: GameState, index: number,ball:Ball) {
if (!gameState.perks.ottawa_treaty) return console.log('!gameState.perks.ottawa_treaty')
if (ball.sapperUses) return console.log('ball.sapperUses')
const originalColor = gameState.bricks[index]
if (originalColor == 'black') return console.log("originalColor == 'black'")
const x = index % gameState.gridSize
const y = Math.floor(index / gameState.gridSize)
let converted = 0
for (let dx = -1; dx <= 1; dx++)
for (let dy = -1; dy <= 1; dy++)
if (dx || dy) {
const nIndex = getRowColIndex(gameState, y + dy, x + dx)
if (gameState.bricks[nIndex] && gameState.bricks[nIndex] === 'black') {
console.log('converted brick '+nIndex+ ' from '+gameState.bricks[nIndex] + ' to '+originalColor)
setBrick(gameState, nIndex, originalColor)
schedulGameSound(gameState, "colorChange", brickCenterX(gameState, index), 1)
// Avoid infinite bricks generation hack
ball.sapperUses=Infinity
converted++
// Don't convert more than one brick per hit normally
if(converted>=gameState.perks.ottawa_treaty) return console.log("converted>=gameState.perks.ottawa_treaty");
}
}
return console.log("done",converted);
}

View file

@ -269,6 +269,9 @@
"upgrades.bricks_attract_coins.name": "الطوب يجذب العملات المعدنية",
"upgrades.bricks_attract_coins.tooltip": "يساعدهم على البقاء هناك",
"upgrades.bricks_attract_coins.verbose_description": "",
"upgrades.buoy.name": "",
"upgrades.buoy.tooltip": "",
"upgrades.buoy.verbose_description": "",
"upgrades.clairvoyant.name": "مستبصر",
"upgrades.clairvoyant.tooltip": "شاهد المستويات القادمة، نقاط الصحة للطوب واتجاه الكرة",
"upgrades.clairvoyant.verbose_description": "يساعدك على اختيار الترقيات المناسبة وفهم كيفية عمل الطوب المتين. يُضيف المستويان 2 و3 معلومات إضافية حول فائدة مشكوك فيها (متوفرة في وضع الحلقة).",
@ -340,6 +343,9 @@
"upgrades.one_more_choice.name": "خيار إضافي",
"upgrades.one_more_choice.tooltip": "ستوفر عمليات رفع المستوى الإضافية {{lvl}} خيارًا إضافيًا في القائمة",
"upgrades.one_more_choice.verbose_description": "ستحتوي كل قائمة ترقية على خيار إضافي. هذا لا يزيد من عدد الترقيات المتاحة.",
"upgrades.ottawa_treaty.name": "",
"upgrades.ottawa_treaty.tooltip": "",
"upgrades.ottawa_treaty.verbose_description": "",
"upgrades.passive_income.name": "الدخل السلبي",
"upgrades.passive_income.tooltip": "+{{lvl}} مجموعة / لبنة، ما لم يتم تحريك المجداف في آخر {{time}}ثانية، ثم يتم إعادة تعيينه بدلاً من ذلك",
"upgrades.passive_income.verbose_description": "يمكن لبعض الامتيازات أن تساعد الكرات على القيام بما تريد دون الحاجة إلى القيام بأي شيء.",

View file

@ -9617,6 +9617,116 @@
</concept_node>
</children>
</folder_node>
<folder_node>
<name>buoy</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>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>tooltip</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>verbose_description</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>
<name>clairvoyant</name>
<children>
@ -12217,6 +12327,116 @@
</concept_node>
</children>
</folder_node>
<folder_node>
<name>ottawa_treaty</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>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>tooltip</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>verbose_description</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>
<name>passive_income</name>
<children>

View file

@ -269,6 +269,9 @@
"upgrades.bricks_attract_coins.name": "Ziegelsteine ziehen Münzen an",
"upgrades.bricks_attract_coins.tooltip": "Hilft ihnen, dort oben zu bleiben",
"upgrades.bricks_attract_coins.verbose_description": "",
"upgrades.buoy.name": "",
"upgrades.buoy.tooltip": "",
"upgrades.buoy.verbose_description": "",
"upgrades.clairvoyant.name": "Hellsichtig",
"upgrades.clairvoyant.tooltip": "Sehen Sie die nächsten Levels, die HP der Steine und die Ballrichtung",
"upgrades.clairvoyant.verbose_description": "Hilft dir, die richtigen Upgrades auszuwählen und zu verstehen, was es mit den robusten Steinen auf sich hat. Level 2 und 3 bringen zusätzliches Wissen von zweifelhaftem Nutzen (erreichbar im Loop-Modus)",
@ -340,6 +343,9 @@
"upgrades.one_more_choice.name": "Extra Auswahl",
"upgrades.one_more_choice.tooltip": "Weitere Stufenaufstiege bieten {{lvl}} weitere Option(en) in der Liste",
"upgrades.one_more_choice.verbose_description": "Jedes Upgrade-Menü wird eine weitere Option enthalten. Erhöht nicht die Anzahl der Upgrades, die Sie auswählen können.",
"upgrades.ottawa_treaty.name": "",
"upgrades.ottawa_treaty.tooltip": "",
"upgrades.ottawa_treaty.verbose_description": "",
"upgrades.passive_income.name": "Passives Einkommen",
"upgrades.passive_income.tooltip": "+{{lvl}} Combo / Brick, es sei denn, das Paddel hat sich in den letzten {{time}}s bewegt, dann wird es stattdessen zurückgesetzt",
"upgrades.passive_income.verbose_description": "Einige Vergünstigungen können den Bällen helfen, das zu tun, was du willst, ohne dass du etwas tun musst.",

View file

@ -269,6 +269,9 @@
"upgrades.bricks_attract_coins.name": "Bricks attract coins",
"upgrades.bricks_attract_coins.tooltip": "Helps them stay up there",
"upgrades.bricks_attract_coins.verbose_description": "",
"upgrades.buoy.name": "Buoy",
"upgrades.buoy.tooltip": "Coins float for {{duration}} seconds on the bottom line. ",
"upgrades.buoy.verbose_description": "Effect is most visible in mobile mode",
"upgrades.clairvoyant.name": "Clairvoyant",
"upgrades.clairvoyant.tooltip": "See upcoming levels, bricks HP and ball direction",
"upgrades.clairvoyant.verbose_description": "Helps you pick the right upgrades and understand what's going on with sturdy bricks. Level 2 and 3 bring additional knowledge of dubious utility (reachable in loop mode)",
@ -340,6 +343,9 @@
"upgrades.one_more_choice.name": "Extra choice",
"upgrades.one_more_choice.tooltip": "Further level ups will offer {{lvl}} more option(s) in the list",
"upgrades.one_more_choice.verbose_description": "Every upgrade menu will have one more option. Doesn't increase the number of upgrades you can pick.",
"upgrades.ottawa_treaty.name": "Ottawa treaty",
"upgrades.ottawa_treaty.tooltip": "Breaking a brick near a bomb disarms it",
"upgrades.ottawa_treaty.verbose_description": "The nearby bomb will be replaced by a colored block. If you have sapper, the ball will loose its sapper effect until next bounce. Only one bomb can be replaced at a time.",
"upgrades.passive_income.name": "Passive income",
"upgrades.passive_income.tooltip": "+{{lvl}} combo / brick, unless the paddle moved in the last {{time}}s, then it resets instead",
"upgrades.passive_income.verbose_description": "Some perks can help the balls do what you want without needing to do anything.",

View file

@ -269,6 +269,9 @@
"upgrades.bricks_attract_coins.name": "Los ladrillos atraen monedas",
"upgrades.bricks_attract_coins.tooltip": "Les ayuda a permanecer allí arriba.",
"upgrades.bricks_attract_coins.verbose_description": "",
"upgrades.buoy.name": "",
"upgrades.buoy.tooltip": "",
"upgrades.buoy.verbose_description": "",
"upgrades.clairvoyant.name": "Perspicaz",
"upgrades.clairvoyant.tooltip": "Revela los niveles, el PV de los ladrillos y la dirección de las balas",
"upgrades.clairvoyant.verbose_description": "Te ayuda a elegir las mejoras adecuadas y a entender qué ocurre con los \"ladrillos macizos\". Los niveles 2 y 3 (en modo bucle) proporcionan información adicional de dudosa utilidad.",
@ -340,6 +343,9 @@
"upgrades.one_more_choice.name": "La respuesta D",
"upgrades.one_more_choice.tooltip": "1 opción de mejora adicional disponible hasta el final del juego",
"upgrades.one_more_choice.verbose_description": "Cada menú de mejoras tendrá una opción adicional. Esto no aumenta el número de mejoras que puede elegir, pero le ayuda a crear el perfil ideal. \"Respuesta D\" es una referencia a un sketch clásico.",
"upgrades.ottawa_treaty.name": "",
"upgrades.ottawa_treaty.tooltip": "",
"upgrades.ottawa_treaty.verbose_description": "",
"upgrades.passive_income.name": "Ingresos pasivos",
"upgrades.passive_income.tooltip": "+{{lvl}} combo / ladrillo, a menos que la raqueta se haya movido en los últimos {{time}} segundos, en cuyo caso se pierde el combo.",
"upgrades.passive_income.verbose_description": "Algunas mejoras hacen que las pelotas se muevan sin necesidad de poner la raqueta en movimiento.",

View file

@ -269,6 +269,9 @@
"upgrades.bricks_attract_coins.name": "Briques attirent les pièces",
"upgrades.bricks_attract_coins.tooltip": "Aide à garder les pièces en suspension",
"upgrades.bricks_attract_coins.verbose_description": "",
"upgrades.buoy.name": "",
"upgrades.buoy.tooltip": "",
"upgrades.buoy.verbose_description": "",
"upgrades.clairvoyant.name": "Clairvoyant",
"upgrades.clairvoyant.tooltip": "Révèle les niveaux, PV des briques et direction des balles",
"upgrades.clairvoyant.verbose_description": "Vous aide à choisir les bonnes améliorations et à comprendre ce qu'il se passe avec \"briques solides\". Les niveaux 2 et 3 (en mode loop) amènent des informations complémentaires d'une utilité douteuse. ",
@ -340,6 +343,9 @@
"upgrades.one_more_choice.name": "La réponse D",
"upgrades.one_more_choice.tooltip": "1 choix supplémentaire d'amélioration proposé jusqu'à la fin de la partie",
"upgrades.one_more_choice.verbose_description": "Chaque menu d'amélioration comportera une option supplémentaire. Cela n'augmente pas le nombre d'améliorations que vous pouvez choisir, mais vous aide à créer le profile idéal. \"La réponse D\" est une référence à un sketch classique. ",
"upgrades.ottawa_treaty.name": "",
"upgrades.ottawa_treaty.tooltip": "",
"upgrades.ottawa_treaty.verbose_description": "",
"upgrades.passive_income.name": "Revenu passif",
"upgrades.passive_income.tooltip": "+{{lvl}} combo / brique, sauf si la raquette à bougé dans les {{time}} dernières secondes, combo perdu dans ce cas",
"upgrades.passive_income.verbose_description": "Certaines amélioration font bouger les balles sans avoir besoin de mettre la raquette en mouvement.",

View file

@ -269,6 +269,9 @@
"upgrades.bricks_attract_coins.name": "Кирпичи притягивают монеты",
"upgrades.bricks_attract_coins.tooltip": "Помогает им оставаться на вершине",
"upgrades.bricks_attract_coins.verbose_description": "",
"upgrades.buoy.name": "",
"upgrades.buoy.tooltip": "",
"upgrades.buoy.verbose_description": "",
"upgrades.clairvoyant.name": "Ясновидящий",
"upgrades.clairvoyant.tooltip": "Просматривайте предстоящие уровни, количество кирпичей и направление движения мяча",
"upgrades.clairvoyant.verbose_description": "Поможет выбрать правильные апгрейды и понять, что происходит с прочными кирпичами. Уровни 2 и 3 дают дополнительные знания сомнительной полезности (достигаются в режиме цикла)",
@ -340,6 +343,9 @@
"upgrades.one_more_choice.name": "Дополнительный выбор",
"upgrades.one_more_choice.tooltip": "При дальнейшем повышении уровня будет предложено на {{lvl}} больше вариантов в списке",
"upgrades.one_more_choice.verbose_description": "В каждом меню апгрейдов появится еще одна опция. Это не увеличивает количество апгрейдов, которые вы можете выбрать.",
"upgrades.ottawa_treaty.name": "",
"upgrades.ottawa_treaty.tooltip": "",
"upgrades.ottawa_treaty.verbose_description": "",
"upgrades.passive_income.name": "Пассивный доход",
"upgrades.passive_income.tooltip": "+{{lvl}} комбо/кирпич, если только паддл не двигался в течение последних {{time}}с, тогда он сбрасывается.",
"upgrades.passive_income.verbose_description": "Некоторые привилегии могут помочь шарам делать то, что вы хотите, без необходимости что-либо предпринимать.",

View file

@ -269,6 +269,9 @@
"upgrades.bricks_attract_coins.name": "Tuğlalar madeni paraları çeker",
"upgrades.bricks_attract_coins.tooltip": "Onların orada kalmalarına yardımcı olur",
"upgrades.bricks_attract_coins.verbose_description": "",
"upgrades.buoy.name": "",
"upgrades.buoy.tooltip": "",
"upgrades.buoy.verbose_description": "",
"upgrades.clairvoyant.name": "Durugörü sahibi",
"upgrades.clairvoyant.tooltip": "Yaklaşan seviyeleri, tuğla HP'sini ve top yönünü görün",
"upgrades.clairvoyant.verbose_description": "Doğru yükseltmeleri seçmenize ve sağlam tuğlalarla neler olup bittiğini anlamanıza yardımcı olur. Seviye 2 ve 3, şüpheli fayda hakkında ek bilgi getirir (döngü modunda erişilebilir)",
@ -340,6 +343,9 @@
"upgrades.one_more_choice.name": "Ekstra seçenek",
"upgrades.one_more_choice.tooltip": "Daha fazla seviye atlama, listede {{lvl}} daha fazla seçenek sunacak",
"upgrades.one_more_choice.verbose_description": "Her yükseltme menüsü bir seçeneğe daha sahip olacak. Seçebileceğiniz yükseltme sayısını artırmaz.",
"upgrades.ottawa_treaty.name": "",
"upgrades.ottawa_treaty.tooltip": "",
"upgrades.ottawa_treaty.verbose_description": "",
"upgrades.passive_income.name": "Pasif gelir",
"upgrades.passive_income.tooltip": "+{{lvl}} kombo / tuğla, kürek son {{time}}saniyede hareket etmediği sürece, bunun yerine sıfırlanır",
"upgrades.passive_income.verbose_description": "Bazı özellikler, topların hiçbir şey yapmanıza gerek kalmadan istediğinizi yapmasına yardımcı olabilir.",

View file

@ -165,10 +165,7 @@ export async function editRawLevelList(nth: number, color = "W") {
text: t("editor.editing.copy"),
value: "copy",
help: t("editor.editing.copy_help"),
disabled:
!level.name ||
!level.credit ||
bricks.filter((b) => b !== "_").length < 6,
},
{
text: t("editor.editing.bigger"),
@ -253,14 +250,14 @@ export async function editRawLevelList(nth: number, color = "W") {
return;
}
if (action === "copy") {
let text = "```\n[" + level.name?.replace(/\[|\]/gi, " ") + "]";
let text = "```\n[" + (level.name||'unnamed level')?.replace(/\[|\]/gi, " ") + "]";
bricks.forEach((b, bi) => {
if (!(bi % level.size)) text += "\n";
text += b;
});
text +=
"\n[" +
(level.credit?.replace(/\[|\]/gi, " ") || "Missing credits!") +
(level.credit?.replace(/\[|\]/gi, " ") || "Missing credits") +
"]\n```";
navigator.clipboard.writeText(text);
// return

View file

@ -302,7 +302,6 @@ export function render(gameState: GameState) {
});
startWork("render:ball shade");
// Black shadow around balls
if (!isOptionOn("basic")) {
ctx.globalCompositeOperation = "source-over";
gameState.balls.forEach((ball) => {
ctx.globalAlpha =
@ -317,7 +316,6 @@ export function render(gameState: GameState) {
ball.y,
);
});
}
startWork("render:bricks");
ctx.globalCompositeOperation = "source-over";
renderAllBricks();

1
src/types.d.ts vendored
View file

@ -84,6 +84,7 @@ export type Coin = {
destroyed?: boolean;
collidedLastFrame?: boolean;
metamorphosisPoints: number;
floatingTime:number;
};
export type Ball = {
x: number;

View file

@ -825,4 +825,25 @@ export const rawUpgrades = [
t("upgrades.bricks_attract_ball.tooltip", { count: lvl * 3 }),
fullHelp: t("upgrades.bricks_attract_ball.verbose_description"),
},
{
requires: "",
threshold: 220000,
gift: false,
id: "buoy",
max: 3,
name: t("upgrades.buoy.name"),
help: (lvl: number) =>
t("upgrades.buoy.tooltip", { duration: lvl * 0.5 }),
fullHelp: t("upgrades.buoy.verbose_description"),
},
{
requires: "",
threshold: 225000,
gift: false,
id: "ottawa_treaty",
max: 1,
name: t("upgrades.ottawa_treaty.name"),
help: () =>t("upgrades.ottawa_treaty.tooltip"),
fullHelp: t("upgrades.ottawa_treaty.verbose_description"),
},
] as const;