Rearranged debug panel a bit; added progress bar for replay playback

This commit is contained in:
Eevee (Evelyn Woods) 2020-12-11 22:15:39 -07:00
parent fde7d9a11c
commit c17169f49d
6 changed files with 161 additions and 103 deletions

View File

@ -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 class="-replay-columns">
<div id="player-debug-input"></div>
<p id="player-debug-demo-playback">
<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>

View File

@ -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;

View File

@ -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;
};

View File

@ -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;

View File

@ -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;

View File

@ -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;
}