mirror of
https://gitlab.com/lecarore/breakout71.git
synced 2025-04-22 13:06:15 -04:00
Build 29087244
This commit is contained in:
parent
5ba93500b4
commit
49f3769b54
21 changed files with 2505 additions and 2517 deletions
12
Readme.md
12
Readme.md
|
@ -11,19 +11,13 @@ Break colourful bricks, catch bouncing coins and select powerful upgrades !
|
|||
- [Google Play](https://play.google.com/store/apps/details?id=me.lecaro.breakout)
|
||||
- [GitLab](https://gitlab.com/lecarore/breakout71)
|
||||
|
||||
# Current priorities
|
||||
|
||||
The goal of this project is to make a game used by many people.
|
||||
The game is already pretty fun.
|
||||
|
||||
I'm now trying to translate it to (Lebanese) Arabic, Russian and (Chilean) Spanish.
|
||||
Other translation are very welcome, contact me if you'd like to submit one.
|
||||
|
||||
|
||||
# Changelog
|
||||
## To do
|
||||
## Done
|
||||
|
||||
- apply percentage boost to combo shown on brick
|
||||
- smaller puck now gives +50% coins per level
|
||||
- transparency now gives +50% coins if ALL balls are fully transparent, less otherwise
|
||||
- new perk : sticky coins (coins stick to bricks)
|
||||
- left/top/right is laval perks : at level 2+, the corresponding borders completely disappears (reachable with limitless)
|
||||
- new perk : three cushion (gain point for indirect hits)
|
||||
|
|
|
@ -29,8 +29,8 @@ android {
|
|||
applicationId = "me.lecaro.breakout"
|
||||
minSdk = 21
|
||||
targetSdk = 34
|
||||
versionCode = 29085904
|
||||
versionName = "29085904"
|
||||
versionCode = 29087244
|
||||
versionName = "29087244"
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables {
|
||||
useSupportLibrary = true
|
||||
|
|
File diff suppressed because one or more lines are too long
819
dist/index.html
vendored
819
dist/index.html
vendored
File diff suppressed because one or more lines are too long
|
@ -1,5 +1,5 @@
|
|||
// The version of the cache.
|
||||
const VERSION = "29085904";
|
||||
const VERSION = "29087244";
|
||||
|
||||
// The name of the cache
|
||||
const CACHE_NAME = `breakout-71-${VERSION}`;
|
||||
|
|
|
@ -44,18 +44,16 @@ export async function openCreativeModePerksPicker() {
|
|||
);
|
||||
|
||||
while (true) {
|
||||
|
||||
const levelOptions = [
|
||||
...allLevels.map((l, li) => {
|
||||
const problem =
|
||||
reasonLevelIsLocked(li, getHistory(), true)?.text || "";
|
||||
const problem = reasonLevelIsLocked(li, getHistory(), true)?.text || "";
|
||||
return {
|
||||
icon: icons[l.name],
|
||||
text: l.name,
|
||||
value: l,
|
||||
disabled: !!problem,
|
||||
tooltip: problem || describeLevel(l),
|
||||
className:''
|
||||
className: "",
|
||||
};
|
||||
}),
|
||||
...customLevels.map((l) => ({
|
||||
|
@ -64,26 +62,28 @@ export async function openCreativeModePerksPicker() {
|
|||
value: l,
|
||||
disabled: !l.bricks.filter((b) => b !== "_").length,
|
||||
tooltip: describeLevel(l),
|
||||
className:''
|
||||
}))
|
||||
]
|
||||
|
||||
const selectedLeveOption= levelOptions.find(l=>l.text===getSettingValue("creativeModeLevel", '')) || levelOptions[0]
|
||||
selectedLeveOption.className= 'highlight'
|
||||
className: "",
|
||||
})),
|
||||
];
|
||||
|
||||
const selectedLeveOption =
|
||||
levelOptions.find(
|
||||
(l) => l.text === getSettingValue("creativeModeLevel", ""),
|
||||
) || levelOptions[0];
|
||||
selectedLeveOption.className = "highlight";
|
||||
|
||||
const choice = await asyncAlert<Upgrade | Level | "reset" | "play">({
|
||||
title: t("lab.menu_entry"),
|
||||
className: "actionsAsGrid",
|
||||
content: [
|
||||
{
|
||||
icon: icons['icon:reset'],
|
||||
icon: icons["icon:reset"],
|
||||
value: "reset",
|
||||
text: t("lab.reset"),
|
||||
disabled: !sumOfValues(creativeModePerks),
|
||||
},
|
||||
{
|
||||
icon: icons['icon:new_run'],
|
||||
icon: icons["icon:new_run"],
|
||||
value: "play",
|
||||
text: t("lab.play"),
|
||||
disabled: !sumOfValues(creativeModePerks),
|
||||
|
@ -106,24 +106,28 @@ export async function openCreativeModePerksPicker() {
|
|||
tooltip: u.help(creativeModePerks[u.id] || 1),
|
||||
})),
|
||||
t("lab.select_level"),
|
||||
...levelOptions
|
||||
...levelOptions,
|
||||
],
|
||||
})
|
||||
if(!choice)return
|
||||
});
|
||||
if (!choice) return;
|
||||
if (choice === "reset") {
|
||||
upgrades.forEach((u) => {
|
||||
creativeModePerks[u.id] = 0;
|
||||
});
|
||||
setSettingValue("creativeModePerks", creativeModePerks);
|
||||
setSettingValue("creativeModeLevel", '')
|
||||
} else if (choice === "play" || ("bricks" in choice && choice.name==getSettingValue("creativeModeLevel", ''))) {
|
||||
setSettingValue("creativeModeLevel", "");
|
||||
} else if (
|
||||
choice === "play" ||
|
||||
("bricks" in choice &&
|
||||
choice.name == getSettingValue("creativeModeLevel", ""))
|
||||
) {
|
||||
if (await confirmRestart(gameState)) {
|
||||
restart({
|
||||
perks: creativeModePerks,
|
||||
level: selectedLeveOption.value,
|
||||
isCreativeRun: true,
|
||||
});
|
||||
return
|
||||
return;
|
||||
}
|
||||
} else if ("bricks" in choice) {
|
||||
setSettingValue("creativeModeLevel", choice.name);
|
||||
|
|
|
@ -1 +1 @@
|
|||
"29085904"
|
||||
"29087244"
|
||||
|
|
|
@ -586,7 +586,6 @@ h2.histogram-title strong {
|
|||
}
|
||||
}
|
||||
.not-highlighed {
|
||||
opacity: 0.8; color: #8a8a8a;
|
||||
|
||||
|
||||
opacity: 0.8;
|
||||
color: #8a8a8a;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import {
|
|||
describeLevel,
|
||||
getRowColIndex,
|
||||
highScoreText,
|
||||
hoursSpentPlaying,
|
||||
isInWebView,
|
||||
levelsListHTMl,
|
||||
max_levels,
|
||||
|
@ -79,7 +80,6 @@ import {
|
|||
catchRateBest,
|
||||
catchRateGood,
|
||||
clamp,
|
||||
hoursSpentPlaying,
|
||||
levelTimeBest,
|
||||
levelTimeGood,
|
||||
missesBest,
|
||||
|
@ -248,7 +248,8 @@ setInterval(() => {
|
|||
}, 1000);
|
||||
|
||||
export async function openUpgradesPicker(gameState: GameState) {
|
||||
const catchRate = (gameState.score - gameState.levelStartScore) /
|
||||
const catchRate =
|
||||
(gameState.score - gameState.levelStartScore) /
|
||||
(gameState.levelSpawnedCoins || 1);
|
||||
|
||||
let repeats = 1;
|
||||
|
|
|
@ -12,7 +12,6 @@ import {
|
|||
} from "./types";
|
||||
|
||||
import {
|
||||
ballTransparency,
|
||||
brickCenterX,
|
||||
brickCenterY,
|
||||
currentLevelInfo,
|
||||
|
@ -50,7 +49,13 @@ import {
|
|||
} from "./game";
|
||||
import { stopRecording } from "./recording";
|
||||
import { isOptionOn } from "./options";
|
||||
import {clamp, coinsBoostedCombo, comboKeepingRate, shouldCoinsStick} from "./pure_functions";
|
||||
import {
|
||||
ballTransparency,
|
||||
clamp,
|
||||
coinsBoostedCombo,
|
||||
comboKeepingRate,
|
||||
shouldCoinsStick,
|
||||
} from "./pure_functions";
|
||||
import { addToTotalScore } from "./addToTotalScore";
|
||||
import { hashCode } from "./getLevelBackground";
|
||||
|
||||
|
@ -216,11 +221,7 @@ export function baseCombo(gameState: GameState) {
|
|||
gameState.perks.minefield &&
|
||||
gameState.bricks.filter((b) => b === "black").length *
|
||||
gameState.perks.minefield;
|
||||
return (
|
||||
1 +
|
||||
gameState.perks.base_combo * 3 +
|
||||
mineFieldBonus
|
||||
);
|
||||
return 1 + gameState.perks.base_combo * 3 + mineFieldBonus;
|
||||
}
|
||||
|
||||
export function resetCombo(
|
||||
|
@ -432,7 +433,7 @@ export function explodeBrick(
|
|||
|
||||
setBrick(gameState, index, "");
|
||||
|
||||
let coinsToSpawn = coinsBoostedCombo(gameState)
|
||||
let coinsToSpawn = coinsBoostedCombo(gameState);
|
||||
|
||||
gameState.levelSpawnedCoins += coinsToSpawn;
|
||||
gameState.runStatistics.coins_spawned += coinsToSpawn;
|
||||
|
@ -630,9 +631,7 @@ export function addToScore(gameState: GameState, coin: Coin) {
|
|||
gameState.highScore = gameState.score;
|
||||
try {
|
||||
localStorage.setItem("breakout-3-hs-short", gameState.score.toString());
|
||||
} catch (e) {
|
||||
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
if (!isOptionOn("basic")) {
|
||||
makeParticle(
|
||||
|
@ -885,11 +884,11 @@ export function coinBrickHitCheck(gameState: GameState, coin: Coin) {
|
|||
if (typeof (vhit ?? hhit ?? chit) !== "undefined") {
|
||||
if (shouldCoinsStick(gameState)) {
|
||||
if (coin.collidedLastFrame) {
|
||||
coin.x = previousX
|
||||
coin.y = previousY
|
||||
coin.x = previousX;
|
||||
coin.y = previousY;
|
||||
}
|
||||
coin.vx = 0
|
||||
coin.vy = 0
|
||||
coin.vx = 0;
|
||||
coin.vy = 0;
|
||||
} else if (gameState.perks.ghost_coins) {
|
||||
// slow down
|
||||
coin.vy *= 1 - 0.2 / gameState.perks.ghost_coins;
|
||||
|
@ -945,7 +944,10 @@ export function bordersHitCheck(
|
|||
let vhit = 0,
|
||||
hhit = 0;
|
||||
|
||||
if (coin.x < gameState.offsetXRoundedDown + radius && gameState.perks.left_is_lava < 2) {
|
||||
if (
|
||||
coin.x < gameState.offsetXRoundedDown + radius &&
|
||||
gameState.perks.left_is_lava < 2
|
||||
) {
|
||||
coin.x =
|
||||
gameState.offsetXRoundedDown +
|
||||
radius +
|
||||
|
@ -958,7 +960,10 @@ export function bordersHitCheck(
|
|||
coin.vy *= -1;
|
||||
vhit = 1;
|
||||
}
|
||||
if (coin.x > gameState.canvasWidth - gameState.offsetXRoundedDown - radius && gameState.perks.right_is_lava < 2) {
|
||||
if (
|
||||
coin.x > gameState.canvasWidth - gameState.offsetXRoundedDown - radius &&
|
||||
gameState.perks.right_is_lava < 2
|
||||
) {
|
||||
coin.x =
|
||||
gameState.canvasWidth -
|
||||
gameState.offsetXRoundedDown -
|
||||
|
@ -969,7 +974,6 @@ export function bordersHitCheck(
|
|||
hhit = 1;
|
||||
}
|
||||
|
||||
|
||||
return hhit + vhit * 2;
|
||||
}
|
||||
|
||||
|
@ -1014,10 +1018,8 @@ export function gameStateTick(
|
|||
if (gameState.perks.hot_start) {
|
||||
if (gameState.combo === baseCombo(gameState)) {
|
||||
// Give 1s of time between catching a coin and tick down
|
||||
gameState.lastTickDown = gameState.levelTime
|
||||
} else if (
|
||||
gameState.levelTime > gameState.lastTickDown + 1000
|
||||
) {
|
||||
gameState.lastTickDown = gameState.levelTime;
|
||||
} else if (gameState.levelTime > gameState.lastTickDown + 1000) {
|
||||
gameState.lastTickDown = gameState.levelTime;
|
||||
decreaseCombo(
|
||||
gameState,
|
||||
|
@ -1028,7 +1030,6 @@ export function gameStateTick(
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
if (
|
||||
remainingBricks <= gameState.perks.skip_last &&
|
||||
!gameState.autoCleanUses
|
||||
|
@ -1161,10 +1162,12 @@ export function gameStateTick(
|
|||
coin.vy *= ratio;
|
||||
coin.vx *= ratio;
|
||||
}
|
||||
if (coin.y > gameState.gameZoneHeight && coin.floatingTime < gameState.perks.buoy * 30) {
|
||||
|
||||
coin.floatingTime += frames
|
||||
coin.vy -= 1.5
|
||||
if (
|
||||
coin.y > gameState.gameZoneHeight &&
|
||||
coin.floatingTime < gameState.perks.buoy * 30
|
||||
) {
|
||||
coin.floatingTime += frames;
|
||||
coin.vy -= 1.5;
|
||||
}
|
||||
|
||||
if (coin.vx > 7 * gameState.baseSpeed) coin.vx = 7 * gameState.baseSpeed;
|
||||
|
@ -1215,7 +1218,6 @@ export function gameStateTick(
|
|||
|
||||
const hitBorder = bordersHitCheck(gameState, coin, coin.size / 2, frames);
|
||||
|
||||
|
||||
if (
|
||||
coin.previousY < gameState.gameZoneHeight &&
|
||||
coin.y > gameState.gameZoneHeight &&
|
||||
|
@ -1267,11 +1269,14 @@ export function gameStateTick(
|
|||
|
||||
if (
|
||||
gameState.combo < gameState.perks.fountain_toss * 30 &&
|
||||
Math.random() / coin.points < (1 / gameState.combo) * gameState.perks.fountain_toss
|
||||
Math.random() / coin.points <
|
||||
(1 / gameState.combo) * gameState.perks.fountain_toss
|
||||
) {
|
||||
increaseCombo(gameState, 1,
|
||||
increaseCombo(
|
||||
gameState,
|
||||
1,
|
||||
clamp(coin.x, 20, gameState.canvasWidth - 20),
|
||||
clamp(coin.y, 20, gameState.gameZoneHeight - 20)
|
||||
clamp(coin.y, 20, gameState.gameZoneHeight - 20),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1319,10 +1324,7 @@ export function gameStateTick(
|
|||
}
|
||||
}
|
||||
// remember collision
|
||||
coin.collidedLastFrame = !!(
|
||||
typeof hitBrick !== "undefined" ||
|
||||
hitBorder
|
||||
)
|
||||
coin.collidedLastFrame = !!(typeof hitBrick !== "undefined" || hitBorder);
|
||||
});
|
||||
|
||||
gameState.balls.forEach((ball) => ballTick(gameState, ball, frames));
|
||||
|
@ -1330,7 +1332,6 @@ export function gameStateTick(
|
|||
if (gameState.perks.shocks) {
|
||||
gameState.balls.forEach((a, ai) =>
|
||||
gameState.balls.forEach((b, bi) => {
|
||||
|
||||
if (
|
||||
ai < bi &&
|
||||
!a.destroyed &&
|
||||
|
@ -1349,7 +1350,7 @@ export function gameStateTick(
|
|||
let y = (a.y + b.y) / 2;
|
||||
// space out the balls with extra speed
|
||||
if (gameState.perks.shocks > 1) {
|
||||
const limit = gameState.baseSpeed * gameState.perks.shocks / 2;
|
||||
const limit = (gameState.baseSpeed * gameState.perks.shocks) / 2;
|
||||
a.vx +=
|
||||
clamp(a.x - x, -limit, limit) +
|
||||
((Math.random() - 0.5) * limit) / 3;
|
||||
|
@ -1641,8 +1642,7 @@ export function ballTick(gameState: GameState, ball: Ball, frames: number) {
|
|||
frames,
|
||||
);
|
||||
if (borderHitCode) {
|
||||
|
||||
ball.sidesHitsSinceBounce++
|
||||
ball.sidesHitsSinceBounce++;
|
||||
if (ball.sidesHitsSinceBounce <= gameState.perks.three_cushion * 3) {
|
||||
increaseCombo(gameState, 1, ball.x, ball.y);
|
||||
}
|
||||
|
@ -1755,13 +1755,10 @@ export function ballTick(gameState: GameState, ball: Ball, frames: number) {
|
|||
|
||||
if (
|
||||
gameState.running &&
|
||||
(
|
||||
ball.y > gameState.gameZoneHeight + gameState.ballSize / 2 ||
|
||||
(ball.y > gameState.gameZoneHeight + gameState.ballSize / 2 ||
|
||||
ball.y < -gameState.gameZoneHeight ||
|
||||
ball.x < -gameState.gameZoneHeight ||
|
||||
ball.x > gameState.canvasWidth + gameState.gameZoneHeight
|
||||
)
|
||||
|
||||
ball.x > gameState.canvasWidth + gameState.gameZoneHeight)
|
||||
) {
|
||||
ball.destroyed = true;
|
||||
gameState.runStatistics.balls_lost++;
|
||||
|
@ -1841,7 +1838,7 @@ export function ballTick(gameState: GameState, ball: Ball, frames: number) {
|
|||
|
||||
if (!gameState.brickHP[hitBrick]) {
|
||||
ball.brokenSinceBounce++;
|
||||
applyOttawaTreatyPerk(gameState, hitBrick, ball)
|
||||
applyOttawaTreatyPerk(gameState, hitBrick, ball);
|
||||
explodeBrick(gameState, hitBrick, ball, false);
|
||||
if (
|
||||
ball.sapperUses < gameState.perks.sapper &&
|
||||
|
@ -1851,8 +1848,6 @@ export function ballTick(gameState: GameState, ball: Ball, frames: number) {
|
|||
setBrick(gameState, hitBrick, "black");
|
||||
ball.sapperUses++;
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
schedulGameSound(gameState, "wallBeep", x, 1);
|
||||
makeLight(
|
||||
|
@ -1971,7 +1966,7 @@ function makeCoin(
|
|||
p.points = points;
|
||||
p.weight = weight;
|
||||
p.metamorphosisPoints = gameState.perks.metamorphosis;
|
||||
p.floatingTime = 0
|
||||
p.floatingTime = 0;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -2146,32 +2141,37 @@ function goToNearestBrick(
|
|||
}
|
||||
}
|
||||
|
||||
function applyOttawaTreatyPerk(
|
||||
gameState: GameState,
|
||||
index: number,
|
||||
ball: Ball,
|
||||
) {
|
||||
if (!gameState.perks.ottawa_treaty) return;
|
||||
if (ball.sapperUses) return;
|
||||
|
||||
function applyOttawaTreatyPerk(gameState: GameState, index: number, ball: Ball) {
|
||||
if (!gameState.perks.ottawa_treaty) return
|
||||
if (ball.sapperUses) return
|
||||
|
||||
const originalColor = gameState.bricks[index]
|
||||
if (originalColor == 'black') return
|
||||
const x = index % gameState.gridSize
|
||||
const y = Math.floor(index / gameState.gridSize)
|
||||
let converted = 0
|
||||
const originalColor = gameState.bricks[index];
|
||||
if (originalColor == "black") return;
|
||||
const x = index % gameState.gridSize;
|
||||
const y = Math.floor(index / gameState.gridSize);
|
||||
let converted = 0;
|
||||
for (let dx = -1; dx <= 1; dx++)
|
||||
for (let dy = -1; dy <= 1; dy++)
|
||||
if (dx || dy) {
|
||||
const nIndex = getRowColIndex(gameState, y + dy, x + dx)
|
||||
if (gameState.bricks[nIndex] && gameState.bricks[nIndex] === 'black') {
|
||||
|
||||
setBrick(gameState, nIndex, originalColor)
|
||||
schedulGameSound(gameState, "colorChange", brickCenterX(gameState, index), 1)
|
||||
const nIndex = getRowColIndex(gameState, y + dy, x + dx);
|
||||
if (gameState.bricks[nIndex] && gameState.bricks[nIndex] === "black") {
|
||||
setBrick(gameState, nIndex, originalColor);
|
||||
schedulGameSound(
|
||||
gameState,
|
||||
"colorChange",
|
||||
brickCenterX(gameState, index),
|
||||
1,
|
||||
);
|
||||
// Avoid infinite bricks generation hack
|
||||
ball.sapperUses = Infinity
|
||||
converted++
|
||||
ball.sapperUses = Infinity;
|
||||
converted++;
|
||||
// Don't convert more than one brick per hit normally
|
||||
if (converted >= gameState.perks.ottawa_treaty) return
|
||||
|
||||
if (converted >= gameState.perks.ottawa_treaty) return;
|
||||
}
|
||||
}
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ import { t } from "./i18n/i18n";
|
|||
import { clamp } from "./pure_functions";
|
||||
import { rawUpgrades } from "./upgrades";
|
||||
import { hashCode } from "./getLevelBackground";
|
||||
import { getTotalScore } from "./settings";
|
||||
import { getSettingValue, getTotalScore } from "./settings";
|
||||
import { isOptionOn } from "./options";
|
||||
|
||||
export function describeLevel(level: Level) {
|
||||
|
@ -392,16 +392,6 @@ 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) * 1.2),
|
||||
0,
|
||||
1,
|
||||
);
|
||||
}
|
||||
|
||||
export function getCoinRenderColor(gameState: GameState, coin: Coin) {
|
||||
if (
|
||||
gameState.perks.metamorphosis ||
|
||||
|
@ -423,3 +413,12 @@ export function getCornerOffset(gameState: GameState) {
|
|||
}
|
||||
|
||||
export const isInWebView = !!window.location.href.includes("isInWebView=true");
|
||||
|
||||
export function hoursSpentPlaying() {
|
||||
try {
|
||||
const timePlayed = getSettingValue("breakout_71_total_play_time", 0);
|
||||
return Math.floor(timePlayed / 1000 / 60 / 60);
|
||||
} catch (e) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
import { icons, transformRawLevel } from "./loadGameData";
|
||||
import { t } from "./i18n/i18n";
|
||||
import {
|
||||
getSettingValue,
|
||||
getTotalScore,
|
||||
setSettingValue,
|
||||
} from "./settings";
|
||||
import { getSettingValue, getTotalScore, setSettingValue } from "./settings";
|
||||
import { asyncAlert } from "./asyncAlert";
|
||||
import { Palette, RawLevel } from "./types";
|
||||
import { levelIconHTML } from "./levelIcon";
|
||||
|
@ -165,7 +161,6 @@ export async function editRawLevelList(nth: number, color = "W") {
|
|||
text: t("editor.editing.copy"),
|
||||
value: "copy",
|
||||
help: t("editor.editing.copy_help"),
|
||||
|
||||
},
|
||||
{
|
||||
text: t("editor.editing.bigger"),
|
||||
|
@ -250,7 +245,10 @@ export async function editRawLevelList(nth: number, color = "W") {
|
|||
return;
|
||||
}
|
||||
if (action === "copy") {
|
||||
let text = "```\n[" + (level.name||'unnamed level')?.replace(/\[|\]/gi, " ") + "]";
|
||||
let text =
|
||||
"```\n[" +
|
||||
(level.name || "unnamed level")?.replace(/\[|\]/gi, " ") +
|
||||
"]";
|
||||
bricks.forEach((b, bi) => {
|
||||
if (!(bi % level.size)) text += "\n";
|
||||
text += b;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import _palette from "./data/palette.json";
|
||||
import _rawLevelsList from "./data/levels.json";
|
||||
import _appVersion from "./data/version.json";
|
||||
import { rawUpgrades } from "./upgrades";
|
||||
|
||||
describe("json data checks", () => {
|
||||
it("_rawLevelsList has icon levels", () => {
|
||||
|
@ -10,13 +9,6 @@ describe("json data checks", () => {
|
|||
).toBeGreaterThan(10);
|
||||
});
|
||||
|
||||
it("all upgrades have icons", () => {
|
||||
const missingIcon = rawUpgrades.filter(
|
||||
(u) => !_rawLevelsList.find((l) => l.name == "icon:" + u.id),
|
||||
);
|
||||
expect(missingIcon).toEqual([]);
|
||||
});
|
||||
|
||||
it("_rawLevelsList has non-icon few levels", () => {
|
||||
expect(
|
||||
_rawLevelsList.filter((l) => !l.name.startsWith("icon:")).length,
|
||||
|
|
|
@ -140,16 +140,15 @@ migrate("set_breakout_71_unlocked_levels" + _appVersion, () => {
|
|||
);
|
||||
});
|
||||
|
||||
migrate('clean_ls', ()=>{
|
||||
migrate("clean_ls", () => {
|
||||
for (let key in localStorage) {
|
||||
try {
|
||||
JSON.parse(localStorage.getItem(key) || "null");
|
||||
} catch (e) {
|
||||
localStorage.removeItem(key)
|
||||
console.warn('Removed invalid key '+key,e);
|
||||
localStorage.removeItem(key);
|
||||
console.warn("Removed invalid key " + key, e);
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
});
|
||||
|
||||
afterMigration();
|
||||
|
|
|
@ -2,7 +2,8 @@ import { t } from "./i18n/i18n";
|
|||
|
||||
import { OptionDef, OptionId } from "./types";
|
||||
import { getSettingValue, setSettingValue } from "./settings";
|
||||
import { hoursSpentPlaying } from "./pure_functions";
|
||||
|
||||
import { hoursSpentPlaying } from "./game_utils";
|
||||
|
||||
export const options = {
|
||||
sound: {
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
import { getSettingValue } from "./settings";
|
||||
import {GameState} from "./types";
|
||||
import {ballTransparency} from "./game_utils";
|
||||
import { Ball, GameState } from "./types";
|
||||
|
||||
export function clamp(value: number, min: number, max: number) {
|
||||
return Math.max(min, Math.min(value, max));
|
||||
|
@ -10,33 +8,39 @@ export function comboKeepingRate(level: number) {
|
|||
return clamp(1 - (1 / (1 + level)) * 1.5, 0, 1);
|
||||
}
|
||||
|
||||
export function hoursSpentPlaying() {
|
||||
try {
|
||||
const timePlayed = getSettingValue("breakout_71_total_play_time", 0);
|
||||
return Math.floor(timePlayed / 1000 / 60 / 60);
|
||||
} catch (e) {
|
||||
return 0;
|
||||
}
|
||||
export function shouldCoinsStick(gameState: GameState) {
|
||||
return (
|
||||
gameState.perks.sticky_coins &&
|
||||
(!gameState.lastExplosion ||
|
||||
gameState.lastExplosion <
|
||||
gameState.levelTime - 300 * gameState.perks.sticky_coins)
|
||||
);
|
||||
}
|
||||
|
||||
export function shouldCoinsStick(gameState:GameState){
|
||||
return gameState.perks.sticky_coins && (!gameState.lastExplosion || gameState.lastExplosion < gameState.levelTime - 300 * gameState.perks.sticky_coins)
|
||||
export function ballTransparency(ball: Ball, gameState: GameState) {
|
||||
if (!gameState.perks.transparency) return 0;
|
||||
return clamp(
|
||||
gameState.perks.transparency *
|
||||
(1 - (ball.y / gameState.gameZoneHeight) * 1.2),
|
||||
0,
|
||||
1,
|
||||
);
|
||||
}
|
||||
|
||||
export function coinsBoostedCombo(gameState: GameState) {
|
||||
let boost = 1+gameState.perks.sturdy_bricks / 2 + gameState.perks.smaller_puck/2
|
||||
let boost =
|
||||
1 + gameState.perks.sturdy_bricks / 2 + gameState.perks.smaller_puck / 2;
|
||||
if (gameState.perks.transparency) {
|
||||
let min = 1;
|
||||
gameState.balls.forEach(ball=>{
|
||||
const bt=ballTransparency(ball, gameState)
|
||||
gameState.balls.forEach((ball) => {
|
||||
const bt = ballTransparency(ball, gameState);
|
||||
if (bt < min) {
|
||||
min=bt
|
||||
min = bt;
|
||||
}
|
||||
})
|
||||
boost+=min*gameState.perks.transparency / 2
|
||||
});
|
||||
boost += (min * gameState.perks.transparency) / 2;
|
||||
}
|
||||
return Math.ceil(Math.max(gameState.combo,gameState.lastCombo) * boost)
|
||||
|
||||
return Math.ceil(Math.max(gameState.combo, gameState.lastCombo) * boost);
|
||||
}
|
||||
|
||||
export function miniMarkDown(md: string) {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { baseCombo, forEachLiveOne, liveCount } from "./gameStateMutators";
|
||||
import {
|
||||
ballTransparency,
|
||||
brickCenterX,
|
||||
brickCenterY,
|
||||
currentLevelInfo,
|
||||
|
@ -18,8 +17,10 @@ import { t } from "./i18n/i18n";
|
|||
import { gameState, lastMeasuredFPS, startWork } from "./game";
|
||||
import { isOptionOn } from "./options";
|
||||
import {
|
||||
ballTransparency,
|
||||
catchRateBest,
|
||||
catchRateGood, coinsBoostedCombo,
|
||||
catchRateGood,
|
||||
coinsBoostedCombo,
|
||||
levelTimeBest,
|
||||
levelTimeGood,
|
||||
missesBest,
|
||||
|
@ -75,12 +76,11 @@ export function render(gameState: GameState) {
|
|||
}
|
||||
|
||||
const catchRate = gameState.levelSpawnedCoins
|
||||
?
|
||||
(gameState.score - gameState.levelStartScore) /
|
||||
? (gameState.score - gameState.levelStartScore) /
|
||||
(gameState.levelSpawnedCoins || 1)
|
||||
// (gameState.levelSpawnedCoins - gameState.levelLostCoins) /
|
||||
: // (gameState.levelSpawnedCoins - gameState.levelLostCoins) /
|
||||
// gameState.levelSpawnedCoins
|
||||
: 1;
|
||||
1;
|
||||
startWork("render:scoreDisplay");
|
||||
scoreDisplay.innerHTML =
|
||||
(isOptionOn("show_fps") || gameState.startParams.computer_controlled
|
||||
|
@ -440,7 +440,7 @@ export function render(gameState: GameState) {
|
|||
);
|
||||
|
||||
startWork("render:combotext");
|
||||
const spawns=coinsBoostedCombo(gameState)
|
||||
const spawns = coinsBoostedCombo(gameState);
|
||||
if (spawns > 1) {
|
||||
ctx.globalCompositeOperation = "source-over";
|
||||
ctx.globalAlpha = 1;
|
||||
|
@ -523,7 +523,6 @@ export function render(gameState: GameState) {
|
|||
1,
|
||||
);
|
||||
} else {
|
||||
|
||||
if (gameState.perks.left_is_lava < 2)
|
||||
drawStraightLine(
|
||||
ctx,
|
||||
|
|
|
@ -14,7 +14,7 @@ try {
|
|||
warnedUserAboutLSIssue = true;
|
||||
toast(`Storage issue : ${(e as Error)?.message}`);
|
||||
}
|
||||
console.warn('Reading '+key,e);
|
||||
console.warn("Reading " + key, e);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
|
|
|
@ -189,7 +189,8 @@ export const rawUpgrades = [
|
|||
id: "smaller_puck",
|
||||
max: 2,
|
||||
name: t("upgrades.smaller_puck.name"),
|
||||
help: (lvl: number) => t("upgrades.smaller_puck.tooltip", {percent:50*lvl}),
|
||||
help: (lvl: number) =>
|
||||
t("upgrades.smaller_puck.tooltip", { percent: 50 * lvl }),
|
||||
fullHelp: t("upgrades.smaller_puck.verbose_description"),
|
||||
},
|
||||
{
|
||||
|
@ -725,7 +726,6 @@ export const rawUpgrades = [
|
|||
requires: "",
|
||||
threshold: 175000,
|
||||
gift: false,
|
||||
|
||||
id: "limitless",
|
||||
max: 1,
|
||||
name: t("upgrades.limitless.name"),
|
||||
|
@ -828,8 +828,7 @@ export const rawUpgrades = [
|
|||
id: "buoy",
|
||||
max: 3,
|
||||
name: t("upgrades.buoy.name"),
|
||||
help: (lvl: number) =>
|
||||
t("upgrades.buoy.tooltip", { duration: lvl * 0.5 }),
|
||||
help: (lvl: number) => t("upgrades.buoy.tooltip", { duration: lvl * 0.5 }),
|
||||
fullHelp: t("upgrades.buoy.verbose_description"),
|
||||
},
|
||||
{
|
||||
|
@ -849,7 +848,8 @@ export const rawUpgrades = [
|
|||
id: "three_cushion",
|
||||
max: 1,
|
||||
name: t("upgrades.three_cushion.name"),
|
||||
help: (lvl:number) =>t("upgrades.three_cushion.tooltip",{max:lvl*3}),
|
||||
help: (lvl: number) =>
|
||||
t("upgrades.three_cushion.tooltip", { max: lvl * 3 }),
|
||||
fullHelp: t("upgrades.three_cushion.verbose_description"),
|
||||
},
|
||||
{
|
||||
|
@ -862,5 +862,4 @@ export const rawUpgrades = [
|
|||
help: (lvl: number) => t("upgrades.sticky_coins.tooltip"),
|
||||
fullHelp: t("upgrades.sticky_coins.verbose_description"),
|
||||
},
|
||||
|
||||
] as const;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue