mirror of
https://gitlab.com/lecarore/breakout71.git
synced 2025-04-21 12:36:15 -04:00
Overcomplicated looping mechanic
This commit is contained in:
parent
5012076039
commit
39b326a15b
18 changed files with 296 additions and 125 deletions
|
@ -25,10 +25,10 @@ Allow players to loop the game :
|
||||||
- real time stats as the option says.
|
- real time stats as the option says.
|
||||||
- [x] Noise of coins against side is annoying.
|
- [x] Noise of coins against side is annoying.
|
||||||
- Change look of loop, to avoid picking randomly at loop end.
|
- Change look of loop, to avoid picking randomly at loop end.
|
||||||
- make red coins scarier,
|
- [x] make red coins scarier,
|
||||||
- add blue coins that only freeze puck.
|
- [x] add blue coins that only freeze puck.
|
||||||
- Make fullscreen an option and turn it back on when playing
|
- Make fullscreen an option and turn it back on when playing
|
||||||
- +1 combo de base par rerolls
|
- [x] +1 combo de base par rerolls
|
||||||
- +1 combo de base par vie restantes (pas attrapable)
|
- +1 combo de base par vie restantes (pas attrapable)
|
||||||
|
|
||||||
# Todo
|
# Todo
|
||||||
|
@ -132,6 +132,7 @@ There's also an easy mode for kids (slower ball).
|
||||||
- [colin] side pucks - same as above but with two side pucks : hard to know where to put them
|
- [colin] side pucks - same as above but with two side pucks : hard to know where to put them
|
||||||
|
|
||||||
# to sort
|
# to sort
|
||||||
|
- double coin value when they hit the sides
|
||||||
- [colin]Brambles — coins that touch the walls and ceiling get stuck and are thrown back when the last brick is destroyed
|
- [colin]Brambles — coins that touch the walls and ceiling get stuck and are thrown back when the last brick is destroyed
|
||||||
- [colin]Ball of Greed — the ball can collect coins (might be worth dividing into levels: lvl 1, can collect coins only after two bounces on bricks or walls. lvl 2, can collect after 1 bounce. lvl 3, can collect coins anytime)(or change the ball collection radius as the level grows)
|
- [colin]Ball of Greed — the ball can collect coins (might be worth dividing into levels: lvl 1, can collect coins only after two bounces on bricks or walls. lvl 2, can collect after 1 bounce. lvl 3, can collect coins anytime)(or change the ball collection radius as the level grows)
|
||||||
- [colin]Fountain toss — each coin lost has a 1 in 10 chance to give +1 combo (until combo 50)
|
- [colin]Fountain toss — each coin lost has a 1 in 10 chance to give +1 combo (until combo 50)
|
||||||
|
|
|
@ -11,8 +11,8 @@ android {
|
||||||
applicationId = "me.lecaro.breakout"
|
applicationId = "me.lecaro.breakout"
|
||||||
minSdk = 21
|
minSdk = 21
|
||||||
targetSdk = 34
|
targetSdk = 34
|
||||||
versionCode = 29053110
|
versionCode = 29053158
|
||||||
versionName = "29053110"
|
versionName = "29053158"
|
||||||
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
109
dist/index.html
vendored
109
dist/index.html
vendored
File diff suppressed because one or more lines are too long
|
@ -1,5 +1,5 @@
|
||||||
// The version of the cache.
|
// The version of the cache.
|
||||||
const VERSION = "29053110";
|
const VERSION = "29053158";
|
||||||
|
|
||||||
// The name of the cache
|
// The name of the cache
|
||||||
const CACHE_NAME = `breakout-71-${VERSION}`;
|
const CACHE_NAME = `breakout-71-${VERSION}`;
|
||||||
|
|
|
@ -82,6 +82,7 @@ export async function asyncAlert<t>({
|
||||||
content
|
content
|
||||||
?.filter((i) => i)
|
?.filter((i) => i)
|
||||||
.forEach((entry, index) => {
|
.forEach((entry, index) => {
|
||||||
|
if(!entry) return;
|
||||||
if (typeof entry == "string") {
|
if (typeof entry == "string") {
|
||||||
const p = document.createElement("div");
|
const p = document.createElement("div");
|
||||||
p.innerHTML = entry;
|
p.innerHTML = entry;
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
"29053110"
|
"29053158"
|
||||||
|
|
|
@ -3,10 +3,16 @@ import { Debuff } from "./types";
|
||||||
|
|
||||||
export const debuffs = [
|
export const debuffs = [
|
||||||
{
|
{
|
||||||
id: "negative_coins",
|
id: "deadly_coins",
|
||||||
max: 20,
|
max: 20,
|
||||||
name: (lvl: number) => t("debuffs.negative_coins.help", { lvl }),
|
name: (lvl: number) => t("debuffs.deadly_coins.help", { lvl }),
|
||||||
help: (lvl: number) => t("debuffs.negative_coins.help", { lvl }),
|
help: (lvl: number) => t("debuffs.deadly_coins.help", { lvl }),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "frozen_coins",
|
||||||
|
max: 20,
|
||||||
|
name: (lvl: number) => t("debuffs.frozen_coins.help", { lvl }),
|
||||||
|
help: (lvl: number) => t("debuffs.frozen_coins.help", { lvl }),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "more_bombs",
|
id: "more_bombs",
|
||||||
|
|
13
src/game.ts
13
src/game.ts
|
@ -466,6 +466,8 @@ async function openScorePanel() {
|
||||||
pickedUpgradesHTMl(gameState),
|
pickedUpgradesHTMl(gameState),
|
||||||
levelsListHTMl(gameState),
|
levelsListHTMl(gameState),
|
||||||
debuffsHTMl(gameState),
|
debuffsHTMl(gameState),
|
||||||
|
gameState.rerolls?
|
||||||
|
t('score_panel.rerolls_count', {rerolls:gameState.rerolls}):''
|
||||||
],
|
],
|
||||||
allowClose: true,
|
allowClose: true,
|
||||||
});
|
});
|
||||||
|
@ -1008,8 +1010,8 @@ restart(
|
||||||
(window.location.search.includes("stressTest") && {
|
(window.location.search.includes("stressTest") && {
|
||||||
level: "Bird",
|
level: "Bird",
|
||||||
perks: {
|
perks: {
|
||||||
// sapper: 5,
|
sapper: 5,
|
||||||
// bigger_explosions: 20,
|
bigger_explosions: 20,
|
||||||
// // unbounded: 1,
|
// // unbounded: 1,
|
||||||
// // pierce_color: 1,
|
// // pierce_color: 1,
|
||||||
// pierce: 1,
|
// pierce: 1,
|
||||||
|
@ -1022,12 +1024,15 @@ restart(
|
||||||
// metamorphosis: 1,
|
// metamorphosis: 1,
|
||||||
// implosions: 1,
|
// implosions: 1,
|
||||||
// sturdy_bricks:5
|
// sturdy_bricks:5
|
||||||
coin_magnet:2,
|
coin_magnet: 2,
|
||||||
extra_life: 3,
|
extra_life: 3,
|
||||||
},
|
},
|
||||||
|
levelsPerLoop:2,
|
||||||
|
|
||||||
debuffs: {
|
debuffs: {
|
||||||
// fragility:3
|
// fragility:3
|
||||||
negative_coins: 100,
|
// deadly_coins: 20,
|
||||||
|
// frozen_coins: 20,
|
||||||
// interference:20,
|
// interference:20,
|
||||||
},
|
},
|
||||||
}) ||
|
}) ||
|
||||||
|
|
|
@ -61,8 +61,8 @@ import { debuffs } from "./debuffs";
|
||||||
import { requiredAsyncAlert } from "./asyncAlert";
|
import { requiredAsyncAlert } from "./asyncAlert";
|
||||||
|
|
||||||
export function setMousePos(gameState: GameState, x: number) {
|
export function setMousePos(gameState: GameState, x: number) {
|
||||||
|
gameState.desiredPuckPosition = 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
|
||||||
gameState.puckPosition = x;
|
|
||||||
gameState.needsRender = true;
|
gameState.needsRender = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,6 +166,11 @@ export function normalizeGameState(gameState: GameState) {
|
||||||
gameState.gameZoneWidthRoundedUp -
|
gameState.gameZoneWidthRoundedUp -
|
||||||
gameState.puckWidth / 2;
|
gameState.puckWidth / 2;
|
||||||
|
|
||||||
|
if (gameState.puckFrozenUntil < gameState.levelTime || !gameState.levelTime) {
|
||||||
|
// Frozen, ignore
|
||||||
|
gameState.puckPosition = gameState.desiredPuckPosition;
|
||||||
|
}
|
||||||
|
|
||||||
gameState.puckPosition = clamp(gameState.puckPosition, minX, maxX);
|
gameState.puckPosition = clamp(gameState.puckPosition, minX, maxX);
|
||||||
|
|
||||||
if (gameState.ballStickToPuck) {
|
if (gameState.ballStickToPuck) {
|
||||||
|
@ -182,7 +187,11 @@ export function normalizeGameState(gameState: GameState) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function baseCombo(gameState: GameState) {
|
export function baseCombo(gameState: GameState) {
|
||||||
return gameState.baseCombo + gameState.perks.base_combo * 3 + gameState.perks.smaller_puck * 5;
|
return (
|
||||||
|
gameState.baseCombo +
|
||||||
|
gameState.perks.base_combo * 3 +
|
||||||
|
gameState.perks.smaller_puck * 5
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resetCombo(
|
export function resetCombo(
|
||||||
|
@ -594,13 +603,13 @@ export async function gotoNextLoop(gameState: GameState) {
|
||||||
gameState.runLevels = getRunLevels(gameState.totalScoreAtRunStart, {});
|
gameState.runLevels = getRunLevels(gameState.totalScoreAtRunStart, {});
|
||||||
gameState.upgradesOfferedFor = -1;
|
gameState.upgradesOfferedFor = -1;
|
||||||
|
|
||||||
let comboText=''
|
let comboText = "";
|
||||||
if(gameState.rerolls) {
|
if (gameState.rerolls) {
|
||||||
comboText=t('loop.converted_rerolls',{n:gameState.rerolls})
|
comboText = t("loop.converted_rerolls", { n: gameState.rerolls });
|
||||||
gameState.baseCombo += gameState.rerolls
|
gameState.baseCombo += gameState.rerolls;
|
||||||
gameState.rerolls=0
|
gameState.rerolls = 0;
|
||||||
}else{
|
} else {
|
||||||
comboText=t('loop.no_rerolls')
|
comboText = t("loop.no_rerolls");
|
||||||
}
|
}
|
||||||
|
|
||||||
const userPerks = upgrades.filter((u) => gameState.perks[u.id]);
|
const userPerks = upgrades.filter((u) => gameState.perks[u.id]);
|
||||||
|
@ -612,7 +621,7 @@ export async function gotoNextLoop(gameState: GameState) {
|
||||||
title: t("loop.title", { loop: gameState.loop }),
|
title: t("loop.title", { loop: gameState.loop }),
|
||||||
content: [
|
content: [
|
||||||
t("loop.instructions"),
|
t("loop.instructions"),
|
||||||
comboText,
|
comboText,
|
||||||
|
|
||||||
...userPerks.map((u) => {
|
...userPerks.map((u) => {
|
||||||
const randomDebuff =
|
const randomDebuff =
|
||||||
|
@ -675,6 +684,7 @@ export async function setLevel(gameState: GameState, l: number) {
|
||||||
gameState.levelStartScore = gameState.score;
|
gameState.levelStartScore = gameState.score;
|
||||||
gameState.levelSpawnedCoins = 0;
|
gameState.levelSpawnedCoins = 0;
|
||||||
gameState.levelMisses = 0;
|
gameState.levelMisses = 0;
|
||||||
|
gameState.puckFrozenUntil = 0;
|
||||||
gameState.runStatistics.levelsPlayed++;
|
gameState.runStatistics.levelsPlayed++;
|
||||||
|
|
||||||
// Reset combo silently
|
// Reset combo silently
|
||||||
|
@ -1049,7 +1059,10 @@ export function gameStateTick(
|
||||||
|
|
||||||
const ratio =
|
const ratio =
|
||||||
1 -
|
1 -
|
||||||
((coin.color === "crimson" ? 3 : gameState.perks.viscosity) * 0.03 +
|
((coin.color === "crimson" || coin.color === "LightSkyBlue"
|
||||||
|
? 3
|
||||||
|
: gameState.perks.viscosity) *
|
||||||
|
0.03 +
|
||||||
0.005) *
|
0.005) *
|
||||||
frames;
|
frames;
|
||||||
|
|
||||||
|
@ -1085,19 +1098,19 @@ export function gameStateTick(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(coin.color === "crimson" && !isOptionOn('basic')){
|
if (coin.color === "crimson" && !isOptionOn("basic")) {
|
||||||
const angle=Math.random()*Math.PI*2
|
const angle = Math.random() * Math.PI * 2;
|
||||||
makeParticle(
|
makeParticle(
|
||||||
gameState,
|
gameState,
|
||||||
coin.x,
|
coin.x,
|
||||||
coin.y,
|
coin.y,
|
||||||
Math.cos(angle)*gameState.baseSpeed*2,
|
Math.cos(angle) * gameState.baseSpeed * 2,
|
||||||
Math.sin(angle)*gameState.baseSpeed*2,
|
Math.sin(angle) * gameState.baseSpeed * 2,
|
||||||
'red',
|
"red",
|
||||||
true,
|
true,
|
||||||
5,
|
5,
|
||||||
250,
|
250,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const speed = (Math.abs(coin.vx) + Math.abs(coin.vy)) * 10;
|
const speed = (Math.abs(coin.vx) + Math.abs(coin.vy)) * 10;
|
||||||
|
@ -1112,16 +1125,22 @@ export function gameStateTick(
|
||||||
// a bit of margin to be nice , negative in case it's a negative coin
|
// a bit of margin to be nice , negative in case it's a negative coin
|
||||||
gameState.puckHeight * (coin.points ? 1 : -1)
|
gameState.puckHeight * (coin.points ? 1 : -1)
|
||||||
) {
|
) {
|
||||||
if (coin.points) {
|
if (coin.color === "crimson") {
|
||||||
addToScore(gameState, coin);
|
if (gameState.perks.extra_life && gameState.balls.length) {
|
||||||
} else if (gameState.perks.extra_life && gameState.balls.length) {
|
justLostALife(gameState, gameState.balls[0], coin.x, coin.y);
|
||||||
justLostALife(gameState, gameState.balls[0], coin.x, coin.y);
|
} else {
|
||||||
} else {
|
gameOver(
|
||||||
gameOver(
|
t("gameOver.because_cursed_coin"),
|
||||||
t("gameOver.because_cursed_coin"),
|
t("gameOver.because_cursed_coin_intro"),
|
||||||
t("gameOver.because_cursed_coin_intro"),
|
);
|
||||||
);
|
}
|
||||||
}
|
}
|
||||||
|
if (coin.color === "LightSkyBlue") {
|
||||||
|
gameState.puckFrozenUntil = gameState.levelTime + 500;
|
||||||
|
schedulGameSound(gameState, "freeze", coin.x, 1);
|
||||||
|
}
|
||||||
|
addToScore(gameState, coin);
|
||||||
|
|
||||||
destroy(gameState.coins, coinIndex);
|
destroy(gameState.coins, coinIndex);
|
||||||
} else if (coin.y > gameState.canvasHeight + coinRadius) {
|
} else if (coin.y > gameState.canvasHeight + coinRadius) {
|
||||||
destroy(gameState.coins, coinIndex);
|
destroy(gameState.coins, coinIndex);
|
||||||
|
@ -1746,12 +1765,21 @@ function makeCoin(
|
||||||
color = "gold",
|
color = "gold",
|
||||||
points = 1,
|
points = 1,
|
||||||
) {
|
) {
|
||||||
if (y<gameState.gameZoneWidth*2/3 &&
|
let weight = 0.8 + Math.random() * 0.2 + Math.min(2, points * 0.01);
|
||||||
gameState.debuffs.negative_coins * points > Math.random() * 10000) {
|
if (
|
||||||
|
y < (gameState.gameZoneWidth * 2) / 3 &&
|
||||||
|
gameState.debuffs.deadly_coins * points > Math.random() * 10000
|
||||||
|
) {
|
||||||
points = 0;
|
points = 0;
|
||||||
color = "crimson";
|
color = "crimson";
|
||||||
vx=0
|
vx = 0;
|
||||||
vy=0
|
vy = 0;
|
||||||
|
schedulGameSound(gameState, "void", x, 0.5);
|
||||||
|
weight = 1;
|
||||||
|
} else if (gameState.debuffs.frozen_coins * points > Math.random() * 10000) {
|
||||||
|
color = "LightSkyBlue";
|
||||||
|
schedulGameSound(gameState, "freeze", x, 0.5);
|
||||||
|
weight = 1;
|
||||||
}
|
}
|
||||||
append(gameState.coins, (p: Partial<Coin>) => {
|
append(gameState.coins, (p: Partial<Coin>) => {
|
||||||
p.x = x;
|
p.x = x;
|
||||||
|
@ -1767,8 +1795,8 @@ function makeCoin(
|
||||||
p.color = color;
|
p.color = color;
|
||||||
p.a = Math.random() * Math.PI * 2;
|
p.a = Math.random() * Math.PI * 2;
|
||||||
p.sa = Math.random() - 0.5;
|
p.sa = Math.random() - 0.5;
|
||||||
p.weight = 0.8 + Math.random() * 0.2 + Math.min(2, points * 0.01);
|
|
||||||
p.points = points;
|
p.points = points;
|
||||||
|
p.weight = weight;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,12 @@ export function sample<T>(arr: T[]): T {
|
||||||
return arr[Math.floor(arr.length * Math.random())];
|
return arr[Math.floor(arr.length * Math.random())];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function sampleN<T>(arr: T[],n:number): T[] {
|
||||||
|
|
||||||
|
return [...arr].sort(()=>Math.random()-0.5)
|
||||||
|
.slice(0,n)
|
||||||
|
}
|
||||||
|
|
||||||
export function sumOfValues(obj: { [key: string]: number } | undefined | null) {
|
export function sumOfValues(obj: { [key: string]: number } | undefined | null) {
|
||||||
if (!obj) return 0;
|
if (!obj) return 0;
|
||||||
return Object.values(obj)?.reduce((a, b) => a + b, 0) || 0;
|
return Object.values(obj)?.reduce((a, b) => a + b, 0) || 0;
|
||||||
|
@ -55,7 +61,7 @@ export function getPossibleUpgrades(gameState: GameState) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function max_levels(gameState: GameState) {
|
export function max_levels(gameState: GameState) {
|
||||||
return 7 + gameState.perks.extra_levels;
|
return gameState.levelsPerLoop + gameState.perks.extra_levels;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function pickedUpgradesHTMl(gameState: GameState) {
|
export function pickedUpgradesHTMl(gameState: GameState) {
|
||||||
|
@ -143,6 +149,7 @@ export function defaultSounds() {
|
||||||
coinCatch: { vol: 0, x: 0 },
|
coinCatch: { vol: 0, x: 0 },
|
||||||
colorChange: { vol: 0, x: 0 },
|
colorChange: { vol: 0, x: 0 },
|
||||||
void: { vol: 0, x: 0 },
|
void: { vol: 0, x: 0 },
|
||||||
|
freeze: { vol: 0, x: 0 },
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -122,6 +122,26 @@
|
||||||
</concept_node>
|
</concept_node>
|
||||||
</children>
|
</children>
|
||||||
</folder_node>
|
</folder_node>
|
||||||
|
<folder_node>
|
||||||
|
<name>deadly_coins</name>
|
||||||
|
<children>
|
||||||
|
<concept_node>
|
||||||
|
<name>help</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>
|
||||||
|
</children>
|
||||||
|
</folder_node>
|
||||||
<folder_node>
|
<folder_node>
|
||||||
<name>fragility</name>
|
<name>fragility</name>
|
||||||
<children>
|
<children>
|
||||||
|
@ -142,6 +162,26 @@
|
||||||
</concept_node>
|
</concept_node>
|
||||||
</children>
|
</children>
|
||||||
</folder_node>
|
</folder_node>
|
||||||
|
<folder_node>
|
||||||
|
<name>frozen_coins</name>
|
||||||
|
<children>
|
||||||
|
<concept_node>
|
||||||
|
<name>help</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>
|
||||||
|
</children>
|
||||||
|
</folder_node>
|
||||||
<folder_node>
|
<folder_node>
|
||||||
<name>interference</name>
|
<name>interference</name>
|
||||||
<children>
|
<children>
|
||||||
|
@ -182,26 +222,6 @@
|
||||||
</concept_node>
|
</concept_node>
|
||||||
</children>
|
</children>
|
||||||
</folder_node>
|
</folder_node>
|
||||||
<folder_node>
|
|
||||||
<name>negative_coins</name>
|
|
||||||
<children>
|
|
||||||
<concept_node>
|
|
||||||
<name>help</name>
|
|
||||||
<description/>
|
|
||||||
<comment/>
|
|
||||||
<translations>
|
|
||||||
<translation>
|
|
||||||
<language>en-US</language>
|
|
||||||
<approved>true</approved>
|
|
||||||
</translation>
|
|
||||||
<translation>
|
|
||||||
<language>fr-FR</language>
|
|
||||||
<approved>false</approved>
|
|
||||||
</translation>
|
|
||||||
</translations>
|
|
||||||
</concept_node>
|
|
||||||
</children>
|
|
||||||
</folder_node>
|
|
||||||
<folder_node>
|
<folder_node>
|
||||||
<name>sturdiness</name>
|
<name>sturdiness</name>
|
||||||
<children>
|
<children>
|
||||||
|
@ -2077,6 +2097,21 @@
|
||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>rerolls_count</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>
|
<concept_node>
|
||||||
<name>test_run</name>
|
<name>test_run</name>
|
||||||
<description/>
|
<description/>
|
||||||
|
|
|
@ -5,10 +5,11 @@
|
||||||
"confirmRestart.yes": "Restart game",
|
"confirmRestart.yes": "Restart game",
|
||||||
"debuffs.banned.description": "{{lvl}} banned perk(s) : {{banned}}.",
|
"debuffs.banned.description": "{{lvl}} banned perk(s) : {{banned}}.",
|
||||||
"debuffs.banned.help": "{{perk}} is banned for this run.",
|
"debuffs.banned.help": "{{perk}} is banned for this run.",
|
||||||
"debuffs.fragility.help": "Explosions destroy {{percent}} of coins and reset combo.",
|
"debuffs.deadly_coins.help": "{{lvl}} ou of 10 000 coins spawn deadly, blinking red. Game over if you catch them, unless you had an extra life to spare.",
|
||||||
|
"debuffs.fragility.help": "Explosions destroy {{percent}}% of coins and reset combo.",
|
||||||
|
"debuffs.frozen_coins.help": "{{lvl}} coins out of 10000 spawn frozen, and will lock the puck for 0.5s if caught.",
|
||||||
"debuffs.interference.help": "Telekinesis and yo-yo glitch for {{lvl}}s every 7s.",
|
"debuffs.interference.help": "Telekinesis and yo-yo glitch for {{lvl}}s every 7s.",
|
||||||
"debuffs.more_bombs.help": "{{lvl}} bricks replaced by bombs.",
|
"debuffs.more_bombs.help": "{{lvl}} bricks replaced by bombs.",
|
||||||
"debuffs.negative_coins.help": "{{lvl}}/10000 of coins spawn cursed, blinking red. Game over if you catch them.",
|
|
||||||
"debuffs.sturdiness.help": "All bricks have +{{lvl}} HP",
|
"debuffs.sturdiness.help": "All bricks have +{{lvl}} HP",
|
||||||
"gameOver.because_cursed_coin": "Game over",
|
"gameOver.because_cursed_coin": "Game over",
|
||||||
"gameOver.because_cursed_coin_intro": "You cough a cursed coin (bright red coins) and didn't have a extra life to spare. ",
|
"gameOver.because_cursed_coin_intro": "You cough a cursed coin (bright red coins) and didn't have a extra life to spare. ",
|
||||||
|
@ -130,6 +131,7 @@
|
||||||
"sandbox.title": "Sandbox mode",
|
"sandbox.title": "Sandbox mode",
|
||||||
"sandbox.unlocks_at": "Unlocks at total score {{score}}",
|
"sandbox.unlocks_at": "Unlocks at total score {{score}}",
|
||||||
"score_panel.bebuffs_list": "De-buffs :",
|
"score_panel.bebuffs_list": "De-buffs :",
|
||||||
|
"score_panel.rerolls_count": "You have accumulated {{rerolls}} rerolls",
|
||||||
"score_panel.test_run": "This is a test run, score is not recorded permanently",
|
"score_panel.test_run": "This is a test run, score is not recorded permanently",
|
||||||
"score_panel.title": "{{score}} points at level {{level}}/{{max}} ",
|
"score_panel.title": "{{score}} points at level {{level}}/{{max}} ",
|
||||||
"score_panel.title_looped": "{{score}} points at level {{level}}/{{max}} of loop {{loop}}",
|
"score_panel.title_looped": "{{score}} points at level {{level}}/{{max}} of loop {{loop}}",
|
||||||
|
|
|
@ -5,10 +5,11 @@
|
||||||
"confirmRestart.yes": "Commencer une nouvelle partie",
|
"confirmRestart.yes": "Commencer une nouvelle partie",
|
||||||
"debuffs.banned.description": "{{lvl}} amélioration(s) bannie(s) : {{banned}}.",
|
"debuffs.banned.description": "{{lvl}} amélioration(s) bannie(s) : {{banned}}.",
|
||||||
"debuffs.banned.help": "{{perk}} est banni pour cette course.",
|
"debuffs.banned.help": "{{perk}} est banni pour cette course.",
|
||||||
"debuffs.fragility.help": "Les explosions détruisent {{percent}} pièces et réinitialisent le combo.",
|
"debuffs.deadly_coins.help": "{{lvl}} pièces sur 10000 apparaissent maudites et clignotent en rouge. La partie est terminée si vous les attrapez, sauf si vous avez une vie en stock.",
|
||||||
|
"debuffs.fragility.help": "Les explosions détruisent {{percent}}% des pièces et réinitialisent le combo.",
|
||||||
|
"debuffs.frozen_coins.help": "{{lvl}} pièces sur 10000 apparaît gelée, et glace le palet pendant 0.5 si vous l'attrapez. ",
|
||||||
"debuffs.interference.help": "Télékinésie et problème de yo-yo pendant {{lvl}}s toutes les 7 s.",
|
"debuffs.interference.help": "Télékinésie et problème de yo-yo pendant {{lvl}}s toutes les 7 s.",
|
||||||
"debuffs.more_bombs.help": "{{lvl}} briques remplacées par des bombes.",
|
"debuffs.more_bombs.help": "{{lvl}} briques remplacées par des bombes.",
|
||||||
"debuffs.negative_coins.help": "{{lvl}}/10000 pièces apparaissent maudites et clignotent en rouge. La partie est terminée si vous les attrapez.",
|
|
||||||
"debuffs.sturdiness.help": "Toutes les briques résistent à +{{lvl}} chocs",
|
"debuffs.sturdiness.help": "Toutes les briques résistent à +{{lvl}} chocs",
|
||||||
"gameOver.because_cursed_coin": "Jeu terminé",
|
"gameOver.because_cursed_coin": "Jeu terminé",
|
||||||
"gameOver.because_cursed_coin_intro": "Vous avez craché une pièce maudite (pièces rouge vif) et vous n'aviez pas de vie supplémentaire à revendre.",
|
"gameOver.because_cursed_coin_intro": "Vous avez craché une pièce maudite (pièces rouge vif) et vous n'aviez pas de vie supplémentaire à revendre.",
|
||||||
|
@ -48,9 +49,9 @@
|
||||||
"level_up.unlocked_level": " (Niveau)",
|
"level_up.unlocked_level": " (Niveau)",
|
||||||
"level_up.unlocked_perk": " (Amélioration)",
|
"level_up.unlocked_perk": " (Amélioration)",
|
||||||
"level_up.upgrade_perk_to_level": " niveau {{level}}",
|
"level_up.upgrade_perk_to_level": " niveau {{level}}",
|
||||||
"loop.converted_rerolls": "",
|
"loop.converted_rerolls": "Vos {{n}} relances restantes ont été converties en +{{n}} combo de base.",
|
||||||
"loop.instructions": "Tous vos avantages seront supprimés, sauf un que vous pouvez choisir ci-dessous. Chaque option comporte un danger supplémentaire qui apparaîtra à tous les niveaux.",
|
"loop.instructions": "Tous vos avantages seront supprimés, sauf un que vous pouvez choisir ci-dessous. Chaque option comporte un danger supplémentaire qui apparaîtra à tous les niveaux.",
|
||||||
"loop.no_rerolls": "",
|
"loop.no_rerolls": "Vous n'aviez plus de relances, donc votre combo de base est resté le même.",
|
||||||
"loop.title": "Boucle de départ {{loop}}",
|
"loop.title": "Boucle de départ {{loop}}",
|
||||||
"main_menu.basic": "Graphismes simplifiés",
|
"main_menu.basic": "Graphismes simplifiés",
|
||||||
"main_menu.basic_help": "Meilleures performances.",
|
"main_menu.basic_help": "Meilleures performances.",
|
||||||
|
@ -130,6 +131,7 @@
|
||||||
"sandbox.title": "Mode bac à sable",
|
"sandbox.title": "Mode bac à sable",
|
||||||
"sandbox.unlocks_at": "Déverrouillé à partir d'un score total de {{score}}",
|
"sandbox.unlocks_at": "Déverrouillé à partir d'un score total de {{score}}",
|
||||||
"score_panel.bebuffs_list": "Handicapes : ",
|
"score_panel.bebuffs_list": "Handicapes : ",
|
||||||
|
"score_panel.rerolls_count": "Vous avez accumulé {{rerolls}} rerolls",
|
||||||
"score_panel.test_run": "Il s'agit d'une partie d'essai, le score n'est pas enregistré.",
|
"score_panel.test_run": "Il s'agit d'une partie d'essai, le score n'est pas enregistré.",
|
||||||
"score_panel.title": "{{score}} points au niveau {{level}}/{{max}} ",
|
"score_panel.title": "{{score}} points au niveau {{level}}/{{max}} ",
|
||||||
"score_panel.title_looped": "{{score}} points au niveau {{level}}/{{max}} ",
|
"score_panel.title_looped": "{{score}} points au niveau {{level}}/{{max}} ",
|
||||||
|
|
|
@ -51,6 +51,7 @@ export function newGameState(params: RunParams): GameState {
|
||||||
ballStickToPuck: true,
|
ballStickToPuck: true,
|
||||||
puckPosition: 400,
|
puckPosition: 400,
|
||||||
lastPuckPosition: 400,
|
lastPuckPosition: 400,
|
||||||
|
desiredPuckPosition: 400,
|
||||||
lastPuckMove: 0,
|
lastPuckMove: 0,
|
||||||
pauseTimeout: null,
|
pauseTimeout: null,
|
||||||
canvasWidth: 0,
|
canvasWidth: 0,
|
||||||
|
@ -111,6 +112,8 @@ export function newGameState(params: RunParams): GameState {
|
||||||
rerolls: 0,
|
rerolls: 0,
|
||||||
loop: 0,
|
loop: 0,
|
||||||
baseCombo: 1,
|
baseCombo: 1,
|
||||||
|
puckFrozenUntil: 0,
|
||||||
|
levelsPerLoop: params?.levelsPerLoop ?? 7,
|
||||||
};
|
};
|
||||||
resetBalls(gameState);
|
resetBalls(gameState);
|
||||||
|
|
||||||
|
|
|
@ -200,27 +200,49 @@ export function render(gameState: GameState) {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (gameState.debuffs.negative_coins) {
|
if (gameState.debuffs.deadly_coins || gameState.debuffs.frozen_coins) {
|
||||||
// Render crimson coins very bright
|
// Render crimson coins very bright
|
||||||
ctx.globalCompositeOperation = "source-over";
|
ctx.globalCompositeOperation = "source-over";
|
||||||
ctx.globalAlpha = 0.8;
|
ctx.globalAlpha = 0.8;
|
||||||
const red = Math.floor(gameState.levelTime / 100) % 2 > 0;
|
const red = Math.floor(gameState.levelTime / 100) % 2 > 0;
|
||||||
forEachLiveOne(gameState.coins, (coin) => {
|
forEachLiveOne(gameState.coins, (coin) => {
|
||||||
if (coin.color !== "crimson") return;
|
if (coin.color == "crimson") {
|
||||||
drawBall(ctx, red ? "red" : "black", coin.size * 3, coin.x, coin.y);
|
drawBall(ctx, red ? "red" : "black", coin.size * 3, coin.x, coin.y);
|
||||||
|
}
|
||||||
|
if (coin.color == "LightSkyBlue") {
|
||||||
|
drawBall(
|
||||||
|
ctx,
|
||||||
|
red ? "LightSkyBlue" : "black",
|
||||||
|
coin.size * 3,
|
||||||
|
coin.x,
|
||||||
|
coin.y,
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
ctx.globalAlpha = 1;
|
ctx.globalAlpha = 1;
|
||||||
forEachLiveOne(gameState.coins, (coin) => {
|
forEachLiveOne(gameState.coins, (coin) => {
|
||||||
if (coin.color !== "crimson") return;
|
if (coin.color == "crimson") {
|
||||||
drawCoin(
|
drawCoin(
|
||||||
ctx,
|
ctx,
|
||||||
!red ? "red" : "black",
|
!red ? "red" : "black",
|
||||||
coin.size,
|
coin.size,
|
||||||
coin.x,
|
coin.x,
|
||||||
coin.y,
|
coin.y,
|
||||||
"red",
|
"red",
|
||||||
coin.a,
|
coin.a,
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
if (coin.color == "LightSkyBlue") {
|
||||||
|
drawCoin(
|
||||||
|
ctx,
|
||||||
|
!red ? "LightSkyBlue" : "black",
|
||||||
|
coin.size,
|
||||||
|
coin.x,
|
||||||
|
coin.y,
|
||||||
|
"LightSkyBlue",
|
||||||
|
coin.a,
|
||||||
|
);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -324,7 +346,9 @@ export function render(gameState: GameState) {
|
||||||
|
|
||||||
drawPuck(
|
drawPuck(
|
||||||
ctx,
|
ctx,
|
||||||
gameState.puckColor,
|
gameState.puckFrozenUntil > gameState.levelTime
|
||||||
|
? "LightSkyBlue"
|
||||||
|
: gameState.puckColor,
|
||||||
gameState.puckWidth,
|
gameState.puckWidth,
|
||||||
gameState.puckHeight,
|
gameState.puckHeight,
|
||||||
0,
|
0,
|
||||||
|
|
|
@ -54,6 +54,11 @@ export const sounds = {
|
||||||
createSingleBounceSound(1200, pan, volume, 0.5, "sawtooth");
|
createSingleBounceSound(1200, pan, volume, 0.5, "sawtooth");
|
||||||
createSingleBounceSound(600, pan, volume, 0.3, "sawtooth");
|
createSingleBounceSound(600, pan, volume, 0.3, "sawtooth");
|
||||||
},
|
},
|
||||||
|
freeze: (volume: number, pan: number) => {
|
||||||
|
if (!isOptionOn("sound")) return;
|
||||||
|
createSingleBounceSound(220, pan, volume, 0.5, "square");
|
||||||
|
createSingleBounceSound(440, pan, volume, 0.5, "square");
|
||||||
|
},
|
||||||
explode: (volume: number, pan: number, combo: number) => {
|
explode: (volume: number, pan: number, combo: number) => {
|
||||||
if (!isOptionOn("sound")) return;
|
if (!isOptionOn("sound")) return;
|
||||||
createExplosionSound(pan);
|
createExplosionSound(pan);
|
||||||
|
|
5
src/types.d.ts
vendored
5
src/types.d.ts
vendored
|
@ -285,10 +285,14 @@ export type GameState = {
|
||||||
coinCatch: { vol: number; x: number };
|
coinCatch: { vol: number; x: number };
|
||||||
colorChange: { vol: number; x: number };
|
colorChange: { vol: number; x: number };
|
||||||
void: { vol: number; x: number };
|
void: { vol: number; x: number };
|
||||||
|
freeze: { vol: number; x: number };
|
||||||
};
|
};
|
||||||
rerolls: number;
|
rerolls: number;
|
||||||
loop: number;
|
loop: number;
|
||||||
baseCombo: number;
|
baseCombo: number;
|
||||||
|
puckFrozenUntil: number;
|
||||||
|
desiredPuckPosition: number;
|
||||||
|
levelsPerLoop:number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type RunParams = {
|
export type RunParams = {
|
||||||
|
@ -296,6 +300,7 @@ export type RunParams = {
|
||||||
levelToAvoid?: string;
|
levelToAvoid?: string;
|
||||||
perks?: Partial<PerksMap>;
|
perks?: Partial<PerksMap>;
|
||||||
debuffs?: Partial<DebuffsMap>;
|
debuffs?: Partial<DebuffsMap>;
|
||||||
|
levelsPerLoop?:number;
|
||||||
};
|
};
|
||||||
export type OptionDef = {
|
export type OptionDef = {
|
||||||
default: boolean;
|
default: boolean;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue