Creative mode, cleanup loop fix

This commit is contained in:
Renan LE CARO 2025-03-07 20:18:18 +01:00
parent 2d2d4fd963
commit 504fd6649c
8 changed files with 3189 additions and 2901 deletions

File diff suppressed because it is too large Load diff

View file

@ -450,9 +450,15 @@
"svg": ""
},
{
"name": "icon:sides_are_lava",
"name": "icon:left_is_lava",
"size": 8,
"bricks": "r______rrttttttrrttttttrr______rr______rr____W_rr______rr_WWW__r",
"bricks": "r_______rtttttt_rtttttt_r_______r_______r____W__r_______r_WWW___",
"svg": ""
},
{
"name": "icon:right_is_lava",
"size": 8,
"bricks": "_______r_ttttttr_ttttttr_______r_______r_____W_r_______r__WWW__r",
"svg": ""
},
{
@ -829,4 +835,4 @@
"bricks": "_W__W_WW__WW____________WW__WW_W__W_",
"svg": ""
}
]
]

View file

@ -1,54 +1,54 @@
import {fitSize, gameCanvas} from "./game";
import { fitSize, gameCanvas } from "./game";
export const options = {
sound: {
default: true,
name: `Game sounds`,
help: `Can slow down some phones.`,
disabled: () => false,
sound: {
default: true,
name: `Game sounds`,
help: `Can slow down some phones.`,
disabled: () => false,
},
"mobile-mode": {
default: window.innerHeight > window.innerWidth,
name: `Mobile mode`,
help: `Leaves space for your thumb.`,
afterChange() {
fitSize();
},
"mobile-mode": {
default: window.innerHeight > window.innerWidth,
name: `Mobile mode`,
help: `Leaves space for your thumb.`,
afterChange() {
fitSize();
},
disabled: () => false,
disabled: () => false,
},
basic: {
default: false,
name: `Basic graphics`,
help: `Better performance on older devices.`,
disabled: () => false,
},
pointerLock: {
default: false,
name: `Mouse pointer lock`,
help: `Locks and hides the mouse cursor.`,
disabled: () => !gameCanvas.requestPointerLock,
},
easy: {
default: false,
name: `Kids mode`,
help: `Start future runs with "slower ball".`,
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
record: {
default: false,
name: `Record gameplay videos`,
help: `Get a video of each level.`,
disabled() {
return window.location.search.includes("isInWebView=true");
},
basic: {
default: false,
name: `Basic graphics`,
help: `Better performance on older devices.`,
disabled: () => false,
},
pointerLock: {
default: false,
name: `Mouse pointer lock`,
help: `Locks and hides the mouse cursor.`,
disabled: () => !gameCanvas.requestPointerLock,
},
easy: {
default: false,
name: `Kids mode`,
help: `Start future runs with "slower ball".`,
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
record: {
default: false,
name: `Record gameplay videos`,
help: `Get a video of each level.`,
disabled() {
return window.location.search.includes("isInWebView=true");
},
},
} as {[k:string]:OptionDef}
},
} as { [k: string]: OptionDef };
export type OptionDef = {
default:boolean;
name:string;
help:string;
disabled:()=>boolean
afterChange?:()=>void
}
export type OptionId = keyof (typeof options)
default: boolean;
name: string;
help: string;
disabled: () => boolean;
afterChange?: () => void;
};
export type OptionId = keyof typeof options;

View file

@ -75,15 +75,31 @@ export const rawUpgrades = [
{
requires: "",
threshold: 0,
id: "sides_are_lava",
id: "left_is_lava",
giftable: true,
name: "Shoot straight",
name: "Avoid left side",
max: 1,
help: (lvl) => `More coins if you don't touch the sides.`,
help: (lvl) => `More coins if you don't touch the left side.`,
fullHelp: `Whenever you break a brick, your combo will increase by one, so you'll get one more coin all the next bricks you break.
However, your combo will reset as soon as your ball hits the left or right side.
As soon as your combo rises, the sides become red to remind you that you should avoid hitting them. The effect stacks with other combo perks, combo rises faster with more upgrades but will also reset if any
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.
However, your combo will reset as soon as your ball hits the left side .
As soon as your combo rises, the left side becomes red to remind you that you should avoid hitting them.
The effect stacks with other combo perks, combo rises faster with more upgrades but will also reset if any
of the reset conditions are met.`,
},
{
requires: "",
threshold: 0,
id: "right_is_lava",
giftable: true,
name: "Avoid right side",
max: 1,
help: (lvl) => `More coins if you don't touch the right side.`,
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.
However, your combo will reset as soon as your ball hits the right side .
As soon as your combo rises, the right side becomes red to remind you that you should avoid hitting them.
The effect stacks with other combo perks, combo rises faster with more upgrades but will also reset if any
of the reset conditions are met.`,
},
{
@ -94,7 +110,6 @@ export const rawUpgrades = [
name: "Sky is the limit",
max: 1,
help: (lvl) => `More coins if you don't touch the top.`,
fullHelp: `Whenever you break a brick, your combo will increase by one. However, your combo will reset as soon as your ball hit the top of the screen.
When your combo is above the minimum, a red bar will appear at the top to remind you that you should avoid hitting it.
The effect stacks with other combo perks.`,

View file

@ -90,6 +90,15 @@ body {
max-width: 450px;
}
.popup.actionsAsGrid > div {
max-width: 800px;
section {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
}
}
.popup > div > * {
padding: 0;
margin: 0;
@ -100,25 +109,58 @@ body {
margin-bottom: 20px;
}
.popup > div > button {
font: inherit;
background: rgba(0, 0, 0, 0.8);
color: white;
padding: 10px;
cursor: pointer;
border: 1px solid white;
text-align: left;
.popup > div > section {
display: flex;
gap: 10px;
margin-top: -1px;
flex-direction: column;
align-items: stretch;
margin-top: 20px;
button {
font: inherit;
background: rgba(0, 0, 0, 0.8);
color: white;
padding: 10px;
cursor: pointer;
border: 1px solid white;
text-align: left;
display: flex;
gap: 10px;
margin-top: -1px;
&:not([disabled]):hover,
&:not([disabled]):focus {
border-color: #f1d33b;
position: relative;
z-index: 1;
}
&[disabled] {
/*border: 1px solid #666;*/
opacity: 0.5;
filter: saturate(0);
pointer-events: none;
}
& > div {
flex-grow: 1;
}
& > div > em {
display: block;
opacity: 0.8;
}
&.grey-out-unless-hovered {
&:not(:hover) {
opacity: 0.6;
img {
filter: saturate(0);
}
}
}
}
}
.popup > div > button:not([disabled]):hover,
.popup > div > button:not([disabled]):focus {
border-color: #f1d33b;
position: relative;
z-index: 1;
}
.popup button.close-modale {
color: white;
@ -131,61 +173,21 @@ body {
border: none;
cursor: pointer;
overflow: hidden;
}
.popup button.close-modale:before {
content: "+";
position: absolute;
transform: translate(-50%, -50%) rotate(45deg);
font-size: 80px;
display: inline-block;
top: 34px;
left: 26px;
}
&:before {
content: "+";
position: absolute;
transform: translate(-50%, -50%) rotate(45deg);
font-size: 80px;
display: inline-block;
top: 34px;
left: 26px;
}
.popup button.close-modale:hover {
font-weight: bold;
background: black;
}
.popup > div > button[disabled] {
/*border: 1px solid #666;*/
opacity: 0.5;
filter: saturate(0);
pointer-events: none;
}
.popup > div > button > div {
flex-grow: 1;
}
.popup > div > button > div > em {
display: block;
opacity: 0.8;
}
.popup > div > button > span.checks {
width: 40px;
height: 40px;
display: inline-flex;
gap: 5px;
flex-grow: 0;
flex-shrink: 0;
}
.popup > div > button > span.checks > span {
flex-basis: 10px;
flex-grow: 1;
flex-shrink: 1;
/*border: 1px solid white;*/
background: white;
opacity: 0.1;
border-radius: 4px;
align-self: stretch;
}
.popup > div > button > span.checks > span.checked {
opacity: 1;
&:hover {
font-weight: bold;
background: black;
}
}
.popup .textAfterButtons {
@ -289,9 +291,11 @@ body {
flex-direction: column;
justify-content: flex-end;
}
.histogram > span.active > span {
background: #4049ca;
}
.histogram > span > span {
/*Visible bar*/
background: #1c1c2f;

222
src/types.d.ts vendored
View file

@ -1,143 +1,139 @@
import {rawUpgrades} from "./rawUpgrades";
import { rawUpgrades } from "./rawUpgrades";
export type colorString = string;
export type RawLevel = {
name: string;
size: number;
bricks: string;
svg: string;
color: string;
name: string;
size: number;
bricks: string;
svg: string;
color: string;
};
export type Level = {
name: string;
size: number;
bricks: colorString[];
svg: string;
color: string;
threshold?: number;
sortKey?: number;
name: string;
size: number;
bricks: colorString[];
svg: string;
color: string;
threshold?: number;
sortKey?: number;
};
export type Palette = { [k: string]: string };
export type Upgrade = {
threshold: number;
giftable: boolean;
id: PerkId;
name: string;
icon: string;
max: number;
help: (lvl: number) => string;
fullHelp: string;
requires: PerkId | "";
threshold: number;
giftable: boolean;
id: PerkId;
name: string;
icon: string;
max: number;
help: (lvl: number) => string;
fullHelp: string;
requires: PerkId | "";
};
export type PerkId = (typeof rawUpgrades)[number]["id"];
declare global {
interface Window {
webkitAudioContext?: typeof AudioContext;
}
interface Window {
webkitAudioContext?: typeof AudioContext;
}
interface Document {
webkitFullscreenEnabled?: boolean;
webkitCancelFullScreen?: () => void;
}
interface Document {
webkitFullscreenEnabled?: boolean;
webkitCancelFullScreen?: () => void;
}
interface Element {
webkitRequestFullscreen: typeof Element.requestFullscreen
}
interface MediaStream {
// https://devdoc.net/web/developer.mozilla.org/en-US/docs/Web/API/CanvasCaptureMediaStream.html
// On firefox, the capture stream has the requestFrame option
// instead of the track, go figure
requestFrame?:()=>void
}
interface Element {
webkitRequestFullscreen: typeof Element.requestFullscreen;
}
interface MediaStream {
// https://devdoc.net/web/developer.mozilla.org/en-US/docs/Web/API/CanvasCaptureMediaStream.html
// On firefox, the capture stream has the requestFrame option
// instead of the track, go figure
requestFrame?: () => void;
}
}
export type BallLike = {
x: number;
y: number;
vx?: number;
vy?: number;
}
x: number;
y: number;
vx?: number;
vy?: number;
};
export type Coin = {
points: number;
color: colorString;
x: number;
y: number;
previousx: number;
previousy: number;
vx: number;
vy: number;
sx: number;
sy: number;
a: number;
sa: number;
weight: number;
destroyed?: boolean;
coloredABrick?: boolean;
}
points: number;
color: colorString;
x: number;
y: number;
previousx: number;
previousy: number;
vx: number;
vy: number;
sx: number;
sy: number;
a: number;
sa: number;
weight: number;
destroyed?: boolean;
coloredABrick?: boolean;
};
export type Ball = {
x: number;
previousx: number;
y: number;
previousy: number;
vx: number;
vy: number;
sx: number;
sy: number;
sparks: number;
piercedSinceBounce: number;
hitSinceBounce: number;
hitItem: { index: number, color: string }[];
bouncesList?: { x: number, y: number }[];
sapperUses: number;
destroyed?: boolean;
previousvx?: number;
previousvy?: number;
}
x: number;
previousx: number;
y: number;
previousy: number;
vx: number;
vy: number;
sx: number;
sy: number;
sparks: number;
piercedSinceBounce: number;
hitSinceBounce: number;
hitItem: { index: number; color: string }[];
bouncesList?: { x: number; y: number }[];
sapperUses: number;
destroyed?: boolean;
previousvx?: number;
previousvy?: number;
};
export type FlashTypes = "text" | "particle" | 'ball'
export type FlashTypes = "text" | "particle" | "ball";
export type Flash = {
type: FlashTypes;
text?: string;
time: number;
color: colorString;
x: number;
y: number;
duration: number;
size: number;
vx?: number;
vy?: number;
ethereal?: boolean;
destroyed?: boolean;
}
type: FlashTypes;
text?: string;
time: number;
color: colorString;
x: number;
y: number;
duration: number;
size: number;
vx?: number;
vy?: number;
ethereal?: boolean;
destroyed?: boolean;
};
export type RunStats= {
started: number;
levelsPlayed: number;
runTime: number;
coins_spawned: number;
score: number;
bricks_broken: number;
misses: number;
balls_lost: number;
puck_bounces: number;
upgrades_picked: number;
max_combo: number;
max_level: number;
}
export type RunHistoryItem =RunStats & {
perks?: {[k in PerkId]:number};
appVersion?:string;
}
export type RunStats = {
started: number;
levelsPlayed: number;
runTime: number;
coins_spawned: number;
score: number;
bricks_broken: number;
misses: number;
balls_lost: number;
puck_bounces: number;
upgrades_picked: number;
max_combo: number;
max_level: number;
};
export type RunHistoryItem = RunStats & {
perks?: { [k in PerkId]: number };
appVersion?: string;
};