Rearranged debug panel a bit; added progress bar for replay playback
This commit is contained in:
parent
fde7d9a11c
commit
c17169f49d
23
index.html
23
index.html
@ -218,6 +218,11 @@
|
||||
</select>
|
||||
</p>
|
||||
|
||||
<h3>Inventory</h3>
|
||||
<div class="-inventory">
|
||||
<!-- populated in js -->
|
||||
</div>
|
||||
|
||||
<h3>Replay</h3>
|
||||
<!-- TODO...
|
||||
play back replay
|
||||
@ -227,18 +232,21 @@
|
||||
browse replay? jump to any point? label points???
|
||||
edit manually?
|
||||
-->
|
||||
<div id="player-debug-input"></div>
|
||||
<p id="player-debug-demo-playback">
|
||||
<div class="-replay-columns">
|
||||
<div id="player-debug-input"></div>
|
||||
<div class="-buttons"></div>
|
||||
</div>
|
||||
<p id="player-debug-replay-playback">
|
||||
<progress max="0" value="0"></progress>
|
||||
<output>100%</output>
|
||||
of
|
||||
<span>0 tics (0:00s)</span>
|
||||
</p>
|
||||
|
||||
<h3>Options</h3>
|
||||
<h3>Misc</h3>
|
||||
<p>Viewport size:
|
||||
<select id="player-debug-viewport">
|
||||
<option value="default" selected>Level default (9 or 10)</option>
|
||||
<option value="default" selected>Standard</option>
|
||||
<option value="12">12 × 12</option>
|
||||
<option value="16">16 × 16</option>
|
||||
<option value="24">24 × 24</option>
|
||||
@ -257,13 +265,6 @@
|
||||
<li><label><input type="checkbox" disabled> Player levitates</label></li>
|
||||
-->
|
||||
</ul>
|
||||
|
||||
<h3>Inventory</h3>
|
||||
<div class="-inventory">
|
||||
<!-- populated in js -->
|
||||
</div>
|
||||
|
||||
<h3>Misc</h3>
|
||||
<div class="-buttons" id="player-debug-misc-buttons">
|
||||
<!-- populated in js -->
|
||||
</div>
|
||||
|
||||
@ -34,6 +34,7 @@ class CC2Demo {
|
||||
for (let p = 3; p < l; p += 2) {
|
||||
duration += this.bytes[p];
|
||||
}
|
||||
duration = Math.floor(duration / 3);
|
||||
|
||||
let inputs = new Uint8Array(duration);
|
||||
let i = 0;
|
||||
|
||||
@ -6,6 +6,7 @@ import { PrimaryView, TransientOverlay, DialogOverlay, load_json_from_storage, s
|
||||
import CanvasRenderer from './renderer-canvas.js';
|
||||
import TILE_TYPES from './tiletypes.js';
|
||||
import { SVG_NS, mk, mk_svg, string_from_buffer_ascii, bytestring_to_buffer, walk_grid } from './util.js';
|
||||
import * as util from './util.js';
|
||||
|
||||
class EditorPackMetaOverlay extends DialogOverlay {
|
||||
constructor(conductor, stored_pack) {
|
||||
@ -53,7 +54,7 @@ class EditorLevelMetaOverlay extends DialogOverlay {
|
||||
text = "No time limit";
|
||||
}
|
||||
else {
|
||||
text = `${time_limit} (${Math.floor(time_limit / 60)}:${('0' + String(time_limit % 60)).slice(-2)})`;
|
||||
text = `${time_limit} (${util.format_duration(time_limit)})`;
|
||||
}
|
||||
time_limit_output.textContent = text;
|
||||
};
|
||||
|
||||
204
js/main.js
204
js/main.js
@ -635,44 +635,14 @@ class Player extends PrimaryView {
|
||||
time_secs_el: this.root.querySelector('#player-debug-time-secs'),
|
||||
};
|
||||
|
||||
let input_el = debug_el.querySelector('#player-debug-input');
|
||||
this.debug.input_els = {};
|
||||
for (let [action, label] of Object.entries({up: 'W', left: 'A', down: 'S', right: 'D', drop: 'Q', cycle: 'E', swap: 'C'})) {
|
||||
let el = mk_svg('svg.svg-icon', {viewBox: '0 0 16 16'},
|
||||
mk_svg('use', {href: `#svg-icon-${action}`}));
|
||||
el.style.gridArea = action;
|
||||
this.debug.input_els[action] = el;
|
||||
input_el.append(el);
|
||||
}
|
||||
|
||||
// Add a button for every kind of inventory item
|
||||
let inventory_el = debug_el.querySelector('.-inventory');
|
||||
let make_button = (label, onclick) => {
|
||||
let button = mk('button', {type: 'button'}, label);
|
||||
button.addEventListener('click', onclick);
|
||||
return button;
|
||||
};
|
||||
for (let name of [
|
||||
'key_blue', 'key_red', 'key_yellow', 'key_green',
|
||||
'flippers', 'fire_boots', 'cleats', 'suction_boots',
|
||||
'bribe', 'railroad_sign', 'hiking_boots', 'speed_boots',
|
||||
'xray_eye', 'helmet', 'foil', 'lightning_bolt',
|
||||
]) {
|
||||
inventory_el.append(make_button(
|
||||
mk('img', {src: this.render_inventory_tile(name)}),
|
||||
() => {
|
||||
this.level.give_actor(this.level.player, name);
|
||||
this.update_ui();
|
||||
}));
|
||||
}
|
||||
let clear_button = mk('button.-wide', {type: 'button'}, "clear inventory");
|
||||
clear_button.addEventListener('click', ev => {
|
||||
this.level.take_all_keys_from_actor(this.level.player);
|
||||
this.level.take_all_tools_from_actor(this.level.player);
|
||||
this.update_ui();
|
||||
});
|
||||
inventory_el.append(clear_button);
|
||||
|
||||
// -- Time --
|
||||
// Hook up back/forward buttons
|
||||
debug_el.querySelector('.-time-controls').addEventListener('click', ev => {
|
||||
let button = ev.target.closest('button.-time-button');
|
||||
if (! button)
|
||||
@ -692,43 +662,7 @@ class Player extends PrimaryView {
|
||||
this._redraw();
|
||||
this.update_ui();
|
||||
});
|
||||
|
||||
let speed_el = debug_el.elements.speed;
|
||||
speed_el.value = "1";
|
||||
speed_el.addEventListener('change', ev => {
|
||||
let speed = ev.target.value;
|
||||
let [numer, denom] = speed.split('/');
|
||||
this.play_speed = parseInt(numer, 10) / parseInt(denom ?? '1', 10);
|
||||
});
|
||||
|
||||
let viewport_el = this.root.querySelector('#player-debug-viewport');
|
||||
viewport_el.value = "default";
|
||||
viewport_el.addEventListener('change', ev => {
|
||||
let viewport = ev.target.value;
|
||||
if (viewport === 'default') {
|
||||
this.debug.viewport_size_override = null;
|
||||
}
|
||||
else if (viewport === 'max') {
|
||||
this.debug.viewport_size_override = 'max';
|
||||
}
|
||||
else {
|
||||
this.debug.viewport_size_override = parseInt(viewport, 10);
|
||||
}
|
||||
this.update_viewport_size();
|
||||
this._redraw();
|
||||
});
|
||||
|
||||
this.debug.replay_button = make_button("view replay", () => {
|
||||
if (this.state === 'playing' || this.state === 'paused' || this.state === 'rewinding') {
|
||||
new ConfirmOverlay(this.conductor, "Restart this level and watch the replay?", () => {
|
||||
this.play_demo();
|
||||
}).open();
|
||||
}
|
||||
else {
|
||||
this.play_demo();
|
||||
}
|
||||
});
|
||||
this._update_replay_button_enabled();
|
||||
// Add buttons for affecting the clock
|
||||
debug_el.querySelector('#player-debug-time-buttons').append(
|
||||
make_button("toggle clock", () => {
|
||||
this.level.pause_timer();
|
||||
@ -747,19 +681,99 @@ class Player extends PrimaryView {
|
||||
this.update_ui();
|
||||
}),
|
||||
);
|
||||
debug_el.querySelector('#player-debug-misc-buttons').append(
|
||||
this.debug.replay_button,
|
||||
make_button("green button", () => {
|
||||
TILE_TYPES['button_green'].do_button(this.level);
|
||||
this._redraw();
|
||||
}),
|
||||
make_button("blue button", () => {
|
||||
TILE_TYPES['button_blue'].do_button(this.level);
|
||||
this._redraw();
|
||||
}),
|
||||
);
|
||||
// Hook up play speed
|
||||
let speed_el = debug_el.elements.speed;
|
||||
speed_el.value = "1";
|
||||
speed_el.addEventListener('change', ev => {
|
||||
let speed = ev.target.value;
|
||||
let [numer, denom] = speed.split('/');
|
||||
this.play_speed = parseInt(numer, 10) / parseInt(denom ?? '1', 10);
|
||||
});
|
||||
|
||||
// Link up some options checkboxes
|
||||
// -- Inventory --
|
||||
// Add a button for every kind of inventory item
|
||||
let inventory_el = debug_el.querySelector('.-inventory');
|
||||
for (let name of [
|
||||
'key_blue', 'key_red', 'key_yellow', 'key_green',
|
||||
'flippers', 'fire_boots', 'cleats', 'suction_boots',
|
||||
'bribe', 'railroad_sign', 'hiking_boots', 'speed_boots',
|
||||
'xray_eye', 'helmet', 'foil', 'lightning_bolt',
|
||||
]) {
|
||||
inventory_el.append(make_button(
|
||||
mk('img', {src: this.render_inventory_tile(name)}),
|
||||
() => {
|
||||
this.level.give_actor(this.level.player, name);
|
||||
this.update_ui();
|
||||
}));
|
||||
}
|
||||
// Add a button to clear your inventory
|
||||
let clear_button = mk('button.-wide', {type: 'button'}, "clear inventory");
|
||||
clear_button.addEventListener('click', ev => {
|
||||
this.level.take_all_keys_from_actor(this.level.player);
|
||||
this.level.take_all_tools_from_actor(this.level.player);
|
||||
this.update_ui();
|
||||
});
|
||||
inventory_el.append(clear_button);
|
||||
|
||||
// -- Replay --
|
||||
// Create the input grid
|
||||
let input_el = debug_el.querySelector('#player-debug-input');
|
||||
this.debug.input_els = {};
|
||||
for (let [action, label] of Object.entries({up: 'W', left: 'A', down: 'S', right: 'D', drop: 'Q', cycle: 'E', swap: 'C'})) {
|
||||
let el = mk_svg('svg.svg-icon', {viewBox: '0 0 16 16'},
|
||||
mk_svg('use', {href: `#svg-icon-${action}`}));
|
||||
el.style.gridArea = action;
|
||||
this.debug.input_els[action] = el;
|
||||
input_el.append(el);
|
||||
}
|
||||
// Add a button to view a replay
|
||||
this.debug.replay_button = make_button("run level's replay", () => {
|
||||
if (this.state === 'playing' || this.state === 'paused' || this.state === 'rewinding') {
|
||||
new ConfirmOverlay(this.conductor, "Restart this level and watch the replay?", () => {
|
||||
this.play_demo();
|
||||
}).open();
|
||||
}
|
||||
else {
|
||||
this.play_demo();
|
||||
}
|
||||
});
|
||||
this._update_replay_button_enabled();
|
||||
debug_el.querySelector('.-replay-columns .-buttons').append(
|
||||
this.debug.replay_button,
|
||||
/*
|
||||
mk('button', {disabled: 'disabled'}, "load external replay"),
|
||||
mk('button', {disabled: 'disabled'}, "regain control"),
|
||||
mk('button', {disabled: 'disabled'}, "record new replay"),
|
||||
mk('button', {disabled: 'disabled'}, "record from here"),
|
||||
mk('button', {disabled: 'disabled'}, "browse/edit manually"),
|
||||
*/
|
||||
);
|
||||
// Progress bar and whatnot
|
||||
let replay_playback_el = debug_el.querySelector('#player-debug-replay-playback');
|
||||
this.debug.replay_playback_el = replay_playback_el;
|
||||
this.debug.replay_progress_el = replay_playback_el.querySelector('progress');
|
||||
this.debug.replay_percent_el = replay_playback_el.querySelector('output');
|
||||
this.debug.replay_duration_el = replay_playback_el.querySelector('span');
|
||||
|
||||
// -- Misc --
|
||||
// Viewport size
|
||||
let viewport_el = this.root.querySelector('#player-debug-viewport');
|
||||
viewport_el.value = "default";
|
||||
viewport_el.addEventListener('change', ev => {
|
||||
let viewport = ev.target.value;
|
||||
if (viewport === 'default') {
|
||||
this.debug.viewport_size_override = null;
|
||||
}
|
||||
else if (viewport === 'max') {
|
||||
this.debug.viewport_size_override = 'max';
|
||||
}
|
||||
else {
|
||||
this.debug.viewport_size_override = parseInt(viewport, 10);
|
||||
}
|
||||
this.update_viewport_size();
|
||||
this._redraw();
|
||||
});
|
||||
// Various checkboxes
|
||||
let wire_checkbox = (name, onclick) => {
|
||||
let checkbox = debug_el.elements[name];
|
||||
checkbox.checked = false; // override browser memory
|
||||
@ -773,6 +787,16 @@ class Player extends PrimaryView {
|
||||
this.use_interpolation = ! ev.target.checked;
|
||||
this._redraw();
|
||||
});
|
||||
debug_el.querySelector('#player-debug-misc-buttons').append(
|
||||
make_button("green button", () => {
|
||||
TILE_TYPES['button_green'].do_button(this.level);
|
||||
this._redraw();
|
||||
}),
|
||||
make_button("blue button", () => {
|
||||
TILE_TYPES['button_blue'].do_button(this.level);
|
||||
this._redraw();
|
||||
}),
|
||||
);
|
||||
|
||||
this.adjust_scale();
|
||||
if (this.level) {
|
||||
@ -875,6 +899,7 @@ class Player extends PrimaryView {
|
||||
this.time_el.classList.remove('--frozen');
|
||||
this.time_el.classList.remove('--danger');
|
||||
this.time_el.classList.remove('--warning');
|
||||
this.root.classList.remove('--replay');
|
||||
this.root.classList.remove('--bonus-visible');
|
||||
|
||||
this.update_ui();
|
||||
@ -894,6 +919,14 @@ class Player extends PrimaryView {
|
||||
this.level._blob_modifier = demo.blob_seed;
|
||||
// FIXME should probably start playback on first real input
|
||||
this.set_state('playing');
|
||||
this.root.classList.add('--replay');
|
||||
|
||||
if (this.debug.enabled) {
|
||||
this.debug.replay_playback_el.style.display = '';
|
||||
let t = this.demo_faucet.length;
|
||||
this.debug.replay_progress_el.setAttribute('max', t);
|
||||
this.debug.replay_duration_el.textContent = `${t} tics (${util.format_duration(t / TICS_PER_SECOND)})`;
|
||||
}
|
||||
}
|
||||
|
||||
get_input() {
|
||||
@ -1196,6 +1229,11 @@ class Player extends PrimaryView {
|
||||
this.debug.time_tics_el.textContent = `${t}`;
|
||||
this.debug.time_moves_el.textContent = `${Math.floor(t/4)}`;
|
||||
this.debug.time_secs_el.textContent = (t / 20).toFixed(2);
|
||||
|
||||
if (this.demo_faucet) {
|
||||
this.debug.replay_progress_el.setAttribute('value', t);
|
||||
this.debug.replay_percent_el.textContent = `${Math.floor((t + 1) / this.demo_faucet.length * 100)}%`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1954,9 +1992,7 @@ class LevelBrowserOverlay extends DialogOverlay {
|
||||
|
||||
// Express absolute time as mm:ss, with two decimals on the seconds (which should be
|
||||
// able to exactly count a number of tics)
|
||||
let absmin = Math.floor(scorecard.abstime / TICS_PER_SECOND / 60);
|
||||
let abssec = scorecard.abstime / TICS_PER_SECOND % 60;
|
||||
abstime = `${absmin}:${abssec < 10 ? '0' : ''}${abssec.toFixed(2)}`;
|
||||
abstime = util.format_duration(scorecard.abstime / TICS_PER_SECOND, 2);
|
||||
}
|
||||
|
||||
let title = meta.title;
|
||||
|
||||
@ -162,6 +162,12 @@ export function bytestring_to_buffer(bytestring) {
|
||||
return Uint8Array.from(bytestring, c => c.charCodeAt(0)).buffer;
|
||||
}
|
||||
|
||||
export function format_duration(seconds, places = 0) {
|
||||
let mins = Math.floor(seconds / 60);
|
||||
let secs = seconds % 60;
|
||||
return `${mins}:${secs < 10 ? '0' : ''}${secs.toFixed(places)}`;
|
||||
}
|
||||
|
||||
export class DelayTimer {
|
||||
constructor() {
|
||||
this.active = false;
|
||||
|
||||
27
style.css
27
style.css
@ -937,7 +937,16 @@ dl.score-chart .-sum {
|
||||
flex: 1 0 max-content;
|
||||
}
|
||||
|
||||
#player-debug .-replay-columns {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 0.5em;
|
||||
}
|
||||
#player-debug .-replay-columns .-buttons {
|
||||
flex-direction: column;
|
||||
}
|
||||
#player-debug-input {
|
||||
flex: none;
|
||||
display: grid;
|
||||
grid:
|
||||
"drop up cycle" 1em
|
||||
@ -953,18 +962,22 @@ dl.score-chart .-sum {
|
||||
#player-debug-input > svg.--held {
|
||||
fill: white;
|
||||
}
|
||||
#player-debug-demo-playback {
|
||||
display: flex;
|
||||
/* TODO finish this later */
|
||||
#player-debug-replay-playback {
|
||||
display: none;
|
||||
gap: 0.25em;
|
||||
}
|
||||
#player-debug-demo-playback progress {
|
||||
#player.--replay #player-debug-replay-playback {
|
||||
display: flex;
|
||||
}
|
||||
#player-debug-replay-playback progress {
|
||||
flex: 1;
|
||||
}
|
||||
#player-debug-demo-playback output {
|
||||
width: 4em;
|
||||
#player-debug-replay-playback output {
|
||||
width: 3em;
|
||||
width: 5ch;
|
||||
text-align: right;
|
||||
}
|
||||
#player-debug-demo-playback span {
|
||||
#player-debug-replay-playback span {
|
||||
flex: none;
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user