Passive income and clairvoyant adjusments

This commit is contained in:
Renan LE CARO 2025-03-24 10:19:15 +01:00
parent 41b3e6f00b
commit 5a10f7d768
10 changed files with 68 additions and 57 deletions

View file

@ -8,7 +8,7 @@ Break colourful bricks, catch bouncing coins and select powerful upgrades !
- [Post your comments on itch.io](https://renanlecaro.itch.io/breakout71) - [Post your comments on itch.io](https://renanlecaro.itch.io/breakout71)
- [Help and tips about the game](./Help.md) - [Help and tips about the game](./Help.md)
- [Credits](./Credits.md) - [Credits](./Credits.md)
- [Open source android version on F-Droid](https://f-droid.org/en/packages/me.lecaro.breakout/) - [F-Droid](https://f-droid.org/en/packages/me.lecaro.breakout/)
- [Google Play](https://play.google.com/store/apps/details?id=me.lecaro.breakout) - [Google Play](https://play.google.com/store/apps/details?id=me.lecaro.breakout)
- [GitLab](https://gitlab.com/lecarore/breakout71) - [GitLab](https://gitlab.com/lecarore/breakout71)
- [HackerNews thread](https://news.ycombinator.com/item?id=43183131) - [HackerNews thread](https://news.ycombinator.com/item?id=43183131)
@ -23,10 +23,11 @@ There's also an easy mode for kids (slower ball).
# Next # Next
# bugs
* [colin] parfois je dois appuyer plusieurs fois sur "Start a new run" pour vraiment commencer une nouvelle partie. dans ce cas, lhécran de jeu derrière se "désassombrit" comme si le jeu avait démarré plusieurs parties en même temps. - passive income : your combo is only lost when you're moving the same moment a ball hits a brick and/or a side
* [colin] le niveau bug parfois et ne peux pas démarrer. dans ce cas, la balle apparait comme démarrant sans être attachée au puck, comme si la partie avait déjà commencée. il faut redémarrer B71 pour que ça fonctionne
# bugs
- uses with popups not scrollable
# UX # UX
- instead of the free perk at level one, offer to skip lvl 1 and directly pick 4 perks, but only if you manage to clear lvl 1 with 4 upgrades. - instead of the free perk at level one, offer to skip lvl 1 and directly pick 4 perks, but only if you manage to clear lvl 1 with 4 upgrades.
@ -93,10 +94,8 @@ There's also an easy mode for kids (slower ball).
- wind (puck positions adds force to coins and balls) - wind (puck positions adds force to coins and balls)
- balls repulse coins - balls repulse coins
- n% of coins missed respawn at the top - n% of coins missed respawn at the top
- lightning : missing triggers and explosive lighting strike around ball path
- coins repulse coins (could get really laggy) - coins repulse coins (could get really laggy)
- balls repulse coins - balls repulse coins
- balls attract coins
- twice as many coins after a wall bounce, twice as little otherwise ? - twice as many coins after a wall bounce, twice as little otherwise ?
- missile goes when you catch coin - missile goes when you catch coin
- missile goes when you break a brick - missile goes when you break a brick

37
dist/index.html vendored

File diff suppressed because one or more lines are too long

View file

@ -973,6 +973,7 @@ document.addEventListener("keyup", async (e) => {
export const gameState = newGameState({}); export const gameState = newGameState({});
export function restart(params: RunParams) { export function restart(params: RunParams) {
fitSize();
Object.assign(gameState, newGameState(params)); Object.assign(gameState, newGameState(params));
pauseRecording(); pauseRecording();
setLevel(gameState, 0); setLevel(gameState, 0);
@ -998,5 +999,4 @@ restart(
} }
: {}, : {},
); );
fitSize();
tick(); tick();

View file

@ -54,12 +54,9 @@ import { use } from "react";
export function setMousePos(gameState: GameState, x: number) { export function setMousePos(gameState: GameState, x: number) {
// Sets the puck position, and updates the ball position if they are supposed to follow it // Sets the puck position, and updates the ball position if they are supposed to follow it
if ( if (
gameState.running && Math.abs(x - gameState.puckPosition) > 1
gameState.levelTime > 500 &&
gameState.perks.passive_income &&
Math.abs(x - gameState.puckPosition) > 3
) { ) {
resetCombo(gameState, x, gameState.gameZoneHeight - gameState.puckHeight); gameState.lastPuckMove=gameState.levelTime
} }
gameState.puckPosition = x; gameState.puckPosition = x;
gameState.needsRender = true; gameState.needsRender = true;
@ -73,6 +70,7 @@ function getBallDefaultVx(gameState: GameState) {
} }
export function resetBalls(gameState: GameState) { export function resetBalls(gameState: GameState) {
console.log('resetBalls',gameState)
const count = 1 + (gameState.perks?.multiball || 0); const count = 1 + (gameState.perks?.multiball || 0);
const perBall = gameState.puckWidth / (count + 1); const perBall = gameState.puckWidth / (count + 1);
gameState.balls = []; gameState.balls = [];
@ -112,7 +110,7 @@ export function putBallsAtPuck(gameState: GameState) {
// This reset could be abused to cheat quite easily // This reset could be abused to cheat quite easily
const count = gameState.balls.length; const count = gameState.balls.length;
const perBall = gameState.puckWidth / (count + 1); const perBall = gameState.puckWidth / (count + 1);
const vx = getBallDefaultVx(gameState); // const vx = getBallDefaultVx(gameState);
gameState.balls.forEach((ball, i) => { gameState.balls.forEach((ball, i) => {
const x = const x =
gameState.puckPosition - gameState.puckWidth / 2 + perBall * (i + 1); gameState.puckPosition - gameState.puckWidth / 2 + perBall * (i + 1);
@ -121,10 +119,10 @@ export function putBallsAtPuck(gameState: GameState) {
ball.previousX = x; ball.previousX = x;
ball.y = gameState.gameZoneHeight - 1.5 * gameState.ballSize; ball.y = gameState.gameZoneHeight - 1.5 * gameState.ballSize;
ball.previousY = ball.y; ball.previousY = ball.y;
ball.vx = vx; // ball.vx = vx;
ball.previousVX = ball.vx; // ball.previousVX = ball.vx;
ball.vy = -gameState.baseSpeed; // ball.vy = -gameState.baseSpeed;
ball.previousVY = ball.vy; // ball.previousVY = ball.vy;
ball.sx = 0; ball.sx = 0;
ball.sy = 0; ball.sy = 0;
ball.hitItem = []; ball.hitItem = [];
@ -430,6 +428,10 @@ export function explodeBrick(
} }
} }
if(gameState.lastPuckMove && gameState.perks.passive_income && gameState.lastPuckMove>gameState.levelTime-500*gameState.perks.passive_income){
resetCombo(gameState, x, y);
}
if (!isExplosion) { if (!isExplosion) {
// color change // color change
if ( if (

View file

@ -3705,21 +3705,6 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>help_plural</name>
<description/>
<comment/>
<translations>
<translation>
<language>en-US</language>
<approved>true</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>true</approved>
</translation>
</translations>
</concept_node>
<concept_node> <concept_node>
<name>name</name> <name>name</name>
<description/> <description/>

View file

@ -188,7 +188,7 @@
"upgrades.one_more_choice.help": "Further level ups will offer one more option in the list", "upgrades.one_more_choice.help": "Further level ups will offer one more option in the list",
"upgrades.one_more_choice.name": "+1 choice until run end", "upgrades.one_more_choice.name": "+1 choice until run end",
"upgrades.passive_income.fullHelp": "Some perks can help the balls do what you want without needing to do anything.", "upgrades.passive_income.fullHelp": "Some perks can help the balls do what you want without needing to do anything.",
"upgrades.passive_income.help": "+1 combo / brick, combo resets when puck moves", "upgrades.passive_income.help": "+{{lvl}} combo / brick, unless the puck moves in the last {{time}}s, then it resets instead",
"upgrades.passive_income.name": "Passive income", "upgrades.passive_income.name": "Passive income",
"upgrades.picky_eater.fullHelp": "Whenever you break a brick the same color as your ball, your combo increases by one. \nIf it's a different color, the ball takes that new color, but the combo resets.\nThe bricks with the right color will get a white border. \nOnce you get a combo higher than your minimum, the bricks of the wrong color will get a red halo. \nIf you have more than one ball, they all change color whenever one of them hits a brick.", "upgrades.picky_eater.fullHelp": "Whenever you break a brick the same color as your ball, your combo increases by one. \nIf it's a different color, the ball takes that new color, but the combo resets.\nThe bricks with the right color will get a white border. \nOnce you get a combo higher than your minimum, the bricks of the wrong color will get a red halo. \nIf you have more than one ball, they all change color whenever one of them hits a brick.",
"upgrades.picky_eater.help": "+1 combo per brick broken, resets on ball color change", "upgrades.picky_eater.help": "+1 combo per brick broken, resets on ball color change",
@ -227,8 +227,7 @@
"upgrades.shunt.help": "Keep {{percent}}% of your combo between levels", "upgrades.shunt.help": "Keep {{percent}}% of your combo between levels",
"upgrades.shunt.name": "Shunt", "upgrades.shunt.name": "Shunt",
"upgrades.side_kick.fullHelp": "When a brick get hit, the game checks the ball's velocity, and add +1 to the combo if its horizontal velocity is higher than its vertical velocity. The combo will decrease by one otherwise. The location of the impact on the brick is irrelevant. ", "upgrades.side_kick.fullHelp": "When a brick get hit, the game checks the ball's velocity, and add +1 to the combo if its horizontal velocity is higher than its vertical velocity. The combo will decrease by one otherwise. The location of the impact on the brick is irrelevant. ",
"upgrades.side_kick.help": "Bricks sometimes resist hits but drop more coins.", "upgrades.side_kick.help": "+{{lvl}} combo per brick broken horizontally, -{{lvl}} otherwise",
"upgrades.side_kick.help_plural": "+{{lvl}} combo per brick broken horizontally, -{{lvl}} otherwise",
"upgrades.side_kick.name": "Side kick", "upgrades.side_kick.name": "Side kick",
"upgrades.skip_last.fullHelp": "You need to break all bricks to go to the next level. However, it can be hard to get the last ones. \n\nClearing a level early brings extra choices when upgrading. Never missing the bricks is also very beneficial. \n\nSo if you find it difficult to break the last bricks, getting this perk a few time can help.", "upgrades.skip_last.fullHelp": "You need to break all bricks to go to the next level. However, it can be hard to get the last ones. \n\nClearing a level early brings extra choices when upgrading. Never missing the bricks is also very beneficial. \n\nSo if you find it difficult to break the last bricks, getting this perk a few time can help.",
"upgrades.skip_last.help": "The last brick will explode.", "upgrades.skip_last.help": "The last brick will explode.",

View file

@ -188,7 +188,7 @@
"upgrades.one_more_choice.help": "Les niveaux suivants offriront une option supplémentaire dans la liste d'améliorations.", "upgrades.one_more_choice.help": "Les niveaux suivants offriront une option supplémentaire dans la liste d'améliorations.",
"upgrades.one_more_choice.name": "+1 choix jusqu'à la fin de la course", "upgrades.one_more_choice.name": "+1 choix jusqu'à la fin de la course",
"upgrades.passive_income.fullHelp": "Certaines amélioration font bouger les balles sans avoir besoin de mettre le palet en mouvement.", "upgrades.passive_income.fullHelp": "Certaines amélioration font bouger les balles sans avoir besoin de mettre le palet en mouvement.",
"upgrades.passive_income.help": "+1 combo par brique cassée, RAZ quand le palet bouge", "upgrades.passive_income.help": "+{{lvl}} combo / brique, sauf si le palet à bougé dans les {{time}}s, RAZ dans ce cas",
"upgrades.passive_income.name": "Revenu passif", "upgrades.passive_income.name": "Revenu passif",
"upgrades.picky_eater.fullHelp": "Chaque fois que vous cassez une brique de la même couleur que votre balle, votre combo augmente d'une unité.\n\nS'il s'agit d'une couleur différente, la balle adopte cette nouvelle couleur, mais la combinaison est réinitialisée.\n\nLes briques de la mauvaise couleur sont entourées en rouge.\n\nSi vous avez plus d'une balle, elles changent toutes de couleur lorsque l'une d'entre elles touche une brique.", "upgrades.picky_eater.fullHelp": "Chaque fois que vous cassez une brique de la même couleur que votre balle, votre combo augmente d'une unité.\n\nS'il s'agit d'une couleur différente, la balle adopte cette nouvelle couleur, mais la combinaison est réinitialisée.\n\nLes briques de la mauvaise couleur sont entourées en rouge.\n\nSi vous avez plus d'une balle, elles changent toutes de couleur lorsque l'une d'entre elles touche une brique.",
"upgrades.picky_eater.help": "Plus de pièces si vous cassez les briques couleur par couleur.", "upgrades.picky_eater.help": "Plus de pièces si vous cassez les briques couleur par couleur.",
@ -227,8 +227,7 @@
"upgrades.shunt.help": "Garer {{percent}}% du combo au changement de niveau ", "upgrades.shunt.help": "Garer {{percent}}% du combo au changement de niveau ",
"upgrades.shunt.name": "Shunt", "upgrades.shunt.name": "Shunt",
"upgrades.side_kick.fullHelp": "Lorsqu'une brique est touchée, le jeu vérifie la vitesse de la balle et ajoute +1 au combo si sa vitesse horizontale est supérieure à sa vitesse verticale. Dans le cas contraire, le combo diminuera d'un point. L'emplacement de l'impact sur la brique n'a aucune importance.", "upgrades.side_kick.fullHelp": "Lorsqu'une brique est touchée, le jeu vérifie la vitesse de la balle et ajoute +1 au combo si sa vitesse horizontale est supérieure à sa vitesse verticale. Dans le cas contraire, le combo diminuera d'un point. L'emplacement de l'impact sur la brique n'a aucune importance.",
"upgrades.side_kick.help": "Les briques résistent parfois aux coups mais font tomber plus de pièces.", "upgrades.side_kick.help": "+{{lvl}} combo par brique cassé horizontalement, -{{lvl}} sinon",
"upgrades.side_kick.help_plural": "+{{lvl}} combo par brique cassé horizontalement, -{{lvl}} sinon",
"upgrades.side_kick.name": "Un coté positif", "upgrades.side_kick.name": "Un coté positif",
"upgrades.skip_last.fullHelp": "Vous devez casser toutes les briques pour passer au niveau suivant. \n\nCependant, il peut être difficile d'obtenir les dernières briques.\n\nTerminer un niveau plus tôt permet d'obtenir des choix supplémentaires lors de la mise à niveau. \n\nNe jamais manquer de briques est également très avantageux.\n\nDonc, si vous avez du mal à casser les dernières briques, obtenir cet avantage plusieurs fois peut vous aider.", "upgrades.skip_last.fullHelp": "Vous devez casser toutes les briques pour passer au niveau suivant. \n\nCependant, il peut être difficile d'obtenir les dernières briques.\n\nTerminer un niveau plus tôt permet d'obtenir des choix supplémentaires lors de la mise à niveau. \n\nNe jamais manquer de briques est également très avantageux.\n\nDonc, si vous avez du mal à casser les dernières briques, obtenir cet avantage plusieurs fois peut vous aider.",
"upgrades.skip_last.help": "La dernière brique s'autodétruit.", "upgrades.skip_last.help": "La dernière brique s'autodétruit.",

View file

@ -227,11 +227,15 @@ export function render(gameState: GameState) {
ctx.globalAlpha = 1; ctx.globalAlpha = 1;
ctx.globalCompositeOperation = "source-over"; ctx.globalCompositeOperation = "source-over";
gameState.balls.forEach((ball) => { gameState.balls.forEach((ball) => {
const drawingColor = (gameState.lastPuckMove&& gameState.perks.passive_income && gameState.combo>baseCombo(gameState) && gameState.lastPuckMove>gameState.levelTime-500*gameState.perks.passive_income && 'red')||gameState.ballsColor
// The white border around is to distinguish colored balls from coins/bg // The white border around is to distinguish colored balls from coins/bg
drawBall( drawBall(
ctx, ctx,
gameState.ballsColor, drawingColor,
gameState.ballSize, gameState.ballSize,
ball.x, ball.x,
ball.y, ball.y,
@ -252,6 +256,17 @@ export function render(gameState: GameState) {
); );
ctx.stroke(); ctx.stroke();
} }
if(gameState.perks.clairvoyant && gameState.ballStickToPuck){
ctx.strokeStyle = gameState.ballsColor;
ctx.beginPath();
ctx.moveTo(ball.x,ball.y);
ctx.lineTo(
ball.x+ball.vx*10,
ball.y+ball.vy*10
);
ctx.stroke();
}
}); });
// The puck // The puck
ctx.globalAlpha = 1; ctx.globalAlpha = 1;

3
src/types.d.ts vendored
View file

@ -132,8 +132,6 @@ interface LightFlash extends BaseFlash {
// type: "ball"; // type: "ball";
} }
export type Flash = ParticleFlash | TextFlash | LightFlash;
export type RunStats = { export type RunStats = {
started: number; started: number;
levelsPlayed: number; levelsPlayed: number;
@ -253,6 +251,7 @@ export type GameState = {
runStatistics: RunStats; runStatistics: RunStats;
lastOffered: Partial<{ [k in PerkId]: number }>; lastOffered: Partial<{ [k in PerkId]: number }>;
levelTime: number; levelTime: number;
lastPuckMove: number;
winAt: number; winAt: number;
levelWallBounces: number; levelWallBounces: number;
autoCleanUses: number; autoCleanUses: number;

View file

@ -570,9 +570,9 @@ export const rawUpgrades = [
threshold: 140000, threshold: 140000,
giftable: false, giftable: false,
id: "passive_income", id: "passive_income",
max: 1, max: 4,
name: t("upgrades.passive_income.name"), name: t("upgrades.passive_income.name"),
help: (lvl: number) => t("upgrades.passive_income.help"), help: (lvl: number) => t("upgrades.passive_income.help",{time:lvl/2,lvl}),
fullHelp: t("upgrades.passive_income.fullHelp"), fullHelp: t("upgrades.passive_income.fullHelp"),
}, },
{ {