This commit is contained in:
Renan LE CARO 2025-03-20 21:02:51 +01:00
parent 3eca148fb8
commit 760fa5715b
16 changed files with 289 additions and 152 deletions

View file

@ -117,11 +117,9 @@ There's also an easy mode for kids (slower ball).
- [colin] mirror puck - a mirrored puck at the top of the screen follows as you move the bottom puck. it helps with keeping combos up and preventing the ball from touching the ceiling. it could appear as a hollow puck so as to not draw too much attention from the main bottom puck.
- [colin] side pucks - same as above but with two side pucks.
- [colin] ball coins - coins share the same physics as coins and bounce on walls and bricks
- [colin] drifting coins - coins slowly drift away from the brick they were generated from, and they need to be collected by the ball
- [colin] bigger ball - self-explanatory
- [colin] smaller ball - yes.
- [colin] sturdy ball - does more damage to bricks, to conter sturdy bricks
- [colin] accumulation - coins agglutinate into bigger coins that hold more value
- [colin] plot - plot the ball's trajectory as you position your puck
- [colin] golden corners - catch coins at the sides of the puck to double their value
- [colin] varied diet - your combo grows if you keep hitting different coloured bricks each time

View file

@ -11,8 +11,8 @@ android {
applicationId = "me.lecaro.breakout"
minSdk = 21
targetSdk = 34
versionCode = 29041544
versionName = "29041544"
versionCode = 29041682
versionName = "29041682"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary = true

File diff suppressed because one or more lines are too long

276
dist/index.html vendored

File diff suppressed because one or more lines are too long

View file

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

View file

@ -46,7 +46,10 @@ export async function asyncAlert<t>({
closeModaleButton.style.display = allowClose ? "" : "none";
const popup = document.createElement("div");
let closed = false;
function closeWithResult(value: t | undefined) {
if (closed) return;
closed = true;
document.body.style.minHeight = document.body.scrollHeight + "px";
setTimeout(() => (document.body.style.minHeight = ""), 100);
popup.remove();
@ -137,7 +140,7 @@ ${icon}
function updateAlertsOpen(delta: number) {
alertsOpen += delta;
if (alertsOpen > 1) {
throw new Error("Cannot open two alerts at once");
alert("Two alerts where opened at once");
}
document.body.classList[alertsOpen ? "add" : "remove"]("has-alert-open");
}

View file

@ -939,5 +939,12 @@
"bricks": "WWW_____WWW_y___WWW____y__y_y____y____y_____y_____y____y___y_y__",
"svg": null,
"color": ""
},
{
"name": "icon:reach",
"size": 8,
"bricks": "_________yyyyyy__yyyyyy__yyyyyy__rrrrrr_______W____________WWW__",
"svg": null,
"color": ""
}
]

View file

@ -1 +1 @@
"29041544"
"29041682"

View file

@ -897,8 +897,3 @@ export function restart(params: RunParams) {
restart({});
fitSize();
tick();
// @ts-ignore
// window.stressTest= ()=>restart({level:'Shark',perks:{base_combo:100, pierce:10, multiball:8}})
window.stressTest = () =>
restart({ level: "Bird", perks: { sapper: 2, pierce: 10, multiball: 3 } });

View file

@ -15,6 +15,8 @@ import {
brickCenterX,
brickCenterY,
clamp,
countBricksAbove,
countBricksBelow,
currentLevelInfo,
distance2,
distanceBetween,
@ -367,6 +369,17 @@ export function explodeBrick(
gameState.perks.zen +
gameState.perks.unbounded;
if (gameState.perks.reach) {
if (
countBricksAbove(gameState, index) &&
!countBricksBelow(gameState, index)
) {
resetCombo(gameState, x, y);
} else {
gameState.combo += gameState.perks.reach;
}
}
if (!isExplosion) {
// color change
if (
@ -503,7 +516,7 @@ export async function setLevel(gameState: GameState, l: number) {
// Reset combo silently
const finalCombo = gameState.combo;
gameState.combo = baseCombo(gameState);
if (!gameState.perks.shunt) {
if (gameState.perks.shunt) {
gameState.combo += Math.round(
Math.max(
0,

View file

@ -148,3 +148,26 @@ export function shouldPierceByColor(
}
return true;
}
export function countBricksAbove(gameState: GameState, index: number) {
const col = index % gameState.gridSize;
const row = Math.floor(index / gameState.gridSize);
let count = 0;
for (let y = 0; y < row; y++) {
if (gameState.bricks[col + y * gameState.gridSize]) {
count++;
}
}
return count;
}
export function countBricksBelow(gameState: GameState, index: number) {
const col = index % gameState.gridSize;
const row = Math.floor(index / gameState.gridSize);
let count = 0;
for (let y = row + 1; y < gameState.gridSize; y++) {
if (gameState.bricks[col + y * gameState.gridSize]) {
count++;
}
}
return count;
}

View file

@ -2942,6 +2942,56 @@
</concept_node>
</children>
</folder_node>
<folder_node>
<name>reach</name>
<children>
<concept_node>
<name>fullHelp</name>
<description/>
<comment/>
<translations>
<translation>
<language>en-US</language>
<approved>false</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>false</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>help</name>
<description/>
<comment/>
<translations>
<translation>
<language>en-US</language>
<approved>true</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>true</approved>
</translation>
</translations>
</concept_node>
<concept_node>
<name>name</name>
<description/>
<comment/>
<translations>
<translation>
<language>en-US</language>
<approved>true</approved>
</translation>
<translation>
<language>fr-FR</language>
<approved>true</approved>
</translation>
</translations>
</concept_node>
</children>
</folder_node>
<folder_node>
<name>respawn</name>
<children>

View file

@ -181,6 +181,9 @@
"upgrades.puck_repulse_ball.help": "Puck repulses balls",
"upgrades.puck_repulse_ball.help_plural": "Stronger repulsion force",
"upgrades.puck_repulse_ball.name": "Soft landing",
"upgrades.reach.fullHelp": "Try to lock the ball up to earn more combo",
"upgrades.reach.help": "+{{lvl}} combo / bricks , lowest brick of a pile resets combo",
"upgrades.reach.name": "Top down",
"upgrades.respawn.fullHelp": "After breaking two or more bricks, when the ball hits the puck, the first brick will be put back in place, provided that space is free and the brick wasn't a bomb.\n\nSome particle effect will let you know where bricks will appear. Leveling this up lets you re-spawn up to 4 bricks at a time, but there should always be at least one destroyed.",
"upgrades.respawn.help": "The first brick hit of two+ will re-spawn",
"upgrades.respawn.help_plural": "More bricks can re-spawn",

View file

@ -181,6 +181,9 @@
"upgrades.puck_repulse_ball.help": "Le palet repousse les balles",
"upgrades.puck_repulse_ball.help_plural": "La force de répulsion est plus grande",
"upgrades.puck_repulse_ball.name": "Atterrissage en douceur",
"upgrades.reach.fullHelp": "Essayez de bloquer la balle au dessus des briques pour plus de combo",
"upgrades.reach.help": "+{{lvl}} combo / brique, la plus basse d'une colonne RAZ le combo",
"upgrades.reach.name": "Attaque aérienne",
"upgrades.respawn.fullHelp": "Après avoir cassé deux briques ou plus, lorsque la balle touche le palet, la première brique est remise en place, à condition que l'espace soit libre et que la brique ne soit pas une bombe.\n\nDes effets de particules vous indiqueront où les briques apparaîtront. \n\nEn montant en niveau, vous pouvez faire réapparaître jusqu'à 4 briques à la fois, mais il doit toujours y en avoir au moins une qui reste détruite.",
"upgrades.respawn.help": "Certaines briques réapparaissent après avoir été détruites.",
"upgrades.respawn.help_plural": "Plus de briques peuvent réapparaître",

View file

@ -2,6 +2,8 @@ import { baseCombo, forEachLiveOne, liveCount } from "./gameStateMutators";
import {
brickCenterX,
brickCenterY,
countBricksAbove,
countBricksBelow,
currentLevelInfo,
isTelekinesisActive,
isYoyoActive,
@ -17,7 +19,6 @@ export const ctx = gameCanvas.getContext("2d", {
alpha: false,
}) as CanvasRenderingContext2D;
export const bombSVG = document.createElement("img");
bombSVG.src =
"data:image/svg+xml;base64," +
btoa(`<svg width="144" height="144" viewBox="0 0 38.101 38.099" xmlns="http://www.w3.org/2000/svg">
@ -150,13 +151,11 @@ export function render(gameState: GameState) {
Math.sin(Date.now() + 36) * amplitude,
);
}
if (gameState.perks.bigger_explosions && !isOptionOn("basic")) {
if (shaked) {
gameCanvas.style.filter =
"brightness(" + (1 + 100 / (1 + lastExplosionDelay)) + ")";
} else {
gameCanvas.style.filter = "";
}
if (gameState.perks.bigger_explosions && !isOptionOn("basic") && shaked) {
gameCanvas.style.filter =
"brightness(" + (1 + 100 / (1 + lastExplosionDelay)) + ")";
} else {
gameCanvas.style.filter = "";
}
// Coins
ctx.globalAlpha = 1;
@ -417,14 +416,18 @@ export function renderAllBricks() {
if (!color) return;
const borderColor =
let redBecauseOfReach =
gameState.perks.reach &&
countBricksAbove(gameState, index) &&
!countBricksBelow(gameState, index);
let redBorder =
(gameState.ballsColor !== color &&
color !== "black" &&
redBorderOnBricksWithWrongColor &&
"red") ||
color;
redBorderOnBricksWithWrongColor) ||
redBecauseOfReach;
drawBrick(canctx, color, (redBorder && "red") || color, x, y);
drawBrick(canctx, color, borderColor, x, y);
if (color === "black") {
canctx.globalCompositeOperation = "source-over";
drawIMG(canctx, bombSVG, gameState.brickWidth, x, y);

View file

@ -553,4 +553,15 @@ export const rawUpgrades = [
help: (lvl: number) => t("upgrades.ball_attracts_coins.help"),
fullHelp: t("upgrades.ball_attracts_coins.fullHelp"),
},
{
requires: "",
rejects: "",
threshold: 135000,
giftable: false,
id: "reach",
max: 3,
name: t("upgrades.reach.name"),
help: (lvl: number) => t("upgrades.reach.help", { lvl }),
fullHelp: t("upgrades.reach.fullHelp"),
},
] as const;