mirror of
https://gitlab.com/lecarore/breakout71.git
synced 2025-04-20 12:15:06 -04:00
wip
This commit is contained in:
parent
2f51f83514
commit
42abc6bc01
25 changed files with 1514 additions and 1328 deletions
10
Readme.md
10
Readme.md
|
@ -18,11 +18,13 @@ Break colourful bricks, catch bouncing coins and select powerful upgrades !
|
||||||
## To do
|
## To do
|
||||||
|
|
||||||
- avoid showing a +1 and -1 at the same time when a combo increase is reset
|
- avoid showing a +1 and -1 at the same time when a combo increase is reset
|
||||||
- add unlock conditions for levels in the form "reach high score X with perk A,B,C but without perk B,C,D"
|
- mention unlock conditions in help
|
||||||
|
- show unlock condition in unlocks menu for perks as tooltip
|
||||||
|
- fallback for mobile user to see unlock conditions
|
||||||
|
|
||||||
## Done
|
## Done
|
||||||
|
|
||||||
|
- add unlock conditions for levels in the form "reach high score X with perk A,B,C but without perk B,C,D"
|
||||||
- remove loop mode :
|
- remove loop mode :
|
||||||
- remove basecombo
|
- remove basecombo
|
||||||
- remove mode
|
- remove mode
|
||||||
|
@ -244,6 +246,8 @@ Break colourful bricks, catch bouncing coins and select powerful upgrades !
|
||||||
- +lvl combo per bricks / resets after 5/lvl seconds without coin catch ?
|
- +lvl combo per bricks / resets after 5/lvl seconds without coin catch ?
|
||||||
- +lvl combo per bricks / resets after 5/lvl seconds without ball color change ?
|
- +lvl combo per bricks / resets after 5/lvl seconds without ball color change ?
|
||||||
- +lvl combo per bricks / resets after 5/lvl seconds without sides hit ?
|
- +lvl combo per bricks / resets after 5/lvl seconds without sides hit ?
|
||||||
|
- + lvl x n combo when destroying a brick after bouncing on a side/top n times ?
|
||||||
|
- make stats a clairvoyant thing
|
||||||
|
|
||||||
## Medium difficulty perks ideas
|
## Medium difficulty perks ideas
|
||||||
- balls collision split them into 4 smaller balls, lvl times (requires rework)
|
- balls collision split them into 4 smaller balls, lvl times (requires rework)
|
||||||
|
@ -261,10 +265,12 @@ Break colourful bricks, catch bouncing coins and select powerful upgrades !
|
||||||
- 2x speed after bouncing on puck
|
- 2x speed after bouncing on puck
|
||||||
- the more balls are close to a brick, the more combo is gained when breaking it. If only one ball, loose one point or reset
|
- the more balls are close to a brick, the more combo is gained when breaking it. If only one ball, loose one point or reset
|
||||||
- ball avoids brick of wrong color
|
- ball avoids brick of wrong color
|
||||||
|
- puck slowly follows desired position, but +1 combo
|
||||||
|
|
||||||
## Hard perk ideas
|
## Hard perk ideas
|
||||||
- accelerometer controls coins and balls
|
- accelerometer controls coins and balls
|
||||||
- [colin] side pucks - same as above but with two side pucks : hard to know where to put them
|
- [colin] side pucks - same as above but with two side pucks : hard to know where to put them
|
||||||
|
- [colin] Perk: second puck in the middle of the screen
|
||||||
|
|
||||||
## ideas to sort
|
## ideas to sort
|
||||||
- wind : move coins based on puck movement not position
|
- wind : move coins based on puck movement not position
|
||||||
|
|
|
@ -29,8 +29,8 @@ android {
|
||||||
applicationId = "me.lecaro.breakout"
|
applicationId = "me.lecaro.breakout"
|
||||||
minSdk = 21
|
minSdk = 21
|
||||||
targetSdk = 34
|
targetSdk = 34
|
||||||
versionCode = 29064070
|
versionCode = 29065772
|
||||||
versionName = "29064070"
|
versionName = "29065772"
|
||||||
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
249
dist/index.html
vendored
249
dist/index.html
vendored
File diff suppressed because one or more lines are too long
|
@ -1,5 +1,5 @@
|
||||||
// The version of the cache.
|
// The version of the cache.
|
||||||
const VERSION = "29064070";
|
const VERSION = "29065772";
|
||||||
|
|
||||||
// The name of the cache
|
// The name of the cache
|
||||||
const CACHE_NAME = `breakout-71-${VERSION}`;
|
const CACHE_NAME = `breakout-71-${VERSION}`;
|
||||||
|
|
|
@ -2,7 +2,12 @@ import { GameState, Level, PerkId, Upgrade } from "./types";
|
||||||
import { allLevels, icons, upgrades } from "./loadGameData";
|
import { allLevels, icons, upgrades } from "./loadGameData";
|
||||||
import { t } from "./i18n/i18n";
|
import { t } from "./i18n/i18n";
|
||||||
import { getSettingValue, getTotalScore, setSettingValue } from "./settings";
|
import { getSettingValue, getTotalScore, setSettingValue } from "./settings";
|
||||||
import {confirmRestart, creativeModeThreshold, gameState, restart} from "./game";
|
import {
|
||||||
|
confirmRestart,
|
||||||
|
creativeModeThreshold,
|
||||||
|
gameState,
|
||||||
|
restart,
|
||||||
|
} from "./game";
|
||||||
import { asyncAlert, requiredAsyncAlert } from "./asyncAlert";
|
import { asyncAlert, requiredAsyncAlert } from "./asyncAlert";
|
||||||
import { describeLevel, highScoreText, sumOfValues } from "./game_utils";
|
import { describeLevel, highScoreText, sumOfValues } from "./game_utils";
|
||||||
|
|
||||||
|
@ -17,15 +22,12 @@ export function creativeMode(gameState: GameState) {
|
||||||
t("lab.help"),
|
t("lab.help"),
|
||||||
disabled: getTotalScore() < creativeModeThreshold,
|
disabled: getTotalScore() < creativeModeThreshold,
|
||||||
async value() {
|
async value() {
|
||||||
openCreativeModePerksPicker()
|
openCreativeModePerksPicker();
|
||||||
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function openCreativeModePerksPicker() {
|
export async function openCreativeModePerksPicker() {
|
||||||
|
|
||||||
|
|
||||||
let creativeModePerks: Partial<{ [id in PerkId]: number }> = getSettingValue(
|
let creativeModePerks: Partial<{ [id in PerkId]: number }> = getSettingValue(
|
||||||
"creativeModePerks",
|
"creativeModePerks",
|
||||||
{},
|
{},
|
||||||
|
@ -55,10 +57,7 @@ export async function openCreativeModePerksPicker() {
|
||||||
.map((u) => ({
|
.map((u) => ({
|
||||||
icon: u.icon,
|
icon: u.icon,
|
||||||
text: u.name,
|
text: u.name,
|
||||||
help:
|
help: (creativeModePerks[u.id] || 0) + "/" + (u.max+creativeModePerks.limitless),
|
||||||
(creativeModePerks[u.id] || 0) +
|
|
||||||
"/" +
|
|
||||||
u.max,
|
|
||||||
value: u,
|
value: u,
|
||||||
className: creativeModePerks[u.id]
|
className: creativeModePerks[u.id]
|
||||||
? "sandbox"
|
? "sandbox"
|
||||||
|
@ -84,14 +83,12 @@ export async function openCreativeModePerksPicker() {
|
||||||
if (await confirmRestart(gameState)) {
|
if (await confirmRestart(gameState)) {
|
||||||
restart({ perks: creativeModePerks, level: choice.name });
|
restart({ perks: creativeModePerks, level: choice.name });
|
||||||
}
|
}
|
||||||
return
|
return;
|
||||||
} else if (choice) {
|
} else if (choice) {
|
||||||
creativeModePerks[choice.id] =
|
creativeModePerks[choice.id] =
|
||||||
((creativeModePerks[choice.id] || 0) + 1) %
|
((creativeModePerks[choice.id] || 0) + 1) % (choice.max + 1 + creativeModePerks.limitless);
|
||||||
(choice.max +1);
|
|
||||||
} else {
|
} else {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
"29064070"
|
"29065772"
|
||||||
|
|
|
@ -158,7 +158,8 @@ body:not(.has-alert-open) #popup {
|
||||||
&[disabled] {
|
&[disabled] {
|
||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
filter: saturate(0);
|
filter: saturate(0);
|
||||||
pointer-events: none;
|
//pointer-events: none;
|
||||||
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
& > div {
|
& > div {
|
||||||
|
|
39
src/game.ts
39
src/game.ts
|
@ -17,6 +17,7 @@ import {
|
||||||
describeLevel,
|
describeLevel,
|
||||||
getRowColIndex,
|
getRowColIndex,
|
||||||
highScoreText,
|
highScoreText,
|
||||||
|
reasonLevelIsLocked,
|
||||||
levelsListHTMl,
|
levelsListHTMl,
|
||||||
max_levels,
|
max_levels,
|
||||||
pickedUpgradesHTMl,
|
pickedUpgradesHTMl,
|
||||||
|
@ -72,7 +73,7 @@ import { creativeMode } from "./creative";
|
||||||
import { setupTooltips } from "./tooltip";
|
import { setupTooltips } from "./tooltip";
|
||||||
import { startingPerkMenuButton } from "./startingPerks";
|
import { startingPerkMenuButton } from "./startingPerks";
|
||||||
import "./migrations";
|
import "./migrations";
|
||||||
import {getCreativeModeWarning} from "./gameOver";
|
import { getCreativeModeWarning, getHistory } from "./gameOver";
|
||||||
|
|
||||||
export async function play() {
|
export async function play() {
|
||||||
if (await applyFullScreenChoice()) return;
|
if (await applyFullScreenChoice()) return;
|
||||||
|
@ -441,7 +442,8 @@ document.addEventListener("visibilitychange", () => {
|
||||||
async function openScorePanel() {
|
async function openScorePanel() {
|
||||||
pause(true);
|
pause(true);
|
||||||
|
|
||||||
const cb = await asyncAlert({
|
|
||||||
|
await asyncAlert({
|
||||||
title: t("score_panel.title", {
|
title: t("score_panel.title", {
|
||||||
score: gameState.score,
|
score: gameState.score,
|
||||||
level: gameState.currentLevel + 1,
|
level: gameState.currentLevel + 1,
|
||||||
|
@ -449,7 +451,6 @@ async function openScorePanel() {
|
||||||
}),
|
}),
|
||||||
|
|
||||||
content: [
|
content: [
|
||||||
|
|
||||||
getCreativeModeWarning(gameState),
|
getCreativeModeWarning(gameState),
|
||||||
pickedUpgradesHTMl(gameState),
|
pickedUpgradesHTMl(gameState),
|
||||||
levelsListHTMl(gameState, gameState.currentLevel),
|
levelsListHTMl(gameState, gameState.currentLevel),
|
||||||
|
@ -485,7 +486,7 @@ export async function openMainMenu() {
|
||||||
help: highScoreText() || t("main_menu.normal_help"),
|
help: highScoreText() || t("main_menu.normal_help"),
|
||||||
value: () => {
|
value: () => {
|
||||||
restart({
|
restart({
|
||||||
levelToAvoid: currentLevelInfo(gameState).name
|
levelToAvoid: currentLevelInfo(gameState).name,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -816,6 +817,8 @@ async function applyFullScreenChoice() {
|
||||||
|
|
||||||
async function openUnlocksList() {
|
async function openUnlocksList() {
|
||||||
const ts = getTotalScore();
|
const ts = getTotalScore();
|
||||||
|
const hintField = isOptionOn("mobile-mode") ? "help" : "tooltip";
|
||||||
|
|
||||||
const upgradeActions = upgrades
|
const upgradeActions = upgrades
|
||||||
.sort((a, b) => a.threshold - b.threshold)
|
.sort((a, b) => a.threshold - b.threshold)
|
||||||
.map(({ name, id, threshold, icon, help }) => ({
|
.map(({ name, id, threshold, icon, help }) => ({
|
||||||
|
@ -823,39 +826,40 @@ async function openUnlocksList() {
|
||||||
disabled: ts < threshold,
|
disabled: ts < threshold,
|
||||||
value: { perks: { [id]: 1 } } as RunParams,
|
value: { perks: { [id]: 1 } } as RunParams,
|
||||||
icon,
|
icon,
|
||||||
tooltip: help(1),
|
[hintField]:
|
||||||
|
ts < threshold
|
||||||
|
? t("unlocks.minTotalScore", { score: threshold })
|
||||||
|
: help(1),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const levelActions = allLevels
|
const levelActions = allLevels.map((l, li) => {
|
||||||
.sort((a, b) => a.threshold - b.threshold)
|
const problem = reasonLevelIsLocked(li, getHistory());
|
||||||
.map((l) => {
|
|
||||||
const available = ts >= l.threshold;
|
|
||||||
return {
|
return {
|
||||||
text: l.name,
|
text: l.name,
|
||||||
disabled: !available,
|
disabled: !!problem,
|
||||||
value: { level: l.name } as RunParams,
|
value: { level: l.name } as RunParams,
|
||||||
icon: icons[l.name],
|
icon: icons[l.name],
|
||||||
tooltip: describeLevel(l),
|
[hintField]: problem || describeLevel(l),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const tryOn = await asyncAlert<RunParams>({
|
const tryOn = await asyncAlert<RunParams>({
|
||||||
title: t("unlocks.title_upgrades", {
|
title: t("unlocks.title_upgrades", {
|
||||||
unlocked: upgradeActions.filter((a) => !a.disabled).length,
|
unlocked: upgradeActions.filter((a) => !a.disabled).length,
|
||||||
out_of:upgradeActions.length
|
out_of: upgradeActions.length,
|
||||||
}),
|
}),
|
||||||
content: [
|
content: [
|
||||||
`<p>${t("unlocks.intro", { ts })}
|
`<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,
|
...upgradeActions,
|
||||||
t("unlocks.level", {
|
t("unlocks.level", {
|
||||||
unlocked: levelActions.filter((a) => !a.disabled).length,
|
unlocked: levelActions.filter((a) => !a.disabled).length,
|
||||||
out_of:levelActions.length
|
out_of: levelActions.length,
|
||||||
}),
|
}),
|
||||||
...levelActions,
|
...levelActions,
|
||||||
],
|
],
|
||||||
allowClose: true,
|
allowClose: true,
|
||||||
className: "actionsAsGrid",
|
className: isOptionOn("mobile-mode") ? "" : "actionsAsGrid",
|
||||||
});
|
});
|
||||||
if (tryOn) {
|
if (tryOn) {
|
||||||
if (await confirmRestart(gameState)) {
|
if (await confirmRestart(gameState)) {
|
||||||
|
@ -865,10 +869,9 @@ async function openUnlocksList() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function confirmRestart(gameState) {
|
export async function confirmRestart(gameState) {
|
||||||
|
|
||||||
if (!gameState.currentLevel) return true;
|
if (!gameState.currentLevel) return true;
|
||||||
if (alertsOpen) return true;
|
if (alertsOpen) return true;
|
||||||
pause(true)
|
pause(true);
|
||||||
return asyncAlert({
|
return asyncAlert({
|
||||||
title: t("confirmRestart.title"),
|
title: t("confirmRestart.title"),
|
||||||
content: [
|
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
|
// When doing ctrl + R in dev to refresh, i don't want to instantly restart a run
|
||||||
if (await confirmRestart(gameState)) {
|
if (await confirmRestart(gameState)) {
|
||||||
restart({
|
restart({
|
||||||
levelToAvoid: currentLevelInfo(gameState).name
|
levelToAvoid: currentLevelInfo(gameState).name,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
177
src/gameOver.ts
177
src/gameOver.ts
|
@ -2,34 +2,17 @@ import {allLevels, appVersion, icons, upgrades} from "./loadGameData";
|
||||||
import { t } from "./i18n/i18n";
|
import { t } from "./i18n/i18n";
|
||||||
import { GameState, RunHistoryItem } from "./types";
|
import { GameState, RunHistoryItem } from "./types";
|
||||||
import { gameState, pause, restart } from "./game";
|
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 { getTotalScore } from "./settings";
|
||||||
import { stopRecording } from "./recording";
|
import { stopRecording } from "./recording";
|
||||||
import { asyncAlert } from "./asyncAlert";
|
import { asyncAlert } from "./asyncAlert";
|
||||||
|
import { rawUpgrades } from "./upgrades";
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function addToTotalPlayTime(ms: number) {
|
export function addToTotalPlayTime(ms: number) {
|
||||||
try {
|
try {
|
||||||
|
@ -58,58 +41,33 @@ export function gameOver(title: string, intro: string) {
|
||||||
return "animation-delay:" + animationDelay + "ms;";
|
return "animation-delay:" + animationDelay + "ms;";
|
||||||
};
|
};
|
||||||
// unlocks
|
// unlocks
|
||||||
let unlocksInfo = "";
|
|
||||||
const endTs = getTotalScore();
|
const endTs = getTotalScore();
|
||||||
const startTs = endTs - gameState.score;
|
const startTs = endTs - gameState.score;
|
||||||
const list = getUpgraderUnlockPoints();
|
const unlockedPerks = rawUpgrades.filter(
|
||||||
list
|
(o) => o.threshold > startTs && o.threshold < endTs,
|
||||||
.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,
|
|
||||||
);
|
);
|
||||||
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
|
// Avoid the sad sound right as we restart a new games
|
||||||
gameState.combo = 1;
|
gameState.combo = 1;
|
||||||
|
@ -123,42 +81,54 @@ export function gameOver(title: string, intro: string) {
|
||||||
<p>${intro}</p>
|
<p>${intro}</p>
|
||||||
<p>${t("gameOver.cumulative_total", { startTs, endTs })}</p>
|
<p>${t("gameOver.cumulative_total", { startTs, endTs })}</p>
|
||||||
`,
|
`,
|
||||||
unlocksInfo,
|
|
||||||
{
|
{
|
||||||
icon: icons["icon:new_run"],
|
icon: icons["icon:new_run"],
|
||||||
value: null,
|
value: null,
|
||||||
text: t("gameOver.restart"),
|
text: t("gameOver.restart"),
|
||||||
help: "",
|
help: "",
|
||||||
},
|
},
|
||||||
`<div id="level-recording-container"></div>
|
`<div id="level-recording-container"></div>`,
|
||||||
${pickedUpgradesHTMl(gameState)}
|
// pickedUpgradesHTMl(gameState),
|
||||||
${getHistograms(gameState)}
|
unlocksInfo,
|
||||||
`,
|
getHistograms(gameState),
|
||||||
],
|
],
|
||||||
}).then(() =>
|
}).then(() =>
|
||||||
restart({
|
restart({
|
||||||
levelToAvoid: currentLevelInfo(gameState).name
|
levelToAvoid: currentLevelInfo(gameState).name,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getCreativeModeWarning(gameState: GameState) {
|
export function getCreativeModeWarning(gameState: GameState) {
|
||||||
if (gameState.creative) {
|
if (gameState.creative) {
|
||||||
return '<p>'+t('gameOver.creative')+'</p>'
|
return "<p>" + t("gameOver.creative") + "</p>";
|
||||||
}
|
}
|
||||||
return ''
|
return "";
|
||||||
}
|
}
|
||||||
export function getHistograms(gameState: GameState) {
|
|
||||||
if(gameState.creative) return ''
|
let runsHistory = [];
|
||||||
let runStats = "";
|
|
||||||
try {
|
try {
|
||||||
// Stores only top 100 runs
|
runsHistory = JSON.parse(
|
||||||
let runsHistory = JSON.parse(
|
|
||||||
localStorage.getItem("breakout_71_runs_history") || "[]",
|
localStorage.getItem("breakout_71_runs_history") || "[]",
|
||||||
) as RunHistoryItem[];
|
) as RunHistoryItem[];
|
||||||
|
} catch (e) {}
|
||||||
|
export function getHistory() {
|
||||||
|
return runsHistory;
|
||||||
|
}
|
||||||
|
|
||||||
runsHistory.sort((a, b) => a.score - b.score).reverse();
|
export function getHistograms(gameState: GameState) {
|
||||||
runsHistory = runsHistory.slice(0, 100);
|
if (gameState.creative) return "";
|
||||||
|
let unlockedLevels = "";
|
||||||
|
let runStats = "";
|
||||||
|
try {
|
||||||
|
const locked = allLevels
|
||||||
|
.map((l, li) => ({
|
||||||
|
li,
|
||||||
|
l,
|
||||||
|
r: reasonLevelIsLocked(li, runsHistory),
|
||||||
|
}))
|
||||||
|
.filter((l) => l.r);
|
||||||
|
|
||||||
runsHistory.push({
|
runsHistory.push({
|
||||||
...gameState.runStatistics,
|
...gameState.runStatistics,
|
||||||
|
@ -166,6 +136,30 @@ export function getHistograms(gameState: GameState) {
|
||||||
appVersion,
|
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
|
// Generate some histogram
|
||||||
|
|
||||||
localStorage.setItem(
|
localStorage.setItem(
|
||||||
|
@ -178,8 +172,7 @@ export function getHistograms(gameState: GameState) {
|
||||||
getter: (hi: RunHistoryItem) => number,
|
getter: (hi: RunHistoryItem) => number,
|
||||||
unit: string,
|
unit: string,
|
||||||
) => {
|
) => {
|
||||||
let values = runsHistory
|
let values = runsHistory.map((h) => getter(h) || 0);
|
||||||
.map((h) => getter(h) || 0);
|
|
||||||
let min = Math.min(...values);
|
let min = Math.min(...values);
|
||||||
let max = Math.max(...values);
|
let max = Math.max(...values);
|
||||||
// No point
|
// No point
|
||||||
|
@ -290,5 +283,5 @@ export function getHistograms(gameState: GameState) {
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn(e);
|
console.warn(e);
|
||||||
}
|
}
|
||||||
return runStats;
|
return runStats + unlockedLevels;
|
||||||
}
|
}
|
||||||
|
|
|
@ -131,7 +131,7 @@ export function normalizeGameState(gameState: GameState) {
|
||||||
gameState.gameZoneWidth / 12 / 10 +
|
gameState.gameZoneWidth / 12 / 10 +
|
||||||
gameState.currentLevel / 3 +
|
gameState.currentLevel / 3 +
|
||||||
gameState.levelTime / (30 * 1000) -
|
gameState.levelTime / (30 * 1000) -
|
||||||
gameState.perks.slow_down * 2
|
gameState.perks.slow_down * 2,
|
||||||
);
|
);
|
||||||
|
|
||||||
gameState.puckWidth = Math.max(
|
gameState.puckWidth = Math.max(
|
||||||
|
@ -172,11 +172,7 @@ export function normalizeGameState(gameState: GameState) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function baseCombo(gameState: GameState) {
|
export function baseCombo(gameState: GameState) {
|
||||||
return (
|
return 1 + gameState.perks.base_combo * 3 + gameState.perks.smaller_puck * 5;
|
||||||
1 +
|
|
||||||
gameState.perks.base_combo * 3 +
|
|
||||||
gameState.perks.smaller_puck * 5
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resetCombo(
|
export function resetCombo(
|
||||||
|
@ -527,7 +523,7 @@ export function pickRandomUpgrades(gameState: GameState, count: number) {
|
||||||
score: Math.random() + (gameState.lastOffered[u.id] || 0),
|
score: Math.random() + (gameState.lastOffered[u.id] || 0),
|
||||||
}))
|
}))
|
||||||
.sort((a, b) => a.score - b.score)
|
.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)
|
.slice(0, count)
|
||||||
.sort((a, b) => (a.id > b.id ? 1 : -1));
|
.sort((a, b) => (a.id > b.id ? 1 : -1));
|
||||||
|
|
||||||
|
@ -564,16 +560,12 @@ export function schedulGameSound(
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addToScore(gameState: GameState, coin: Coin) {
|
export function addToScore(gameState: GameState, coin: Coin) {
|
||||||
|
|
||||||
gameState.score += coin.points;
|
gameState.score += coin.points;
|
||||||
gameState.lastScoreIncrease = gameState.levelTime;
|
gameState.lastScoreIncrease = gameState.levelTime;
|
||||||
addToTotalScore(gameState, coin.points);
|
addToTotalScore(gameState, coin.points);
|
||||||
if (gameState.score > gameState.highScore && !gameState.creative) {
|
if (gameState.score > gameState.highScore && !gameState.creative) {
|
||||||
gameState.highScore = gameState.score;
|
gameState.highScore = gameState.score;
|
||||||
localStorage.setItem(
|
localStorage.setItem("breakout-3-hs-short", gameState.score.toString());
|
||||||
"breakout-3-hs-short" ,
|
|
||||||
gameState.score.toString(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
if (!isOptionOn("basic")) {
|
if (!isOptionOn("basic")) {
|
||||||
makeParticle(
|
makeParticle(
|
||||||
|
@ -701,16 +693,16 @@ function setBrick(gameState: GameState, index: number, color: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const rainbow = [
|
const rainbow = [
|
||||||
'#ff2e2e',
|
"#ff2e2e",
|
||||||
'#ffe02e',
|
"#ffe02e",
|
||||||
'#70ff33',
|
"#70ff33",
|
||||||
'#33ffa7',
|
"#33ffa7",
|
||||||
'#38acff',
|
"#38acff",
|
||||||
'#7038ff',
|
"#7038ff",
|
||||||
'#ff3de5',
|
"#ff3de5",
|
||||||
]
|
];
|
||||||
export function rainbowColor(): colorString {
|
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(
|
export function repulse(
|
||||||
|
|
|
@ -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 { icons, upgrades } from "./loadGameData";
|
||||||
import { t } from "./i18n/i18n";
|
import { t } from "./i18n/i18n";
|
||||||
import { brickAt } from "./level_editor/levels_editor_util";
|
|
||||||
import { clamp } from "./pure_functions";
|
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) {
|
export function describeLevel(level: Level) {
|
||||||
let bricks = 0,
|
let bricks = 0,
|
||||||
|
@ -81,14 +87,14 @@ export function getPossibleUpgrades(gameState: GameState) {
|
||||||
|
|
||||||
export function max_levels(gameState: GameState) {
|
export function max_levels(gameState: GameState) {
|
||||||
if (gameState.creative) return 1;
|
if (gameState.creative) return 1;
|
||||||
return 7 + gameState.perks.extra_levels
|
return 7 + gameState.perks.extra_levels;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function pickedUpgradesHTMl(gameState: GameState) {
|
export function pickedUpgradesHTMl(gameState: GameState) {
|
||||||
const upgradesList = getPossibleUpgrades(gameState)
|
const upgradesList = getPossibleUpgrades(gameState)
|
||||||
.filter((u) => gameState.bannedPerks[u.id] || gameState.perks[u.id])
|
.filter((u) => gameState.perks[u.id])
|
||||||
.map((u) => {
|
.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 = [];
|
let bars = [];
|
||||||
for (let i = 0; i < Math.max(u.max, newMax, gameState.perks[u.id]); i++) {
|
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
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function yoyoEffectRate(gameState: GameState, ball: Ball) {
|
export function yoyoEffectRate(gameState: GameState, ball: Ball) {
|
||||||
return (
|
return (
|
||||||
(gameState.perks.yoyo &&
|
(gameState.perks.yoyo &&
|
||||||
|
@ -259,47 +266,63 @@ export function isMovingWhilePassiveIncome(gameState: GameState) {
|
||||||
|
|
||||||
export function getHighScore() {
|
export function getHighScore() {
|
||||||
try {
|
try {
|
||||||
return parseInt(
|
return parseInt(localStorage.getItem("breakout-3-hs-short") || "0");
|
||||||
localStorage.getItem("breakout-3-hs-short" ) || "0",
|
|
||||||
);
|
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
return 0
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function highScoreText() {
|
export function highScoreText() {
|
||||||
|
|
||||||
if (getHighScore()) {
|
if (getHighScore()) {
|
||||||
return t("main_menu.high_score", { score: getHighScore() });
|
return t("main_menu.high_score", { score: getHighScore() });
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function reasonLevelIsLocked(
|
||||||
export function unlockCondition(levelIndex:number){
|
levelIndex: number,
|
||||||
if(levelIndex<7){
|
history: RunHistoryItem[],
|
||||||
return {}
|
) {
|
||||||
|
// Returns "" if level is unlocked, otherwise a string explaining how to unlock it
|
||||||
|
if (levelIndex <= 10) {
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
if (levelIndex < 20) {
|
if (levelIndex < 20) {
|
||||||
return {
|
const minScore = 100 * levelIndex;
|
||||||
minScore:100*levelIndex
|
return history.find((r) => r.score >= minScore)
|
||||||
}
|
? ""
|
||||||
|
: t("unlocks.minScore", { minScore });
|
||||||
}
|
}
|
||||||
const excluded: PerkId[] = [
|
const excluded: PerkId[] = [
|
||||||
'extra_levels','extra_life', "one_more_choice", "instant_upgrade"
|
"extra_levels",
|
||||||
]
|
"extra_life",
|
||||||
|
"one_more_choice",
|
||||||
|
"instant_upgrade",
|
||||||
|
];
|
||||||
|
|
||||||
const possibletargets = rawUpgrades.slice(0,Math.floor(levelIndex/2))
|
const possibletargets = rawUpgrades
|
||||||
.map(u=>u.id)
|
.slice(0, Math.floor(levelIndex / 2))
|
||||||
.filter(u=>!excluded.includes(u))
|
.map((u) => u)
|
||||||
.sort((a,b)=>Math.random()-0.5)
|
.filter((u) => !excluded.includes(u.id))
|
||||||
|
.sort((a, b) => hashCode(levelIndex + a.id) - hashCode(levelIndex + b.id));
|
||||||
|
|
||||||
const length=Math.ceil(levelIndex/30)
|
const length = Math.ceil(levelIndex / 30);
|
||||||
|
const required = possibletargets.slice(0, length);
|
||||||
return {
|
const forbidden = possibletargets.slice(length, length + length);
|
||||||
minScore:100*levelIndex*Math.pow(1.02,levelIndex),
|
const minScore = 100 * levelIndex * Math.floor(Math.pow(1.01, levelIndex));
|
||||||
withUpgrades : possibletargets.slice(0,length),
|
if (
|
||||||
withoutUpgrades : possibletargets.slice(length,length+length),
|
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>
|
</concept_node>
|
||||||
</children>
|
</children>
|
||||||
</folder_node>
|
</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>
|
<concept_node>
|
||||||
<name>restart</name>
|
<name>restart</name>
|
||||||
<description/>
|
<description/>
|
||||||
|
@ -368,7 +353,22 @@
|
||||||
</children>
|
</children>
|
||||||
</folder_node>
|
</folder_node>
|
||||||
<concept_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/>
|
<description/>
|
||||||
<comment/>
|
<comment/>
|
||||||
<translations>
|
<translations>
|
||||||
|
@ -448,7 +448,7 @@
|
||||||
</translation>
|
</translation>
|
||||||
<translation>
|
<translation>
|
||||||
<language>fr-FR</language>
|
<language>fr-FR</language>
|
||||||
<approved>true</approved>
|
<approved>false</approved>
|
||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
@ -2167,6 +2167,36 @@
|
||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</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>
|
<concept_node>
|
||||||
<name>level</name>
|
<name>level</name>
|
||||||
<description/>
|
<description/>
|
||||||
|
@ -2197,6 +2227,51 @@
|
||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</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>
|
<concept_node>
|
||||||
<name>title_upgrades</name>
|
<name>title_upgrades</name>
|
||||||
<description/>
|
<description/>
|
||||||
|
@ -3477,6 +3552,56 @@
|
||||||
</concept_node>
|
</concept_node>
|
||||||
</children>
|
</children>
|
||||||
</folder_node>
|
</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>
|
<folder_node>
|
||||||
<name>metamorphosis</name>
|
<name>metamorphosis</name>
|
||||||
<children>
|
<children>
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
"gameOver.cumulative_total": "Your total cumulative score went from {{startTs}} to {{endTs}}.",
|
"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.summary": "You dropped the ball after catching {{score}} coins.",
|
||||||
"gameOver.lost.title": "Game Over",
|
"gameOver.lost.title": "Game Over",
|
||||||
"gameOver.next_unlock": "Score {{points}} more points to reach the next unlock.",
|
|
||||||
"gameOver.restart": "Start a new game",
|
"gameOver.restart": "Start a new game",
|
||||||
"gameOver.stats.balls_lost": "Balls lost",
|
"gameOver.stats.balls_lost": "Balls lost",
|
||||||
"gameOver.stats.bricks_broken": "Bricks broken",
|
"gameOver.stats.bricks_broken": "Bricks broken",
|
||||||
|
@ -21,11 +20,12 @@
|
||||||
"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_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.summary": "This game is over. You stashed {{score}} coins. ",
|
||||||
"gameOver.win.title": "You completed this game",
|
"gameOver.win.title": "You completed this game",
|
||||||
"lab.help": "Try any build you want",
|
"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.menu_entry": "Creative mode",
|
||||||
"lab.reset": "Reset all to 0",
|
"lab.reset": "Reset all to 0",
|
||||||
"lab.select_level": "Select a level to play on",
|
"lab.select_level": "Select a level to play on",
|
||||||
|
@ -138,8 +138,13 @@
|
||||||
"score_panel.upgrades_picked": "Upgrades picked so far : ",
|
"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.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.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": "<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.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}}",
|
"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.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. ",
|
"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.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.help": "+{{lvl}} combo per brick broken, resets on left side hit",
|
||||||
"upgrades.left_is_lava.name": "Avoid left side",
|
"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.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.help": "Each coins can stain {{lvl}} brick(s) with its color",
|
||||||
"upgrades.metamorphosis.name": "Metamorphosis",
|
"upgrades.metamorphosis.name": "Metamorphosis",
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
"gameOver.cumulative_total": "Votre score total cumulé est passé de {{startTs}} à {{endTs}}.",
|
"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.summary": "Vous avez fait tomber la balle après avoir attrapé {{score}} pièces.",
|
||||||
"gameOver.lost.title": "Balle perdue",
|
"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.restart": "Nouvelle partie",
|
||||||
"gameOver.stats.balls_lost": "Balles perdues",
|
"gameOver.stats.balls_lost": "Balles perdues",
|
||||||
"gameOver.stats.bricks_broken": "Briques cassées",
|
"gameOver.stats.bricks_broken": "Briques cassées",
|
||||||
|
@ -21,11 +20,12 @@
|
||||||
"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_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.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",
|
||||||
"lab.help": "Essayez n'importe quel build",
|
"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.menu_entry": "Mode créatif",
|
||||||
"lab.reset": "RAZ toutes les améliorations",
|
"lab.reset": "RAZ toutes les améliorations",
|
||||||
"lab.select_level": "Sélectionnez un niveau sur lequel jouer",
|
"lab.select_level": "Sélectionnez un niveau sur lequel jouer",
|
||||||
|
@ -138,8 +138,13 @@
|
||||||
"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.",
|
"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.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": "<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.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}}",
|
"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.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",
|
"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.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.help": "+{{lvl}} combo par brique, RAZ en touchant le bord gauche",
|
||||||
"upgrades.left_is_lava.name": "Éviter le côté 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.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.help": "Chaque pièces peut tacher {{lvl}} brique(s) avec sa couleur",
|
||||||
"upgrades.metamorphosis.name": "Métamorphose",
|
"upgrades.metamorphosis.name": "Métamorphose",
|
||||||
|
|
|
@ -12,34 +12,35 @@ const backgrounds = _backgrounds as string[];
|
||||||
const palette = _palette as Palette;
|
const palette = _palette as Palette;
|
||||||
|
|
||||||
// let allLevels = _allLevels ;
|
// let allLevels = _allLevels ;
|
||||||
let allLevels=null
|
let allLevels = null;
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [selected, setSelected] = useState("W");
|
const [selected, setSelected] = useState("W");
|
||||||
const [applying, setApplying] = useState("");
|
const [applying, setApplying] = useState("");
|
||||||
const [levels, setLevels] = useState([]);
|
const [levels, setLevels] = useState([]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetch('http://localhost:4400/src/data/levels.json')
|
fetch("http://localhost:4400/src/data/levels.json")
|
||||||
.then(r=>r.json())
|
.then((r) => r.json())
|
||||||
.then(list=>{
|
.then((list) => {
|
||||||
setLevels(list as RawLevel[])
|
setLevels(list as RawLevel[]);
|
||||||
allLevels=list
|
allLevels = list;
|
||||||
})
|
});
|
||||||
},[])
|
}, []);
|
||||||
|
|
||||||
const updateLevel = (index: number, change: Partial<RawLevel>) => {
|
const updateLevel = (index: number, change: Partial<RawLevel>) => {
|
||||||
setLevels((list) =>
|
setLevels((list) =>
|
||||||
list.map((l, li) => (li === index ? { ...l, ...change } : l)),
|
list.map((l, li) => (li === index ? { ...l, ...change } : l)),
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
const deleteLevel = (index: number) => {
|
const deleteLevel = (index: number) => {
|
||||||
if (confirm("Delete level")) {
|
if (confirm("Delete level")) {
|
||||||
setLevels(levels.filter((l, i) => i !== index));
|
setLevels(levels.filter((l, i) => i !== index));
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if(!allLevels||JSON.stringify(allLevels) === JSON.stringify(levels)) return
|
if (!allLevels || JSON.stringify(allLevels) === JSON.stringify(levels))
|
||||||
|
return;
|
||||||
const timoutId = setTimeout(() => {
|
const timoutId = setTimeout(() => {
|
||||||
return fetch("http://localhost:4400/src/data/levels.json", {
|
return fetch("http://localhost:4400/src/data/levels.json", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
|
|
@ -35,12 +35,6 @@ export const allLevels = rawLevelsList
|
||||||
.filter((l) => !l.name.startsWith("icon:"))
|
.filter((l) => !l.name.startsWith("icon:"))
|
||||||
.map((l, li) => ({
|
.map((l, li) => ({
|
||||||
...l,
|
...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,
|
sortKey: ((Math.random() + 3) / 3.5) * l.bricksCount,
|
||||||
})) as Level[];
|
})) as Level[];
|
||||||
|
|
||||||
|
|
|
@ -41,17 +41,14 @@ migrate("remove_long_and_creative_mode_data", () => {
|
||||||
localStorage.getItem("breakout_71_runs_history") || "[]",
|
localStorage.getItem("breakout_71_runs_history") || "[]",
|
||||||
) as RunHistoryItem[];
|
) as RunHistoryItem[];
|
||||||
|
|
||||||
let cleaned=runsHistory.filter(r=> {
|
let cleaned = runsHistory.filter((r) => {
|
||||||
if('mode' in r){
|
if ("mode" in r) {
|
||||||
if(r.mode !== 'short'){
|
if (r.mode !== "short") {
|
||||||
return false
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return true;
|
||||||
})
|
});
|
||||||
if(cleaned.length!==runsHistory.length)
|
if (cleaned.length !== runsHistory.length)
|
||||||
localStorage.setItem(
|
localStorage.setItem("breakout_71_runs_history", JSON.stringify(cleaned));
|
||||||
"breakout_71_runs_history",
|
|
||||||
JSON.stringify(cleaned),
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,22 +1,30 @@
|
||||||
import { GameState, RunParams } from "./types";
|
import { GameState, RunParams } from "./types";
|
||||||
import { getTotalScore } from "./settings";
|
|
||||||
import { allLevels, upgrades } from "./loadGameData";
|
import { allLevels, upgrades } from "./loadGameData";
|
||||||
import {
|
import {
|
||||||
defaultSounds, getHighScore,
|
defaultSounds,
|
||||||
getPossibleUpgrades, highScoreText,
|
getHighScore,
|
||||||
|
getPossibleUpgrades,
|
||||||
|
highScoreText,
|
||||||
|
reasonLevelIsLocked,
|
||||||
makeEmptyPerksMap,
|
makeEmptyPerksMap,
|
||||||
sumOfValues,
|
sumOfValues,
|
||||||
} from "./game_utils";
|
} from "./game_utils";
|
||||||
import { dontOfferTooSoon, resetBalls } from "./gameStateMutators";
|
import { dontOfferTooSoon, resetBalls } from "./gameStateMutators";
|
||||||
import { isOptionOn } from "./options";
|
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
|
const firstLevel = params?.level
|
||||||
? allLevels.filter((l) => l.name === params?.level)
|
? unlocked.filter((l) => l.name === params?.level)
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
const restInRandomOrder = allLevels
|
const restInRandomOrder = unlocked
|
||||||
.filter((l) => totalScoreAtRunStart >= l.threshold)
|
|
||||||
.filter((l) => l.name !== params?.level)
|
.filter((l) => l.name !== params?.level)
|
||||||
.filter((l) => l.name !== params?.levelToAvoid)
|
.filter((l) => l.name !== params?.levelToAvoid)
|
||||||
.sort(() => Math.random() - 0.5);
|
.sort(() => Math.random() - 0.5);
|
||||||
|
@ -27,9 +35,7 @@ export function getRunLevels(totalScoreAtRunStart: number, params: RunParams) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function newGameState(params: RunParams): GameState {
|
export function newGameState(params: RunParams): GameState {
|
||||||
const totalScoreAtRunStart = getTotalScore();
|
const runLevels = getRunLevels(params);
|
||||||
|
|
||||||
const runLevels = getRunLevels(totalScoreAtRunStart, params);
|
|
||||||
|
|
||||||
const perks = { ...makeEmptyPerksMap(upgrades), ...(params?.perks || {}) };
|
const perks = { ...makeEmptyPerksMap(upgrades), ...(params?.perks || {}) };
|
||||||
|
|
||||||
|
@ -39,7 +45,6 @@ export function newGameState(params: RunParams): GameState {
|
||||||
currentLevel: 0,
|
currentLevel: 0,
|
||||||
upgradesOfferedFor: -1,
|
upgradesOfferedFor: -1,
|
||||||
perks,
|
perks,
|
||||||
bannedPerks: makeEmptyPerksMap(upgrades),
|
|
||||||
puckWidth: 200,
|
puckWidth: 200,
|
||||||
baseSpeed: 12,
|
baseSpeed: 12,
|
||||||
combo: 1,
|
combo: 1,
|
||||||
|
@ -80,7 +85,7 @@ export function newGameState(params: RunParams): GameState {
|
||||||
ballSize: 20,
|
ballSize: 20,
|
||||||
coinSize: 14,
|
coinSize: 14,
|
||||||
puckHeight: 20,
|
puckHeight: 20,
|
||||||
totalScoreAtRunStart,
|
totalScoreAtRunStart: getTotalScore(),
|
||||||
pauseUsesDuringRun: 0,
|
pauseUsesDuringRun: 0,
|
||||||
keyboardPuckSpeed: 0,
|
keyboardPuckSpeed: 0,
|
||||||
lastTick: performance.now(),
|
lastTick: performance.now(),
|
||||||
|
@ -110,7 +115,7 @@ export function newGameState(params: RunParams): GameState {
|
||||||
autoCleanUses: 0,
|
autoCleanUses: 0,
|
||||||
...defaultSounds(),
|
...defaultSounds(),
|
||||||
rerolls: 0,
|
rerolls: 0,
|
||||||
creative: sumOfValues(params.perks)>1
|
creative: sumOfValues(params.perks) > 1,
|
||||||
};
|
};
|
||||||
resetBalls(gameState);
|
resetBalls(gameState);
|
||||||
|
|
||||||
|
|
|
@ -97,13 +97,12 @@ export function render(gameState: GameState) {
|
||||||
haloCanvasCtx.fillStyle = level.color;
|
haloCanvasCtx.fillStyle = level.color;
|
||||||
haloCanvasCtx.fillRect(0, 0, width / haloScale, height / haloScale);
|
haloCanvasCtx.fillRect(0, 0, width / haloScale, height / haloScale);
|
||||||
|
|
||||||
|
const brightness = isOptionOn("extra_bright") ? 3 : 1;
|
||||||
const brightness = isOptionOn("extra_bright") ? 3:1
|
|
||||||
|
|
||||||
haloCanvasCtx.globalCompositeOperation = "lighten";
|
haloCanvasCtx.globalCompositeOperation = "lighten";
|
||||||
haloCanvasCtx.globalAlpha = 0.1;
|
haloCanvasCtx.globalAlpha = 0.1;
|
||||||
forEachLiveOne(gameState.coins, (coin) => {
|
forEachLiveOne(gameState.coins, (coin) => {
|
||||||
const color = getCoinRenderColor(gameState, coin)
|
const color = getCoinRenderColor(gameState, coin);
|
||||||
drawFuzzyBall(
|
drawFuzzyBall(
|
||||||
haloCanvasCtx,
|
haloCanvasCtx,
|
||||||
color,
|
color,
|
||||||
|
@ -111,7 +110,6 @@ export function render(gameState: GameState) {
|
||||||
coin.x / haloScale,
|
coin.x / haloScale,
|
||||||
coin.y / haloScale,
|
coin.y / haloScale,
|
||||||
);
|
);
|
||||||
|
|
||||||
});
|
});
|
||||||
haloCanvasCtx.globalAlpha = 0.1;
|
haloCanvasCtx.globalAlpha = 0.1;
|
||||||
gameState.balls.forEach((ball) => {
|
gameState.balls.forEach((ball) => {
|
||||||
|
@ -122,9 +120,8 @@ export function render(gameState: GameState) {
|
||||||
ball.x / haloScale,
|
ball.x / haloScale,
|
||||||
ball.y / haloScale,
|
ball.y / haloScale,
|
||||||
);
|
);
|
||||||
|
|
||||||
});
|
});
|
||||||
haloCanvasCtx.globalAlpha = 0.05
|
haloCanvasCtx.globalAlpha = 0.05;
|
||||||
gameState.bricks.forEach((color, index) => {
|
gameState.bricks.forEach((color, index) => {
|
||||||
if (!color) return;
|
if (!color) return;
|
||||||
const x = brickCenterX(gameState, index),
|
const x = brickCenterX(gameState, index),
|
||||||
|
@ -151,7 +148,6 @@ export function render(gameState: GameState) {
|
||||||
x / haloScale,
|
x / haloScale,
|
||||||
y / haloScale,
|
y / haloScale,
|
||||||
);
|
);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
ctx.globalAlpha = 1;
|
ctx.globalAlpha = 1;
|
||||||
|
@ -237,10 +233,10 @@ export function render(gameState: GameState) {
|
||||||
// Coins
|
// Coins
|
||||||
ctx.globalAlpha = 1;
|
ctx.globalAlpha = 1;
|
||||||
forEachLiveOne(gameState.coins, (coin) => {
|
forEachLiveOne(gameState.coins, (coin) => {
|
||||||
const color = getCoinRenderColor(gameState,coin)
|
const color = getCoinRenderColor(gameState, coin);
|
||||||
|
|
||||||
// ctx.globalCompositeOperation = "source-over";
|
// ctx.globalCompositeOperation = "source-over";
|
||||||
ctx.globalCompositeOperation = "source-over"
|
ctx.globalCompositeOperation = "source-over";
|
||||||
drawCoin(
|
drawCoin(
|
||||||
ctx,
|
ctx,
|
||||||
color,
|
color,
|
||||||
|
@ -249,12 +245,14 @@ export function render(gameState: GameState) {
|
||||||
coin.y,
|
coin.y,
|
||||||
(hasCombo && gameState.perks.asceticism && "#FF0000") ||
|
(hasCombo && gameState.perks.asceticism && "#FF0000") ||
|
||||||
(color === "#ffd300" && "#ffd300") ||
|
(color === "#ffd300" && "#ffd300") ||
|
||||||
(color == "#231f20" && gameState.level.color == "#000000" && "#FFFFFF")
|
(color == "#231f20" &&
|
||||||
|| (gameState.level.color ),
|
gameState.level.color == "#000000" &&
|
||||||
|
"#FFFFFF") ||
|
||||||
|
gameState.level.color,
|
||||||
coin.a,
|
coin.a,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
console.log(gameState.level.color)
|
console.log(gameState.level.color);
|
||||||
// Black shadow around balls
|
// Black shadow around balls
|
||||||
if (!isOptionOn("basic")) {
|
if (!isOptionOn("basic")) {
|
||||||
ctx.globalCompositeOperation = "source-over";
|
ctx.globalCompositeOperation = "source-over";
|
||||||
|
@ -278,7 +276,15 @@ export function render(gameState: GameState) {
|
||||||
const { x, y, time, color, size, duration } = flash;
|
const { x, y, time, color, size, duration } = flash;
|
||||||
const elapsed = gameState.levelTime - time;
|
const elapsed = gameState.levelTime - time;
|
||||||
ctx.globalAlpha = Math.min(1, 2 - (elapsed / duration) * 2) * 0.5;
|
ctx.globalAlpha = Math.min(1, 2 - (elapsed / duration) * 2) * 0.5;
|
||||||
drawBrick(gameState, ctx, color, x, y, -1, gameState.perks.clairvoyant >= 2);
|
drawBrick(
|
||||||
|
gameState,
|
||||||
|
ctx,
|
||||||
|
color,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
-1,
|
||||||
|
gameState.perks.clairvoyant >= 2,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
ctx.globalCompositeOperation = "screen";
|
ctx.globalCompositeOperation = "screen";
|
||||||
|
@ -587,7 +593,7 @@ export function renderAllBricks() {
|
||||||
const redColorOnAllBricks = hasCombo && isMovingWhilePassiveIncome(gameState);
|
const redColorOnAllBricks = hasCombo && isMovingWhilePassiveIncome(gameState);
|
||||||
|
|
||||||
const redRowReach = reachRedRowIndex(gameState);
|
const redRowReach = reachRedRowIndex(gameState);
|
||||||
const {clairvoyant}=gameState.perks
|
const { clairvoyant } = gameState.perks;
|
||||||
let offset = getDashOffset(gameState);
|
let offset = getDashOffset(gameState);
|
||||||
if (
|
if (
|
||||||
!(
|
!(
|
||||||
|
@ -653,7 +659,8 @@ export function renderAllBricks() {
|
||||||
redColorOnAllBricks;
|
redColorOnAllBricks;
|
||||||
|
|
||||||
canctx.globalCompositeOperation = "source-over";
|
canctx.globalCompositeOperation = "source-over";
|
||||||
drawBrick(gameState,
|
drawBrick(
|
||||||
|
gameState,
|
||||||
canctx,
|
canctx,
|
||||||
color,
|
color,
|
||||||
x,
|
x,
|
||||||
|
@ -662,8 +669,7 @@ export function renderAllBricks() {
|
||||||
clairvoyant >= 2,
|
clairvoyant >= 2,
|
||||||
);
|
);
|
||||||
if (gameState.brickHP[index] > 1 && clairvoyant) {
|
if (gameState.brickHP[index] > 1 && clairvoyant) {
|
||||||
|
canctx.globalCompositeOperation = "source-over";
|
||||||
canctx.globalCompositeOperation = "source-over"
|
|
||||||
drawText(
|
drawText(
|
||||||
canctx,
|
canctx,
|
||||||
gameState.brickHP[index].toString(),
|
gameState.brickHP[index].toString(),
|
||||||
|
@ -891,7 +897,7 @@ export function drawFuzzyBall(
|
||||||
size / 2,
|
size / 2,
|
||||||
);
|
);
|
||||||
gradient.addColorStop(0, color);
|
gradient.addColorStop(0, color);
|
||||||
console.log(color)
|
console.log(color);
|
||||||
gradient.addColorStop(0.3, color + "88");
|
gradient.addColorStop(0.3, color + "88");
|
||||||
gradient.addColorStop(0.6, color + "22");
|
gradient.addColorStop(0.6, color + "22");
|
||||||
gradient.addColorStop(1, "transparent");
|
gradient.addColorStop(1, "transparent");
|
||||||
|
@ -923,7 +929,11 @@ export function drawBrick(
|
||||||
const width = brx - tlx,
|
const width = brx - tlx,
|
||||||
height = bry - tly;
|
height = bry - tly;
|
||||||
|
|
||||||
const whiteBorder = (offset == -1 && color == "#231f20" && gameState.level.color == "#000000" && "#FFFFFF")
|
const whiteBorder =
|
||||||
|
offset == -1 &&
|
||||||
|
color == "#231f20" &&
|
||||||
|
gameState.level.color == "#000000" &&
|
||||||
|
"#FFFFFF";
|
||||||
|
|
||||||
const key =
|
const key =
|
||||||
"brick" +
|
"brick" +
|
||||||
|
@ -936,7 +946,9 @@ export function drawBrick(
|
||||||
"_" +
|
"_" +
|
||||||
offset +
|
offset +
|
||||||
"_" +
|
"_" +
|
||||||
borderOnly + '_' + whiteBorder;
|
borderOnly +
|
||||||
|
"_" +
|
||||||
|
whiteBorder;
|
||||||
|
|
||||||
if (!cachedGraphics[key]) {
|
if (!cachedGraphics[key]) {
|
||||||
const can = document.createElement("canvas");
|
const can = document.createElement("canvas");
|
||||||
|
@ -1072,7 +1084,6 @@ export function getDashOffset(gameState: GameState) {
|
||||||
|
|
||||||
function getCoinRenderColor(gameState: GameState, coin: Coin) {
|
function getCoinRenderColor(gameState: GameState, coin: Coin) {
|
||||||
if (gameState.perks.metamorphosis || isOptionOn("colorful_coins"))
|
if (gameState.perks.metamorphosis || isOptionOn("colorful_coins"))
|
||||||
return coin.color
|
return coin.color;
|
||||||
return "#ffd300"
|
return "#ffd300";
|
||||||
|
|
||||||
}
|
}
|
2
src/types.d.ts
vendored
2
src/types.d.ts
vendored
|
@ -18,7 +18,6 @@ export type Level = {
|
||||||
bricksCount: number;
|
bricksCount: number;
|
||||||
svg: string;
|
svg: string;
|
||||||
color: string;
|
color: string;
|
||||||
threshold: number;
|
|
||||||
sortKey: number;
|
sortKey: number;
|
||||||
credit?: string;
|
credit?: string;
|
||||||
};
|
};
|
||||||
|
@ -202,7 +201,6 @@ export type GameState = {
|
||||||
puckWidth: number;
|
puckWidth: number;
|
||||||
// perks the user currently has
|
// perks the user currently has
|
||||||
perks: PerksMap;
|
perks: PerksMap;
|
||||||
bannedPerks: PerksMap;
|
|
||||||
// Base speed of the ball in pixels/tick
|
// Base speed of the ball in pixels/tick
|
||||||
baseSpeed: number;
|
baseSpeed: number;
|
||||||
// Score multiplier
|
// Score multiplier
|
||||||
|
|
|
@ -654,4 +654,15 @@ export const rawUpgrades = [
|
||||||
t("upgrades.fountain_toss.help", { lvl, max: lvl * 30 }),
|
t("upgrades.fountain_toss.help", { lvl, max: lvl * 30 }),
|
||||||
fullHelp: t("upgrades.fountain_toss.fullHelp"),
|
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;
|
] as const;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue