mirror of
https://gitlab.com/lecarore/breakout71.git
synced 2025-04-20 20:16:16 -04:00
starting perk, settings icons
This commit is contained in:
parent
991e7a1d85
commit
1cf82d6641
12 changed files with 467 additions and 185 deletions
24
Readme.md
24
Readme.md
|
@ -16,10 +16,19 @@ Break colourful bricks, catch bouncing coins and select powerful upgrades !
|
||||||
# changelog
|
# changelog
|
||||||
|
|
||||||
## next goals
|
## next goals
|
||||||
- choose starting perks
|
|
||||||
- wind : move coins based on puck movement not position
|
- wind : move coins based on puck movement not position
|
||||||
|
|
||||||
## next release
|
## next release.
|
||||||
|
|
||||||
|
- choose starting perks
|
||||||
|
- fixed issue with reloading with [R] key
|
||||||
|
- gameover screen restarts in the same game mode
|
||||||
|
- Trampoline render sides in red.
|
||||||
|
- tooltips stuck on mobile
|
||||||
|
- Check in fullscreen worked, otherwise resume playing
|
||||||
|
|
||||||
|
## 29058981
|
||||||
|
|
||||||
- [jaceys] A visual indication of whether a ball has hit a brick this serve (as an option)
|
- [jaceys] A visual indication of whether a ball has hit a brick this serve (as an option)
|
||||||
- Top down /reach: now only the lowest level of N bricks resets combo, and all other bricks do +N combo
|
- Top down /reach: now only the lowest level of N bricks resets combo, and all other bricks do +N combo
|
||||||
|
@ -106,13 +115,13 @@ Break colourful bricks, catch bouncing coins and select powerful upgrades !
|
||||||
|
|
||||||
- on mobile, relative movement of the touch would be amplified and added to the puck
|
- on mobile, relative movement of the touch would be amplified and added to the puck
|
||||||
- option : don't pause on mobile when lifting finger
|
- option : don't pause on mobile when lifting finger
|
||||||
- [obigre] Offer to level ups perks separately
|
- [obigre] Offer to level ups perks separately from picking new ones
|
||||||
- https://weblate.org/fr/
|
|
||||||
- strict sample size red borders ?
|
- strict sample size red borders ?
|
||||||
- on mobile, add an element that feels like it can be "grabbed" and make it shine while writing "Push here to play"
|
- on mobile, add an element that feels like it can be "grabbed" and make it shine while writing "Push here to play"
|
||||||
- add a clickable button to allow sound to play in chrome android
|
- add a clickable button to allow sound to play in chrome android
|
||||||
- see how to do fullscreen on ios, or at least explain to do aA/hide toolbars
|
- see how to do fullscreen on ios, or at least explain to do aA/hide toolbars
|
||||||
- when game resumes near bottom, be unvulnerable for .5s ? , once per level
|
- when game resumes near bottom, be unvulnerable for .5s ? , once per level
|
||||||
|
- https://weblate.org/fr/ quite annoying actually
|
||||||
|
|
||||||
|
|
||||||
## Game engine features ideas
|
## Game engine features ideas
|
||||||
|
@ -159,9 +168,10 @@ Break colourful bricks, catch bouncing coins and select powerful upgrades !
|
||||||
- [colin] varied diet - your combo grows by 2 when your ball changes color, but decreses by one when a brick is broken ?
|
- [colin] varied diet - your combo grows by 2 when your ball changes color, but decreses by one when a brick is broken ?
|
||||||
- [colin] trickle up - inverse of reach more or less
|
- [colin] trickle up - inverse of reach more or less
|
||||||
- Dividends — +1 combo per 10 coins lost (band-aid for players who struggle, useful addition when choosing Ascetism)
|
- Dividends — +1 combo per 10 coins lost (band-aid for players who struggle, useful addition when choosing Ascetism)
|
||||||
- +1 combo per bricks / resets after 5/lvl seconds without explosion
|
- +lvl combo per bricks / resets after 5/lvl seconds without explosion ?
|
||||||
- +1 combo per bricks / resets after 5/lvl seconds without coin catch
|
- +lvl combo per bricks / resets after 5/lvl seconds without coin catch ?
|
||||||
- +1 combo per bricks / resets after 5/lvl seconds without coin puck bounce
|
- +lvl combo per bricks / resets after 5/lvl seconds without ball color change ?
|
||||||
|
- +lvl combo per bricks / resets after 5/lvl seconds without sides hit ?
|
||||||
|
|
||||||
## Medium difficulty perks ideas
|
## Medium difficulty perks ideas
|
||||||
- balls collision split them into 4 smaller balls, lvl times (requires rework)
|
- balls collision split them into 4 smaller balls, lvl times (requires rework)
|
||||||
|
|
231
dist/index.html
vendored
231
dist/index.html
vendored
File diff suppressed because one or more lines are too long
|
@ -1121,5 +1121,47 @@
|
||||||
"bricks": "bbb______tttttt________tttttt________tttttt______bbtttttt______bbbbbttt______bbbbbb________bbbbbb________bbbbbb______ttbbbbbb______tttttbbb______tttttt________tttttt________tttttt______bbtttttt______bbbbbttt______bbbbbb________bbbbbb________bbbbbb________bbbbbb___________bbb______________",
|
"bricks": "bbb______tttttt________tttttt________tttttt______bbtttttt______bbbbbttt______bbbbbb________bbbbbb________bbbbbb______ttbbbbbb______tttttbbb______tttttt________tttttt________tttttt______bbtttttt______bbbbbttt______bbbbbb________bbbbbb________bbbbbb________bbbbbb___________bbb______________",
|
||||||
"svg": null,
|
"svg": null,
|
||||||
"color": ""
|
"color": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "icon:starting_perks",
|
||||||
|
"size": 8,
|
||||||
|
"bricks": "_________y_y_y___________l_l_l_l_________l_l_l_l_________l_l_l_l",
|
||||||
|
"svg": null,
|
||||||
|
"color": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "icon:download",
|
||||||
|
"size": 8,
|
||||||
|
"bricks": "___yy______yy______yy______yy______yy____yyyyyy___yyyy__gggyyggg",
|
||||||
|
"svg": null,
|
||||||
|
"color": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "icon:upload",
|
||||||
|
"size": 8,
|
||||||
|
"bricks": "gggyyggg__yyyy___yyyyyy____yy______yy______yy______yy______yy___",
|
||||||
|
"svg": null,
|
||||||
|
"color": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "icon:coins",
|
||||||
|
"size": 8,
|
||||||
|
"bricks": "__yyyy___yyOOyy_yyOOOOyyyOOOOOOyyOOOOOOyyyOOOOyy_yyOOyy___yyyy__",
|
||||||
|
"svg": null,
|
||||||
|
"color": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "icon:particles",
|
||||||
|
"size": 8,
|
||||||
|
"bricks": "_y_y_y__________y_yyy_y___yyy___y_yyy__y_____y___y_y__y________y",
|
||||||
|
"svg": null,
|
||||||
|
"color": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "icon:reset",
|
||||||
|
"size": 8,
|
||||||
|
"bricks": "__rrrr___r____r_r_r__r_rr__rr__rr__rr__rr_r__r_r_r____r___rrrr__",
|
||||||
|
"svg": null,
|
||||||
|
"color": ""
|
||||||
}
|
}
|
||||||
]
|
]
|
|
@ -1,10 +1,9 @@
|
||||||
* {
|
* {
|
||||||
font-family:
|
font-family: Courier New,
|
||||||
Courier New,
|
Courier,
|
||||||
Courier,
|
Lucida Sans Typewriter,
|
||||||
Lucida Sans Typewriter,
|
Lucida Typewriter,
|
||||||
Lucida Typewriter,
|
monospace;
|
||||||
monospace;
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,6 +46,7 @@ body {
|
||||||
line-height: 20px;
|
line-height: 20px;
|
||||||
max-width: calc(100vw - 80px);
|
max-width: calc(100vw - 80px);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
&:hover,
|
&:hover,
|
||||||
&:focus {
|
&:focus {
|
||||||
background: rgba(0, 0, 0, 0.3);
|
background: rgba(0, 0, 0, 0.3);
|
||||||
|
@ -173,10 +173,12 @@ body:not(.has-alert-open) #popup {
|
||||||
filter: saturate(0);
|
filter: saturate(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&[disabled] {
|
&[disabled] {
|
||||||
opacity: 0.2;
|
opacity: 0.2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -193,6 +195,7 @@ body:not(.has-alert-open) #popup {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
section {
|
section {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||||
|
@ -379,15 +382,18 @@ h2.histogram-title strong {
|
||||||
width: 32px;
|
width: 32px;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
p {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
color: rgba(255, 255, 255, 0.6);
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
|
||||||
margin: 0 20px;
|
margin: 0 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.used p strong {
|
&.used p strong {
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
& > span {
|
& > span {
|
||||||
flex-grow: 0;
|
flex-grow: 0;
|
||||||
|
|
||||||
|
@ -410,6 +416,7 @@ h2.histogram-title strong {
|
||||||
background: red;
|
background: red;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.used {
|
&.used {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
148
src/game.ts
148
src/game.ts
|
@ -68,9 +68,11 @@ import { hoursSpentPlaying } from "./pure_functions";
|
||||||
import { helpMenuEntry } from "./help";
|
import { helpMenuEntry } from "./help";
|
||||||
import { creativeMode } from "./creative";
|
import { creativeMode } from "./creative";
|
||||||
import { setupTooltips } from "./tooltip";
|
import { setupTooltips } from "./tooltip";
|
||||||
|
import {startingPerkMenuButton} from "./startingPerks";
|
||||||
|
|
||||||
export function play() {
|
export async function play() {
|
||||||
if (applyFullScreenChoice()) return;
|
|
||||||
|
if (await applyFullScreenChoice()) return;
|
||||||
if (gameState.running) return;
|
if (gameState.running) return;
|
||||||
gameState.running = true;
|
gameState.running = true;
|
||||||
gameState.ballStickToPuck = false;
|
gameState.ballStickToPuck = false;
|
||||||
|
@ -556,8 +558,45 @@ function donationNag(gameState) {
|
||||||
async function openSettingsMenu() {
|
async function openSettingsMenu() {
|
||||||
pause(true);
|
pause(true);
|
||||||
|
|
||||||
const actions: AsyncAlertAction<() => void>[] = [];
|
const actions: AsyncAlertAction<() => void>[] = [
|
||||||
|
startingPerkMenuButton()
|
||||||
|
];
|
||||||
|
|
||||||
|
const languages= [
|
||||||
|
{
|
||||||
|
text: "English",
|
||||||
|
value: "en",
|
||||||
|
icon: icons['UK']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "Français",
|
||||||
|
value: "fr",
|
||||||
|
icon: icons['France']
|
||||||
|
}
|
||||||
|
]
|
||||||
|
actions.push({
|
||||||
|
icon:languages.find(l=>l.value===getCurrentLang())?.icon,
|
||||||
|
text: t("main_menu.language"),
|
||||||
|
help: t("main_menu.language_help"),
|
||||||
|
async value() {
|
||||||
|
const pick = await asyncAlert({
|
||||||
|
title: t("main_menu.language"),
|
||||||
|
content: [
|
||||||
|
t("main_menu.language_help"),
|
||||||
|
...languages
|
||||||
|
],
|
||||||
|
allowClose: true,
|
||||||
|
});
|
||||||
|
if (
|
||||||
|
pick &&
|
||||||
|
pick !== getCurrentLang() &&
|
||||||
|
(await confirmRestart(gameState))
|
||||||
|
) {
|
||||||
|
setSettingValue("lang", pick);
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
for (const key of Object.keys(options) as OptionId[]) {
|
for (const key of Object.keys(options) as OptionId[]) {
|
||||||
if (options[key])
|
if (options[key])
|
||||||
actions.push({
|
actions.push({
|
||||||
|
@ -575,33 +614,7 @@ async function openSettingsMenu() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
actions.push({
|
actions.push({
|
||||||
text: t("main_menu.reset"),
|
icon:icons['icon:download'],
|
||||||
help: t("main_menu.reset_help"),
|
|
||||||
async value() {
|
|
||||||
if (
|
|
||||||
await asyncAlert({
|
|
||||||
title: t("main_menu.reset"),
|
|
||||||
content: [
|
|
||||||
t("main_menu.reset_instruction"),
|
|
||||||
{
|
|
||||||
text: t("main_menu.reset_confirm"),
|
|
||||||
value: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: t("main_menu.reset_cancel"),
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
allowClose: true,
|
|
||||||
})
|
|
||||||
) {
|
|
||||||
localStorage.clear();
|
|
||||||
window.location.reload();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
actions.push({
|
|
||||||
text: t("main_menu.download_save_file"),
|
text: t("main_menu.download_save_file"),
|
||||||
help: t("main_menu.download_save_file_help"),
|
help: t("main_menu.download_save_file_help"),
|
||||||
async value() {
|
async value() {
|
||||||
|
@ -650,6 +663,7 @@ async function openSettingsMenu() {
|
||||||
});
|
});
|
||||||
|
|
||||||
actions.push({
|
actions.push({
|
||||||
|
icon:icons['icon:upload'],
|
||||||
text: t("main_menu.load_save_file"),
|
text: t("main_menu.load_save_file"),
|
||||||
help: t("main_menu.load_save_file_help"),
|
help: t("main_menu.load_save_file_help"),
|
||||||
async value() {
|
async value() {
|
||||||
|
@ -733,37 +747,9 @@ async function openSettingsMenu() {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
actions.push({
|
|
||||||
text: t("main_menu.language"),
|
|
||||||
help: t("main_menu.language_help"),
|
|
||||||
async value() {
|
|
||||||
const pick = await asyncAlert({
|
|
||||||
title: t("main_menu.language"),
|
|
||||||
content: [
|
|
||||||
t("main_menu.language_help"),
|
|
||||||
{
|
|
||||||
text: "English",
|
|
||||||
value: "en",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: "Français",
|
|
||||||
value: "fr",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
allowClose: true,
|
|
||||||
});
|
|
||||||
if (
|
|
||||||
pick &&
|
|
||||||
pick !== getCurrentLang() &&
|
|
||||||
(await confirmRestart(gameState))
|
|
||||||
) {
|
|
||||||
setSettingValue("lang", pick);
|
|
||||||
window.location.reload();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
actions.push({
|
actions.push({
|
||||||
|
icon:icons['icon:coins'],
|
||||||
text: t("main_menu.max_coins", { max: getCurrentMaxCoins() }),
|
text: t("main_menu.max_coins", { max: getCurrentMaxCoins() }),
|
||||||
help: t("main_menu.max_coins_help"),
|
help: t("main_menu.max_coins_help"),
|
||||||
async value() {
|
async value() {
|
||||||
|
@ -772,6 +758,7 @@ async function openSettingsMenu() {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
actions.push({
|
actions.push({
|
||||||
|
icon:icons['icon:particles'],
|
||||||
text: t("main_menu.max_particles", { max: getCurrentMaxParticles() }),
|
text: t("main_menu.max_particles", { max: getCurrentMaxParticles() }),
|
||||||
help: t("main_menu.max_particles_help"),
|
help: t("main_menu.max_particles_help"),
|
||||||
async value() {
|
async value() {
|
||||||
|
@ -780,6 +767,34 @@ async function openSettingsMenu() {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
actions.push({
|
||||||
|
icon:icons['icon:reset'],
|
||||||
|
text: t("main_menu.reset"),
|
||||||
|
help: t("main_menu.reset_help"),
|
||||||
|
async value() {
|
||||||
|
if (
|
||||||
|
await asyncAlert({
|
||||||
|
title: t("main_menu.reset"),
|
||||||
|
content: [
|
||||||
|
t("main_menu.reset_instruction"),
|
||||||
|
{
|
||||||
|
text: t("main_menu.reset_confirm"),
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: t("main_menu.reset_cancel"),
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
allowClose: true,
|
||||||
|
})
|
||||||
|
) {
|
||||||
|
localStorage.clear();
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const cb = await asyncAlert<() => void>({
|
const cb = await asyncAlert<() => void>({
|
||||||
title: t("main_menu.settings_title"),
|
title: t("main_menu.settings_title"),
|
||||||
content: [t("main_menu.settings_help"), ...actions],
|
content: [t("main_menu.settings_help"), ...actions],
|
||||||
|
@ -791,7 +806,7 @@ async function openSettingsMenu() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyFullScreenChoice(): boolean {
|
async function applyFullScreenChoice() {
|
||||||
try {
|
try {
|
||||||
if (!(document.fullscreenEnabled || document.webkitFullscreenEnabled)) {
|
if (!(document.fullscreenEnabled || document.webkitFullscreenEnabled)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -799,19 +814,19 @@ function applyFullScreenChoice(): boolean {
|
||||||
|
|
||||||
if (document.fullscreenElement !== null && !isOptionOn("fullscreen")) {
|
if (document.fullscreenElement !== null && !isOptionOn("fullscreen")) {
|
||||||
if (document.exitFullscreen) {
|
if (document.exitFullscreen) {
|
||||||
document.exitFullscreen();
|
await document.exitFullscreen();
|
||||||
return true;
|
return true;
|
||||||
} else if (document.webkitCancelFullScreen) {
|
} else if (document.webkitCancelFullScreen) {
|
||||||
document.webkitCancelFullScreen();
|
await document.webkitCancelFullScreen();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else if (isOptionOn("fullscreen") && !document.fullscreenElement) {
|
} else if (isOptionOn("fullscreen") && !document.fullscreenElement) {
|
||||||
const docel = document.documentElement;
|
const docel = document.documentElement;
|
||||||
if (docel.requestFullscreen) {
|
if (docel.requestFullscreen) {
|
||||||
docel.requestFullscreen();
|
await docel.requestFullscreen()
|
||||||
return true;
|
return true;
|
||||||
} else if (docel.webkitRequestFullscreen) {
|
} else if (docel.webkitRequestFullscreen) {
|
||||||
docel.webkitRequestFullscreen();
|
await docel.webkitRequestFullscreen();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -926,6 +941,9 @@ document.addEventListener("keydown", async (e) => {
|
||||||
|
|
||||||
let pageLoad = new Date();
|
let pageLoad = new Date();
|
||||||
document.addEventListener("keyup", async (e) => {
|
document.addEventListener("keyup", async (e) => {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const focused = document.querySelector("button:focus");
|
const focused = document.querySelector("button:focus");
|
||||||
if (e.key in pressed) {
|
if (e.key in pressed) {
|
||||||
setKeyPressed(e.key, 0);
|
setKeyPressed(e.key, 0);
|
||||||
|
@ -950,7 +968,7 @@ document.addEventListener("keyup", async (e) => {
|
||||||
} else if (
|
} else if (
|
||||||
e.key.toLowerCase() === "r" &&
|
e.key.toLowerCase() === "r" &&
|
||||||
!alertsOpen &&
|
!alertsOpen &&
|
||||||
pageLoad > Date.now() + 1000
|
pageLoad < Date.now() - 500
|
||||||
) {
|
) {
|
||||||
// When doing ctrl + R in dev to refresh, i don't want to instantly restart a run
|
// When doing ctrl + R in dev to refresh, i don't want to instantly restart a run
|
||||||
if (await confirmRestart(gameState)) {
|
if (await confirmRestart(gameState)) {
|
||||||
|
|
|
@ -136,6 +136,7 @@ export function gameOver(title: string, intro: string) {
|
||||||
}).then(() =>
|
}).then(() =>
|
||||||
restart({
|
restart({
|
||||||
levelToAvoid: currentLevelInfo(gameState).name,
|
levelToAvoid: currentLevelInfo(gameState).name,
|
||||||
|
mode:gameState.mode
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1732,6 +1732,66 @@
|
||||||
</translation>
|
</translation>
|
||||||
</translations>
|
</translations>
|
||||||
</concept_node>
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>starting_perks</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>starting_perks_checked</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>starting_perks_help</name>
|
||||||
|
<description/>
|
||||||
|
<comment/>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-FR</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
|
<concept_node>
|
||||||
|
<name>starting_perks_unchecked</name>
|
||||||
|
<description/>
|
||||||
|
<comment/>
|
||||||
|
<translations>
|
||||||
|
<translation>
|
||||||
|
<language>en-US</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
<translation>
|
||||||
|
<language>fr-FR</language>
|
||||||
|
<approved>false</approved>
|
||||||
|
</translation>
|
||||||
|
</translations>
|
||||||
|
</concept_node>
|
||||||
<concept_node>
|
<concept_node>
|
||||||
<name>title</name>
|
<name>title</name>
|
||||||
<description/>
|
<description/>
|
||||||
|
|
|
@ -110,6 +110,10 @@
|
||||||
"main_menu.show_stats_help": "Coins, time, bounces, misses",
|
"main_menu.show_stats_help": "Coins, time, bounces, misses",
|
||||||
"main_menu.sounds": "Game sounds",
|
"main_menu.sounds": "Game sounds",
|
||||||
"main_menu.sounds_help": "Can slow down some phones.",
|
"main_menu.sounds_help": "Can slow down some phones.",
|
||||||
|
"main_menu.starting_perks": "Starting perks",
|
||||||
|
"main_menu.starting_perks_checked": "When you start a new game, one of those perks will be give to you. You can click the list to exclude some perks from the pool.",
|
||||||
|
"main_menu.starting_perks_help": "Choose possible starting upgrades",
|
||||||
|
"main_menu.starting_perks_unchecked": "The perks below are not offered as starting perks, but you can click to add them to the pool. ",
|
||||||
"main_menu.title": "Breakout 71",
|
"main_menu.title": "Breakout 71",
|
||||||
"main_menu.unlocks": "Unlocked content",
|
"main_menu.unlocks": "Unlocked content",
|
||||||
"main_menu.unlocks_help": "Try perks and levels you unlocked",
|
"main_menu.unlocks_help": "Try perks and levels you unlocked",
|
||||||
|
|
|
@ -110,6 +110,10 @@
|
||||||
"main_menu.show_stats_help": "Pièces, temps, rebonds, ratés",
|
"main_menu.show_stats_help": "Pièces, temps, rebonds, ratés",
|
||||||
"main_menu.sounds": "Sons du jeu",
|
"main_menu.sounds": "Sons du jeu",
|
||||||
"main_menu.sounds_help": "Ralentis certains téléphones.",
|
"main_menu.sounds_help": "Ralentis certains téléphones.",
|
||||||
|
"main_menu.starting_perks": "Avantages de départ",
|
||||||
|
"main_menu.starting_perks_checked": "Lorsque vous démarrez une nouvelle partie, l'un de ces avantages vous sera attribué. Vous pouvez cliquer sur la liste pour exclure certains avantages de la sélection.",
|
||||||
|
"main_menu.starting_perks_help": "Choisissez les avantages de départ",
|
||||||
|
"main_menu.starting_perks_unchecked": "Les avantages ci-dessous ne sont pas proposés comme avantages de départ, mais vous pouvez cliquer pour les ajouter aux avantages de départ possibles.",
|
||||||
"main_menu.title": "Breakout 71",
|
"main_menu.title": "Breakout 71",
|
||||||
"main_menu.unlocks": "Contenu débloqué",
|
"main_menu.unlocks": "Contenu débloqué",
|
||||||
"main_menu.unlocks_help": "Essayez les éléments débloqués",
|
"main_menu.unlocks_help": "Essayez les éléments débloqués",
|
||||||
|
|
|
@ -397,18 +397,16 @@ export function render(gameState: GameState) {
|
||||||
ctx.globalCompositeOperation = "source-over";
|
ctx.globalCompositeOperation = "source-over";
|
||||||
ctx.globalAlpha = gameState.perks.unbounded ? 0.1 : 1;
|
ctx.globalAlpha = gameState.perks.unbounded ? 0.1 : 1;
|
||||||
|
|
||||||
|
let redLeftSide = hasCombo &&!gameState.perks.unbounded&& (gameState.perks.left_is_lava || gameState.perks.trampoline)
|
||||||
|
let redRightSide = hasCombo &&!gameState.perks.unbounded&& (gameState.perks.right_is_lava || gameState.perks.trampoline)
|
||||||
|
let redTop = hasCombo && gameState.perks.unbounded<=2 && (gameState.perks.top_is_lava || gameState.perks.trampoline)
|
||||||
|
|
||||||
if (gameState.offsetXRoundedDown) {
|
if (gameState.offsetXRoundedDown) {
|
||||||
// draw outside of gaming area to avoid capturing borders in recordings
|
// draw outside of gaming area to avoid capturing borders in recordings
|
||||||
ctx.fillStyle =
|
|
||||||
hasCombo && gameState.perks.left_is_lava ? "red" : gameState.puckColor;
|
|
||||||
|
|
||||||
drawStraightLine(
|
drawStraightLine(
|
||||||
ctx,
|
ctx,
|
||||||
gameState,
|
gameState,
|
||||||
(hasCombo &&
|
(redLeftSide && "red") ||
|
||||||
gameState.perks.left_is_lava &&
|
|
||||||
!gameState.perks.unbounded &&
|
|
||||||
"red") ||
|
|
||||||
"white",
|
"white",
|
||||||
gameState.offsetX - 1,
|
gameState.offsetX - 1,
|
||||||
0,
|
0,
|
||||||
|
@ -420,10 +418,7 @@ export function render(gameState: GameState) {
|
||||||
drawStraightLine(
|
drawStraightLine(
|
||||||
ctx,
|
ctx,
|
||||||
gameState,
|
gameState,
|
||||||
(hasCombo &&
|
(redRightSide && "red") ||
|
||||||
gameState.perks.right_is_lava &&
|
|
||||||
!gameState.perks.unbounded &&
|
|
||||||
"red") ||
|
|
||||||
"white",
|
"white",
|
||||||
width - gameState.offsetX + 1,
|
width - gameState.offsetX + 1,
|
||||||
0,
|
0,
|
||||||
|
@ -432,15 +427,11 @@ export function render(gameState: GameState) {
|
||||||
gameState.perks.unbounded ? 0.1 : 1,
|
gameState.perks.unbounded ? 0.1 : 1,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
ctx.fillStyle = "red";
|
|
||||||
|
|
||||||
drawStraightLine(
|
drawStraightLine(
|
||||||
ctx,
|
ctx,
|
||||||
gameState,
|
gameState,
|
||||||
(hasCombo &&
|
(redLeftSide && "red") ||
|
||||||
gameState.perks.left_is_lava &&
|
|
||||||
!gameState.perks.unbounded &&
|
|
||||||
"red") ||
|
|
||||||
"",
|
"",
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
|
@ -452,10 +443,7 @@ export function render(gameState: GameState) {
|
||||||
drawStraightLine(
|
drawStraightLine(
|
||||||
ctx,
|
ctx,
|
||||||
gameState,
|
gameState,
|
||||||
(hasCombo &&
|
(redRightSide && "red") ||
|
||||||
gameState.perks.right_is_lava &&
|
|
||||||
!gameState.perks.unbounded &&
|
|
||||||
"red") ||
|
|
||||||
"",
|
"",
|
||||||
width - 1,
|
width - 1,
|
||||||
0,
|
0,
|
||||||
|
@ -464,15 +452,14 @@ export function render(gameState: GameState) {
|
||||||
1,
|
1,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if(redTop)
|
||||||
ctx.globalAlpha = gameState.perks.unbounded > 1 ? 0.1 : 1;
|
|
||||||
drawStraightLine(
|
drawStraightLine(
|
||||||
ctx,
|
ctx,
|
||||||
gameState,
|
gameState,
|
||||||
(hasCombo && gameState.perks.top_is_lava && "red") || "",
|
"red",
|
||||||
gameState.offsetXRoundedDown,
|
gameState.perks.unbounded ? 0 : gameState.offsetXRoundedDown,
|
||||||
1,
|
1,
|
||||||
width - gameState.offsetXRoundedDown,
|
gameState.perks.unbounded ? width : width - gameState.offsetXRoundedDown,
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
);
|
);
|
||||||
|
|
54
src/startingPerks.ts
Normal file
54
src/startingPerks.ts
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
import {asyncAlert} from "./asyncAlert";
|
||||||
|
import {PerkId, Upgrade} from "./types";
|
||||||
|
import {t} from "./i18n/i18n";
|
||||||
|
import {icons, upgrades} from "./loadGameData";
|
||||||
|
import {getSettingValue, getTotalScore, setSettingValue} from "./settings";
|
||||||
|
|
||||||
|
|
||||||
|
export function startingPerkMenuButton(){
|
||||||
|
return {
|
||||||
|
icon:icons['icon:starting_perks'],
|
||||||
|
text:t('main_menu.starting_perks'),
|
||||||
|
help:t('main_menu.starting_perks_help'),
|
||||||
|
async value(){
|
||||||
|
await openStartingPerksEditor()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function isChecked(u:Upgrade):boolean{
|
||||||
|
return getSettingValue('start_with_'+u.id, u.giftable)
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function openStartingPerksEditor(){
|
||||||
|
const ts=getTotalScore()
|
||||||
|
const avaliable=upgrades.filter(u=>!u.requires && !['instant_upgrade'].includes(u.id) && u.threshold<=ts)
|
||||||
|
const starting = avaliable.filter(u=>isChecked(u))
|
||||||
|
const buttons=avaliable
|
||||||
|
.map(u=> {
|
||||||
|
const checked = isChecked(u);
|
||||||
|
return {
|
||||||
|
icon: u.icon,
|
||||||
|
text: u.name,
|
||||||
|
tooltip: u.help(1),
|
||||||
|
value:u,
|
||||||
|
disabled:checked && starting.length<2,
|
||||||
|
checked
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const perk :Upgrade|null|void= await asyncAlert({
|
||||||
|
title:t('main_menu.starting_perks'),
|
||||||
|
actionsAsGrid:true,
|
||||||
|
content:[
|
||||||
|
t('main_menu.starting_perks_checked'),
|
||||||
|
...buttons.filter(b=>b.checked),
|
||||||
|
t('main_menu.starting_perks_unchecked'),
|
||||||
|
...buttons.filter(b=>!b.checked),
|
||||||
|
|
||||||
|
]
|
||||||
|
})
|
||||||
|
if(perk){
|
||||||
|
setSettingValue('start_with_'+perk.id,!isChecked(perk))
|
||||||
|
openStartingPerksEditor()
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,12 +7,18 @@ export function setupTooltips() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function updateTooltipPosition(e: MouseEvent) {
|
function updateTooltipPosition(e: MouseEvent) {
|
||||||
tooltip.style.transform =
|
tooltip.style.transform =
|
||||||
`translate(${e.clientX}px,${e.clientY + 20}px) ` +
|
`translate(${e.clientX}px,${e.clientY + 20}px) ` +
|
||||||
(e.clientX > window.innerWidth / 2 ? " translate(-100%,0)" : "");
|
(e.clientX > window.innerWidth / 2 ? " translate(-100%,0)" : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function closeToolTip(){
|
||||||
|
tooltip.style.display = "none";
|
||||||
|
hovering=null
|
||||||
|
}
|
||||||
|
let hovering:HTMLElement|null=null
|
||||||
document.body.addEventListener(
|
document.body.addEventListener(
|
||||||
"mouseenter",
|
"mouseenter",
|
||||||
(e: MouseEvent) => {
|
(e: MouseEvent) => {
|
||||||
|
@ -21,15 +27,24 @@ export function setupTooltips() {
|
||||||
parent = parent.parentElement;
|
parent = parent.parentElement;
|
||||||
}
|
}
|
||||||
if (parent?.hasAttribute("data-tooltip")) {
|
if (parent?.hasAttribute("data-tooltip")) {
|
||||||
tooltip.innerHTML = parent?.getAttribute("data-tooltip");
|
hovering=parent as HTMLElement
|
||||||
|
tooltip.innerHTML = hovering.getAttribute("data-tooltip") || '';
|
||||||
tooltip.style.display = "";
|
tooltip.style.display = "";
|
||||||
updateTooltipPosition(e);
|
updateTooltipPosition(e);
|
||||||
} else {
|
} else {
|
||||||
tooltip.style.display = "none";
|
closeToolTip()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
setInterval(()=>{
|
||||||
|
if(hovering){
|
||||||
|
if(!document.body.contains(hovering)){
|
||||||
|
closeToolTip()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},200)
|
||||||
document.body.addEventListener(
|
document.body.addEventListener(
|
||||||
"mousemove",
|
"mousemove",
|
||||||
(e) => {
|
(e) => {
|
||||||
|
@ -42,8 +57,7 @@ export function setupTooltips() {
|
||||||
document.body.addEventListener(
|
document.body.addEventListener(
|
||||||
"mouseleave",
|
"mouseleave",
|
||||||
(e) => {
|
(e) => {
|
||||||
// tooltip.style.display = 'none';
|
closeToolTip()
|
||||||
},
|
}
|
||||||
true,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue