Build 29071900

This commit is contained in:
Renan LE CARO 2025-04-10 21:40:45 +02:00
parent 4bb5d820c5
commit 1b94bcd6be
16 changed files with 526 additions and 143 deletions

View file

@ -23,14 +23,25 @@ Some upgrades currently are not really useful
- remove them
- add more upgrades to complement them
- update them to be more useful
-
# Changelog
## To do
- rework unbounded to just add padding around bricks
- bricks attract balls
## Done
- new perk : hypnosis
- new perk : rainbow
- new perk : bricks attract coins
- super hot level rework
- zen level added bombs
## 29071527
- super hot : time moves only when paddle moves. Later levels slow down even more the time when you're not moving.
- fixed memory leak in language detection code
- transparency : ball becomes transparent towards top of screen, +50% coins.
- space coins : coins bounce without loosing momentum
- trickledown : coins spawn at the top of the screen
@ -38,6 +49,7 @@ Some upgrades currently are not really useful
- allow removing all starting perks, to get full random
- rename "puck" into paddle
- use french as base language to keep consistent formal/informal tone
- fixed memory leak in language detection code
## 29069860

View file

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

File diff suppressed because one or more lines are too long

133
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 = "29071541";
const VERSION = "29071900";
// The name of the cache
const CACHE_NAME = `breakout-71-${VERSION}`;

View file

@ -910,7 +910,7 @@
{
"name": "icon:zen",
"size": 9,
"bricks": "___vv______vvvv______vv______bbbb____bbbbbb____bbbb____tttttt__tttttttt__tttttt__",
"bricks": "___tt______tttt_____BttB_____bbbb____bbbbbb___BbbbbB___tttttt__tttttttt__tttttt__",
"svg": null,
"color": ""
},
@ -1310,7 +1310,7 @@
{
"name": "icon:minefield",
"size": 7,
"bricks": "W__W__W_W___W___W_W__W_____W_W___W____l_____lll__",
"bricks": "W__B__WWWBBBWWB__W__BBBWWWBBW__B__WWWBBBWW_______",
"svg": null,
"color": ""
},
@ -1379,8 +1379,29 @@
},
{
"name": "icon:superhot",
"size": 19,
"bricks": "_________________________________________________________WWWBWBWBWWWBWWWBWWWWBBBWBWBWBWBWBBBWBWWWWBWBWBWWWBWWBBWWWBBWBWBWBWBBBWBBBWWBWWWBWWWBWBBBWWWBWBW___________________WWBBWWBWWWWWBWWWWWWWWBBWWBWWWWWBWWWWWWWWBBWWBWWBWWBBBWWBBWWWWWWBWWBWWBBBWWBBWWWWWWBWWBWWBBBWWBBWWBBWWBWWWWWBBBWWBBWWBBWWBWWWWWBBBWWBB_________________________________________________________",
"size": 11,
"bricks": "____________________________________________W_W_WWW_WWWWWW_W_W__W_W_W_WWW__W_____________________________________________",
"svg": null,
"color": ""
},
{
"name": "icon:bricks_attract_coins",
"size": 7,
"bricks": "_y__y___tttttyyttttt__ttttt_yttttty_ttttt___y__y_",
"svg": null,
"color": ""
},
{
"name": "icon:rainbow",
"size": 6,
"bricks": "__rOyC_rOyCa_rOyCarOyCatrOyCatrOyCat",
"svg": null,
"color": ""
},
{
"name": "icon:hypnosis",
"size": 8,
"bricks": "WrrrrrrrWrWWWWWrWrWrrrWrWrWrWrWrWrWWWrWrWrrrrrWrWWWWWWWrrrrrrrrr",
"svg": null,
"color": ""
}

View file

@ -1 +1 @@
"29071541"
"29071900"

View file

@ -957,7 +957,7 @@ document.addEventListener("keyup", async (e) => {
} else if (e.key.toLowerCase() === "m" && !alertsOpen) {
openMainMenu().then();
} else if (e.key.toLowerCase() === "s" && !alertsOpen) {
openScorePanel().then();
openScorePanel(gameState).then();
} else if (
e.key.toLowerCase() === "r" &&
!alertsOpen &&

View file

@ -18,6 +18,7 @@ import {
currentLevelInfo,
distance2,
distanceBetween,
getClosestBall,
getMajorityValue,
getPossibleUpgrades,
getRowColIndex,
@ -412,6 +413,7 @@ export function explodeBrick(
gameState.levelSpawnedCoins += coinsToSpawn;
gameState.runStatistics.coins_spawned += coinsToSpawn;
gameState.runStatistics.bricks_broken++;
const maxCoins = getCurrentMaxCoins() * (isOptionOn("basic") ? 0.5 : 1);
const spawnableCoins =
liveCount(gameState.coins) > getCurrentMaxCoins()
@ -435,6 +437,7 @@ export function explodeBrick(
cy =
y +
(Math.random() - 0.5) * (gameState.brickWidth - gameState.coinSize);
makeCoin(
gameState,
cx,
@ -442,7 +445,6 @@ export function explodeBrick(
ball.previousVX * (0.5 + Math.random()),
ball.previousVY * (0.5 + Math.random()),
color,
points,
);
}
@ -1050,18 +1052,11 @@ export function gameStateTick(
coin.sa -= attractionX / 10;
}
if (gameState.perks.ball_attracts_coins) {
if (gameState.perks.ball_attracts_coins && gameState.balls.length) {
// Find closest ball
let closestBall = gameState.balls[0];
let closestBall = getClosestBall(gameState, coin.x, coin.y);
if (closestBall) {
let dist = distance2(closestBall, coin);
gameState.balls.forEach((ball) => {
if (ball == closestBall) return;
const d2 = distance2(ball, coin);
if (d2 < dist) {
closestBall = ball;
dist = d2;
}
});
const minDist = gameState.brickWidth * gameState.brickWidth;
if (
@ -1069,7 +1064,8 @@ export function gameStateTick(
dist < minDist * 4 * 4 * gameState.perks.ball_attracts_coins
) {
// Slow down coins in effect radius
const ratio = 1 - 0.02 * (0.5 + gameState.perks.ball_attracts_coins);
const ratio =
1 - 0.02 * (0.5 + gameState.perks.ball_attracts_coins);
coin.vx *= ratio;
coin.vy *= ratio;
coin.vy *= ratio;
@ -1103,12 +1099,41 @@ export function gameStateTick(
}
}
}
}
if (gameState.perks.bricks_attract_coins) {
const row = Math.floor(coin.y / gameState.brickWidth);
const col = Math.floor(
(coin.x - gameState.offsetX) / gameState.brickWidth,
);
const size = 2;
for (let dcol = -size; dcol < size; dcol++) {
for (let drow = -size; drow < size; drow++) {
const index = getRowColIndex(gameState, row + drow, col + dcol);
if (gameState.bricks[index]) {
const dx =
brickCenterX(gameState, index) +
(clamp(dcol, -1, 1) * gameState.brickWidth) / 2 -
coin.x;
const dy =
brickCenterY(gameState, index) +
(clamp(drow, -1, 1) * gameState.brickWidth) / 2 -
coin.y;
const d = dx * dx + dy * dy;
coin.vx +=
(dx / d) * 80 * gameState.perks.bricks_attract_coins * frames;
coin.vy +=
(dy / d) * 100 * gameState.perks.bricks_attract_coins * frames;
}
}
}
}
const ratio =
1 -
((gameState.perks.viscosity * 0.03 + 0.002) * frames) /
(1 + gameState.perks.etherealcoins);
if (!gameState.perks.etherealcoins) {
coin.vy *= ratio;
coin.vx *= ratio;
@ -1122,20 +1147,34 @@ export function gameStateTick(
coin.a += coin.sa;
// Gravity
if (!gameState.perks.etherealcoins) {
const flip =
gameState.perks.helium > 0 &&
Math.abs(coin.x - gameState.puckPosition) * 2 >
gameState.puckWidth + coin.size;
coin.vy +=
let dvy =
frames * coin.weight * 0.8 * (flip ? -gameState.perks.helium : 1);
if (flip && !isOptionOn("basic") && Math.random() < 0.1 * frames) {
if (gameState.perks.etherealcoins) {
if (gameState.perks.helium) {
dvy *= 0.2 / gameState.perks.etherealcoins;
} else {
dvy *= 0;
}
}
coin.vy += dvy;
if (
gameState.perks.helium &&
!isOptionOn("basic") &&
Math.random() < 0.1 * frames
) {
makeParticle(
gameState,
coin.x,
coin.y,
0,
gameState.baseSpeed,
dvy * 10,
gameState.perks.metamorphosis || isOptionOn("colorful_coins")
? coin.color
: "#ffd300",
@ -1144,7 +1183,6 @@ export function gameStateTick(
250,
);
}
}
const speed = (Math.abs(coin.vx) + Math.abs(coin.vy)) * 10;
const hitBorder = bordersHitCheck(gameState, coin, coin.size / 2, frames);
@ -1195,6 +1233,17 @@ export function gameStateTick(
gameState.bricks[hitBrick] = coin.color;
coin.metamorphosisPoints--;
schedulGameSound(gameState, "colorChange", coin.x, 0.3);
if (gameState.perks.hypnosis) {
const closestBall = getClosestBall(gameState, coin.x, coin.y);
if (closestBall) {
coin.x = closestBall.x;
coin.y = closestBall.y;
coin.vx = (Math.random() - 0.5) * gameState.baseSpeed;
coin.vy = (Math.random() - 0.5) * gameState.baseSpeed;
coin.metamorphosisPoints = gameState.perks.metamorphosis;
}
}
}
}
@ -1202,13 +1251,12 @@ export function gameStateTick(
(!gameState.perks.ghost_coins && typeof hitBrick !== "undefined") ||
hitBorder
) {
if (!gameState.perks.etherealcoins) {
coin.vx *= 0.8;
coin.vy *= 0.8;
if (Math.abs(coin.vy) < 3) {
const ratio = 1 - 0.2 / (1 + gameState.perks.etherealcoins);
coin.vx *= ratio;
coin.vy *= ratio;
if (Math.abs(coin.vy) < 1) {
coin.vy = 0;
}
}
coin.sa *= 0.9;
if (speed > 20 && !coin.collidedLastFrame) {
schedulGameSound(gameState, "coinBounce", coin.x, 0.2);
@ -1732,12 +1780,16 @@ export function ballTick(gameState: GameState, ball: Ball, delta: number) {
}
}
if (!isOptionOn("basic")) {
if (
!isOptionOn("basic") &&
ballTransparency(ball, gameState) < Math.random()
) {
const remainingPierce = ball.piercePoints;
const remainingSapper = ball.sapperUses < gameState.perks.sapper;
const willMiss =
isOptionOn("red_miss") && ball.vy > 0 && !ball.hitSinceBounce;
const extraCombo = gameState.combo - 1;
if (
willMiss ||
(extraCombo && Math.random() > 0.1 / (1 + extraCombo)) ||
@ -1809,9 +1861,12 @@ function makeCoin(
let weight = 0.8 + Math.random() * 0.2 + Math.min(2, points * 0.01);
weight *= 5 / (5 + gameState.perks.etherealcoins);
if (gameState.perks.trickledown) {
y = -20;
}
if (gameState.perks.trickledown) y = -20;
if (
gameState.perks.rainbow &&
Math.random() > 1 / (1 + gameState.perks.rainbow)
)
color = rainbowColor();
append(gameState.coins, (p: Partial<Coin>) => {
p.x = x;

View file

@ -81,6 +81,22 @@ export function getRowColIndex(gameState: GameState, row: number, col: number) {
return row * gameState.gridSize + col;
}
export function getClosestBall(
gameState: GameState,
x: number,
y: number,
): Ball | null {
let closestBall: Ball | null = null;
let dist = 0;
gameState.balls.forEach((ball) => {
const d2 = (ball.x - x) * (ball.x - x) + (ball.y - y) * (ball.y - y);
if (d2 < dist || !closestBall) {
closestBall = ball;
dist = d2;
}
});
return closestBall;
}
export function getPossibleUpgrades(gameState: GameState) {
return upgrades
.filter((u) => getTotalScore() >= u.threshold)
@ -375,9 +391,10 @@ export function reasonLevelIsLocked(
}
export function ballTransparency(ball: Ball, gameState: GameState) {
if (!gameState.perks.transparency) return 0;
return clamp(
gameState.perks.transparency * (1 - ball.y / gameState.gameZoneHeight) -
0.2,
gameState.perks.transparency *
(1 - (ball.y / gameState.gameZoneHeight) * 1.2),
0,
1,
);

View file

@ -2697,6 +2697,56 @@
</concept_node>
</children>
</folder_node>
<folder_node>
<name>bricks_attract_coins</name>
<children>
<concept_node>
<name>name</name>
<description/>
<comment/>
<translations>
<translation>
<language>en-US</language>
<approved>true</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>true</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>tooltip</name>
<description/>
<comment/>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>true</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>verbose_description</name>
<description/>
<comment/>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>true</approved>
</translation>
</translations>
</concept_node>
</children>
</folder_node>
<folder_node>
<name>clairvoyant</name>
<children>
@ -3377,6 +3427,56 @@
</concept_node>
</children>
</folder_node>
<folder_node>
<name>hypnosis</name>
<children>
<concept_node>
<name>name</name>
<description/>
<comment/>
<translations>
<translation>
<language>en-US</language>
<approved>true</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>true</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>tooltip</name>
<description/>
<comment/>
<translations>
<translation>
<language>en-US</language>
<approved>true</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>verbose_description</name>
<description/>
<comment/>
<translations>
<translation>
<language>en-US</language>
<approved>true</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>true</approved>
</translation>
</translations>
</concept_node>
</children>
</folder_node>
<folder_node>
<name>implosions</name>
<children>
@ -4092,6 +4192,56 @@
</concept_node>
</children>
</folder_node>
<folder_node>
<name>rainbow</name>
<children>
<concept_node>
<name>name</name>
<description/>
<comment/>
<translations>
<translation>
<language>en-US</language>
<approved>true</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>true</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>tooltip</name>
<description/>
<comment/>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>true</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>verbose_description</name>
<description/>
<comment/>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>true</approved>
</translation>
</translations>
</concept_node>
</children>
</folder_node>
<folder_node>
<name>reach</name>
<children>
@ -5538,7 +5688,7 @@
<custom_languages/>
<id_extractor_ignores/>
</editor_configuration>
<primary_language>fr-FR</primary_language>
<primary_language>en-US</primary_language>
<configuration>
<indent>space2</indent>
<format>json</format>

View file

@ -170,6 +170,9 @@
"upgrades.bigger_puck.name": "Bigger paddle",
"upgrades.bigger_puck.tooltip": "Easily catch more coins.",
"upgrades.bigger_puck.verbose_description": "A bigger paddle makes it easier to never miss the ball and to catch more coins, and also to precisely angle the bounces (the ball's angle only depends on where it hits the paddle). ",
"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.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)",
@ -211,6 +214,9 @@
"upgrades.hot_start.name": "Hot start",
"upgrades.hot_start.tooltip": "Start at combo {{start}}, -{{loss}} combo per second",
"upgrades.hot_start.verbose_description": "At the start of every level, your combo will start at +30 points, but then every second it will be decreased by one. The effect stacks with other perks. ",
"upgrades.hypnosis.name": "Hypnosis",
"upgrades.hypnosis.tooltip": "Whenever a brick changes color, teleport that coin to the nearest ball and recharge its ability to stain a brick. ",
"upgrades.hypnosis.verbose_description": "",
"upgrades.implosions.name": "Implosions",
"upgrades.implosions.tooltip": "Explosions suck coins in instead of blowing them out",
"upgrades.implosions.verbose_description": "The explosion force is applied the other way. Further levels act as \"bigger explosion\"",
@ -225,7 +231,7 @@
"upgrades.limitless.verbose_description": "Choosing this perk also raises his own limit by one, letting you pick it again.",
"upgrades.metamorphosis.name": "Metamorphosis",
"upgrades.metamorphosis.tooltip": "Each coin can stain {{lvl}} brick(s) with its color",
"upgrades.metamorphosis.verbose_description": "With this perk, coins will be of the color of the brick they come from, and will color the first brick they touch in the same color. \n\nCoins spawn with the speed of the ball that broke them, which means you can aim a bit in the direction of the bricks you want to \"paint\".",
"upgrades.metamorphosis.verbose_description": "With this perk, coins will be of the color of the brick they come from, and will color the first brick they touch in the same color. Coins spawn with the speed of the ball that broke them, which means you can aim a bit in the direction of the bricks you want to \"paint\". At level 1, each coin can color 1 bricks before it is \"spent\" and appears hollow. ",
"upgrades.minefield.name": "Minefield",
"upgrades.minefield.tooltip": "+{{lvl}} combo per bomb brick on screen",
"upgrades.minefield.verbose_description": "Adds +lvl to the combo when a brick is place, -lvl when it is destroyed, and raises the base combo by the number of bricks times lvl",
@ -254,6 +260,9 @@
"upgrades.puck_repulse_ball.name": "Soft landing",
"upgrades.puck_repulse_ball.tooltip": "Paddle repulses balls",
"upgrades.puck_repulse_ball.verbose_description": "When a ball gets close to the paddle, it will start slowing down, and even potentially bouncing without touching the paddle.",
"upgrades.rainbow.name": "Rainbow",
"upgrades.rainbow.tooltip": "Coins spawn with rainbow color.",
"upgrades.rainbow.verbose_description": "Each level increases the proportion of colored coins. The color depends on level time. ",
"upgrades.reach.name": "Top down",
"upgrades.reach.tooltip": " Touching the N bricks of the lowest row resets the combo. Otherwise, +N combo",
"upgrades.reach.verbose_description": "If there is only one row of bricks, or if the lowest row of bricks cover the whole width of the game, then this perk does nothing. Otherwise, breaking this lowest row resets the combo, while breaking anything else increases the combo by the number of bricks present on that lowest row. \n\nThe lowest row will be highlighted in red. ",

View file

@ -170,6 +170,9 @@
"upgrades.bigger_puck.name": "Raquette plus grande",
"upgrades.bigger_puck.tooltip": "Attrapez facilement plus de pièces.",
"upgrades.bigger_puck.verbose_description": "Une grande raquette permet de ne jamais rater la balle et d'attraper plus de pièces, ainsi que d'orienter précisément les rebonds. Cependant, une grande raquette est plus difficile à utiliser sur les côtés du niveau.",
"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.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. ",
@ -211,6 +214,9 @@
"upgrades.hot_start.name": "Démarrage à chaud",
"upgrades.hot_start.tooltip": "Combo à {{start}}, -{{loss}} combo par seconde",
"upgrades.hot_start.verbose_description": "Au début de chaque niveau, votre combo commencera à +30 points, mais à chaque seconde, il sera diminué d'un point. ",
"upgrades.hypnosis.name": "Hypnose",
"upgrades.hypnosis.tooltip": "Chaque fois qu'une brique change de couleur, téléportez cette pièce vers la boule la plus proche et rechargez sa capacité à tacher une brique.",
"upgrades.hypnosis.verbose_description": "",
"upgrades.implosions.name": "Implosions",
"upgrades.implosions.tooltip": "Les explosions aspirent les pièces au lieu de les faire exploser.",
"upgrades.implosions.verbose_description": "La force dexplosion est appliquée dans lautre sens. Les niveaux 2+ augmentent la puissance de l'implosion. ",
@ -225,7 +231,7 @@
"upgrades.limitless.verbose_description": "Choisir cet avantage augmente également sa propre limite d'un point, vous permettant de le choisir à nouveau.",
"upgrades.metamorphosis.name": "Métamorphose",
"upgrades.metamorphosis.tooltip": "Chaque pièce peut tacher {{lvl}} brique(s) avec sa couleur",
"upgrades.metamorphosis.verbose_description": "Avec cette amélioration, les pièces seront de la couleur de la brique d'où elles proviennent et coloreront la première brique qu'elles toucheront. \n\nLes pièces apparaissent à la vitesse de la balle qui les a cassées, ce qui signifie que vous pouvez viser un peu dans la direction des briques que vous voulez \"peindre\".",
"upgrades.metamorphosis.verbose_description": "Avec cette amélioration, les pièces seront de la couleur de la brique d'où elles proviennent et coloreront la première brique qu'elles toucheront. Les pièces apparaissent à la vitesse de la balle qui les a cassées, ce qui signifie que vous pouvez viser un peu dans la direction des briques que vous voulez \"peindre\". Au à chaque niveau, chaque pièce peut colorier une brique de plus avant d'être \"épuisée\" et d'apparaître vide.",
"upgrades.minefield.name": "Terrain miné",
"upgrades.minefield.tooltip": "+{{lvl}} combo par brique explosive à l'écran",
"upgrades.minefield.verbose_description": "Ajoute +lvl au combo lorsqu'une brique est placée, -lvl lorsqu'elle est détruite et augmente le combo de base du nombre de briques multiplié par le niveau",
@ -254,6 +260,9 @@
"upgrades.puck_repulse_ball.name": "Atterrissage en douceur",
"upgrades.puck_repulse_ball.tooltip": "La raquette repousse les balles",
"upgrades.puck_repulse_ball.verbose_description": "Lorsqu'une balle s'approche de la raquette, elle commence à ralentir, voire à rebondir sans toucher le palet.",
"upgrades.rainbow.name": "Arc en ciel",
"upgrades.rainbow.tooltip": "Les pièces apparaissent avec la couleur de l'arc en ciel.",
"upgrades.rainbow.verbose_description": "Chaque niveau augment la proportion de pièces colorée. La couleur dépends du temps de jeu. ",
"upgrades.reach.name": "Attaque aérienne",
"upgrades.reach.tooltip": "Casser une des N briques de la ligne la plus basse détruit le combo. Sinon, +N combo.",
"upgrades.reach.verbose_description": "S'il n'y a qu'une seule rangée de briques, ou si la rangée la plus basse couvre toute la largeur du jeu, cet avantage est sans effet. Sinon, briser cette rangée la plus basse réinitialise le combo ; briser toute autre rangée augmente le combo du nombre de briques présentes sur cette rangée.\n\nLa rangée de briques du bas sera entourée en rouge pour vous rappeler de ne pas la toucher. ",

View file

@ -136,7 +136,9 @@ export function newGameState(params: RunParams): GameState {
autoCleanUses: 0,
...defaultSounds(),
rerolls: 0,
creative: sumOfValues(params.perks) > 1 || params.level,
creative:
sumOfValues(params.perks) > 1 ||
(params.level && params.level !== "icon:" + randomGift),
};
resetBalls(gameState);

View file

@ -123,8 +123,9 @@ export function render(gameState: GameState) {
coin.y / haloScale,
);
});
haloCanvasCtx.globalAlpha = 0.3;
gameState.balls.forEach((ball) => {
haloCanvasCtx.globalAlpha = 0.3 * (1 - ballTransparency(ball, gameState));
drawFuzzyBall(
haloCanvasCtx,
gameState.ballsColor,
@ -251,12 +252,12 @@ export function render(gameState: GameState) {
ctx.globalAlpha = 1;
forEachLiveOne(gameState.coins, (coin) => {
const color = getCoinRenderColor(gameState, coin);
const hollow = gameState.perks.metamorphosis && !coin.metamorphosisPoints;
// ctx.globalCompositeOperation = "source-over";
ctx.globalCompositeOperation = "source-over";
drawCoin(
ctx,
color,
hollow ? "transparent" : color,
coin.size,
coin.x,
coin.y,
@ -264,6 +265,7 @@ export function render(gameState: GameState) {
(hasCombo && gameState.perks.asceticism && "#FF0000") ||
// Gold coins
// (color === "#ffd300" && "#ffd300") ||
(hollow && color) ||
gameState.level.color,
coin.a,
);
@ -869,6 +871,9 @@ export function drawCoin(
canctx.lineWidth = 2;
canctx.setLineDash(redBorderDash);
}
if (color === "transparent") {
canctx.lineWidth = 2;
}
canctx.stroke();
if (color === "#ffd300") {

View file

@ -710,4 +710,34 @@ export const rawUpgrades = [
help: (lvl: number) => t("upgrades.superhot.tooltip", { lvl }),
fullHelp: t("upgrades.superhot.verbose_description"),
},
{
requires: "",
threshold: 200000,
giftable: false,
id: "bricks_attract_coins",
max: 3,
name: t("upgrades.bricks_attract_coins.name"),
help: (lvl: number) => t("upgrades.bricks_attract_coins.tooltip", { lvl }),
fullHelp: t("upgrades.bricks_attract_coins.verbose_description"),
},
{
requires: "",
threshold: 205000,
giftable: false,
id: "rainbow",
max: 7,
name: t("upgrades.rainbow.name"),
help: (lvl: number) => t("upgrades.rainbow.tooltip", { lvl }),
fullHelp: t("upgrades.rainbow.verbose_description"),
},
{
requires: "metamorphosis",
threshold: 210000,
giftable: false,
id: "hypnosis",
max: 1,
name: t("upgrades.hypnosis.name"),
help: (lvl: number) => t("upgrades.hypnosis.tooltip", { lvl }),
fullHelp: t("upgrades.hypnosis.verbose_description"),
},
] as const;