Build 29068563

This commit is contained in:
Renan LE CARO 2025-04-08 14:03:38 +02:00
parent 6ef13f2d19
commit df8bfbb350
25 changed files with 384 additions and 336 deletions

View file

@ -39,11 +39,13 @@ New players get confused as to which upgrades they have and why a side became re
## To do ## To do
- As soon as level condition is reached, lock it in and tell the user
- change fortunate ball to work more like coin magnet, carrying the balls around to catch them at next puck bounce - change fortunate ball to work more like coin magnet, carrying the balls around to catch them at next puck bounce
## Done ## Done
- review the "next unlocks" in score and game over
- As soon as upgrade condition is reached, toast
- As soon as level condition is reached, lock it in and tell the user
- extra life only saves your last ball, max 7 instead of 3 - extra life only saves your last ball, max 7 instead of 3
- Don't use "RAZ" in French explanations. - Don't use "RAZ" in French explanations.
- explain ghost coin's slow down effect - explain ghost coin's slow down effect

View file

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

File diff suppressed because one or more lines are too long

217
dist/index.html vendored

File diff suppressed because one or more lines are too long

View file

@ -1,5 +1,5 @@
// The version of the cache. // The version of the cache.
const VERSION = "29068232"; const VERSION = "29068563";
// The name of the cache // The name of the cache
const CACHE_NAME = `breakout-71-${VERSION}`; const CACHE_NAME = `breakout-71-${VERSION}`;

25
src/addToTotalScore.ts Normal file
View file

@ -0,0 +1,25 @@
import { GameState } from "./types";
import { icons, upgrades } from "./loadGameData";
import { schedulGameSound } from "./gameStateMutators";
import { toast } from "./toast";
import { t } from "./i18n/i18n";
import { getTotalScore, setSettingValue } from "./settings";
export function addToTotalScore(gameState: GameState, points: number) {
if (gameState.creative) return;
const pastScore = getTotalScore();
const newScore = pastScore + points;
setSettingValue("breakout_71_total_score", newScore);
// Check unlocked upgrades
upgrades.forEach((u) => {
if (u.threshold > pastScore && u.threshold <= newScore) {
schedulGameSound(gameState, "colorChange", 0, 1);
toast(
icons["icon:" + u.id] +
"<strong>" +
t("gameOver.unlocked_perk") +
"</strong>",
);
}
});
}

View file

