mirror of
https://gitlab.com/lecarore/breakout71.git
synced 2025-05-04 10:49:13 -04:00
wip
This commit is contained in:
parent
4c66cc820c
commit
cee5c6bc60
28 changed files with 948 additions and 344 deletions
24
Readme.md
24
Readme.md
|
@ -13,25 +13,25 @@ Break colourful bricks, catch bouncing coins and select powerful upgrades !
|
|||
|
||||
# Changelog
|
||||
## To do
|
||||
- instead of bouncing the ball,loosing a life pauses the game (with coins still in the air)
|
||||
|
||||
- rewoks perks choices
|
||||
- goal : limit perk fatigue, avoid wall of texts, clarify challenges, allow users to skip
|
||||
- remove rerolls
|
||||
## Done
|
||||
- rewoked perks choices to limit perk fatigue, avoid wall of texts, allow users to skip
|
||||
- removed rerolls
|
||||
- offer to pick 1 upgrade out of 3 choices
|
||||
- playing well adds 1 upgrade and 1 choice
|
||||
- playing even better adds 1 choice
|
||||
- "more choices" perk adds 1 choice
|
||||
- you can skip the upgrades and they'll be saved for later
|
||||
- you can pick an upgrade multiple time to level it up
|
||||
- you can skip the upgrades and they'll be saved for later as extra lives
|
||||
- you can pick an upgrade multiple time to level it up
|
||||
- bigger "level X of Y cleared"
|
||||
- continue to level X/Y as button
|
||||
- lives = upgrade points
|
||||
- clarify challenges
|
||||
- missed challenges show as greyed out choices (with unlock condition).
|
||||
- bigger "level X or Y cleared", continue to level X/Y as button
|
||||
- lives = upgrade points
|
||||
- instead of bouncing the ball,loosing a life pauses the game (with coins still in the air)
|
||||
|
||||
## Done
|
||||
|
||||
- somehow score clicks didn't register while the game was playing, that's solved
|
||||
|
||||
- upgrades list now uses numbers instead of bars, looks better with limitless
|
||||
- somehow score clicks didn't register while the game was playing, that's solved
|
||||
- Fix : click tooltip to open on mobile, click anywhere to close
|
||||
- Can't press help buttons in Creative Menu
|
||||
|
||||
|
|
|
@ -29,8 +29,8 @@ android {
|
|||
applicationId = "me.lecaro.breakout"
|
||||
minSdk = 21
|
||||
targetSdk = 34
|
||||
versionCode = 29097764
|
||||
versionName = "29097764"
|
||||
versionCode = 29099215
|
||||
versionName = "29099215"
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables {
|
||||
useSupportLibrary = true
|
||||
|
|
File diff suppressed because one or more lines are too long
127
dist/index.html
vendored
127
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 = "29097764";
|
||||
const VERSION = "29099215";
|
||||
|
||||
// The name of the cache
|
||||
const CACHE_NAME = `breakout-71-${VERSION}`;
|
||||
|
|
|
@ -101,8 +101,8 @@ export async function asyncAlert<t>({
|
|||
popup.appendChild(addto);
|
||||
}
|
||||
|
||||
const buttonWrap = document.createElement("div")
|
||||
addto.appendChild(buttonWrap)
|
||||
const buttonWrap = document.createElement("div");
|
||||
addto.appendChild(buttonWrap);
|
||||
|
||||
const {
|
||||
text,
|
||||
|
@ -136,7 +136,9 @@ ${icon}
|
|||
}
|
||||
|
||||
button.className =
|
||||
className + (lastClickedItemIndex === index ? " needs-focus" : "")+' choice-button';
|
||||
className +
|
||||
(lastClickedItemIndex === index ? " needs-focus" : "") +
|
||||
" choice-button";
|
||||
|
||||
buttonWrap.appendChild(button);
|
||||
|
||||
|
|
|
@ -1392,66 +1392,71 @@
|
|||
"credit": "Suggested by Big Goober. The red ghost, Blinky, from the arcade game \"Pac Man\""
|
||||
},
|
||||
{
|
||||
"color": "#000000",
|
||||
"name": "Fish",
|
||||
"size": 11,
|
||||
"bricks": "______________________________________________bbbb______tttttt___btgttbttt_bbtttttbtttb___ttbttt_bb_tttttt___b___________",
|
||||
"name": "Fish",
|
||||
"credit": "A fish based on the fish discord emoji. Suggested by Big Goober. "
|
||||
},
|
||||
{
|
||||
"color": "#115988",
|
||||
"name": "Spider",
|
||||
"size": 7,
|
||||
"bricks": "_l_____Sgg____ggSgBB_gSgBBBBSgggggg_gg___g_g_g_g_",
|
||||
"name": "Spider",
|
||||
"credit": "Suggested by obigre."
|
||||
},
|
||||
{
|
||||
"color": "#115988",
|
||||
"name": "Gliders",
|
||||
"size": 8,
|
||||
"bricks": "g_g______gg___l__g__l_l______ll__c________cc__W__cc____W_____WWW",
|
||||
"name": "Gliders",
|
||||
"credit": "iSuggested by obigre. Inspired by Conway's gliders"
|
||||
},
|
||||
{
|
||||
"color": "#000000",
|
||||
"name": "Lone island",
|
||||
"size": 8,
|
||||
"bricks": "C__________kkk____kkOkk___kkkO_k_k_k_O_______O______CC__tttCCCCt",
|
||||
"name": "Lone island",
|
||||
"credit": "Suggested by obigre. Which game would you take there ?"
|
||||
},
|
||||
{
|
||||
"color": "#000000",
|
||||
"name": "Spacewyrm Jon",
|
||||
"size": 8,
|
||||
"bricks": "___PPP____PPPP____SSSP____WPWP_P__PPP_PP___PP_____yPPy__bWWyyWWb",
|
||||
"name": "Spacewyrm Jon",
|
||||
"credit": "Suggested by obigre. The invertebrate hero with a gun"
|
||||
},
|
||||
{
|
||||
"color": "#115988",
|
||||
"name": "Taijitu",
|
||||
"size": 7,
|
||||
"bricks": "_WWWWW_W__WWWWgg__WBWggg_WWWgBg__WWgggg__g_ggggg_",
|
||||
"name": "Taijitu",
|
||||
"credit": "Suggested by obigre. Yin and yang fishes"
|
||||
},
|
||||
{
|
||||
"color": "#115988",
|
||||
"name": "Egg pan",
|
||||
"size": 5,
|
||||
"bricks": "WWWWgWWyWggWWWggggg____g_",
|
||||
"name": "Egg pan",
|
||||
"credit": "Suggested by obigre. Fried and tasty"
|
||||
},
|
||||
{
|
||||
"color": "#000000",
|
||||
"name": "Inception",
|
||||
"size": 20,
|
||||
"bricks": "____llllllllllll________lbbbbbb____l________lbbbbbb____l________l____bbtt__l________l____bbtt__l________l__bbtttt__l________l__bbtttt__l________l______tt__l________l____y_tt__l________l______ttttl________l_____yttttl________l__W_______l________l_____y____l________l__y_y_____l________l_y___y_y__l________l__________l________l___WWW____l________llllllllllll____________________________________________",
|
||||
"name": "Inception",
|
||||
"credit": "Breakout 71 within Breakout 71. By Noodlemire"
|
||||
},
|
||||
{
|
||||
"color": "#000000",
|
||||
"name": "Chess",
|
||||
"size": 21,
|
||||
"bricks": "_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_W_",
|
||||
"name": "Chess",
|
||||
"credit": "White n black by Topenvy"
|
||||
},
|
||||
{
|
||||
"name": "icon:locked",
|
||||
"size": 8,
|
||||
"bricks": "__eeee____e__e____e__e____e__e___llllll__llllll__llllll__llllll_",
|
||||
"svg": null,
|
||||
"color": ""
|
||||
},
|
||||
{
|
||||
"color": "#000000",
|
||||
"size": 8,
|
||||
"bricks": "_________GGWWrr__GGWWrr__GGWWrr__GGWWrr_________________________",
|
||||
"name": "italy",
|
||||
"credit": "italia by Topenvy"
|
||||
}
|
||||
]
|
||||
|
|
|
@ -767,5 +767,10 @@
|
|||
"required": ["respawn", "wrap_left", "sapper"],
|
||||
"forbidden": ["shocks", "metamorphosis", "pierce"],
|
||||
"minScore": 14200
|
||||
},
|
||||
"italy": {
|
||||
"required": ["sticky_coins", "pierce_color", "left_is_lava"],
|
||||
"forbidden": ["transparency", "etherealcoins", "three_cushion"],
|
||||
"minScore": 14300
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
"29097764"
|
||||
"29099215"
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
* {
|
||||
font-family: Courier New,
|
||||
Courier,
|
||||
Lucida Sans Typewriter,
|
||||
Lucida Typewriter,
|
||||
monospace;
|
||||
font-family:
|
||||
Courier New,
|
||||
Courier,
|
||||
Lucida Sans Typewriter,
|
||||
Lucida Typewriter,
|
||||
monospace;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
|
@ -73,7 +74,6 @@ canvas:not(#game) {
|
|||
line-height: 20px;
|
||||
box-shadow: 0 2px #fff;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#score {
|
||||
|
@ -210,7 +210,6 @@ body:not(.has-alert-open) #popup {
|
|||
opacity: 0.2;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
> button[data-help-content] {
|
||||
|
@ -425,7 +424,7 @@ h2.histogram-title strong {
|
|||
|
||||
.level {
|
||||
color: #000;
|
||||
background: #FFF;
|
||||
background: #fff;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
font-size: 12px;
|
||||
|
@ -435,7 +434,7 @@ h2.histogram-title strong {
|
|||
position: relative;
|
||||
//top: -3px;
|
||||
font-weight: bold;
|
||||
border: 1px solid #FFF;
|
||||
border: 1px solid #fff;
|
||||
//margin-left: 5px;
|
||||
|
||||
> span {
|
||||
|
@ -443,38 +442,34 @@ h2.histogram-title strong {
|
|||
position: relative;
|
||||
|
||||
&:first-child {
|
||||
|
||||
padding: 3px 6px 0 2px;
|
||||
color: #000;
|
||||
background: #FFF;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
|
||||
padding: 3px 3px 0 2px;
|
||||
color: #FFF;
|
||||
color: #fff;
|
||||
background: #000;
|
||||
|
||||
&:before {
|
||||
content: "";
|
||||
display: block;
|
||||
background: black;;
|
||||
background: black;
|
||||
position: absolute;
|
||||
left: -2px;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 4px;
|
||||
transform: skewX(-10deg)
|
||||
transform: skewX(-10deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.capped {
|
||||
> span:first-child {
|
||||
|
||||
color: #FFF;
|
||||
color: #fff;
|
||||
background: #000;
|
||||
|
||||
}
|
||||
|
||||
> span:last-child::before {
|
||||
|
@ -511,16 +506,22 @@ h2.histogram-title strong {
|
|||
font-weight: bold;
|
||||
padding: 5px;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 4px 0 gold, 0 4px 10px black;
|
||||
box-shadow:
|
||||
0 4px 0 gold,
|
||||
0 4px 10px black;
|
||||
border: 2px solid white;
|
||||
cursor: pointer;
|
||||
text-shadow: 0 0 4px rgba(0, 0, 0, 0.3);
|
||||
user-select: none;
|
||||
&:active{
|
||||
transform: translate(0,4px);
|
||||
box-shadow: 0 0px 0 gold, 0 0px 10px black;
|
||||
user-select: none;
|
||||
&:active {
|
||||
transform: translate(0, 4px);
|
||||
box-shadow:
|
||||
0 0px 0 gold,
|
||||
0 0px 10px black;
|
||||
}
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
transition:
|
||||
transform 0.2s,
|
||||
box-shadow 0.2s;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -614,8 +615,9 @@ h2.histogram-title strong {
|
|||
border-radius: 2px;
|
||||
padding-right: 10px;
|
||||
pointer-events: none;
|
||||
transition: opacity 200ms,
|
||||
transform 200ms;
|
||||
transition:
|
||||
opacity 200ms,
|
||||
transform 200ms;
|
||||
z-index: 7;
|
||||
|
||||
&.hidden {
|
||||
|
|
113
src/game.ts
113
src/game.ts
|
@ -1,6 +1,22 @@
|
|||
import {allLevels, allLevelsAndIcons, appVersion, icons, upgrades,} from "./loadGameData";
|
||||
import {Ball, Coin, GameState, LightFlash, OptionId, ParticleFlash, PerksMap, RunParams, TextFlash,} from "./types";
|
||||
import {getAudioContext, playPendingSounds} from "./sounds";
|
||||
import {
|
||||
allLevels,
|
||||
allLevelsAndIcons,
|
||||
appVersion,
|
||||
icons,
|
||||
upgrades,
|
||||
} from "./loadGameData";
|
||||
import {
|
||||
Ball,
|
||||
Coin,
|
||||
GameState,
|
||||
LightFlash,
|
||||
OptionId,
|
||||
ParticleFlash,
|
||||
PerksMap,
|
||||
RunParams,
|
||||
TextFlash,
|
||||
} from "./types";
|
||||
import { getAudioContext, playPendingSounds } from "./sounds";
|
||||
import {
|
||||
currentLevelInfo,
|
||||
describeLevel,
|
||||
|
@ -12,7 +28,7 @@ import {
|
|||
} from "./game_utils";
|
||||
|
||||
import "./PWA/sw_loader";
|
||||
import {getCurrentLang, languages, t} from "./i18n/i18n";
|
||||
import { getCurrentLang, languages, t } from "./i18n/i18n";
|
||||
import {
|
||||
commitSettingsChangesToLocalStorage,
|
||||
cycleMaxCoins,
|
||||
|
@ -30,25 +46,42 @@ import {
|
|||
setLevel,
|
||||
setMousePos,
|
||||
} from "./gameStateMutators";
|
||||
import {backgroundCanvas, gameCanvas, getHaloScale, haloCanvas, render, 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 {clamp, miniMarkDown,} from "./pure_functions";
|
||||
import {helpMenuEntry} from "./help";
|
||||
import {creativeMode} from "./creative";
|
||||
import {hideAnyTooltip, setupTooltips} from "./tooltip";
|
||||
import {startingPerkMenuButton} from "./startingPerks";
|
||||
import {
|
||||
backgroundCanvas,
|
||||
gameCanvas,
|
||||
getHaloScale,
|
||||
haloCanvas,
|
||||
render,
|
||||
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 { clamp, miniMarkDown } from "./pure_functions";
|
||||
import { helpMenuEntry } from "./help";
|
||||
import { creativeMode } from "./creative";
|
||||
import { hideAnyTooltip, setupTooltips } from "./tooltip";
|
||||
import { startingPerkMenuButton } from "./startingPerks";
|
||||
import "./migrations";
|
||||
import {getHistory} from "./gameOver";
|
||||
import {generateSaveFileContent} from "./generateSaveFileContent";
|
||||
import {runHistoryViewerMenuEntry} from "./runHistoryViewer";
|
||||
import {openScorePanel} from "./openScorePanel";
|
||||
import {monitorLevelsUnlocks} from "./monitorLevelsUnlocks";
|
||||
import {levelEditorMenuEntry} from "./levelEditor";
|
||||
import {categories} from "./upgrades";
|
||||
import {reasonLevelIsLocked} from "./get_level_unlock_condition";
|
||||
import { getHistory } from "./gameOver";
|
||||
import { generateSaveFileContent } from "./generateSaveFileContent";
|
||||
import { runHistoryViewerMenuEntry } from "./runHistoryViewer";
|
||||
import { openScorePanel } from "./openScorePanel";
|
||||
import { monitorLevelsUnlocks } from "./monitorLevelsUnlocks";
|
||||
import { levelEditorMenuEntry } from "./levelEditor";
|
||||
import { categories } from "./upgrades";
|
||||
import { reasonLevelIsLocked } from "./get_level_unlock_condition";
|
||||
|
||||
export async function play() {
|
||||
if (await applyFullScreenChoice()) return;
|
||||
|
@ -369,43 +402,37 @@ setInterval(() => {
|
|||
monitorLevelsUnlocks(gameState);
|
||||
}, 500);
|
||||
|
||||
|
||||
document.addEventListener("visibilitychange", () => {
|
||||
if (document.hidden) {
|
||||
pause(true);
|
||||
}
|
||||
});
|
||||
if(getSettingValue('score-opened',0 )<3){
|
||||
scoreDisplay.classList.add('button-look')
|
||||
if (getSettingValue("score-opened", 0) < 3) {
|
||||
scoreDisplay.classList.add("button-look");
|
||||
}
|
||||
const menuDisplay = document.getElementById("menu") as HTMLButtonElement;
|
||||
if(getSettingValue('menu-opened',0 )<3){
|
||||
menuDisplay.classList.add('button-look')
|
||||
if (getSettingValue("menu-opened", 0) < 3) {
|
||||
menuDisplay.classList.add("button-look");
|
||||
}
|
||||
|
||||
function scoreOpen(e){
|
||||
e.preventDefault();
|
||||
function scoreOpen(e) {
|
||||
e.preventDefault();
|
||||
if (!alertsOpen) {
|
||||
setSettingValue('score-opened',getSettingValue('score-opened',0 )+1 )
|
||||
setSettingValue("score-opened", getSettingValue("score-opened", 0) + 1);
|
||||
openScorePanel(gameState);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
scoreDisplay.addEventListener("click",scoreOpen);
|
||||
scoreDisplay.addEventListener("click", scoreOpen);
|
||||
scoreDisplay.addEventListener("mousedown", scoreOpen);
|
||||
|
||||
menuDisplay.addEventListener(
|
||||
"click",
|
||||
(e) => {
|
||||
e.preventDefault();
|
||||
if (!alertsOpen) {
|
||||
|
||||
setSettingValue('menu-opened',getSettingValue('menu-opened',0 )+1 )
|
||||
openMainMenu();
|
||||
}
|
||||
},
|
||||
);
|
||||
menuDisplay.addEventListener("click", (e) => {
|
||||
e.preventDefault();
|
||||
if (!alertsOpen) {
|
||||
setSettingValue("menu-opened", getSettingValue("menu-opened", 0) + 1);
|
||||
openMainMenu();
|
||||
}
|
||||
});
|
||||
|
||||
export const creativeModeThreshold = Math.max(
|
||||
...upgrades.map((u) => u.threshold),
|
||||
|
|
|
@ -29,18 +29,31 @@ import {
|
|||
telekinesisEffectRate,
|
||||
yoyoEffectRate,
|
||||
} from "./game_utils";
|
||||
import {t} from "./i18n/i18n";
|
||||
import { t } from "./i18n/i18n";
|
||||
|
||||
import {getCurrentMaxCoins, getCurrentMaxParticles} from "./settings";
|
||||
import {background} from "./render";
|
||||
import {gameOver} from "./gameOver";
|
||||
import {brickIndex, fitSize, gameState, hasBrick, hitsSomething, pause, startComputerControlledGame,} from "./game";
|
||||
import {stopRecording} from "./recording";
|
||||
import {isOptionOn} from "./options";
|
||||
import {ballTransparency, clamp, coinsBoostedCombo, comboKeepingRate,} from "./pure_functions";
|
||||
import {addToTotalScore} from "./addToTotalScore";
|
||||
import {hashCode} from "./getLevelBackground";
|
||||
import {openUpgradesPicker} from "./openUpgradesPicker";
|
||||
import { getCurrentMaxCoins, getCurrentMaxParticles } from "./settings";
|
||||
import { background } from "./render";
|
||||
import { gameOver } from "./gameOver";
|
||||
import {
|
||||
brickIndex,
|
||||
fitSize,
|
||||
gameState,
|
||||
hasBrick,
|
||||
hitsSomething,
|
||||
pause,
|
||||
startComputerControlledGame,
|
||||
} from "./game";
|
||||
import { stopRecording } from "./recording";
|
||||
import { isOptionOn } from "./options";
|
||||
import {
|
||||
ballTransparency,
|
||||
clamp,
|
||||
coinsBoostedCombo,
|
||||
comboKeepingRate,
|
||||
} from "./pure_functions";
|
||||
import { addToTotalScore } from "./addToTotalScore";
|
||||
import { hashCode } from "./getLevelBackground";
|
||||
import { openUpgradesPicker } from "./openUpgradesPicker";
|
||||
|
||||
export function setMousePos(gameState: GameState, x: number) {
|
||||
if (gameState.startParams.computer_controlled) return;
|
||||
|
@ -606,7 +619,6 @@ export async function setLevel(gameState: GameState, l: number) {
|
|||
gameState.currentLevel = l;
|
||||
gameState.level = gameState.runLevels[l % gameState.runLevels.length];
|
||||
|
||||
|
||||
if (l > 0) {
|
||||
await openUpgradesPicker(gameState);
|
||||
}
|
||||
|
@ -956,8 +968,9 @@ export function gameStateTick(
|
|||
}
|
||||
}
|
||||
|
||||
if (( window.location.search.includes("skipplaying") ||
|
||||
remainingBricks <= gameState.perks.skip_last) &&
|
||||
if (
|
||||
(window.location.search.includes("skipplaying") ||
|
||||
remainingBricks <= gameState.perks.skip_last) &&
|
||||
!gameState.autoCleanUses
|
||||
) {
|
||||
gameState.bricks.forEach((type, index) => {
|
||||
|
|
|
@ -1,9 +1,17 @@
|
|||
import {Ball, Coin, GameState, Level, PerkId, PerksMap, Upgrade} from "./types";
|
||||
import {icons, upgrades} from "./loadGameData";
|
||||
import {t} from "./i18n/i18n";
|
||||
import {clamp} from "./pure_functions";
|
||||
import {getSettingValue, getTotalScore} from "./settings";
|
||||
import {isOptionOn} from "./options";
|
||||
import {
|
||||
Ball,
|
||||
Coin,
|
||||
GameState,
|
||||
Level,
|
||||
PerkId,
|
||||
PerksMap,
|
||||
Upgrade,
|
||||
} from "./types";
|
||||
import { icons, upgrades } from "./loadGameData";
|
||||
import { t } from "./i18n/i18n";
|
||||
import { clamp } from "./pure_functions";
|
||||
import { getSettingValue, getTotalScore } from "./settings";
|
||||
import { isOptionOn } from "./options";
|
||||
|
||||
export function describeLevel(level: Level) {
|
||||
let bricks = 0,
|
||||
|
@ -99,10 +107,13 @@ export function max_levels(gameState: GameState) {
|
|||
return 7 + gameState.perks.extra_levels;
|
||||
}
|
||||
|
||||
export function upgradeLevelAndMaxDisplay(upgrade: Upgrade, gameState: GameState) {
|
||||
export function upgradeLevelAndMaxDisplay(
|
||||
upgrade: Upgrade,
|
||||
gameState: GameState,
|
||||
) {
|
||||
const lvl = gameState.perks[upgrade.id];
|
||||
const max = upgrade.max + gameState.perks.limitless
|
||||
return `<span class="level ${lvl < max ? 'can-upgrade' : 'capped'}"><span>${lvl}</span><span>${max}</span></span>`
|
||||
const max = upgrade.max + gameState.perks.limitless;
|
||||
return `<span class="level ${lvl < max ? "can-upgrade" : "capped"}"><span>${lvl}</span><span>${max}</span></span>`;
|
||||
}
|
||||
|
||||
export function pickedUpgradesHTMl(gameState: GameState) {
|
||||
|
@ -111,7 +122,6 @@ export function pickedUpgradesHTMl(gameState: GameState) {
|
|||
.map((u) => {
|
||||
const newMax = Math.max(0, u.max + gameState.perks.limitless);
|
||||
|
||||
|
||||
const state = (gameState.perks[u.id] && 1) || (!newMax && 2) || 3;
|
||||
return {
|
||||
state,
|
||||
|
@ -320,6 +330,10 @@ export function hoursSpentPlaying() {
|
|||
}
|
||||
}
|
||||
|
||||
export function escapeAttribute(str:String){
|
||||
return str.replace(/&/gi,'&').replace(/</gi,'<').replace(/"/gi,'"').replace(/'/gi,''')
|
||||
}
|
||||
export function escapeAttribute(str: String) {
|
||||
return str
|
||||
.replace(/&/gi, "&")
|
||||
.replace(/</gi, "<")
|
||||
.replace(/"/gi, """)
|
||||
.replace(/'/gi, "'");
|
||||
}
|
||||
|
|
|
@ -71,6 +71,16 @@
|
|||
"level_up.go": "",
|
||||
"level_up.instructions": "",
|
||||
"level_up.maxed_upgrade": "",
|
||||
"level_up.missed.best": "",
|
||||
"level_up.missed.catchRate.best": "",
|
||||
"level_up.missed.catchRate.good": "",
|
||||
"level_up.missed.good": "",
|
||||
"level_up.missed.levelMisses.best": "",
|
||||
"level_up.missed.levelMisses.good": "",
|
||||
"level_up.missed.levelTime.best": "",
|
||||
"level_up.missed.levelTime.good": "",
|
||||
"level_up.missed.levelWallBounces.best": "",
|
||||
"level_up.missed.levelWallBounces.good": "",
|
||||
"level_up.no_points": "",
|
||||
"level_up.pick": "",
|
||||
"level_up.pick_upgrade": "",
|
||||
|
|
|
@ -2597,6 +2597,381 @@
|
|||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<folder_node>
|
||||
<name>missed</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>best</name>
|
||||
<description/>
|
||||
<comment/>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>ar-LB</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>de-DE</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-CL</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-FR</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>ru-RU</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>tr-TR</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<folder_node>
|
||||
<name>catchRate</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>best</name>
|
||||
<description/>
|
||||
<comment/>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>ar-LB</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>de-DE</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-CL</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-FR</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>ru-RU</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>tr-TR</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>good</name>
|
||||
<description/>
|
||||
<comment/>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>ar-LB</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>de-DE</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-CL</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-FR</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>ru-RU</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>tr-TR</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
<concept_node>
|
||||
<name>good</name>
|
||||
<description/>
|
||||
<comment/>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>ar-LB</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>de-DE</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-CL</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-FR</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>ru-RU</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>tr-TR</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<folder_node>
|
||||
<name>levelMisses</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>best</name>
|
||||
<description/>
|
||||
<comment/>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>ar-LB</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>de-DE</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-CL</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-FR</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>ru-RU</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>tr-TR</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>good</name>
|
||||
<description/>
|
||||
<comment/>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>ar-LB</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>de-DE</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-CL</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-FR</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>ru-RU</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>tr-TR</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
<name>levelTime</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>best</name>
|
||||
<description/>
|
||||
<comment/>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>ar-LB</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>de-DE</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-CL</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-FR</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>ru-RU</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>tr-TR</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>good</name>
|
||||
<description/>
|
||||
<comment/>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>ar-LB</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>de-DE</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-CL</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-FR</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>ru-RU</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>tr-TR</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
<name>levelWallBounces</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>best</name>
|
||||
<description/>
|
||||
<comment/>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>ar-LB</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>de-DE</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-CL</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-FR</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>ru-RU</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>tr-TR</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>good</name>
|
||||
<description/>
|
||||
<comment/>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>ar-LB</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>de-DE</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>es-CL</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-FR</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>ru-RU</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>tr-TR</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
<concept_node>
|
||||
<name>no_points</name>
|
||||
<description/>
|
||||
|
|
|
@ -71,6 +71,16 @@
|
|||
"level_up.go": "",
|
||||
"level_up.instructions": "",
|
||||
"level_up.maxed_upgrade": "",
|
||||
"level_up.missed.best": "",
|
||||
"level_up.missed.catchRate.best": "",
|
||||
"level_up.missed.catchRate.good": "",
|
||||
"level_up.missed.good": "",
|
||||
"level_up.missed.levelMisses.best": "",
|
||||
"level_up.missed.levelMisses.good": "",
|
||||
"level_up.missed.levelTime.best": "",
|
||||
"level_up.missed.levelTime.good": "",
|
||||
"level_up.missed.levelWallBounces.best": "",
|
||||
"level_up.missed.levelWallBounces.good": "",
|
||||
"level_up.no_points": "",
|
||||
"level_up.pick": "",
|
||||
"level_up.pick_upgrade": "",
|
||||
|
|
|
@ -69,8 +69,18 @@
|
|||
"lab.select_level": "Select a level to play on",
|
||||
"lab.unlocks_at": "Unlocks at total score {{score}}",
|
||||
"level_up.go": "Continue to level \"{{name}}\"",
|
||||
"level_up.instructions": "You gained {{gain}} extra lives. You can use your {{count}} extra lives to buy upgrades below, or keep them to be safe. ",
|
||||
"level_up.instructions": "You can use your {{count}} lives to buy upgrades below or keep them to be safe. ",
|
||||
"level_up.maxed_upgrade": "\"{{name}}\" is at max level",
|
||||
"level_up.missed.best": "Expert challenge",
|
||||
"level_up.missed.catchRate.best": "Catch {{target}}% of coins to gain one more choice.",
|
||||
"level_up.missed.catchRate.good": "Catch {{target}}% of coins to gain one more life and choice.",
|
||||
"level_up.missed.good": "Enthusiast challenge",
|
||||
"level_up.missed.levelMisses.best": "Miss the bricks less than {{target}} times to gain one more choice.",
|
||||
"level_up.missed.levelMisses.good": "Miss the bricks less than {{target}} times to gain one more life and choice.",
|
||||
"level_up.missed.levelTime.best": "Clear the level in less than {{target}} seconds to gain one more choice.",
|
||||
"level_up.missed.levelTime.good": "Clear the level in less than {{target}} seconds to gain one more life and choice.",
|
||||
"level_up.missed.levelWallBounces.best": "Hit the walls less than {{target}} times to gain one more choice.",
|
||||
"level_up.missed.levelWallBounces.good": "Hit the walls less than {{target}} times to gain one more life and choice.",
|
||||
"level_up.no_points": "You've spent all your extra lives.",
|
||||
"level_up.pick": "Pick",
|
||||
"level_up.pick_upgrade": "Get \"{{name}}\"",
|
||||
|
@ -433,8 +443,8 @@
|
|||
"upgrades.trampoline.tooltip": "More coins if you bounce on bricks and the paddle only",
|
||||
"upgrades.trampoline.verbose_description": "+{{lvl}} combo per paddle bounce,-{{lvl}} combo per bounce on any border. One of the rare combo upgrades that don't add a reset condition",
|
||||
"upgrades.transparency.name": "Transparency",
|
||||
"upgrades.transparency.tooltip": "+50% coins but the ball is sometimes invisible",
|
||||
"upgrades.transparency.verbose_description": "Ball becomes transparent at the top of the screen.\n +{{percent}} % coins when all balls are at full transparency. \nHigher levels make the ball transparent sooner and increase the point bonus.",
|
||||
"upgrades.transparency.tooltip": "+50% coins. The ball is invisible at the top of the screen.",
|
||||
"upgrades.transparency.verbose_description": "Ball becomes transparent at the top of the screen.\n +{{percent}} % coins. \nHigher levels make the ball transparent sooner and increase the point bonus.",
|
||||
"upgrades.trickledown.name": "Trickle down",
|
||||
"upgrades.trickledown.tooltip": "Coins appear at the top of the screen.",
|
||||
"upgrades.trickledown.verbose_description": "The coins might sit on top of a brick if there are bricks on the top row, in that case they will fall down after you break that brick. ",
|
||||
|
|
|
@ -71,6 +71,16 @@
|
|||
"level_up.go": "",
|
||||
"level_up.instructions": "",
|
||||
"level_up.maxed_upgrade": "",
|
||||
"level_up.missed.best": "",
|
||||
"level_up.missed.catchRate.best": "",
|
||||
"level_up.missed.catchRate.good": "",
|
||||
"level_up.missed.good": "",
|
||||
"level_up.missed.levelMisses.best": "",
|
||||
"level_up.missed.levelMisses.good": "",
|
||||
"level_up.missed.levelTime.best": "",
|
||||
"level_up.missed.levelTime.good": "",
|
||||
"level_up.missed.levelWallBounces.best": "",
|
||||
"level_up.missed.levelWallBounces.good": "",
|
||||
"level_up.no_points": "",
|
||||
"level_up.pick": "",
|
||||
"level_up.pick_upgrade": "",
|
||||
|
|
|
@ -71,6 +71,16 @@
|
|||
"level_up.go": "",
|
||||
"level_up.instructions": "",
|
||||
"level_up.maxed_upgrade": "",
|
||||
"level_up.missed.best": "",
|
||||
"level_up.missed.catchRate.best": "",
|
||||
"level_up.missed.catchRate.good": "",
|
||||
"level_up.missed.good": "",
|
||||
"level_up.missed.levelMisses.best": "",
|
||||
"level_up.missed.levelMisses.good": "",
|
||||
"level_up.missed.levelTime.best": "",
|
||||
"level_up.missed.levelTime.good": "",
|
||||
"level_up.missed.levelWallBounces.best": "",
|
||||
"level_up.missed.levelWallBounces.good": "",
|
||||
"level_up.no_points": "",
|
||||
"level_up.pick": "",
|
||||
"level_up.pick_upgrade": "",
|
||||
|
|
|
@ -71,6 +71,16 @@
|
|||
"level_up.go": "",
|
||||
"level_up.instructions": "",
|
||||
"level_up.maxed_upgrade": "",
|
||||
"level_up.missed.best": "",
|
||||
"level_up.missed.catchRate.best": "",
|
||||
"level_up.missed.catchRate.good": "",
|
||||
"level_up.missed.good": "",
|
||||
"level_up.missed.levelMisses.best": "",
|
||||
"level_up.missed.levelMisses.good": "",
|
||||
"level_up.missed.levelTime.best": "",
|
||||
"level_up.missed.levelTime.good": "",
|
||||
"level_up.missed.levelWallBounces.best": "",
|
||||
"level_up.missed.levelWallBounces.good": "",
|
||||
"level_up.no_points": "",
|
||||
"level_up.pick": "",
|
||||
"level_up.pick_upgrade": "",
|
||||
|
|
|
@ -71,6 +71,16 @@
|
|||
"level_up.go": "",
|
||||
"level_up.instructions": "",
|
||||
"level_up.maxed_upgrade": "",
|
||||
"level_up.missed.best": "",
|
||||
"level_up.missed.catchRate.best": "",
|
||||
"level_up.missed.catchRate.good": "",
|
||||
"level_up.missed.good": "",
|
||||
"level_up.missed.levelMisses.best": "",
|
||||
"level_up.missed.levelMisses.good": "",
|
||||
"level_up.missed.levelTime.best": "",
|
||||
"level_up.missed.levelTime.good": "",
|
||||
"level_up.missed.levelWallBounces.best": "",
|
||||
"level_up.missed.levelWallBounces.good": "",
|
||||
"level_up.no_points": "",
|
||||
"level_up.pick": "",
|
||||
"level_up.pick_upgrade": "",
|
||||
|
|
|
@ -17,7 +17,7 @@ import {
|
|||
isLevelLocked,
|
||||
reasonLevelIsLocked,
|
||||
} from "./get_level_unlock_condition";
|
||||
import {dontOfferTooSoon} from "./openUpgradesPicker";
|
||||
import { dontOfferTooSoon } from "./openUpgradesPicker";
|
||||
|
||||
export function getRunLevels(
|
||||
params: RunParams,
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
import {GameState} from "./types";
|
||||
import {asyncAlert} from "./asyncAlert";
|
||||
import {t} from "./i18n/i18n";
|
||||
import {levelsListHTMl, max_levels, pickedUpgradesHTMl} from "./game_utils";
|
||||
import {getCreativeModeWarning, getHistory} from "./gameOver";
|
||||
import {pause} from "./game";
|
||||
import {allLevels, icons} from "./loadGameData";
|
||||
import {firstWhere} from "./pure_functions";
|
||||
import {getSettingValue, getTotalScore} from "./settings";
|
||||
import {getLevelUnlockCondition, reasonLevelIsLocked, upgradeName,} from "./get_level_unlock_condition";
|
||||
import { GameState } from "./types";
|
||||
import { asyncAlert } from "./asyncAlert";
|
||||
import { t } from "./i18n/i18n";
|
||||
import { levelsListHTMl, max_levels, pickedUpgradesHTMl } from "./game_utils";
|
||||
import { getCreativeModeWarning, getHistory } from "./gameOver";
|
||||
import { pause } from "./game";
|
||||
import { allLevels, icons } from "./loadGameData";
|
||||
import { firstWhere } from "./pure_functions";
|
||||
import { getSettingValue, getTotalScore } from "./settings";
|
||||
import {
|
||||
getLevelUnlockCondition,
|
||||
reasonLevelIsLocked,
|
||||
upgradeName,
|
||||
} from "./get_level_unlock_condition";
|
||||
|
||||
export async function openScorePanel(gameState: GameState) {
|
||||
pause(true);
|
||||
|
@ -96,4 +100,3 @@ export function getNearestUnlockHTML(gameState: GameState) {
|
|||
|
||||
`;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,147 +1,223 @@
|
|||
import {GameState, PerkId} from "./types";
|
||||
import { GameState, PerkId } from "./types";
|
||||
import {
|
||||
catchRateBest,
|
||||
catchRateGood,
|
||||
levelTimeBest,
|
||||
levelTimeGood,
|
||||
missesBest,
|
||||
missesGood,
|
||||
wallBouncedBest,
|
||||
wallBouncedGood
|
||||
catchRateBest,
|
||||
catchRateGood,
|
||||
levelTimeBest,
|
||||
levelTimeGood,
|
||||
missesBest,
|
||||
missesGood,
|
||||
wallBouncedBest,
|
||||
wallBouncedGood,
|
||||
} from "./pure_functions";
|
||||
import {t} from "./i18n/i18n";
|
||||
import {icons, upgrades} from "./loadGameData";
|
||||
import {asyncAlert} from "./asyncAlert";
|
||||
import { t } from "./i18n/i18n";
|
||||
import { icons, upgrades } from "./loadGameData";
|
||||
import { asyncAlert } from "./asyncAlert";
|
||||
import {
|
||||
escapeAttribute,
|
||||
getPossibleUpgrades,
|
||||
levelsListHTMl,
|
||||
max_levels,
|
||||
upgradeLevelAndMaxDisplay
|
||||
escapeAttribute,
|
||||
getPossibleUpgrades,
|
||||
levelsListHTMl,
|
||||
max_levels,
|
||||
upgradeLevelAndMaxDisplay,
|
||||
} from "./game_utils";
|
||||
import {getNearestUnlockHTML} from "./openScorePanel";
|
||||
import { getNearestUnlockHTML } from "./openScorePanel";
|
||||
|
||||
export async function openUpgradesPicker(gameState: GameState) {
|
||||
const catchRate =
|
||||
gameState.levelCoughtCoins / (gameState.levelSpawnedCoins || 1);
|
||||
const catchRate =
|
||||
gameState.levelCoughtCoins / (gameState.levelSpawnedCoins || 1);
|
||||
|
||||
let choices = 3
|
||||
let livesWon=1
|
||||
let choices = 3;
|
||||
let livesWon = 1;
|
||||
let missedOpportunities = [];
|
||||
const good = ""; // '<strong>'+t('level_up.missed.good')+'</strong>: '
|
||||
const best = ""; //'<strong>'+t('level_up.missed.best')+'</strong>: '
|
||||
|
||||
if (gameState.levelWallBounces < wallBouncedGood) {
|
||||
choices++;
|
||||
if (gameState.levelWallBounces < wallBouncedGood) {
|
||||
choices++;
|
||||
livesWon++;
|
||||
} else {
|
||||
missedOpportunities.push(
|
||||
good +
|
||||
t("level_up.missed.levelWallBounces.good", {
|
||||
target: wallBouncedGood,
|
||||
}),
|
||||
);
|
||||
}
|
||||
if (gameState.levelWallBounces < wallBouncedBest) {
|
||||
choices++;
|
||||
} else {
|
||||
missedOpportunities.push(
|
||||
best +
|
||||
t("level_up.missed.levelWallBounces.best", {
|
||||
target: wallBouncedBest,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
livesWon++;
|
||||
if (gameState.levelWallBounces < wallBouncedBest) {
|
||||
choices++;
|
||||
}
|
||||
}
|
||||
if (gameState.levelTime < levelTimeGood * 1000) {
|
||||
choices++;
|
||||
livesWon++;
|
||||
} else {
|
||||
missedOpportunities.push(
|
||||
good +
|
||||
t("level_up.missed.levelTime.good", {
|
||||
target: levelTimeGood,
|
||||
}),
|
||||
);
|
||||
}
|
||||
if (gameState.levelTime < levelTimeBest * 1000) {
|
||||
choices++;
|
||||
} else {
|
||||
missedOpportunities.push(
|
||||
best +
|
||||
t("level_up.missed.levelTime.best", {
|
||||
target: levelTimeBest,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (gameState.levelTime < levelTimeGood * 1000) {
|
||||
choices++;
|
||||
livesWon++;
|
||||
if (gameState.levelTime < levelTimeBest * 1000) {
|
||||
choices++;
|
||||
}
|
||||
}
|
||||
if (catchRate > catchRateGood / 100) {
|
||||
choices++;
|
||||
livesWon++;
|
||||
if (catchRate > catchRateBest / 100) {
|
||||
choices++;
|
||||
}
|
||||
}
|
||||
if (gameState.levelMisses < missesGood) {
|
||||
choices++;
|
||||
livesWon++;
|
||||
if (gameState.levelMisses < missesBest) {
|
||||
choices++;
|
||||
}
|
||||
}
|
||||
if (catchRate > catchRateGood / 100) {
|
||||
choices++;
|
||||
livesWon++;
|
||||
} else {
|
||||
missedOpportunities.push(
|
||||
good +
|
||||
t("level_up.missed.catchRate.good", {
|
||||
target: catchRateGood,
|
||||
}),
|
||||
);
|
||||
}
|
||||
if (catchRate > catchRateBest / 100) {
|
||||
choices++;
|
||||
} else {
|
||||
missedOpportunities.push(
|
||||
best +
|
||||
t("level_up.missed.catchRate.best", {
|
||||
target: catchRateBest,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
gameState.extra_lives+=livesWon
|
||||
if (gameState.levelMisses < missesGood) {
|
||||
choices++;
|
||||
livesWon++;
|
||||
} else {
|
||||
missedOpportunities.push(
|
||||
good +
|
||||
t("level_up.missed.levelMisses.good", {
|
||||
target: missesGood,
|
||||
}),
|
||||
);
|
||||
}
|
||||
if (gameState.levelMisses < missesBest) {
|
||||
choices++;
|
||||
} else {
|
||||
missedOpportunities.push(
|
||||
best +
|
||||
t("level_up.missed.levelMisses.best", {
|
||||
target: missesBest,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
let offered: PerkId[] = getPossibleUpgrades(gameState)
|
||||
.map((u) => ({
|
||||
...u,
|
||||
score: Math.random() + (gameState.lastOffered[u.id] || 0),
|
||||
}))
|
||||
.sort((a, b) => a.score - b.score)
|
||||
.filter((u) => gameState.perks[u.id] < u.max + gameState.perks.limitless)
|
||||
.map(u => u.id)
|
||||
gameState.extra_lives += livesWon;
|
||||
|
||||
const fromStart = upgrades.map(u => u.id).filter(id => gameState.perks[id])
|
||||
let offered: PerkId[] = getPossibleUpgrades(gameState)
|
||||
.map((u) => ({
|
||||
...u,
|
||||
score: Math.random() + (gameState.lastOffered[u.id] || 0),
|
||||
}))
|
||||
.sort((a, b) => a.score - b.score)
|
||||
.filter((u) => gameState.perks[u.id] < u.max + gameState.perks.limitless)
|
||||
.map((u) => u.id);
|
||||
|
||||
while (true) {
|
||||
const fromStart = upgrades
|
||||
.map((u) => u.id)
|
||||
.filter((id) => gameState.perks[id]);
|
||||
|
||||
const updatedChoices = gameState.perks.one_more_choice + choices
|
||||
let list = upgrades.filter(u => offered.slice(0, updatedChoices).includes(u.id) || gameState.perks[u.id])
|
||||
while (true) {
|
||||
const updatedChoices = gameState.perks.one_more_choice + choices;
|
||||
let list = upgrades.filter(
|
||||
(u) =>
|
||||
offered.slice(0, updatedChoices).includes(u.id) ||
|
||||
gameState.perks[u.id],
|
||||
);
|
||||
|
||||
list = list.filter(u => fromStart.includes(u.id))
|
||||
.concat(list.filter(u => !fromStart.includes(u.id)))
|
||||
list = list
|
||||
.filter((u) => fromStart.includes(u.id))
|
||||
.concat(list.filter((u) => !fromStart.includes(u.id)));
|
||||
|
||||
list.forEach((u) => {
|
||||
dontOfferTooSoon(gameState, u.id);
|
||||
});
|
||||
list.forEach((u) => {
|
||||
dontOfferTooSoon(gameState, u.id);
|
||||
});
|
||||
|
||||
const upgradeId = await asyncAlert<PerkId | null>({
|
||||
title: t("level_up.title", {
|
||||
level: gameState.currentLevel,
|
||||
max: max_levels(gameState),
|
||||
}),
|
||||
content: [
|
||||
{
|
||||
text: t("level_up.go", { name: gameState.level.name }),
|
||||
icon: icons[gameState.level.name],
|
||||
value: null,
|
||||
},
|
||||
|
||||
const upgradeId = await asyncAlert<PerkId | null>({
|
||||
title:
|
||||
t("level_up.title", {
|
||||
level: gameState.currentLevel,
|
||||
max: max_levels(gameState),
|
||||
}),
|
||||
content: [
|
||||
{
|
||||
text: t('level_up.go', {name: gameState.level.name}),
|
||||
icon: icons[gameState.level.name],
|
||||
value: null,
|
||||
},
|
||||
gameState.extra_lives
|
||||
? `<p>${t("level_up.instructions", {
|
||||
count: gameState.extra_lives,
|
||||
gain: livesWon,
|
||||
})}</p>`
|
||||
: `<p>${t("level_up.no_points")}</p>`,
|
||||
...list.map((u) => {
|
||||
const max = u.max + gameState.perks.limitless;
|
||||
const lvl = gameState.perks[u.id];
|
||||
|
||||
gameState.extra_lives ? `<p>${t("level_up.instructions", {
|
||||
count: gameState.extra_lives,
|
||||
gain:livesWon
|
||||
})}</p>` : `<p>${t("level_up.no_points")}</p>`,
|
||||
...list.map((u) => {
|
||||
const max = u.max + gameState.perks.limitless
|
||||
const lvl = gameState.perks[u.id]
|
||||
const button =
|
||||
!gameState.extra_lives || gameState.perks[u.id] >= max
|
||||
? ""
|
||||
: ` <button data-resolve-to="${u.id}">${
|
||||
lvl ? t("level_up.upgrade") : t("level_up.pick")
|
||||
}</button>`;
|
||||
|
||||
const button = !gameState.extra_lives || gameState.perks[u.id] >= max ?
|
||||
'' : ` <button data-resolve-to="${u.id}">${
|
||||
lvl ? t('level_up.upgrade') : t('level_up.pick')
|
||||
}</button>`
|
||||
|
||||
const lvlInfo = lvl ? upgradeLevelAndMaxDisplay(u, gameState) : ''
|
||||
return `<div class="upgrade choice ${
|
||||
(!lvl && gameState.extra_lives && 'free') ||
|
||||
(lvl && 'used') ||
|
||||
'greyed-out'
|
||||
}" >
|
||||
const lvlInfo = lvl ? upgradeLevelAndMaxDisplay(u, gameState) : "";
|
||||
return `<div class="upgrade choice ${
|
||||
(!lvl && gameState.extra_lives && "free") ||
|
||||
(lvl && "used") ||
|
||||
"greyed-out"
|
||||
}" >
|
||||
${icons["icon:" + u.id]}
|
||||
<p data-tooltip="${escapeAttribute(u.fullHelp(Math.max(1, lvl)))}">
|
||||
<strong>${u.name}</strong> ${lvlInfo}
|
||||
${u.help(Math.max(1, lvl))}
|
||||
</p>
|
||||
${button}
|
||||
</div>`
|
||||
})
|
||||
,
|
||||
levelsListHTMl(gameState, gameState.currentLevel),
|
||||
getNearestUnlockHTML(gameState),
|
||||
`<div id="level-recording-container"></div>`,
|
||||
],
|
||||
});
|
||||
</div>`;
|
||||
}),
|
||||
...missedOpportunities.map(
|
||||
(reason) =>
|
||||
`<div class="upgrade choice greyed-out" >
|
||||
${icons["icon:locked"]}
|
||||
<p>
|
||||
${reason}
|
||||
</p>
|
||||
</div>`,
|
||||
),
|
||||
levelsListHTMl(gameState, gameState.currentLevel),
|
||||
getNearestUnlockHTML(gameState),
|
||||
`<div id="level-recording-container"></div>`,
|
||||
],
|
||||
});
|
||||
|
||||
if (upgradeId) {
|
||||
gameState.perks[upgradeId]++;
|
||||
gameState.runStatistics.upgrades_picked++;
|
||||
gameState.extra_lives--
|
||||
} else {
|
||||
return
|
||||
}
|
||||
if (upgradeId) {
|
||||
gameState.perks[upgradeId]++;
|
||||
gameState.runStatistics.upgrades_picked++;
|
||||
gameState.extra_lives--;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function dontOfferTooSoon(gameState: GameState, id: PerkId) {
|
||||
gameState.lastOffered[id] = Math.round(Date.now() / 1000);
|
||||
}
|
||||
gameState.lastOffered[id] = Math.round(Date.now() / 1000);
|
||||
}
|
||||
|
|
|
@ -20,17 +20,11 @@ export function ballTransparency(ball: Ball, gameState: GameState) {
|
|||
|
||||
export function coinsBoostedCombo(gameState: GameState) {
|
||||
let boost =
|
||||
1 + gameState.perks.sturdy_bricks / 2 + gameState.perks.smaller_puck / 2;
|
||||
if (gameState.perks.transparency) {
|
||||
let min = 1;
|
||||
gameState.balls.forEach((ball) => {
|
||||
const bt = ballTransparency(ball, gameState);
|
||||
if (bt < min) {
|
||||
min = bt;
|
||||
}
|
||||
});
|
||||
boost += (min * gameState.perks.transparency) / 2;
|
||||
}
|
||||
1 +
|
||||
gameState.perks.sturdy_bricks / 2 +
|
||||
gameState.perks.smaller_puck / 2 +
|
||||
gameState.perks.transparency / 2;
|
||||
|
||||
if (gameState.perks.minefield) {
|
||||
gameState.bricks.forEach((brick) => {
|
||||
if (brick === "black") {
|
||||
|
@ -101,13 +95,13 @@ export function firstWhere<Input, Output>(
|
|||
}
|
||||
}
|
||||
|
||||
export const wallBouncedBest = 3,
|
||||
wallBouncedGood = 10,
|
||||
levelTimeBest = 30,
|
||||
levelTimeGood = 60,
|
||||
catchRateBest = 95,
|
||||
export const wallBouncedBest = 2,
|
||||
wallBouncedGood = 7,
|
||||
levelTimeBest = 10,
|
||||
levelTimeGood = 45,
|
||||
catchRateBest = 99,
|
||||
catchRateGood = 90,
|
||||
missesBest = 3,
|
||||
missesBest = 1,
|
||||
missesGood = 6;
|
||||
|
||||
export const MAX_LEVEL_SIZE = 21;
|
||||
|
|
|
@ -108,8 +108,12 @@ export function render(gameState: GameState) {
|
|||
: "") +
|
||||
`<span class="score" data-tooltip="${t("play.score_tooltip")}">$${gameState.score}</span>`;
|
||||
|
||||
scoreDisplay.classList[gameState.startParams.computer_controlled ? 'add':'remove']('computer_controlled');
|
||||
scoreDisplay.classList[gameState.lastScoreIncrease > gameState.levelTime - 500 ? 'add':'remove']('active');
|
||||
scoreDisplay.classList[
|
||||
gameState.startParams.computer_controlled ? "add" : "remove"
|
||||
]("computer_controlled");
|
||||
scoreDisplay.classList[
|
||||
gameState.lastScoreIncrease > gameState.levelTime - 500 ? "add" : "remove"
|
||||
]("active");
|
||||
|
||||
// Clear
|
||||
if (!isOptionOn("basic") && level.svg && level.color === "#000000") {
|
||||
|
|
|
@ -13,11 +13,10 @@ export function hideAnyTooltip() {
|
|||
tooltip.style.display = "none";
|
||||
}
|
||||
|
||||
|
||||
function setupMobileTooltips(tooltip: HTMLDivElement) {
|
||||
tooltip.className = "mobile";
|
||||
function openTooltip(e: Event) {
|
||||
console.log('openTooltip',e)
|
||||
console.log("openTooltip", e);
|
||||
hideAnyTooltip();
|
||||
const hovering = e.target as HTMLElement;
|
||||
if (!hovering?.hasAttribute("data-help-content")) {
|
||||
|
@ -33,7 +32,6 @@ function setupMobileTooltips(tooltip: HTMLDivElement) {
|
|||
|
||||
document.body.addEventListener("click", openTooltip, true);
|
||||
document.addEventListener("scroll", hideAnyTooltip);
|
||||
|
||||
}
|
||||
|
||||
function setupDesktopTooltips(tooltip: HTMLDivElement) {
|
||||
|
|
|
@ -914,7 +914,10 @@ export const rawUpgrades = [
|
|||
max: 4,
|
||||
name: t("upgrades.passive_income.name"),
|
||||
help: (lvl: number) =>
|
||||
t("upgrades.passive_income.tooltip", { time: (lvl * 0.1 - 0.05).toFixed(2), lvl }),
|
||||
t("upgrades.passive_income.tooltip", {
|
||||
time: (lvl * 0.1 - 0.05).toFixed(2),
|
||||
lvl,
|
||||
}),
|
||||
fullHelp: (lvl: number) =>
|
||||
t("upgrades.passive_income.verbose_description", {
|
||||
time: (lvl * 0.1 - 0.05).toFixed(2),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue