mirror of
https://gitlab.com/lecarore/breakout71.git
synced 2025-04-20 12:15:06 -04:00
Build 29040298
This commit is contained in:
parent
dce41a43ec
commit
d2266de792
13 changed files with 2155 additions and 2103 deletions
|
@ -11,8 +11,8 @@ android {
|
||||||
applicationId = "me.lecaro.breakout"
|
applicationId = "me.lecaro.breakout"
|
||||||
minSdk = 21
|
minSdk = 21
|
||||||
targetSdk = 34
|
targetSdk = 34
|
||||||
versionCode = 29040074
|
versionCode = 29040298
|
||||||
versionName = "29040074"
|
versionName = "29040298"
|
||||||
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
6
dist/index.html
vendored
6
dist/index.html
vendored
|
@ -1270,7 +1270,7 @@ const upgrades = (0, _upgrades.rawUpgrades).map((u)=>({
|
||||||
}));
|
}));
|
||||||
|
|
||||||
},{"./data/palette.json":"ktRBU","./data/levels.json":"8JSUc","./data/version.json":"iyP6E","./getLevelBackground":"7OIPf","./levelIcon":"6rQoT","@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3","./upgrades":"1u3Dx"}],"iyP6E":[function(require,module,exports,__globalThis) {
|
},{"./data/palette.json":"ktRBU","./data/levels.json":"8JSUc","./data/version.json":"iyP6E","./getLevelBackground":"7OIPf","./levelIcon":"6rQoT","@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3","./upgrades":"1u3Dx"}],"iyP6E":[function(require,module,exports,__globalThis) {
|
||||||
module.exports = JSON.parse("\"29040074\"");
|
module.exports = JSON.parse("\"29040298\"");
|
||||||
|
|
||||||
},{}],"6rQoT":[function(require,module,exports,__globalThis) {
|
},{}],"6rQoT":[function(require,module,exports,__globalThis) {
|
||||||
var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js");
|
var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js");
|
||||||
|
@ -2402,7 +2402,7 @@ function spawnExplosion(gameState, count, x, y, color) {
|
||||||
for(let i = 0; i < count; i++)makeParticle(gameState, x + (Math.random() - 0.5) * gameState.brickWidth / 2, y + (Math.random() - 0.5) * gameState.brickWidth / 2, (Math.random() - 0.5) * 30, (Math.random() - 0.5) * 30, color, false);
|
for(let i = 0; i < count; i++)makeParticle(gameState, x + (Math.random() - 0.5) * gameState.brickWidth / 2, y + (Math.random() - 0.5) * gameState.brickWidth / 2, (Math.random() - 0.5) * 30, (Math.random() - 0.5) * 30, color, false);
|
||||||
}
|
}
|
||||||
function explosionAt(gameState, index, x, y, ball) {
|
function explosionAt(gameState, index, x, y, ball) {
|
||||||
if (gameState.bricks[index] == 'black') delete gameState.bricks[index];
|
if (gameState.bricks[index] == "black") delete gameState.bricks[index];
|
||||||
schedulGameSound(gameState, "explode", ball.x, 1);
|
schedulGameSound(gameState, "explode", ball.x, 1);
|
||||||
const col = index % gameState.gridSize;
|
const col = index % gameState.gridSize;
|
||||||
const row = Math.floor(index / gameState.gridSize);
|
const row = Math.floor(index / gameState.gridSize);
|
||||||
|
@ -2710,7 +2710,7 @@ frames = 1) {
|
||||||
if (!gameState.perks.etherealcoins) {
|
if (!gameState.perks.etherealcoins) {
|
||||||
const flip = gameState.perks.helium > 0 && Math.abs(coin.x - gameState.puckPosition) * 2 > gameState.puckWidth + coin.size;
|
const flip = gameState.perks.helium > 0 && Math.abs(coin.x - gameState.puckPosition) * 2 > gameState.puckWidth + coin.size;
|
||||||
coin.vy += frames * coin.weight * 0.8 * (flip ? -1 : 1);
|
coin.vy += frames * coin.weight * 0.8 * (flip ? -1 : 1);
|
||||||
if (flip && !(0, _options.isOptionOn)('basic') && Math.random() < 0.1) makeParticle(gameState, coin.x, coin.y, 0, gameState.baseSpeed, rainbowColor(), true, 5, 250);
|
if (flip && !(0, _options.isOptionOn)("basic") && Math.random() < 0.1) makeParticle(gameState, coin.x, coin.y, 0, gameState.baseSpeed, rainbowColor(), true, 5, 250);
|
||||||
}
|
}
|
||||||
const speed = Math.abs(coin.sx) + Math.abs(coin.sx);
|
const speed = Math.abs(coin.sx) + Math.abs(coin.sx);
|
||||||
const hitBorder = bordersHitCheck(gameState, coin, coin.size / 2, frames);
|
const hitBorder = bordersHitCheck(gameState, coin, coin.size / 2, frames);
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
// The version of the cache.
|
// The version of the cache.
|
||||||
const VERSION = "29040074";
|
const VERSION = "29040298";
|
||||||
|
|
||||||
// The name of the cache
|
// The name of the cache
|
||||||
const CACHE_NAME = `breakout-71-${VERSION}`;
|
const CACHE_NAME = `breakout-71-${VERSION}`;
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
"29040074"
|
"29040298"
|
||||||
|
|
55
src/game.ts
55
src/game.ts
|
@ -1,4 +1,4 @@
|
||||||
import {allLevels, appVersion, icons, upgrades} from "./loadGameData";
|
import { allLevels, appVersion, icons, upgrades } from "./loadGameData";
|
||||||
import {
|
import {
|
||||||
Ball,
|
Ball,
|
||||||
Coin,
|
Coin,
|
||||||
|
@ -11,12 +11,17 @@ import {
|
||||||
TextFlash,
|
TextFlash,
|
||||||
Upgrade,
|
Upgrade,
|
||||||
} from "./types";
|
} from "./types";
|
||||||
import {getAudioContext, playPendingSounds} from "./sounds";
|
import { getAudioContext, playPendingSounds } from "./sounds";
|
||||||
import {currentLevelInfo, getRowColIndex, max_levels, pickedUpgradesHTMl,} from "./game_utils";
|
import {
|
||||||
|
currentLevelInfo,
|
||||||
|
getRowColIndex,
|
||||||
|
max_levels,
|
||||||
|
pickedUpgradesHTMl,
|
||||||
|
} from "./game_utils";
|
||||||
|
|
||||||
import "./PWA/sw_loader";
|
import "./PWA/sw_loader";
|
||||||
import {getCurrentLang, t} from "./i18n/i18n";
|
import { getCurrentLang, t } from "./i18n/i18n";
|
||||||
import {getSettingValue, getTotalScore, setSettingValue} from "./settings";
|
import { getSettingValue, getTotalScore, setSettingValue } from "./settings";
|
||||||
import {
|
import {
|
||||||
forEachLiveOne,
|
forEachLiveOne,
|
||||||
gameStateTick,
|
gameStateTick,
|
||||||
|
@ -25,12 +30,28 @@ import {
|
||||||
setLevel,
|
setLevel,
|
||||||
setMousePos,
|
setMousePos,
|
||||||
} from "./gameStateMutators";
|
} from "./gameStateMutators";
|
||||||
import {backgroundCanvas, ctx, gameCanvas, render, scoreDisplay,} from "./render";
|
import {
|
||||||
import {pauseRecording, recordOneFrame, resumeRecording, startRecordingGame,} from "./recording";
|
backgroundCanvas,
|
||||||
import {newGameState} from "./newGameState";
|
ctx,
|
||||||
import {alertsOpen, asyncAlert, AsyncAlertAction, closeModal,} from "./asyncAlert";
|
gameCanvas,
|
||||||
import {isOptionOn, options, toggleOption} from "./options";
|
render,
|
||||||
import {hashCode} from "./getLevelBackground";
|
scoreDisplay,
|
||||||
|
} from "./render";
|
||||||
|
import {
|
||||||
|
pauseRecording,
|
||||||
|
recordOneFrame,
|
||||||
|
resumeRecording,
|
||||||
|
startRecordingGame,
|
||||||
|
} from "./recording";
|
||||||
|
import { newGameState } from "./newGameState";
|
||||||
|
import {
|
||||||
|
alertsOpen,
|
||||||
|
asyncAlert,
|
||||||
|
AsyncAlertAction,
|
||||||
|
closeModal,
|
||||||
|
} from "./asyncAlert";
|
||||||
|
import { isOptionOn, options, toggleOption } from "./options";
|
||||||
|
import { hashCode } from "./getLevelBackground";
|
||||||
|
|
||||||
export function play() {
|
export function play() {
|
||||||
if (gameState.running) return;
|
if (gameState.running) return;
|
||||||
|
@ -406,8 +427,7 @@ async function openScorePanel() {
|
||||||
async function openSettingsPanel() {
|
async function openSettingsPanel() {
|
||||||
pause(true);
|
pause(true);
|
||||||
|
|
||||||
const actions: AsyncAlertAction<() => void>[] = [
|
const actions: AsyncAlertAction<() => void>[] = [];
|
||||||
];
|
|
||||||
|
|
||||||
for (const key of Object.keys(options) as OptionId[]) {
|
for (const key of Object.keys(options) as OptionId[]) {
|
||||||
if (options[key])
|
if (options[key])
|
||||||
|
@ -449,21 +469,18 @@ async function openSettingsPanel() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
actions.push(
|
actions.push({
|
||||||
{
|
|
||||||
text: t("main_menu.resume"),
|
text: t("main_menu.resume"),
|
||||||
help: t("main_menu.resume_help"),
|
help: t("main_menu.resume_help"),
|
||||||
value() {},
|
value() {},
|
||||||
})
|
});
|
||||||
actions.push({
|
actions.push({
|
||||||
text: t("main_menu.unlocks"),
|
text: t("main_menu.unlocks"),
|
||||||
help: t("main_menu.unlocks_help"),
|
help: t("main_menu.unlocks_help"),
|
||||||
value() {
|
value() {
|
||||||
openUnlocksList();
|
openUnlocksList();
|
||||||
},
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
actions.push({
|
actions.push({
|
||||||
text: t("sandbox.title"),
|
text: t("sandbox.title"),
|
||||||
|
|
|
@ -23,18 +23,27 @@ import {
|
||||||
getRowColIndex,
|
getRowColIndex,
|
||||||
isTelekinesisActive,
|
isTelekinesisActive,
|
||||||
isYoyoActive,
|
isYoyoActive,
|
||||||
max_levels, sample,
|
max_levels,
|
||||||
|
sample,
|
||||||
shouldPierceByColor,
|
shouldPierceByColor,
|
||||||
} from "./game_utils";
|
} from "./game_utils";
|
||||||
import {t} from "./i18n/i18n";
|
import { t } from "./i18n/i18n";
|
||||||
import {icons} from "./loadGameData";
|
import { icons } from "./loadGameData";
|
||||||
|
|
||||||
import {addToTotalScore} from "./settings";
|
import { addToTotalScore } from "./settings";
|
||||||
import {background} from "./render";
|
import { background } from "./render";
|
||||||
import {gameOver} from "./gameOver";
|
import { gameOver } from "./gameOver";
|
||||||
import {brickIndex, fitSize, gameState, hasBrick, hitsSomething, openUpgradesPicker, pause,} from "./game";
|
import {
|
||||||
import {stopRecording} from "./recording";
|
brickIndex,
|
||||||
import {isOptionOn} from "./options";
|
fitSize,
|
||||||
|
gameState,
|
||||||
|
hasBrick,
|
||||||
|
hitsSomething,
|
||||||
|
openUpgradesPicker,
|
||||||
|
pause,
|
||||||
|
} from "./game";
|
||||||
|
import { stopRecording } from "./recording";
|
||||||
|
import { isOptionOn } from "./options";
|
||||||
|
|
||||||
export function setMousePos(gameState: GameState, x: number) {
|
export function setMousePos(gameState: GameState, x: number) {
|
||||||
// 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
|
||||||
|
@ -227,12 +236,11 @@ export function spawnExplosion(
|
||||||
export function explosionAt(
|
export function explosionAt(
|
||||||
gameState: GameState,
|
gameState: GameState,
|
||||||
index: number,
|
index: number,
|
||||||
x: number, y: number,
|
x: number,
|
||||||
ball: Ball) {
|
y: number,
|
||||||
|
ball: Ball,
|
||||||
if(gameState.bricks[index]=='black')
|
) {
|
||||||
delete gameState.bricks[index];
|
if (gameState.bricks[index] == "black") delete gameState.bricks[index];
|
||||||
|
|
||||||
|
|
||||||
schedulGameSound(gameState, "explode", ball.x, 1);
|
schedulGameSound(gameState, "explode", ball.x, 1);
|
||||||
|
|
||||||
|
@ -276,8 +284,8 @@ export function explosionAt(
|
||||||
);
|
);
|
||||||
gameState.runStatistics.bricks_broken++;
|
gameState.runStatistics.bricks_broken++;
|
||||||
|
|
||||||
if(gameState.perks.zen){
|
if (gameState.perks.zen) {
|
||||||
resetCombo(gameState, x,y)
|
resetCombo(gameState, x, y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -293,10 +301,7 @@ export function explodeBrick(
|
||||||
if (color === "black") {
|
if (color === "black") {
|
||||||
const x = brickCenterX(gameState, index),
|
const x = brickCenterX(gameState, index),
|
||||||
y = brickCenterY(gameState, index);
|
y = brickCenterY(gameState, index);
|
||||||
explosionAt(gameState,
|
explosionAt(gameState, index, x, y, ball);
|
||||||
index, x, y,
|
|
||||||
ball)
|
|
||||||
|
|
||||||
} else if (color) {
|
} else if (color) {
|
||||||
// Even if it bounces we don't want to count that as a miss
|
// Even if it bounces we don't want to count that as a miss
|
||||||
|
|
||||||
|
@ -328,7 +333,7 @@ export function explodeBrick(
|
||||||
while (coinsToSpawn > 0) {
|
while (coinsToSpawn > 0) {
|
||||||
const points = Math.min(pointsPerCoin, coinsToSpawn);
|
const points = Math.min(pointsPerCoin, coinsToSpawn);
|
||||||
if (points < 0 || isNaN(points)) {
|
if (points < 0 || isNaN(points)) {
|
||||||
console.error({points});
|
console.error({ points });
|
||||||
debugger;
|
debugger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -351,7 +356,8 @@ export function explodeBrick(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
gameState.combo += gameState.perks.streak_shots +
|
gameState.combo +=
|
||||||
|
gameState.perks.streak_shots +
|
||||||
gameState.perks.compound_interest +
|
gameState.perks.compound_interest +
|
||||||
gameState.perks.left_is_lava +
|
gameState.perks.left_is_lava +
|
||||||
gameState.perks.right_is_lava +
|
gameState.perks.right_is_lava +
|
||||||
|
@ -359,9 +365,7 @@ export function explodeBrick(
|
||||||
gameState.perks.picky_eater +
|
gameState.perks.picky_eater +
|
||||||
gameState.perks.asceticism +
|
gameState.perks.asceticism +
|
||||||
gameState.perks.zen +
|
gameState.perks.zen +
|
||||||
gameState.perks.unbounded
|
gameState.perks.unbounded;
|
||||||
|
|
||||||
;
|
|
||||||
|
|
||||||
if (!isExplosion) {
|
if (!isExplosion) {
|
||||||
// color change
|
// color change
|
||||||
|
@ -475,7 +479,7 @@ export function addToScore(gameState: GameState, coin: Coin) {
|
||||||
}
|
}
|
||||||
gameState.runStatistics.score += coin.points;
|
gameState.runStatistics.score += coin.points;
|
||||||
if (gameState.perks.asceticism) {
|
if (gameState.perks.asceticism) {
|
||||||
resetCombo(gameState, coin.x, coin.y)
|
resetCombo(gameState, coin.x, coin.y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -498,7 +502,7 @@ export async function setLevel(gameState: GameState, l: number) {
|
||||||
|
|
||||||
// Reset combo silently
|
// Reset combo silently
|
||||||
if (!gameState.perks.shunt) {
|
if (!gameState.perks.shunt) {
|
||||||
gameState.combo = baseCombo(gameState)
|
gameState.combo = baseCombo(gameState);
|
||||||
}
|
}
|
||||||
gameState.combo += gameState.perks.hot_start * 15;
|
gameState.combo += gameState.perks.hot_start * 15;
|
||||||
|
|
||||||
|
@ -630,10 +634,10 @@ export function attract(gameState: GameState, a: Ball, b: Ball, power: number) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function coinBrickHitCheck(gameState: GameState, coin: Coin) {
|
export function coinBrickHitCheck(gameState: GameState, coin: Coin) {
|
||||||
if(gameState.perks.ghost_coins)return undefined
|
if (gameState.perks.ghost_coins) return undefined;
|
||||||
// Make ball/coin bonce, and return bricks that were hit
|
// Make ball/coin bonce, and return bricks that were hit
|
||||||
const radius = coin.size / 2;
|
const radius = coin.size / 2;
|
||||||
const {x, y, previousX, previousY} = coin;
|
const { x, y, previousX, previousY } = coin;
|
||||||
|
|
||||||
const vhit = hitsSomething(previousX, y, radius);
|
const vhit = hitsSomething(previousX, y, radius);
|
||||||
const hhit = hitsSomething(x, previousY, radius);
|
const hhit = hitsSomething(x, previousY, radius);
|
||||||
|
@ -697,7 +701,10 @@ export function bordersHitCheck(
|
||||||
let vhit = 0,
|
let vhit = 0,
|
||||||
hhit = 0;
|
hhit = 0;
|
||||||
|
|
||||||
if (coin.x < gameState.offsetXRoundedDown + radius && !gameState.perks.unbounded) {
|
if (
|
||||||
|
coin.x < gameState.offsetXRoundedDown + radius &&
|
||||||
|
!gameState.perks.unbounded
|
||||||
|
) {
|
||||||
coin.x =
|
coin.x =
|
||||||
gameState.offsetXRoundedDown +
|
gameState.offsetXRoundedDown +
|
||||||
radius +
|
radius +
|
||||||
|
@ -710,7 +717,10 @@ export function bordersHitCheck(
|
||||||
coin.vy *= -1;
|
coin.vy *= -1;
|
||||||
vhit = 1;
|
vhit = 1;
|
||||||
}
|
}
|
||||||
if (coin.x > gameState.canvasWidth - gameState.offsetXRoundedDown - radius && !gameState.perks.unbounded) {
|
if (
|
||||||
|
coin.x > gameState.canvasWidth - gameState.offsetXRoundedDown - radius &&
|
||||||
|
!gameState.perks.unbounded
|
||||||
|
) {
|
||||||
coin.x =
|
coin.x =
|
||||||
gameState.canvasWidth -
|
gameState.canvasWidth -
|
||||||
gameState.offsetXRoundedDown -
|
gameState.offsetXRoundedDown -
|
||||||
|
@ -765,15 +775,19 @@ export function gameStateTick(
|
||||||
gameState.autoCleanUses++;
|
gameState.autoCleanUses++;
|
||||||
}
|
}
|
||||||
if (!remainingBricks && gameState.noBricksSince == 0) {
|
if (!remainingBricks && gameState.noBricksSince == 0) {
|
||||||
gameState.noBricksSince ||= gameState.levelTime
|
gameState.noBricksSince ||= gameState.levelTime;
|
||||||
}
|
}
|
||||||
if (!remainingBricks && (!liveCount(gameState.coins) || (gameState.levelTime > gameState.noBricksSince + 5000))) {
|
if (
|
||||||
|
!remainingBricks &&
|
||||||
|
(!liveCount(gameState.coins) ||
|
||||||
|
gameState.levelTime > gameState.noBricksSince + 5000)
|
||||||
|
) {
|
||||||
if (gameState.currentLevel + 1 < max_levels(gameState)) {
|
if (gameState.currentLevel + 1 < max_levels(gameState)) {
|
||||||
setLevel(gameState, gameState.currentLevel + 1);
|
setLevel(gameState, gameState.currentLevel + 1);
|
||||||
} else {
|
} else {
|
||||||
gameOver(
|
gameOver(
|
||||||
t("gameOver.win.title"),
|
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 if (gameState.running || gameState.levelTime) {
|
||||||
|
@ -781,25 +795,30 @@ export function gameStateTick(
|
||||||
|
|
||||||
forEachLiveOne(gameState.coins, (coin, coinIndex) => {
|
forEachLiveOne(gameState.coins, (coin, coinIndex) => {
|
||||||
if (gameState.perks.coin_magnet) {
|
if (gameState.perks.coin_magnet) {
|
||||||
const strength = 100 / (100 +
|
const strength =
|
||||||
|
(100 /
|
||||||
|
(100 +
|
||||||
Math.pow(coin.y - gameState.gameZoneHeight, 2) +
|
Math.pow(coin.y - gameState.gameZoneHeight, 2) +
|
||||||
Math.pow(coin.x - gameState.puckPosition, 2)) *
|
Math.pow(coin.x - gameState.puckPosition, 2))) *
|
||||||
gameState.perks.coin_magnet;
|
gameState.perks.coin_magnet;
|
||||||
|
|
||||||
const attractionX = frames * (gameState.puckPosition - coin.x) * strength
|
const attractionX =
|
||||||
|
frames * (gameState.puckPosition - coin.x) * strength;
|
||||||
|
|
||||||
coin.vx += attractionX;
|
coin.vx += attractionX;
|
||||||
coin.vy += frames * (gameState.gameZoneHeight - coin.y) * strength / 2
|
coin.vy +=
|
||||||
|
(frames * (gameState.gameZoneHeight - coin.y) * strength) / 2;
|
||||||
coin.sa -= attractionX / 10;
|
coin.sa -= attractionX / 10;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(gameState.perks.ball_attracts_coins){
|
if (gameState.perks.ball_attracts_coins) {
|
||||||
gameState.balls.forEach(ball=>{
|
gameState.balls.forEach((ball) => {
|
||||||
const d2= distance2(ball, coin)
|
const d2 = distance2(ball, coin);
|
||||||
coin.vx+=(ball.x-coin.x)/d2*30*gameState.perks.ball_attracts_coins
|
coin.vx +=
|
||||||
coin.vy+=(ball.y-coin.y)/d2*30*gameState.perks.ball_attracts_coins
|
((ball.x - coin.x) / d2) * 30 * gameState.perks.ball_attracts_coins;
|
||||||
})
|
coin.vy +=
|
||||||
|
((ball.y - coin.y) / d2) * 30 * gameState.perks.ball_attracts_coins;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const ratio = 1 - (gameState.perks.viscosity * 0.03 + 0.005) * frames;
|
const ratio = 1 - (gameState.perks.viscosity * 0.03 + 0.005) * frames;
|
||||||
|
@ -816,10 +835,14 @@ export function gameStateTick(
|
||||||
|
|
||||||
// Gravity
|
// Gravity
|
||||||
if (!gameState.perks.etherealcoins) {
|
if (!gameState.perks.etherealcoins) {
|
||||||
const flip = gameState.perks.helium > 0 && Math.abs(coin.x - gameState.puckPosition) * 2 > gameState.puckWidth + coin.size
|
const flip =
|
||||||
|
gameState.perks.helium > 0 &&
|
||||||
|
Math.abs(coin.x - gameState.puckPosition) * 2 >
|
||||||
|
gameState.puckWidth + coin.size;
|
||||||
coin.vy += frames * coin.weight * 0.8 * (flip ? -1 : 1);
|
coin.vy += frames * coin.weight * 0.8 * (flip ? -1 : 1);
|
||||||
if (flip && !isOptionOn('basic') && Math.random() < 0.1) {
|
if (flip && !isOptionOn("basic") && Math.random() < 0.1) {
|
||||||
makeParticle(gameState,
|
makeParticle(
|
||||||
|
gameState,
|
||||||
coin.x,
|
coin.x,
|
||||||
coin.y,
|
coin.y,
|
||||||
0,
|
0,
|
||||||
|
@ -827,10 +850,9 @@ export function gameStateTick(
|
||||||
rainbowColor(),
|
rainbowColor(),
|
||||||
true,
|
true,
|
||||||
5,
|
5,
|
||||||
250
|
250,
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const speed = Math.abs(coin.sx) + Math.abs(coin.sx);
|
const speed = Math.abs(coin.sx) + Math.abs(coin.sx);
|
||||||
|
@ -851,12 +873,14 @@ export function gameStateTick(
|
||||||
if (gameState.perks.compound_interest) {
|
if (gameState.perks.compound_interest) {
|
||||||
resetCombo(gameState, coin.x, coin.y);
|
resetCombo(gameState, coin.x, coin.y);
|
||||||
}
|
}
|
||||||
} else if (gameState.perks.unbounded && (coin.x < -50 || coin.x > gameState.canvasWidth + 50)) {
|
} else if (
|
||||||
|
gameState.perks.unbounded &&
|
||||||
|
(coin.x < -50 || coin.x > gameState.canvasWidth + 50)
|
||||||
|
) {
|
||||||
// Out of bound on sides
|
// Out of bound on sides
|
||||||
destroy(gameState.coins, coinIndex);
|
destroy(gameState.coins, coinIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const hitBrick = coinBrickHitCheck(gameState, coin);
|
const hitBrick = coinBrickHitCheck(gameState, coin);
|
||||||
|
|
||||||
if (gameState.perks.metamorphosis && typeof hitBrick !== "undefined") {
|
if (gameState.perks.metamorphosis && typeof hitBrick !== "undefined") {
|
||||||
|
@ -891,29 +915,41 @@ export function gameStateTick(
|
||||||
if (gameState.perks.shocks) {
|
if (gameState.perks.shocks) {
|
||||||
gameState.balls.forEach((a, ai) =>
|
gameState.balls.forEach((a, ai) =>
|
||||||
gameState.balls.forEach((b, bi) => {
|
gameState.balls.forEach((b, bi) => {
|
||||||
if (ai < bi && !a.destroyed && !b.destroyed && distance2(a, b) < gameState.ballSize * gameState.ballSize) {
|
if (
|
||||||
let tempVx = a.vx
|
ai < bi &&
|
||||||
let tempVy = a.vy
|
!a.destroyed &&
|
||||||
a.vx = b.vx
|
!b.destroyed &&
|
||||||
a.vy = b.vy
|
distance2(a, b) < gameState.ballSize * gameState.ballSize
|
||||||
b.vx = tempVx
|
) {
|
||||||
b.vy = tempVy
|
let tempVx = a.vx;
|
||||||
|
let tempVy = a.vy;
|
||||||
|
a.vx = b.vx;
|
||||||
|
a.vy = b.vy;
|
||||||
|
b.vx = tempVx;
|
||||||
|
b.vy = tempVy;
|
||||||
|
|
||||||
let x = (a.x + b.x) / 2
|
let x = (a.x + b.x) / 2;
|
||||||
let y = (a.y + b.y) / 2
|
let y = (a.y + b.y) / 2;
|
||||||
const limit = gameState.baseSpeed
|
const limit = gameState.baseSpeed;
|
||||||
a.vx += clamp(a.x - x, -limit, limit)+(Math.random()-0.5) * limit/3
|
a.vx +=
|
||||||
a.vy += clamp(a.y - y, -limit, limit)+(Math.random()-0.5) * limit/3
|
clamp(a.x - x, -limit, limit) +
|
||||||
b.vx += clamp(b.x - x, -limit, limit)+(Math.random()-0.5) * limit/3
|
((Math.random() - 0.5) * limit) / 3;
|
||||||
b.vy += clamp(b.y - y, -limit, limit)+(Math.random()-0.5) * limit/3
|
a.vy +=
|
||||||
|
clamp(a.y - y, -limit, limit) +
|
||||||
|
((Math.random() - 0.5) * limit) / 3;
|
||||||
|
b.vx +=
|
||||||
|
clamp(b.x - x, -limit, limit) +
|
||||||
|
((Math.random() - 0.5) * limit) / 3;
|
||||||
|
b.vy +=
|
||||||
|
clamp(b.y - y, -limit, limit) +
|
||||||
|
((Math.random() - 0.5) * limit) / 3;
|
||||||
|
|
||||||
let index = brickIndex(x, y)
|
let index = brickIndex(x, y);
|
||||||
|
|
||||||
explosionAt(gameState, index, x, y, a)
|
explosionAt(gameState, index, x, y, a);
|
||||||
}
|
}
|
||||||
}
|
}),
|
||||||
)
|
);
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (gameState.perks.wind) {
|
if (gameState.perks.wind) {
|
||||||
|
@ -1081,9 +1117,7 @@ export function ballTick(gameState: GameState, ball: Ball, delta: number) {
|
||||||
if (isYoyoActive(gameState, ball)) {
|
if (isYoyoActive(gameState, ball)) {
|
||||||
speedLimitDampener += 3;
|
speedLimitDampener += 3;
|
||||||
ball.vx +=
|
ball.vx +=
|
||||||
((gameState.puckPosition - ball.x) / 1000) *
|
((gameState.puckPosition - ball.x) / 1000) * delta * gameState.perks.yoyo;
|
||||||
delta *
|
|
||||||
gameState.perks.yoyo;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
|
@ -1143,7 +1177,7 @@ export function ballTick(gameState: GameState, ball: Ball, delta: number) {
|
||||||
i < ball.hitItem?.length - 1 && i < gameState.perks.respawn;
|
i < ball.hitItem?.length - 1 && i < gameState.perks.respawn;
|
||||||
i++
|
i++
|
||||||
) {
|
) {
|
||||||
const {index, color} = ball.hitItem[i];
|
const { index, color } = ball.hitItem[i];
|
||||||
if (gameState.bricks[index] || color === "black") continue;
|
if (gameState.bricks[index] || color === "black") continue;
|
||||||
const vertical = Math.random() > 0.5;
|
const vertical = Math.random() > 0.5;
|
||||||
const dx = Math.random() > 0.5 ? 1 : -1;
|
const dx = Math.random() > 0.5 ? 1 : -1;
|
||||||
|
@ -1163,7 +1197,12 @@ export function ballTick(gameState: GameState, ball: Ball, delta: number) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const borderHitCode = bordersHitCheck(gameState, ball, gameState.ballSize / 2, delta);
|
const borderHitCode = bordersHitCheck(
|
||||||
|
gameState,
|
||||||
|
ball,
|
||||||
|
gameState.ballSize / 2,
|
||||||
|
delta,
|
||||||
|
);
|
||||||
if (borderHitCode) {
|
if (borderHitCode) {
|
||||||
if (
|
if (
|
||||||
gameState.perks.left_is_lava &&
|
gameState.perks.left_is_lava &&
|
||||||
|
@ -1216,46 +1255,43 @@ export function ballTick(gameState: GameState, ball: Ball, delta: number) {
|
||||||
} else {
|
} else {
|
||||||
ball.vy *= -1;
|
ball.vy *= -1;
|
||||||
|
|
||||||
gameState.perks.extra_life -= 1
|
gameState.perks.extra_life -= 1;
|
||||||
if(gameState.perks.extra_life <0){
|
if (gameState.perks.extra_life < 0) {
|
||||||
gameState.perks.extra_life=0
|
gameState.perks.extra_life = 0;
|
||||||
}else if(gameState.perks.sacrifice){
|
} else if (gameState.perks.sacrifice) {
|
||||||
if(liveCount(gameState.coins)<gameState.MAX_COINS/2){
|
if (liveCount(gameState.coins) < gameState.MAX_COINS / 2) {
|
||||||
// true duplication
|
// true duplication
|
||||||
let remaining = liveCount(gameState.coins)
|
let remaining = liveCount(gameState.coins);
|
||||||
|
|
||||||
forEachLiveOne(gameState.coins, (source, index)=>{
|
forEachLiveOne(gameState.coins, (source, index) => {
|
||||||
if(!remaining) return
|
if (!remaining) return;
|
||||||
append(gameState.coins, copy=>{
|
append(gameState.coins, (copy) => {
|
||||||
copy.points=source.points
|
copy.points = source.points;
|
||||||
copy.color=source.color
|
copy.color = source.color;
|
||||||
copy.x=source.x
|
copy.x = source.x;
|
||||||
copy.y=source.y
|
copy.y = source.y;
|
||||||
copy.size=source.size
|
copy.size = source.size;
|
||||||
copy.previousX=source.previousX
|
copy.previousX = source.previousX;
|
||||||
copy.previousY=source.previousY
|
copy.previousY = source.previousY;
|
||||||
copy.vx=-source.vx
|
copy.vx = -source.vx;
|
||||||
copy.vy=-source.vy
|
copy.vy = -source.vy;
|
||||||
copy.sx=source.sx
|
copy.sx = source.sx;
|
||||||
copy.sy=source.sy
|
copy.sy = source.sy;
|
||||||
copy.a=source.a
|
copy.a = source.a;
|
||||||
copy.sa=-source.sa
|
copy.sa = -source.sa;
|
||||||
copy.weight=source.weight
|
copy.weight = source.weight;
|
||||||
copy.coloredABrick=source.coloredABrick
|
copy.coloredABrick = source.coloredABrick;
|
||||||
})
|
});
|
||||||
remaining--
|
remaining--;
|
||||||
})
|
});
|
||||||
}else{
|
} else {
|
||||||
forEachLiveOne(gameState.coins, (source, index)=>{
|
forEachLiveOne(gameState.coins, (source, index) => {
|
||||||
source.points*=2
|
source.points *= 2;
|
||||||
})
|
});
|
||||||
// spawn a few coins for effect, but mostly increment poitns counter
|
// spawn a few coins for effect, but mostly increment poitns counter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
schedulGameSound(gameState, "lifeLost", ball.x, 1);
|
schedulGameSound(gameState, "lifeLost", ball.x, 1);
|
||||||
if (!isOptionOn("basic")) {
|
if (!isOptionOn("basic")) {
|
||||||
for (let i = 0; i < 10; i++)
|
for (let i = 0; i < 10; i++)
|
||||||
|
@ -1276,16 +1312,15 @@ export function ballTick(gameState: GameState, ball: Ball, delta: number) {
|
||||||
resetCombo(gameState, ball.x, ball.y);
|
resetCombo(gameState, ball.x, ball.y);
|
||||||
}
|
}
|
||||||
if (gameState.perks.trampoline) {
|
if (gameState.perks.trampoline) {
|
||||||
gameState.combo+=gameState.perks.trampoline
|
gameState.combo += gameState.perks.trampoline;
|
||||||
}
|
}
|
||||||
if (gameState.perks.nbricks) {
|
if (gameState.perks.nbricks) {
|
||||||
if (ball.hitSinceBounce) {
|
if (ball.hitSinceBounce) {
|
||||||
if (gameState.perks.nbricks === ball.hitSinceBounce) {
|
if (gameState.perks.nbricks === ball.hitSinceBounce) {
|
||||||
gameState.combo += gameState.perks.nbricks
|
gameState.combo += gameState.perks.nbricks;
|
||||||
} else {
|
} else {
|
||||||
resetCombo(gameState, ball.x, ball.y)
|
resetCombo(gameState, ball.x, ball.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1293,7 +1328,7 @@ export function ballTick(gameState: GameState, ball: Ball, delta: number) {
|
||||||
ball.hitItem
|
ball.hitItem
|
||||||
.slice(0, -1)
|
.slice(0, -1)
|
||||||
.slice(0, gameState.perks.respawn)
|
.slice(0, gameState.perks.respawn)
|
||||||
.forEach(({index, color}) => {
|
.forEach(({ index, color }) => {
|
||||||
if (!gameState.bricks[index] && color !== "black")
|
if (!gameState.bricks[index] && color !== "black")
|
||||||
gameState.bricks[index] = color;
|
gameState.bricks[index] = color;
|
||||||
});
|
});
|
||||||
|
@ -1302,12 +1337,13 @@ export function ballTick(gameState: GameState, ball: Ball, delta: number) {
|
||||||
if (!ball.hitSinceBounce) {
|
if (!ball.hitSinceBounce) {
|
||||||
gameState.runStatistics.misses++;
|
gameState.runStatistics.misses++;
|
||||||
gameState.levelMisses++;
|
gameState.levelMisses++;
|
||||||
if(gameState.perks.forgiving){
|
if (gameState.perks.forgiving) {
|
||||||
const indexes = gameState.bricks.map((b,i)=>b ? i:-1)
|
const indexes = gameState.bricks
|
||||||
.filter(i=>i>-1)
|
.map((b, i) => (b ? i : -1))
|
||||||
const pick = sample(indexes)
|
.filter((i) => i > -1);
|
||||||
explodeBrick(gameState,pick, ball, false)
|
const pick = sample(indexes);
|
||||||
}else{
|
explodeBrick(gameState, pick, ball, false);
|
||||||
|
} else {
|
||||||
resetCombo(gameState, ball.x, ball.y);
|
resetCombo(gameState, ball.x, ball.y);
|
||||||
}
|
}
|
||||||
makeText(
|
makeText(
|
||||||
|
@ -1326,23 +1362,25 @@ export function ballTick(gameState: GameState, ball: Ball, delta: number) {
|
||||||
ball.piercedSinceBounce = 0;
|
ball.piercedSinceBounce = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const lostOnSides = gameState.perks.unbounded && ball.x < 50 || ball.x > gameState.canvasWidth + 50
|
const lostOnSides =
|
||||||
if (gameState.running &&
|
(gameState.perks.unbounded && ball.x < 50) ||
|
||||||
|
ball.x > gameState.canvasWidth + 50;
|
||||||
|
if (
|
||||||
|
gameState.running &&
|
||||||
(ball.y > gameState.gameZoneHeight + gameState.ballSize / 2 || lostOnSides)
|
(ball.y > gameState.gameZoneHeight + gameState.ballSize / 2 || lostOnSides)
|
||||||
|
|
||||||
) {
|
) {
|
||||||
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(
|
gameOver(
|
||||||
t("gameOver.lost.title"),
|
t("gameOver.lost.title"),
|
||||||
t("gameOver.lost.summary", {score: gameState.score}),
|
t("gameOver.lost.summary", { score: gameState.score }),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const radius = gameState.ballSize / 2;
|
const radius = gameState.ballSize / 2;
|
||||||
// Make ball/coin bonce, and return bricks that were hit
|
// 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 vhit = hitsSomething(previousX, y, radius);
|
||||||
const hhit = hitsSomething(x, previousY, radius);
|
const hhit = hitsSomething(x, previousY, radius);
|
||||||
|
@ -1542,7 +1580,7 @@ export function append<T>(
|
||||||
makeItem(where.list[where.indexMin]);
|
makeItem(where.list[where.indexMin]);
|
||||||
where.indexMin++;
|
where.indexMin++;
|
||||||
} else {
|
} else {
|
||||||
const p = {destroyed: false};
|
const p = { destroyed: false };
|
||||||
makeItem(p);
|
makeItem(p);
|
||||||
where.list.push(p);
|
where.list.push(p);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import {Ball, GameState, PerkId, PerksMap} from "./types";
|
import { Ball, GameState, PerkId, PerksMap } from "./types";
|
||||||
import {icons, upgrades} from "./loadGameData";
|
import { icons, upgrades } from "./loadGameData";
|
||||||
|
|
||||||
export function getMajorityValue(arr: string[]): string {
|
export function getMajorityValue(arr: string[]): string {
|
||||||
const count: { [k: string]: number } = {};
|
const count: { [k: string]: number } = {};
|
||||||
|
@ -103,9 +103,8 @@ export function distanceBetween(
|
||||||
return Math.sqrt(distance2(a, b));
|
return Math.sqrt(distance2(a, b));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function clamp(value: number, min: number, max: number) {
|
||||||
export function clamp(value, min, max){
|
return Math.max(min, Math.min(value, max));
|
||||||
return Math.max(min, Math.min(value, max))
|
|
||||||
}
|
}
|
||||||
export function defaultSounds() {
|
export function defaultSounds() {
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import {baseCombo, forEachLiveOne, liveCount} from "./gameStateMutators";
|
import { baseCombo, forEachLiveOne, liveCount } from "./gameStateMutators";
|
||||||
import {
|
import {
|
||||||
brickCenterX,
|
brickCenterX,
|
||||||
brickCenterY,
|
brickCenterY,
|
||||||
|
@ -7,10 +7,10 @@ import {
|
||||||
isYoyoActive,
|
isYoyoActive,
|
||||||
max_levels,
|
max_levels,
|
||||||
} from "./game_utils";
|
} from "./game_utils";
|
||||||
import {colorString, GameState} from "./types";
|
import { colorString, GameState } from "./types";
|
||||||
import {t} from "./i18n/i18n";
|
import { t } from "./i18n/i18n";
|
||||||
import {gameState} from "./game";
|
import { gameState } from "./game";
|
||||||
import {isOptionOn} from "./options";
|
import { isOptionOn } from "./options";
|
||||||
|
|
||||||
export const gameCanvas = document.getElementById("game") as HTMLCanvasElement;
|
export const gameCanvas = document.getElementById("game") as HTMLCanvasElement;
|
||||||
export const ctx = gameCanvas.getContext("2d", {
|
export const ctx = gameCanvas.getContext("2d", {
|
||||||
|
@ -29,7 +29,7 @@ export const backgroundCanvas = document.createElement("canvas");
|
||||||
|
|
||||||
export function render(gameState: GameState) {
|
export function render(gameState: GameState) {
|
||||||
const level = currentLevelInfo(gameState);
|
const level = currentLevelInfo(gameState);
|
||||||
const {width, height} = gameCanvas;
|
const { width, height } = gameCanvas;
|
||||||
if (!width || !height) return;
|
if (!width || !height) return;
|
||||||
|
|
||||||
if (gameState.currentLevel || gameState.levelTime) {
|
if (gameState.currentLevel || gameState.levelTime) {
|
||||||
|
@ -82,13 +82,13 @@ export function render(gameState: GameState) {
|
||||||
});
|
});
|
||||||
ctx.globalAlpha = 1;
|
ctx.globalAlpha = 1;
|
||||||
forEachLiveOne(gameState.lights, (flash) => {
|
forEachLiveOne(gameState.lights, (flash) => {
|
||||||
const {x, y, time, color, size, duration} = flash;
|
const { x, y, time, color, size, duration } = flash;
|
||||||
const elapsed = gameState.levelTime - time;
|
const elapsed = gameState.levelTime - time;
|
||||||
ctx.globalAlpha = Math.min(1, 2 - (elapsed / duration) * 2);
|
ctx.globalAlpha = Math.min(1, 2 - (elapsed / duration) * 2);
|
||||||
drawFuzzyBall(ctx, color, size, x, y);
|
drawFuzzyBall(ctx, color, size, x, y);
|
||||||
});
|
});
|
||||||
forEachLiveOne(gameState.particles, (flash) => {
|
forEachLiveOne(gameState.particles, (flash) => {
|
||||||
const {x, y, time, color, size, duration} = flash;
|
const { x, y, time, color, size, duration } = flash;
|
||||||
const elapsed = gameState.levelTime - time;
|
const elapsed = gameState.levelTime - time;
|
||||||
ctx.globalAlpha = Math.min(1, 2 - (elapsed / duration) * 2);
|
ctx.globalAlpha = Math.min(1, 2 - (elapsed / duration) * 2);
|
||||||
drawFuzzyBall(ctx, color, size * 3, x, y);
|
drawFuzzyBall(ctx, color, size * 3, x, y);
|
||||||
|
@ -131,7 +131,7 @@ export function render(gameState: GameState) {
|
||||||
ctx.fillStyle = level.color || "#000";
|
ctx.fillStyle = level.color || "#000";
|
||||||
ctx.fillRect(0, 0, width, height);
|
ctx.fillRect(0, 0, width, height);
|
||||||
forEachLiveOne(gameState.particles, (flash) => {
|
forEachLiveOne(gameState.particles, (flash) => {
|
||||||
const {x, y, time, color, size, duration} = flash;
|
const { x, y, time, color, size, duration } = flash;
|
||||||
const elapsed = gameState.levelTime - time;
|
const elapsed = gameState.levelTime - time;
|
||||||
ctx.globalAlpha = Math.min(1, 2 - (elapsed / duration) * 2);
|
ctx.globalAlpha = Math.min(1, 2 - (elapsed / duration) * 2);
|
||||||
drawBall(ctx, color, size, x, y);
|
drawBall(ctx, color, size, x, y);
|
||||||
|
@ -194,7 +194,7 @@ export function render(gameState: GameState) {
|
||||||
|
|
||||||
ctx.globalCompositeOperation = "screen";
|
ctx.globalCompositeOperation = "screen";
|
||||||
forEachLiveOne(gameState.texts, (flash) => {
|
forEachLiveOne(gameState.texts, (flash) => {
|
||||||
const {x, y, time, color, size, duration} = flash;
|
const { x, y, time, color, size, duration } = flash;
|
||||||
const elapsed = gameState.levelTime - time;
|
const elapsed = gameState.levelTime - time;
|
||||||
ctx.globalAlpha = Math.max(0, Math.min(1, 2 - (elapsed / duration) * 2));
|
ctx.globalAlpha = Math.max(0, Math.min(1, 2 - (elapsed / duration) * 2));
|
||||||
ctx.globalCompositeOperation = "source-over";
|
ctx.globalCompositeOperation = "source-over";
|
||||||
|
@ -202,7 +202,7 @@ export function render(gameState: GameState) {
|
||||||
});
|
});
|
||||||
|
|
||||||
forEachLiveOne(gameState.particles, (particle) => {
|
forEachLiveOne(gameState.particles, (particle) => {
|
||||||
const {x, y, time, color, size, duration} = particle;
|
const { x, y, time, color, size, duration } = particle;
|
||||||
const elapsed = gameState.levelTime - time;
|
const elapsed = gameState.levelTime - time;
|
||||||
ctx.globalAlpha = Math.max(0, Math.min(1, 2 - (elapsed / duration) * 2));
|
ctx.globalAlpha = Math.max(0, Math.min(1, 2 - (elapsed / duration) * 2));
|
||||||
ctx.globalCompositeOperation = "screen";
|
ctx.globalCompositeOperation = "screen";
|
||||||
|
@ -218,7 +218,9 @@ export function render(gameState: GameState) {
|
||||||
ctx.fillRect(
|
ctx.fillRect(
|
||||||
gameState.perks.unbounded ? 0 : gameState.offsetXRoundedDown,
|
gameState.perks.unbounded ? 0 : gameState.offsetXRoundedDown,
|
||||||
gameState.gameZoneHeight - gameState.puckHeight / 2 + 2 * i,
|
gameState.gameZoneHeight - gameState.puckHeight / 2 + 2 * i,
|
||||||
gameState.perks.unbounded ? gameState.canvasWidth : gameState.gameZoneWidthRoundedUp,
|
gameState.perks.unbounded
|
||||||
|
? gameState.canvasWidth
|
||||||
|
: gameState.gameZoneWidthRoundedUp,
|
||||||
1,
|
1,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -240,7 +242,7 @@ export function render(gameState: GameState) {
|
||||||
if (isTelekinesisActive(gameState, ball) || isYoyoActive(gameState, ball)) {
|
if (isTelekinesisActive(gameState, ball) || isYoyoActive(gameState, ball)) {
|
||||||
ctx.strokeStyle = gameState.puckColor;
|
ctx.strokeStyle = gameState.puckColor;
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.moveTo(gameState.puckPosition, gameState.gameZoneHeight );
|
ctx.moveTo(gameState.puckPosition, gameState.gameZoneHeight);
|
||||||
ctx.bezierCurveTo(
|
ctx.bezierCurveTo(
|
||||||
gameState.puckPosition,
|
gameState.puckPosition,
|
||||||
gameState.gameZoneHeight,
|
gameState.gameZoneHeight,
|
||||||
|
@ -251,9 +253,6 @@ export function render(gameState: GameState) {
|
||||||
);
|
);
|
||||||
ctx.stroke();
|
ctx.stroke();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
// The puck
|
// The puck
|
||||||
ctx.globalAlpha = 1;
|
ctx.globalAlpha = 1;
|
||||||
|
@ -318,7 +317,7 @@ export function render(gameState: GameState) {
|
||||||
const hasCombo = gameState.combo > baseCombo(gameState);
|
const hasCombo = gameState.combo > baseCombo(gameState);
|
||||||
ctx.globalCompositeOperation = "source-over";
|
ctx.globalCompositeOperation = "source-over";
|
||||||
|
|
||||||
ctx.globalAlpha = gameState.perks.unbounded ? 0.1 : 1
|
ctx.globalAlpha = gameState.perks.unbounded ? 0.1 : 1;
|
||||||
if (gameState.offsetXRoundedDown) {
|
if (gameState.offsetXRoundedDown) {
|
||||||
// draw outside of gaming area to avoid capturing borders in recordings
|
// draw outside of gaming area to avoid capturing borders in recordings
|
||||||
ctx.fillStyle =
|
ctx.fillStyle =
|
||||||
|
|
2
src/types.d.ts
vendored
2
src/types.d.ts
vendored
|
@ -244,7 +244,7 @@ export type GameState = {
|
||||||
runStatistics: RunStats;
|
runStatistics: RunStats;
|
||||||
lastOffered: Partial<{ [k in PerkId]: number }>;
|
lastOffered: Partial<{ [k in PerkId]: number }>;
|
||||||
levelTime: number;
|
levelTime: number;
|
||||||
noBricksSince: number ;
|
noBricksSince: number;
|
||||||
levelWallBounces: number;
|
levelWallBounces: number;
|
||||||
autoCleanUses: number;
|
autoCleanUses: number;
|
||||||
aboutToPlaySound: {
|
aboutToPlaySound: {
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
|
|
||||||
import _rawLevelsList from "./data/levels.json";
|
import _rawLevelsList from "./data/levels.json";
|
||||||
import {rawUpgrades} from "./upgrades";
|
import { rawUpgrades } from "./upgrades";
|
||||||
|
|
||||||
describe("rawUpgrades", ()=>{
|
describe("rawUpgrades", () => {
|
||||||
|
it("has an icon for each upgrade", () => {
|
||||||
it('has an icon for each upgrade',()=>{
|
const missingIcon = rawUpgrades
|
||||||
const missingIcon = rawUpgrades.map(u=>u.id).filter(id=>!_rawLevelsList.find(l=>l.name === 'icon:'+id))
|
.map((u) => u.id)
|
||||||
expect(missingIcon.join(', ')).toEqual('')
|
.filter((id) => !_rawLevelsList.find((l) => l.name === "icon:" + id));
|
||||||
})
|
expect(missingIcon.join(", ")).toEqual("");
|
||||||
})
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -383,7 +383,7 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
threshold:70000,
|
threshold: 70000,
|
||||||
giftable: false,
|
giftable: false,
|
||||||
id: "asceticism",
|
id: "asceticism",
|
||||||
max: 1,
|
max: 1,
|
||||||
|
@ -393,7 +393,7 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
threshold:75000,
|
threshold: 75000,
|
||||||
giftable: false,
|
giftable: false,
|
||||||
id: "unbounded",
|
id: "unbounded",
|
||||||
max: 1,
|
max: 1,
|
||||||
|
@ -403,7 +403,7 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
threshold:80000,
|
threshold: 80000,
|
||||||
giftable: false,
|
giftable: false,
|
||||||
id: "shunt",
|
id: "shunt",
|
||||||
max: 1,
|
max: 1,
|
||||||
|
@ -413,7 +413,7 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
threshold:85000,
|
threshold: 85000,
|
||||||
giftable: false,
|
giftable: false,
|
||||||
id: "yoyo",
|
id: "yoyo",
|
||||||
max: 2,
|
max: 2,
|
||||||
|
@ -423,17 +423,17 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
threshold:90000,
|
threshold: 90000,
|
||||||
giftable: false,
|
giftable: false,
|
||||||
id: "nbricks",
|
id: "nbricks",
|
||||||
max: 3,
|
max: 3,
|
||||||
name: t("upgrades.nbricks.name"),
|
name: t("upgrades.nbricks.name"),
|
||||||
help: (lvl: number) => t("upgrades.nbricks.help",{lvl}),
|
help: (lvl: number) => t("upgrades.nbricks.help", { lvl }),
|
||||||
fullHelp: t("upgrades.nbricks.fullHelp"),
|
fullHelp: t("upgrades.nbricks.fullHelp"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
threshold:95000,
|
threshold: 95000,
|
||||||
giftable: false,
|
giftable: false,
|
||||||
id: "etherealcoins",
|
id: "etherealcoins",
|
||||||
max: 1,
|
max: 1,
|
||||||
|
@ -443,7 +443,7 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "multiball",
|
requires: "multiball",
|
||||||
threshold:100000,
|
threshold: 100000,
|
||||||
giftable: false,
|
giftable: false,
|
||||||
id: "shocks",
|
id: "shocks",
|
||||||
max: 1,
|
max: 1,
|
||||||
|
@ -453,7 +453,7 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
threshold:105000,
|
threshold: 105000,
|
||||||
giftable: false,
|
giftable: false,
|
||||||
id: "zen",
|
id: "zen",
|
||||||
max: 1,
|
max: 1,
|
||||||
|
@ -463,7 +463,7 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "extra_life",
|
requires: "extra_life",
|
||||||
threshold:110000,
|
threshold: 110000,
|
||||||
giftable: false,
|
giftable: false,
|
||||||
id: "sacrifice",
|
id: "sacrifice",
|
||||||
max: 1,
|
max: 1,
|
||||||
|
@ -474,27 +474,27 @@ export const rawUpgrades = [
|
||||||
|
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
threshold:115000,
|
threshold: 115000,
|
||||||
giftable: false,
|
giftable: false,
|
||||||
id: "trampoline",
|
id: "trampoline",
|
||||||
max: 3,
|
max: 3,
|
||||||
name: t("upgrades.trampoline.name"),
|
name: t("upgrades.trampoline.name"),
|
||||||
help: (lvl: number) => t("upgrades.trampoline.help",{lvl}),
|
help: (lvl: number) => t("upgrades.trampoline.help", { lvl }),
|
||||||
fullHelp: t("upgrades.trampoline.fullHelp"),
|
fullHelp: t("upgrades.trampoline.fullHelp"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
threshold:120000,
|
threshold: 120000,
|
||||||
giftable: false,
|
giftable: false,
|
||||||
id: "ghost_coins",
|
id: "ghost_coins",
|
||||||
max: 1,
|
max: 1,
|
||||||
name: t("upgrades.ghost_coins.name"),
|
name: t("upgrades.ghost_coins.name"),
|
||||||
help: (lvl: number) => t("upgrades.ghost_coins.help",{lvl}),
|
help: (lvl: number) => t("upgrades.ghost_coins.help", { lvl }),
|
||||||
fullHelp: t("upgrades.ghost_coins.fullHelp"),
|
fullHelp: t("upgrades.ghost_coins.fullHelp"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
threshold:125000,
|
threshold: 125000,
|
||||||
giftable: false,
|
giftable: false,
|
||||||
id: "forgiving",
|
id: "forgiving",
|
||||||
max: 1,
|
max: 1,
|
||||||
|
@ -504,7 +504,7 @@ export const rawUpgrades = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
requires: "",
|
requires: "",
|
||||||
threshold:130000,
|
threshold: 130000,
|
||||||
giftable: false,
|
giftable: false,
|
||||||
id: "ball_attracts_coins",
|
id: "ball_attracts_coins",
|
||||||
max: 3,
|
max: 3,
|
||||||
|
@ -512,5 +512,4 @@ export const rawUpgrades = [
|
||||||
help: (lvl: number) => t("upgrades.ball_attracts_coins.help"),
|
help: (lvl: number) => t("upgrades.ball_attracts_coins.help"),
|
||||||
fullHelp: t("upgrades.ball_attracts_coins.fullHelp"),
|
fullHelp: t("upgrades.ball_attracts_coins.fullHelp"),
|
||||||
},
|
},
|
||||||
|
|
||||||
] as const;
|
] as const;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue