mirror of
https://gitlab.com/lecarore/breakout71.git
synced 2025-04-20 04:05:06 -04:00
Build 29068563
This commit is contained in:
parent
6ef13f2d19
commit
df8bfbb350
25 changed files with 384 additions and 336 deletions
|
@ -39,11 +39,13 @@ New players get confused as to which upgrades they have and why a side became re
|
|||
|
||||
## To do
|
||||
|
||||
- As soon as level condition is reached, lock it in and tell the user
|
||||
- change fortunate ball to work more like coin magnet, carrying the balls around to catch them at next puck bounce
|
||||
|
||||
## Done
|
||||
|
||||
- review the "next unlocks" in score and game over
|
||||
- As soon as upgrade condition is reached, toast
|
||||
- As soon as level condition is reached, lock it in and tell the user
|
||||
- extra life only saves your last ball, max 7 instead of 3
|
||||
- Don't use "RAZ" in French explanations.
|
||||
- explain ghost coin's slow down effect
|
||||
|
|
|
@ -29,8 +29,8 @@ android {
|
|||
applicationId = "me.lecaro.breakout"
|
||||
minSdk = 21
|
||||
targetSdk = 34
|
||||
versionCode = 29068232
|
||||
versionName = "29068232"
|
||||
versionCode = 29068563
|
||||
versionName = "29068563"
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables {
|
||||
useSupportLibrary = true
|
||||
|
|
File diff suppressed because one or more lines are too long
217
dist/index.html
vendored
217
dist/index.html
vendored
File diff suppressed because one or more lines are too long
|
@ -1,5 +1,5 @@
|
|||
// The version of the cache.
|
||||
const VERSION = "29068232";
|
||||
const VERSION = "29068563";
|
||||
|
||||
// The name of the cache
|
||||
const CACHE_NAME = `breakout-71-${VERSION}`;
|
||||
|
|
25
src/addToTotalScore.ts
Normal file
25
src/addToTotalScore.ts
Normal file
|
@ -0,0 +1,25 @@
|
|||
import { GameState } from "./types";
|
||||
import { icons, upgrades } from "./loadGameData";
|
||||
import { schedulGameSound } from "./gameStateMutators";
|
||||
import { toast } from "./toast";
|
||||
import { t } from "./i18n/i18n";
|
||||
import { getTotalScore, setSettingValue } from "./settings";
|
||||
|
||||
export function addToTotalScore(gameState: GameState, points: number) {
|
||||
if (gameState.creative) return;
|
||||
const pastScore = getTotalScore();
|
||||
const newScore = pastScore + points;
|
||||
setSettingValue("breakout_71_total_score", newScore);
|
||||
// Check unlocked upgrades
|
||||
upgrades.forEach((u) => {
|
||||
if (u.threshold > pastScore && u.threshold <= newScore) {
|
||||
schedulGameSound(gameState, "colorChange", 0, 1);
|
||||
toast(
|
||||
icons["icon:" + u.id] +
|
||||
"<strong>" +
|
||||
t("gameOver.unlocked_perk") +
|
||||
"</strong>",
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
|
@ -442,8 +442,8 @@
|
|||
},
|
||||
{
|
||||
"name": "icon:base_combo",
|
||||
"size": 5,
|
||||
"bricks": "ttttttytytttttttytytttttt",
|
||||
"size": 7,
|
||||
"bricks": "________bbbbb__bybyb__bbbbb__bybyb__bbbbb________",
|
||||
"svg": null
|
||||
},
|
||||
{
|
||||
|
|
|
@ -1 +1 @@
|
|||
"29068232"
|
||||
"29068563"
|
||||
|
|
|
@ -513,34 +513,31 @@ h2.histogram-title strong {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
.toast {
|
||||
position:fixed;
|
||||
left:0;
|
||||
top:40px;
|
||||
display:flex;
|
||||
align-items:center;
|
||||
gap:10px;
|
||||
opacity:0.8;
|
||||
background:black;
|
||||
border:1px solid white;
|
||||
border-radius:2px;
|
||||
padding-right:10px;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 40px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
opacity: 0.8;
|
||||
background: black;
|
||||
border: 1px solid white;
|
||||
border-radius: 2px;
|
||||
padding-right: 10px;
|
||||
pointer-events: none;
|
||||
animation: toast 800ms forwards;
|
||||
animation: toast forwards;
|
||||
}
|
||||
|
||||
@keyframes toast {
|
||||
0%{
|
||||
0%,
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform:translate(-20px, 0);
|
||||
transform: translate(-20px, -20px) scale(0.5);
|
||||
}
|
||||
10%,90%{
|
||||
10%,
|
||||
90% {
|
||||
opacity: 0.8;
|
||||
transform: none;
|
||||
}
|
||||
100%{
|
||||
opacity: 0;
|
||||
transform:translate(20px, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ import { getHistory } from "./gameOver";
|
|||
import { generateSaveFileContent } from "./generateSaveFileContent";
|
||||
import { runHistoryViewerMenuEntry } from "./runHistoryViewer";
|
||||
import { getNearestUnlockHTML, openScorePanel } from "./openScorePanel";
|
||||
import {monitorLevelsUnlocks} from "./monitorLevelsUnlocks";
|
||||
import { monitorLevelsUnlocks } from "./monitorLevelsUnlocks";
|
||||
|
||||
export async function play() {
|
||||
if (await applyFullScreenChoice()) return;
|
||||
|
@ -298,8 +298,8 @@ export async function openUpgradesPicker(gameState: GameState) {
|
|||
`,
|
||||
...actions,
|
||||
|
||||
getNearestUnlockHTML(gameState),
|
||||
pickedUpgradesHTMl(gameState),
|
||||
getNearestUnlockHTML(gameState),
|
||||
|
||||
`<div id="level-recording-container"></div>`,
|
||||
],
|
||||
|
@ -424,7 +424,7 @@ setInterval(() => {
|
|||
}, 1000);
|
||||
|
||||
setInterval(() => {
|
||||
monitorLevelsUnlocks(gameState)
|
||||
monitorLevelsUnlocks(gameState);
|
||||
}, 500);
|
||||
|
||||
window.addEventListener("visibilitychange", () => {
|
||||
|
|
|
@ -31,11 +31,7 @@ import {
|
|||
import { t } from "./i18n/i18n";
|
||||
import { icons } from "./loadGameData";
|
||||
|
||||
import {
|
||||
addToTotalScore,
|
||||
getCurrentMaxCoins,
|
||||
getCurrentMaxParticles,
|
||||
} from "./settings";
|
||||
import { getCurrentMaxCoins, getCurrentMaxParticles } from "./settings";
|
||||
import { background } from "./render";
|
||||
import { gameOver } from "./gameOver";
|
||||
import {
|
||||
|
@ -50,6 +46,7 @@ import {
|
|||
import { stopRecording } from "./recording";
|
||||
import { isOptionOn } from "./options";
|
||||
import { clamp, comboKeepingRate } from "./pure_functions";
|
||||
import { addToTotalScore } from "./addToTotalScore";
|
||||
|
||||
export function setMousePos(gameState: GameState, x: number) {
|
||||
gameState.puckPosition = x;
|
||||
|
@ -1508,7 +1505,8 @@ export function ballTick(gameState: GameState, ball: Ball, delta: number) {
|
|||
ball.y > ylimit &&
|
||||
ball.vy > 0 &&
|
||||
(ballIsUnderPuck ||
|
||||
(gameState.balls.length<2 && gameState.perks.extra_life &&
|
||||
(gameState.balls.length < 2 &&
|
||||
gameState.perks.extra_life &&
|
||||
ball.y > ylimit + gameState.puckHeight / 2))
|
||||
) {
|
||||
if (ballIsUnderPuck) {
|
||||
|
|
|
@ -1,9 +1,18 @@
|
|||
import {Ball, GameState, Level, PerkId, PerksMap, RunHistoryItem, UpgradeLike,} from "./types";
|
||||
import {icons, upgrades} from "./loadGameData";
|
||||
import {t} from "./i18n/i18n";
|
||||
import {clamp} from "./pure_functions";
|
||||
import {rawUpgrades} from "./upgrades";
|
||||
import {hashCode} from "./getLevelBackground";
|
||||
import {
|
||||
Ball,
|
||||
GameState,
|
||||
Level,
|
||||
PerkId,
|
||||
PerksMap,
|
||||
RunHistoryItem,
|
||||
UpgradeLike,
|
||||
} from "./types";
|
||||
import { icons, upgrades } from "./loadGameData";
|
||||
import { t } from "./i18n/i18n";
|
||||
import { clamp } from "./pure_functions";
|
||||
import { rawUpgrades } from "./upgrades";
|
||||
import { hashCode } from "./getLevelBackground";
|
||||
import { getTotalScore } from "./settings";
|
||||
|
||||
export function describeLevel(level: Level) {
|
||||
let bricks = 0,
|
||||
|
@ -74,7 +83,7 @@ export function getRowColIndex(gameState: GameState, row: number, col: number) {
|
|||
|
||||
export function getPossibleUpgrades(gameState: GameState) {
|
||||
return upgrades
|
||||
.filter((u) => gameState.totalScoreAtRunStart >= u.threshold)
|
||||
.filter((u) => getTotalScore() >= u.threshold)
|
||||
.filter((u) => !u?.requires || gameState.perks[u?.requires]);
|
||||
}
|
||||
|
||||
|
@ -272,8 +281,8 @@ export function highScoreText() {
|
|||
}
|
||||
|
||||
let excluded: Set<PerkId>;
|
||||
function isExcluded(id:PerkId){
|
||||
if(!excluded) {
|
||||
function isExcluded(id: PerkId) {
|
||||
if (!excluded) {
|
||||
excluded = new Set([
|
||||
"extra_levels",
|
||||
"extra_life",
|
||||
|
@ -287,7 +296,7 @@ function isExcluded(id:PerkId){
|
|||
if (u.requires) excluded.add(u.requires);
|
||||
});
|
||||
}
|
||||
return excluded.has(id)
|
||||
return excluded.has(id);
|
||||
}
|
||||
|
||||
export function getLevelUnlockCondition(levelIndex: number) {
|
||||
|
@ -297,7 +306,6 @@ export function getLevelUnlockCondition(levelIndex: number) {
|
|||
minScore = Math.max(-1000 + 100 * levelIndex, 0);
|
||||
|
||||
if (levelIndex > 20) {
|
||||
|
||||
const possibletargets = rawUpgrades
|
||||
.slice(0, Math.floor(levelIndex / 2))
|
||||
.map((u) => u)
|
||||
|
|
|
@ -2262,21 +2262,6 @@
|
|||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>continue_to_unlock</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>
|
||||
<name>get_upgrades_to_unlock</name>
|
||||
<description/>
|
||||
|
@ -2337,21 +2322,6 @@
|
|||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>title_looped</name>
|
||||
<description/>
|
||||
<comment/>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>true</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-FR</language>
|
||||
<approved>true</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>upcoming_levels</name>
|
||||
<description/>
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
"gameOver.stats.level_reached": "Level reached",
|
||||
"gameOver.stats.total_score": "Total score",
|
||||
"gameOver.stats.upgrades_applied": "Upgrades applied",
|
||||
"gameOver.unlocked_perk": "You just unlocked a perk",
|
||||
"gameOver.unlocked_perk": "Upgrade unlocked",
|
||||
"gameOver.unlocked_perk_plural": "You just unlocked {{count}} perks",
|
||||
"gameOver.win.summary": "This game is over. You stashed {{score}} coins. ",
|
||||
"gameOver.win.title": "You completed this game",
|
||||
|
@ -143,13 +143,11 @@
|
|||
"play.stats.levelMisses": "Missed shots, where you hit nothing",
|
||||
"play.stats.levelTime": "Level time",
|
||||
"play.stats.levelWallBounces": "Wall bounces",
|
||||
"score_panel.close_to_unlock": "You could unlock a level at the end of this run:",
|
||||
"score_panel.continue_to_unlock": "You are about to unlock level \"{{level}}\"",
|
||||
"score_panel.close_to_unlock": "Next level unlock :",
|
||||
"score_panel.get_upgrades_to_unlock": "Get {{missingUpgrades}} and score {{points}} more points to unlock level \"{{level}}\"",
|
||||
"score_panel.rerolls_count": "You have accumulated {{rerolls}} rerolls",
|
||||
"score_panel.score_to_unlock": "Score {{points}} more points to unlock level \"{{level}}\"",
|
||||
"score_panel.title": "{{score}} points at level {{level}}/{{max}} ",
|
||||
"score_panel.title_looped": "{{score}} points at level {{level}}/{{max}} of loop {{loop}}",
|
||||
"score_panel.upcoming_levels": "Upcoming levels :",
|
||||
"score_panel.upgrades_picked": "Upgrades picked so far : ",
|
||||
"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.",
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
"gameOver.stats.level_reached": "Niveau atteint",
|
||||
"gameOver.stats.total_score": "Score total",
|
||||
"gameOver.stats.upgrades_applied": "Mises à jour appliquées",
|
||||
"gameOver.unlocked_perk": "Vous avez débloqué une amélioration",
|
||||
"gameOver.unlocked_perk": "Amélioration débloquée",
|
||||
"gameOver.unlocked_perk_plural": "Vous avez débloqué {{count}} améliorations",
|
||||
"gameOver.win.summary": "Cette partie est terminée. Vous avez accumulé {{score}} pièces. ",
|
||||
"gameOver.win.title": "Vous avez terminé cette partie",
|
||||
|
@ -143,13 +143,11 @@
|
|||
"play.stats.levelMisses": "Tirs ratés, ou vous n'avez touché aucune brique",
|
||||
"play.stats.levelTime": "Durée du niveau",
|
||||
"play.stats.levelWallBounces": "Rebonds sur les murs",
|
||||
"score_panel.close_to_unlock": "Vous pourriez débloquer un niveau à la fin de cette partie :",
|
||||
"score_panel.continue_to_unlock": "Vous êtes sur le point de débloquer le niveau « {{level}} »",
|
||||
"score_panel.get_upgrades_to_unlock": "Obtenez {{missingUpgrades}} et marquez {{points}} points supplémentaires pour débloquer le niveau « {{level}} »",
|
||||
"score_panel.close_to_unlock": "Prochain niveau débloqué : ",
|
||||
"score_panel.get_upgrades_to_unlock": "Obtenez {{missingUpgrades}} et attrapez {{points}} pièces supplémentaires pour débloquer le niveau « {{level}} »",
|
||||
"score_panel.rerolls_count": "Vous avez accumulé {{rerolls}} rerolls",
|
||||
"score_panel.score_to_unlock": "Marquez {{points}} points supplémentaires pour débloquer le niveau « {{level}} »",
|
||||
"score_panel.score_to_unlock": "Attrapez {{points}} pièces supplémentaires pour débloquer le niveau « {{level}} »",
|
||||
"score_panel.title": "{{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.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, en dehors des parties de test.",
|
||||
|
|
|
@ -2,8 +2,8 @@ import { RunHistoryItem } from "./types";
|
|||
|
||||
import _appVersion from "./data/version.json";
|
||||
import { generateSaveFileContent } from "./generateSaveFileContent";
|
||||
import {getLevelUnlockCondition, reasonLevelIsLocked} from "./game_utils";
|
||||
import {allLevels} from "./loadGameData";
|
||||
import { getLevelUnlockCondition, reasonLevelIsLocked } from "./game_utils";
|
||||
import { allLevels } from "./loadGameData";
|
||||
|
||||
// The page will be reloaded if any migrations were run
|
||||
let migrationsRun = 0;
|
||||
|
@ -19,16 +19,16 @@ function migrate(name: string, cb: () => void) {
|
|||
}
|
||||
}
|
||||
}
|
||||
function afterMigration(){
|
||||
// Avoid a boot loop by setting the hash before reloading
|
||||
// We can't set the query string as it is used for other things
|
||||
if (migrationsRun && !window.location.hash) {
|
||||
window.location.hash = "#reloadAfterMigration";
|
||||
window.location.reload();
|
||||
}
|
||||
if (!migrationsRun) {
|
||||
window.location.hash = "";
|
||||
}
|
||||
function afterMigration() {
|
||||
// Avoid a boot loop by setting the hash before reloading
|
||||
// We can't set the query string as it is used for other things
|
||||
if (migrationsRun && !window.location.hash) {
|
||||
window.location.hash = "#reloadAfterMigration";
|
||||
window.location.reload();
|
||||
}
|
||||
if (!migrationsRun) {
|
||||
window.location.hash = "";
|
||||
}
|
||||
}
|
||||
|
||||
migrate("save_data_before_upgrade_to_" + _appVersion, () => {
|
||||
|
@ -103,25 +103,27 @@ migrate("compact_runs_data", () => {
|
|||
localStorage.setItem("breakout_71_runs_history", JSON.stringify(runsHistory));
|
||||
});
|
||||
|
||||
migrate("set_breakout_71_unlocked_levels"+_appVersion, () => {
|
||||
migrate("set_breakout_71_unlocked_levels" + _appVersion, () => {
|
||||
// We want to lock any level unlocked by an app upgrade too
|
||||
let runsHistory = JSON.parse(
|
||||
let runsHistory = JSON.parse(
|
||||
localStorage.getItem("breakout_71_runs_history") || "[]",
|
||||
) as RunHistoryItem[];
|
||||
|
||||
let breakout_71_unlocked_levels = JSON.parse(
|
||||
let breakout_71_unlocked_levels = JSON.parse(
|
||||
localStorage.getItem("breakout_71_unlocked_levels") || "[]",
|
||||
) as string[];
|
||||
|
||||
allLevels.filter((l,li)=>!reasonLevelIsLocked(li,runsHistory,false)).forEach(l=>{
|
||||
if(!breakout_71_unlocked_levels.includes(l.name)){
|
||||
breakout_71_unlocked_levels.push(l.name)
|
||||
allLevels
|
||||
.filter((l, li) => !reasonLevelIsLocked(li, runsHistory, false))
|
||||
.forEach((l) => {
|
||||
if (!breakout_71_unlocked_levels.includes(l.name)) {
|
||||
breakout_71_unlocked_levels.push(l.name);
|
||||
}
|
||||
})
|
||||
localStorage.setItem("breakout_71_unlocked_levels", JSON.stringify(breakout_71_unlocked_levels));
|
||||
});
|
||||
localStorage.setItem(
|
||||
"breakout_71_unlocked_levels",
|
||||
JSON.stringify(breakout_71_unlocked_levels),
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
afterMigration()
|
||||
afterMigration();
|
||||
|
|
|
@ -1,41 +1,50 @@
|
|||
import {GameState, UpgradeLike} from "./types";
|
||||
import {getSettingValue, setSettingValue} from "./settings";
|
||||
import {allLevels, icons} from "./loadGameData";
|
||||
import { getLevelUnlockCondition} from "./game_utils";
|
||||
import { GameState, UpgradeLike } from "./types";
|
||||
import { getSettingValue, setSettingValue } from "./settings";
|
||||
import { allLevels, icons } from "./loadGameData";
|
||||
import { getLevelUnlockCondition } from "./game_utils";
|
||||
|
||||
import {t} from "./i18n/i18n";
|
||||
import {toast} from "./toast";
|
||||
import {schedulGameSound} from "./gameStateMutators";
|
||||
import { t } from "./i18n/i18n";
|
||||
import { toast } from "./toast";
|
||||
import { schedulGameSound } from "./gameStateMutators";
|
||||
|
||||
let list: {minScore: number, forbidden: UpgradeLike[], required: UpgradeLike[]}[] ;
|
||||
let unlocked=new Set(getSettingValue('breakout_71_unlocked_levels',[]) as string[])
|
||||
let list: {
|
||||
minScore: number;
|
||||
forbidden: UpgradeLike[];
|
||||
required: UpgradeLike[];
|
||||
}[];
|
||||
let unlocked = new Set(
|
||||
getSettingValue("breakout_71_unlocked_levels", []) as string[],
|
||||
);
|
||||
|
||||
export function monitorLevelsUnlocks(gameState:GameState){
|
||||
if(gameState.creative) return;
|
||||
export function monitorLevelsUnlocks(gameState: GameState) {
|
||||
if (gameState.creative) return;
|
||||
|
||||
if(!list){
|
||||
list=allLevels.map((l,li)=>({
|
||||
name:l.name,li,l,
|
||||
...getLevelUnlockCondition(li)
|
||||
}))
|
||||
if (!list) {
|
||||
list = allLevels.map((l, li) => ({
|
||||
name: l.name,
|
||||
li,
|
||||
l,
|
||||
...getLevelUnlockCondition(li),
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
||||
list.forEach(({ name, minScore, forbidden, required, l }) => {
|
||||
// Already unlocked
|
||||
if (unlocked.has(name)) return;
|
||||
// Score not reached yet
|
||||
if (gameState.score < minScore) return;
|
||||
// We are missing a required perk
|
||||
if (required.find((id) => !gameState.perks[id])) return;
|
||||
// We have a forbidden perk
|
||||
if (forbidden.find((id) => gameState.perks[id])) return;
|
||||
// Level just got unlocked
|
||||
unlocked.add(name);
|
||||
setSettingValue(
|
||||
"breakout_71_unlocked_levels",
|
||||
getSettingValue("breakout_71_unlocked_levels", []).concat([name]),
|
||||
);
|
||||
|
||||
list.forEach(({name, minScore, forbidden, required, l})=>{
|
||||
// Already unlocked
|
||||
if(unlocked.has(name)) return
|
||||
// Score not reached yet
|
||||
if(gameState.score<minScore) return
|
||||
// We are missing a required perk
|
||||
if(required.find(id=>!gameState.perks[id])) return;
|
||||
// We have a forbidden perk
|
||||
if(forbidden.find(id=>gameState.perks[id])) return;
|
||||
// Level just got unlocked
|
||||
unlocked.add(name)
|
||||
setSettingValue('breakout_71_unlocked_levels', getSettingValue('breakout_71_unlocked_levels',[]).concat([name]))
|
||||
|
||||
toast(icons[name]+'<strong>'+t('unlocks.just_unlocked')+'</strong>')
|
||||
schedulGameSound(gameState, 'colorChange', 0, 1)
|
||||
|
||||
})
|
||||
}
|
||||
toast(icons[name] + "<strong>" + t("unlocks.just_unlocked") + "</strong>");
|
||||
schedulGameSound(gameState, "colorChange", 0, 1);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -42,14 +42,13 @@ export function getRunLevels(
|
|||
|
||||
export function newGameState(params: RunParams): GameState {
|
||||
const highScore = getHighScore();
|
||||
const totalScoreAtRunStart=getTotalScore()
|
||||
|
||||
const perks = { ...makeEmptyPerksMap(upgrades), ...(params?.perks || {}) };
|
||||
|
||||
let randomGift: PerkId | undefined = undefined;
|
||||
if (!sumOfValues(perks)) {
|
||||
const giftable = upgrades.filter(
|
||||
(u) => totalScoreAtRunStart >= u.threshold && isStartingPerk(u),
|
||||
(u) => getTotalScore() >= u.threshold && isStartingPerk(u),
|
||||
);
|
||||
|
||||
randomGift =
|
||||
|
@ -108,7 +107,6 @@ export function newGameState(params: RunParams): GameState {
|
|||
coinSize: 14,
|
||||
puckHeight: 20,
|
||||
|
||||
totalScoreAtRunStart,
|
||||
pauseUsesDuringRun: 0,
|
||||
keyboardPuckSpeed: 0,
|
||||
lastTick: performance.now(),
|
||||
|
|
|
@ -11,6 +11,8 @@ import {
|
|||
import { getCreativeModeWarning, getHistory } from "./gameOver";
|
||||
import { pause } from "./game";
|
||||
import { allLevels, icons } from "./loadGameData";
|
||||
import { firstWhere } from "./pure_functions";
|
||||
import { getSettingValue, getTotalScore } from "./settings";
|
||||
|
||||
export async function openScorePanel(gameState: GameState) {
|
||||
pause(true);
|
||||
|
@ -37,31 +39,34 @@ export async function openScorePanel(gameState: GameState) {
|
|||
|
||||
export function getNearestUnlockHTML(gameState: GameState) {
|
||||
if (gameState.creative) return "";
|
||||
const unlockable = allLevels
|
||||
.map((l, li) => {
|
||||
const { minScore, forbidden, required } = getLevelUnlockCondition(li);
|
||||
return {
|
||||
l,
|
||||
li,
|
||||
minScore,
|
||||
forbidden,
|
||||
required,
|
||||
missing: required.filter((u) => !gameState?.perks?.[u.id]),
|
||||
reason: reasonLevelIsLocked(li, getHistory(), false),
|
||||
};
|
||||
})
|
||||
.filter(
|
||||
({ reason, forbidden, missing }) =>
|
||||
// Level needs to be locked
|
||||
reason &&
|
||||
// we can't have a forbidden perk
|
||||
!forbidden.find((u) => gameState?.perks?.[u.id]) &&
|
||||
// All required upgrades need to be unlocked
|
||||
!missing.find((u) => u.threshold > gameState.totalScoreAtRunStart),
|
||||
);
|
||||
const unlocked = new Set(getSettingValue("breakout_71_unlocked_levels", []));
|
||||
const firstUnlockable = firstWhere(allLevels, (l, li) => {
|
||||
if (unlocked.has(l.name)) return;
|
||||
const reason = reasonLevelIsLocked(li, getHistory(), false);
|
||||
if (!reason) return;
|
||||
|
||||
const firstUnlockable =
|
||||
unlockable.find(({ missing }) => !missing.length) || unlockable[0];
|
||||
const { minScore, forbidden, required } = getLevelUnlockCondition(li);
|
||||
const missing = required.filter((u) => !gameState?.perks?.[u.id]);
|
||||
// we can't have a forbidden perk
|
||||
if (forbidden.find((u) => gameState?.perks?.[u.id])) {
|
||||
return;
|
||||
}
|
||||
|
||||
// All required upgrades need to be unlocked
|
||||
if (missing.find((u) => u.threshold > getTotalScore())) {
|
||||
return;
|
||||
}
|
||||
|
||||
return {
|
||||
l,
|
||||
li,
|
||||
minScore,
|
||||
forbidden,
|
||||
required,
|
||||
missing,
|
||||
reason,
|
||||
};
|
||||
});
|
||||
|
||||
if (!firstUnlockable) return "";
|
||||
let missingPoints = firstUnlockable.minScore - gameState.score;
|
||||
|
@ -74,12 +79,8 @@ export function getNearestUnlockHTML(gameState: GameState) {
|
|||
points: missingPoints,
|
||||
level: firstUnlockable.l.name,
|
||||
})) ||
|
||||
(missingPoints > 0 &&
|
||||
t("score_panel.score_to_unlock", {
|
||||
points: missingPoints,
|
||||
level: firstUnlockable.l.name,
|
||||
})) ||
|
||||
t("score_panel.continue_to_unlock", {
|
||||
t("score_panel.score_to_unlock", {
|
||||
points: missingPoints,
|
||||
level: firstUnlockable.l.name,
|
||||
});
|
||||
|
||||
|
|
|
@ -65,3 +65,13 @@ export function miniMarkDown(md: string) {
|
|||
)
|
||||
.join("\n");
|
||||
}
|
||||
|
||||
export function firstWhere<Input, Output>(
|
||||
arr: Input[],
|
||||
mapper: (item: Input, index: number) => Output | undefined,
|
||||
): Output | undefined {
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
const result = mapper(arr[i], i);
|
||||
if (typeof result !== "undefined") return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
// Settings
|
||||
|
||||
import { GameState } from "./types";
|
||||
|
||||
let cachedSettings: { [key: string]: unknown } = {};
|
||||
|
||||
export function getSettingValue<T>(key: string, defaultValue: T) {
|
||||
|
@ -29,11 +27,6 @@ export function getTotalScore() {
|
|||
return getSettingValue("breakout_71_total_score", 0);
|
||||
}
|
||||
|
||||
export function addToTotalScore(gameState: GameState, points: number) {
|
||||
if (!gameState.creative)
|
||||
setSettingValue("breakout_71_total_score", getTotalScore() + points);
|
||||
}
|
||||
|
||||
export function getCurrentMaxCoins() {
|
||||
return Math.pow(2, getSettingValue("max_coins", 1)) * 200;
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ export function startingPerkMenuButton() {
|
|||
};
|
||||
}
|
||||
export function isStartingPerk(u: Upgrade): boolean {
|
||||
|
||||
return getSettingValue("start_with_" + u.id, u.giftable);
|
||||
}
|
||||
|
||||
|
|
25
src/toast.ts
25
src/toast.ts
|
@ -1,8 +1,17 @@
|
|||
export function toast(html){
|
||||
const div =document.createElement('div')
|
||||
div.classList='toast'
|
||||
div.innerHTML=html
|
||||
document.body.appendChild(div)
|
||||
// TOast
|
||||
// setTimeout(()=>div.remove() , 500 )
|
||||
}
|
||||
let onScreen = 0;
|
||||
|
||||
export function toast(html) {
|
||||
const div = document.createElement("div");
|
||||
div.classList = "toast";
|
||||
div.innerHTML = html;
|
||||
const lasts = 1500 + onScreen * 200;
|
||||
div.style.animationDuration = lasts + "ms";
|
||||
div.style.top = 40 + onScreen * 50 + "px";
|
||||
|
||||
document.body.appendChild(div);
|
||||
onScreen++;
|
||||
setTimeout(() => {
|
||||
div.remove();
|
||||
onScreen--;
|
||||
}, lasts);
|
||||
}
|
||||
|
|
12
src/types.d.ts
vendored
12
src/types.d.ts
vendored
|
@ -1,5 +1,5 @@
|
|||
import {rawUpgrades} from "./upgrades";
|
||||
import {options} from "./options";
|
||||
import { rawUpgrades } from "./upgrades";
|
||||
import { options } from "./options";
|
||||
|
||||
export type colorString = string;
|
||||
|
||||
|
@ -258,7 +258,6 @@ export type GameState = {
|
|||
ballSize: number;
|
||||
coinSize: number;
|
||||
puckHeight: number;
|
||||
totalScoreAtRunStart: number;
|
||||
pauseUsesDuringRun: number;
|
||||
keyboardPuckSpeed: number;
|
||||
lastTick: number;
|
||||
|
@ -296,4 +295,9 @@ export type OptionDef = {
|
|||
help: string;
|
||||
};
|
||||
export type OptionId = keyof typeof options;
|
||||
export type UpgradeLike = { id: PerkId; name: string; requires: string };
|
||||
export type UpgradeLike = {
|
||||
id: PerkId;
|
||||
name: string;
|
||||
requires: string;
|
||||
threshold: number;
|
||||
};
|
||||
|
|
|
@ -17,20 +17,9 @@ export const rawUpgrades = [
|
|||
: t("upgrades.extra_life.help_plural", { lvl }),
|
||||
fullHelp: t("upgrades.extra_life.fullHelp"),
|
||||
},
|
||||
{
|
||||
requires: "",
|
||||
threshold: 0,
|
||||
id: "streak_shots",
|
||||
giftable: true,
|
||||
max: 1,
|
||||
name: t("upgrades.streak_shots.name"),
|
||||
help: (lvl: number) => t("upgrades.streak_shots.help", { lvl }),
|
||||
fullHelp: t("upgrades.streak_shots.fullHelp"),
|
||||
},
|
||||
|
||||
{
|
||||
requires: "",
|
||||
|
||||
threshold: 0,
|
||||
id: "base_combo",
|
||||
giftable: true,
|
||||
|
@ -42,7 +31,6 @@ export const rawUpgrades = [
|
|||
},
|
||||
{
|
||||
requires: "",
|
||||
|
||||
threshold: 0,
|
||||
giftable: false,
|
||||
id: "slow_down",
|
||||
|
@ -53,7 +41,6 @@ export const rawUpgrades = [
|
|||
},
|
||||
{
|
||||
requires: "",
|
||||
|
||||
threshold: 0,
|
||||
giftable: false,
|
||||
id: "bigger_puck",
|
||||
|
@ -64,7 +51,6 @@ export const rawUpgrades = [
|
|||
},
|
||||
{
|
||||
requires: "",
|
||||
|
||||
threshold: 0,
|
||||
giftable: false,
|
||||
id: "viscosity",
|
||||
|
@ -76,8 +62,32 @@ export const rawUpgrades = [
|
|||
},
|
||||
{
|
||||
requires: "",
|
||||
threshold: 50,
|
||||
giftable: false,
|
||||
id: "skip_last",
|
||||
max: 7,
|
||||
name: t("upgrades.skip_last.name"),
|
||||
help: (lvl: number) =>
|
||||
lvl == 1
|
||||
? t("upgrades.skip_last.help")
|
||||
: t("upgrades.skip_last.help_plural", { lvl }),
|
||||
fullHelp: t("upgrades.skip_last.fullHelp"),
|
||||
},
|
||||
{
|
||||
requires: "",
|
||||
threshold: 100,
|
||||
id: "streak_shots",
|
||||
giftable: true,
|
||||
max: 1,
|
||||
name: t("upgrades.streak_shots.name"),
|
||||
help: (lvl: number) => t("upgrades.streak_shots.help", { lvl }),
|
||||
fullHelp: t("upgrades.streak_shots.fullHelp"),
|
||||
},
|
||||
|
||||
threshold: 0,
|
||||
{
|
||||
requires: "",
|
||||
|
||||
threshold: 200,
|
||||
id: "left_is_lava",
|
||||
giftable: true,
|
||||
max: 1,
|
||||
|
@ -89,7 +99,7 @@ export const rawUpgrades = [
|
|||
{
|
||||
requires: "",
|
||||
|
||||
threshold: 0,
|
||||
threshold: 300,
|
||||
id: "right_is_lava",
|
||||
giftable: true,
|
||||
max: 1,
|
||||
|
@ -100,7 +110,7 @@ export const rawUpgrades = [
|
|||
{
|
||||
requires: "",
|
||||
|
||||
threshold: 0,
|
||||
threshold: 400,
|
||||
id: "top_is_lava",
|
||||
giftable: true,
|
||||
max: 1,
|
||||
|
@ -111,20 +121,6 @@ export const rawUpgrades = [
|
|||
{
|
||||
requires: "",
|
||||
|
||||
threshold: 0,
|
||||
giftable: false,
|
||||
id: "skip_last",
|
||||
max: 7,
|
||||
name: t("upgrades.skip_last.name"),
|
||||
help: (lvl: number) =>
|
||||
lvl == 1
|
||||
? t("upgrades.skip_last.help")
|
||||
: t("upgrades.skip_last.help_plural", { lvl }),
|
||||
fullHelp: t("upgrades.skip_last.fullHelp"),
|
||||
},
|
||||
{
|
||||
requires: "",
|
||||
|
||||
threshold: 500,
|
||||
id: "telekinesis",
|
||||
giftable: true,
|
||||
|
@ -138,8 +134,7 @@ export const rawUpgrades = [
|
|||
},
|
||||
{
|
||||
requires: "",
|
||||
|
||||
threshold: 1000,
|
||||
threshold: 700,
|
||||
giftable: false,
|
||||
id: "coin_magnet",
|
||||
max: 3,
|
||||
|
@ -153,7 +148,7 @@ export const rawUpgrades = [
|
|||
{
|
||||
requires: "",
|
||||
|
||||
threshold: 1500,
|
||||
threshold: 800,
|
||||
id: "multiball",
|
||||
giftable: true,
|
||||
max: 6,
|
||||
|
@ -164,7 +159,7 @@ export const rawUpgrades = [
|
|||
{
|
||||
requires: "",
|
||||
|
||||
threshold: 2000,
|
||||
threshold: 1000,
|
||||
giftable: false,
|
||||
id: "smaller_puck",
|
||||
max: 2,
|
||||
|
@ -177,8 +172,7 @@ export const rawUpgrades = [
|
|||
},
|
||||
{
|
||||
requires: "",
|
||||
|
||||
threshold: 3000,
|
||||
threshold: 1500,
|
||||
id: "pierce",
|
||||
giftable: false,
|
||||
max: 3,
|
||||
|
@ -189,7 +183,7 @@ export const rawUpgrades = [
|
|||
{
|
||||
requires: "",
|
||||
|
||||
threshold: 4000,
|
||||
threshold: 2000,
|
||||
id: "picky_eater",
|
||||
giftable: true,
|
||||
max: 1,
|
||||
|
@ -200,7 +194,7 @@ export const rawUpgrades = [
|
|||
{
|
||||
requires: "",
|
||||
|
||||
threshold: 5000,
|
||||
threshold: 2500,
|
||||
giftable: false,
|
||||
id: "metamorphosis",
|
||||
max: 1,
|
||||
|
@ -211,7 +205,7 @@ export const rawUpgrades = [
|
|||
{
|
||||
requires: "",
|
||||
|
||||
threshold: 6000,
|
||||
threshold: 3000,
|
||||
id: "compound_interest",
|
||||
giftable: true,
|
||||
max: 1,
|
||||
|
@ -221,7 +215,7 @@ export const rawUpgrades = [
|
|||
},
|
||||
{
|
||||
requires: "",
|
||||
threshold: 7000,
|
||||
threshold: 4000,
|
||||
id: "hot_start",
|
||||
giftable: true,
|
||||
max: 3,
|
||||
|
@ -236,7 +230,7 @@ export const rawUpgrades = [
|
|||
{
|
||||
requires: "",
|
||||
|
||||
threshold: 9000,
|
||||
threshold: 6000,
|
||||
id: "sapper",
|
||||
giftable: false,
|
||||
max: 7,
|
||||
|
@ -250,7 +244,7 @@ export const rawUpgrades = [
|
|||
{
|
||||
requires: "",
|
||||
|
||||
threshold: 11000,
|
||||
threshold: 9000,
|
||||
id: "bigger_explosions",
|
||||
giftable: false,
|
||||
max: 1,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue