mirror of
https://gitlab.com/lecarore/breakout71.git
synced 2025-04-27 15:36:16 -04:00
wip
This commit is contained in:
parent
cb90fef3a9
commit
401e9b4548
22 changed files with 1024 additions and 959 deletions
|
@ -29,8 +29,8 @@ android {
|
|||
applicationId = "me.lecaro.breakout"
|
||||
minSdk = 21
|
||||
targetSdk = 34
|
||||
versionCode = 29088937
|
||||
versionName = "29088937"
|
||||
versionCode = 29090246
|
||||
versionName = "29090246"
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables {
|
||||
useSupportLibrary = true
|
||||
|
|
File diff suppressed because one or more lines are too long
83
dist/index.html
vendored
83
dist/index.html
vendored
File diff suppressed because one or more lines are too long
|
@ -1,5 +1,5 @@
|
|||
// The version of the cache.
|
||||
const VERSION = "29088937";
|
||||
const VERSION = "29090246";
|
||||
|
||||
// The name of the cache
|
||||
const CACHE_NAME = `breakout-71-${VERSION}`;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { t } from "./i18n/i18n";
|
||||
import { isOptionOn } from "./options";
|
||||
|
||||
export let alertsOpen = 0,
|
||||
closeModal: null | (() => void) = null;
|
||||
|
@ -118,9 +119,6 @@ ${icon}
|
|||
<em>${help || ""}</em>
|
||||
</div>`;
|
||||
|
||||
if (tooltip) {
|
||||
button.setAttribute("data-tooltip", tooltip);
|
||||
}
|
||||
if (disabled) {
|
||||
button.setAttribute("disabled", "disabled");
|
||||
} else {
|
||||
|
@ -137,6 +135,31 @@ ${icon}
|
|||
className + (lastClickedItemIndex === index ? " needs-focus" : "");
|
||||
|
||||
addto.appendChild(button);
|
||||
|
||||
if (tooltip) {
|
||||
if (isOptionOn("mobile-mode")) {
|
||||
const helpBtn = document.createElement("button");
|
||||
helpBtn.innerText = "?";
|
||||
helpBtn.className = "help";
|
||||
let helpTooltip = document.createElement("div");
|
||||
helpTooltip.textContent = tooltip;
|
||||
helpTooltip.className = "help_button_tooltip";
|
||||
helpBtn.addEventListener("click", (e) => {
|
||||
e.stopPropagation();
|
||||
});
|
||||
helpBtn.addEventListener("touchstart", (e) => {
|
||||
e.stopPropagation();
|
||||
document.body.appendChild(helpTooltip);
|
||||
});
|
||||
helpBtn.addEventListener("touchend", (e) => {
|
||||
document.body.removeChild(helpTooltip);
|
||||
});
|
||||
|
||||
button.appendChild(helpBtn);
|
||||
} else {
|
||||
button.setAttribute("data-tooltip", tooltip);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
popup.addEventListener(
|
||||
|
|
|
@ -57,7 +57,7 @@ export async function openCreativeModePerksPicker() {
|
|||
};
|
||||
}),
|
||||
...customLevels.map((l) => ({
|
||||
icon: levelIconHTML(l.bricks, l.size, l.color),
|
||||
icon: levelIconHTML(l.bricks, l.size),
|
||||
text: l.name,
|
||||
value: l,
|
||||
disabled: !l.bricks.filter((b) => b !== "_").length,
|
||||
|
@ -93,7 +93,7 @@ export async function openCreativeModePerksPicker() {
|
|||
...upgrades
|
||||
.filter((u) => !noCreative.includes(u.id))
|
||||
.map((u) => ({
|
||||
icon: u.icon,
|
||||
icon: icons['icon:'+u.id],
|
||||
text: u.name,
|
||||
help:
|
||||
(creativeModePerks[u.id] || 0) +
|
||||
|
|
|
@ -1 +1 @@
|
|||
"29088937"
|
||||
"29090246"
|
||||
|
|
|
@ -197,6 +197,7 @@ body:not(.has-alert-open) #popup {
|
|||
&.actionsAsGrid.large > div > section {
|
||||
grid-template-columns: repeat(auto-fill, minmax(400px, 1fr));
|
||||
}
|
||||
|
||||
&.actionsAsGrid > div {
|
||||
max-width: none;
|
||||
|
||||
|
@ -504,10 +505,12 @@ h2.histogram-title strong {
|
|||
opacity 200ms,
|
||||
transform 200ms;
|
||||
z-index: 7;
|
||||
|
||||
&.hidden {
|
||||
opacity: 0;
|
||||
transform: translate(-20px, -20px) scale(0.5);
|
||||
}
|
||||
|
||||
&.visible {
|
||||
opacity: 0.8;
|
||||
transform: none;
|
||||
|
@ -523,6 +526,7 @@ h2.histogram-title strong {
|
|||
height: 40px;
|
||||
border: 1px solid;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
border-color: gold;
|
||||
z-index: 1;
|
||||
|
@ -530,6 +534,7 @@ h2.histogram-title strong {
|
|||
box-shadow: inset 2px 2px 4px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
}
|
||||
|
||||
.gridEdit {
|
||||
& > div {
|
||||
display: flex;
|
||||
|
@ -544,6 +549,7 @@ h2.histogram-title strong {
|
|||
.palette {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
& > span {
|
||||
&[data-selected="true"] {
|
||||
border: 2px solid white;
|
||||
|
@ -561,15 +567,18 @@ h2.histogram-title strong {
|
|||
z-index: 3;
|
||||
pointer-events: none;
|
||||
opacity: 1;
|
||||
|
||||
& > div {
|
||||
background: rgba(38, 38, 38, 0.5);
|
||||
position: relative;
|
||||
|
||||
> div {
|
||||
background: @purple;
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
transform-origin: top left;
|
||||
}
|
||||
|
||||
> strong {
|
||||
position: relative;
|
||||
padding: 0 5px;
|
||||
|
@ -579,6 +588,7 @@ h2.histogram-title strong {
|
|||
|
||||
.highlight {
|
||||
position: relative;
|
||||
|
||||
&:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
|
@ -588,7 +598,18 @@ h2.histogram-title strong {
|
|||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
|
||||
.not-highlighed {
|
||||
opacity: 0.8;
|
||||
color: #8a8a8a;
|
||||
}
|
||||
|
||||
.help_button_tooltip {
|
||||
position: fixed;
|
||||
z-index: 8;
|
||||
left: 0;
|
||||
top: 0;
|
||||
background: black;
|
||||
padding: 20px;
|
||||
border-bottom: 1px solid white;
|
||||
}
|
||||
|
|
19
src/game.ts
19
src/game.ts
|
@ -80,7 +80,8 @@ import {
|
|||
catchRateGood,
|
||||
clamp,
|
||||
levelTimeBest,
|
||||
levelTimeGood, miniMarkDown,
|
||||
levelTimeGood,
|
||||
miniMarkDown,
|
||||
missesBest,
|
||||
missesGood,
|
||||
wallBouncedBest,
|
||||
|
@ -925,7 +926,7 @@ async function openUnlockedUpgradesList() {
|
|||
ts < threshold
|
||||
? t("unlocks.minTotalScore", { score: threshold })
|
||||
: help(1),
|
||||
tooltip:ts < threshold ? '': fullHelp,
|
||||
tooltip: ts < threshold ? "" : fullHelp,
|
||||
}));
|
||||
|
||||
const tryOn = await asyncAlert<RunParams>({
|
||||
|
@ -935,17 +936,19 @@ async function openUnlockedUpgradesList() {
|
|||
}),
|
||||
content: [
|
||||
t("unlocks.intro", { ts }),
|
||||
upgradeActions.find((u) => u.disabled) ? t("unlocks.greyed_out_help") : "",
|
||||
upgradeActions.find((u) => u.disabled)
|
||||
? t("unlocks.greyed_out_help")
|
||||
: "",
|
||||
miniMarkDown(t("unlocks.category.beginner")),
|
||||
...upgradeActions.filter(u=>u.category==categories.beginner),
|
||||
...upgradeActions.filter((u) => u.category == categories.beginner),
|
||||
miniMarkDown(t("unlocks.category.combo")),
|
||||
...upgradeActions.filter(u=>u.category==categories.combo),
|
||||
...upgradeActions.filter((u) => u.category == categories.combo),
|
||||
miniMarkDown(t("unlocks.category.combo_boost")),
|
||||
...upgradeActions.filter(u=>u.category==categories.combo_boost),
|
||||
...upgradeActions.filter((u) => u.category == categories.combo_boost),
|
||||
miniMarkDown(t("unlocks.category.simple")),
|
||||
...upgradeActions.filter(u=>u.category==categories.simple),
|
||||
...upgradeActions.filter((u) => u.category == categories.simple),
|
||||
miniMarkDown(t("unlocks.category.advanced")),
|
||||
...upgradeActions.filter(u=>u.category==categories.advanced),
|
||||
...upgradeActions.filter((u) => u.category == categories.advanced),
|
||||
],
|
||||
allowClose: true,
|
||||
// className: "actionsAsGrid large",
|
||||
|
|
|
@ -16,7 +16,6 @@ import {
|
|||
} from "./settings";
|
||||
import { stopRecording } from "./recording";
|
||||
import { asyncAlert } from "./asyncAlert";
|
||||
import { rawUpgrades } from "./upgrades";
|
||||
import { editRawLevelList } from "./levelEditor";
|
||||
import { openCreativeModePerksPicker } from "./creative";
|
||||
|
||||
|
@ -52,7 +51,7 @@ export function gameOver(title: string, intro: string) {
|
|||
// unlocks
|
||||
const endTs = getTotalScore();
|
||||
const startTs = endTs - gameState.score;
|
||||
const unlockedPerks = rawUpgrades.filter(
|
||||
const unlockedPerks = upgrades.filter(
|
||||
(o) => o.threshold > startTs && o.threshold < endTs,
|
||||
);
|
||||
|
||||
|
|
|
@ -11,7 +11,6 @@ import {
|
|||
import { icons, upgrades } from "./loadGameData";
|
||||
import { t } from "./i18n/i18n";
|
||||
import { clamp } from "./pure_functions";
|
||||
import { rawUpgrades } from "./upgrades";
|
||||
import { hashCode } from "./getLevelBackground";
|
||||
import { getSettingValue, getTotalScore } from "./settings";
|
||||
import { isOptionOn } from "./options";
|
||||
|
@ -132,7 +131,7 @@ export function pickedUpgradesHTMl(gameState: GameState) {
|
|||
state,
|
||||
html: `
|
||||
<div class="upgrade ${["??", "used", "banned", "free"][state]}">
|
||||
${u.icon}
|
||||
${icons['icon:'+u.id]}
|
||||
<p>
|
||||
<strong>${u.name}</strong>
|
||||
${u.help(Math.max(1, gameState.perks[u.id]))}
|
||||
|
@ -310,7 +309,7 @@ function isExcluded(id: PerkId) {
|
|||
"slow_down",
|
||||
]);
|
||||
// Avoid excluding a perk that's needed for the required one
|
||||
rawUpgrades.forEach((u) => {
|
||||
upgrades.forEach((u) => {
|
||||
if (u.requires) excluded.add(u.requires);
|
||||
});
|
||||
}
|
||||
|
@ -323,7 +322,7 @@ export function getLevelUnlockCondition(levelIndex: number) {
|
|||
minScore = Math.max(-1000 + 100 * levelIndex, 0);
|
||||
|
||||
if (levelIndex > 20) {
|
||||
const possibletargets = [...rawUpgrades]
|
||||
const possibletargets = [...upgrades]
|
||||
.slice(0, Math.floor(levelIndex / 2))
|
||||
.filter((u) => !isExcluded(u.id))
|
||||
.sort(
|
||||
|
|
|
@ -39,7 +39,7 @@ export function helpMenuEntry() {
|
|||
...upgrades.map(
|
||||
(u) => `
|
||||
<div class="upgrade used">
|
||||
${u.icon}
|
||||
${icons['icon:'+u.id]}
|
||||
<p>
|
||||
<strong>${u.name}</strong><br/>
|
||||
${u.help(1)}
|
||||
|
|
|
@ -40,7 +40,7 @@ async function openLevelEditorLevelsList() {
|
|||
content: [
|
||||
...customLevels.map((l, li) => ({
|
||||
text: l.name,
|
||||
icon: levelIconHTML(l.bricks, l.size, l.color),
|
||||
icon: levelIconHTML(l.bricks, l.size),
|
||||
value() {
|
||||
editRawLevelList(li);
|
||||
},
|
||||
|
|
|
@ -10,7 +10,6 @@ const levelIconHTMLCanvasCtx =
|
|||
export function levelIconHTML(
|
||||
bricks: string[],
|
||||
levelSize: number,
|
||||
color: string,
|
||||
) {
|
||||
const size = 46;
|
||||
const c = levelIconHTMLCanvas;
|
||||
|
|
|
@ -9,7 +9,6 @@ import {
|
|||
levelCodeToRawLevel,
|
||||
} from "../pure_functions";
|
||||
|
||||
|
||||
const palette = _palette as Palette;
|
||||
|
||||
let allLevels = null;
|
||||
|
@ -22,13 +21,22 @@ function App() {
|
|||
fetch("http://localhost:4400/src/data/levels.json")
|
||||
.then((r) => r.json())
|
||||
.then((lvls) => {
|
||||
|
||||
const cleaned = lvls.map(l=>({name:l.name, size:l.size, bricks:(l.bricks+'_'.repeat(l.size*l.size)).slice(0,l.size*l.size), credit:l.credit||''}))
|
||||
const cleaned = lvls.map((l) => ({
|
||||
name: l.name,
|
||||
size: l.size,
|
||||
bricks: (l.bricks + "_".repeat(l.size * l.size)).slice(
|
||||
0,
|
||||
l.size * l.size,
|
||||
),
|
||||
credit: l.credit || "",
|
||||
}));
|
||||
const sorted = [
|
||||
...cleaned.filter(l=>l.name.match('icon:')).sort((a,b)=>a.name>b.name ? 1:-1),
|
||||
...cleaned.filter(l=>!l.name.match('icon:'))
|
||||
]
|
||||
setLevels(sorted as RawLevel[])
|
||||
...cleaned
|
||||
.filter((l) => l.name.match("icon:"))
|
||||
.sort((a, b) => (a.name > b.name ? 1 : -1)),
|
||||
...cleaned.filter((l) => !l.name.match("icon:")),
|
||||
];
|
||||
setLevels(sorted as RawLevel[]);
|
||||
allLevels = sorted;
|
||||
});
|
||||
}, []);
|
||||
|
|
|
@ -30,18 +30,6 @@ describe("json data checks", () => {
|
|||
it("Has a few colors", () => {
|
||||
expect(Object.keys(_palette).length).toBeGreaterThan(10);
|
||||
});
|
||||
it("Avoids dark bricks on dark bg", () => {
|
||||
const levelsWithDarkBricksAndBG = _rawLevelsList
|
||||
.filter((l) => !l.color && !l.name.match(/^icon:/))
|
||||
.map((l) => ({
|
||||
name: l.name,
|
||||
bricks: l.bricks.split("").filter((c) => c !== "_").length,
|
||||
darkBricks: l.bricks.split("").filter((c) => c === "g").length,
|
||||
}))
|
||||
.filter((l) => l.darkBricks > 0.05 * l.bricks);
|
||||
|
||||
expect(levelsWithDarkBricksAndBG).toEqual([]);
|
||||
});
|
||||
it("Has an _appVersion", () => {
|
||||
expect(parseInt(_appVersion)).toBeGreaterThan(2000);
|
||||
});
|
||||
|
|
|
@ -8,6 +8,7 @@ import { levelIconHTML } from "./levelIcon";
|
|||
|
||||
import { automaticBackgroundColor } from "./pure_functions";
|
||||
|
||||
export const upgrades = [...rawUpgrades].sort((a, b) => a.category - b.category || a.threshold - b.threshold) as Upgrade[];
|
||||
const palette = _palette as Palette;
|
||||
|
||||
const rawLevelsList = _rawLevelsList as RawLevel[];
|
||||
|
@ -22,7 +23,7 @@ export function transformRawLevel(level: RawLevel) {
|
|||
.map((c) => palette[c])
|
||||
.slice(0, level.size * level.size);
|
||||
const bricksCount = bricks.filter((i) => i).length;
|
||||
const icon = levelIconHTML(bricks, level.size, level.color);
|
||||
const icon = levelIconHTML(bricks, level.size);
|
||||
icons[level.name] = icon;
|
||||
return {
|
||||
...level,
|
||||
|
@ -43,7 +44,3 @@ export const allLevels = allLevelsAndIcons.filter(
|
|||
(l) => !l.name.startsWith("icon:"),
|
||||
);
|
||||
|
||||
export const upgrades = rawUpgrades.map((u) => ({
|
||||
...u,
|
||||
icon: icons["icon:" + u.id],
|
||||
})) as Upgrade[];
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { getHistory } from "./gameOver";
|
||||
import { appVersion, icons } from "./loadGameData";
|
||||
import {appVersion, icons, upgrades} from "./loadGameData";
|
||||
import { t } from "./i18n/i18n";
|
||||
import { asyncAlert } from "./asyncAlert";
|
||||
import { rawUpgrades } from "./upgrades";
|
||||
import { getSettingValue, setSettingValue } from "./settings";
|
||||
|
||||
export function runHistoryViewerMenuEntry() {
|
||||
|
@ -31,7 +30,7 @@ export function runHistoryViewerMenuEntry() {
|
|||
label: t("history.columns.score"),
|
||||
field: (r) => r.score,
|
||||
},
|
||||
...rawUpgrades.map((u) => ({
|
||||
...upgrades.map((u) => ({
|
||||
label: icons["icon:" + u.id],
|
||||
tooltip: u.name,
|
||||
field: (r) => r.perks?.[u.id] || 0,
|
||||
|
|
|
@ -31,7 +31,7 @@ export async function openStartingPerksEditor() {
|
|||
const buttons = avaliable.map((u) => {
|
||||
const checked = isStartingPerk(u);
|
||||
return {
|
||||
icon: u.icon,
|
||||
icon: icons['icon:'+u.id],
|
||||
text: u.name,
|
||||
tooltip: u.help(1),
|
||||
value: [u],
|
||||
|
|
4
src/types.d.ts
vendored
4
src/types.d.ts
vendored
|
@ -7,7 +7,6 @@ export type RawLevel = {
|
|||
name: string;
|
||||
size: number;
|
||||
bricks: string;
|
||||
color: string;
|
||||
credit?: string;
|
||||
};
|
||||
export type Level = {
|
||||
|
@ -28,8 +27,7 @@ export type Upgrade = {
|
|||
gift: boolean;
|
||||
id: PerkId;
|
||||
name: string;
|
||||
category: string;
|
||||
icon: string;
|
||||
category: number;
|
||||
max: number;
|
||||
help: (lvl: number) => string;
|
||||
fullHelp: string;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { t } from "./i18n/i18n";
|
||||
|
||||
import { comboKeepingRate } from "./pure_functions";
|
||||
import { PerkId } from "./types";
|
||||
import {PerkId, Upgrade} from "./types";
|
||||
|
||||
// Those perks are excluded from creative mode
|
||||
export const noCreative: PerkId[] = [
|
||||
|
@ -16,7 +16,7 @@ export const categories={
|
|||
combo_boost: 2.5,
|
||||
simple: 3,
|
||||
advanced: 4,
|
||||
}
|
||||
};
|
||||
|
||||
export const rawUpgrades = [
|
||||
{
|
||||
|
@ -314,7 +314,8 @@ export const rawUpgrades = [
|
|||
id: "asceticism",
|
||||
max: 1,
|
||||
name: t("upgrades.asceticism.name"),
|
||||
help: (lvl: number) => t("upgrades.asceticism.tooltip", { combo: lvl * 3 }),
|
||||
help: (lvl: number) =>
|
||||
t("upgrades.asceticism.tooltip", { combo: lvl * 3 }),
|
||||
fullHelp: t("upgrades.asceticism.verbose_description"),
|
||||
},
|
||||
// Regular
|
||||
|
@ -349,7 +350,8 @@ export const rawUpgrades = [
|
|||
gift: true,
|
||||
max: 6,
|
||||
name: t("upgrades.multiball.name"),
|
||||
help: (lvl: number) => t("upgrades.multiball.tooltip", { count: lvl + 1 }),
|
||||
help: (lvl: number) =>
|
||||
t("upgrades.multiball.tooltip", { count: lvl + 1 }),
|
||||
fullHelp: t("upgrades.multiball.verbose_description"),
|
||||
},
|
||||
{
|
||||
|
@ -425,7 +427,8 @@ export const rawUpgrades = [
|
|||
id: "bricks_attract_coins",
|
||||
max: 3,
|
||||
name: t("upgrades.bricks_attract_coins.name"),
|
||||
help: (lvl: number) => t("upgrades.bricks_attract_coins.tooltip", { lvl }),
|
||||
help: (lvl: number) =>
|
||||
t("upgrades.bricks_attract_coins.tooltip", { lvl }),
|
||||
fullHelp: t("upgrades.bricks_attract_coins.verbose_description"),
|
||||
},
|
||||
{
|
||||
|
@ -685,7 +688,8 @@ export const rawUpgrades = [
|
|||
id: "buoy",
|
||||
max: 3,
|
||||
name: t("upgrades.buoy.name"),
|
||||
help: (lvl: number) => t("upgrades.buoy.tooltip", { duration: lvl * 0.5 }),
|
||||
help: (lvl: number) =>
|
||||
t("upgrades.buoy.tooltip", { duration: lvl * 0.5 }),
|
||||
fullHelp: t("upgrades.buoy.verbose_description"),
|
||||
},
|
||||
{
|
||||
|
@ -883,4 +887,5 @@ export const rawUpgrades = [
|
|||
t("upgrades.sturdy_bricks.tooltip", { lvl, percent: lvl * 50 }),
|
||||
fullHelp: t("upgrades.sturdy_bricks.verbose_description"),
|
||||
},
|
||||
].sort((a,b)=>(a.category-b.category) || (a.threshold-b.threshold)) as const;
|
||||
] as const
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue