mirror of
https://gitlab.com/lecarore/breakout71.git
synced 2025-04-23 21:46:15 -04:00
Creative mode, cleanup loop fix
This commit is contained in:
parent
2d2d4fd963
commit
504fd6649c
8 changed files with 3189 additions and 2901 deletions
5247
src/game.ts
5247
src/game.ts
File diff suppressed because it is too large
Load diff
|
@ -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": ""
|
||||
}
|
||||
]
|
||||
]
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.`,
|
||||
|
|
142
src/style.css
142
src/style.css
|
@ -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
222
src/types.d.ts
vendored
|
@ -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;
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue