mirror of
https://gitlab.com/lecarore/breakout71.git
synced 2025-04-25 14:36:15 -04:00
wip
This commit is contained in:
parent
46228a2128
commit
9624c5b351
16 changed files with 518 additions and 52 deletions
|
@ -43,7 +43,7 @@ export async function asyncAlert<t>({
|
|||
content: (string | AsyncAlertAction<t>)[];
|
||||
allowClose?: boolean;
|
||||
className?: string;
|
||||
}): Promise<t | void> {
|
||||
}): Promise<t | void|string> {
|
||||
updateAlertsOpen(+1);
|
||||
return new Promise((resolve) => {
|
||||
popupWrap.className = className;
|
||||
|
@ -139,6 +139,11 @@ ${icon}
|
|||
addto.appendChild(button);
|
||||
});
|
||||
|
||||
popup.addEventListener('click', e=>{
|
||||
if(e.target.getAttribute('data-resolve-to')){
|
||||
closeWithResult(e.target.getAttribute('data-resolve-to'))
|
||||
}
|
||||
},true)
|
||||
popupWrap.appendChild(popup);
|
||||
(
|
||||
popupWrap.querySelector(
|
||||
|
|
|
@ -1280,5 +1280,12 @@
|
|||
"bricks": "_________________________bbb____ttb_bbbbb__tttbbbb_bbbttt_bbbb__bbbt__bbbb_ttbbb__bbttttttbbbbbb_ttt___bbbb_____________________________________",
|
||||
"svg": null,
|
||||
"color": ""
|
||||
},
|
||||
{
|
||||
"name": "icon:history",
|
||||
"size": 8,
|
||||
"bricks": "__gggg___ggbggg_gggbgggggggbggggggggbbgggggggggg_gggggg___gggg__",
|
||||
"svg": null,
|
||||
"color": ""
|
||||
}
|
||||
]
|
||||
]
|
|
@ -1,10 +1,9 @@
|
|||
* {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -465,3 +464,32 @@ h2.histogram-title strong {
|
|||
border: 1px solid white;
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
#popup.history > div {
|
||||
max-width: none;
|
||||
|
||||
table {
|
||||
th:hover{
|
||||
cursor: pointer;
|
||||
background: black;
|
||||
}
|
||||
td, th {
|
||||
padding: 0 5px;
|
||||
line-height: 20px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
th:first-child, td:first-child {
|
||||
text-align: left
|
||||
}
|
||||
img{
|
||||
width: 20px;
|
||||
height: auto;
|
||||
pointer-events: none;
|
||||
}
|
||||
tr:nth-child(2n) {
|
||||
background: rgba(0, 0, 0, 0.58);;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ import {startingPerkMenuButton} from "./startingPerks";
|
|||
import "./migrations";
|
||||
import {getCreativeModeWarning, getHistory} from "./gameOver";
|
||||
import {generateSaveFileContent} from "./generateSaveFileContent";
|
||||
import {runHistoryViewerMenuEntry} from "./runHistoryViewer";
|
||||
|
||||
export async function play() {
|
||||
if (await applyFullScreenChoice()) return;
|
||||
|
@ -460,6 +461,7 @@ export async function openMainMenu() {
|
|||
},
|
||||
},
|
||||
creativeMode(gameState),
|
||||
runHistoryViewerMenuEntry(),
|
||||
{
|
||||
icon: icons["icon:unlocks"],
|
||||
text: t("main_menu.unlocks"),
|
||||
|
|
|
@ -33,7 +33,6 @@ export function gameOver(title: string, intro: string) {
|
|||
pause(true);
|
||||
stopRecording();
|
||||
addToTotalPlayTime(gameState.runStatistics.runTime);
|
||||
gameState.runStatistics.max_level = gameState.currentLevel + 1;
|
||||
|
||||
let animationDelay = -300;
|
||||
const getDelay = () => {
|
||||
|
@ -112,6 +111,7 @@ try {
|
|||
localStorage.getItem("breakout_71_runs_history") || "[]",
|
||||
) as RunHistoryItem[];
|
||||
} catch (e) {}
|
||||
|
||||
export function getHistory() {
|
||||
return runsHistory;
|
||||
}
|
||||
|
|
|
@ -596,23 +596,6 @@ export function addToScore(gameState: GameState, coin: Coin) {
|
|||
}
|
||||
}
|
||||
|
||||
function recordBestWorstLevelScore(gameState: GameState) {
|
||||
const levelScore = gameState.score - gameState.levelStartScore;
|
||||
const { runStatistics } = gameState;
|
||||
if (
|
||||
runStatistics.best_level_score === -1 ||
|
||||
runStatistics.best_level_score < levelScore
|
||||
) {
|
||||
runStatistics.best_level_score = levelScore;
|
||||
}
|
||||
if (
|
||||
runStatistics.worst_level_score === -1 ||
|
||||
runStatistics.worst_level_score > levelScore
|
||||
) {
|
||||
runStatistics.worst_level_score = levelScore;
|
||||
}
|
||||
}
|
||||
|
||||
export async function setLevel(gameState: GameState, l: number) {
|
||||
// Here to alleviate double upgrades issues
|
||||
if (gameState.upgradesOfferedFor >= l) {
|
||||
|
@ -622,7 +605,6 @@ export async function setLevel(gameState: GameState, l: number) {
|
|||
gameState.upgradesOfferedFor = l;
|
||||
pause(false);
|
||||
stopRecording();
|
||||
recordBestWorstLevelScore(gameState);
|
||||
|
||||
if (l > 0) {
|
||||
await openUpgradesPicker(gameState);
|
||||
|
|
|
@ -419,6 +419,196 @@
|
|||
</folder_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
<name>history</name>
|
||||
<children>
|
||||
<folder_node>
|
||||
<name>columns</name>
|
||||
<children>
|
||||
<concept_node>
|
||||
<name>max_combo</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>max_level</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>puck_bounces</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>puck_bounces_tooltip</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>runTime</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>runTime_tooltip</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>score</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>started</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>upgrades_picked</name>
|
||||
<description/>
|
||||
<comment/>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-FR</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
<concept_node>
|
||||
<name>help</name>
|
||||
<description/>
|
||||
<comment/>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-FR</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
<concept_node>
|
||||
<name>locked</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>title</name>
|
||||
<description/>
|
||||
<comment/>
|
||||
<translations>
|
||||
<translation>
|
||||
<language>en-US</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
<translation>
|
||||
<language>fr-FR</language>
|
||||
<approved>false</approved>
|
||||
</translation>
|
||||
</translations>
|
||||
</concept_node>
|
||||
</children>
|
||||
</folder_node>
|
||||
<folder_node>
|
||||
<name>lab</name>
|
||||
<children>
|
||||
|
|
|
@ -24,6 +24,18 @@
|
|||
"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",
|
||||
"history.columns.max_combo": "Max combo",
|
||||
"history.columns.max_level": "Levels",
|
||||
"history.columns.puck_bounces": "PB",
|
||||
"history.columns.puck_bounces_tooltip": "Puck bounces : number of time the ball bounced on the puck",
|
||||
"history.columns.runTime": "Dur.",
|
||||
"history.columns.runTime_tooltip": "Duration of the run, in seconds, only counting time where the game is running and the ball is in motion",
|
||||
"history.columns.score": "Score",
|
||||
"history.columns.started": "Date",
|
||||
"history.columns.upgrades_picked": "Upgrades",
|
||||
"history.help": "See the list of your {{count}} game",
|
||||
"history.locked": "Play at least ten games to unlock",
|
||||
"history.title": "Runs history",
|
||||
"lab.help": "Try any build you want",
|
||||
"lab.instructions": "Select upgrades below, then pick the level to play. Creative mode runs are ignored in unlocks, high score, total score and statistics, and only last one level.",
|
||||
"lab.menu_entry": "Creative mode",
|
||||
|
|
|
@ -24,6 +24,18 @@
|
|||
"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",
|
||||
"history.columns.max_combo": "",
|
||||
"history.columns.max_level": "",
|
||||
"history.columns.puck_bounces": "",
|
||||
"history.columns.puck_bounces_tooltip": "",
|
||||
"history.columns.runTime": "Dur.",
|
||||
"history.columns.runTime_tooltip": "",
|
||||
"history.columns.score": "",
|
||||
"history.columns.started": "",
|
||||
"history.columns.upgrades_picked": "",
|
||||
"history.help": "",
|
||||
"history.locked": "",
|
||||
"history.title": "",
|
||||
"lab.help": "Essayez n'importe quel build",
|
||||
"lab.instructions": "Sélectionnez les améliorations ci-dessous, puis choisissez le niveau à jouer. Les parties en mode créatif sont ignorées dans les déblocages, le meilleur score, le score total et les statistiques, et ne durent qu'un seul niveau.",
|
||||
"lab.menu_entry": "Mode créatif",
|
||||
|
|
|
@ -78,7 +78,15 @@ migrate("compact_runs_data", () => {
|
|||
delete r.perks[key]
|
||||
}
|
||||
}
|
||||
if('best_level_score' in r) {
|
||||
delete r.best_level_score
|
||||
}
|
||||
if('worst_level_score' in r) {
|
||||
delete r.worst_level_score
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
localStorage.setItem("breakout_71_runs_history", JSON.stringify(runsHistory));
|
||||
});
|
||||
|
||||
|
|
|
@ -103,8 +103,6 @@ export function newGameState(params: RunParams): GameState {
|
|||
runTime: 0,
|
||||
coins_spawned: 0,
|
||||
score: 0,
|
||||
best_level_score: -1,
|
||||
worst_level_score: -1,
|
||||
bricks_broken: 0,
|
||||
misses: 0,
|
||||
balls_lost: 0,
|
||||
|
@ -112,7 +110,6 @@ export function newGameState(params: RunParams): GameState {
|
|||
wall_bounces: 0,
|
||||
upgrades_picked: 1,
|
||||
max_combo: 1,
|
||||
max_level: 0,
|
||||
},
|
||||
lastOffered: {},
|
||||
levelTime: 0,
|
||||
|
|
99
src/runHistoryViewer.ts
Normal file
99
src/runHistoryViewer.ts
Normal file
|
@ -0,0 +1,99 @@
|
|||
import {getHistory} from "./gameOver";
|
||||
import {icons} from "./loadGameData";
|
||||
import {t} from "./i18n/i18n";
|
||||
import {asyncAlert} from "./asyncAlert";
|
||||
import {rawUpgrades} from "./upgrades";
|
||||
|
||||
export function runHistoryViewerMenuEntry(){
|
||||
const history = getHistory()
|
||||
|
||||
return {
|
||||
icon:icons['icon:history'],
|
||||
text:t('history.title'),
|
||||
disabled : history.length<10,
|
||||
help: history.length<10 ? t('history.locked'):t('history.help',{count:history.length}),
|
||||
async value(){
|
||||
let sort = 0
|
||||
let sortDir = -1
|
||||
let columns = [
|
||||
{
|
||||
label:t('history.columns.started'),
|
||||
field: r=>r.started,
|
||||
render(v){
|
||||
return new Date(v).toISOString().slice(0,10)
|
||||
}
|
||||
},
|
||||
{
|
||||
label:t('history.columns.score'),
|
||||
field: r=>r.score
|
||||
},
|
||||
{
|
||||
label:t('history.columns.runTime'),
|
||||
tooltip:t('history.columns.runTime_tooltip'),
|
||||
|
||||
field: r=>r.runTime,
|
||||
render(v){
|
||||
return Math.floor(v/1000)+'s'
|
||||
}
|
||||
},
|
||||
{
|
||||
label:t('history.columns.puck_bounces'),
|
||||
tooltip:t('history.columns.puck_bounces_tooltip'),
|
||||
field: r=>r.puck_bounces,
|
||||
},
|
||||
{
|
||||
label:t('history.columns.max_combo'),
|
||||
field: r=>r.max_combo,
|
||||
},
|
||||
{
|
||||
label:t('history.columns.upgrades_picked'),
|
||||
field: r=>r.upgrades_picked,
|
||||
},
|
||||
...rawUpgrades.map(u=>({
|
||||
label: icons['icon:'+u.id],
|
||||
tooltip:u.name,
|
||||
field: r=>r.perks[u.id]||0,
|
||||
render(v){
|
||||
if(!v) return '-'
|
||||
return v
|
||||
}
|
||||
}))
|
||||
]
|
||||
while(true){
|
||||
const header = columns.map((c, ci) => `<th data-tooltip="${c.tooltip || ''}" data-resolve-to="sort:${ci}">${c.label}</th>`).join('')
|
||||
const toString = v => v.toString()
|
||||
const tbody = history.sort((a, b) => sortDir * (columns[sort].field(a) - columns[sort].field(b))).map(h => '<tr>' + columns.map(c => {
|
||||
const value = c.field(h) ?? 0
|
||||
const render = c.render || toString
|
||||
return '<td>' + render(value) + '</td>'
|
||||
}).join('') + '</tr>').join('')
|
||||
|
||||
|
||||
const result = await asyncAlert({
|
||||
title: t('history.title'),
|
||||
className: 'history',
|
||||
content: [
|
||||
`
|
||||
<table>
|
||||
<thead><tr>${header}</tr></thead>
|
||||
<tbody>${tbody}</tbody>
|
||||
</table>
|
||||
`
|
||||
|
||||
]
|
||||
})
|
||||
if(!result) return
|
||||
if(result.startsWith('sort:')){
|
||||
const newSort = parseInt(result.split(':')[1])
|
||||
if(newSort==sort){
|
||||
sortDir*=-1
|
||||
}else{
|
||||
sortDir=-1
|
||||
sort=newSort
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -25,7 +25,7 @@ export function setupTooltips() {
|
|||
while (parent && !parent.hasAttribute("data-tooltip")) {
|
||||
parent = parent.parentElement;
|
||||
}
|
||||
if (parent?.hasAttribute("data-tooltip")) {
|
||||
if (parent?.getAttribute("data-tooltip")?.trim()) {
|
||||
hovering = parent as HTMLElement;
|
||||
tooltip.innerHTML = hovering.getAttribute("data-tooltip") || "";
|
||||
tooltip.style.display = "";
|
||||
|
|
3
src/types.d.ts
vendored
3
src/types.d.ts
vendored
|
@ -147,9 +147,6 @@ export type RunStats = {
|
|||
wall_bounces: number;
|
||||
upgrades_picked: number;
|
||||
max_combo: number;
|
||||
max_level: number;
|
||||
best_level_score: number;
|
||||
worst_level_score: number;
|
||||
};
|
||||
|
||||
export type PerksMap = {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue