Allow holding R (for one second) to restart the level
This commit is contained in:
parent
1df89884ed
commit
a3b283b51e
@ -227,8 +227,7 @@
|
|||||||
<span class="-optional-label">pause</span> <span class="keyhint"><kbd>p</kbd></span></button>
|
<span class="-optional-label">pause</span> <span class="keyhint"><kbd>p</kbd></span></button>
|
||||||
<button class="control-restart" type="button" title="restart">
|
<button class="control-restart" type="button" title="restart">
|
||||||
<svg class="svg-icon" viewBox="0 0 16 16"><path d="M13,13 A 7,7 270 1,1 13,3 L15,1 15,7 9,7 11,5 A 4,4 270 1,0 11,11 z"></path></svg>
|
<svg class="svg-icon" viewBox="0 0 16 16"><path d="M13,13 A 7,7 270 1,1 13,3 L15,1 15,7 9,7 11,5 A 4,4 270 1,0 11,11 z"></path></svg>
|
||||||
<span class="-optional-label">retry</span>
|
<span class="-optional-label">retry</span> <span class="keyhint"><kbd>r</kbd></span></button>
|
||||||
</button>
|
|
||||||
<button class="control-undo" type="button" title="undo">
|
<button class="control-undo" type="button" title="undo">
|
||||||
<svg class="svg-icon" viewBox="0 0 16 16"><path d="M6,5 6,2 1,7 6,12 6,9 A 10,10 60 0,1 15,12 A 10,10 90 0,0 6,5"></path></svg>
|
<svg class="svg-icon" viewBox="0 0 16 16"><path d="M6,5 6,2 1,7 6,12 6,9 A 10,10 60 0,1 15,12 A 10,10 90 0,0 6,5"></path></svg>
|
||||||
<span class="-optional-label">undo</span> <span class="keyhint"><kbd>u</kbd></span></button>
|
<span class="-optional-label">undo</span> <span class="keyhint"><kbd>u</kbd></span></button>
|
||||||
|
|||||||
79
js/main.js
79
js/main.js
@ -21,6 +21,7 @@ const PAGE_TITLE = "Lexy's Labyrinth";
|
|||||||
// This prefix is LLDEMO in base64, used to be somewhat confident that a string is a valid demo
|
// This prefix is LLDEMO in base64, used to be somewhat confident that a string is a valid demo
|
||||||
// (it's 6 characters so it becomes exactly 8 base64 chars with no leftovers to entangle)
|
// (it's 6 characters so it becomes exactly 8 base64 chars with no leftovers to entangle)
|
||||||
const REPLAY_PREFIX = "TExERU1P";
|
const REPLAY_PREFIX = "TExERU1P";
|
||||||
|
const RESTART_KEY_DELAY = 1.0;
|
||||||
|
|
||||||
function format_replay_duration(t) {
|
function format_replay_duration(t) {
|
||||||
return `${t} tics (${util.format_duration(t / TICS_PER_SECOND)})`;
|
return `${t} tics (${util.format_duration(t / TICS_PER_SECOND)})`;
|
||||||
@ -715,7 +716,6 @@ class Player extends PrimaryView {
|
|||||||
this.inventory_el.append(img);
|
this.inventory_el.append(img);
|
||||||
}
|
}
|
||||||
|
|
||||||
let last_key;
|
|
||||||
this.pending_player_move = null;
|
this.pending_player_move = null;
|
||||||
this.next_player_move = null;
|
this.next_player_move = null;
|
||||||
this.player_used_move = false;
|
this.player_used_move = false;
|
||||||
@ -750,6 +750,11 @@ class Player extends PrimaryView {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ev.key === 'r') {
|
||||||
|
this.start_restarting();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Per-tic navigation; only useful if the game isn't running
|
// Per-tic navigation; only useful if the game isn't running
|
||||||
if (ev.key === ',') {
|
if (ev.key === ',') {
|
||||||
if (this.state === 'stopped' || this.state === 'paused' || this.turn_based_mode) {
|
if (this.state === 'stopped' || this.state === 'paused' || this.turn_based_mode) {
|
||||||
@ -849,10 +854,16 @@ class Player extends PrimaryView {
|
|||||||
if (! this.active)
|
if (! this.active)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (ev.key === 'r') {
|
||||||
|
this.stop_restarting();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (ev.key === 'z') {
|
if (ev.key === 'z') {
|
||||||
if (this.state === 'rewinding') {
|
if (this.state === 'rewinding') {
|
||||||
this.set_state('playing');
|
this.set_state('playing');
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.key_mapping[ev.key]) {
|
if (this.key_mapping[ev.key]) {
|
||||||
@ -939,23 +950,13 @@ class Player extends PrimaryView {
|
|||||||
|
|
||||||
// When we lose focus, act as though every key was released, and pause the game
|
// When we lose focus, act as though every key was released, and pause the game
|
||||||
window.addEventListener('blur', () => {
|
window.addEventListener('blur', () => {
|
||||||
this.current_keys.clear();
|
this.enter_background();
|
||||||
this.current_touches = {};
|
|
||||||
|
|
||||||
if (this.state === 'playing' || this.state === 'rewinding') {
|
|
||||||
this.autopause();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
// Same when the window becomes hidden (especially important on phones, where this covers
|
// Same when the window becomes hidden (especially important on phones, where this covers
|
||||||
// turning the screen off!)
|
// turning the screen off!)
|
||||||
document.addEventListener('visibilitychange', ev => {
|
document.addEventListener('visibilitychange', ev => {
|
||||||
if (document.visibilityState === 'hidden') {
|
if (document.visibilityState === 'hidden') {
|
||||||
this.current_keys.clear();
|
this.enter_background();
|
||||||
this.current_touches = {};
|
|
||||||
|
|
||||||
if (this.state === 'playing' || this.state === 'rewinding') {
|
|
||||||
this.autopause();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1402,6 +1403,17 @@ class Player extends PrimaryView {
|
|||||||
this.captions_el.textContent = '';
|
this.captions_el.textContent = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Called when we lose focus; assume all keys are released, since we can't be sure any more
|
||||||
|
enter_background() {
|
||||||
|
this.stop_restarting();
|
||||||
|
this.current_keys.clear();
|
||||||
|
this.current_touches = {};
|
||||||
|
|
||||||
|
if (this.state === 'playing' || this.state === 'rewinding') {
|
||||||
|
this.autopause();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
reload_options(options) {
|
reload_options(options) {
|
||||||
this.music_audio_el.volume = options.music_volume ?? 1.0;
|
this.music_audio_el.volume = options.music_volume ?? 1.0;
|
||||||
// TODO hide music info when disabled?
|
// TODO hide music info when disabled?
|
||||||
@ -1946,6 +1958,38 @@ class Player extends PrimaryView {
|
|||||||
this.set_state('paused');
|
this.set_state('paused');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
start_restarting() {
|
||||||
|
this.stop_restarting();
|
||||||
|
|
||||||
|
if (! (this.state === 'playing' || this.state === 'paused' || this.state === 'rewinding'))
|
||||||
|
return;
|
||||||
|
|
||||||
|
let t0 = performance.now();
|
||||||
|
let update = () => {
|
||||||
|
let t = performance.now();
|
||||||
|
let p = (t - t0) / 1000 / RESTART_KEY_DELAY;
|
||||||
|
this.restart_button.style.setProperty('--restart-progress', p);
|
||||||
|
|
||||||
|
if (p < 1) {
|
||||||
|
this._restart_handle = requestAnimationFrame(update);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.restart_level();
|
||||||
|
this.stop_restarting();
|
||||||
|
this._restart_handle = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
stop_restarting() {
|
||||||
|
if (this._restart_handle) {
|
||||||
|
cancelAnimationFrame(this._restart_handle);
|
||||||
|
this._restart_handle = null;
|
||||||
|
}
|
||||||
|
this.restart_button.style.setProperty('--restart-progress', 0);
|
||||||
|
}
|
||||||
|
|
||||||
// waiting: haven't yet pressed a key so the timer isn't going
|
// waiting: haven't yet pressed a key so the timer isn't going
|
||||||
// playing: playing normally
|
// playing: playing normally
|
||||||
// paused: um, paused
|
// paused: um, paused
|
||||||
@ -2217,8 +2261,13 @@ class Player extends PrimaryView {
|
|||||||
|
|
||||||
this.update_music_playback_state();
|
this.update_music_playback_state();
|
||||||
|
|
||||||
// The advance and redraw methods run in a loop, but they cancel
|
// Restarting makes no sense if we're not playing
|
||||||
// themselves if the game isn't running, so restart them here
|
if (this.state === 'waiting' || this.state === 'stopped' || this.state === 'ended') {
|
||||||
|
this.stop_restarting();
|
||||||
|
}
|
||||||
|
|
||||||
|
// The advance and redraw methods run in a loop, but they cancel themselves if the game
|
||||||
|
// isn't running, so restart them here
|
||||||
if (this.state === 'playing' || this.state === 'rewinding') {
|
if (this.state === 'playing' || this.state === 'rewinding') {
|
||||||
if (! this._advance_handle) {
|
if (! this._advance_handle) {
|
||||||
this.advance();
|
this.advance();
|
||||||
|
|||||||
10
style.css
10
style.css
@ -19,6 +19,7 @@ body {
|
|||||||
|
|
||||||
--panel-bg-color: hsl(220, 10%, 15%);
|
--panel-bg-color: hsl(220, 10%, 15%);
|
||||||
--button-bg-color: hsl(220, 20%, 25%);
|
--button-bg-color: hsl(220, 20%, 25%);
|
||||||
|
--button-bg-gradient: linear-gradient(to bottom, var(--button-bg-shadow-color), transparent 75%);
|
||||||
--button-bg-shadow-color: #fff1;
|
--button-bg-shadow-color: #fff1;
|
||||||
--button-bg-hover-color: hsl(220, 30%, 30%);
|
--button-bg-hover-color: hsl(220, 30%, 30%);
|
||||||
--generic-bg-hover-on-white: hsl(220, 60%, 90%);
|
--generic-bg-hover-on-white: hsl(220, 60%, 90%);
|
||||||
@ -50,7 +51,7 @@ button,
|
|||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
color: white;
|
color: white;
|
||||||
background-color: var(--button-bg-color);
|
background-color: var(--button-bg-color);
|
||||||
background-image: linear-gradient(to bottom, var(--button-bg-shadow-color), transparent 75%);
|
background-image: var(--button-bg-gradient);
|
||||||
border: 1px solid hsl(220, 10%, 7.5%);
|
border: 1px solid hsl(220, 10%, 7.5%);
|
||||||
box-shadow:
|
box-shadow:
|
||||||
inset 0 0 1px 1px #fff2,
|
inset 0 0 1px 1px #fff2,
|
||||||
@ -1238,6 +1239,13 @@ ol.packtest-summary > li {
|
|||||||
#player button:disabled .keyhint {
|
#player button:disabled .keyhint {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
#player-controls button:enabled.control-restart {
|
||||||
|
/* Special shenanigans for holding R to restart */
|
||||||
|
--restart-progress: 0;
|
||||||
|
background-image: var(--button-bg-gradient), conic-gradient(
|
||||||
|
hsl(345, 60%, 40%) 0deg calc(var(--restart-progress) * 360deg),
|
||||||
|
transparent calc(var(--restart-progress) * 360deg) 360deg)
|
||||||
|
}
|
||||||
@media (orientation: portrait) {
|
@media (orientation: portrait) {
|
||||||
/* On a portrait screen, put the controls on top */
|
/* On a portrait screen, put the controls on top */
|
||||||
#player-main {
|
#player-main {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user