@ -442,8 +442,8 @@
}, },
{ {
"name": "icon:base_combo", "name": "icon:base_combo",
"size": 5, "size": 7,
"bricks": "ttttttytytttttttytytttttt", "bricks": "________bbbbb__bybyb__bbbbb__bybyb__bbbbb________",
"svg": null "svg": null
}, },
{ {

View file

@ -1 +1 @@
"29068232" "29068563"

View file

@ -513,7 +513,6 @@ h2.histogram-title strong {
} }
} }
.toast { .toast {
position: fixed; position: fixed;
left: 0; left: 0;
@ -527,20 +526,18 @@ h2.histogram-title strong {
border-radius: 2px; border-radius: 2px;
padding-right: 10px; padding-right: 10px;
pointer-events: none; pointer-events: none;
animation: toast 800ms forwards; animation: toast forwards;
} }
@keyframes toast { @keyframes toast {
0%{ 0%,
100% {
opacity: 0; opacity: 0;
transform:translate(-20px, 0); transform: translate(-20px, -20px) scale(0.5);
} }
10%,90%{ 10%,
90% {
opacity: 0.8; opacity: 0.8;
transform: none; transform: none;
} }
100%{
opacity: 0;
transform:translate(20px, 0);
}
} }

View file

@ -298,8 +298,8 @@ export async function openUpgradesPicker(gameState: GameState) {
`, `,
...actions, ...actions,
getNearestUnlockHTML(gameState),
pickedUpgradesHTMl(gameState), pickedUpgradesHTMl(gameState),
getNearestUnlockHTML(gameState),
`<div id="level-recording-container"></div>`, `<div id="level-recording-container"></div>`,
], ],
@ -424,7 +424,7 @@ setInterval(() => {
}, 1000); }, 1000);
setInterval(() => { setInterval(() => {
monitorLevelsUnlocks(gameState) monitorLevelsUnlocks(gameState);
}, 500); }, 500);
window.addEventListener("visibilitychange", () => { window.addEventListener("visibilitychange", () => {

View file

@ -31,11 +31,7 @@ import {
import { t } from "./i18n/i18n"; import { t } from "./i18n/i18n";
import { icons } from "./loadGameData"; import { icons } from "./loadGameData";
import { import { getCurrentMaxCoins, getCurrentMaxParticles } from "./settings";
addToTotalScore,
getCurrentMaxCoins,
getCurrentMaxParticles,
} from "./settings";
import { background } from "./render"; import { background } from "./render";
import { gameOver } from "./gameOver"; import { gameOver } from "./gameOver";
import { import {
@ -50,6 +46,7 @@ import {
import { stopRecording } from "./recording"; import { stopRecording } from "./recording";
import { isOptionOn } from "./options"; import { isOptionOn } from "./options";
import { clamp, comboKeepingRate } from "./pure_functions"; import { clamp, comboKeepingRate } from "./pure_functions";
import { addToTotalScore } from "./addToTotalScore";
export function setMousePos(gameState: GameState, x: number) { export function setMousePos(gameState: GameState, x: number) {
gameState.puckPosition = x; gameState.puckPosition = x;
@ -1508,7 +1505,8 @@ export function ballTick(gameState: GameState, ball: Ball, delta: number) {
ball.y > ylimit && ball.y > ylimit &&
ball.vy > 0 && ball.vy > 0 &&
(ballIsUnderPuck || (ballIsUnderPuck ||
(gameState.balls.length<2 && gameState.perks.extra_life && (gameState.balls.length < 2 &&
gameState.perks.extra_life &&
ball.y > ylimit + gameState.puckHeight / 2)) ball.y > ylimit + gameState.puckHeight / 2))
) { ) {
if (ballIsUnderPuck) { if (ballIsUnderPuck) {

View file

@ -1,9 +1,18 @@
import {Ball, GameState, Level, PerkId, PerksMap, RunHistoryItem, UpgradeLike,} from "./types"; import {
Ball,
GameState,
Level,
PerkId,
PerksMap,
RunHistoryItem,
UpgradeLike,
} from "./types";
import { icons, upgrades } from "./loadGameData"; import { icons, upgrades } from "./loadGameData";
import { t } from "./i18n/i18n"; import { t } from "./i18n/i18n";
import { clamp } from "./pure_functions"; import { clamp } from "./pure_functions";
import { rawUpgrades } from "./upgrades"; import { rawUpgrades } from "./upgrades";
import { hashCode } from "./getLevelBackground"; import { hashCode } from "./getLevelBackground";
import { getTotalScore } from "./settings";
export function describeLevel(level: Level) { export function describeLevel(level: Level) {
let bricks = 0, let bricks = 0,
@ -74,7 +83,7 @@ export function getRowColIndex(gameState: GameState, row: number, col: number) {
export function getPossibleUpgrades(gameState: GameState) { export function getPossibleUpgrades(gameState: GameState) {
return upgrades return upgrades
.filter((u) => gameState.totalScoreAtRunStart >= u.threshold) .filter((u) => getTotalScore() >= u.threshold)
.filter((u) => !u?.requires || gameState.perks[u?.requires]); .filter((u) => !u?.requires || gameState.perks[u?.requires]);
} }
@ -287,7 +296,7 @@ function isExcluded(id:PerkId){
if (u.requires) excluded.add(u.requires); if (u.requires) excluded.add(u.requires);
}); });
} }
return excluded.has(id) return excluded.has(id);
} }
export function getLevelUnlockCondition(levelIndex: number) { export function getLevelUnlockCondition(levelIndex: number) {
@ -297,7 +306,6 @@ export function getLevelUnlockCondition(levelIndex: number) {
minScore = Math.max(-1000 + 100 * levelIndex, 0); minScore = Math.max(-1000 + 100 * levelIndex, 0);
if (levelIndex > 20) { if (levelIndex > 20) {
const possibletargets = rawUpgrades const possibletargets = rawUpgrades
.slice(0, Math.floor(levelIndex / 2)) .slice(0, Math.floor(levelIndex / 2))
.map((u) => u) .map((u) => u)

View file

@ -2262,21 +2262,6 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>continue_to_unlock</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> <concept_node>
<name>get_upgrades_to_unlock</name> <name>get_upgrades_to_unlock</name>
<description/> <description/>
@ -2337,21 +2322,6 @@
</translation> </translation>
</translations> </translations>
</concept_node> </concept_node>
<concept_node>
<name>title_looped</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>upcoming_levels</name> <name>upcoming_levels</name>
<description/> <description/>

View file

@ -20,7 +20,7 @@
"gameOver.stats.level_reached": "Level reached", "gameOver.stats.level_reached": "Level reached",
"gameOver.stats.total_score": "Total score", "gameOver.stats.total_score": "Total score",
"gameOver.stats.upgrades_applied": "Upgrades applied", "gameOver.stats.upgrades_applied": "Upgrades applied",
"gameOver.unlocked_perk": "You just unlocked a perk", "gameOver.unlocked_perk": "Upgrade unlocked",
"gameOver.unlocked_perk_plural": "You just unlocked {{count}} perks", "gameOver.unlocked_perk_plural": "You just unlocked {{count}} perks",
"gameOver.win.summary": "This game is over. You stashed {{score}} coins. ", "gameOver.win.summary": "This game is over. You stashed {{score}} coins. ",
"gameOver.win.title": "You completed this game", "gameOver.win.title": "You completed this game",
@ -143,13 +143,11 @@
"play.stats.levelMisses": "Missed shots, where you hit nothing", "play.stats.levelMisses": "Missed shots, where you hit nothing",
"play.stats.levelTime": "Level time", "play.stats.levelTime": "Level time",
"play.stats.levelWallBounces": "Wall bounces", "play.stats.levelWallBounces": "Wall bounces",
"score_panel.close_to_unlock": "You could unlock a level at the end of this run:", "score_panel.close_to_unlock": "Next level unlock :",
"score_panel.continue_to_unlock": "You are about to unlock level \"{{level}}\"",
"score_panel.get_upgrades_to_unlock": "Get {{missingUpgrades}} and score {{points}} more points to unlock level \"{{level}}\"", "score_panel.get_upgrades_to_unlock": "Get {{missingUpgrades}} and score {{points}} more points to unlock level \"{{level}}\"",
"score_panel.rerolls_count": "You have accumulated {{rerolls}} rerolls", "score_panel.rerolls_count": "You have accumulated {{rerolls}} rerolls",
"score_panel.score_to_unlock": "Score {{points}} more points to unlock level \"{{level}}\"", "score_panel.score_to_unlock": "Score {{points}} more points to unlock level \"{{level}}\"",
"score_panel.title": "{{score}} points at level {{level}}/{{max}} ", "score_panel.title": "{{score}} points at level {{level}}/{{max}} ",
"score_panel.title_looped": "{{score}} points at level {{level}}/{{max}} of loop {{loop}}",
"score_panel.upcoming_levels": "Upcoming levels :", "score_panel.upcoming_levels": "Upcoming levels :",
"score_panel.upgrades_picked": "Upgrades picked so far : ", "score_panel.upgrades_picked": "Upgrades picked so far : ",
"unlocks.greyed_out_help": "The grayed out upgrades can be unlocked by increasing your total score. The total score increases every time you score in game, outside of test runs.", "unlocks.greyed_out_help": "The grayed out upgrades can be unlocked by increasing your total score. The total score increases every time you score in game, outside of test runs.",

View file

@ -20,7 +20,7 @@
"gameOver.stats.level_reached": "Niveau atteint", "gameOver.stats.level_reached": "Niveau atteint",
"gameOver.stats.total_score": "Score total", "gameOver.stats.total_score": "Score total",
"gameOver.stats.upgrades_applied": "Mises à jour appliquées", "gameOver.stats.upgrades_applied": "Mises à jour appliquées",
"gameOver.unlocked_perk": "Vous avez débloqué une amélioration", "gameOver.unlocked_perk": "Amélioration débloquée",
"gameOver.unlocked_perk_plural": "Vous avez débloqué {{count}} améliorations", "gameOver.unlocked_perk_plural": "Vous avez débloqué {{count}} améliorations",
"gameOver.win.summary": "Cette partie est terminée. Vous avez accumulé {{score}} pièces. ", "gameOver.win.summary": "Cette partie est terminée. Vous avez accumulé {{score}} pièces. ",
"gameOver.win.title": "Vous avez terminé cette partie", "gameOver.win.title": "Vous avez terminé cette partie",
@ -143,13 +143,11 @@
"play.stats.levelMisses": "Tirs ratés, ou vous n'avez touché aucune brique", "play.stats.levelMisses": "Tirs ratés, ou vous n'avez touché aucune brique",
"play.stats.levelTime": "Durée du niveau", "play.stats.levelTime": "Durée du niveau",
"play.stats.levelWallBounces": "Rebonds sur les murs", "play.stats.levelWallBounces": "Rebonds sur les murs",
"score_panel.close_to_unlock": "Vous pourriez débloquer un niveau à la fin de cette partie :", "score_panel.close_to_unlock": "Prochain niveau débloqué : ",
"score_panel.continue_to_unlock": "Vous êtes sur le point de débloquer le niveau « {{level}} »", "score_panel.get_upgrades_to_unlock": "Obtenez {{missingUpgrades}} et attrapez {{points}} pièces supplémentaires pour débloquer le niveau « {{level}} »",
"score_panel.get_upgrades_to_unlock": "Obtenez {{missingUpgrades}} et marquez {{points}} points supplémentaires pour débloquer le niveau « {{level}} »",
"score_panel.rerolls_count": "Vous avez accumulé {{rerolls}} rerolls", "score_panel.rerolls_count": "Vous avez accumulé {{rerolls}} rerolls",
"score_panel.score_to_unlock": "Marquez {{points}} points supplémentaires pour débloquer le niveau « {{level}} »", "score_panel.score_to_unlock": "Attrapez {{points}} pièces supplémentaires pour débloquer le niveau « {{level}} »",
"score_panel.title": "{{score}} points au niveau {{level}}/{{max}} ", "score_panel.title": "{{score}} points au niveau {{level}}/{{max}} ",
"score_panel.title_looped": "{{score}} points au niveau {{level}}/{{max}} ",
"score_panel.upcoming_levels": "Niveaux de la parties : ", "score_panel.upcoming_levels": "Niveaux de la parties : ",
"score_panel.upgrades_picked": "Améliorations choisies jusqu'à présent :", "score_panel.upgrades_picked": "Améliorations choisies jusqu'à présent :",
"unlocks.greyed_out_help": "Les éléments grisées peuvent être débloquées en augmentant votre score total. Le score total augmente à chaque fois que vous marquez des points dans le jeu, en dehors des parties de test.", "unlocks.greyed_out_help": "Les éléments grisées peuvent être débloquées en augmentant votre score total. Le score total augmente à chaque fois que vous marquez des points dans le jeu, en dehors des parties de test.",

View file

@ -113,15 +113,17 @@ migrate("set_breakout_71_unlocked_levels"+_appVersion, () => {
localStorage.getItem("breakout_71_unlocked_levels") || "[]", localStorage.getItem("breakout_71_unlocked_levels") || "[]",
) as string[]; ) as string[];
allLevels.filter((l,li)=>!reasonLevelIsLocked(li,runsHistory,false)).forEach(l=>{ allLevels
.filter((l, li) => !reasonLevelIsLocked(li, runsHistory, false))
.forEach((l) => {
if (!breakout_71_unlocked_levels.includes(l.name)) { if (!breakout_71_unlocked_levels.includes(l.name)) {
breakout_71_unlocked_levels.push(l.name) breakout_71_unlocked_levels.push(l.name);
} }
}) });
localStorage.setItem("breakout_71_unlocked_levels", JSON.stringify(breakout_71_unlocked_levels)); localStorage.setItem(
"breakout_71_unlocked_levels",
JSON.stringify(breakout_71_unlocked_levels),
);
}); });
afterMigration();
afterMigration()

View file

@ -7,35 +7,44 @@ import {t} from "./i18n/i18n";
import { toast } from "./toast"; import { toast } from "./toast";
import { schedulGameSound } from "./gameStateMutators"; import { schedulGameSound } from "./gameStateMutators";
let list: {minScore: number, forbidden: UpgradeLike[], required: UpgradeLike[]}[] ; let list: {
let unlocked=new Set(getSettingValue('breakout_71_unlocked_levels',[]) as string[]) minScore: number;
forbidden: UpgradeLike[];
required: UpgradeLike[];
}[];
let unlocked = new Set(
getSettingValue("breakout_71_unlocked_levels", []) as string[],
);
export function monitorLevelsUnlocks(gameState: GameState) { export function monitorLevelsUnlocks(gameState: GameState) {
if (gameState.creative) return; if (gameState.creative) return;
if (!list) { if (!list) {
list = allLevels.map((l, li) => ({ list = allLevels.map((l, li) => ({
name:l.name,li,l, name: l.name,
...getLevelUnlockCondition(li) li,
})) l,
...getLevelUnlockCondition(li),
}));
} }
list.forEach(({ name, minScore, forbidden, required, l }) => { list.forEach(({ name, minScore, forbidden, required, l }) => {
// Already unlocked // Already unlocked
if(unlocked.has(name)) return if (unlocked.has(name)) return;
// Score not reached yet // Score not reached yet
if(gameState.score<minScore) return if (gameState.score < minScore) return;
// We are missing a required perk // We are missing a required perk
if(required.find(id=>!gameState.perks[id])) return; if (required.find((id) => !gameState.perks[id])) return;
// We have a forbidden perk // We have a forbidden perk
if(forbidden.find(id=>gameState.perks[id])) return; if (forbidden.find((id) => gameState.perks[id])) return;
// Level just got unlocked // Level just got unlocked
unlocked.add(name) unlocked.add(name);
setSettingValue('breakout_71_unlocked_levels', getSettingValue('breakout_71_unlocked_levels',[]).concat([name])) setSettingValue(
"breakout_71_unlocked_levels",
getSettingValue("breakout_71_unlocked_levels", []).concat([name]),
);
toast(icons[name]+'<strong>'+t('unlocks.just_unlocked')+'</strong>') toast(icons[name] + "<strong>" + t("unlocks.just_unlocked") + "</strong>");
schedulGameSound(gameState, 'colorChange', 0, 1) schedulGameSound(gameState, "colorChange", 0, 1);
});
})
} }

View file

@ -42,14 +42,13 @@ export function getRunLevels(
export function newGameState(params: RunParams): GameState { export function newGameState(params: RunParams): GameState {
const highScore = getHighScore(); const highScore = getHighScore();
const totalScoreAtRunStart=getTotalScore()
const perks = { ...makeEmptyPerksMap(upgrades), ...(params?.perks || {}) }; const perks = { ...makeEmptyPerksMap(upgrades), ...(params?.perks || {}) };
let randomGift: PerkId | undefined = undefined; let randomGift: PerkId | undefined = undefined;
if (!sumOfValues(perks)) { if (!sumOfValues(perks)) {
const giftable = upgrades.filter( const giftable = upgrades.filter(
(u) => totalScoreAtRunStart >= u.threshold && isStartingPerk(u), (u) => getTotalScore() >= u.threshold && isStartingPerk(u),
); );
randomGift = randomGift =
@ -108,7 +107,6 @@ export function newGameState(params: RunParams): GameState {
coinSize: 14, coinSize: 14,
puckHeight: 20, puckHeight: 20,
totalScoreAtRunStart,
pauseUsesDuringRun: 0, pauseUsesDuringRun: 0,
keyboardPuckSpeed: 0, keyboardPuckSpeed: 0,
lastTick: performance.now(), lastTick: performance.now(),

View file

@ -11,6 +11,8 @@ import {
import { getCreativeModeWarning, getHistory } from "./gameOver"; import { getCreativeModeWarning, getHistory } from "./gameOver";
import { pause } from "./game"; import { pause } from "./game";
import { allLevels, icons } from "./loadGameData"; import { allLevels, icons } from "./loadGameData";
import { firstWhere } from "./pure_functions";
import { getSettingValue, getTotalScore } from "./settings";
export async function openScorePanel(gameState: GameState) { export async function openScorePanel(gameState: GameState) {
pause(true); pause(true);
@ -37,31 +39,34 @@ export async function openScorePanel(gameState: GameState) {
export function getNearestUnlockHTML(gameState: GameState) { export function getNearestUnlockHTML(gameState: GameState) {
if (gameState.creative) return ""; if (gameState.creative) return "";
const unlockable = allLevels const unlocked = new Set(getSettingValue("breakout_71_unlocked_levels", []));
.map((l, li) => { const firstUnlockable = firstWhere(allLevels, (l, li) => {
if (unlocked.has(l.name)) return;
const reason = reasonLevelIsLocked(li, getHistory(), false);
if (!reason) return;
const { minScore, forbidden, required } = getLevelUnlockCondition(li); const { minScore, forbidden, required } = getLevelUnlockCondition(li);
const missing = required.filter((u) => !gameState?.perks?.[u.id]);
// we can't have a forbidden perk
if (forbidden.find((u) => gameState?.perks?.[u.id])) {
return;
}
// All required upgrades need to be unlocked
if (missing.find((u) => u.threshold > getTotalScore())) {
return;
}
return { return {
l, l,
li, li,
minScore, minScore,
forbidden, forbidden,
required, required,
missing: required.filter((u) => !gameState?.perks?.[u.id]), missing,
reason: reasonLevelIsLocked(li, getHistory(), false), reason,
}; };
}) });
.filter(
({ reason, forbidden, missing }) =>
// Level needs to be locked
reason &&
// we can't have a forbidden perk
!forbidden.find((u) => gameState?.perks?.[u.id]) &&
// All required upgrades need to be unlocked
!missing.find((u) => u.threshold > gameState.totalScoreAtRunStart),
);
const firstUnlockable =
unlockable.find(({ missing }) => !missing.length) || unlockable[0];
if (!firstUnlockable) return ""; if (!firstUnlockable) return "";
let missingPoints = firstUnlockable.minScore - gameState.score; let missingPoints = firstUnlockable.minScore - gameState.score;
@ -74,13 +79,9 @@ export function getNearestUnlockHTML(gameState: GameState) {
points: missingPoints, points: missingPoints,
level: firstUnlockable.l.name, level: firstUnlockable.l.name,
})) || })) ||
(missingPoints > 0 &&
t("score_panel.score_to_unlock", { t("score_panel.score_to_unlock", {
points: missingPoints, points: missingPoints,
level: firstUnlockable.l.name, level: firstUnlockable.l.name,
})) ||
t("score_panel.continue_to_unlock", {
level: firstUnlockable.l.name,
}); });
return ` return `

View file

@ -65,3 +65,13 @@ export function miniMarkDown(md: string) {
) )
.join("\n"); .join("\n");
} }
export function firstWhere<Input, Output>(
arr: Input[],
mapper: (item: Input, index: number) => Output | undefined,
): Output | undefined {
for (let i = 0; i < arr.length; i++) {
const result = mapper(arr[i], i);
if (typeof result !== "undefined") return result;
}
}

View file

@ -1,7 +1,5 @@
// Settings // Settings
import { GameState } from "./types";
let cachedSettings: { [key: string]: unknown } = {}; let cachedSettings: { [key: string]: unknown } = {};
export function getSettingValue<T>(key: string, defaultValue: T) { export function getSettingValue<T>(key: string, defaultValue: T) {
@ -29,11 +27,6 @@ export function getTotalScore() {
return getSettingValue("breakout_71_total_score", 0); return getSettingValue("breakout_71_total_score", 0);
} }
export function addToTotalScore(gameState: GameState, points: number) {
if (!gameState.creative)
setSettingValue("breakout_71_total_score", getTotalScore() + points);
}
export function getCurrentMaxCoins() { export function getCurrentMaxCoins() {
return Math.pow(2, getSettingValue("max_coins", 1)) * 200; return Math.pow(2, getSettingValue("max_coins", 1)) * 200;
} }

View file

@ -17,7 +17,6 @@ export function startingPerkMenuButton() {
}; };
} }
export function isStartingPerk(u: Upgrade): boolean { export function isStartingPerk(u: Upgrade): boolean {
return getSettingValue("start_with_" + u.id, u.giftable); return getSettingValue("start_with_" + u.id, u.giftable);
} }

View file

@ -1,8 +1,17 @@
let onScreen = 0;
export function toast(html) { export function toast(html) {
const div =document.createElement('div') const div = document.createElement("div");
div.classList='toast' div.classList = "toast";
div.innerHTML=html div.innerHTML = html;
document.body.appendChild(div) const lasts = 1500 + onScreen * 200;
// TOast div.style.animationDuration = lasts + "ms";
// setTimeout(()=>div.remove() , 500 ) div.style.top = 40 + onScreen * 50 + "px";
document.body.appendChild(div);
onScreen++;
setTimeout(() => {
div.remove();
onScreen--;
}, lasts);
} }

8
src/types.d.ts vendored
View file

@ -258,7 +258,6 @@ export type GameState = {
ballSize: number; ballSize: number;
coinSize: number; coinSize: number;
puckHeight: number; puckHeight: number;
totalScoreAtRunStart: number;
pauseUsesDuringRun: number; pauseUsesDuringRun: number;
keyboardPuckSpeed: number; keyboardPuckSpeed: number;
lastTick: number; lastTick: number;
@ -296,4 +295,9 @@ export type OptionDef = {
help: string; help: string;
}; };
export type OptionId = keyof typeof options; export type OptionId = keyof typeof options;
export type UpgradeLike = { id: PerkId; name: string; requires: string }; export type UpgradeLike = {
id: PerkId;
name: string;
requires: string;
threshold: number;
};

View file

@ -17,20 +17,9 @@ export const rawUpgrades = [
: t("upgrades.extra_life.help_plural", { lvl }), : t("upgrades.extra_life.help_plural", { lvl }),
fullHelp: t("upgrades.extra_life.fullHelp"), fullHelp: t("upgrades.extra_life.fullHelp"),
}, },
{
requires: "",
threshold: 0,
id: "streak_shots",
giftable: true,
max: 1,
name: t("upgrades.streak_shots.name"),
help: (lvl: number) => t("upgrades.streak_shots.help", { lvl }),
fullHelp: t("upgrades.streak_shots.fullHelp"),
},
{ {
requires: "", requires: "",
threshold: 0, threshold: 0,
id: "base_combo", id: "base_combo",
giftable: true, giftable: true,
@ -42,7 +31,6 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
threshold: 0, threshold: 0,
giftable: false, giftable: false,
id: "slow_down", id: "slow_down",
@ -53,7 +41,6 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
threshold: 0, threshold: 0,
giftable: false, giftable: false,
id: "bigger_puck", id: "bigger_puck",
@ -64,7 +51,6 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
threshold: 0, threshold: 0,
giftable: false, giftable: false,
id: "viscosity", id: "viscosity",
@ -76,8 +62,32 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
threshold: 50,
giftable: false,
id: "skip_last",
max: 7,
name: t("upgrades.skip_last.name"),
help: (lvl: number) =>
lvl == 1
? t("upgrades.skip_last.help")
: t("upgrades.skip_last.help_plural", { lvl }),
fullHelp: t("upgrades.skip_last.fullHelp"),
},
{
requires: "",
threshold: 100,
id: "streak_shots",
giftable: true,
max: 1,
name: t("upgrades.streak_shots.name"),
help: (lvl: number) => t("upgrades.streak_shots.help", { lvl }),
fullHelp: t("upgrades.streak_shots.fullHelp"),
},
threshold: 0, {
requires: "",
threshold: 200,
id: "left_is_lava", id: "left_is_lava",
giftable: true, giftable: true,
max: 1, max: 1,
@ -89,7 +99,7 @@ export const rawUpgrades = [
{ {
requires: "", requires: "",
threshold: 0, threshold: 300,
id: "right_is_lava", id: "right_is_lava",
giftable: true, giftable: true,
max: 1, max: 1,
@ -100,7 +110,7 @@ export const rawUpgrades = [
{ {
requires: "", requires: "",
threshold: 0, threshold: 400,
id: "top_is_lava", id: "top_is_lava",
giftable: true, giftable: true,
max: 1, max: 1,
@ -111,20 +121,6 @@ export const rawUpgrades = [
{ {
requires: "", requires: "",
threshold: 0,
giftable: false,
id: "skip_last",
max: 7,
name: t("upgrades.skip_last.name"),
help: (lvl: number) =>
lvl == 1
? t("upgrades.skip_last.help")
: t("upgrades.skip_last.help_plural", { lvl }),
fullHelp: t("upgrades.skip_last.fullHelp"),
},
{
requires: "",
threshold: 500, threshold: 500,
id: "telekinesis", id: "telekinesis",
giftable: true, giftable: true,
@ -138,8 +134,7 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
threshold: 700,
threshold: 1000,
giftable: false, giftable: false,
id: "coin_magnet", id: "coin_magnet",
max: 3, max: 3,
@ -153,7 +148,7 @@ export const rawUpgrades = [
{ {
requires: "", requires: "",
threshold: 1500, threshold: 800,
id: "multiball", id: "multiball",
giftable: true, giftable: true,
max: 6, max: 6,
@ -164,7 +159,7 @@ export const rawUpgrades = [
{ {
requires: "", requires: "",
threshold: 2000, threshold: 1000,
giftable: false, giftable: false,
id: "smaller_puck", id: "smaller_puck",
max: 2, max: 2,
@ -177,8 +172,7 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
threshold: 1500,
threshold: 3000,
id: "pierce", id: "pierce",
giftable: false, giftable: false,
max: 3, max: 3,
@ -189,7 +183,7 @@ export const rawUpgrades = [
{ {
requires: "", requires: "",
threshold: 4000, threshold: 2000,
id: "picky_eater", id: "picky_eater",
giftable: true, giftable: true,
max: 1, max: 1,
@ -200,7 +194,7 @@ export const rawUpgrades = [
{ {
requires: "", requires: "",
threshold: 5000, threshold: 2500,
giftable: false, giftable: false,
id: "metamorphosis", id: "metamorphosis",
max: 1, max: 1,
@ -211,7 +205,7 @@ export const rawUpgrades = [
{ {
requires: "", requires: "",
threshold: 6000, threshold: 3000,
id: "compound_interest", id: "compound_interest",
giftable: true, giftable: true,
max: 1, max: 1,
@ -221,7 +215,7 @@ export const rawUpgrades = [
}, },
{ {
requires: "", requires: "",
threshold: 7000, threshold: 4000,
id: "hot_start", id: "hot_start",
giftable: true, giftable: true,
max: 3, max: 3,
@ -236,7 +230,7 @@ export const rawUpgrades = [
{ {
requires: "", requires: "",
threshold: 9000, threshold: 6000,
id: "sapper", id: "sapper",
giftable: false, giftable: false,
max: 7, max: 7,
@ -250,7 +244,7 @@ export const rawUpgrades = [
{ {
requires: "", requires: "",
threshold: 11000, threshold: 9000,
id: "bigger_explosions", id: "bigger_explosions",
giftable: false, giftable: false,
max: 1, max: 1,