mirror of
https://gitlab.com/lecarore/breakout71.git
synced 2025-04-26 15:06:16 -04:00
wip
This commit is contained in:
parent
aa8d816d68
commit
0035a9abb5
7 changed files with 2124 additions and 2124 deletions
|
@ -356,8 +356,9 @@ Break colourful bricks, catch bouncing coins and select powerful upgrades !
|
|||
|
||||
# Ideas and features
|
||||
|
||||
## Easy perks ideas
|
||||
## Easy perk ideas
|
||||
|
||||
- square coins : coins loose all horizontal momentum when hitting something.
|
||||
- ball turns following puck motion
|
||||
- "+1 coin for each ball within a small radius of the broken brick" ?
|
||||
- two for one : add a 2 for one upgrade combo to the choice lists
|
||||
|
@ -389,6 +390,7 @@ Break colourful bricks, catch bouncing coins and select powerful upgrades !
|
|||
- fan : paddle motion creates upward draft that lifts coins and balls
|
||||
|
||||
## Medium difficulty perks ideas
|
||||
- coins combine when they hit (into one coin with the sum of the values, but need a way to represent that)
|
||||
- balls collision split them into 4 smaller balls, lvl times (requires rework)
|
||||
- offer next level choice after upgrade pick
|
||||
- [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.
|
||||
|
|
29
dist/index.html
vendored
29
dist/index.html
vendored
File diff suppressed because one or more lines are too long
|
@ -204,7 +204,7 @@
|
|||
"credit": ""
|
||||
},
|
||||
{
|
||||
"name": "icon:hypnosis",
|
||||
"name": "icon:golden_goose",
|
||||
"size": 8,
|
||||
"bricks": "_bbby____bbb_y___bbby_y__y_y_y_y__y_y_y____y_y_y____y_y______y_y",
|
||||
"credit": ""
|
||||
|
|
11
src/game.ts
11
src/game.ts
|
@ -113,6 +113,7 @@ export async function play() {
|
|||
}
|
||||
|
||||
export function pause(playerAskedForPause: boolean) {
|
||||
|
||||
if (!gameState.running) return;
|
||||
if (gameState.pauseTimeout) return;
|
||||
if (gameState.startParams.computer_controlled) {
|
||||
|
@ -457,7 +458,10 @@ export function tick() {
|
|||
Math.max(0, ...gameState.balls.map(({ vx, vy }) => vx * vx + vy * vy)),
|
||||
) * frames;
|
||||
const steps = Math.ceil(maxBallSpeed / 8);
|
||||
for (let i = 0; i < steps; i++) gameStateTick(gameState, frames / steps);
|
||||
for (let i = 0; i < steps; i++) {
|
||||
gameStateTick(gameState, frames / steps);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (gameState.running || gameState.needsRender) {
|
||||
|
@ -535,11 +539,6 @@ setInterval(() => {
|
|||
monitorLevelsUnlocks(gameState);
|
||||
}, 500);
|
||||
|
||||
window.addEventListener("visibilitychange", () => {
|
||||
if (document.hidden) {
|
||||
pause(true);
|
||||
}
|
||||
});
|
||||
|
||||
scoreDisplay.addEventListener("click", (e) => {
|
||||
e.preventDefault();
|
||||
|
|
|
@ -28,10 +28,12 @@ export function addToTotalPlayTime(ms: number) {
|
|||
|
||||
export function gameOver(title: string, intro: string) {
|
||||
if (!gameState.running) return;
|
||||
|
||||
// Ignore duplicated calls, can happen when ticking is split in multiple updates because the ball goes fast
|
||||
if (gameState.isGameOver) return;
|
||||
|
||||
gameState.isGameOver = true;
|
||||
pause(true);
|
||||
pause(false);
|
||||
askForPersistentStorage();
|
||||
stopRecording();
|
||||
addToTotalPlayTime(gameState.runStatistics.runTime);
|
||||
|
|
|
@ -31,12 +31,12 @@ import {
|
|||
telekinesisEffectRate,
|
||||
yoyoEffectRate,
|
||||
} from "./game_utils";
|
||||
import { t } from "./i18n/i18n";
|
||||
import { icons } from "./loadGameData";
|
||||
import {t} from "./i18n/i18n";
|
||||
import {icons} from "./loadGameData";
|
||||
|
||||
import { getCurrentMaxCoins, getCurrentMaxParticles } from "./settings";
|
||||
import { background } from "./render";
|
||||
import { gameOver } from "./gameOver";
|
||||
import {getCurrentMaxCoins, getCurrentMaxParticles} from "./settings";
|
||||
import {background} from "./render";
|
||||
import {gameOver} from "./gameOver";
|
||||
import {
|
||||
brickIndex,
|
||||
fitSize,
|
||||
|
@ -47,16 +47,11 @@ import {
|
|||
pause,
|
||||
startComputerControlledGame,
|
||||
} from "./game";
|
||||
import { stopRecording } from "./recording";
|
||||
import { isOptionOn } from "./options";
|
||||
import {
|
||||
ballTransparency,
|
||||
clamp,
|
||||
coinsBoostedCombo,
|
||||
comboKeepingRate,
|
||||
} from "./pure_functions";
|
||||
import { addToTotalScore } from "./addToTotalScore";
|
||||
import { hashCode } from "./getLevelBackground";
|
||||
import {stopRecording} from "./recording";
|
||||
import {isOptionOn} from "./options";
|
||||
import {ballTransparency, clamp, coinsBoostedCombo, comboKeepingRate,} from "./pure_functions";
|
||||
import {addToTotalScore} from "./addToTotalScore";
|
||||
import {hashCode} from "./getLevelBackground";
|
||||
|
||||
export function setMousePos(gameState: GameState, x: number) {
|
||||
if (gameState.startParams.computer_controlled) return;
|
||||
|
@ -269,14 +264,14 @@ export function offsetCombo(
|
|||
x: number,
|
||||
y: number,
|
||||
) {
|
||||
if(!by) return
|
||||
if (!by) return
|
||||
if (by > 0) {
|
||||
|
||||
by *= 1 + gameState.perks.double_or_nothing;
|
||||
gameState.combo += by;
|
||||
makeText(gameState, x, y, "#ffd300", "+" + by, 25, 400 + by);
|
||||
|
||||
}else{
|
||||
} else {
|
||||
const prev = gameState.combo;
|
||||
gameState.combo = Math.max(baseCombo(gameState), gameState.combo + by);
|
||||
const lost = Math.max(0, prev - gameState.combo);
|
||||
|
@ -440,7 +435,7 @@ export function explodeBrick(
|
|||
while (coinsToSpawn > 0) {
|
||||
const points = Math.min(pointsPerCoin, coinsToSpawn);
|
||||
if (points < 0 || isNaN(points)) {
|
||||
console.error({ points });
|
||||
console.error({points});
|
||||
debugger;
|
||||
}
|
||||
|
||||
|
@ -463,7 +458,7 @@ export function explodeBrick(
|
|||
points,
|
||||
);
|
||||
}
|
||||
let resetComboNeeeded=false
|
||||
let resetComboNeeeded = false
|
||||
let comboGain = gameState.perks.streak_shots +
|
||||
gameState.perks.compound_interest +
|
||||
gameState.perks.left_is_lava +
|
||||
|
@ -478,17 +473,17 @@ export function explodeBrick(
|
|||
if (Math.abs(ball.y - y) < Math.abs(ball.x - x)) {
|
||||
if (gameState.perks.side_kick) {
|
||||
if (ball.previousVX > 0) {
|
||||
comboGain+=gameState.perks.side_kick
|
||||
comboGain += gameState.perks.side_kick
|
||||
} else {
|
||||
comboGain-=gameState.perks.side_kick * 2
|
||||
comboGain -= gameState.perks.side_kick * 2
|
||||
|
||||
}
|
||||
}
|
||||
if (gameState.perks.side_flip) {
|
||||
if (ball.previousVX < 0) {
|
||||
comboGain+=gameState.perks.side_flip
|
||||
comboGain += gameState.perks.side_flip
|
||||
} else {
|
||||
comboGain-=gameState.perks.side_flip * 2
|
||||
comboGain -= gameState.perks.side_flip * 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -496,11 +491,11 @@ export function explodeBrick(
|
|||
|
||||
if (redRowReach !== -1) {
|
||||
if (Math.floor(index / gameState.level.size) === redRowReach) {
|
||||
resetComboNeeeded=true
|
||||
resetComboNeeeded = true
|
||||
} else {
|
||||
for (let x = 0; x < gameState.level.size; x++) {
|
||||
if (gameState.bricks[redRowReach * gameState.level.size + x])
|
||||
comboGain+=gameState.perks.reach;
|
||||
comboGain += gameState.perks.reach;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -513,7 +508,7 @@ export function explodeBrick(
|
|||
color
|
||||
) {
|
||||
if (wasPickyEaterPossible) {
|
||||
resetComboNeeeded=true
|
||||
resetComboNeeeded = true
|
||||
}
|
||||
schedulGameSound(gameState, "colorChange", ball.x, 0.8);
|
||||
// gameState.lastExplosion = gameState.levelTime;
|
||||
|
@ -528,11 +523,11 @@ export function explodeBrick(
|
|||
}
|
||||
}
|
||||
|
||||
if(resetComboNeeeded){
|
||||
if (resetComboNeeeded) {
|
||||
resetCombo(gameState,
|
||||
ball.x,
|
||||
ball.y)
|
||||
}else {
|
||||
} else {
|
||||
offsetCombo(
|
||||
gameState,
|
||||
comboGain,
|
||||
|
@ -618,7 +613,8 @@ export function addToScore(gameState: GameState, coin: Coin) {
|
|||
gameState.highScore = gameState.score;
|
||||
try {
|
||||
localStorage.setItem("breakout-3-hs-short", gameState.score.toString());
|
||||
} catch (e) {}
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
if (!isOptionOn("basic")) {
|
||||
makeParticle(
|
||||
|
@ -639,7 +635,7 @@ export function addToScore(gameState: GameState, coin: Coin) {
|
|||
if (gameState.perks.asceticism) {
|
||||
offsetCombo(
|
||||
gameState,
|
||||
- gameState.perks.asceticism * 3 * coin.points,
|
||||
-gameState.perks.asceticism * 3 * coin.points,
|
||||
coin.x,
|
||||
coin.y,
|
||||
);
|
||||
|
@ -647,13 +643,13 @@ export function addToScore(gameState: GameState, coin: Coin) {
|
|||
}
|
||||
|
||||
export async function setLevel(gameState: GameState, l: number) {
|
||||
// Here to alleviate double upgrades issues
|
||||
|
||||
// Ignore duplicated calls, can happen when ticking is split in multiple updates because the ball goes fast
|
||||
if (gameState.upgradesOfferedFor >= l) {
|
||||
debugger;
|
||||
return console.warn("Extra upgrade request ignored ");
|
||||
return
|
||||
}
|
||||
gameState.upgradesOfferedFor = l;
|
||||
pause(false);
|
||||
gameState.upgradesOfferedFor = l;
|
||||
stopRecording();
|
||||
|
||||
if (l > 0) {
|
||||
|
@ -851,7 +847,7 @@ export function attract(gameState: GameState, a: Ball, b: Ball, power: number) {
|
|||
export function coinBrickHitCheck(gameState: GameState, coin: Coin) {
|
||||
// Make ball/coin bonce, and return bricks that were hit
|
||||
const radius = coin.size / 2;
|
||||
const { x, y, previousX, previousY } = coin;
|
||||
const {x, y, previousX, previousY} = coin;
|
||||
|
||||
const vhit = hitsSomething(previousX, y, radius);
|
||||
const hhit = hitsSomething(x, previousY, radius);
|
||||
|
@ -955,6 +951,11 @@ export function gameStateTick(
|
|||
// How many frames to compute at once, can go above 1 to compensate lag
|
||||
frames = 1,
|
||||
) {
|
||||
|
||||
// Going to the next level or getting a game over in a previous sub-tick would pause the game
|
||||
if(!gameState.running) {
|
||||
return
|
||||
}
|
||||
// Ai movement of puck
|
||||
if (gameState.startParams.computer_controlled) computerControl(gameState);
|
||||
|
||||
|
@ -996,7 +997,7 @@ export function gameStateTick(
|
|||
gameState.lastTickDown = gameState.levelTime;
|
||||
offsetCombo(
|
||||
gameState,
|
||||
- gameState.perks.hot_start,
|
||||
-gameState.perks.hot_start,
|
||||
gameState.puckPosition,
|
||||
gameState.gameZoneHeight - 2 * gameState.puckHeight,
|
||||
);
|
||||
|
@ -1017,7 +1018,7 @@ export function gameStateTick(
|
|||
|
||||
const hasPendingBricks = liveCount(gameState.respawns);
|
||||
|
||||
if (gameState.running && !remainingBricks && !hasPendingBricks) {
|
||||
if (!remainingBricks && !hasPendingBricks) {
|
||||
if (!gameState.winAt) {
|
||||
gameState.winAt = gameState.levelTime + 5000;
|
||||
}
|
||||
|
@ -1026,12 +1027,12 @@ export function gameStateTick(
|
|||
}
|
||||
|
||||
if (
|
||||
(gameState.running &&
|
||||
((
|
||||
// Delayed win when coins are still flying
|
||||
gameState.winAt &&
|
||||
gameState.levelTime > gameState.winAt) ||
|
||||
// instant win condition
|
||||
(gameState.levelTime && !remainingBricks && !liveCount(gameState.coins))
|
||||
(gameState.levelTime && !remainingBricks && !liveCount(gameState.coins)))
|
||||
) {
|
||||
if (gameState.startParams.computer_controlled) {
|
||||
startComputerControlledGame(gameState.startParams.stress);
|
||||
|
@ -1040,10 +1041,10 @@ export function gameStateTick(
|
|||
} else {
|
||||
gameOver(
|
||||
t("gameOver.win.title"),
|
||||
t("gameOver.win.summary", { score: gameState.score }),
|
||||
t("gameOver.win.summary", {score: gameState.score}),
|
||||
);
|
||||
}
|
||||
} else if (gameState.running || gameState.levelTime) {
|
||||
} else {
|
||||
const coinRadius = Math.round(gameState.coinSize / 2);
|
||||
|
||||
forEachLiveOne(gameState.coins, (coin, coinIndex) => {
|
||||
|
@ -1587,7 +1588,7 @@ export function gameStateTick(
|
|||
setBrick(gameState, r.index, r.color);
|
||||
destroy(gameState.respawns, ri);
|
||||
} else {
|
||||
const { index, color } = r;
|
||||
const {index, color} = r;
|
||||
const vertical = Math.random() > 0.5;
|
||||
const dx = Math.random() > 0.5 ? 1 : -1;
|
||||
const dy = Math.random() > 0.5 ? 1 : -1;
|
||||
|
@ -1844,7 +1845,7 @@ export function ballTick(gameState: GameState, ball: Ball, frames: number) {
|
|||
(gameState.levelMisses / 10 / gameState.perks.forgiving) *
|
||||
(gameState.combo - baseCombo(gameState)),
|
||||
);
|
||||
offsetCombo(gameState, - loss, ball.x, ball.y);
|
||||
offsetCombo(gameState, -loss, ball.x, ball.y);
|
||||
} else {
|
||||
resetCombo(gameState, ball.x, ball.y);
|
||||
}
|
||||
|
@ -1868,7 +1869,6 @@ export function ballTick(gameState: GameState, ball: Ball, frames: number) {
|
|||
}
|
||||
|
||||
if (
|
||||
gameState.running &&
|
||||
(ball.y > gameState.gameZoneHeight + gameState.ballSize / 2 ||
|
||||
ball.y < -gameState.gameZoneHeight ||
|
||||
ball.x < -gameState.gameZoneHeight ||
|
||||
|
@ -1885,14 +1885,14 @@ export function ballTick(gameState: GameState, ball: Ball, frames: number) {
|
|||
} else {
|
||||
gameOver(
|
||||
t("gameOver.lost.title"),
|
||||
t("gameOver.lost.summary", { score: gameState.score }),
|
||||
t("gameOver.lost.summary", {score: gameState.score}),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
const radius = gameState.ballSize / 2;
|
||||
// Make ball/coin bonce, and return bricks that were hit
|
||||
const { x, y, previousX, previousY } = ball;
|
||||
const {x, y, previousX, previousY} = ball;
|
||||
|
||||
const vhit = hitsSomething(previousX, y, radius);
|
||||
const hhit = hitsSomething(x, previousY, radius);
|
||||
|
@ -2169,7 +2169,7 @@ export function append<T>(
|
|||
makeItem(where.list[where.indexMin]);
|
||||
where.indexMin++;
|
||||
} else {
|
||||
const p = { destroyed: false };
|
||||
const p = {destroyed: false};
|
||||
makeItem(p);
|
||||
where.list.push(p);
|
||||
}
|
||||
|
|
|
@ -230,7 +230,7 @@
|
|||
"unlocks.category.advanced": "## Advanced upgrades\n\nThose are typically not very useful by themselves, but will can become very powerful when combined with the right combo upgrade. ",
|
||||
"unlocks.category.beginner": "## Beginner friendly upgrades\n\nThose upgrades are very helpful for beginners, they help you play longer and miss the ball less.\n",
|
||||
"unlocks.category.combo": "## Combo upgrades\n\nThose upgrades help increase your combo progressively, but also add a combo reset condition. Taking one is a good idea, taking more increases the risk and reward.",
|
||||
"unlocks.category.combo_boost": "## Combo boosters\n\nThose upgrades increase the combo or combo multiplier without adding a reset condition. ",
|
||||
"unlocks.category.combo_boost": "## Combo booster upgrades\n\nThose upgrades increase the combo or combo multiplier without adding a reset condition. ",
|
||||
"unlocks.category.simple": "## Helper upgrades\n\nThose upgrades are useful in almost any build.\n",
|
||||
"unlocks.greyed_out_help": "The grayed out upgrades can be unlocked by increasing your total score. The total score increases every time you score in game.",
|
||||
"unlocks.intro": "Your total score is {{ts}}. Click an upgrade below to start a game with it.",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue