Build and deploy of version 29028296

This commit is contained in:
Renan LE CARO 2025-03-11 13:56:42 +01:00
parent a136475f88
commit 8b1278cb55
8 changed files with 325 additions and 3926 deletions

View file

@ -11,8 +11,8 @@ android {
applicationId = "me.lecaro.breakout" applicationId = "me.lecaro.breakout"
minSdk = 21 minSdk = 21
targetSdk = 34 targetSdk = 34
versionCode = 29022953 versionCode = 29028296
versionName = "29022953" versionName = "29028296"
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

3807
dist/index.html vendored

File diff suppressed because one or more lines are too long

View file

@ -12,19 +12,20 @@ import {
RunStats, RunStats,
Upgrade, Upgrade,
} from "./types"; } from "./types";
import { OptionId, options} from "./options"; import { OptionId, options } from "./options";
const MAX_COINS = 400; const MAX_COINS = 400;
const MAX_PARTICLES = 600; const MAX_PARTICLES = 600;
export const gameCanvas = document.getElementById("game") as HTMLCanvasElement; export const gameCanvas = document.getElementById("game") as HTMLCanvasElement;
const ctx = gameCanvas.getContext("2d", { alpha: false }) as CanvasRenderingContext2D; const ctx = gameCanvas.getContext("2d", {
alpha: false,
}) as CanvasRenderingContext2D;
const puckColor = "#FFF"; const puckColor = "#FFF";
let ballSize = 20; let ballSize = 20;
const coinSize = Math.round(ballSize * 0.8); const coinSize = Math.round(ballSize * 0.8);
const puckHeight = ballSize; const puckHeight = ballSize;
let runLevels: Level[] = []; let runLevels: Level[] = [];
let currentLevel = 0; let currentLevel = 0;
@ -39,11 +40,11 @@ bombSVG.src =
// Whatever // Whatever
let puckWidth = 200; let puckWidth = 200;
const makeEmptyPerksMap = ()=>{ const makeEmptyPerksMap = () => {
const p = {} as any const p = {} as any;
upgrades.forEach(u=>p[u.id]=0) upgrades.forEach((u) => (p[u.id] = 0));
return p as PerksMap return p as PerksMap;
} };
const perks: PerksMap = makeEmptyPerksMap(); const perks: PerksMap = makeEmptyPerksMap();
@ -166,8 +167,12 @@ background.addEventListener("load", () => {
needsRender = true; needsRender = true;
}); });
let lastWidth = 0,
lastHeight = 0;
export const fitSize = () => { export const fitSize = () => {
const { width, height } = gameCanvas.getBoundingClientRect(); const { width, height } = gameCanvas.getBoundingClientRect();
lastWidth = width;
lastHeight = height;
gameCanvas.width = width; gameCanvas.width = width;
gameCanvas.height = height; gameCanvas.height = height;
ctx.fillStyle = currentLevelInfo()?.color || "black"; ctx.fillStyle = currentLevelInfo()?.color || "black";
@ -177,12 +182,10 @@ export const fitSize = () => {
backgroundCanvas.height = height; backgroundCanvas.height = height;
gameZoneHeight = isSettingOn("mobile-mode") ? (height * 80) / 100 : height; gameZoneHeight = isSettingOn("mobile-mode") ? (height * 80) / 100 : height;
const baseWidth = Math.round( const baseWidth = Math.round(Math.min(lastWidth, gameZoneHeight * 0.73));
Math.min(gameCanvas.width, gameZoneHeight * 0.73),
);
brickWidth = Math.floor(baseWidth / gridSize / 2) * 2; brickWidth = Math.floor(baseWidth / gridSize / 2) * 2;
gameZoneWidth = brickWidth * gridSize; gameZoneWidth = brickWidth * gridSize;
offsetX = Math.floor((gameCanvas.width - gameZoneWidth) / 2); offsetX = Math.floor((lastWidth - gameZoneWidth) / 2);
offsetXRoundedDown = offsetX; offsetXRoundedDown = offsetX;
if (offsetX < ballSize) offsetXRoundedDown = 0; if (offsetX < ballSize) offsetXRoundedDown = 0;
gameZoneWidthRoundedUp = width - 2 * offsetXRoundedDown; gameZoneWidthRoundedUp = width - 2 * offsetXRoundedDown;
@ -202,6 +205,12 @@ export const fitSize = () => {
window.addEventListener("resize", fitSize); window.addEventListener("resize", fitSize);
window.addEventListener("fullscreenchange", fitSize); window.addEventListener("fullscreenchange", fitSize);
setInterval(() => {
// Sometimes, the page changes size without triggering the event (when switching to fullscreen, closing debug panel..)
const { width, height } = gameCanvas.getBoundingClientRect();
if (width !== lastWidth || height !== lastHeight) fitSize();
}, 1000);
function recomputeTargetBaseSpeed() { function recomputeTargetBaseSpeed() {
// We never want the ball to completely stop, it will move at least 3px per frame // We never want the ball to completely stop, it will move at least 3px per frame
baseSpeed = Math.max( baseSpeed = Math.max(
@ -250,7 +259,7 @@ function spawnExplosion(
vy: (Math.random() - 0.5) * 30, vy: (Math.random() - 0.5) * 30,
color, color,
duration, duration,
ethereal:false, ethereal: false,
}); });
} }
} }
@ -280,7 +289,7 @@ function addToScore(coin: Coin) {
color: coin.color, color: coin.color,
x: coin.previousX, x: coin.previousX,
y: coin.previousY, y: coin.previousY,
vx: (gameCanvas.width - coin.x) / 100, vx: (lastWidth - coin.x) / 100,
vy: -coin.y / 100, vy: -coin.y / 100,
ethereal: true, ethereal: true,
}); });
@ -306,15 +315,15 @@ function resetBalls() {
} }
for (let i = 0; i < count; i++) { for (let i = 0; i < count; i++) {
const x = puck - puckWidth / 2 + perBall * (i + 1); const x = puck - puckWidth / 2 + perBall * (i + 1);
const vx=Math.random() > 0.5 ? baseSpeed : -baseSpeed const vx = Math.random() > 0.5 ? baseSpeed : -baseSpeed;
balls.push({ balls.push({
x, x,
previousX: x, previousX: x,
y: gameZoneHeight - 1.5 * ballSize, y: gameZoneHeight - 1.5 * ballSize,
previousY: gameZoneHeight - 1.5 * ballSize, previousY: gameZoneHeight - 1.5 * ballSize,
vx , vx,
previousVX:vx, previousVX: vx,
vy: -baseSpeed, vy: -baseSpeed,
previousVY: -baseSpeed, previousVY: -baseSpeed,
@ -341,9 +350,9 @@ function putBallsAtPuck() {
ball.y = gameZoneHeight - 1.5 * ballSize; ball.y = gameZoneHeight - 1.5 * ballSize;
ball.previousY = ball.y; ball.previousY = ball.y;
ball.vx = Math.random() > 0.5 ? baseSpeed : -baseSpeed; ball.vx = Math.random() > 0.5 ? baseSpeed : -baseSpeed;
ball.previousVX=ball.vx ball.previousVX = ball.vx;
ball.vy = -baseSpeed; ball.vy = -baseSpeed;
ball.previousVY=ball.vy ball.previousVY = ball.vy;
ball.sx = 0; ball.sx = 0;
ball.sy = 0; ball.sy = 0;
ball.hitItem = []; ball.hitItem = [];
@ -484,8 +493,9 @@ function getPossibleUpgrades() {
.filter((u) => !u?.requires || perks[u?.requires]); .filter((u) => !u?.requires || perks[u?.requires]);
} }
function shuffleLevels(nameToAvoid :string|null= null) { function shuffleLevels(nameToAvoid: string | null = null) {
const target = nextRunOverrides?.level; const target = nextRunOverrides?.level;
delete nextRunOverrides.level;
const firstLevel = nextRunOverrides?.level const firstLevel = nextRunOverrides?.level
? allLevels.filter((l) => l.name === target) ? allLevels.filter((l) => l.name === target)
: []; : [];
@ -502,7 +512,7 @@ function shuffleLevels(nameToAvoid :string|null= null) {
} }
function getUpgraderUnlockPoints() { function getUpgraderUnlockPoints() {
let list = [] as {threshold:number,title:string}[]; let list = [] as { threshold: number; title: string }[];
upgrades.forEach((u) => { upgrades.forEach((u) => {
if (u.threshold) { if (u.threshold) {
@ -803,8 +813,8 @@ function bordersHitCheck(coin: Coin | Ball, radius: number, delta: number) {
coin.vy *= -1; coin.vy *= -1;
vhit = 1; vhit = 1;
} }
if (coin.x > gameCanvas.width - offsetXRoundedDown - radius) { if (coin.x > lastWidth - offsetXRoundedDown - radius) {
coin.x = gameCanvas.width - offsetXRoundedDown - radius; coin.x = lastWidth - offsetXRoundedDown - radius;
coin.vx *= -1; coin.vx *= -1;
hhit = 1; hhit = 1;
} }
@ -902,7 +912,7 @@ function tick() {
puckHeight puckHeight
) { ) {
addToScore(coin); addToScore(coin);
} else if (coin.y > gameCanvas.height + coinRadius) { } else if (coin.y > lastHeight + coinRadius) {
coin.destroyed = true; coin.destroyed = true;
if (perks.compound_interest) { if (perks.compound_interest) {
resetCombo(coin.x, coin.y); resetCombo(coin.x, coin.y);
@ -1275,7 +1285,7 @@ function ballTick(ball: Ball, delta: number) {
y: ball.y, y: ball.y,
vx: (Math.random() - 0.5) * baseSpeed, vx: (Math.random() - 0.5) * baseSpeed,
vy: (Math.random() - 0.5) * baseSpeed, vy: (Math.random() - 0.5) * baseSpeed,
ethereal:false, ethereal: false,
}); });
ball.sparks = 0; ball.sparks = 0;
} }
@ -1761,15 +1771,15 @@ function render() {
if (level.svg && background.width && background.complete) { if (level.svg && background.width && background.complete) {
if (backgroundCanvas.title !== level.name) { if (backgroundCanvas.title !== level.name) {
backgroundCanvas.title = level.name; backgroundCanvas.title = level.name;
backgroundCanvas.width = gameCanvas.width; backgroundCanvas.width = lastWidth;
backgroundCanvas.height = gameCanvas.height; backgroundCanvas.height = lastHeight;
const bgctx = backgroundCanvas.getContext( const bgctx = backgroundCanvas.getContext(
"2d", "2d",
) as CanvasRenderingContext2D; ) as CanvasRenderingContext2D;
bgctx.fillStyle = level.color || "#000"; bgctx.fillStyle = level.color || "#000";
bgctx.fillRect(0, 0, gameCanvas.width, gameCanvas.height); bgctx.fillRect(0, 0, lastWidth, lastHeight);
const pattern=ctx.createPattern(background, "repeat") const pattern = ctx.createPattern(background, "repeat");
if(pattern){ if (pattern) {
bgctx.fillStyle = pattern; bgctx.fillStyle = pattern;
bgctx.fillRect(0, 0, width, height); bgctx.fillRect(0, 0, width, height);
} }
@ -1940,8 +1950,8 @@ function render() {
"Press and hold here to play", "Press and hold here to play",
puckColor, puckColor,
puckHeight, puckHeight,
gameCanvas.width / 2, lastWidth / 2,
gameZoneHeight + (gameCanvas.height - gameZoneHeight) / 2, gameZoneHeight + (lastHeight - gameZoneHeight) / 2,
); );
} }
} else if (redBottom) { } else if (redBottom) {
@ -1961,7 +1971,7 @@ function render() {
} }
let cachedBricksRender = document.createElement("canvas"); let cachedBricksRender = document.createElement("canvas");
let cachedBricksRenderKey = ''; let cachedBricksRenderKey = "";
function renderAllBricks() { function renderAllBricks() {
ctx.globalAlpha = 1; ctx.globalAlpha = 1;
@ -2016,7 +2026,7 @@ function renderAllBricks() {
ctx.drawImage(cachedBricksRender, offsetX, 0); ctx.drawImage(cachedBricksRender, offsetX, 0);
} }
let cachedGraphics : {[k:string]:HTMLCanvasElement}= {}; let cachedGraphics: { [k: string]: HTMLCanvasElement } = {};
function drawPuck( function drawPuck(
ctx: CanvasRenderingContext2D, ctx: CanvasRenderingContext2D,
@ -2560,7 +2570,7 @@ window.addEventListener("visibilitychange", () => {
const scoreDisplay = document.getElementById("score") as HTMLButtonElement; const scoreDisplay = document.getElementById("score") as HTMLButtonElement;
let alertsOpen = 0, let alertsOpen = 0,
closeModal :null |( ()=>void) = null; closeModal: null | (() => void) = null;
type AsyncAlertAction<t> = { type AsyncAlertAction<t> = {
text?: string; text?: string;
@ -2569,7 +2579,8 @@ type AsyncAlertAction<t> = {
disabled?: boolean; disabled?: boolean;
icon?: string; icon?: string;
className?: string; className?: string;
} };
function asyncAlert<t>({ function asyncAlert<t>({
title, title,
text, text,
@ -2679,13 +2690,13 @@ ${icon}
} }
// Settings // Settings
let cachedSettings : Partial<{[key in OptionId]:boolean}>= {}; let cachedSettings: Partial<{ [key in OptionId]: boolean }> = {};
export function isSettingOn(key: OptionId) { export function isSettingOn(key: OptionId) {
if (typeof cachedSettings[key] == "undefined") { if (typeof cachedSettings[key] == "undefined") {
try { try {
const ls=localStorage.getItem("breakout-settings-enable-" + key) const ls = localStorage.getItem("breakout-settings-enable-" + key);
if(ls) cachedSettings[key] = JSON.parse(ls) as boolean; if (ls) cachedSettings[key] = JSON.parse(ls) as boolean;
} catch (e) { } catch (e) {
console.warn(e); console.warn(e);
} }
@ -2749,7 +2760,8 @@ document.getElementById("menu")?.addEventListener("click", (e) => {
async function openSettingsPanel() { async function openSettingsPanel() {
pause(true); pause(true);
const actions :AsyncAlertAction<()=>void>[]= [{ const actions: AsyncAlertAction<() => void>[] = [
{
text: "Resume", text: "Resume",
help: "Return to your run", help: "Return to your run",
value() {}, value() {},
@ -2760,9 +2772,10 @@ async function openSettingsPanel() {
value() { value() {
openUnlocksList(); openUnlocksList();
}, },
}]; },
];
for (const key of Object.keys(options) as OptionId[] ) { for (const key of Object.keys(options) as OptionId[]) {
if (options[key]) if (options[key])
actions.push({ actions.push({
disabled: options[key].disabled(), disabled: options[key].disabled(),
@ -2779,25 +2792,25 @@ async function openSettingsPanel() {
} }
const creativeModeThreshold = Math.max(...upgrades.map((u) => u.threshold)); const creativeModeThreshold = Math.max(...upgrades.map((u) => u.threshold));
if(document.fullscreenEnabled || document.webkitFullscreenEnabled){ if (document.fullscreenEnabled || document.webkitFullscreenEnabled) {
if(document.fullscreenElement !== null){ if (document.fullscreenElement !== null) {
actions.push( { actions.push({
text: "Exit Fullscreen", text: "Exit Fullscreen",
icon: icons["icon:exit_fullscreen"], icon: icons["icon:exit_fullscreen"],
help: "Might not work on some machines", help: "Might not work on some machines",
value() { value() {
toggleFullScreen(); toggleFullScreen();
}, },
} ) });
}else{ } else {
actions.push({ actions.push({
icon: icons["icon:fullscreen"], icon: icons["icon:fullscreen"],
text: "Fullscreen", text: "Fullscreen",
help: "Might not work on some machines", help: "Might not work on some machines",
value() { value() {
toggleFullScreen(); toggleFullScreen();
} },
}) });
} }
} }
actions.push({ actions.push({
@ -2808,7 +2821,7 @@ async function openSettingsPanel() {
: "Test runs with custom perks", : "Test runs with custom perks",
disabled: getTotalScore() < creativeModeThreshold, disabled: getTotalScore() < creativeModeThreshold,
async value() { async value() {
let creativeModePerks :Partial<{ [id in PerkId]:number }>= {}, let creativeModePerks: Partial<{ [id in PerkId]: number }> = {},
choice: "start" | Upgrade | void; choice: "start" | Upgrade | void;
while ( while (
(choice = await asyncAlert<"start" | Upgrade>({ (choice = await asyncAlert<"start" | Upgrade>({
@ -2842,7 +2855,7 @@ async function openSettingsPanel() {
} }
} }
}, },
}) });
actions.push({ actions.push({
text: "Reset Game", text: "Reset Game",
help: "Erase high score and statistics", help: "Erase high score and statistics",
@ -2867,14 +2880,13 @@ async function openSettingsPanel() {
window.location.reload(); window.location.reload();
} }
}, },
}) });
const cb = await asyncAlert<() => void>({ const cb = await asyncAlert<() => void>({
title: "Breakout 71", title: "Breakout 71",
text: ``, text: ``,
allowClose: true, allowClose: true,
actions , actions,
textAfterButtons: ` textAfterButtons: `
<p> <p>
<span>Made in France by <a href="https://lecaro.me">Renan LE CARO</a>.</span> <span>Made in France by <a href="https://lecaro.me">Renan LE CARO</a>.</span>
@ -2990,7 +3002,11 @@ function repulse(a: Ball, b: BallLike, power: number, impactsBToo: boolean) {
(((-power * (max - distance)) / (max * 1.2) / 3) * (((-power * (max - distance)) / (max * 1.2) / 3) *
Math.min(500, levelTime)) / Math.min(500, levelTime)) /
500; 500;
if (impactsBToo && typeof b.vx !== 'undefined' && typeof b.vy !== 'undefined') { if (
impactsBToo &&
typeof b.vx !== "undefined" &&
typeof b.vy !== "undefined"
) {
b.vx += dx * fact; b.vx += dx * fact;
b.vy += dy * fact; b.vy += dy * fact;
} }
@ -3011,7 +3027,11 @@ function repulse(a: Ball, b: BallLike, power: number, impactsBToo: boolean) {
vx: -dx * speed + a.vx + (Math.random() - 0.5) * rand, vx: -dx * speed + a.vx + (Math.random() - 0.5) * rand,
vy: -dy * speed + a.vy + (Math.random() - 0.5) * rand, vy: -dy * speed + a.vy + (Math.random() - 0.5) * rand,
}); });
if (impactsBToo&& typeof b.vx !== 'undefined' && typeof b.vy !== 'undefined') { if (
impactsBToo &&
typeof b.vx !== "undefined" &&
typeof b.vy !== "undefined"
) {
flashes.push({ flashes.push({
type: "particle", type: "particle",
duration: 100, duration: 100,
@ -3071,7 +3091,7 @@ function attract(a: Ball, b: Ball, power: number) {
}); });
} }
let mediaRecorder: MediaRecorder|null, let mediaRecorder: MediaRecorder | null,
captureStream: MediaStream, captureStream: MediaStream,
captureTrack: CanvasCaptureMediaStreamTrack, captureTrack: CanvasCaptureMediaStreamTrack,
recordCanvas: HTMLCanvasElement, recordCanvas: HTMLCanvasElement,
@ -3145,7 +3165,7 @@ function startRecordingGame() {
recordCanvas.height = gameZoneHeight; recordCanvas.height = gameZoneHeight;
// drawMainCanvasOnSmallCanvas() // drawMainCanvasOnSmallCanvas()
const recordedChunks :Blob[]= []; const recordedChunks: Blob[] = [];
const instance = new MediaRecorder(captureStream, { const instance = new MediaRecorder(captureStream, {
videoBitsPerSecond: 3500000, videoBitsPerSecond: 3500000,
@ -3158,7 +3178,7 @@ function startRecordingGame() {
}; };
instance.onstop = async function () { instance.onstop = async function () {
let targetDiv: HTMLElement|null; let targetDiv: HTMLElement | null;
let blob = new Blob(recordedChunks, { type: "video/webm" }); let blob = new Blob(recordedChunks, { type: "video/webm" });
if (blob.size < 200000) return; // under 0.2MB, probably bugged out or pointlessly short if (blob.size < 200000) return; // under 0.2MB, probably bugged out or pointlessly short
@ -3257,13 +3277,13 @@ function toggleFullScreen() {
} }
} }
const pressed :{[k:string]:number}= { const pressed: { [k: string]: number } = {
ArrowLeft: 0, ArrowLeft: 0,
ArrowRight: 0, ArrowRight: 0,
Shift: 0, Shift: 0,
}; };
function setKeyPressed(key: string , on: 0 | 1) { function setKeyPressed(key: string, on: 0 | 1) {
pressed[key] = on; pressed[key] = on;
keyboardPuckSpeed = keyboardPuckSpeed =
((pressed.ArrowRight - pressed.ArrowLeft) * ((pressed.ArrowRight - pressed.ArrowLeft) *
@ -3323,7 +3343,7 @@ function sample<T>(arr: T[]): T {
} }
function getMajorityValue(arr: string[]): string { function getMajorityValue(arr: string[]): string {
const count :{[k:string]:number}= {}; const count: { [k: string]: number } = {};
arr.forEach((v) => (count[v] = (count[v] || 0) + 1)); arr.forEach((v) => (count[v] = (count[v] || 0) + 1));
// Object.values inline polyfill // Object.values inline polyfill
const max = Math.max(...Object.keys(count).map((k) => count[k])); const max = Math.max(...Object.keys(count).map((k) => count[k]));

View file

@ -69,11 +69,14 @@ function levelIconHTML(
return `<img alt="${levelName}" width="${size}" height="${size}" src="${c.toDataURL()}"/>`; return `<img alt="${levelName}" width="${size}" height="${size}" src="${c.toDataURL()}"/>`;
} }
export const icons = {} as {[k:string]:string}; export const icons = {} as { [k: string]: string };
export const allLevels = rawLevelsList export const allLevels = rawLevelsList
.map((level) => { .map((level) => {
const bricks = level.bricks.split("").map((c) => palette[c]); const bricks = level.bricks
.split("")
.map((c) => palette[c])
.slice(0, level.size * level.size);
const icon = levelIconHTML(bricks, level.size, level.name, level.color); const icon = levelIconHTML(bricks, level.size, level.name, level.color);
icons[level.name] = icon; icons[level.name] = icon;
let svg = level.svg; let svg = level.svg;
@ -89,18 +92,17 @@ 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 threshold:
li < 8
? 0 ? 0
: Math.round( : Math.round(
Math.min(Math.pow(10, 1 + (li + l.size) / 30) * 10, 5000) * li, Math.min(Math.pow(10, 1 + (li + l.size) / 30) * 10, 5000) * li,
), ),
sortKey:((Math.random() + 3) / 3.5) * l.bricks.filter((i) => i).length sortKey: ((Math.random() + 3) / 3.5) * l.bricks.filter((i) => i).length,
})) as Level[]; })) as Level[];
export const upgrades = rawUpgrades.map((u) => ({ export const upgrades = rawUpgrades.map((u) => ({
...u, ...u,
icon: icons["icon:" + u.id], icon: icons["icon:" + u.id],

View file

@ -5,7 +5,7 @@ export const options = {
default: true, default: true,
name: `Game sounds`, name: `Game sounds`,
help: `Can slow down some phones.`, help: `Can slow down some phones.`,
afterChange:()=>{}, afterChange: () => {},
disabled: () => false, disabled: () => false,
}, },
"mobile-mode": { "mobile-mode": {
@ -21,33 +21,33 @@ export const options = {
default: false, default: false,
name: `Basic graphics`, name: `Basic graphics`,
help: `Better performance on older devices.`, help: `Better performance on older devices.`,
afterChange:()=>{}, afterChange: () => {},
disabled: () => false, disabled: () => false,
}, },
pointerLock: { pointerLock: {
default: false, default: false,
name: `Mouse pointer lock`, name: `Mouse pointer lock`,
help: `Locks and hides the mouse cursor.`, help: `Locks and hides the mouse cursor.`,
afterChange:()=>{}, afterChange: () => {},
disabled: () => !gameCanvas.requestPointerLock, disabled: () => !gameCanvas.requestPointerLock,
}, },
easy: { easy: {
default: false, default: false,
name: `Kids mode`, name: `Kids mode`,
help: `Start future runs with "slower ball".`, help: `Start future runs with "slower ball".`,
afterChange:()=>{}, afterChange: () => {},
disabled: () => false, disabled: () => false,
}, // Could not get the sharing to work without loading androidx and all the modern android things so for now i'll just disable sharing in the android app }, // Could not get the sharing to work without loading androidx and all the modern android things so for now i'll just disable sharing in the android app
record: { record: {
default: false, default: false,
name: `Record gameplay videos`, name: `Record gameplay videos`,
help: `Get a video of each level.`, help: `Get a video of each level.`,
afterChange:()=>{}, afterChange: () => {},
disabled() { disabled() {
return window.location.search.includes("isInWebView=true"); return window.location.search.includes("isInWebView=true");
}, },
}, },
} as const satisfies {[k:string]:OptionDef}; } as const satisfies { [k: string]: OptionDef };
export type OptionDef = { export type OptionDef = {
default: boolean; default: boolean;
@ -56,4 +56,4 @@ export type OptionDef = {
disabled: () => boolean; disabled: () => boolean;
afterChange: () => void; afterChange: () => void;
}; };
export type OptionId = keyof typeof options ; export type OptionId = keyof typeof options;

17
src/types.d.ts vendored
View file

@ -110,24 +110,23 @@ interface BaseFlash {
x: number; x: number;
y: number; y: number;
} }
interface ParticleFlash extends BaseFlash{ interface ParticleFlash extends BaseFlash {
type: 'particle'; type: "particle";
vx: number; vx: number;
vy: number; vy: number;
ethereal: boolean; ethereal: boolean;
} }
interface TextFlash extends BaseFlash{ interface TextFlash extends BaseFlash {
type:'text'; type: "text";
text: string; text: string;
} }
interface BallFlash extends BaseFlash{ interface BallFlash extends BaseFlash {
type:'ball'; type: "ball";
} }
export type Flash = ParticleFlash|TextFlash|BallFlash export type Flash = ParticleFlash | TextFlash | BallFlash;
export type RunStats = { export type RunStats = {
started: number; started: number;
@ -148,8 +147,6 @@ export type PerksMap = {
[k in PerkId]: number; [k in PerkId]: number;
}; };
export type RunHistoryItem = RunStats & { export type RunHistoryItem = RunStats & {
perks?: PerksMap; perks?: PerksMap;
appVersion?: string; appVersion?: string;

View file

@ -1 +1 @@
"29022953" "29028296"