Rearranged debug panel a bit; added progress bar for replay playback
This commit is contained in:
parent
fde7d9a11c
commit
c17169f49d
21
index.html
21
index.html
@ -218,6 +218,11 @@
|
|||||||
</select>
|
</select>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<h3>Inventory</h3>
|
||||||
|
<div class="-inventory">
|
||||||
|
<!-- populated in js -->
|
||||||
|
</div>
|
||||||
|
|
||||||
<h3>Replay</h3>
|
<h3>Replay</h3>
|
||||||
<!-- TODO...
|
<!-- TODO...
|
||||||
play back replay
|
play back replay
|
||||||
@ -227,18 +232,21 @@
|
|||||||
browse replay? jump to any point? label points???
|
browse replay? jump to any point? label points???
|
||||||
edit manually?
|
edit manually?
|
||||||
-->
|
-->
|
||||||
|
<div class="-replay-columns">
|
||||||
<div id="player-debug-input"></div>
|
<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>
|
<progress max="0" value="0"></progress>
|
||||||
<output>100%</output>
|
<output>100%</output>
|
||||||
of
|
of
|
||||||
<span>0 tics (0:00s)</span>
|
<span>0 tics (0:00s)</span>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<h3>Options</h3>
|
<h3>Misc</h3>
|
||||||
<p>Viewport size:
|
<p>Viewport size:
|
||||||
<select id="player-debug-viewport">
|
<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="12">12 × 12</option>
|
||||||
<option value="16">16 × 16</option>
|
<option value="16">16 × 16</option>
|
||||||
<option value="24">24 × 24</option>
|
<option value="24">24 × 24</option>
|
||||||
@ -257,13 +265,6 @@
|
|||||||
<li><label><input type="checkbox" disabled> Player levitates</label></li>
|
<li><label><input type="checkbox" disabled> Player levitates</label></li>
|
||||||
-->
|
-->
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h3>Inventory</h3>
|
|
||||||
<div class="-inventory">
|
|
||||||
<!-- populated in js -->
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h3>Misc</h3>
|
|
||||||
<div class="-buttons" id="player-debug-misc-buttons">
|
<div class="-buttons" id="player-debug-misc-buttons">
|
||||||
<!-- populated in js -->
|
<!-- populated in js -->
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -34,6 +34,7 @@ class CC2Demo {
|
|||||||
for (let p = 3; p < l; p += 2) {
|
for (let p = 3; p < l; p += 2) {
|
||||||
duration += this.bytes[p];
|
duration += this.bytes[p];
|
||||||
}
|
}
|
||||||
|
duration = Math.floor(duration / 3);
|
||||||
|
|
||||||
let inputs = new Uint8Array(duration);
|
let inputs = new Uint8Array(duration);
|
||||||
let i = 0;
|
let i = 0;
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import { PrimaryView, TransientOverlay, DialogOverlay, load_json_from_storage, s
|
|||||||
import CanvasRenderer from './renderer-canvas.js';
|
import CanvasRenderer from './renderer-canvas.js';
|
||||||
import TILE_TYPES from './tiletypes.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 { 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 {
|
class EditorPackMetaOverlay extends DialogOverlay {
|
||||||
constructor(conductor, stored_pack) {
|
constructor(conductor, stored_pack) {
|
||||||
@ -53,7 +54,7 @@ class EditorLevelMetaOverlay extends DialogOverlay {
|
|||||||
text = "No time limit";
|
text = "No time limit";
|
||||||
}
|
}
|
||||||
else {
|
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;
|
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'),
|
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 make_button = (label, onclick) => {
|
||||||
let button = mk('button', {type: 'button'}, label);
|
let button = mk('button', {type: 'button'}, label);
|
||||||
button.addEventListener('click', onclick);
|
button.addEventListener('click', onclick);
|
||||||
return button;
|
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 => {
|
debug_el.querySelector('.-time-controls').addEventListener('click', ev => {
|
||||||
let button = ev.target.closest('button.-time-button');
|
let button = ev.target.closest('button.-time-button');
|
||||||
if (! button)
|
if (! button)
|
||||||
@ -692,43 +662,7 @@ class Player extends PrimaryView {
|
|||||||
this._redraw();
|
this._redraw();
|
||||||
this.update_ui();
|
this.update_ui();
|
||||||
});
|
});
|
||||||
|
// Add buttons for affecting the clock
|
||||||
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();
|
|
||||||
debug_el.querySelector('#player-debug-time-buttons').append(
|
debug_el.querySelector('#player-debug-time-buttons').append(
|
||||||
make_button("toggle clock", () => {
|
make_button("toggle clock", () => {
|
||||||
this.level.pause_timer();
|
this.level.pause_timer();
|
||||||
@ -747,19 +681,99 @@ class Player extends PrimaryView {
|
|||||||
this.update_ui();
|
this.update_ui();
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
debug_el.querySelector('#player-debug-misc-buttons').append(
|
// Hook up play speed
|
||||||
this.debug.replay_button,
|
let speed_el = debug_el.elements.speed;
|
||||||
make_button("green button", () => {
|
speed_el.value = "1";
|
||||||
TILE_TYPES['button_green'].do_button(this.level);
|
speed_el.addEventListener('change', ev => {
|
||||||
this._redraw();
|
let speed = ev.target.value;
|
||||||
}),
|
let [numer, denom] = speed.split('/');
|
||||||
make_button("blue button", () => {
|
this.play_speed = parseInt(numer, 10) / parseInt(denom ?? '1', 10);
|
||||||
TILE_TYPES['button_blue'].do_button(this.level);
|
});
|
||||||
this._redraw();
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
// 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 wire_checkbox = (name, onclick) => {
|
||||||
let checkbox = debug_el.elements[name];
|
let checkbox = debug_el.elements[name];
|
||||||
checkbox.checked = false; // override browser memory
|
checkbox.checked = false; // override browser memory
|
||||||
@ -773,6 +787,16 @@ class Player extends PrimaryView {
|
|||||||
this.use_interpolation = ! ev.target.checked;
|
this.use_interpolation = ! ev.target.checked;
|
||||||
this._redraw();
|
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();
|
this.adjust_scale();
|
||||||
if (this.level) {
|
if (this.level) {
|
||||||
@ -875,6 +899,7 @@ class Player extends PrimaryView {
|
|||||||
this.time_el.classList.remove('--frozen');
|
this.time_el.classList.remove('--frozen');
|
||||||
this.time_el.classList.remove('--danger');
|
this.time_el.classList.remove('--danger');
|
||||||
this.time_el.classList.remove('--warning');
|
this.time_el.classList.remove('--warning');
|
||||||
|
this.root.classList.remove('--replay');
|
||||||
this.root.classList.remove('--bonus-visible');
|
this.root.classList.remove('--bonus-visible');
|
||||||
|
|
||||||
this.update_ui();
|
this.update_ui();
|
||||||
@ -894,6 +919,14 @@ class Player extends PrimaryView {
|
|||||||
this.level._blob_modifier = demo.blob_seed;
|
this.level._blob_modifier = demo.blob_seed;
|
||||||
// FIXME should probably start playback on first real input
|
// FIXME should probably start playback on first real input
|
||||||
this.set_state('playing');
|
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() {
|
get_input() {
|
||||||
@ -1196,6 +1229,11 @@ class Player extends PrimaryView {
|
|||||||
this.debug.time_tics_el.textContent = `${t}`;
|
this.debug.time_tics_el.textContent = `${t}`;
|
||||||
this.debug.time_moves_el.textContent = `${Math.floor(t/4)}`;
|
this.debug.time_moves_el.textContent = `${Math.floor(t/4)}`;
|
||||||
this.debug.time_secs_el.textContent = (t / 20).toFixed(2);
|
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
|
// Express absolute time as mm:ss, with two decimals on the seconds (which should be
|
||||||
// able to exactly count a number of tics)
|
// able to exactly count a number of tics)
|
||||||
let absmin = Math.floor(scorecard.abstime / TICS_PER_SECOND / 60);
|
abstime = util.format_duration(scorecard.abstime / TICS_PER_SECOND, 2);
|
||||||
let abssec = scorecard.abstime / TICS_PER_SECOND % 60;
|
|
||||||
abstime = `${absmin}:${abssec < 10 ? '0' : ''}${abssec.toFixed(2)}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let title = meta.title;
|
let title = meta.title;
|
||||||
|
|||||||
@ -162,6 +162,12 @@ export function bytestring_to_buffer(bytestring) {
|
|||||||
return Uint8Array.from(bytestring, c => c.charCodeAt(0)).buffer;
|
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 {
|
export class DelayTimer {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.active = false;
|
this.active = false;
|
||||||
|
|||||||
27
style.css
27
style.css
@ -937,7 +937,16 @@ dl.score-chart .-sum {
|
|||||||
flex: 1 0 max-content;
|
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 {
|
#player-debug-input {
|
||||||
|
flex: none;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid:
|
grid:
|
||||||
"drop up cycle" 1em
|
"drop up cycle" 1em
|
||||||
@ -953,18 +962,22 @@ dl.score-chart .-sum {
|
|||||||
#player-debug-input > svg.--held {
|
#player-debug-input > svg.--held {
|
||||||
fill: white;
|
fill: white;
|
||||||
}
|
}
|
||||||
#player-debug-demo-playback {
|
#player-debug-replay-playback {
|
||||||
display: flex;
|
|
||||||
/* TODO finish this later */
|
|
||||||
display: none;
|
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;
|
flex: 1;
|
||||||
}
|
}
|
||||||
#player-debug-demo-playback output {
|
#player-debug-replay-playback output {
|
||||||
width: 4em;
|
width: 3em;
|
||||||
|
width: 5ch;
|
||||||
|
text-align: right;
|
||||||
}
|
}
|
||||||
#player-debug-demo-playback span {
|
#player-debug-replay-playback span {
|
||||||
flex: none;
|
flex: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user