diff --git a/js/game.js b/js/game.js index ebdd85b..a247f1e 100644 --- a/js/game.js +++ b/js/game.js @@ -2235,7 +2235,7 @@ export class Level extends LevelInterface { return null; } - let time = Math.ceil((this.time_remaining ?? 0) / 20); + let time = Math.ceil((this.time_remaining ?? 0) / TICS_PER_SECOND); return { time: time, abstime: this.tic_counter, diff --git a/js/main.js b/js/main.js index 41ebc2a..779f4ed 100644 --- a/js/main.js +++ b/js/main.js @@ -1941,35 +1941,41 @@ class Player extends PrimaryView { const BUILTIN_LEVEL_PACKS = [{ path: 'levels/lexys-lessons.zip', + preview: 'levels/previews/lexys-lessons.png', ident: "Lexy's Lessons", title: "Lexy's Lessons (WIP)", desc: "A set of beginner levels that introduces every mechanic in Chip's Challenge 2, made specifically for Lexy's Labyrinth!", }, { path: 'levels/CC2LP1.zip', + preview: 'levels/previews/cc2lp1.png', ident: 'Chips Challenge 2 Level Pack 1', title: "Chip's Challenge 2 Level Pack 1", desc: "Thoroughly demonstrates what Chip's Challenge 2 is capable of. Fair, but doesn't hold your hand; you'd better have at least a passing familiarity with the CC2 elements.", url: 'https://wiki.bitbusters.club/Chip%27s_Challenge_2_Level_Pack_1', }, { path: 'levels/CCLP1.ccl', + preview: 'levels/previews/cclp1.png', ident: 'cclp1', title: "Chip's Challenge Level Pack 1", desc: "Designed like a direct replacement for Chip's Challenge 1, with introductory levels for new players and a gentle difficulty curve.", url: 'https://wiki.bitbusters.club/Chip%27s_Challenge_Level_Pack_1', }, { path: 'levels/CCLP4.ccl', + preview: 'levels/previews/cclp4.png', ident: 'cclp4', title: "Chip's Challenge Level Pack 4", desc: "Moderately difficult, but not unfair.", url: 'https://wiki.bitbusters.club/Chip%27s_Challenge_Level_Pack_4', }, { path: 'levels/CCLXP2.ccl', + preview: 'levels/previews/cclxp2.png', ident: 'cclxp2', title: "Chip's Challenge Level Pack 2-X", desc: "The first community pack released, tricky and rough around the edges.", url: 'https://wiki.bitbusters.club/Chip%27s_Challenge_Level_Pack_2_(Lynx)', }, { path: 'levels/CCLP3.ccl', + preview: 'levels/previews/cclp3.png', ident: 'cclp3', title: "Chip's Challenge Level Pack 3", desc: "A tough challenge, by and for veteran players.", @@ -2253,7 +2259,12 @@ class Splash extends PrimaryView { button.disabled = true; } - let li = mk('li.--unplayed', {'data-ident': ident}, button); + let li = mk('li.--unplayed', {'data-ident': ident}); + if (packdef && packdef.preview) { + li.append(mk('img.-preview', {src: packdef.preview})); + } + li.append(button); + let forget_button = mk('button.-forget', {type: 'button'}, "Forget"); forget_button.addEventListener('click', ev => { new ConfirmOverlay(this.conductor, `Clear all your progress for ${title}? This can't be undone.`, () => { @@ -2268,6 +2279,13 @@ class Splash extends PrimaryView { } }).open(); }); + li.append(mk('div.-progress', + mk('div.-levels'), + mk('span.-score'), + mk('span.-time'), + forget_button, + )); + if (packdef) { let p = mk('p', packdef.desc); if (packdef.url) { @@ -2275,12 +2293,7 @@ class Splash extends PrimaryView { } li.append(p); } - li.append(mk('div.-progress', - mk('span.-score'), - mk('span.-time'), - mk('span.-levels'), - forget_button, - )); + this.played_pack_elements[ident] = li; return li; } @@ -2322,19 +2335,19 @@ class Splash extends PrimaryView { } progress.querySelector('.-score').textContent = score; - // This stuff isn't available in old saves + let level_el = progress.querySelector('.-levels'); if (packinfo.total_levels === undefined) { + // This stuff isn't available in old saves progress.querySelector('.-time').textContent = ""; progress.querySelector('.-levels').textContent = ""; } else { - // TODO not used: total_abstime, aidless_levels - progress.querySelector('.-time').textContent = util.format_duration(packinfo.total_time); + progress.querySelector('.-time').textContent = util.format_duration(packinfo.total_abstime / TICS_PER_SECOND, 2); let levels = `${packinfo.cleared_levels}/${packinfo.total_levels}`; - if (packinfo.cleared_levels === packinfo.total_levels) { - levels += '★'; - } - progress.querySelector('.-levels').textContent = levels; + levels = `cleared ${packinfo.cleared_levels} of ${packinfo.total_levels} levels, ${packinfo.aidless_levels}★ without aid`; + level_el.textContent = levels; + level_el.style.setProperty('--cleared', packinfo.cleared_levels / packinfo.total_levels); + level_el.style.setProperty('--aidless', packinfo.aidless_levels / packinfo.total_levels); } } @@ -3354,7 +3367,8 @@ class LevelBrowserOverlay extends DialogOverlay { // Main storage: // packs: // total_score -// total_time +// total_time -- FIXME this is nonsense lol, it's time left on the clock +// total_abstime // total_levels // cleared_levels // aidless_levels diff --git a/levels/previews/cc2lp1.png b/levels/previews/cc2lp1.png new file mode 100644 index 0000000..7eccc3e Binary files /dev/null and b/levels/previews/cc2lp1.png differ diff --git a/levels/previews/cclp1.png b/levels/previews/cclp1.png new file mode 100644 index 0000000..4284f84 Binary files /dev/null and b/levels/previews/cclp1.png differ diff --git a/levels/previews/cclp3.png b/levels/previews/cclp3.png new file mode 100644 index 0000000..bb4ee25 Binary files /dev/null and b/levels/previews/cclp3.png differ diff --git a/levels/previews/cclp4.png b/levels/previews/cclp4.png new file mode 100644 index 0000000..f888ef8 Binary files /dev/null and b/levels/previews/cclp4.png differ diff --git a/levels/previews/cclxp2.png b/levels/previews/cclxp2.png new file mode 100644 index 0000000..e2f5832 Binary files /dev/null and b/levels/previews/cclxp2.png differ diff --git a/levels/previews/lexys-lessons.png b/levels/previews/lexys-lessons.png new file mode 100644 index 0000000..81df4a5 Binary files /dev/null and b/levels/previews/lexys-lessons.png differ diff --git a/style.css b/style.css index 24c17a8..241566d 100644 --- a/style.css +++ b/style.css @@ -208,12 +208,15 @@ svg.svg-icon { } .button-row { - display: flex; + display: grid; + /* Put the buttons in a row most of the time, but change to a column when out of space */ + grid: auto-flow auto / repeat(auto-fit, minmax(10em, 1fr)); gap: 0.5em; align-items: stretch; + margin: 0.5em 0; } .button-row > button { - flex: 1; + margin: 0; } /* Overlay styling */ @@ -556,12 +559,12 @@ pre.stack-trace { "header header" "links links" "stock yours" - / 1fr 1fr + / 3fr minmax(18em, 1fr) ; gap: 1em; position: relative; - padding: 1em 10%; + padding: 1em 7.5%; margin: auto 0; overflow: auto; } @@ -701,21 +704,17 @@ pre.stack-trace { .played-pack-list { } -.played-pack-list > li { - margin-bottom: 1em; +#splash-stock-levels .played-pack-list { + display: grid; + grid: auto-flow auto / repeat(auto-fill, minmax(320px, 1fr)); /* 10x10 */ + gap: 1em; + margin: 1em 0; } -.played-pack-list p { - margin: 0 0.5em; - color: #e8e8e8; -} -.played-pack-list .-progress { - display: flex; - align-items: center; - gap: 0.5em; - text-align: right; -} -.played-pack-list > li.--unplayed .-progress { - display: none; +.played-pack-list .-preview { + display: block; + height: 320px; + margin: auto; + object-fit: none; } .played-pack-list > li > button { font-size: 1.25em; @@ -735,18 +734,72 @@ pre.stack-trace { .played-pack-list > li > button:enabled:hover { background: hsl(225, 40%, 30%); } -.played-pack-list .-progress > .-score, -.played-pack-list .-progress > .-time, -.played-pack-list .-progress > .-levels { - flex: 2; +.played-pack-list p { + color: #c0c0c0; + font-style: italic; } -.played-pack-list .-progress > button { - flex: 1; +.played-pack-list .-progress { + display: grid; + grid: + "levels levels levels" + "score time button" + / 2fr 2fr 1fr + ; + gap: 0.5em; + margin: 0.5em 0; + align-items: center; +} +.played-pack-list > li.--unplayed .-progress { + display: none; +} +.played-pack-list .-progress > .-levels { + grid-area: levels; + + position: relative; + z-index: 1; + padding: 0.25em; + border: 1px solid hsl(225, 25%, 40%); + text-shadow: 0 1px 1px black; + text-align: center; +} +.played-pack-list .-progress > .-levels::before, +.played-pack-list .-progress > .-levels::after { + content: ''; + position: absolute; + z-index: -1; + top: 0; + bottom: 0; + left: 0; +} +.played-pack-list .-progress > .-levels::before { + width: calc(var(--cleared) * 100%); + background: hsl(225, 25%, 30%); +} +.played-pack-list .-progress > .-levels::after { + width: calc(var(--aidless) * 100%); + background: hsl(225, 25%, 40%); +} +.played-pack-list .-progress > .-score { + grid-area: score; +} +.played-pack-list .-progress > .-time { + grid-area: time; +} +.played-pack-list .-progress > .-levels { + grid-area: levels; +} +.played-pack-list .-progress > .-score::before { + content: "Score: "; + color: #909090; +} +.played-pack-list .-progress > .-time::before { + content: "Time: "; + color: #909090; } .played-pack-list .-editor-status { display: flex; gap: 0.5em; - margin: 0 0.5em; + margin: 0.5em 0 1em; } .played-pack-list .-editor-status > .-level-count { flex: auto; @@ -1777,7 +1830,7 @@ body.--debug #player-debug { .editor-level-browser { display: grid; - grid: auto-flow auto / repeat(auto-fill, minmax(13em, 1fr)); /* 12em preview wdith + padding */ + grid: auto-flow auto / repeat(auto-fill, minmax(13em, 1fr)); /* 12em preview width + padding */ gap: 0.5em; width: 70vw; /* seems to go into the parent's right padding fsr, i guess because the scrollbar is there */