mirror of
https://gitlab.com/lecarore/breakout71.git
synced 2025-04-22 21:16:14 -04:00
wip
This commit is contained in:
parent
2f51f83514
commit
42abc6bc01
25 changed files with 1514 additions and 1328 deletions
|
@ -1,5 +1,5 @@
|
|||
// The version of the cache.
|
||||
const VERSION = "29064070";
|
||||
const VERSION = "29065772";
|
||||
|
||||
// The name of the cache
|
||||
const CACHE_NAME = `breakout-71-${VERSION}`;
|
||||
|
|
|
@ -2,8 +2,13 @@ import { GameState, Level, PerkId, Upgrade } from "./types";
|
|||
import { allLevels, icons, upgrades } from "./loadGameData";
|
||||
import { t } from "./i18n/i18n";
|
||||
import { getSettingValue, getTotalScore, setSettingValue } from "./settings";
|
||||
import {confirmRestart, creativeModeThreshold, gameState, restart} from "./game";
|
||||
import {asyncAlert, requiredAsyncAlert} from "./asyncAlert";
|
||||
import {
|
||||
confirmRestart,
|
||||
creativeModeThreshold,
|
||||
gameState,
|
||||
restart,
|
||||
} from "./game";
|
||||
import { asyncAlert, requiredAsyncAlert } from "./asyncAlert";
|
||||
import { describeLevel, highScoreText, sumOfValues } from "./game_utils";
|
||||
|
||||
export function creativeMode(gameState: GameState) {
|
||||
|
@ -17,17 +22,14 @@ export function creativeMode(gameState: GameState) {
|
|||
t("lab.help"),
|
||||
disabled: getTotalScore() < creativeModeThreshold,
|
||||
async value() {
|
||||
openCreativeModePerksPicker()
|
||||
|
||||
openCreativeModePerksPicker();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export async function openCreativeModePerksPicker() {
|
||||
|
||||
|
||||
let creativeModePerks: Partial<{ [id in PerkId]: number }> = getSettingValue(
|
||||
"creativeModePerks" ,
|
||||
"creativeModePerks",
|
||||
{},
|
||||
),
|
||||
choice: Upgrade | Level | "reset" | void;
|
||||
|
@ -55,10 +57,7 @@ export async function openCreativeModePerksPicker() {
|
|||
.map((u) => ({
|
||||
icon: u.icon,
|
||||
text: u.name,
|
||||
help:
|
||||
(creativeModePerks[u.id] || 0) +
|
||||
"/" +
|
||||
u.max,
|
||||
help: (creativeModePerks[u.id] || 0) + "/" + (u.max+creativeModePerks.limitless),
|
||||
value: u,
|
||||
className: creativeModePerks[u.id]
|
||||
? "sandbox"
|
||||
|
@ -80,18 +79,16 @@ export async function openCreativeModePerksPicker() {
|
|||
creativeModePerks[u.id] = 0;
|
||||
});
|
||||
} else if ("bricks" in choice) {
|
||||
setSettingValue("creativeModePerks" , creativeModePerks);
|
||||
if (await confirmRestart(gameState)) {
|
||||
restart({ perks:creativeModePerks, level:choice.name});
|
||||
setSettingValue("creativeModePerks", creativeModePerks);
|
||||
if (await confirmRestart(gameState)) {
|
||||
restart({ perks: creativeModePerks, level: choice.name });
|
||||
}
|
||||
return
|
||||
return;
|
||||
} else if (choice) {
|
||||
creativeModePerks[choice.id] =
|
||||
((creativeModePerks[choice.id] || 0) + 1) %
|
||||
(choice.max +1);
|
||||
}else{
|
||||
return
|
||||
((creativeModePerks[choice.id] || 0) + 1) % (choice.max + 1 + creativeModePerks.limitless);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1274,4 +1274,4 @@
|
|||
"svg": null,
|
||||
"color": ""
|
||||
}
|
||||
]
|
||||
]
|
||||
|
|
|
@ -21,4 +21,4 @@
|
|||
"k": "#618227",
|
||||
"e": "#e1c8b4",
|
||||
"l": "#9b9fa4"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
"29064070"
|
||||
"29065772"
|
||||
|
|
|
@ -158,7 +158,8 @@ body:not(.has-alert-open) #popup {
|
|||
&[disabled] {
|
||||
opacity: 0.5;
|
||||
filter: saturate(0);
|
||||
pointer-events: none;
|
||||
//pointer-events: none;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
& > div {
|
||||
|
|
73
src/game.ts
73
src/game.ts
|
@ -17,6 +17,7 @@ import {
|
|||
describeLevel,
|
||||
getRowColIndex,
|
||||
highScoreText,
|
||||
reasonLevelIsLocked,
|
||||
levelsListHTMl,
|
||||
max_levels,
|
||||
pickedUpgradesHTMl,
|
||||
|
@ -72,7 +73,7 @@ import { creativeMode } from "./creative";
|
|||
import { setupTooltips } from "./tooltip";
|
||||
import { startingPerkMenuButton } from "./startingPerks";
|
||||
import "./migrations";
|
||||
import {getCreativeModeWarning} from "./gameOver";
|
||||
import { getCreativeModeWarning, getHistory } from "./gameOver";
|
||||
|
||||
export async function play() {
|
||||
if (await applyFullScreenChoice()) return;
|
||||
|
@ -441,16 +442,16 @@ document.addEventListener("visibilitychange", () => {
|
|||
async function openScorePanel() {
|
||||
pause(true);
|
||||
|
||||
const cb = await asyncAlert({
|
||||
title: t("score_panel.title", {
|
||||
score: gameState.score,
|
||||
level: gameState.currentLevel + 1,
|
||||
max: max_levels(gameState),
|
||||
}),
|
||||
|
||||
await asyncAlert({
|
||||
title: t("score_panel.title", {
|
||||
score: gameState.score,
|
||||
level: gameState.currentLevel + 1,
|
||||
max: max_levels(gameState),
|
||||
}),
|
||||
|
||||
content: [
|
||||
|
||||
getCreativeModeWarning(gameState),
|
||||
getCreativeModeWarning(gameState),
|
||||
pickedUpgradesHTMl(gameState),
|
||||
levelsListHTMl(gameState, gameState.currentLevel),
|
||||
gameState.rerolls
|
||||
|
@ -485,7 +486,7 @@ export async function openMainMenu() {
|
|||
help: highScoreText() || t("main_menu.normal_help"),
|
||||
value: () => {
|
||||
restart({
|
||||
levelToAvoid: currentLevelInfo(gameState).name
|
||||
levelToAvoid: currentLevelInfo(gameState).name,
|
||||
});
|
||||
},
|
||||
},
|
||||
|
@ -816,6 +817,8 @@ async function applyFullScreenChoice() {
|
|||
|
||||
async function openUnlocksList() {
|
||||
const ts = getTotalScore();
|
||||
const hintField = isOptionOn("mobile-mode") ? "help" : "tooltip";
|
||||
|
||||
const upgradeActions = upgrades
|
||||
.sort((a, b) => a.threshold - b.threshold)
|
||||
.map(({ name, id, threshold, icon, help }) => ({
|
||||
|
@ -823,39 +826,40 @@ async function openUnlocksList() {
|
|||
disabled: ts < threshold,
|
||||
value: { perks: { [id]: 1 } } as RunParams,
|
||||
icon,
|
||||
tooltip: help(1),
|
||||
[hintField]:
|
||||
ts < threshold
|
||||
? t("unlocks.minTotalScore", { score: threshold })
|
||||
: help(1),
|
||||
}));
|
||||
|
||||
const levelActions = allLevels
|
||||
.sort((a, b) => a.threshold - b.threshold)
|
||||
.map((l) => {
|
||||
const available = ts >= l.threshold;
|
||||
return {
|
||||
text: l.name,
|
||||
disabled: !available,
|
||||
value: { level: l.name } as RunParams,
|
||||
icon: icons[l.name],
|
||||
tooltip: describeLevel(l),
|
||||
};
|
||||
});
|
||||
const levelActions = allLevels.map((l, li) => {
|
||||
const problem = reasonLevelIsLocked(li, getHistory());
|
||||
return {
|
||||
text: l.name,
|
||||
disabled: !!problem,
|
||||
value: { level: l.name } as RunParams,
|
||||
icon: icons[l.name],
|
||||
[hintField]: problem || describeLevel(l),
|
||||
};
|
||||
});
|
||||
|
||||
const tryOn = await asyncAlert<RunParams>({
|
||||
title: t("unlocks.title_upgrades", {
|
||||
unlocked:upgradeActions.filter((a) => !a.disabled).length,
|
||||
out_of:upgradeActions.length
|
||||
unlocked: upgradeActions.filter((a) => !a.disabled).length,
|
||||
out_of: upgradeActions.length,
|
||||
}),
|
||||
content: [
|
||||
`<p>${t("unlocks.intro", { ts })}
|
||||
${upgradeActions.find(u=>u.disabled)? t("unlocks.greyed_out_help") : ""}</p> `,
|
||||
${upgradeActions.find((u) => u.disabled) ? t("unlocks.greyed_out_help") : ""}</p> `,
|
||||
...upgradeActions,
|
||||
t("unlocks.level",{
|
||||
unlocked:levelActions.filter((a) => !a.disabled).length,
|
||||
out_of:levelActions.length
|
||||
}),
|
||||
t("unlocks.level", {
|
||||
unlocked: levelActions.filter((a) => !a.disabled).length,
|
||||
out_of: levelActions.length,
|
||||
}),
|
||||
...levelActions,
|
||||
],
|
||||
allowClose: true,
|
||||
className: "actionsAsGrid",
|
||||
className: isOptionOn("mobile-mode") ? "" : "actionsAsGrid",
|
||||
});
|
||||
if (tryOn) {
|
||||
if (await confirmRestart(gameState)) {
|
||||
|
@ -865,10 +869,9 @@ async function openUnlocksList() {
|
|||
}
|
||||
|
||||
export async function confirmRestart(gameState) {
|
||||
|
||||
if (!gameState.currentLevel) return true;
|
||||
if (alertsOpen) return true;
|
||||
pause(true)
|
||||
pause(true);
|
||||
return asyncAlert({
|
||||
title: t("confirmRestart.title"),
|
||||
content: [
|
||||
|
@ -950,7 +953,7 @@ document.addEventListener("keyup", async (e) => {
|
|||
// When doing ctrl + R in dev to refresh, i don't want to instantly restart a run
|
||||
if (await confirmRestart(gameState)) {
|
||||
restart({
|
||||
levelToAvoid: currentLevelInfo(gameState).name
|
||||
levelToAvoid: currentLevelInfo(gameState).name,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
|
@ -968,7 +971,7 @@ export function restart(params: RunParams) {
|
|||
setLevel(gameState, 0);
|
||||
}
|
||||
|
||||
restart({ });
|
||||
restart({});
|
||||
|
||||
tick();
|
||||
setupTooltips();
|
||||
|
|
189
src/gameOver.ts
189
src/gameOver.ts
|
@ -1,35 +1,18 @@
|
|||
import {allLevels, appVersion, icons, upgrades} from "./loadGameData";
|
||||
import { allLevels, appVersion, icons, upgrades } from "./loadGameData";
|
||||
import { t } from "./i18n/i18n";
|
||||
import { GameState, RunHistoryItem } from "./types";
|
||||
import { gameState, pause, restart } from "./game";
|
||||
import { currentLevelInfo, findLast, pickedUpgradesHTMl } from "./game_utils";
|
||||
import {
|
||||
currentLevelInfo,
|
||||
describeLevel,
|
||||
findLast,
|
||||
pickedUpgradesHTMl,
|
||||
reasonLevelIsLocked,
|
||||
} from "./game_utils";
|
||||
import { getTotalScore } from "./settings";
|
||||
import { stopRecording } from "./recording";
|
||||
import { asyncAlert } from "./asyncAlert";
|
||||
|
||||
export function getUpgraderUnlockPoints() {
|
||||
let list = [] as { threshold: number; title: string }[];
|
||||
|
||||
upgrades.forEach((u) => {
|
||||
if (u.threshold) {
|
||||
list.push({
|
||||
threshold: u.threshold,
|
||||
title: u.name + " " + t("level_up.unlocked_perk"),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
allLevels.forEach((l) => {
|
||||
list.push({
|
||||
threshold: l.threshold,
|
||||
title: l.name + " " + t("level_up.unlocked_level"),
|
||||
});
|
||||
});
|
||||
|
||||
return list
|
||||
.filter((o) => o.threshold)
|
||||
.sort((a, b) => a.threshold - b.threshold);
|
||||
}
|
||||
import { rawUpgrades } from "./upgrades";
|
||||
|
||||
export function addToTotalPlayTime(ms: number) {
|
||||
try {
|
||||
|
@ -58,58 +41,33 @@ export function gameOver(title: string, intro: string) {
|
|||
return "animation-delay:" + animationDelay + "ms;";
|
||||
};
|
||||
// unlocks
|
||||
let unlocksInfo = "";
|
||||
|
||||
const endTs = getTotalScore();
|
||||
const startTs = endTs - gameState.score;
|
||||
const list = getUpgraderUnlockPoints();
|
||||
list
|
||||
.filter((u) => u.threshold > startTs && u.threshold < endTs)
|
||||
.forEach((u) => {
|
||||
unlocksInfo += `
|
||||
<p class="progress" >
|
||||
<span>${u.title}</span>
|
||||
<span class="progress_bar_part" style="${getDelay()}"></span>
|
||||
</p>
|
||||
`;
|
||||
});
|
||||
const previousUnlockAt =
|
||||
findLast(list, (u) => u.threshold <= endTs)?.threshold || 0;
|
||||
const nextUnlock = list.find((u) => u.threshold > endTs);
|
||||
|
||||
if (nextUnlock) {
|
||||
const total = nextUnlock?.threshold - previousUnlockAt;
|
||||
const done = endTs - previousUnlockAt;
|
||||
|
||||
intro += t("gameOver.next_unlock", {
|
||||
points: nextUnlock.threshold - endTs,
|
||||
});
|
||||
|
||||
const scaleX = (done / total).toFixed(2);
|
||||
unlocksInfo += `
|
||||
<p class="progress" >
|
||||
<span>${nextUnlock.title}</span>
|
||||
<span style="transform: scale(${scaleX},1);${getDelay()}" class="progress_bar_part"></span>
|
||||
</p>
|
||||
|
||||
`;
|
||||
list
|
||||
.slice(list.indexOf(nextUnlock) + 1)
|
||||
.slice(0, 3)
|
||||
.forEach((u) => {
|
||||
unlocksInfo += `
|
||||
<p class="progress" >
|
||||
<span>${u.title}</span>
|
||||
</p>
|
||||
`;
|
||||
});
|
||||
}
|
||||
|
||||
let unlockedItems = list.filter(
|
||||
(u) => u.threshold > startTs && u.threshold < endTs,
|
||||
const unlockedPerks = rawUpgrades.filter(
|
||||
(o) => o.threshold > startTs && o.threshold < endTs,
|
||||
);
|
||||
if (unlockedItems.length) {
|
||||
unlocksInfo += `<p>${t("gameOver.unlocked_count", { count: unlockedItems.length })} ${unlockedItems.map((u) => u.title).join(", ")}</p>`;
|
||||
}
|
||||
|
||||
let unlocksInfo = unlockedPerks.length
|
||||
? `
|
||||
|
||||
<h2>${unlockedPerks.length === 1 ? t("gameOver.unlocked_perk") : t("gameOver.unlocked_perk_plural", { count: unlockedPerks.length })}</h2>
|
||||
|
||||
${unlockedPerks
|
||||
.map(
|
||||
(u) => `
|
||||
<div class="upgrade used">
|
||||
${icons["icon:" + u.id]}
|
||||
<p>
|
||||
<strong>${u.name}</strong>
|
||||
${u.help(1)}
|
||||
</p>
|
||||
</div>
|
||||
`,
|
||||
)
|
||||
.join("\n")}
|
||||
`
|
||||
: "";
|
||||
|
||||
// Avoid the sad sound right as we restart a new games
|
||||
gameState.combo = 1;
|
||||
|
@ -118,47 +76,59 @@ export function gameOver(title: string, intro: string) {
|
|||
allowClose: true,
|
||||
title,
|
||||
content: [
|
||||
getCreativeModeWarning(gameState),
|
||||
getCreativeModeWarning(gameState),
|
||||
`
|
||||
<p>${intro}</p>
|
||||
<p>${t("gameOver.cumulative_total", { startTs, endTs })}</p>
|
||||
`,
|
||||
unlocksInfo,
|
||||
{
|
||||
icon:icons["icon:new_run"],
|
||||
icon: icons["icon:new_run"],
|
||||
value: null,
|
||||
text: t("gameOver.restart"),
|
||||
help: "",
|
||||
},
|
||||
`<div id="level-recording-container"></div>
|
||||
${pickedUpgradesHTMl(gameState)}
|
||||
${getHistograms(gameState)}
|
||||
`,
|
||||
`<div id="level-recording-container"></div>`,
|
||||
// pickedUpgradesHTMl(gameState),
|
||||
unlocksInfo,
|
||||
getHistograms(gameState),
|
||||
],
|
||||
}).then(() =>
|
||||
restart({
|
||||
levelToAvoid: currentLevelInfo(gameState).name
|
||||
levelToAvoid: currentLevelInfo(gameState).name,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
export function getCreativeModeWarning(gameState: GameState){
|
||||
if(gameState.creative){
|
||||
return '<p>'+t('gameOver.creative')+'</p>'
|
||||
export function getCreativeModeWarning(gameState: GameState) {
|
||||
if (gameState.creative) {
|
||||
return "<p>" + t("gameOver.creative") + "</p>";
|
||||
}
|
||||
return ''
|
||||
return "";
|
||||
}
|
||||
|
||||
let runsHistory = [];
|
||||
|
||||
try {
|
||||
runsHistory = JSON.parse(
|
||||
localStorage.getItem("breakout_71_runs_history") || "[]",
|
||||
) as RunHistoryItem[];
|
||||
} catch (e) {}
|
||||
export function getHistory() {
|
||||
return runsHistory;
|
||||
}
|
||||
|
||||
export function getHistograms(gameState: GameState) {
|
||||
if(gameState.creative) return ''
|
||||
if (gameState.creative) return "";
|
||||
let unlockedLevels = "";
|
||||
let runStats = "";
|
||||
try {
|
||||
// Stores only top 100 runs
|
||||
let runsHistory = JSON.parse(
|
||||
localStorage.getItem("breakout_71_runs_history") || "[]",
|
||||
) as RunHistoryItem[];
|
||||
|
||||
runsHistory.sort((a, b) => a.score - b.score).reverse();
|
||||
runsHistory = runsHistory.slice(0, 100);
|
||||
const locked = allLevels
|
||||
.map((l, li) => ({
|
||||
li,
|
||||
l,
|
||||
r: reasonLevelIsLocked(li, runsHistory),
|
||||
}))
|
||||
.filter((l) => l.r);
|
||||
|
||||
runsHistory.push({
|
||||
...gameState.runStatistics,
|
||||
|
@ -166,6 +136,30 @@ export function getHistograms(gameState: GameState) {
|
|||
appVersion,
|
||||
});
|
||||
|
||||
const unlocked = locked.filter(
|
||||
({ li }) => !reasonLevelIsLocked(li, runsHistory),
|
||||
);
|
||||
if (unlocked.length) {
|
||||
unlockedLevels = `
|
||||
|
||||
<h2>${unlocked.length === 1 ? t("unlocks.just_unlocked") : t("unlocks.just_unlocked_plural", { count: unlocked.length })}</h2>
|
||||
|
||||
${unlocked
|
||||
.map(
|
||||
({ l, r }) => `
|
||||
<div class="upgrade used">
|
||||
${icons[l.name]}
|
||||
<p>
|
||||
<strong>${l.name}</strong>
|
||||
${describeLevel(l)}
|
||||
</p>
|
||||
</div>
|
||||
`,
|
||||
)
|
||||
.join("\n")}
|
||||
`;
|
||||
}
|
||||
|
||||
// Generate some histogram
|
||||
|
||||
localStorage.setItem(
|
||||
|
@ -178,8 +172,7 @@ export function getHistograms(gameState: GameState) {
|
|||
getter: (hi: RunHistoryItem) => number,
|
||||
unit: string,
|
||||
) => {
|
||||
let values = runsHistory
|
||||
.map((h) => getter(h) || 0);
|
||||
let values = runsHistory.map((h) => getter(h) || 0);
|
||||
let min = Math.min(...values);
|
||||
let max = Math.max(...values);
|
||||
// No point
|
||||
|
@ -290,5 +283,5 @@ export function getHistograms(gameState: GameState) {
|
|||
} catch (e) {
|
||||
console.warn(e);
|
||||
}
|
||||
return runStats;
|
||||
return runStats + unlockedLevels;
|
||||
}
|
||||
|
|
|
@ -131,7 +131,7 @@ export function normalizeGameState(gameState: GameState) {
|
|||
gameState.gameZoneWidth / 12 / 10 +
|
||||
gameState.currentLevel / 3 +
|
||||
gameState.levelTime / (30 * 1000) -
|
||||
gameState.perks.slow_down * 2
|
||||
gameState.perks.slow_down * 2,
|
||||
);
|
||||
|
||||
gameState.puckWidth = Math.max(
|
||||
|
@ -172,11 +172,7 @@ export function normalizeGameState(gameState: GameState) {
|
|||
}
|
||||
|
||||
export function baseCombo(gameState: GameState) {
|
||||
return (
|
||||
1 +
|
||||
gameState.perks.base_combo * 3 +
|
||||
gameState.perks.smaller_puck * 5
|
||||
);
|
||||
return 1 + gameState.perks.base_combo * 3 + gameState.perks.smaller_puck * 5;
|
||||
}
|
||||
|
||||
export function resetCombo(
|
||||
|
@ -527,7 +523,7 @@ export function pickRandomUpgrades(gameState: GameState, count: number) {
|
|||
score: Math.random() + (gameState.lastOffered[u.id] || 0),
|
||||
}))
|
||||
.sort((a, b) => a.score - b.score)
|
||||
.filter((u) => gameState.perks[u.id] < u.max - gameState.bannedPerks[u.id])
|
||||
.filter((u) => gameState.perks[u.id] < u.max + gameState.perks.limitless)
|
||||
.slice(0, count)
|
||||
.sort((a, b) => (a.id > b.id ? 1 : -1));
|
||||
|
||||
|
@ -564,16 +560,12 @@ export function schedulGameSound(
|
|||
}
|
||||
|
||||
export function addToScore(gameState: GameState, coin: Coin) {
|
||||
|
||||
gameState.score += coin.points;
|
||||
gameState.lastScoreIncrease = gameState.levelTime;
|
||||
addToTotalScore(gameState, coin.points);
|
||||
if (gameState.score > gameState.highScore && !gameState.creative) {
|
||||
gameState.highScore = gameState.score;
|
||||
localStorage.setItem(
|
||||
"breakout-3-hs-short" ,
|
||||
gameState.score.toString(),
|
||||
);
|
||||
localStorage.setItem("breakout-3-hs-short", gameState.score.toString());
|
||||
}
|
||||
if (!isOptionOn("basic")) {
|
||||
makeParticle(
|
||||
|
@ -632,7 +624,7 @@ export async function setLevel(gameState: GameState, l: number) {
|
|||
stopRecording();
|
||||
recordBestWorstLevelScore(gameState);
|
||||
|
||||
if (l > 0) {
|
||||
if (l > 0) {
|
||||
await openUpgradesPicker(gameState);
|
||||
}
|
||||
gameState.currentLevel = l;
|
||||
|
@ -700,17 +692,17 @@ function setBrick(gameState: GameState, index: number, color: string) {
|
|||
0;
|
||||
}
|
||||
|
||||
const rainbow=[
|
||||
'#ff2e2e',
|
||||
'#ffe02e',
|
||||
'#70ff33',
|
||||
'#33ffa7',
|
||||
'#38acff',
|
||||
'#7038ff',
|
||||
'#ff3de5',
|
||||
]
|
||||
const rainbow = [
|
||||
"#ff2e2e",
|
||||
"#ffe02e",
|
||||
"#70ff33",
|
||||
"#33ffa7",
|
||||
"#38acff",
|
||||
"#7038ff",
|
||||
"#ff3de5",
|
||||
];
|
||||
export function rainbowColor(): colorString {
|
||||
return rainbow[Math.floor(gameState.levelTime / 50) %rainbow.length ]
|
||||
return rainbow[Math.floor(gameState.levelTime / 50) % rainbow.length];
|
||||
}
|
||||
|
||||
export function repulse(
|
||||
|
@ -999,9 +991,9 @@ export function gameStateTick(
|
|||
) {
|
||||
if (gameState.currentLevel + 1 < max_levels(gameState)) {
|
||||
setLevel(gameState, gameState.currentLevel + 1);
|
||||
} else {
|
||||
} else {
|
||||
gameOver(
|
||||
t("gameOver.win.title" ),
|
||||
t("gameOver.win.title"),
|
||||
t("gameOver.win.summary", { score: gameState.score }),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,10 +1,16 @@
|
|||
import {Ball, Coin, GameState, Level, PerkId, PerksMap} from "./types";
|
||||
import {
|
||||
Ball,
|
||||
GameState,
|
||||
Level,
|
||||
PerkId,
|
||||
PerksMap,
|
||||
RunHistoryItem,
|
||||
} from "./types";
|
||||
import { icons, upgrades } from "./loadGameData";
|
||||
import { t } from "./i18n/i18n";
|
||||
import { brickAt } from "./level_editor/levels_editor_util";
|
||||
import { clamp } from "./pure_functions";
|
||||
import {isOptionOn} from "./options";
|
||||
import {rawUpgrades} from "./upgrades";
|
||||
import { rawUpgrades } from "./upgrades";
|
||||
import { hashCode } from "./getLevelBackground";
|
||||
|
||||
export function describeLevel(level: Level) {
|
||||
let bricks = 0,
|
||||
|
@ -80,15 +86,15 @@ export function getPossibleUpgrades(gameState: GameState) {
|
|||
}
|
||||
|
||||
export function max_levels(gameState: GameState) {
|
||||
if (gameState.creative ) return 1;
|
||||
return 7 + gameState.perks.extra_levels
|
||||
if (gameState.creative) return 1;
|
||||
return 7 + gameState.perks.extra_levels;
|
||||
}
|
||||
|
||||
export function pickedUpgradesHTMl(gameState: GameState) {
|
||||
const upgradesList = getPossibleUpgrades(gameState)
|
||||
.filter((u) => gameState.bannedPerks[u.id] || gameState.perks[u.id])
|
||||
.filter((u) => gameState.perks[u.id])
|
||||
.map((u) => {
|
||||
const newMax = Math.max(0, u.max - gameState.bannedPerks[u.id]);
|
||||
const newMax = Math.max(0, u.max +gameState.perks.limitless);
|
||||
|
||||
let bars = [];
|
||||
for (let i = 0; i < Math.max(u.max, newMax, gameState.perks[u.id]); i++) {
|
||||
|
@ -171,6 +177,7 @@ export function telekinesisEffectRate(gameState: GameState, ball: Ball) {
|
|||
0
|
||||
);
|
||||
}
|
||||
|
||||
export function yoyoEffectRate(gameState: GameState, ball: Ball) {
|
||||
return (
|
||||
(gameState.perks.yoyo &&
|
||||
|
@ -257,49 +264,65 @@ export function isMovingWhilePassiveIncome(gameState: GameState) {
|
|||
);
|
||||
}
|
||||
|
||||
export function getHighScore(){
|
||||
try {
|
||||
return parseInt(
|
||||
localStorage.getItem("breakout-3-hs-short" ) || "0",
|
||||
);
|
||||
export function getHighScore() {
|
||||
try {
|
||||
return parseInt(localStorage.getItem("breakout-3-hs-short") || "0");
|
||||
} catch (e) {}
|
||||
return 0
|
||||
return 0;
|
||||
}
|
||||
|
||||
export function highScoreText( ) {
|
||||
|
||||
if (getHighScore()) {
|
||||
return t("main_menu.high_score", { score:getHighScore() });
|
||||
}
|
||||
export function highScoreText() {
|
||||
if (getHighScore()) {
|
||||
return t("main_menu.high_score", { score: getHighScore() });
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
export function unlockCondition(levelIndex:number){
|
||||
if(levelIndex<7){
|
||||
return {}
|
||||
export function reasonLevelIsLocked(
|
||||
levelIndex: number,
|
||||
history: RunHistoryItem[],
|
||||
) {
|
||||
// Returns "" if level is unlocked, otherwise a string explaining how to unlock it
|
||||
if (levelIndex <= 10) {
|
||||
return "";
|
||||
}
|
||||
if(levelIndex<20){
|
||||
return {
|
||||
minScore:100*levelIndex
|
||||
}
|
||||
if (levelIndex < 20) {
|
||||
const minScore = 100 * levelIndex;
|
||||
return history.find((r) => r.score >= minScore)
|
||||
? ""
|
||||
: t("unlocks.minScore", { minScore });
|
||||
}
|
||||
const excluded:PerkId[]=[
|
||||
'extra_levels','extra_life', "one_more_choice", "instant_upgrade"
|
||||
]
|
||||
const excluded: PerkId[] = [
|
||||
"extra_levels",
|
||||
"extra_life",
|
||||
"one_more_choice",
|
||||
"instant_upgrade",
|
||||
];
|
||||
|
||||
const possibletargets = rawUpgrades.slice(0,Math.floor(levelIndex/2))
|
||||
.map(u=>u.id)
|
||||
.filter(u=>!excluded.includes(u))
|
||||
.sort((a,b)=>Math.random()-0.5)
|
||||
const possibletargets = rawUpgrades
|
||||
.slice(0, Math.floor(levelIndex / 2))
|
||||
.map((u) => u)
|
||||
.filter((u) => !excluded.includes(u.id))
|
||||
.sort((a, b) => hashCode(levelIndex + a.id) - hashCode(levelIndex + b.id));
|
||||
|
||||
const length=Math.ceil(levelIndex/30)
|
||||
|
||||
return {
|
||||
minScore:100*levelIndex*Math.pow(1.02,levelIndex),
|
||||
withUpgrades : possibletargets.slice(0,length),
|
||||
withoutUpgrades : possibletargets.slice(length,length+length),
|
||||
const length = Math.ceil(levelIndex / 30);
|
||||
const required = possibletargets.slice(0, length);
|
||||
const forbidden = possibletargets.slice(length, length + length);
|
||||
const minScore = 100 * levelIndex * Math.floor(Math.pow(1.01, levelIndex));
|
||||
if (
|
||||
history.find(
|
||||
(r) =>
|
||||
r.score >= minScore &&
|
||||
!required.find((u) => !r?.perks?.[u.id]) &&
|
||||
!forbidden.find((u) => r?.perks?.[u.id]),
|
||||
)
|
||||
) {
|
||||
return "";
|
||||
} else {
|
||||
return t("unlocks.minScoreWithPerks", {
|
||||
minScore,
|
||||
required: required.map((u) => u.name).join(", "),
|
||||
forbidden: forbidden.map((u) => u.name).join(", "),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -152,21 +152,6 @@
|
|||
</concept_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
<concept_node>
|
||||
<name>next_unlock</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>restart</name>
|
||||
<description/>
|
||||
|
@ -368,7 +353,22 @@
|
|||
</children>
|
||||
</folder_node>
|
||||
<concept_node>
|
||||
<name>unlocked_count</name>
|
||||
<name>unlocked_perk</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>unlocked_perk_plural</name>
|
||||
<description/>
|
||||
<comment/>
|
||||
<translations>
|
||||
|
@ -448,7 +448,7 @@
|
|||
</translation>
|
||||
<translation>
|
||||
<language>fr-FR</language>
|
||||
<approved>true</approved>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
|
@ -2167,6 +2167,36 @@
|
|||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>just_unlocked</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>just_unlocked_plural</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>level</name>
|
||||
<description/>
|
||||
|
@ -2197,6 +2227,51 @@
|
|||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>minScore</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>minScoreWithPerks</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>minTotalScore</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>title_upgrades</name>
|
||||
<description/>
|
||||
|
@ -3477,6 +3552,56 @@
|
|||
</concept_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
<name>limitless</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>metamorphosis</name>
|
||||
<children>
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
"gameOver.cumulative_total": "Your total cumulative score went from {{startTs}} to {{endTs}}.",
|
||||
"gameOver.lost.summary": "You dropped the ball after catching {{score}} coins.",
|
||||
"gameOver.lost.title": "Game Over",
|
||||
"gameOver.next_unlock": "Score {{points}} more points to reach the next unlock.",
|
||||
"gameOver.restart": "Start a new game",
|
||||
"gameOver.stats.balls_lost": "Balls lost",
|
||||
"gameOver.stats.bricks_broken": "Bricks broken",
|
||||
|
@ -21,11 +20,12 @@
|
|||
"gameOver.stats.level_reached": "Level reached",
|
||||
"gameOver.stats.total_score": "Total score",
|
||||
"gameOver.stats.upgrades_applied": "Upgrades applied",
|
||||
"gameOver.unlocked_count": "You unlocked {{count}} items :",
|
||||
"gameOver.unlocked_perk": "You just unlocked a perk",
|
||||
"gameOver.unlocked_perk_plural": "You just unlocked {{count}} perks",
|
||||
"gameOver.win.summary": "This game is over. You stashed {{score}} coins. ",
|
||||
"gameOver.win.title": "You completed this game",
|
||||
"lab.help": "Try any build you want",
|
||||
"lab.instructions": "Select upgrades below, then pick the level to play. ",
|
||||
"lab.instructions": "Select upgrades below, then pick the level to play. Creative mode runs are ignored in unlocks, high score, total score and statistics, and only last one level.",
|
||||
"lab.menu_entry": "Creative mode",
|
||||
"lab.reset": "Reset all to 0",
|
||||
"lab.select_level": "Select a level to play on",
|
||||
|
@ -138,8 +138,13 @@
|
|||
"score_panel.upgrades_picked": "Upgrades picked so far : ",
|
||||
"unlocks.greyed_out_help": "The greyed out ones can be unlocked by increasing your total score. The total score increases every time you score in game.",
|
||||
"unlocks.intro": "Your total score is {{ts}}. Below are all the upgrades and levels the games has to offer. Click an upgrade or level below to start a game with it.",
|
||||
"unlocks.just_unlocked": "You just unlocked a level",
|
||||
"unlocks.just_unlocked_plural": "You just unlocked {{count}} levels",
|
||||
"unlocks.level": "<h2>You unlocked {{unlocked}} levels out of {{out_of}}</h2>\n<p>Here are all the game levels, click one to start a game with that starting level. </p> ",
|
||||
"unlocks.level_description": "A {{size}}x{{size}} level with {{bricks}} bricks, {{colors}} colors and {{bombs}} bombs.",
|
||||
"unlocks.minScore": "Reach ${{minScore}}",
|
||||
"unlocks.minScoreWithPerks": "Reach ${{minScore}} in a run with {{required}} but without {{forbidden}}",
|
||||
"unlocks.minTotalScore": "Accumulate a total of ${{score}}",
|
||||
"unlocks.title_upgrades": "You unlocked {{unlocked}} upgrades out of {{out_of}}",
|
||||
"upgrades.addiction.fullHelp": "The countdown only starts after breaking the first brick of each level. It stops as soon as all bricks are destroyed.",
|
||||
"upgrades.addiction.help": "+{{lvl}} combo / brick, combo resets {{delay}}s after breaking a brick. ",
|
||||
|
@ -217,6 +222,9 @@
|
|||
"upgrades.left_is_lava.fullHelp": "Whenever you break a brick, your combo will increase by one, so you'll get one more coin from all the next bricks you break.\n\nHowever, your combo will reset as soon as your ball hits the left side . \n\nAs soon as your combo rises, the left side becomes red to remind you that you should avoid hitting them. \n",
|
||||
"upgrades.left_is_lava.help": "+{{lvl}} combo per brick broken, resets on left side hit",
|
||||
"upgrades.left_is_lava.name": "Avoid left side",
|
||||
"upgrades.limitless.fullHelp": "Choosing this perk also raises his own limit by one, letting you pick it again.",
|
||||
"upgrades.limitless.help": "Raise all upgrade's maximum level by {{lvl}} ",
|
||||
"upgrades.limitless.name": "Limitless",
|
||||
"upgrades.metamorphosis.fullHelp": "With this perk, coins will be of the color of the brick they come from, and will color the first brick they touch in the same color. \n\nCoins spawn with the speed of the ball that broke them, which means you can aim a bit in the direction of the bricks you want to \"paint\".",
|
||||
"upgrades.metamorphosis.help": "Each coins can stain {{lvl}} brick(s) with its color",
|
||||
"upgrades.metamorphosis.name": "Metamorphosis",
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
"gameOver.cumulative_total": "Votre score total cumulé est passé de {{startTs}} à {{endTs}}.",
|
||||
"gameOver.lost.summary": "Vous avez fait tomber la balle après avoir attrapé {{score}} pièces.",
|
||||
"gameOver.lost.title": "Balle perdue",
|
||||
"gameOver.next_unlock": "Marquez {{points}} points supplémentaires pour débloquer la prochaine amélioration ou le prochain niveau.",
|
||||
"gameOver.restart": "Nouvelle partie",
|
||||
"gameOver.stats.balls_lost": "Balles perdues",
|
||||
"gameOver.stats.bricks_broken": "Briques cassées",
|
||||
|
@ -21,11 +20,12 @@
|
|||
"gameOver.stats.level_reached": "Niveau atteint",
|
||||
"gameOver.stats.total_score": "Score total",
|
||||
"gameOver.stats.upgrades_applied": "Mises à jour appliquées",
|
||||
"gameOver.unlocked_count": "Vous avez débloqué {{count}} objet(s) :",
|
||||
"gameOver.unlocked_perk": "Vous avez débloqué une amélioration",
|
||||
"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.title": "Vous avez terminé cette partie",
|
||||
"lab.help": "Essayez n'importe quel build",
|
||||
"lab.instructions": "Sélectionnez les améliorations ci-dessous, puis choisissez le niveau à jouer. ",
|
||||
"lab.instructions": "Sélectionnez les améliorations ci-dessous, puis choisissez le niveau à jouer. Les parties en mode créatif sont ignorées dans les déblocages, le meilleur score, le score total et les statistiques, et ne durent qu'un seul niveau.",
|
||||
"lab.menu_entry": "Mode créatif",
|
||||
"lab.reset": "RAZ toutes les améliorations",
|
||||
"lab.select_level": "Sélectionnez un niveau sur lequel jouer",
|
||||
|
@ -138,8 +138,13 @@
|
|||
"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.",
|
||||
"unlocks.intro": "Votre score total est de {{ts}}. Vous trouverez ci-dessous toutes les améliorations et tous les niveaux que le jeu peut offrir. Cliquez sur l'un d'entre eux pour commencer une nouvelle partie. ",
|
||||
"unlocks.just_unlocked": "Vous venez de débloquer un niveau",
|
||||
"unlocks.just_unlocked_plural": "Vous venez de débloquer {{count}} niveaux",
|
||||
"unlocks.level": "<h2>Vous avez débloqué {{unlocked}} niveaux sur {{out_of}}</h2>\n<p>Voici tous les niveaux du jeu, cliquez sur l'un d'eux pour démarrer une partie avec ce niveau de départ. </p> ",
|
||||
"unlocks.level_description": "Un niveau {{size}}x{{size}} avec {{bricks}} briques, {{colors}} couleurs et {{bombs}} bombes.",
|
||||
"unlocks.minScore": "Atteindre ${{minScore}}",
|
||||
"unlocks.minScoreWithPerks": "Atteignez ${{minScore}} dans une course avec {{required}} mais sans {{forbidden}}",
|
||||
"unlocks.minTotalScore": "Accumuler un total de ${{score}}",
|
||||
"unlocks.title_upgrades": "Vous avez débloqué {{unlocked}} améliorations sur {{out_of}}",
|
||||
"upgrades.addiction.fullHelp": "Le décompte ne commence qu'à parti de la destruction de la première brique du niveau, et s'arrête dès qu'il n'y a plus de briques. ",
|
||||
"upgrades.addiction.help": "+{{lvl}} combo / brique, le combo RAZ après {{delay}}s sans casser de briques",
|
||||
|
@ -217,6 +222,9 @@
|
|||
"upgrades.left_is_lava.fullHelp": "Chaque fois que vous cassez une brique, votre combo augmente d'une unité, ce qui vous permet d'obtenir une pièce de plus à chaque fois que vous cassez une brique.\n\nCependant, votre combinaison se réinitialise dès que votre balle touche le côté gauche.\n\nDès que votre combo augmente, le côté gauche devient rouge pour vous rappeler que vous devez éviter de le frapper.",
|
||||
"upgrades.left_is_lava.help": "+{{lvl}} combo par brique, RAZ en touchant le bord gauche",
|
||||
"upgrades.left_is_lava.name": "Éviter le côté gauche",
|
||||
"upgrades.limitless.fullHelp": "",
|
||||
"upgrades.limitless.help": "",
|
||||
"upgrades.limitless.name": "Sans limites",
|
||||
"upgrades.metamorphosis.fullHelp": "Avec cette amélioration, les pièces seront de la couleur de la brique d'où elles proviennent et coloreront la première brique qu'elles toucheront. \n\nLes pièces apparaissent à la vitesse de la balle qui les a cassées, ce qui signifie que vous pouvez viser un peu dans la direction des briques que vous voulez \"peindre\".",
|
||||
"upgrades.metamorphosis.help": "Chaque pièces peut tacher {{lvl}} brique(s) avec sa couleur",
|
||||
"upgrades.metamorphosis.name": "Métamorphose",
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {Level, Palette, RawLevel} from "../types";
|
||||
import { Level, Palette, RawLevel } from "../types";
|
||||
import _backgrounds from "../data/backgrounds.json";
|
||||
import _palette from "../data/palette.json";
|
||||
// import _allLevels from "../data/levels.json";
|
||||
|
@ -12,34 +12,35 @@ const backgrounds = _backgrounds as string[];
|
|||
const palette = _palette as Palette;
|
||||
|
||||
// let allLevels = _allLevels ;
|
||||
let allLevels=null
|
||||
let allLevels = null;
|
||||
|
||||
function App() {
|
||||
const [selected, setSelected] = useState("W");
|
||||
const [applying, setApplying] = useState("");
|
||||
const [levels, setLevels] = useState([]);
|
||||
useEffect(()=>{
|
||||
fetch('http://localhost:4400/src/data/levels.json')
|
||||
.then(r=>r.json())
|
||||
.then(list=>{
|
||||
setLevels(list as RawLevel[])
|
||||
allLevels=list
|
||||
})
|
||||
},[])
|
||||
useEffect(() => {
|
||||
fetch("http://localhost:4400/src/data/levels.json")
|
||||
.then((r) => r.json())
|
||||
.then((list) => {
|
||||
setLevels(list as RawLevel[]);
|
||||
allLevels = list;
|
||||
});
|
||||
}, []);
|
||||
|
||||
const updateLevel = (index: number, change: Partial<RawLevel>) => {
|
||||
setLevels((list) =>
|
||||
list.map((l, li) => (li === index ? { ...l, ...change } : l)),
|
||||
);
|
||||
}
|
||||
const updateLevel = (index: number, change: Partial<RawLevel>) => {
|
||||
setLevels((list) =>
|
||||
list.map((l, li) => (li === index ? { ...l, ...change } : l)),
|
||||
);
|
||||
};
|
||||
|
||||
const deleteLevel = (index: number) => {
|
||||
if (confirm("Delete level")) {
|
||||
setLevels(levels.filter((l, i) => i!==index));
|
||||
setLevels(levels.filter((l, i) => i !== index));
|
||||
}
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
if(!allLevels||JSON.stringify(allLevels) === JSON.stringify(levels)) return
|
||||
if (!allLevels || JSON.stringify(allLevels) === JSON.stringify(levels))
|
||||
return;
|
||||
const timoutId = setTimeout(() => {
|
||||
return fetch("http://localhost:4400/src/data/levels.json", {
|
||||
method: "POST",
|
||||
|
|
|
@ -35,12 +35,6 @@ export const allLevels = rawLevelsList
|
|||
.filter((l) => !l.name.startsWith("icon:"))
|
||||
.map((l, li) => ({
|
||||
...l,
|
||||
threshold:
|
||||
li < 8
|
||||
? 0
|
||||
: Math.round(
|
||||
Math.min(Math.pow(10, 1 + (li + l.size) / 30) * 10, 5000) * li,
|
||||
),
|
||||
sortKey: ((Math.random() + 3) / 3.5) * l.bricksCount,
|
||||
})) as Level[];
|
||||
|
||||
|
|
|
@ -41,17 +41,14 @@ migrate("remove_long_and_creative_mode_data", () => {
|
|||
localStorage.getItem("breakout_71_runs_history") || "[]",
|
||||
) as RunHistoryItem[];
|
||||
|
||||
let cleaned=runsHistory.filter(r=> {
|
||||
if('mode' in r){
|
||||
if(r.mode !== 'short'){
|
||||
return false
|
||||
let cleaned = runsHistory.filter((r) => {
|
||||
if ("mode" in r) {
|
||||
if (r.mode !== "short") {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
if(cleaned.length!==runsHistory.length)
|
||||
localStorage.setItem(
|
||||
"breakout_71_runs_history",
|
||||
JSON.stringify(cleaned),
|
||||
);
|
||||
return true;
|
||||
});
|
||||
if (cleaned.length !== runsHistory.length)
|
||||
localStorage.setItem("breakout_71_runs_history", JSON.stringify(cleaned));
|
||||
});
|
||||
|
|
|
@ -1,22 +1,30 @@
|
|||
import { GameState, RunParams } from "./types";
|
||||
import { getTotalScore } from "./settings";
|
||||
import { allLevels, upgrades } from "./loadGameData";
|
||||
import {
|
||||
defaultSounds, getHighScore,
|
||||
getPossibleUpgrades, highScoreText,
|
||||
defaultSounds,
|
||||
getHighScore,
|
||||
getPossibleUpgrades,
|
||||
highScoreText,
|
||||
reasonLevelIsLocked,
|
||||
makeEmptyPerksMap,
|
||||
sumOfValues,
|
||||
} from "./game_utils";
|
||||
import { dontOfferTooSoon, resetBalls } from "./gameStateMutators";
|
||||
import { isOptionOn } from "./options";
|
||||
import { getHistory } from "./gameOver";
|
||||
import { getTotalScore } from "./settings";
|
||||
|
||||
export function getRunLevels(params: RunParams) {
|
||||
const history = getHistory();
|
||||
const unlocked = allLevels.filter(
|
||||
(l, li) => !reasonLevelIsLocked(li, history),
|
||||
);
|
||||
|
||||
export function getRunLevels(totalScoreAtRunStart: number, params: RunParams) {
|
||||
const firstLevel = params?.level
|
||||
? allLevels.filter((l) => l.name === params?.level)
|
||||
? unlocked.filter((l) => l.name === params?.level)
|
||||
: [];
|
||||
|
||||
const restInRandomOrder = allLevels
|
||||
.filter((l) => totalScoreAtRunStart >= l.threshold)
|
||||
const restInRandomOrder = unlocked
|
||||
.filter((l) => l.name !== params?.level)
|
||||
.filter((l) => l.name !== params?.levelToAvoid)
|
||||
.sort(() => Math.random() - 0.5);
|
||||
|
@ -27,9 +35,7 @@ export function getRunLevels(totalScoreAtRunStart: number, params: RunParams) {
|
|||
}
|
||||
|
||||
export function newGameState(params: RunParams): GameState {
|
||||
const totalScoreAtRunStart = getTotalScore();
|
||||
|
||||
const runLevels = getRunLevels(totalScoreAtRunStart, params);
|
||||
const runLevels = getRunLevels(params);
|
||||
|
||||
const perks = { ...makeEmptyPerksMap(upgrades), ...(params?.perks || {}) };
|
||||
|
||||
|
@ -39,7 +45,6 @@ export function newGameState(params: RunParams): GameState {
|
|||
currentLevel: 0,
|
||||
upgradesOfferedFor: -1,
|
||||
perks,
|
||||
bannedPerks: makeEmptyPerksMap(upgrades),
|
||||
puckWidth: 200,
|
||||
baseSpeed: 12,
|
||||
combo: 1,
|
||||
|
@ -80,7 +85,7 @@ export function newGameState(params: RunParams): GameState {
|
|||
ballSize: 20,
|
||||
coinSize: 14,
|
||||
puckHeight: 20,
|
||||
totalScoreAtRunStart,
|
||||
totalScoreAtRunStart: getTotalScore(),
|
||||
pauseUsesDuringRun: 0,
|
||||
keyboardPuckSpeed: 0,
|
||||
lastTick: performance.now(),
|
||||
|
@ -110,7 +115,7 @@ export function newGameState(params: RunParams): GameState {
|
|||
autoCleanUses: 0,
|
||||
...defaultSounds(),
|
||||
rerolls: 0,
|
||||
creative: sumOfValues(params.perks)>1
|
||||
creative: sumOfValues(params.perks) > 1,
|
||||
};
|
||||
resetBalls(gameState);
|
||||
|
||||
|
|
1811
src/render.ts
1811
src/render.ts
File diff suppressed because it is too large
Load diff
|
@ -30,8 +30,8 @@ export function getTotalScore() {
|
|||
}
|
||||
|
||||
export function addToTotalScore(gameState: GameState, points: number) {
|
||||
if(!gameState.creative)
|
||||
setSettingValue("breakout_71_total_score", getTotalScore() + points);
|
||||
if (!gameState.creative)
|
||||
setSettingValue("breakout_71_total_score", getTotalScore() + points);
|
||||
}
|
||||
|
||||
export function getCurrentMaxCoins() {
|
||||
|
|
4
src/types.d.ts
vendored
4
src/types.d.ts
vendored
|
@ -18,7 +18,6 @@ export type Level = {
|
|||
bricksCount: number;
|
||||
svg: string;
|
||||
color: string;
|
||||
threshold: number;
|
||||
sortKey: number;
|
||||
credit?: string;
|
||||
};
|
||||
|
@ -202,7 +201,6 @@ export type GameState = {
|
|||
puckWidth: number;
|
||||
// perks the user currently has
|
||||
perks: PerksMap;
|
||||
bannedPerks: PerksMap;
|
||||
// Base speed of the ball in pixels/tick
|
||||
baseSpeed: number;
|
||||
// Score multiplier
|
||||
|
@ -285,7 +283,7 @@ export type GameState = {
|
|||
colorChange: { vol: number; x: number };
|
||||
};
|
||||
rerolls: number;
|
||||
creative:boolean;
|
||||
creative: boolean;
|
||||
};
|
||||
|
||||
export type RunParams = {
|
||||
|
|
|
@ -654,4 +654,15 @@ export const rawUpgrades = [
|
|||
t("upgrades.fountain_toss.help", { lvl, max: lvl * 30 }),
|
||||
fullHelp: t("upgrades.fountain_toss.fullHelp"),
|
||||
},
|
||||
{
|
||||
requires: "",
|
||||
threshold: 175000,
|
||||
giftable: false,
|
||||
id: "limitless",
|
||||
max: 1,
|
||||
name: t("upgrades.limitless.name"),
|
||||
help: (lvl: number) =>
|
||||
t("upgrades.limitless.help", { lvl }),
|
||||
fullHelp: t("upgrades.limitless.fullHelp"),
|
||||
},
|
||||
] as const;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue