This commit is contained in:
Renan LE CARO 2025-04-07 14:50:35 +02:00
parent 156c060b96
commit be49fb9d6e
16 changed files with 70 additions and 68 deletions

View file

@ -13,13 +13,12 @@ Break colourful bricks, catch bouncing coins and select powerful upgrades !
# Game issues and potential solutions # Game issues and potential solutions
I should show what the starting perk is :
- make the perk icon playable as the first level of the run
- show it on screen for the first 5 seconds
When you have already a nice build and still get offered many perks, it gets tiring: When you have already a nice build and still get offered many perks, it gets tiring:
- limit all build to N perks (maybe could be boosted with a perk) - limit all build to N perks (maybe could be boosted with a perk)
- add a "no more upgrade in the run, but double coins" perk - add a "no more upgrade in the run, but double coins" perk
- permanent "skip" option on the upgrades, for when you don't want any of them.
- add a "double last level's coins" perk, then decreasing reward
One play style is too OP, no reason to try other things One play style is too OP, no reason to try other things
- encourage varied play style with level unlock requirements (testing) - encourage varied play style with level unlock requirements (testing)
@ -35,16 +34,12 @@ Some upgrades currently are not really useful
## To do ## To do
- maybe just make the starting perk icon the first level ? kind of silly, kind of fun.
- avoid showing a +1 and -1 at the same time when a combo increase is reset
- display closest unlock with current perks in score and gameover screens
- show the initial perk when we start a new game.
- "skip" option on the upgrades, for when you don't want any of them.
- fix starting perk option not working
## Done ## Done
- display closest unlock with current perks in score and gameover screens
- initial perk icon = first level
- fix starting perk option not working
- progress bar for unlock in unlocks menu - progress bar for unlock in unlocks menu
- display runs history - display runs history
- in the runs history, only save perks that were chosen by the user - in the runs history, only save perks that were chosen by the user
@ -217,6 +212,7 @@ Some upgrades currently are not really useful
## UX / gameplay ## UX / gameplay
- avoid showing a +1 and -1 at the same time when a combo increase is reset
- translate fastlane presentation texts to french - translate fastlane presentation texts to french
- mobile option: relative movement of the touch would be amplified and added to the puck - mobile option: relative movement of the touch would be amplified and added to the puck
- mobile option: don't pause on mobile when lifting finger - mobile option: don't pause on mobile when lifting finger

View file

@ -29,8 +29,8 @@ android {
applicationId = "me.lecaro.breakout" applicationId = "me.lecaro.breakout"
minSdk = 21 minSdk = 21
targetSdk = 34 targetSdk = 34
versionCode = 29067102 versionCode = 29067144
versionName = "29067102" versionName = "29067144"
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

28
dist/index.html vendored

File diff suppressed because one or more lines are too long

View file

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

View file

@ -437,13 +437,13 @@
{ {
"name": "icon:streak_shots", "name": "icon:streak_shots",
"size": 8, "size": 8,
"bricks": "_W_W_W__W_W_W_W_tttttt_WttttttW_tttttt_W______W______W_____WWWW", "bricks": "_W_W_W__W_W_W_W_tttttt_WttttttW________W______W______W_____WWWW_",
"svg": null "svg": null
}, },
{ {
"name": "icon:base_combo", "name": "icon:base_combo",
"size": 8, "size": 5,
"bricks": "ttttttttttyyttttttyytyyttttttyyttyyttttttyytyyttttttyytttttttttt________", "bricks": "ttttttytytttttttytytttttt",
"svg": null "svg": null
}, },
{ {

View file

@ -1 +1 @@
"29067102" "29067144"

View file

@ -75,8 +75,7 @@ export function gameOver(title: string, intro: string) {
allowClose: true, allowClose: true,
title, title,
content: [ content: [
getCreativeModeWarning(gameState), getCreativeModeWarning(gameState) || `
`
<p>${intro}</p> <p>${intro}</p>
<p>${t("gameOver.cumulative_total", { startTs, endTs })}</p> <p>${t("gameOver.cumulative_total", { startTs, endTs })}</p>
`, `,

View file

@ -285,12 +285,9 @@ export function getLevelUnlockCondition(levelIndex: number) {
// Returns "" if level is unlocked, otherwise a string explaining how to unlock it // Returns "" if level is unlocked, otherwise a string explaining how to unlock it
let required: UpgradeLike[] = [], let required: UpgradeLike[] = [],
forbidden: UpgradeLike[] = [], forbidden: UpgradeLike[] = [],
minScore = 0; minScore = Math.max(-1000 + 100 * levelIndex,0);
if (levelIndex <= 10) {
// Keep all as is if (levelIndex > 20) {
} else if (levelIndex < 20) {
minScore = 100 * levelIndex;
} else {
const excluded: Set<PerkId> = new Set([ const excluded: Set<PerkId> = new Set([
"extra_levels", "extra_levels",
"extra_life", "extra_life",
@ -315,7 +312,6 @@ export function getLevelUnlockCondition(levelIndex: number) {
const length = Math.ceil(levelIndex / 30); const length = Math.ceil(levelIndex / 30);
required = possibletargets.slice(0, length); required = possibletargets.slice(0, length);
forbidden = possibletargets.slice(length, length + length); forbidden = possibletargets.slice(length, length + length);
minScore = 100 * levelIndex;
} }
return { return {
required, required,

View file

@ -152,11 +152,11 @@
"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}}",
"score_panel.upcoming_levels": "Upcoming levels :", "score_panel.upcoming_levels": "Upcoming levels :",
"score_panel.upgrades_picked": "Upgrades picked so far : ", "score_panel.upgrades_picked": "Upgrades picked so far : ",
"unlocks.greyed_out_help": "The greyed out ones can be unlocked by increasing your total score. The total score increases every time you score in game.", "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, outside of test runs.",
"unlocks.intro": "Your total score is {{ts}}. Below are all the upgrades and levels the games has to offer. Click an upgrade or level below to start a game with it.", "unlocks.intro": "Your total score is {{ts}}. Below are all the upgrades and levels the games has to offer. Click an upgrade or level below to start a test game with it. Hint: you can set the starting upgrades in the settings.",
"unlocks.just_unlocked": "You just unlocked a level", "unlocks.just_unlocked": "You just unlocked a level",
"unlocks.just_unlocked_plural": "You just unlocked {{count}} levels", "unlocks.just_unlocked_plural": "You just unlocked {{count}} levels",
"unlocks.level": "<h2>You unlocked {{unlocked}} levels out of {{out_of}}</h2>\n<p>Here are all the game levels, click one to start a game with that starting level. </p> ", "unlocks.level": "<h2>You unlocked {{unlocked}} levels out of {{out_of}}</h2>\n<p>Here are all the game levels, click one to start a test game with that starting level. </p> ",
"unlocks.level_description": "A {{size}}x{{size}} level with {{bricks}} bricks, {{colors}} colors and {{bombs}} bombs.", "unlocks.level_description": "A {{size}}x{{size}} level with {{bricks}} bricks, {{colors}} colors and {{bombs}} bombs.",
"unlocks.minScore": "Reach ${{minScore}} in a run to unlock.", "unlocks.minScore": "Reach ${{minScore}} in a run to unlock.",
"unlocks.minScoreWithPerks": "Reach ${{minScore}} in a run with {{required}} but without {{forbidden}} to unlock.", "unlocks.minScoreWithPerks": "Reach ${{minScore}} in a run with {{required}} but without {{forbidden}} to unlock.",

View file

@ -152,11 +152,11 @@
"score_panel.title_looped": "{{score}} points au niveau {{level}}/{{max}} ", "score_panel.title_looped": "{{score}} points au niveau {{level}}/{{max}} ",
"score_panel.upcoming_levels": "Niveaux de la parties : ", "score_panel.upcoming_levels": "Niveaux de la parties : ",
"score_panel.upgrades_picked": "Améliorations choisies jusqu'à présent :", "score_panel.upgrades_picked": "Améliorations choisies jusqu'à présent :",
"unlocks.greyed_out_help": "Les éléments grisées peuvent être débloquées en augmentant votre score total. Le score total augmente à chaque fois que vous marquez des points dans le jeu.", "unlocks.greyed_out_help": "Les éléments grisées peuvent être débloquées en augmentant votre score total. Le score total augmente à chaque fois que vous marquez des points dans le jeu, en dehors des parties de test.",
"unlocks.intro": "Votre score total est de {{ts}}. Vous trouverez ci-dessous toutes les améliorations et tous les niveaux que le jeu peut offrir. Cliquez sur l'un d'entre eux pour commencer une nouvelle partie. ", "unlocks.intro": "Votre score total est de {{ts}}. Vous trouverez ci-dessous toutes les améliorations et tous les niveaux que le jeu peut offrir. Cliquez sur l'un d'entre eux pour les essayer dans une partie de test. Astuce : vous pouvez choisir les améliorations de départ dans les réglages.",
"unlocks.just_unlocked": "Vous venez de débloquer un niveau", "unlocks.just_unlocked": "Vous venez de débloquer un niveau",
"unlocks.just_unlocked_plural": "Vous venez de débloquer {{count}} niveaux", "unlocks.just_unlocked_plural": "Vous venez de débloquer {{count}} niveaux",
"unlocks.level": "<h2>Vous avez débloqué {{unlocked}} niveaux sur {{out_of}}</h2>\n<p>Voici tous les niveaux du jeu, cliquez sur l'un d'eux pour démarrer une partie avec ce niveau de départ. </p> ", "unlocks.level": "<h2>Vous avez débloqué {{unlocked}} niveaux sur {{out_of}}</h2>\n<p>Voici tous les niveaux du jeu, cliquez sur l'un d'eux pour démarrer une partie de test avec ce niveau de départ. </p> ",
"unlocks.level_description": "Un niveau {{size}}x{{size}} avec {{bricks}} briques, {{colors}} couleurs et {{bombs}} bombes.", "unlocks.level_description": "Un niveau {{size}}x{{size}} avec {{bricks}} briques, {{colors}} couleurs et {{bombs}} bombes.",
"unlocks.minScore": "Atteignez un score de ${{minScore}} dans une partie pour débloquer.", "unlocks.minScore": "Atteignez un score de ${{minScore}} dans une partie pour débloquer.",
"unlocks.minScoreWithPerks": "Atteignez ${{minScore}} dans une partie avec {{required}} mais sans {{forbidden}}.", "unlocks.minScoreWithPerks": "Atteignez ${{minScore}} dans une partie avec {{required}} mais sans {{forbidden}}.",

View file

@ -36,9 +36,9 @@ export const allLevelsAndIcons = rawLevelsList
sortKey: ((Math.random() + 3) / 3.5) * l.bricksCount, sortKey: ((Math.random() + 3) / 3.5) * l.bricksCount,
})) as Level[]; })) as Level[];
export const allLevels = export const allLevels = allLevelsAndIcons.filter(
allLevelsAndIcons.filter((l) => !l.name.startsWith("icon:")) (l) => !l.name.startsWith("icon:"),
);
export const upgrades = rawUpgrades.map((u) => ({ export const upgrades = rawUpgrades.map((u) => ({
...u, ...u,

View file

@ -96,3 +96,7 @@ if (migrationsRun && !window.location.hash) {
window.location.hash = "#reloadAfterMigration"; window.location.hash = "#reloadAfterMigration";
window.location.reload(); window.location.reload();
} }
if (!migrationsRun) {
window.location.hash = "";
}

View file

@ -1,5 +1,5 @@
import {GameState, PerkId, RunParams} from "./types"; import { GameState, PerkId, RunParams } from "./types";
import {allLevels, allLevelsAndIcons, upgrades} from "./loadGameData"; import { allLevels, allLevelsAndIcons, upgrades } from "./loadGameData";
import { import {
defaultSounds, defaultSounds,
getHighScore, getHighScore,
@ -15,17 +15,20 @@ import { getHistory } from "./gameOver";
import { getTotalScore } from "./settings"; import { getTotalScore } from "./settings";
import { isStartingPerk } from "./startingPerks"; import { isStartingPerk } from "./startingPerks";
export function getRunLevels(params: RunParams, randomGift:PerkId|undefined) { export function getRunLevels(
params: RunParams,
randomGift: PerkId | undefined,
) {
const history = getHistory(); const history = getHistory();
const unlocked = allLevels.filter( const unlocked = allLevels.filter(
(l, li) => !reasonLevelIsLocked(li, history, false), (l, li) => !reasonLevelIsLocked(li, history, false),
); );
const firstLevel =
const firstLevel = (params?.level && unlocked.filter((l) => l.name === params?.level)) (params?.level && unlocked.filter((l) => l.name === params?.level)) ||
|| ( (randomGift &&
randomGift && allLevelsAndIcons.filter(l=>l.name=='icon:'+randomGift) allLevelsAndIcons.filter((l) => l.name == "icon:" + randomGift)) ||
) || []; [];
const restInRandomOrder = unlocked const restInRandomOrder = unlocked
.filter((l) => l.name !== params?.level) .filter((l) => l.name !== params?.level)
@ -38,23 +41,24 @@ export function getRunLevels(params: RunParams, randomGift:PerkId|undefined) {
} }
export function newGameState(params: RunParams): GameState { export function newGameState(params: RunParams): GameState {
const highScore= getHighScore() const highScore = getHighScore();
const perks = { ...makeEmptyPerksMap(upgrades), ...(params?.perks || {}) }; const perks = { ...makeEmptyPerksMap(upgrades), ...(params?.perks || {}) };
let randomGift:PerkId|undefined =undefined let randomGift: PerkId | undefined = undefined;
if (!sumOfValues(perks)) { if (!sumOfValues(perks)) {
const giftable = upgrades.filter(
(u) => highScore >= u.threshold && !u.requires && isStartingPerk(u),
);
const giftable = upgrades.filter((u) => highScore >= u.threshold && !u.requires && isStartingPerk(u)); randomGift =
randomGift =
(isOptionOn("easy") && "slow_down") || (isOptionOn("easy") && "slow_down") ||
giftable[Math.floor(Math.random() * giftable.length)].id; giftable[Math.floor(Math.random() * giftable.length)].id;
perks[randomGift] = 1; perks[randomGift] = 1;
} }
const runLevels = getRunLevels(params,randomGift); const runLevels = getRunLevels(params, randomGift);
console.log(randomGift, params,runLevels) console.log(randomGift, params, runLevels);
const gameState: GameState = { const gameState: GameState = {
runLevels, runLevels,
@ -129,7 +133,7 @@ export function newGameState(params: RunParams): GameState {
autoCleanUses: 0, autoCleanUses: 0,
...defaultSounds(), ...defaultSounds(),
rerolls: 0, rerolls: 0,
creative: sumOfValues(params.perks) > 1, creative: sumOfValues(params.perks) > 1 || params.level,
}; };
resetBalls(gameState); resetBalls(gameState);

View file

@ -36,6 +36,7 @@ export async function openScorePanel(gameState: GameState) {
} }
export function getNearestUnlockHTML(gameState: GameState) { export function getNearestUnlockHTML(gameState: GameState) {
if(gameState.creative)return ''
const unlockable = allLevels const unlockable = allLevels
.map((l, li) => { .map((l, li) => {
const { minScore, forbidden, required } = getLevelUnlockCondition(li); const { minScore, forbidden, required } = getLevelUnlockCondition(li);

View file

@ -3,9 +3,11 @@ import { PerkId, Upgrade } from "./types";
import { t } from "./i18n/i18n"; import { t } from "./i18n/i18n";
import { icons, upgrades } from "./loadGameData"; import { icons, upgrades } from "./loadGameData";
import { getSettingValue, getTotalScore, setSettingValue } from "./settings"; import { getSettingValue, getTotalScore, setSettingValue } from "./settings";
import {isOptionOn} from "./options";
export function startingPerkMenuButton() { export function startingPerkMenuButton() {
return { return {
disabled:isOptionOn('easy'),
icon: icons["icon:starting_perks"], icon: icons["icon:starting_perks"],
text: t("main_menu.starting_perks"), text: t("main_menu.starting_perks"),
help: t("main_menu.starting_perks_help"), help: t("main_menu.starting_perks_help"),