Trying to get file dowload to work

This commit is contained in:
Renan LE CARO 2025-03-18 14:16:12 +01:00
parent 5ca2d58c9d
commit ffdbd71a88
28 changed files with 1525 additions and 5236 deletions

View file

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

View file

@ -842,4 +842,4 @@
"svg": null,
"color": ""
}
]
]

View file

@ -1 +1 @@
"29036807"
"29038230"

View file

@ -3,12 +3,15 @@ import {
Ball,
Coin,
GameState,
LightFlash,
OptionId,
ParticleFlash,
PerkId,
RunParams,
TextFlash,
Upgrade,
} from "./types";
import {getAudioContext, playPendingSounds} from "./sounds";
import { getAudioContext, playPendingSounds } from "./sounds";
import {
currentLevelInfo,
getRowColIndex,
@ -20,6 +23,8 @@ import "./PWA/sw_loader";
import { getCurrentLang, t } from "./i18n/i18n";
import { getSettingValue, getTotalScore, setSettingValue } from "./settings";
import {
empty,
forEachLiveOne,
gameStateTick,
normalizeGameState,
pickRandomUpgrades,
@ -51,11 +56,12 @@ import {
closeModal,
} from "./asyncAlert";
import { isOptionOn, options, toggleOption } from "./options";
import {hashCode} from "./getLevelBackground";
import { hashCode } from "./getLevelBackground";
export function play() {
if (gameState.running) return;
gameState.running = true;
gameState.ballStickToPuck = false;
startRecordingGame(gameState);
getAudioContext()?.resume();
@ -95,6 +101,10 @@ export function pause(playerAskedForPause: boolean) {
}
export const fitSize = () => {
const past_off = gameState.offsetXRoundedDown,
past_width = gameState.gameZoneWidthRoundedUp,
past_heigh = gameState.gameZoneHeight;
const { width, height } = gameCanvas.getBoundingClientRect();
gameState.canvasWidth = width;
gameState.canvasHeight = height;
@ -123,10 +133,27 @@ export const fitSize = () => {
backgroundCanvas.title = "resized";
// Ensure puck stays within bounds
setMousePos(gameState, gameState.puckPosition);
gameState.coins = [];
gameState.flashes = [];
function mapXY(item: ParticleFlash | TextFlash | LightFlash) {
item.x =
gameState.offsetXRoundedDown +
((item.x - past_off) / past_width) * gameState.gameZoneWidthRoundedUp;
item.y = (item.y / past_heigh) * gameState.gameZoneHeight;
}
function mapXYPastCoord(coin: Coin | Ball) {
coin.x =
gameState.offsetXRoundedDown +
((coin.x - past_off) / past_width) * gameState.gameZoneWidthRoundedUp;
coin.y = (coin.y / past_heigh) * gameState.gameZoneHeight;
coin.previousX = coin.x;
coin.previousY = coin.y;
}
gameState.balls.forEach(mapXYPastCoord);
forEachLiveOne(gameState.coins, mapXYPastCoord);
forEachLiveOne(gameState.particles, mapXY);
forEachLiveOne(gameState.texts, mapXY);
forEachLiveOne(gameState.lights, mapXY);
pause(true);
putBallsAtPuck(gameState);
// For safari mobile https://css-tricks.com/the-trick-to-viewport-units-on-mobile/
document.documentElement.style.setProperty(
"--vh",
@ -273,7 +300,9 @@ gameCanvas.addEventListener("mousemove", (e) => {
gameCanvas.addEventListener("touchstart", (e) => {
e.preventDefault();
if (!e.touches?.length) return;
setMousePos(gameState, e.touches[0].pageX);
normalizeGameState(gameState);
play();
});
gameCanvas.addEventListener("touchend", (e) => {
@ -442,7 +471,6 @@ export function tick() {
gameState.puckPosition + gameState.keyboardPuckSpeed,
);
}
normalizeGameState(gameState);
if (gameState.running) {
@ -457,8 +485,8 @@ export function tick() {
if (gameState.running) {
recordOneFrame(gameState);
}
if(isOptionOn('sound') ){
playPendingSounds(gameState)
if (isOptionOn("sound")) {
playPendingSounds(gameState);
}
requestAnimationFrame(tick);
}
@ -514,10 +542,13 @@ async function openScorePanel() {
}
}
document.getElementById("menu")?.addEventListener("click", (e) => {
e.preventDefault();
openSettingsPanel();
});
(document.getElementById("menu") as HTMLButtonElement).addEventListener(
"click",
(e) => {
e.preventDefault();
openSettingsPanel();
},
);
async function openSettingsPanel() {
pause(true);
@ -665,7 +696,7 @@ async function openSettingsPanel() {
localStorageContent[key] = value;
}
const signedPayload=JSON.stringify(localStorageContent)
const signedPayload = JSON.stringify(localStorageContent);
const dlLink = document.createElement("a");
dlLink.setAttribute(
@ -676,7 +707,10 @@ async function openSettingsPanel() {
fileType: "B71-save-file",
appVersion,
signedPayload,
key: hashCode('Security by obscurity, but really the game is oss so eh'+signedPayload)
key: hashCode(
"Security by obscurity, but really the game is oss so eh" +
signedPayload,
),
}),
),
);
@ -727,7 +761,8 @@ async function openSettingsPanel() {
const {
fileType,
appVersion: fileVersion,
signedPayload,key
signedPayload,
key,
} = JSON.parse(content);
if (fileType !== "B71-save-file")
throw new Error("Not a B71 save file");
@ -738,11 +773,17 @@ async function openSettingsPanel() {
" or newer.",
);
if(key!== hashCode('Security by obscurity, but really the game is oss so eh'+signedPayload)){
throw new Error("Key does not match content.")
if (
key !==
hashCode(
"Security by obscurity, but really the game is oss so eh" +
signedPayload,
)
) {
throw new Error("Key does not match content.");
}
const localStorageContent=JSON.parse(signedPayload)
const localStorageContent = JSON.parse(signedPayload);
localStorage.clear();
for (let key in localStorageContent) {
localStorage.setItem(key, localStorageContent[key]);
@ -982,4 +1023,4 @@ tick();
// @ts-ignore
// window.stressTest= ()=>restart({level:'Shark',perks:{base_combo:100, pierce:10, multiball:8}})
window.stressTest = () =>
restart({ level: "Shark", perks: { sapper: 2, pierce: 10, multiball: 3 } });
restart({ level: "Bird", perks: { sapper: 2, pierce: 10, multiball: 3 } });

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
import {Ball, GameState, PerkId, PerksMap} from "./types";
import {icons, upgrades} from "./loadGameData";
import { Ball, GameState, PerkId, PerksMap } from "./types";
import { icons, upgrades } from "./loadGameData";
export function getMajorityValue(arr: string[]): string {
const count: { [k: string]: number } = {};
@ -101,16 +101,16 @@ export function distanceBetween(
}
export function defaultSounds() {
return {
aboutToPlaySound: {
wallBeep: {vol: 0, x: 0},
comboIncreaseMaybe: {vol: 0, x: 0},
comboDecrease: {vol: 0, x: 0},
coinBounce: {vol: 0, x: 0},
explode: {vol: 0, x: 0},
lifeLost: {vol: 0, x: 0},
coinCatch: {vol: 0, x: 0},
colorChange: {vol: 0, x: 0},
}
}
}
return {
aboutToPlaySound: {
wallBeep: { vol: 0, x: 0 },
comboIncreaseMaybe: { vol: 0, x: 0 },
comboDecrease: { vol: 0, x: 0 },
coinBounce: { vol: 0, x: 0 },
explode: { vol: 0, x: 0 },
lifeLost: { vol: 0, x: 0 },
coinCatch: { vol: 0, x: 0 },
colorChange: { vol: 0, x: 0 },
},
};
}

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<babeledit_project be_version="5.3.0" version="1.3">
<babeledit_project be_version="5.2.0" version="1.3">
<!--
BabelEdit project file
@ -3353,7 +3353,6 @@
</package_node>
</children>
</folder_node>
<embedded_source_texts>false</embedded_source_texts>
<isTemplateProject>false</isTemplateProject>
<languages>
<language>

View file

@ -39,7 +39,7 @@
"main_menu.basic_help": "Fewer particles and flashes, better performance.",
"main_menu.download_save_file": "Download save file",
"main_menu.download_save_file_help": "Get a transferable .b71 file with your score and stats",
"main_menu.footer_html": " <p> <span>Made in France by <a href=\"https://lecaro.me\">Renan LE CARO</a>.</span> \n <a href=\"https://breakout.lecaro.me/privacy.html\" target=\"_blank\">Privacy Policy</a>\n <a href=\"https://f-droid.org/en/packages/me.lecaro.breakout/\" target=\"_blank\">F-Droid</a>\n <a href=\"https://play.google.com/store/apps/details?id=me.lecaro.breakout\" target=\"_blank\">Google Play</a>\n <a href=\"https://renanlecaro.itch.io/breakout71\" target=\"_blank\">itch.io</a> \n <a href=\"https://gitlab.com/lecarore/breakout71\" target=\"_blank\">Gitlab</a>\n <a href=\"https://breakout.lecaro.me/\" target=\"_blank\">Web version</a>\n <a href=\"https://news.ycombinator.com/item?id=43183131\" target=\"_blank\">HackerNews</a>\n <span>v.{{appVersion}}</span></p>",
"main_menu.footer_html": "<p> \n<span>Made in France by <a href=\"https://lecaro.me\">Renan LE CARO</a>.</span> \n<a href=\"https://paypal.me/renanlecaro\" target=\"_blank\">Donate</a>\n<a href=\"https://discord.gg/DZSPqyJkwP\" target=\"_blank\">Discord</a>\n<a href=\"https://f-droid.org/en/packages/me.lecaro.breakout/\" target=\"_blank\">F-Droid</a>\n<a href=\"https://play.google.com/store/apps/details?id=me.lecaro.breakout\" target=\"_blank\">Google Play</a>\n<a href=\"https://renanlecaro.itch.io/breakout71\" target=\"_blank\">itch.io</a> \n<a href=\"https://gitlab.com/lecarore/breakout71\" target=\"_blank\">Gitlab</a>\n<a href=\"https://breakout.lecaro.me/\" target=\"_blank\">Web version</a>\n<a href=\"https://news.ycombinator.com/item?id=43183131\" target=\"_blank\">HackerNews</a>\n<a href=\"https://breakout.lecaro.me/privacy.html\" target=\"_blank\">Privacy Policy</a>\n<span>v.{{appVersion}}</span>\n</p>\n",
"main_menu.fullscreen": "Fullscreen",
"main_menu.fullscreen_exit": "Exit Fullscreen",
"main_menu.fullscreen_exit_help": "Might not work on some machines",

View file

@ -39,7 +39,7 @@
"main_menu.basic_help": "Moins de particules et effets, meilleures performances.",
"main_menu.download_save_file": "Sauvegarder mes progrès",
"main_menu.download_save_file_help": "Obtenir un fichier de sauvegarde .b71 transférable",
"main_menu.footer_html": " <p> <span>Programmé en France par <a href=\"https://lecaro.me\">Renan LE CARO</a>.</span> <a href=\"https://breakout.lecaro.me/privacy.html\" target=\"_blank\">Politique de confidentialité</a> <a href=\"https://f-droid.org/en/packages/me.lecaro.breakout/\" target=\"_blank\">F-Droid</a> <a href=\"https://play.google.com/store/apps/details?id=me.lecaro.breakout\" target=\"_blank\">Google Play</a> <a href=\"https://renanlecaro.itch.io/breakout71\" target=\"_blank\">itch.io</a> <a href=\"https://gitlab.com/lecarore/breakout71\" target=\"_blank\">Gitlab</a> <a href=\"https://breakout.lecaro.me/\" target=\"_blank\">Version web</a> <a href=\"https://news.ycombinator.com/item?id=43183131\" target=\"_blank\">HackerNews</a> <span>v.{{appVersion}}</span></p>",
"main_menu.footer_html": " <p> \n<span>Programmé en France par <a href=\"https://lecaro.me\">Renan LE CARO</a>.</span>\n<a href=\"https://paypal.me/renanlecaro\" target=\"_blank\">Donner</a>\n<a href=\"https://discord.gg/DZSPqyJkwP\" target=\"_blank\">Discord</a>\n<a href=\"https://f-droid.org/en/packages/me.lecaro.breakout/\" target=\"_blank\">F-Droid</a>\n<a href=\"https://play.google.com/store/apps/details?id=me.lecaro.breakout\" target=\"_blank\">Google Play</a>\n<a href=\"https://renanlecaro.itch.io/breakout71\" target=\"_blank\">itch.io</a>\n<a href=\"https://gitlab.com/lecarore/breakout71\" target=\"_blank\">Gitlab</a>\n<a href=\"https://breakout.lecaro.me/\" target=\"_blank\">Version web</a>\n<a href=\"https://news.ycombinator.com/item?id=43183131\" target=\"_blank\">HackerNews</a>\n<a href=\"https://breakout.lecaro.me/privacy.html\" target=\"_blank\">Politique de confidentialité</a> \n<span>v.{{appVersion}}</span>\n</p>",
"main_menu.fullscreen": "Plein écran",
"main_menu.fullscreen_exit": "Quitter le plein écran",
"main_menu.fullscreen_exit_help": "Peut ne pas fonctionner sur certaines machines",

View file

@ -1,10 +1,14 @@
import {GameState, RunParams} from "./types";
import {getTotalScore} from "./settings";
import {allLevels, upgrades} from "./loadGameData";
import {defaultSounds, getPossibleUpgrades, makeEmptyPerksMap, sumOfKeys,} from "./game_utils";
import {dontOfferTooSoon, resetBalls} from "./gameStateMutators";
import {isOptionOn} from "./options";
import { GameState, RunParams } from "./types";
import { getTotalScore } from "./settings";
import { allLevels, upgrades } from "./loadGameData";
import {
defaultSounds,
getPossibleUpgrades,
makeEmptyPerksMap,
sumOfKeys,
} from "./game_utils";
import { dontOfferTooSoon, resetBalls } from "./gameStateMutators";
import { isOptionOn } from "./options";
export function newGameState(params: RunParams): GameState {
const totalScoreAtRunStart = getTotalScore();
@ -33,6 +37,7 @@ export function newGameState(params: RunParams): GameState {
combo: 1,
gridSize: 12,
running: false,
ballStickToPuck: true,
puckPosition: 400,
pauseTimeout: null,
canvasWidth: 0,
@ -50,10 +55,10 @@ export function newGameState(params: RunParams): GameState {
balls: [],
ballsColor: "white",
bricks: [],
lights: {indexMin:0,list:[]},
particles: {indexMin:0,list:[]},
texts: {indexMin:0,list:[]},
coins: {indexMin:0,list:[]},
lights: { indexMin: 0, total: 0, list: [] },
particles: { indexMin: 0, total: 0, list: [] },
texts: { indexMin: 0, total: 0, list: [] },
coins: { indexMin: 0, total: 0, list: [] },
levelStartScore: 0,
levelMisses: 0,
levelSpawnedCoins: 0,
@ -90,7 +95,7 @@ export function newGameState(params: RunParams): GameState {
levelWallBounces: 0,
needsRender: true,
autoCleanUses: 0,
...defaultSounds()
...defaultSounds(),
};
resetBalls(gameState);
@ -109,4 +114,3 @@ export function newGameState(params: RunParams): GameState {
}
return gameState;
}

View file

@ -323,12 +323,12 @@ export const rawUpgrades = [
giftable: false,
id: "sturdy_bricks",
max: 4,
name: t("upgrades.telekinesis.name"),
name: t("upgrades.sturdy_bricks.name"),
help: (lvl: number) =>
lvl == 1
? t("upgrades.telekinesis.help")
: t("upgrades.telekinesis.help_plural"),
fullHelp: t("upgrades.telekinesis.fullHelp"),
? t("upgrades.sturdy_bricks.help")
: t("upgrades.sturdy_bricks.help_plural"),
fullHelp: t("upgrades.sturdy_bricks.fullHelp"),
},
{
requires: "",

View file

@ -1,4 +1,4 @@
import { baseCombo } from "./gameStateMutators";
import { baseCombo, forEachLiveOne, liveCount } from "./gameStateMutators";
import {
brickCenterX,
brickCenterY,
@ -54,9 +54,8 @@ export function render(gameState: GameState) {
ctx.globalCompositeOperation = "screen";
ctx.globalAlpha = 0.6;
gameState.coins.forEach((coin) => {
if (!coin.destroyed)
drawFuzzyBall(ctx, coin.color, gameState.coinSize * 2, coin.x, coin.y);
forEachLiveOne(gameState.coins, (coin) => {
drawFuzzyBall(ctx, coin.color, gameState.coinSize * 2, coin.x, coin.y);
});
gameState.balls.forEach((ball) => {
drawFuzzyBall(
@ -81,17 +80,19 @@ export function render(gameState: GameState) {
);
});
ctx.globalAlpha = 1;
gameState.flashes.forEach((flash) => {
const { x, y, time, color, size, type, duration } = flash;
forEachLiveOne(gameState.lights, (flash) => {
const { x, y, time, color, size, duration } = flash;
const elapsed = gameState.levelTime - time;
ctx.globalAlpha = Math.min(1, 2 - (elapsed / duration) * 2);
if (type === "ball") {
drawFuzzyBall(ctx, color, size, x, y);
}
if (type === "particle") {
drawFuzzyBall(ctx, color, size * 3, x, y);
}
drawFuzzyBall(ctx, color, size, x, y);
});
forEachLiveOne(gameState.particles, (flash) => {
const { x, y, time, color, size, duration } = flash;
const elapsed = gameState.levelTime - time;
ctx.globalAlpha = Math.min(1, 2 - (elapsed / duration) * 2);
drawFuzzyBall(ctx, color, size * 3, x, y);
});
// Decides how brights the bg black parts can get
ctx.globalAlpha = 0.2;
ctx.globalCompositeOperation = "multiply";
@ -128,14 +129,11 @@ export function render(gameState: GameState) {
ctx.globalCompositeOperation = "source-over";
ctx.fillStyle = level.color || "#000";
ctx.fillRect(0, 0, width, height);
gameState.flashes.forEach((flash) => {
const { x, y, time, color, size, type, duration } = flash;
forEachLiveOne(gameState.particles, (flash) => {
const { x, y, time, color, size, duration } = flash;
const elapsed = gameState.levelTime - time;
ctx.globalAlpha = Math.min(1, 2 - (elapsed / duration) * 2);
if (type === "particle") {
drawBall(ctx, color, size, x, y);
}
drawBall(ctx, color, size, x, y);
});
}
@ -161,27 +159,24 @@ export function render(gameState: GameState) {
}
// Coins
ctx.globalAlpha = 1;
gameState.coins.forEach((coin) => {
if (!coin.destroyed) {
ctx.globalCompositeOperation =
coin.color === "gold" || level.color ? "source-over" : "screen";
drawCoin(
ctx,
coin.color,
coin.size,
coin.x,
coin.y,
level.color || "black",
coin.a,
);
}
forEachLiveOne(gameState.coins, (coin) => {
ctx.globalCompositeOperation =
coin.color === "gold" || level.color ? "source-over" : "screen";
drawCoin(
ctx,
coin.color,
coin.size,
coin.x,
coin.y,
level.color || "black",
coin.a,
);
});
// Black shadow around balls
if (!isOptionOn("basic")) {
ctx.globalCompositeOperation = "source-over";
ctx.globalAlpha = Math.min(0.8, gameState.coins.length / 20);
ctx.globalAlpha = Math.min(0.8, liveCount(gameState.coins) / 20);
gameState.balls.forEach((ball) => {
drawBall(
ctx,
@ -197,22 +192,21 @@ export function render(gameState: GameState) {
renderAllBricks();
ctx.globalCompositeOperation = "screen";
gameState.flashes = gameState.flashes.filter(
(f) => gameState.levelTime - f.time < f.duration && !f.destroyed,
);
gameState.flashes.forEach((flash) => {
const { x, y, time, color, size, type, duration } = flash;
forEachLiveOne(gameState.texts, (flash) => {
const { x, y, time, color, size, duration } = flash;
const elapsed = gameState.levelTime - time;
ctx.globalAlpha = Math.max(0, Math.min(1, 2 - (elapsed / duration) * 2));
if (type === "text") {
ctx.globalCompositeOperation = "source-over";
drawText(ctx, flash.text, color, size, x, y - elapsed / 10);
} else if (type === "particle") {
ctx.globalCompositeOperation = "screen";
drawBall(ctx, color, size, x, y);
drawFuzzyBall(ctx, color, size, x, y);
}
ctx.globalCompositeOperation = "source-over";
drawText(ctx, flash.text, color, size, x, y - elapsed / 10);
});
forEachLiveOne(gameState.particles, (particle) => {
const { x, y, time, color, size, duration } = particle;
const elapsed = gameState.levelTime - time;
ctx.globalAlpha = Math.max(0, Math.min(1, 2 - (elapsed / duration) * 2));
ctx.globalCompositeOperation = "screen";
drawBall(ctx, color, size, x, y);
drawFuzzyBall(ctx, color, size, x, y);
});
if (gameState.perks.extra_life) {

View file

@ -1,31 +1,36 @@
import { isOptionOn } from "./options";
import {GameState} from "./types";
import { GameState } from "./types";
let lastPlay = Date.now()
let lastPlay = Date.now();
export function playPendingSounds(gameState:GameState){
if(lastPlay>Date.now()-60){
return
export function playPendingSounds(gameState: GameState) {
if (lastPlay > Date.now() - 60) {
return;
}
lastPlay=Date.now()
for(let key in gameState.aboutToPlaySound){
const soundName = key as keyof GameState["aboutToPlaySound"]
const ex = gameState.aboutToPlaySound[soundName] as {vol:number, x:number}
if(ex.vol){
sounds[soundName](Math.min(2,ex.vol),pixelsToPan(gameState, ex.x), gameState.combo)
ex.vol=0
lastPlay = Date.now();
for (let key in gameState.aboutToPlaySound) {
const soundName = key as keyof GameState["aboutToPlaySound"];
const ex = gameState.aboutToPlaySound[soundName] as {
vol: number;
x: number;
};
if (ex.vol) {
sounds[soundName](
Math.min(2, ex.vol),
pixelsToPan(gameState, ex.x),
gameState.combo,
);
ex.vol = 0;
}
}
}
export const sounds = {
wallBeep: (vol:number, pan: number, combo:number) => {
wallBeep: (vol: number, pan: number, combo: number) => {
if (!isOptionOn("sound")) return;
createSingleBounceSound(800, pan, vol);
},
comboIncreaseMaybe: ( volume: number,pan: number,combo: number, ) => {
comboIncreaseMaybe: (volume: number, pan: number, combo: number) => {
if (!isOptionOn("sound")) return;
let delta = 0;
if (!isNaN(lastComboPlayed)) {
@ -36,28 +41,28 @@ export const sounds = {
lastComboPlayed = combo;
},
comboDecrease(volume: number,pan: number,combo: number) {
comboDecrease(volume: number, pan: number, combo: number) {
if (!isOptionOn("sound")) return;
playShepard(-1, pan, volume);
},
coinBounce: (volume: number,pan: number,combo: number) => {
coinBounce: (volume: number, pan: number, combo: number) => {
if (!isOptionOn("sound")) return;
createSingleBounceSound(1200, pan, volume, 0.1, "triangle");
},
explode: (volume: number,pan: number,combo: number) => {
explode: (volume: number, pan: number, combo: number) => {
if (!isOptionOn("sound")) return;
createExplosionSound(pan);
},
lifeLost(volume: number,pan: number,combo: number) {
lifeLost(volume: number, pan: number, combo: number) {
if (!isOptionOn("sound")) return;
createShatteredGlassSound(pan);
},
coinCatch(volume: number,pan: number,combo: number) {
coinCatch(volume: number, pan: number, combo: number) {
if (!isOptionOn("sound")) return;
createSingleBounceSound(900, (pan), volume, 0.1, "triangle");
createSingleBounceSound(900, pan, volume, 0.1, "triangle");
},
colorChange(volume: number,pan: number,combo: number) {
colorChange(volume: number, pan: number, combo: number) {
createSingleBounceSound(400, pan, volume, 0.5, "sine");
createSingleBounceSound(800, pan, volume * 0.5, 0.2, "square");
},
@ -175,7 +180,7 @@ function createExplosionSound(pan = 0.5) {
noiseSource.stop(context.currentTime + 1);
}
function pixelsToPan(gameState:GameState, pan: number) {
function pixelsToPan(gameState: GameState, pan: number) {
return Math.max(
0,
Math.min(

46
src/types.d.ts vendored
View file

@ -113,22 +113,22 @@ interface BaseFlash {
}
interface ParticleFlash extends BaseFlash {
type: "particle";
// type: "particle";
vx: number;
vy: number;
ethereal: boolean;
}
interface TextFlash extends BaseFlash {
type: "text";
// type: "text";
text: string;
}
interface BallFlash extends BaseFlash {
type: "ball";
interface LightFlash extends BaseFlash {
// type: "ball";
}
export type Flash = ParticleFlash | TextFlash | BallFlash;
export type Flash = ParticleFlash | TextFlash | LightFlash;
export type RunStats = {
started: number;
@ -153,9 +153,10 @@ export type PerksMap = {
// TODO ensure T has a destroyed;boolean field
export type ReusableArray<T> = {
// All items below that index should not be destroyed
indexMin:number;
list:T[]
}
indexMin: number;
total: number;
list: T[];
};
export type RunHistoryItem = RunStats & {
perks?: PerksMap;
@ -197,6 +198,7 @@ export type GameState = {
combo: number;
// Whether the game is running or paused
running: boolean;
ballStickToPuck: boolean;
// Whether the game should be re-rendered once even if not running
needsRender: boolean;
// Position of the center of the puck on the canvas in pixels, from the left of the canvas.
@ -220,11 +222,9 @@ export type GameState = {
// Array of bricks to display. 'black' means bomb. '' means no brick.
bricks: colorString[];
particles: ReusableArray<ParticleFlash>
texts: ReusableArray<TextFlash>
lights: ReusableArray<BallFlash>
particles: ReusableArray<ParticleFlash>;
texts: ReusableArray<TextFlash>;
lights: ReusableArray<LightFlash>;
coins: ReusableArray<Coin>;
levelStartScore: number;
levelMisses: number;
@ -248,16 +248,16 @@ export type GameState = {
levelTime: number;
levelWallBounces: number;
autoCleanUses: number;
aboutToPlaySound:{
wallBeep:{vol:number, x:number},
comboIncreaseMaybe:{vol:number, x:number},
comboDecrease:{vol:number, x:number},
coinBounce:{vol:number, x:number},
explode:{vol:number, x:number},
lifeLost:{vol:number, x:number},
coinCatch:{vol:number, x:number},
colorChange:{vol:number, x:number},
}
aboutToPlaySound: {
wallBeep: { vol: number; x: number };
comboIncreaseMaybe: { vol: number; x: number };
comboDecrease: { vol: number; x: number };
coinBounce: { vol: number; x: number };
explode: { vol: number; x: number };
lifeLost: { vol: number; x: number };
coinCatch: { vol: number; x: number };
colorChange: { vol: number; x: number };
};
};
export type RunParams = {