Build 29074681

This commit is contained in:
Renan LE CARO 2025-04-12 20:01:43 +02:00
parent 48ac639901
commit 19ee1d0d33
9 changed files with 152 additions and 40 deletions

View file

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

File diff suppressed because one or more lines are too long

73
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. // The version of the cache.
const VERSION = "29074419"; const VERSION = "29074681";
// The name of the cache // The name of the cache
const CACHE_NAME = `breakout-71-${VERSION}`; const CACHE_NAME = `breakout-71-${VERSION}`;

View file

@ -1 +1 @@
"29074419" "29074681"

View file

@ -7,6 +7,7 @@ import {
OptionId, OptionId,
ParticleFlash, ParticleFlash,
PerkId, PerkId,
PerksMap,
RunParams, RunParams,
TextFlash, TextFlash,
} from "./types"; } from "./types";
@ -20,6 +21,7 @@ import {
max_levels, max_levels,
pickedUpgradesHTMl, pickedUpgradesHTMl,
reasonLevelIsLocked, reasonLevelIsLocked,
sample,
} from "./game_utils"; } from "./game_utils";
import "./PWA/sw_loader"; import "./PWA/sw_loader";
@ -103,6 +105,7 @@ export async function play() {
export function pause(playerAskedForPause: boolean) { export function pause(playerAskedForPause: boolean) {
if (!gameState.running) return; if (!gameState.running) return;
if (gameState.pauseTimeout) return; if (gameState.pauseTimeout) return;
if (gameState.computer_controlled) return;
const stop = () => { const stop = () => {
gameState.running = false; gameState.running = false;
@ -580,6 +583,7 @@ function donationNag(gameState) {
}, },
]; ];
} }
async function openSettingsMenu() { async function openSettingsMenu() {
pause(true); pause(true);
@ -999,22 +1003,30 @@ export function restart(params: RunParams) {
fitSize(gameState); fitSize(gameState);
pauseRecording(); pauseRecording();
setLevel(gameState, 0); setLevel(gameState, 0);
if (params?.computer_controlled) {
play();
}
} }
restart( if (window.location.search.includes("autoplay")) {
window.location.search.includes("stress") startComputerControlledGame();
? { } else {
perks: { restart({});
bricks_attract_ball: 2, }
superhot: 1,
bricks_attract_coins: 3, export function startComputerControlledGame() {
hot_start: 3, const perks: Partial<PerksMap> = { base_combo: 7, pierce: 3 };
pierce: 3, for (let i = 0; i < 10; i++) {
rainbow: 3, const u = sample(upgrades);
}, perks[u.id] = Math.floor(Math.random() * u.max) + 1;
} }
: {}, perks.superhot = 0;
); restart({
level: sample(allLevels)?.name,
computer_controlled: true,
perks,
});
}
tick(); tick();
setupTooltips(); setupTooltips();

View file

@ -44,13 +44,16 @@ import {
hitsSomething, hitsSomething,
openUpgradesPicker, openUpgradesPicker,
pause, pause,
startComputerControlledGame,
} from "./game"; } from "./game";
import { stopRecording } from "./recording"; import { stopRecording } from "./recording";
import { isOptionOn } from "./options"; import { isOptionOn } from "./options";
import { clamp, comboKeepingRate } from "./pure_functions"; import { clamp, comboKeepingRate } from "./pure_functions";
import { addToTotalScore } from "./addToTotalScore"; import { addToTotalScore } from "./addToTotalScore";
import { hashCode } from "./getLevelBackground";
export function setMousePos(gameState: GameState, x: number) { export function setMousePos(gameState: GameState, x: number) {
if (gameState.computer_controlled) return;
gameState.puckPosition = x; gameState.puckPosition = x;
// Sets the puck position, and updates the ball position if they are supposed to follow it // Sets the puck position, and updates the ball position if they are supposed to follow it
@ -64,6 +67,48 @@ function getBallDefaultVx(gameState: GameState) {
); );
} }
function computerControl(gameState: GameState) {
let targetX = gameState.puckPosition;
const ball = getClosestBall(
gameState,
gameState.puckPosition,
gameState.gameZoneHeight,
);
if (!ball) return;
const puckOffset =
(((hashCode(gameState.runStatistics.puck_bounces + "goeirjgoriejg") % 100) -
50) /
100) *
gameState.puckWidth;
if (ball.y > gameState.gameZoneHeight / 2 && ball.vy > 0) {
targetX = ball.x + puckOffset;
} else {
let coinsTotalX = 0,
coinsCount = 0;
forEachLiveOne(gameState.coins, (c) => {
if (c.vy > 0 && c.y > gameState.gameZoneHeight / 2) {
coinsTotalX += c.x;
coinsCount++;
}
});
if (coinsCount) {
targetX = coinsTotalX / coinsCount;
} else {
targetX = ball.x;
}
}
gameState.puckPosition += clamp(
(targetX - gameState.puckPosition) / 10,
-10,
10,
);
if (gameState.levelTime > 30000) {
startComputerControlledGame();
}
}
export function resetBalls(gameState: GameState) { export function resetBalls(gameState: GameState) {
// Always compute speed first // Always compute speed first
normalizeGameState(gameState); normalizeGameState(gameState);
@ -592,6 +637,7 @@ export function schedulGameSound(
) { ) {
if (!vol) return; if (!vol) return;
if (!isOptionOn("sound")) return; if (!isOptionOn("sound")) return;
if (gameState.computer_controlled) return;
x ??= gameState.offsetX + gameState.gameZoneWidth / 2; x ??= gameState.offsetX + gameState.gameZoneWidth / 2;
const ex = gameState.aboutToPlaySound[sound] as { vol: number; x: number }; const ex = gameState.aboutToPlaySound[sound] as { vol: number; x: number };
@ -945,6 +991,9 @@ export function gameStateTick(
// How many frames to compute at once, can go above 1 to compensate lag // How many frames to compute at once, can go above 1 to compensate lag
frames = 1, frames = 1,
) { ) {
// Ai movement of puck
if (gameState.computer_controlled) computerControl(gameState);
gameState.runStatistics.max_combo = Math.max( gameState.runStatistics.max_combo = Math.max(
gameState.runStatistics.max_combo, gameState.runStatistics.max_combo,
gameState.combo, gameState.combo,
@ -1019,7 +1068,9 @@ export function gameStateTick(
// instant win condition // instant win condition
(gameState.levelTime && !remainingBricks && !liveCount(gameState.coins)) (gameState.levelTime && !remainingBricks && !liveCount(gameState.coins))
) { ) {
if (gameState.currentLevel + 1 < max_levels(gameState)) { if (gameState.computer_controlled) {
startComputerControlledGame();
} else if (gameState.currentLevel + 1 < max_levels(gameState)) {
setLevel(gameState, gameState.currentLevel + 1); setLevel(gameState, gameState.currentLevel + 1);
} else { } else {
gameOver( gameOver(
@ -1659,10 +1710,14 @@ export function ballTick(gameState: GameState, ball: Ball, frames: number) {
ball.destroyed = true; ball.destroyed = true;
gameState.runStatistics.balls_lost++; gameState.runStatistics.balls_lost++;
if (!gameState.balls.find((b) => !b.destroyed)) { if (!gameState.balls.find((b) => !b.destroyed)) {
gameOver( if (gameState.computer_controlled) {
t("gameOver.lost.title"), startComputerControlledGame();
t("gameOver.lost.summary", { score: gameState.score }), } else {
); gameOver(
t("gameOver.lost.title"),
t("gameOver.lost.summary", { score: gameState.score }),
);
}
} }
} }
const radius = gameState.ballSize / 2; const radius = gameState.ballSize / 2;

View file

@ -139,8 +139,10 @@ export function newGameState(params: RunParams): GameState {
...defaultSounds(), ...defaultSounds(),
rerolls: 0, rerolls: 0,
creative: creative:
params?.computer_controlled ||
sumOfValues(params.perks) > 1 || sumOfValues(params.perks) > 1 ||
(params.level && !params.level.startsWith("icon:")), (params.level && !params.level.startsWith("icon:")),
computer_controlled: params?.computer_controlled || false,
}; };
resetBalls(gameState); resetBalls(gameState);

2
src/types.d.ts vendored
View file

@ -281,6 +281,7 @@ export type GameState = {
}; };
rerolls: number; rerolls: number;
creative: boolean; creative: boolean;
computer_controlled: boolean;
}; };
export type RunParams = { export type RunParams = {
@ -288,6 +289,7 @@ export type RunParams = {
levelToAvoid?: string; levelToAvoid?: string;
perkToAvoid?: PerkId; perkToAvoid?: PerkId;
perks?: Partial<PerksMap>; perks?: Partial<PerksMap>;
computer_controlled?: boolean;
}; };
export type OptionDef = { export type OptionDef = {
default: boolean; default: boolean;