mirror of
https://gitlab.com/lecarore/breakout71.git
synced 2025-04-21 20:46:14 -04:00
Build and deploy of version 29028296
This commit is contained in:
parent
a136475f88
commit
8b1278cb55
8 changed files with 325 additions and 3926 deletions
|
@ -11,8 +11,8 @@ android {
|
|||
applicationId = "me.lecaro.breakout"
|
||||
minSdk = 21
|
||||
targetSdk = 34
|
||||
versionCode = 29022953
|
||||
versionName = "29022953"
|
||||
versionCode = 29028296
|
||||
versionName = "29028296"
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables {
|
||||
useSupportLibrary = true
|
||||
|
|
File diff suppressed because one or more lines are too long
3807
dist/index.html
vendored
3807
dist/index.html
vendored
File diff suppressed because one or more lines are too long
94
src/game.ts
94
src/game.ts
|
@ -17,14 +17,15 @@ import { OptionId, options} from "./options";
|
|||
const MAX_COINS = 400;
|
||||
const MAX_PARTICLES = 600;
|
||||
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";
|
||||
let ballSize = 20;
|
||||
const coinSize = Math.round(ballSize * 0.8);
|
||||
const puckHeight = ballSize;
|
||||
|
||||
|
||||
let runLevels: Level[] = [];
|
||||
|
||||
let currentLevel = 0;
|
||||
|
@ -40,10 +41,10 @@ bombSVG.src =
|
|||
let puckWidth = 200;
|
||||
|
||||
const makeEmptyPerksMap = () => {
|
||||
const p = {} as any
|
||||
upgrades.forEach(u=>p[u.id]=0)
|
||||
return p as PerksMap
|
||||
}
|
||||
const p = {} as any;
|
||||
upgrades.forEach((u) => (p[u.id] = 0));
|
||||
return p as PerksMap;
|
||||
};
|
||||
|
||||
const perks: PerksMap = makeEmptyPerksMap();
|
||||
|
||||
|
@ -166,8 +167,12 @@ background.addEventListener("load", () => {
|
|||
needsRender = true;
|
||||
});
|
||||
|
||||
let lastWidth = 0,
|
||||
lastHeight = 0;
|
||||
export const fitSize = () => {
|
||||
const { width, height } = gameCanvas.getBoundingClientRect();
|
||||
lastWidth = width;
|
||||
lastHeight = height;
|
||||
gameCanvas.width = width;
|
||||
gameCanvas.height = height;
|
||||
ctx.fillStyle = currentLevelInfo()?.color || "black";
|
||||
|
@ -177,12 +182,10 @@ export const fitSize = () => {
|
|||
backgroundCanvas.height = height;
|
||||
|
||||
gameZoneHeight = isSettingOn("mobile-mode") ? (height * 80) / 100 : height;
|
||||
const baseWidth = Math.round(
|
||||
Math.min(gameCanvas.width, gameZoneHeight * 0.73),
|
||||
);
|
||||
const baseWidth = Math.round(Math.min(lastWidth, gameZoneHeight * 0.73));
|
||||
brickWidth = Math.floor(baseWidth / gridSize / 2) * 2;
|
||||
gameZoneWidth = brickWidth * gridSize;
|
||||
offsetX = Math.floor((gameCanvas.width - gameZoneWidth) / 2);
|
||||
offsetX = Math.floor((lastWidth - gameZoneWidth) / 2);
|
||||
offsetXRoundedDown = offsetX;
|
||||
if (offsetX < ballSize) offsetXRoundedDown = 0;
|
||||
gameZoneWidthRoundedUp = width - 2 * offsetXRoundedDown;
|
||||
|
@ -202,6 +205,12 @@ export const fitSize = () => {
|
|||
window.addEventListener("resize", 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() {
|
||||
// We never want the ball to completely stop, it will move at least 3px per frame
|
||||
baseSpeed = Math.max(
|
||||
|
@ -280,7 +289,7 @@ function addToScore(coin: Coin) {
|
|||
color: coin.color,
|
||||
x: coin.previousX,
|
||||
y: coin.previousY,
|
||||
vx: (gameCanvas.width - coin.x) / 100,
|
||||
vx: (lastWidth - coin.x) / 100,
|
||||
vy: -coin.y / 100,
|
||||
ethereal: true,
|
||||
});
|
||||
|
@ -306,7 +315,7 @@ function resetBalls() {
|
|||
}
|
||||
for (let i = 0; i < count; i++) {
|
||||
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({
|
||||
x,
|
||||
|
@ -341,9 +350,9 @@ function putBallsAtPuck() {
|
|||
ball.y = gameZoneHeight - 1.5 * ballSize;
|
||||
ball.previousY = ball.y;
|
||||
ball.vx = Math.random() > 0.5 ? baseSpeed : -baseSpeed;
|
||||
ball.previousVX=ball.vx
|
||||
ball.previousVX = ball.vx;
|
||||
ball.vy = -baseSpeed;
|
||||
ball.previousVY=ball.vy
|
||||
ball.previousVY = ball.vy;
|
||||
ball.sx = 0;
|
||||
ball.sy = 0;
|
||||
ball.hitItem = [];
|
||||
|
@ -486,6 +495,7 @@ function getPossibleUpgrades() {
|
|||
|
||||
function shuffleLevels(nameToAvoid: string | null = null) {
|
||||
const target = nextRunOverrides?.level;
|
||||
delete nextRunOverrides.level;
|
||||
const firstLevel = nextRunOverrides?.level
|
||||
? allLevels.filter((l) => l.name === target)
|
||||
: [];
|
||||
|
@ -502,7 +512,7 @@ function shuffleLevels(nameToAvoid :string|null= null) {
|
|||
}
|
||||
|
||||
function getUpgraderUnlockPoints() {
|
||||
let list = [] as {threshold:number,title:string}[];
|
||||
let list = [] as { threshold: number; title: string }[];
|
||||
|
||||
upgrades.forEach((u) => {
|
||||
if (u.threshold) {
|
||||
|
@ -803,8 +813,8 @@ function bordersHitCheck(coin: Coin | Ball, radius: number, delta: number) {
|
|||
coin.vy *= -1;
|
||||
vhit = 1;
|
||||
}
|
||||
if (coin.x > gameCanvas.width - offsetXRoundedDown - radius) {
|
||||
coin.x = gameCanvas.width - offsetXRoundedDown - radius;
|
||||
if (coin.x > lastWidth - offsetXRoundedDown - radius) {
|
||||
coin.x = lastWidth - offsetXRoundedDown - radius;
|
||||
coin.vx *= -1;
|
||||
hhit = 1;
|
||||
}
|
||||
|
@ -902,7 +912,7 @@ function tick() {
|
|||
puckHeight
|
||||
) {
|
||||
addToScore(coin);
|
||||
} else if (coin.y > gameCanvas.height + coinRadius) {
|
||||
} else if (coin.y > lastHeight + coinRadius) {
|
||||
coin.destroyed = true;
|
||||
if (perks.compound_interest) {
|
||||
resetCombo(coin.x, coin.y);
|
||||
|
@ -1761,14 +1771,14 @@ function render() {
|
|||
if (level.svg && background.width && background.complete) {
|
||||
if (backgroundCanvas.title !== level.name) {
|
||||
backgroundCanvas.title = level.name;
|
||||
backgroundCanvas.width = gameCanvas.width;
|
||||
backgroundCanvas.height = gameCanvas.height;
|
||||
backgroundCanvas.width = lastWidth;
|
||||
backgroundCanvas.height = lastHeight;
|
||||
const bgctx = backgroundCanvas.getContext(
|
||||
"2d",
|
||||
) as CanvasRenderingContext2D;
|
||||
bgctx.fillStyle = level.color || "#000";
|
||||
bgctx.fillRect(0, 0, gameCanvas.width, gameCanvas.height);
|
||||
const pattern=ctx.createPattern(background, "repeat")
|
||||
bgctx.fillRect(0, 0, lastWidth, lastHeight);
|
||||
const pattern = ctx.createPattern(background, "repeat");
|
||||
if (pattern) {
|
||||
bgctx.fillStyle = pattern;
|
||||
bgctx.fillRect(0, 0, width, height);
|
||||
|
@ -1940,8 +1950,8 @@ function render() {
|
|||
"Press and hold here to play",
|
||||
puckColor,
|
||||
puckHeight,
|
||||
gameCanvas.width / 2,
|
||||
gameZoneHeight + (gameCanvas.height - gameZoneHeight) / 2,
|
||||
lastWidth / 2,
|
||||
gameZoneHeight + (lastHeight - gameZoneHeight) / 2,
|
||||
);
|
||||
}
|
||||
} else if (redBottom) {
|
||||
|
@ -1961,7 +1971,7 @@ function render() {
|
|||
}
|
||||
|
||||
let cachedBricksRender = document.createElement("canvas");
|
||||
let cachedBricksRenderKey = '';
|
||||
let cachedBricksRenderKey = "";
|
||||
|
||||
function renderAllBricks() {
|
||||
ctx.globalAlpha = 1;
|
||||
|
@ -2569,7 +2579,8 @@ type AsyncAlertAction<t> = {
|
|||
disabled?: boolean;
|
||||
icon?: string;
|
||||
className?: string;
|
||||
}
|
||||
};
|
||||
|
||||
function asyncAlert<t>({
|
||||
title,
|
||||
text,
|
||||
|
@ -2684,7 +2695,7 @@ let cachedSettings : Partial<{[key in OptionId]:boolean}>= {};
|
|||
export function isSettingOn(key: OptionId) {
|
||||
if (typeof cachedSettings[key] == "undefined") {
|
||||
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;
|
||||
} catch (e) {
|
||||
console.warn(e);
|
||||
|
@ -2749,7 +2760,8 @@ document.getElementById("menu")?.addEventListener("click", (e) => {
|
|||
async function openSettingsPanel() {
|
||||
pause(true);
|
||||
|
||||
const actions :AsyncAlertAction<()=>void>[]= [{
|
||||
const actions: AsyncAlertAction<() => void>[] = [
|
||||
{
|
||||
text: "Resume",
|
||||
help: "Return to your run",
|
||||
value() {},
|
||||
|
@ -2760,7 +2772,8 @@ async function openSettingsPanel() {
|
|||
value() {
|
||||
openUnlocksList();
|
||||
},
|
||||
}];
|
||||
},
|
||||
];
|
||||
|
||||
for (const key of Object.keys(options) as OptionId[]) {
|
||||
if (options[key])
|
||||
|
@ -2788,7 +2801,7 @@ async function openSettingsPanel() {
|
|||
value() {
|
||||
toggleFullScreen();
|
||||
},
|
||||
} )
|
||||
});
|
||||
} else {
|
||||
actions.push({
|
||||
icon: icons["icon:fullscreen"],
|
||||
|
@ -2796,8 +2809,8 @@ async function openSettingsPanel() {
|
|||
help: "Might not work on some machines",
|
||||
value() {
|
||||
toggleFullScreen();
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
actions.push({
|
||||
|
@ -2842,7 +2855,7 @@ async function openSettingsPanel() {
|
|||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
});
|
||||
actions.push({
|
||||
text: "Reset Game",
|
||||
help: "Erase high score and statistics",
|
||||
|
@ -2867,8 +2880,7 @@ async function openSettingsPanel() {
|
|||
window.location.reload();
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
const cb = await asyncAlert<() => void>({
|
||||
title: "Breakout 71",
|
||||
|
@ -2990,7 +3002,11 @@ function repulse(a: Ball, b: BallLike, power: number, impactsBToo: boolean) {
|
|||
(((-power * (max - distance)) / (max * 1.2) / 3) *
|
||||
Math.min(500, levelTime)) /
|
||||
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.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,
|
||||
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({
|
||||
type: "particle",
|
||||
duration: 100,
|
||||
|
|
|
@ -73,7 +73,10 @@ export const icons = {} as {[k:string]:string};
|
|||
|
||||
export const allLevels = rawLevelsList
|
||||
.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);
|
||||
icons[level.name] = icon;
|
||||
let svg = level.svg;
|
||||
|
@ -91,16 +94,15 @@ export const allLevels = rawLevelsList
|
|||
.filter((l) => !l.name.startsWith("icon:"))
|
||||
.map((l, li) => ({
|
||||
...l,
|
||||
threshold:li < 8
|
||||
threshold:
|
||||
li < 8
|
||||
? 0
|
||||
: Math.round(
|
||||
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[];
|
||||
|
||||
|
||||
|
||||
export const upgrades = rawUpgrades.map((u) => ({
|
||||
...u,
|
||||
icon: icons["icon:" + u.id],
|
||||
|
|
11
src/types.d.ts
vendored
11
src/types.d.ts
vendored
|
@ -111,23 +111,22 @@ interface BaseFlash {
|
|||
y: number;
|
||||
}
|
||||
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';
|
||||
type: "ball";
|
||||
}
|
||||
|
||||
export type Flash = ParticleFlash|TextFlash|BallFlash
|
||||
|
||||
export type Flash = ParticleFlash | TextFlash | BallFlash;
|
||||
|
||||
export type RunStats = {
|
||||
started: number;
|
||||
|
@ -148,8 +147,6 @@ export type PerksMap = {
|
|||
[k in PerkId]: number;
|
||||
};
|
||||
|
||||
|
||||
|
||||
export type RunHistoryItem = RunStats & {
|
||||
perks?: PerksMap;
|
||||
appVersion?: string;
|
||||
|
|
|
@ -1 +1 @@
|
|||
"29022953"
|
||||
"29028296"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue