mirror of
https://gitlab.com/lecarore/breakout71.git
synced 2025-04-20 12:15:06 -04:00
A few perks
This commit is contained in:
parent
6e6b0991ac
commit
5abbf5263e
15 changed files with 1746 additions and 1087 deletions
10
Readme.md
10
Readme.md
|
@ -23,10 +23,8 @@ There's also an easy mode for kids (slower ball).
|
|||
|
||||
|
||||
# Next
|
||||
- different visual effects on ball to represent which perks it's imbued with (pierce, sapper…). remove visual while it's not affected (can't pierce/sap anymore until touching the puck).
|
||||
- check which ball color you get right after picking a color related perk
|
||||
|
||||
- sturdy bricks: map of remaining hits
|
||||
- looks like offline PWA mode does not work
|
||||
|
||||
# bugs
|
||||
|
||||
|
@ -77,6 +75,7 @@ There's also an easy mode for kids (slower ball).
|
|||
- the white outline on bricks associated with picky eater kinda works but i feel it's more distracting than anything. maybe try something different ? put a cross on matching coloured bricks, or the contrary, grey out other bricks.
|
||||
|
||||
# New perks ideas
|
||||
- [colin] shocks - balls can bounce off of each others and produce a shock that destroys a random block at the current combo
|
||||
- second puck (symmetric to the first one)
|
||||
- keep combo between level, loose half your run score when missing any bricks
|
||||
- offer next level choice after upgrade pick
|
||||
|
@ -123,8 +122,6 @@ There's also an easy mode for kids (slower ball).
|
|||
- gravity is flipped on the opposite side to the puck (for coins)
|
||||
- balls have gravity
|
||||
- coins don't have gravity
|
||||
- [colin] yoyo - when the ball falls back down, it curbs towards your puck (after hitting a brick or top)
|
||||
- [colin] single block combo - get +1 combo if the ball only breaks a single block before reaching the puck
|
||||
- [colin] mirror puck - a mirrored puck at the top of the screen follows as you move the bottom puck. it helps with keeping combos up and preventing the ball from touching the ceiling. it could appear as a hollow puck so as to not draw too much attention from the main bottom puck.
|
||||
- [colin] side pucks - same as above but with two side pucks.
|
||||
- [colin] ball coins - coins share the same physics as coins and bounce on walls and bricks
|
||||
|
@ -154,13 +151,10 @@ There's also an easy mode for kids (slower ball).
|
|||
- squirell : keep coins on screen to have a higher combo
|
||||
- [colin] peaceful combo - le combo monte chaque seconde tant que les 2+ balles ne se touchent pas OU qu'on ne touche pas de bloc explosif.
|
||||
- [colin] close quarters - balle attirée par tous les blocs/par un bloc aléatoire, actif à portée de bloc (+1bloc au lvlup)/proportionnel à une force (+puissance au lvlup)…
|
||||
- [colin] shocks - balls can bounce off of each others and produce a shock that destroys a random block at the current combo
|
||||
- [colin] plusieurs perks qui déclenchent des effets quand une balle est perdue. par ex: +3 combo à chaque balle perdue, 5 blocs transformés en bombe, balle et coins ralentis, blocs régénérés…
|
||||
- [colin] faster style - augmente le combo en fonction de la vitesse de la balle
|
||||
- [colin] perk: analyzer - permet de voir les caractéristiques cachées des blocs (sturdy…)
|
||||
- [colin] perk: roulette - gagne instantanément 2 perks aléatoires
|
||||
- let coins go out of bounds left and right, where they'll get lost, but +1 combo per brick
|
||||
- more combo if no coin catch
|
||||
- combo climbs every time a ball bounces on puck (but bounce is random?)
|
||||
|
||||
# extra levels
|
||||
|
|
576
dist/index.html
vendored
576
dist/index.html
vendored
File diff suppressed because one or more lines are too long
|
@ -838,8 +838,57 @@
|
|||
{
|
||||
"name": "icon:concave_puck",
|
||||
"size": 8,
|
||||
"bricks": "___________W_______________W_______________W_____________WWWWW__",
|
||||
"bricks": "___________W_______________W____________W__W__W_WW___WW_WWWWWWW_",
|
||||
"svg": null,
|
||||
"color": ""
|
||||
},
|
||||
{
|
||||
"name": "icon:antigrav",
|
||||
"size": 8,
|
||||
"bricks": "v______vvv____vvv______vv______vv______vv______vv______v__WWWW__",
|
||||
"svg": null,
|
||||
"color": ""
|
||||
},
|
||||
{
|
||||
"name": "icon:asceticism",
|
||||
"size": 8,
|
||||
"bricks": "_________y____y____W____y______y_________y____y_________y_WWWW_y",
|
||||
"svg": null,
|
||||
"color": ""
|
||||
},
|
||||
{
|
||||
"name": "icon:unbounded",
|
||||
"size": 9,
|
||||
"bricks": "y_WWWWW_y__________yttttt____ttttt__y____W__y______________y_y____________WWW___y",
|
||||
"svg": null,
|
||||
"color": ""
|
||||
},
|
||||
{
|
||||
"name": "icon:shunt",
|
||||
"size": 8,
|
||||
"bricks": "_______y______yy______yy__yCCyyy__y__yyy_yy__yyy_yy__yyyyyy__yyy",
|
||||
"svg": null,
|
||||
"color": ""
|
||||
},
|
||||
{
|
||||
"name": "icon:yoyo",
|
||||
"size": 8,
|
||||
"bricks": "_rrrrrr_rrrrrrrrrrrrrrrr_WWWWWW_rWrrrrrrrrWrrrrr_rrWrrr_____W___",
|
||||
"svg": null,
|
||||
"color": ""
|
||||
},
|
||||
{
|
||||
"name": "icon:nbricks",
|
||||
"size": 6,
|
||||
"bricks": "yy__yyyyy_yyyyyyyyyyyyyyyy_yyyyy__yy",
|
||||
"svg": null,
|
||||
"color": ""
|
||||
},
|
||||
{
|
||||
"name": "icon:etherealcoins",
|
||||
"size": 11,
|
||||
"bricks": "___WWWWW_____W_y___W____W_ttt_W___W__ttt_yW__W_______W__W_______W_W_y_y_____WW_____W_y_WW_WWW_____WWWWWWWWWWWW___________",
|
||||
"svg": null,
|
||||
"color": ""
|
||||
}
|
||||
]
|
||||
]
|
194
src/game.ts
194
src/game.ts
|
@ -1,4 +1,4 @@
|
|||
import { allLevels, appVersion, icons, upgrades } from "./loadGameData";
|
||||
import {allLevels, appVersion, icons, upgrades} from "./loadGameData";
|
||||
import {
|
||||
Ball,
|
||||
Coin,
|
||||
|
@ -11,52 +11,26 @@ import {
|
|||
TextFlash,
|
||||
Upgrade,
|
||||
} from "./types";
|
||||
import { getAudioContext, playPendingSounds } from "./sounds";
|
||||
import {
|
||||
currentLevelInfo,
|
||||
getRowColIndex,
|
||||
max_levels,
|
||||
pickedUpgradesHTMl,
|
||||
} from "./game_utils";
|
||||
import {getAudioContext, playPendingSounds} from "./sounds";
|
||||
import {currentLevelInfo, getRowColIndex, max_levels, pickedUpgradesHTMl,} from "./game_utils";
|
||||
|
||||
import "./PWA/sw_loader";
|
||||
import { getCurrentLang, t } from "./i18n/i18n";
|
||||
import { getSettingValue, getTotalScore, setSettingValue } from "./settings";
|
||||
import {getCurrentLang, t} from "./i18n/i18n";
|
||||
import {getSettingValue, getTotalScore, setSettingValue} from "./settings";
|
||||
import {
|
||||
empty,
|
||||
forEachLiveOne,
|
||||
gameStateTick,
|
||||
normalizeGameState,
|
||||
pickRandomUpgrades,
|
||||
putBallsAtPuck,
|
||||
resetBalls,
|
||||
resetCombo,
|
||||
setLevel,
|
||||
setMousePos,
|
||||
} from "./gameStateMutators";
|
||||
import {
|
||||
backgroundCanvas,
|
||||
bombSVG,
|
||||
ctx,
|
||||
gameCanvas,
|
||||
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 { hashCode } from "./getLevelBackground";
|
||||
import {backgroundCanvas, ctx, gameCanvas, 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 {hashCode} from "./getLevelBackground";
|
||||
|
||||
export function play() {
|
||||
if (gameState.running) return;
|
||||
|
@ -337,125 +311,6 @@ export function hitsSomething(x: number, y: number, radius: number) {
|
|||
);
|
||||
}
|
||||
|
||||
export function shouldPierceByColor(
|
||||
vhit: number | undefined,
|
||||
hhit: number | undefined,
|
||||
chit: number | undefined,
|
||||
) {
|
||||
if (!gameState.perks.pierce_color) return false;
|
||||
if (
|
||||
typeof vhit !== "undefined" &&
|
||||
gameState.bricks[vhit] !== gameState.ballsColor
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
typeof hhit !== "undefined" &&
|
||||
gameState.bricks[hhit] !== gameState.ballsColor
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
typeof chit !== "undefined" &&
|
||||
gameState.bricks[chit] !== gameState.ballsColor
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function coinBrickHitCheck(coin: Coin) {
|
||||
// Make ball/coin bonce, and return bricks that were hit
|
||||
const radius = coin.size / 2;
|
||||
const { x, y, previousX, previousY } = coin;
|
||||
|
||||
const vhit = hitsSomething(previousX, y, radius);
|
||||
const hhit = hitsSomething(x, previousY, radius);
|
||||
const chit =
|
||||
(typeof vhit == "undefined" &&
|
||||
typeof hhit == "undefined" &&
|
||||
hitsSomething(x, y, radius)) ||
|
||||
undefined;
|
||||
|
||||
if (typeof vhit !== "undefined" || typeof chit !== "undefined") {
|
||||
coin.y = coin.previousY;
|
||||
coin.vy *= -1;
|
||||
|
||||
// Roll on corners
|
||||
const leftHit = gameState.bricks[brickIndex(x - radius, y + radius)];
|
||||
const rightHit = gameState.bricks[brickIndex(x + radius, y + radius)];
|
||||
|
||||
if (leftHit && !rightHit) {
|
||||
coin.vx += 1;
|
||||
coin.sa -= 1;
|
||||
}
|
||||
if (!leftHit && rightHit) {
|
||||
coin.vx -= 1;
|
||||
coin.sa += 1;
|
||||
}
|
||||
}
|
||||
if (typeof hhit !== "undefined" || typeof chit !== "undefined") {
|
||||
coin.x = coin.previousX;
|
||||
coin.vx *= -1;
|
||||
}
|
||||
return vhit ?? hhit ?? chit;
|
||||
}
|
||||
|
||||
export function bordersHitCheck(
|
||||
coin: Coin | Ball,
|
||||
radius: number,
|
||||
delta: number,
|
||||
) {
|
||||
if (coin.destroyed) return;
|
||||
coin.previousX = coin.x;
|
||||
coin.previousY = coin.y;
|
||||
coin.x += coin.vx * delta;
|
||||
coin.y += coin.vy * delta;
|
||||
coin.sx ||= 0;
|
||||
coin.sy ||= 0;
|
||||
coin.sx += coin.previousX - coin.x;
|
||||
coin.sy += coin.previousY - coin.y;
|
||||
coin.sx *= 0.9;
|
||||
coin.sy *= 0.9;
|
||||
|
||||
if (gameState.perks.wind) {
|
||||
coin.vx +=
|
||||
((gameState.puckPosition -
|
||||
(gameState.offsetX + gameState.gameZoneWidth / 2)) /
|
||||
gameState.gameZoneWidth) *
|
||||
gameState.perks.wind *
|
||||
0.5;
|
||||
}
|
||||
|
||||
let vhit = 0,
|
||||
hhit = 0;
|
||||
|
||||
if (coin.x < gameState.offsetXRoundedDown + radius) {
|
||||
coin.x =
|
||||
gameState.offsetXRoundedDown +
|
||||
radius +
|
||||
(gameState.offsetXRoundedDown + radius - coin.x);
|
||||
coin.vx *= -1;
|
||||
hhit = 1;
|
||||
}
|
||||
if (coin.y < radius) {
|
||||
coin.y = radius + (radius - coin.y);
|
||||
coin.vy *= -1;
|
||||
vhit = 1;
|
||||
}
|
||||
if (coin.x > gameState.canvasWidth - gameState.offsetXRoundedDown - radius) {
|
||||
coin.x =
|
||||
gameState.canvasWidth -
|
||||
gameState.offsetXRoundedDown -
|
||||
radius -
|
||||
(coin.x -
|
||||
(gameState.canvasWidth - gameState.offsetXRoundedDown - radius));
|
||||
coin.vx *= -1;
|
||||
hhit = 1;
|
||||
}
|
||||
|
||||
return hhit + vhit * 2;
|
||||
}
|
||||
export function tick() {
|
||||
const currentTick = performance.now();
|
||||
const timeDeltaMs = currentTick - gameState.lastTick;
|
||||
|
@ -552,18 +407,6 @@ async function openSettingsPanel() {
|
|||
pause(true);
|
||||
|
||||
const actions: AsyncAlertAction<() => void>[] = [
|
||||
{
|
||||
text: t("main_menu.resume"),
|
||||
help: t("main_menu.resume_help"),
|
||||
value() {},
|
||||
},
|
||||
{
|
||||
text: t("main_menu.unlocks"),
|
||||
help: t("main_menu.unlocks_help"),
|
||||
value() {
|
||||
openUnlocksList();
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
for (const key of Object.keys(options) as OptionId[]) {
|
||||
|
@ -606,6 +449,21 @@ async function openSettingsPanel() {
|
|||
});
|
||||
}
|
||||
}
|
||||
actions.push(
|
||||
{
|
||||
text: t("main_menu.resume"),
|
||||
help: t("main_menu.resume_help"),
|
||||
value() {},
|
||||
})
|
||||
actions.push({
|
||||
text: t("main_menu.unlocks"),
|
||||
help: t("main_menu.unlocks_help"),
|
||||
value() {
|
||||
openUnlocksList();
|
||||
},
|
||||
})
|
||||
|
||||
|
||||
|
||||
actions.push({
|
||||
text: t("sandbox.title"),
|
||||
|
|
|
@ -19,29 +19,27 @@ import {
|
|||
getMajorityValue,
|
||||
getPossibleUpgrades,
|
||||
getRowColIndex,
|
||||
isTelekinesisActive,
|
||||
max_levels,
|
||||
isTelekinesisActive, isYoyoActive,
|
||||
max_levels, shouldPierceByColor,
|
||||
} from "./game_utils";
|
||||
import { t } from "./i18n/i18n";
|
||||
import { icons } from "./loadGameData";
|
||||
import {t} from "./i18n/i18n";
|
||||
import {icons} from "./loadGameData";
|
||||
|
||||
import { addToTotalScore } from "./settings";
|
||||
import { background } from "./render";
|
||||
import { gameOver } from "./gameOver";
|
||||
import {addToTotalScore} from "./settings";
|
||||
import {background} from "./render";
|
||||
import {gameOver} from "./gameOver";
|
||||
import {
|
||||
bordersHitCheck,
|
||||
brickIndex,
|
||||
coinBrickHitCheck,
|
||||
fitSize,
|
||||
gameState,
|
||||
hasBrick,
|
||||
hitsSomething,
|
||||
openUpgradesPicker,
|
||||
pause,
|
||||
shouldPierceByColor,
|
||||
|
||||
} from "./game";
|
||||
import { stopRecording } from "./recording";
|
||||
import { isOptionOn } from "./options";
|
||||
import {stopRecording} from "./recording";
|
||||
import {isOptionOn} from "./options";
|
||||
|
||||
export function setMousePos(gameState: GameState, x: number) {
|
||||
// Sets the puck position, and updates the ball position if they are supposed to follow it
|
||||
|
@ -284,11 +282,9 @@ export function explodeBrick(
|
|||
y,
|
||||
"white",
|
||||
);
|
||||
ball.hitSinceBounce++;
|
||||
gameState.runStatistics.bricks_broken++;
|
||||
} else if (color) {
|
||||
// Even if it bounces we don't want to count that as a miss
|
||||
ball.hitSinceBounce++;
|
||||
|
||||
// Flashing is take care of by the tick loop
|
||||
const x = brickCenterX(gameState, index),
|
||||
|
@ -341,15 +337,16 @@ export function explodeBrick(
|
|||
);
|
||||
}
|
||||
|
||||
gameState.combo += Math.max(
|
||||
0,
|
||||
gameState.perks.streak_shots +
|
||||
gameState.combo += gameState.perks.streak_shots +
|
||||
gameState.perks.compound_interest +
|
||||
gameState.perks.left_is_lava +
|
||||
gameState.perks.right_is_lava +
|
||||
gameState.perks.top_is_lava +
|
||||
gameState.perks.picky_eater,
|
||||
);
|
||||
gameState.perks.picky_eater+
|
||||
gameState.perks.asceticism +
|
||||
gameState.perks.unbounded
|
||||
|
||||
;
|
||||
|
||||
if (!isExplosion) {
|
||||
// color change
|
||||
|
@ -462,6 +459,9 @@ export function addToScore(gameState: GameState, coin: Coin) {
|
|||
schedulGameSound(gameState, "coinCatch", coin.x, 1);
|
||||
}
|
||||
gameState.runStatistics.score += coin.points;
|
||||
if(gameState.perks.asceticism){
|
||||
resetCombo(gameState, coin.x, coin.y)
|
||||
}
|
||||
}
|
||||
|
||||
export async function setLevel(gameState: GameState, l: number) {
|
||||
|
@ -472,6 +472,7 @@ export async function setLevel(gameState: GameState, l: number) {
|
|||
}
|
||||
gameState.currentLevel = l;
|
||||
gameState.levelTime = 0;
|
||||
gameState.noBricksSince = 0;
|
||||
gameState.levelWallBounces = 0;
|
||||
gameState.autoCleanUses = 0;
|
||||
gameState.lastTickDown = gameState.levelTime;
|
||||
|
@ -481,7 +482,10 @@ export async function setLevel(gameState: GameState, l: number) {
|
|||
gameState.runStatistics.levelsPlayed++;
|
||||
|
||||
// Reset combo silently
|
||||
gameState.combo = baseCombo(gameState) + gameState.perks.hot_start * 15;
|
||||
if(!gameState.perks.shunt) {
|
||||
gameState.combo = baseCombo(gameState)
|
||||
}
|
||||
gameState.combo += gameState.perks.hot_start * 15;
|
||||
|
||||
resetBalls(gameState);
|
||||
|
||||
|
@ -610,6 +614,100 @@ 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 vhit = hitsSomething(previousX, y, radius);
|
||||
const hhit = hitsSomething(x, previousY, radius);
|
||||
const chit =
|
||||
(typeof vhit == "undefined" &&
|
||||
typeof hhit == "undefined" &&
|
||||
hitsSomething(x, y, radius)) ||
|
||||
undefined;
|
||||
|
||||
if (typeof vhit !== "undefined" || typeof chit !== "undefined") {
|
||||
coin.y = coin.previousY;
|
||||
coin.vy *= -1;
|
||||
|
||||
// Roll on corners
|
||||
const leftHit = gameState.bricks[brickIndex(x - radius, y + radius)];
|
||||
const rightHit = gameState.bricks[brickIndex(x + radius, y + radius)];
|
||||
|
||||
if (leftHit && !rightHit) {
|
||||
coin.vx += 1;
|
||||
coin.sa -= 1;
|
||||
}
|
||||
if (!leftHit && rightHit) {
|
||||
coin.vx -= 1;
|
||||
coin.sa += 1;
|
||||
}
|
||||
}
|
||||
if (typeof hhit !== "undefined" || typeof chit !== "undefined") {
|
||||
coin.x = coin.previousX;
|
||||
coin.vx *= -1;
|
||||
}
|
||||
return vhit ?? hhit ?? chit;
|
||||
}
|
||||
|
||||
export function bordersHitCheck(
|
||||
gameState: GameState,
|
||||
coin: Coin | Ball,
|
||||
radius: number,
|
||||
delta: number,
|
||||
) {
|
||||
if (coin.destroyed) return;
|
||||
coin.previousX = coin.x;
|
||||
coin.previousY = coin.y;
|
||||
coin.x += coin.vx * delta;
|
||||
coin.y += coin.vy * delta;
|
||||
coin.sx ||= 0;
|
||||
coin.sy ||= 0;
|
||||
coin.sx += coin.previousX - coin.x;
|
||||
coin.sy += coin.previousY - coin.y;
|
||||
coin.sx *= 0.9;
|
||||
coin.sy *= 0.9;
|
||||
|
||||
if (gameState.perks.wind) {
|
||||
coin.vx +=
|
||||
((gameState.puckPosition -
|
||||
(gameState.offsetX + gameState.gameZoneWidth / 2)) /
|
||||
gameState.gameZoneWidth) *
|
||||
gameState.perks.wind *
|
||||
0.5;
|
||||
}
|
||||
|
||||
let vhit = 0,
|
||||
hhit = 0;
|
||||
|
||||
if (coin.x < gameState.offsetXRoundedDown + radius && !gameState.perks.unbounded) {
|
||||
coin.x =
|
||||
gameState.offsetXRoundedDown +
|
||||
radius +
|
||||
(gameState.offsetXRoundedDown + radius - coin.x);
|
||||
coin.vx *= -1;
|
||||
hhit = 1;
|
||||
}
|
||||
if (coin.y < radius) {
|
||||
coin.y = radius + (radius - coin.y);
|
||||
coin.vy *= -1;
|
||||
vhit = 1;
|
||||
}
|
||||
if (coin.x > gameState.canvasWidth - gameState.offsetXRoundedDown - radius && !gameState.perks.unbounded) {
|
||||
coin.x =
|
||||
gameState.canvasWidth -
|
||||
gameState.offsetXRoundedDown -
|
||||
radius -
|
||||
(coin.x -
|
||||
(gameState.canvasWidth - gameState.offsetXRoundedDown - radius));
|
||||
coin.vx *= -1;
|
||||
hhit = 1;
|
||||
}
|
||||
|
||||
return hhit + vhit * 2;
|
||||
}
|
||||
|
||||
export function gameStateTick(
|
||||
gameState: GameState,
|
||||
// How many frames to compute at once, can go above 1 to compensate lag
|
||||
|
@ -650,7 +748,10 @@ export function gameStateTick(
|
|||
});
|
||||
gameState.autoCleanUses++;
|
||||
}
|
||||
if (!remainingBricks && !liveCount(gameState.coins)) {
|
||||
if(!remainingBricks &&gameState.noBricksSince==0 ){
|
||||
gameState.noBricksSince ||= gameState.levelTime
|
||||
}
|
||||
if (!remainingBricks && ( !liveCount(gameState.coins) || (gameState.levelTime > gameState.noBricksSince+5000))) {
|
||||
if (gameState.currentLevel + 1 < max_levels(gameState)) {
|
||||
setLevel(gameState, gameState.currentLevel + 1);
|
||||
} else {
|
||||
|
@ -664,17 +765,20 @@ export function gameStateTick(
|
|||
|
||||
forEachLiveOne(gameState.coins, (coin, coinIndex) => {
|
||||
if (gameState.perks.coin_magnet) {
|
||||
const attractionX =
|
||||
((frames * (gameState.puckPosition - coin.x)) /
|
||||
(100 +
|
||||
const strength = 100/ (100 +
|
||||
Math.pow(coin.y - gameState.gameZoneHeight, 2) +
|
||||
Math.pow(coin.x - gameState.puckPosition, 2))) *
|
||||
gameState.perks.coin_magnet *
|
||||
100;
|
||||
Math.pow(coin.x - gameState.puckPosition, 2)) *
|
||||
gameState.perks.coin_magnet ;
|
||||
|
||||
const attractionX = frames * (gameState.puckPosition - coin.x) *strength
|
||||
|
||||
coin.vx += attractionX;
|
||||
coin.vy += frames * (gameState.gameZoneHeight - coin.y) * strength / 2
|
||||
coin.sa -= attractionX / 10;
|
||||
|
||||
}
|
||||
|
||||
|
||||
const ratio = 1 - (gameState.perks.viscosity * 0.03 + 0.005) * frames;
|
||||
|
||||
coin.vy *= ratio;
|
||||
|
@ -688,10 +792,26 @@ export function gameStateTick(
|
|||
coin.a += coin.sa;
|
||||
|
||||
// Gravity
|
||||
coin.vy += frames * coin.weight * 0.8;
|
||||
if(!gameState.perks.etherealcoins){
|
||||
const flip = gameState.perks.antigrav >0 && Math.abs(coin.x - gameState.puckPosition)*2>gameState.puckWidth+coin.size
|
||||
coin.vy += frames * coin.weight * 0.8 * (flip?-1:1);
|
||||
if(flip && !isOptionOn('basic') && Math.random()<0.1){
|
||||
makeParticle(gameState,
|
||||
coin.x,
|
||||
coin.y,
|
||||
0,
|
||||
gameState.baseSpeed,
|
||||
rainbowColor(),
|
||||
true,
|
||||
5,
|
||||
250
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const speed = Math.abs(coin.sx) + Math.abs(coin.sx);
|
||||
const hitBorder = bordersHitCheck(coin, coin.size / 2, frames);
|
||||
const hitBorder = bordersHitCheck(gameState, coin, coin.size / 2, frames);
|
||||
|
||||
if (
|
||||
coin.y > gameState.gameZoneHeight - coinRadius - gameState.puckHeight &&
|
||||
|
@ -708,9 +828,12 @@ export function gameStateTick(
|
|||
if (gameState.perks.compound_interest) {
|
||||
resetCombo(gameState, coin.x, coin.y);
|
||||
}
|
||||
}else if( gameState.perks.unbounded && (coin.x<-50 || coin.x>gameState.canvasWidth+50)){
|
||||
// Out of bound on sides
|
||||
destroy(gameState.coins, coinIndex);
|
||||
}
|
||||
|
||||
const hitBrick = coinBrickHitCheck(coin);
|
||||
const hitBrick = coinBrickHitCheck(gameState, coin);
|
||||
|
||||
if (gameState.perks.metamorphosis && typeof hitBrick !== "undefined") {
|
||||
if (
|
||||
|
@ -895,6 +1018,7 @@ export function ballTick(gameState: GameState, ball: Ball, delta: number) {
|
|||
gameState.perks.ball_repulse_ball +
|
||||
gameState.perks.puck_repulse_ball +
|
||||
gameState.perks.ball_attract_ball;
|
||||
|
||||
if (isTelekinesisActive(gameState, ball)) {
|
||||
speedLimitDampener += 3;
|
||||
ball.vx +=
|
||||
|
@ -902,6 +1026,13 @@ export function ballTick(gameState: GameState, ball: Ball, delta: number) {
|
|||
delta *
|
||||
gameState.perks.telekinesis;
|
||||
}
|
||||
if (isYoyoActive(gameState, ball)) {
|
||||
speedLimitDampener += 3;
|
||||
ball.vx +=
|
||||
((gameState.puckPosition - ball.x) / 1000) *
|
||||
delta *
|
||||
gameState.perks.yoyo;
|
||||
}
|
||||
|
||||
if (
|
||||
ball.vx * ball.vx + ball.vy * ball.vy <
|
||||
|
@ -980,7 +1111,7 @@ export function ballTick(gameState: GameState, ball: Ball, delta: number) {
|
|||
}
|
||||
}
|
||||
|
||||
const borderHitCode = bordersHitCheck(ball, gameState.ballSize / 2, delta);
|
||||
const borderHitCode = bordersHitCheck(gameState, ball, gameState.ballSize / 2, delta);
|
||||
if (borderHitCode) {
|
||||
if (
|
||||
gameState.perks.left_is_lava &&
|
||||
|
@ -1053,6 +1184,16 @@ export function ballTick(gameState: GameState, ball: Ball, delta: number) {
|
|||
if (gameState.perks.streak_shots) {
|
||||
resetCombo(gameState, ball.x, ball.y);
|
||||
}
|
||||
if(gameState.perks.nbricks ){
|
||||
if(ball.hitSinceBounce){
|
||||
if(gameState.perks.nbricks===ball.hitSinceBounce){
|
||||
gameState.combo+=gameState.perks.nbricks
|
||||
}else{
|
||||
resetCombo(gameState, ball.x,ball.y)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (gameState.perks.respawn) {
|
||||
ball.hitItem
|
||||
|
@ -1084,9 +1225,10 @@ export function ballTick(gameState: GameState, ball: Ball, delta: number) {
|
|||
ball.piercedSinceBounce = 0;
|
||||
}
|
||||
|
||||
if (
|
||||
ball.y > gameState.gameZoneHeight + gameState.ballSize / 2 &&
|
||||
gameState.running
|
||||
const lostOnSides = gameState.perks.unbounded && ball.x < 50 || ball.x>gameState.canvasWidth+50
|
||||
if (gameState.running &&
|
||||
(ball.y > gameState.gameZoneHeight + gameState.ballSize / 2 || lostOnSides)
|
||||
|
||||
) {
|
||||
ball.destroyed = true;
|
||||
gameState.runStatistics.balls_lost++;
|
||||
|
@ -1119,7 +1261,7 @@ export function ballTick(gameState: GameState, ball: Ball, delta: number) {
|
|||
let pierce = false;
|
||||
if (sturdyBounce || typeof hitBrick === "undefined") {
|
||||
// cannot pierce
|
||||
} else if (shouldPierceByColor(vhit, hhit, chit)) {
|
||||
} else if (shouldPierceByColor(gameState, vhit, hhit, chit)) {
|
||||
pierce = true;
|
||||
} else if (ball.piercedSinceBounce < gameState.perks.pierce * 3) {
|
||||
pierce = true;
|
||||
|
@ -1146,6 +1288,7 @@ export function ballTick(gameState: GameState, ball: Ball, delta: number) {
|
|||
if (typeof hitBrick !== "undefined") {
|
||||
const initialBrickColor = gameState.bricks[hitBrick];
|
||||
|
||||
ball.hitSinceBounce++;
|
||||
explodeBrick(gameState, hitBrick, ball, false);
|
||||
|
||||
if (
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Ball, GameState, PerkId, PerksMap } from "./types";
|
||||
import { icons, upgrades } from "./loadGameData";
|
||||
import {Ball, GameState, PerkId, PerksMap} from "./types";
|
||||
import {icons, upgrades} from "./loadGameData";
|
||||
|
||||
export function getMajorityValue(arr: string[]): string {
|
||||
const count: { [k: string]: number } = {};
|
||||
|
@ -72,7 +72,10 @@ export function currentLevelInfo(gameState: GameState) {
|
|||
}
|
||||
|
||||
export function isTelekinesisActive(gameState: GameState, ball: Ball) {
|
||||
return gameState.perks.telekinesis && !ball.hitSinceBounce && ball.vy < 0;
|
||||
return gameState.perks.telekinesis && ball.vy < 0;
|
||||
}
|
||||
export function isYoyoActive(gameState: GameState, ball: Ball) {
|
||||
return gameState.perks.yoyo && ball.vy > 0;
|
||||
}
|
||||
|
||||
export function findLast<T>(
|
||||
|
@ -114,3 +117,31 @@ export function defaultSounds() {
|
|||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function shouldPierceByColor(
|
||||
gameState: GameState,
|
||||
vhit: number | undefined,
|
||||
hhit: number | undefined,
|
||||
chit: number | undefined,
|
||||
) {
|
||||
if (!gameState.perks.pierce_color) return false;
|
||||
if (
|
||||
typeof vhit !== "undefined" &&
|
||||
gameState.bricks[vhit] !== gameState.ballsColor
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
typeof hhit !== "undefined" &&
|
||||
gameState.bricks[hhit] !== gameState.ballsColor
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
if (
|
||||
typeof chit !== "undefined" &&
|
||||
gameState.bricks[chit] !== gameState.ballsColor
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
|
@ -1517,6 +1517,106 @@
|
|||
<folder_node>
|
||||
<name>upgrades</name>
|
||||
<children>
|
||||
<folder_node>
|
||||
<name>antigrav</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>fullHelp</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>help</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>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>
|
||||
</children>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
<name>asceticism</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>fullHelp</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>help</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>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>
|
||||
</children>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
<name>ball_attract_ball</name>
|
||||
<children>
|
||||
|
@ -1962,6 +2062,56 @@
|
|||
</concept_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
<name>etherealcoins</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>fullHelp</name>
|
||||
<description/>
|
||||
<comment/>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-FR</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>help</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>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>
|
||||
</children>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
<name>extra_levels</name>
|
||||
<children>
|
||||
|
@ -2327,6 +2477,56 @@
|
|||
</concept_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
<name>nbricks</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>fullHelp</name>
|
||||
<description/>
|
||||
<comment/>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-FR</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>help</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>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>
|
||||
</children>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
<name>one_more_choice</name>
|
||||
<children>
|
||||
|
@ -2772,6 +2972,56 @@
|
|||
</concept_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
<name>shunt</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>fullHelp</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>help</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>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>
|
||||
</children>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
<name>skip_last</name>
|
||||
<children>
|
||||
|
@ -3232,6 +3482,56 @@
|
|||
</concept_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
<name>unbounded</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>fullHelp</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>help</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>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>
|
||||
</children>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
<name>viscosity</name>
|
||||
<children>
|
||||
|
@ -3347,6 +3647,56 @@
|
|||
</concept_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
<name>yoyo</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>fullHelp</name>
|
||||
<description/>
|
||||
<comment/>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-FR</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>help</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>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>
|
||||
</children>
|
||||
</folder_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
</children>
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
"main_menu.basic": "Basic graphics",
|
||||
"main_menu.basic_help": "Fewer particles and flashes, better performance.",
|
||||
"main_menu.download_save_file": "Download save file",
|
||||
"main_menu.download_save_file_help": "Get a transferable .b71 file with your score and stats",
|
||||
"main_menu.download_save_file_help": "Get a transferable file with your score and stats",
|
||||
"main_menu.footer_html": "<p> \n<span>Made in France by <a href=\"https://lecaro.me\">Renan LE CARO</a>.</span> \n<a href=\"https://paypal.me/renanlecaro\" target=\"_blank\">Donate</a>\n<a href=\"https://discord.gg/DZSPqyJkwP\" target=\"_blank\">Discord</a>\n<a href=\"https://f-droid.org/en/packages/me.lecaro.breakout/\" target=\"_blank\">F-Droid</a>\n<a href=\"https://play.google.com/store/apps/details?id=me.lecaro.breakout\" target=\"_blank\">Google Play</a>\n<a href=\"https://renanlecaro.itch.io/breakout71\" target=\"_blank\">itch.io</a> \n<a href=\"https://gitlab.com/lecarore/breakout71\" target=\"_blank\">Gitlab</a>\n<a href=\"https://breakout.lecaro.me/\" target=\"_blank\">Web version</a>\n<a href=\"https://news.ycombinator.com/item?id=43183131\" target=\"_blank\">HackerNews</a>\n<a href=\"https://breakout.lecaro.me/privacy.html\" target=\"_blank\">Privacy Policy</a>\n<span>v.{{appVersion}}</span>\n</p>\n",
|
||||
"main_menu.fullscreen": "Fullscreen",
|
||||
"main_menu.fullscreen_exit": "Exit Fullscreen",
|
||||
|
@ -49,7 +49,7 @@
|
|||
"main_menu.language": "Language",
|
||||
"main_menu.language_help": "Choose the game's language",
|
||||
"main_menu.load_save_file": "Load save file",
|
||||
"main_menu.load_save_file_help": "Select a .b71 file on your device",
|
||||
"main_menu.load_save_file_help": "Select a save file on your device",
|
||||
"main_menu.mobile": "Mobile mode",
|
||||
"main_menu.mobile_help": "Leaves space for your thumb under the puck.",
|
||||
"main_menu.pointer_lock": "Mouse pointer lock",
|
||||
|
@ -95,6 +95,12 @@
|
|||
"unlocks.level_description": "A {{size}}x{{size}} level with {{bricks}} bricks",
|
||||
"unlocks.title": "You unlocked {{percentUnlock}}% of the game.",
|
||||
"unlocks.unlocks_at": "Unlocks at total score {{threshold}}.",
|
||||
"upgrades.antigrav.fullHelp": "This affects the coins and will let the float up until you are ready to pick them up.",
|
||||
"upgrades.antigrav.help": "Gravity reversed left and right of puck",
|
||||
"upgrades.antigrav.name": "Antigrav",
|
||||
"upgrades.asceticism.fullHelp": "This affects the coins and will let the float up until you are ready to pick them up.",
|
||||
"upgrades.asceticism.help": "+1 combo / brick, com resets on coin catch",
|
||||
"upgrades.asceticism.name": "Asceticism",
|
||||
"upgrades.ball_attract_ball.fullHelp": "Balls that are more than half a screen width away will start attracting each other. \n\nThe attraction force is stronger when they are furthest away from each other.\n\nRainbow particles will fly to symbolize the attraction force. This perk is only offered if you have more than one ball already.",
|
||||
"upgrades.ball_attract_ball.help": "Balls attract balls",
|
||||
"upgrades.ball_attract_ball.help_plural": "Stronger attraction force",
|
||||
|
@ -122,6 +128,9 @@
|
|||
"upgrades.concave_puck.fullHelp": "Balls starts the level going straight up, and bounces with less angle.",
|
||||
"upgrades.concave_puck.help": " Helps with aiming straight up",
|
||||
"upgrades.concave_puck.name": "Concave puck",
|
||||
"upgrades.etherealcoins.fullHelp": "You'll have to make sure that the coins fall down somehow",
|
||||
"upgrades.etherealcoins.help": "Coins are no longer affected by gravity",
|
||||
"upgrades.etherealcoins.name": "Ethereal coins",
|
||||
"upgrades.extra_levels.fullHelp": "The default run can last a max of 7 levels, after which the game is over and whatever score you reached is your run score. \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_levels.help": "Play {{count}} levels instead of 7",
|
||||
"upgrades.extra_levels.name": "+1 level",
|
||||
|
@ -144,6 +153,9 @@
|
|||
"upgrades.multiball.fullHelp": "As soon as you drop the ball in Breakout 71, you loose. \n\nWith this perk, you get two balls, and so you can afford to lose one. \n\nThe lost balls come back on the next level. \n\nHaving more than one balls makes some further perks available, and of course clears the level faster.",
|
||||
"upgrades.multiball.help": "Start every levels with {{count}} balls.",
|
||||
"upgrades.multiball.name": "+1 ball",
|
||||
"upgrades.nbricks.fullHelp": "You don't necessarily need to destroy those bricks, but you need to hit them. Bricks destroyed by explosions don't count",
|
||||
"upgrades.nbricks.help": "Hit exactly {{lvl}} bricks per puck bounce for +{{lvl}} combo, otherwise it resets",
|
||||
"upgrades.nbricks.name": "Strict sample size",
|
||||
"upgrades.one_more_choice.fullHelp": "Every upgrade menu will have one more option. Doesn't increase the number of upgrades you can pick.",
|
||||
"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",
|
||||
|
@ -171,6 +183,9 @@
|
|||
"upgrades.sapper.help": "The first brick broken becomes a bomb.",
|
||||
"upgrades.sapper.help_plural": "The first {{lvl}} bricks broken become bombs.",
|
||||
"upgrades.sapper.name": "Sapper",
|
||||
"upgrades.shunt.fullHelp": "If you also have hot start, the hot start is just added to the current combo",
|
||||
"upgrades.shunt.help": "Combo no longer resets between levels",
|
||||
"upgrades.shunt.name": "Shunt",
|
||||
"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_plural": "The last {{lvl}} bricks will explode.",
|
||||
|
@ -199,11 +214,17 @@
|
|||
"upgrades.top_is_lava.fullHelp": "Whenever you break a brick, your combo will increase by one. However, your combo will reset as soon as your ball hit the top of the screen. \n\nWhen your combo is above the minimum, a red bar will appear at the top to remind you that you should avoid hitting it. \n\nThe effect stacks with other combo perks.",
|
||||
"upgrades.top_is_lava.help": "More coins if you don't touch the top.",
|
||||
"upgrades.top_is_lava.name": "Sky is the limit",
|
||||
"upgrades.unbounded.fullHelp": "I hope you've found a way to keep your ball on screen",
|
||||
"upgrades.unbounded.help": "+1 combo per brick, no more sides",
|
||||
"upgrades.unbounded.name": "Unbounded",
|
||||
"upgrades.viscosity.fullHelp": "Coins normally accelerate with gravity and explosions to pretty high speeds. \n\nThis perk constantly makes them slow down, as if they were in some sort of viscous liquid. \n\nThis makes catching them easier, and combines nicely with perks that influence the coin's movement.",
|
||||
"upgrades.viscosity.help": "Slower coin fall",
|
||||
"upgrades.viscosity.name": "Viscosity",
|
||||
"upgrades.wind.fullHelp": "The wind depends on where your puck is, if it's in the center of the screen nothing happens, if it's on the left it will blow left-wise, if it's on the right of the screen then it will blow right-wise. \n\nThe wind affects both the balls and coins.",
|
||||
"upgrades.wind.help": "Puck position creates wind",
|
||||
"upgrades.wind.help_plural": "Stronger wind force",
|
||||
"upgrades.wind.name": "Wind"
|
||||
"upgrades.wind.name": "Wind",
|
||||
"upgrades.yoyo.fullHelp": "It's the opposite of telekinesis",
|
||||
"upgrades.yoyo.help": "Ball falls toward puck",
|
||||
"upgrades.yoyo.name": "Yo-yo"
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
"main_menu.basic": "Graphismes simplifiés",
|
||||
"main_menu.basic_help": "Moins de particules et effets, meilleures performances.",
|
||||
"main_menu.download_save_file": "Sauvegarder mes progrès",
|
||||
"main_menu.download_save_file_help": "Obtenir un fichier de sauvegarde .b71 transférable",
|
||||
"main_menu.download_save_file_help": "Obtenir un fichier de sauvegarde transférable",
|
||||
"main_menu.footer_html": " <p> \n<span>Programmé en France par <a href=\"https://lecaro.me\">Renan LE CARO</a>.</span>\n<a href=\"https://paypal.me/renanlecaro\" target=\"_blank\">Donner</a>\n<a href=\"https://discord.gg/DZSPqyJkwP\" target=\"_blank\">Discord</a>\n<a href=\"https://f-droid.org/en/packages/me.lecaro.breakout/\" target=\"_blank\">F-Droid</a>\n<a href=\"https://play.google.com/store/apps/details?id=me.lecaro.breakout\" target=\"_blank\">Google Play</a>\n<a href=\"https://renanlecaro.itch.io/breakout71\" target=\"_blank\">itch.io</a>\n<a href=\"https://gitlab.com/lecarore/breakout71\" target=\"_blank\">Gitlab</a>\n<a href=\"https://breakout.lecaro.me/\" target=\"_blank\">Version web</a>\n<a href=\"https://news.ycombinator.com/item?id=43183131\" target=\"_blank\">HackerNews</a>\n<a href=\"https://breakout.lecaro.me/privacy.html\" target=\"_blank\">Politique de confidentialité</a> \n<span>v.{{appVersion}}</span>\n</p>",
|
||||
"main_menu.fullscreen": "Plein écran",
|
||||
"main_menu.fullscreen_exit": "Quitter le plein écran",
|
||||
|
@ -49,7 +49,7 @@
|
|||
"main_menu.language": "Langue",
|
||||
"main_menu.language_help": "Changer la langue d'affichage",
|
||||
"main_menu.load_save_file": "Charger une sauvegarde",
|
||||
"main_menu.load_save_file_help": "Sélectionnez un fichier .b71 sur votre appareil ",
|
||||
"main_menu.load_save_file_help": "Sélectionnez une sauvegarde sur votre appareil ",
|
||||
"main_menu.mobile": "Mode mobile",
|
||||
"main_menu.mobile_help": "Laisse un espace pour le pouce sous le palet.",
|
||||
"main_menu.pointer_lock": "Verrouillage du pointeur de la souris",
|
||||
|
@ -95,6 +95,12 @@
|
|||
"unlocks.level_description": "Un niveau {{size}}x{{size}} avec {{bricks}} briques",
|
||||
"unlocks.title": "Vous avez débloqué {{percentUnlock}}% du jeu.",
|
||||
"unlocks.unlocks_at": "Déverrouillé au score total {{threshold}}.",
|
||||
"upgrades.antigrav.fullHelp": "Les pièces attendront d'être sous le palet pour tomber. ",
|
||||
"upgrades.antigrav.help": "La gravité est inversée à droite et à gauche du palet",
|
||||
"upgrades.antigrav.name": "Antigrav",
|
||||
"upgrades.asceticism.fullHelp": "Les pièces attendront d'être sous le palet pour tomber. ",
|
||||
"upgrades.asceticism.help": "+1 combo par brique cassée, RAZ quand une pièce est attrapée",
|
||||
"upgrades.asceticism.name": "Ascétisme",
|
||||
"upgrades.ball_attract_ball.fullHelp": "Les balles qui sont éloignées de plus d'une demi-largeur d'écran commencent à s'attirer. La force d'attraction est plus forte lorsque les balles sont plus éloignées l'une de l'autre. Des particules arc-en-ciel voleront pour symboliser la force d'attraction. Cet avantage n'est offert que si vous avez déjà plus d'une balle en jeu.",
|
||||
"upgrades.ball_attract_ball.help": "Les balles attirent les balles",
|
||||
"upgrades.ball_attract_ball.help_plural": "Force d'attraction plus forte",
|
||||
|
@ -122,6 +128,9 @@
|
|||
"upgrades.concave_puck.fullHelp": " Les balles démarrent verticalement en début de niveau, et rebondi sur le palet de manière plus verticale et inversée.",
|
||||
"upgrades.concave_puck.help": "Aide à éviter les bords.",
|
||||
"upgrades.concave_puck.name": "Palet concave",
|
||||
"upgrades.etherealcoins.fullHelp": "Il faudrait vous assurer que les pièces tomberont bien quand même à un moment",
|
||||
"upgrades.etherealcoins.help": "Les pièces ne subissent plus la gravité",
|
||||
"upgrades.etherealcoins.name": "Pièces sans poids",
|
||||
"upgrades.extra_levels.fullHelp": "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_levels.help": "Jouer {{count}} niveaux au lieu de 7",
|
||||
"upgrades.extra_levels.name": "+1 niveau",
|
||||
|
@ -144,6 +153,9 @@
|
|||
"upgrades.multiball.fullHelp": "Dès que vous laissez tomber la balle dans Breakout 71, vous perdez. \n\nAvec cet avantage, vous obtenez deux balles, et vous pouvez donc vous permettre d'en perdre une.\n\nLes balles perdues reviennent au niveau suivant. \n\nLe fait d'avoir plus d'une balle permet d'obtenir d'autres avantages et, bien sûr, de franchir le niveau plus rapidement.",
|
||||
"upgrades.multiball.help": "Chaque niveau commence avec {{count}} balles.",
|
||||
"upgrades.multiball.name": "+1 balle",
|
||||
"upgrades.nbricks.fullHelp": "Si votre balle rebondis sans casser une brique, ça compte quand même comme une frappe. Les briques détruites par des explosions ne comptent pas.",
|
||||
"upgrades.nbricks.help": "Frappez exactement {{lvl}} briques par rebond pour +{{lvl}} combo, sinon RAZ",
|
||||
"upgrades.nbricks.name": "Prélèvement",
|
||||
"upgrades.one_more_choice.fullHelp": "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. ",
|
||||
"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",
|
||||
|
@ -171,6 +183,9 @@
|
|||
"upgrades.sapper.help": "La première brique cassée devient une bombe.",
|
||||
"upgrades.sapper.help_plural": "Les premières briques {{lvl}} cassées deviennent des bombes.",
|
||||
"upgrades.sapper.name": "Sapeur",
|
||||
"upgrades.shunt.fullHelp": "Démarrage à chaud sera simplement ajouté au combo actuel",
|
||||
"upgrades.shunt.help": "Le combo ne se remet plus à zéro en début de niveau",
|
||||
"upgrades.shunt.name": "Shunt",
|
||||
"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_plural": "Les {{lvl}} dernières briques restantes s'autodétruiront",
|
||||
|
@ -199,11 +214,17 @@
|
|||
"upgrades.top_is_lava.fullHelp": "Chaque fois que vous cassez une brique, votre combo augmente d'une unité. Cependant, votre combo sera réinitialisé dès que votre balle atteindra le haut de l'écran.\n\nLorsque votre combo est supérieur au minimum, une barre rouge apparaît en haut de l'écran pour vous rappeler que vous devez éviter de la frapper.\n\nCet effet s'ajoute aux autres avantages du combo.",
|
||||
"upgrades.top_is_lava.help": "Plus de pièces si vous ne touchez pas le haut de la zone de jeu",
|
||||
"upgrades.top_is_lava.name": "Icare ",
|
||||
"upgrades.unbounded.fullHelp": "J'espère que vous avez prévu un moyen de récupérer vos balles",
|
||||
"upgrades.unbounded.help": "+1 combo par brique, plus de cotés",
|
||||
"upgrades.unbounded.name": "Libérée, délivrée",
|
||||
"upgrades.viscosity.fullHelp": "Les pièces accélèrent normalement avec la gravité et les explosions pour atteindre des vitesses assez élevées. \n\nCette compétence les ralentit constamment, comme si elles se trouvaient dans une sorte de liquide visqueux.\n\nCela permet de les attraper plus facilement et se combine bien avec les améliorations qui influencent le mouvement de la pièce.",
|
||||
"upgrades.viscosity.help": "Chute plus lente des pièces",
|
||||
"upgrades.viscosity.name": "Fluide visqueux ",
|
||||
"upgrades.wind.fullHelp": "Le vent dépend de l'endroit où se trouve le palet, s'il est au centre de l'écran, il ne se passe rien, s'il est à gauche, il soufflera vers la gauche, s'il est à droite de l'écran, il soufflera vers la droite.\n\nLe vent affecte à la fois les balles et les pièces.",
|
||||
"upgrades.wind.help": "La position du palet crée du vent",
|
||||
"upgrades.wind.help_plural": "Force du vent plus importante",
|
||||
"upgrades.wind.name": "Vive le vent"
|
||||
"upgrades.wind.name": "Vive le vent",
|
||||
"upgrades.yoyo.fullHelp": "C'est l'inverse de Télékinésie",
|
||||
"upgrades.yoyo.help": "La balle descend vers le palet",
|
||||
"upgrades.yoyo.name": "Yo-yo"
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import { Level, Palette, RawLevel, Upgrade } from "./types";
|
|||
import _palette from "./data/palette.json";
|
||||
import _rawLevelsList from "./data/levels.json";
|
||||
import _appVersion from "./data/version.json";
|
||||
import { rawUpgrades } from "./rawUpgrades";
|
||||
import { rawUpgrades } from "./upgrades";
|
||||
import { getLevelBackground } from "./getLevelBackground";
|
||||
import { levelIconHTML } from "./levelIcon";
|
||||
|
||||
|
|
|
@ -92,6 +92,7 @@ export function newGameState(params: RunParams): GameState {
|
|||
},
|
||||
lastOffered: {},
|
||||
levelTime: 0,
|
||||
noBricksSince: 0,
|
||||
levelWallBounces: 0,
|
||||
needsRender: true,
|
||||
autoCleanUses: 0,
|
||||
|
|
1259
src/render.ts
1259
src/render.ts
File diff suppressed because it is too large
Load diff
3
src/types.d.ts
vendored
3
src/types.d.ts
vendored
|
@ -1,4 +1,4 @@
|
|||
import { rawUpgrades } from "./rawUpgrades";
|
||||
import { rawUpgrades } from "./upgrades";
|
||||
import { options } from "./options";
|
||||
|
||||
export type colorString = string;
|
||||
|
@ -244,6 +244,7 @@ export type GameState = {
|
|||
runStatistics: RunStats;
|
||||
lastOffered: Partial<{ [k in PerkId]: number }>;
|
||||
levelTime: number;
|
||||
noBricksSince: number ;
|
||||
levelWallBounces: number;
|
||||
autoCleanUses: number;
|
||||
aboutToPlaySound: {
|
||||
|
|
11
src/upgrades.test.ts
Normal file
11
src/upgrades.test.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
|
||||
import _rawLevelsList from "./data/levels.json";
|
||||
import {rawUpgrades} from "./upgrades";
|
||||
|
||||
describe("rawUpgrades", ()=>{
|
||||
|
||||
it('has an icon for each upgrade',()=>{
|
||||
const missingIcon = rawUpgrades.map(u=>u.id).filter(id=>!_rawLevelsList.find(l=>l.name === 'icon:'+id))
|
||||
expect(missingIcon.join(', ')).toEqual('')
|
||||
})
|
||||
})
|
|
@ -371,4 +371,74 @@ export const rawUpgrades = [
|
|||
help: (lvl: number) => t("upgrades.concave_puck.help"),
|
||||
fullHelp: t("upgrades.concave_puck.fullHelp"),
|
||||
},
|
||||
{
|
||||
requires: "",
|
||||
threshold: 65000,
|
||||
giftable: false,
|
||||
id: "antigrav",
|
||||
max: 1,
|
||||
name: t("upgrades.antigrav.name"),
|
||||
help: (lvl: number) => t("upgrades.antigrav.help"),
|
||||
fullHelp: t("upgrades.antigrav.fullHelp"),
|
||||
},
|
||||
{
|
||||
requires: "",
|
||||
threshold:70000,
|
||||
giftable: false,
|
||||
id: "asceticism",
|
||||
max: 1,
|
||||
name: t("upgrades.asceticism.name"),
|
||||
help: (lvl: number) => t("upgrades.asceticism.help"),
|
||||
fullHelp: t("upgrades.asceticism.fullHelp"),
|
||||
},
|
||||
{
|
||||
requires: "",
|
||||
threshold:75000,
|
||||
giftable: false,
|
||||
id: "unbounded",
|
||||
max: 1,
|
||||
name: t("upgrades.unbounded.name"),
|
||||
help: (lvl: number) => t("upgrades.unbounded.help"),
|
||||
fullHelp: t("upgrades.unbounded.fullHelp"),
|
||||
},
|
||||
{
|
||||
requires: "",
|
||||
threshold:80000,
|
||||
giftable: false,
|
||||
id: "shunt",
|
||||
max: 1,
|
||||
name: t("upgrades.shunt.name"),
|
||||
help: (lvl: number) => t("upgrades.shunt.help"),
|
||||
fullHelp: t("upgrades.shunt.fullHelp"),
|
||||
},
|
||||
{
|
||||
requires: "",
|
||||
threshold:85000,
|
||||
giftable: false,
|
||||
id: "yoyo",
|
||||
max: 2,
|
||||
name: t("upgrades.yoyo.name"),
|
||||
help: (lvl: number) => t("upgrades.yoyo.help"),
|
||||
fullHelp: t("upgrades.yoyo.fullHelp"),
|
||||
},
|
||||
{
|
||||
requires: "",
|
||||
threshold:90000,
|
||||
giftable: false,
|
||||
id: "nbricks",
|
||||
max: 3,
|
||||
name: t("upgrades.nbricks.name"),
|
||||
help: (lvl: number) => t("upgrades.nbricks.help",{lvl}),
|
||||
fullHelp: t("upgrades.nbricks.fullHelp"),
|
||||
},
|
||||
{
|
||||
requires: "",
|
||||
threshold:95000,
|
||||
giftable: false,
|
||||
id: "etherealcoins",
|
||||
max: 1,
|
||||
name: t("upgrades.etherealcoins.name"),
|
||||
help: (lvl: number) => t("upgrades.etherealcoins.help"),
|
||||
fullHelp: t("upgrades.etherealcoins.fullHelp"),
|
||||
},
|
||||
] as const;
|
Loading…
Add table
Add a link
Reference in a new issue