Add a loading and error screen; also include compat CSS oops

This commit is contained in:
Eevee (Evelyn Woods) 2020-12-24 03:38:13 -07:00
parent 756a563135
commit a8800838d4
3 changed files with 297 additions and 40 deletions

View File

@ -12,7 +12,8 @@
<meta name="og:description" content="A (work in progress) reimplementation of Chip's Challenge 1 and 2, using entirely free assets.">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
</head>
<body data-mode="splash">
<body data-mode="failed">
<script>document.body.setAttribute('data-mode', 'loading');</script>
<svg id="svg-iconsheet">
<defs>
<!-- Actions -->
@ -44,12 +45,11 @@
</svg>
<header id="header-main">
<img id="header-icon" src="icon.png" alt="">
<h1>Lexy's Labyrinth</h1>
<p>— a game by <a href="https://eev.ee/">eevee</a></p>
<h1><a href="https://github.com/eevee/lexys-labyrinth">Lexy's Labyrinth</a></h1>
<p>— an <a href="https://github.com/eevee/lexys-labyrinth">open source</a> game by <a href="https://eev.ee/">eevee</a></p>
<nav>
<button id="main-about" type="button">about</button>
<button id="main-help" type="button" disabled>help</button>
<button id="main-options" type="button" disabled>options</button>
<button id="main-compat" type="button">compat mode: <output>lexy</output></button>
</nav>
</header>
<header id="header-pack">
@ -73,16 +73,41 @@
</button>
</nav>
</header>
<main id="splash">
<main id="failed">
<h1>oops!</h1>
<p>Sorry, the game was unable to load at all.</p>
<p>If you have JavaScript partly or wholly blocked, I salute you! ...but this is an interactive game and cannot work without it.</p>
</main>
<main id="loading" hidden>
<p>...loading...</p>
<div class="scrolling-sidewalk">
<img src="loading.gif" alt="Lexy walking">
</div>
</main>
<script>
document.querySelector('#failed').setAttribute('hidden', '');
document.querySelector('#loading').removeAttribute('hidden');
</script>
<main id="splash" hidden>
<div class="drag-overlay"></div>
<header>
<h1><img src="og-preview.png" alt="">Lexy's Labyrinth</h1>
<p>an unapproved Chip's Challenge emulator</p>
</header>
<section id="splash-intro">
<p><strong>Welcome</strong> to Lexy's Labyrinth, an open source puzzle game that is curiously similar to — but legally distinct from — the Atari classic <a href="https://en.wikipedia.org/wiki/Chip%27s_Challenge">Chip's Challenge</a>!</p>
<p><strong>Welcome</strong> to Lexy's Labyrinth, an open source puzzle game that is curiously similar to — but legally distinct from! — the Atari classic <a href="https://en.wikipedia.org/wiki/Chip%27s_Challenge">Chip's Challenge</a>. It's been lovingly crafted from scratch with completely new art, sounds, and music; it lets you undo your mistakes; and it's the <em>only</em> way to play Chip's Challenge 2 levels on Linux, Mac, or a phone!</p>
<p>Pick a level pack to get started! You can also load and play any levels you've got lying around, or brave the level editor and make one of your own.</p>
<p>If you're not familiar with the game, read up on <a href="https://github.com/eevee/lexys-labyrinth/wiki/How-To-Play">how to play</a>. You can also get more technical details or report bugs on <a href="https://github.com/eevee/lexys-labyrinth">GitHub</a>, find out more about Chip's Challenge via the <a href="https://bitbusters.club/">Bit Busters Club</a> fansite, or support this endeavor (and other things I do) on <a href="https://www.patreon.com/eevee">Patreon</a>.</p>
</section>
<section id="splash-links">
<h2>More resources</h2>
<ul class="normal-list">
<li><a href="https://github.com/eevee/lexys-labyrinth/wiki/How-To-Play">How to play</a></li>
<li><a href="https://github.com/eevee/lexys-labyrinth">Source code and project details on GitHub</a></li>
<li><a href="https://patreon.com/eevee">Support this project (and others) on my Patreon</a></li>
<li><a href="https://bitbusters.club/">Bit Busters Club</a>, a fansite with an extensive <a href="https://wiki.bitbusters.club/Main_Page">wiki</a>, vastly more <a href="https://sets.bitbusters.club/">community levels</a>, speedrun records, and more</li>
<li><a href="https://store.steampowered.com/app/346850/Chips_Challenge_1/">Chip's Challenge 1</a> (free) and <a href="https://store.steampowered.com/app/348300/Chips_Challenge_2/">Chip's Challenge 2</a> ($5) on Steam</li>
<li><a href="http://www.niffler.co.uk/">Niffler</a>, the current publisher of Chip's Challenge and its spiritual sequel <a href="https://store.steampowered.com/app/262590/Chucks_Challenge_3D_2020/">Chuck's Challenge 3D</a></li>
</ul>
</section>
<section id="splash-stock-levels">
@ -91,16 +116,15 @@
</section>
<section id="splash-upload-levels">
<h2>Load other levels</h2>
<p>Load and play any levels you have on hand, including the original levels. Supports CCL/DAT, C2G, and individual C2Ms (though scores aren't saved for those). Find more on the <a href="https://sets.bitbusters.club/">Bit Busters Club set list</a>.</p>
<h2>Other levels</h2>
<p>You can load custom levels as well — CCL/DAT, C2G, or individual C2Ms (though scores aren't saved for those).</p>
<!-- TODO zip files! -->
<p>You can also drag and drop files or directories into this window.</p>
<input id="splash-upload-file" type="file" accept=".dat,.ccl,.c2m,.ccs" multiple>
<input id="splash-upload-dir" type="file" webkitdirectory>
<button type="button" id="splash-upload-file-button" class="button-big">Load files</button>
<button type="button" id="splash-upload-dir-button" class="button-big">Load directory</button>
<p>If you still have the original Microsoft "BOWEP" game lying around, you can play the Chip's Challenge 1 levels by loading <code>CHIPS.DAT</code>.</p>
<p>If you own the Steam versions of <a href="https://store.steampowered.com/app/346850/Chips_Challenge_1/">Chip's Challenge 1</a> (<em>free!</em>) or <a href="https://store.steampowered.com/app/348300/Chips_Challenge_2/">Chip's Challenge 2</a> ($5 last I checked), you can play those too, even on Linux or Mac:</p>
<p>To play the original levels, you can load <code>CHIPS.DAT</code> from the ancient Microsoft port, or load the Steam levels as follows:</p>
<ol class="normal-list">
<li>Right-click the game in Steam and choose <em>Properties</em>. On the <em>Local Files</em> tab, click <em>Browse local files</em>.</li>
<li>Open the <code>data</code> folder, then <code>games</code>.</li>

View File

@ -1683,6 +1683,102 @@ const BUILTIN_LEVEL_PACKS = [{
ident: 'cclp3',
title: "Chip's Challenge Level Pack 3",
desc: "A tough challenge, by and for veteran players.",
/*
* TODO: this is tricky. it's a massive hodgepodge of levels mostly made by individual people...
}, {
path: 'levels/CCLP3.ccl',
ident: 'jblp1',
title: "JBLP1",
author: 'jb',
desc: "\"Meant to be simple and straightforward in the spirit of the original game, though the difficulty peak is ultimately a bit higher.\"",
}, {
path: 'levels/CCLP3.ccl',
ident: 'cclp3',
title: "Pit of 100 Tiles",
author: 'ajmiam',
desc: "A tough challenge, by and for veteran players.",
}, {
path: 'levels/CCLP3.ccl',
ident: 'cclp3',
title: "The Other 100 Tiles",
author: 'ajmiam',
desc: "A tough challenge, by and for veteran players.",
}, {
path: 'levels/CCLP3.ccl',
ident: 'cclp3',
title: "JoshL5",
desc: "A tough challenge, by and for veteran players.",
}, {
path: 'levels/CCLP3.ccl',
ident: 'cclp3',
title: "JoshL6",
desc: "A tough challenge, by and for veteran players.",
}, {
path: 'levels/CCLP3.ccl',
ident: 'cclp3',
title: "JoshL7",
desc: "A tough challenge, by and for veteran players.",
}, {
path: 'levels/CCLP3.ccl',
ident: 'cclp3',
title: "Neverstopgaming",
desc: "A tough challenge, by and for veteran players.",
}, {
path: 'levels/CCLP3.ccl',
ident: 'cclp3',
title: "Ultimate Chip 4",
desc: "A tough challenge, by and for veteran players.",
}, {
path: 'levels/CCLP3.ccl',
ident: 'cclp3',
title: "Ultimate Chip 5",
desc: "A tough challenge, by and for veteran players.",
}, {
path: 'levels/CCLP3.ccl',
ident: 'cclp3',
title: "Ultimate Chip 6",
desc: "A tough challenge, by and for veteran players.",
}, {
path: 'levels/CCLP3.ccl',
ident: 'cclp3',
title: "Walls of CCLP 1",
desc: "A tough challenge, by and for veteran players.",
}, {
path: 'levels/CCLP3.ccl',
ident: 'cclp3',
title: "Walls of CCLP 3",
desc: "A tough challenge, by and for veteran players.",
}, {
path: 'levels/CCLP3.ccl',
ident: 'cclp3',
title: "Walls of CCLP 4",
desc: "A tough challenge, by and for veteran players.",
}, {
path: 'levels/CCLP3.ccl',
ident: 'cclp3',
title: "TS0",
desc: "A tough challenge, by and for veteran players.",
}, {
path: 'levels/CCLP3.ccl',
ident: 'cclp3',
title: "TS1",
desc: "A tough challenge, by and for veteran players.",
}, {
path: 'levels/CCLP3.ccl',
ident: 'cclp3',
title: "TS2",
desc: "A tough challenge, by and for veteran players.",
}, {
path: 'levels/CCLP3.ccl',
ident: 'cclp3',
title: "Chip56",
desc: "A tough challenge, by and for veteran players.",
}, {
path: 'levels/CCLP3.ccl',
ident: 'cclp3',
title: "kidsfair",
desc: "A tough challenge, by and for veteran players.",
*/
}];
class Splash extends PrimaryView {
@ -2555,9 +2651,6 @@ class Conductor {
this.player = new Player(this);
// Bind the header buttons
document.querySelector('#main-about').addEventListener('click', ev => {
new AboutOverlay(this).open();
});
document.querySelector('#main-options').addEventListener('click', ev => {
new OptionsOverlay(this).open();
});
@ -2633,6 +2726,7 @@ class Conductor {
});
this.update_nav_buttons();
document.querySelector('#loading').setAttribute('hidden', '');
this.switch_to_splash();
}
@ -2891,4 +2985,22 @@ async function main() {
}
}
main();
(async () => {
try {
await main();
}
catch (e) {
let failed = document.getElementById('failed');
document.getElementById('loading').setAttribute('hidden', '');
failed.removeAttribute('hidden');
document.body.setAttribute('data-mode', 'failed');
failed.appendChild(mk('p',
"I did manage to capture this error, which you might be able to ",
mk('a', {href: 'https://github.com/eevee/lexys-labyrinth'}, "report somewhere"),
":",
));
failed.appendChild(mk('pre.stack-trace', e.toString(), "\n\n", (e.stack ?? "").replace(/^/mg, " ")));
throw e;
}
})();

169
style.css
View File

@ -19,6 +19,7 @@ body {
--panel-bg-color: hsl(225, 10%, 20%);
--button-bg-color: hsl(225, 10%, 25%);
--button-bg-hover-color: hsl(225, 15%, 30%);
--generic-bg-hover-on-white: hsl(225, 60%, 85%);
}
/* Generic element styling */
@ -31,7 +32,8 @@ input[type=range] {
margin: 0.125em;
vertical-align: middle;
}
button {
button,
.radio-faux-button-set > label > input + span {
font-size: inherit;
padding: 0.25em 0.5em;
font-family: inherit;
@ -45,12 +47,12 @@ button {
text-transform: lowercase;
cursor: pointer;
}
button:hover {
button:hover,
.radio-faux-button-set > label:hover > input + span {
background: var(--button-bg-hover-color);
}
button:active {
box-shadow:
inset 0 0 2px 1px hsl(225, 10%, 33%);
button:active,
.radio-faux-button-set > label:active > input + span {
transform: translateY(1px);
/* Need this for the editor's tool help things and i'm not questioning it */
z-index: 1;
@ -143,6 +145,40 @@ svg.svg-icon {
fill-rule: evenodd;
}
/* Set of radio buttons in a row, styled like buttons */
/* (you know, like /actual/ radio buttons) */
/* Button-esque styling is shared with the button definition above */
.radio-faux-button-set {
display: flex;
margin: 0.5em 0;
}
.radio-faux-button-set > label {
flex: 1;
}
.radio-faux-button-set > label > input {
display: none;
}
.radio-faux-button-set > label > input + span {
display: block;
border-radius: 0;
text-align: center;
}
.radio-faux-button-set > label:first-child > input + span {
border-top-left-radius: 0.25em;
border-bottom-left-radius: 0.25em;
}
.radio-faux-button-set > label:last-child > input + span {
border-top-right-radius: 0.25em;
border-bottom-right-radius: 0.25em;
}
.radio-faux-button-set > label > input:checked + span {
background: hsl(225, 80%, 50%);
box-shadow:
inset 0 1px 0 1px hsl(225, 10%, 20%),
inset 0 -0em 2em 0.5em hsl(225, 50%, 30%),
0 1px 1px hsl(225, 10%, 10%);
}
/* Overlay styling */
.overlay {
display: flex;
@ -266,12 +302,42 @@ table.level-browser tbody tr {
cursor: pointer;
}
table.level-browser tbody tr:hover {
background: hsl(225, 60%, 85%);
background: var(--generic-hover-bg-on-white);
}
table.level-browser tbody tr:nth-child(10n) td {
border-bottom: 2px solid hsl(225, 20%, 80%);
}
/* Compat dialog */
.dialog-compat {
max-width: 60em;
}
ul.compat-flags > li {
padding: 0.125em;
}
ul.compat-flags > li.-checked {
background: hsl(225, 60%, 90%);
}
ul.compat-flags > li:hover {
background: var(--generic-bg-hover-on-white);
}
ul.compat-flags > li > label {
display: flex;
align-items: center;
gap: 0.25em;
}
ul.compat-flags > li > label > span.-desc {
flex: 1;
}
img.compat-icon,
.compat-icon-gap {
display: inline-block;
width: 32px;
height: 32px;
vertical-align: middle;
}
/* Options dialog */
.dialog-options {
height: 60%;
@ -334,24 +400,6 @@ label.option .option-label {
}
}
/* Bits and pieces */
img.compat-icon {
margin: 0 0.25em 0.125em;
vertical-align: middle;
}
.compat-lynx,
.compat-ms {
font-size: 0.75em;
display: inline-block;
margin: 0 0.25em;
padding: 0.25em;
line-height: 1;
vertical-align: middle;
color: white;
background: gray;
border-radius: 0.25em;
}
/**************************************************************************************************/
/* Main page structure */
@ -382,7 +430,19 @@ body > header > nav {
body > header button {
font-size: 0.75em;
}
body > header h1 a {
color: inherit !important;
text-decoration: none !important;
}
body[data-mode=failed] #header-pack,
body[data-mode=failed] #header-level,
body[data-mode=failed] #header-main > nav,
body[data-mode=loading] #header-pack,
body[data-mode=loading] #header-level,
body[data-mode=loading] #header-main > nav {
display: none;
}
body[data-mode=splash] #header-pack,
body[data-mode=splash] #header-level {
display: none;
@ -391,6 +451,50 @@ body[data-mode=editor] #player-edit,
body[data-mode=player] #editor-play {
display: none;
}
#failed,
#loading {
margin: auto;
text-align: center;
}
#loading {
font-size: 2em;
}
.scrolling-sidewalk {
height: 32px;
width: 50vw;
margin: auto;
background: url(icons/tool-bg-selected.png) repeat;
animation: scrolling-sidewalk linear 0.4s infinite;
/*
box-shadow:
inset 5vw 0 5vw -5vw black,
inset -5vw 0 5vw -5vw black;
*/
mask: linear-gradient(to right, transparent 0, #ffffff40 10%, white 20%, white 80%, #ffffff40 90%, transparent 100%);
}
.scrolling-sidewalk > img {
display: block;
margin: auto;
}
@keyframes scrolling-sidewalk {
0% {
background-position: 0px 0px;
}
100% {
background-position: -32px 0px;
}
}
pre.stack-trace {
overflow: auto;
width: 90vw;
max-width: 50em;
padding: 0.5em;
margin: 1em auto;
white-space: pre;
text-align: left;
background: hsl(345, 20%, 10%);
border: 3px double hsl(345, 75%, 20%);
}
#header-main {
order: 3;
@ -422,9 +526,16 @@ body[data-mode=player] #editor-play {
#splash {
display: grid;
grid:
"header header header"
"intro stock upload"
"links stock yours"
/ 1fr 1fr 1fr
;
grid:
"header header header"
"intro intro intro"
"links links links"
"stock upload yours"
/ 1fr 1fr 1fr
;
@ -487,6 +598,12 @@ body[data-mode=player] #editor-play {
grid-area: intro;
font-size: 20px;
}
#splash > #splash-links {
grid-area: links;
}
#splash > #splash-links ul.normal-list li {
margin: 0.5em 0;
}
#splash > #splash-stock-levels {
grid-area: stock;
}
@ -517,6 +634,10 @@ body[data-mode=player] #editor-play {
#splash > header h1 {
font-size: 2em;
}
/* No need to boost the font size here */
#splash > #splash-intro {
font-size: inherit;
}
}
button.level-pack-button {