Add some more actor inspection to debug mode

This commit is contained in:
Eevee (Evelyn Woods) 2021-01-22 08:37:14 -07:00
parent 134270e3e3
commit 54823f62bf
4 changed files with 168 additions and 42 deletions

View File

@ -221,22 +221,18 @@
<div class="-buttons" id="player-debug-time-buttons"> <div class="-buttons" id="player-debug-time-buttons">
<!-- populated in js --> <!-- populated in js -->
</div> </div>
<p>Game speed: <div id="player-debug-speed" class="radio-faux-button-set">
<select name="speed"> <label><input type="radio" name="speed" value="100"><span class="-button">100</span></label>
<option value="100">100× faster</option> <label><input type="radio" name="speed" value="25"><span class="-button">25</span></label>
<option value="50">50× faster</option> <label><input type="radio" name="speed" value="10"><span class="-button">10</span></label>
<option value="20">20× faster</option> <label><input type="radio" name="speed" value="5"><span class="-button">5</span></label>
<option value="10">10× faster</option> <label><input type="radio" name="speed" value="3"><span class="-button">3</span></label>
<option value="5">5× faster</option> <label><input type="radio" name="speed" value="2"><span class="-button">2</span></label>
<option value="3">3× faster</option> <label><input type="radio" name="speed" value="1"><span class="-button"><strong>1×</strong></span></label>
<option value="2">2× faster</option> <label><input type="radio" name="speed" value="1/2"><span class="-button">½</span></label>
<option value="3/2">× faster</option> <label><input type="radio" name="speed" value="1/3"><span class="-button"></span></label>
<option value="1" selected>Normal speed</option> <label><input type="radio" name="speed" value="1/4"><span class="-button">¼</span></label>
<option value="1/2">2× slower</option> </div>
<option value="1/3">3× slower</option>
<option value="1/4">4× slower</option>
</select>
</p>
<h3>Inventory</h3> <h3>Inventory</h3>
<div class="-inventory"> <div class="-inventory">
@ -280,10 +276,11 @@
</select> </select>
</p> </p>
<ul> <ul>
<li><label><input type="checkbox" name="show_actor_bboxes"> Show actor bounding boxes</label></li>
<li><label><input type="checkbox" name="disable_interpolation"> Disable interpolation</label></li> <li><label><input type="checkbox" name="disable_interpolation"> Disable interpolation</label></li>
<li><label><input type="checkbox" name="show_actor_bboxes"> Show actor bounding boxes</label></li>
<li><label><input type="checkbox" name="show_actor_order"> Show actor order</label></li>
<li><label><input type="checkbox" name="show_actor_tooltips"> Show actor tooltips</label></li>
<!-- <!--
<li><label><input type="checkbox" disabled> Show actor info</label></li>
<li><label><input type="checkbox" disabled> Freeze time for everything else</label></li> <li><label><input type="checkbox" disabled> Freeze time for everything else</label></li>
<li><label><input type="checkbox" disabled> Player is immortal</label></li> <li><label><input type="checkbox" disabled> Player is immortal</label></li>
<li><label><input type="checkbox" disabled> Player ignores collision</label></li> <li><label><input type="checkbox" disabled> Player ignores collision</label></li>

View File

@ -515,18 +515,6 @@ class Player extends PrimaryView {
this.renderer = new CanvasRenderer(this.conductor.tilesets['ll']); this.renderer = new CanvasRenderer(this.conductor.tilesets['ll']);
this._loaded_tileset = false; this._loaded_tileset = false;
this.level_el.append(this.renderer.canvas); this.level_el.append(this.renderer.canvas);
this.renderer.canvas.addEventListener('auxclick', ev => {
if (ev.button !== 1)
return;
if (! this.debug.enabled)
return;
let [x, y] = this.renderer.cell_coords_from_event(ev);
this.level.move_to(this.level.player, this.level.cell(x, y), 1);
// TODO this behaves a bit weirdly when paused (doesn't redraw even with a force), i
// think because we're still claiming a speed of 1 so time has to pass before the move
// actually "happens"
});
// Populate a skeleton inventory // Populate a skeleton inventory
this.inventory_key_nodes = {}; this.inventory_key_nodes = {};
@ -864,10 +852,9 @@ class Player extends PrimaryView {
}), }),
); );
// Hook up play speed // Hook up play speed
let speed_el = debug_el.elements.speed; debug_el.elements.speed.value = "1";
speed_el.value = "1"; debug_el.querySelector('#player-debug-speed').addEventListener('change', ev => {
speed_el.addEventListener('change', ev => { let speed = debug_el.elements.speed.value;
let speed = ev.target.value;
let [numer, denom] = speed.split('/'); let [numer, denom] = speed.split('/');
this.play_speed = parseInt(numer, 10) / parseInt(denom ?? '1', 10); this.play_speed = parseInt(numer, 10) / parseInt(denom ?? '1', 10);
}); });
@ -877,9 +864,10 @@ class Player extends PrimaryView {
let inventory_el = debug_el.querySelector('.-inventory'); let inventory_el = debug_el.querySelector('.-inventory');
for (let name of [ for (let name of [
'key_blue', 'key_red', 'key_yellow', 'key_green', 'key_blue', 'key_red', 'key_yellow', 'key_green',
'xray_eye', 'foil', 'lightning_bolt', 'hook',
'flippers', 'fire_boots', 'cleats', 'suction_boots', 'flippers', 'fire_boots', 'cleats', 'suction_boots',
'bribe', 'railroad_sign', 'hiking_boots', 'speed_boots', 'hiking_boots', 'speed_boots', 'railroad_sign', 'helmet',
'xray_eye', 'helmet', 'foil', 'lightning_bolt', 'bribe', 'bowling_ball', 'dynamite',
]) { ]) {
inventory_el.append(make_button( inventory_el.append(make_button(
mk('img', {src: this.render_inventory_tile(name)}), mk('img', {src: this.render_inventory_tile(name)}),
@ -1067,6 +1055,31 @@ class Player extends PrimaryView {
this.renderer.show_actor_bboxes = ev.target.checked; this.renderer.show_actor_bboxes = ev.target.checked;
this._redraw(); this._redraw();
}); });
wire_checkbox('show_actor_order', ev => {
this.renderer.show_actor_order = ev.target.checked;
this._redraw();
});
wire_checkbox('show_actor_tooltips', ev => {
if (ev.target.checked) {
let element = mk('div.player-debug-actor-tooltip');
let header = mk('h3');
let dl = mk('dl');
let props = {};
for (let key of ['direction', 'movement_speed', 'movement_cooldown']) {
let dd = mk('dd');
props[key] = dd;
dl.append(mk('dt', key), dd);
}
let inventory = mk('p');
element.append(header, dl, inventory);
this.debug.actor_tooltip = {element, header, props, inventory};
document.body.append(element);
}
else if (this.debug.actor_tooltip) {
this.debug.actor_tooltip.element.remove();
this.debug.actor_tooltip = null;
}
});
wire_checkbox('disable_interpolation', ev => { wire_checkbox('disable_interpolation', ev => {
this.use_interpolation = ! ev.target.checked; this.use_interpolation = ! ev.target.checked;
this._redraw(); this._redraw();
@ -1082,6 +1095,60 @@ class Player extends PrimaryView {
}), }),
); );
// Bind some debug events on the canvas
this.renderer.canvas.addEventListener('auxclick', ev => {
if (ev.button !== 1)
return;
let [x, y] = this.renderer.cell_coords_from_event(ev);
this.level.move_to(this.level.player, this.level.cell(x, y), 1);
// TODO this behaves a bit weirdly when paused (doesn't redraw even with a force), i
// think because we're still claiming a speed of 1 so time has to pass before the move
// actually "happens"
});
this.renderer.canvas.addEventListener('mousemove', ev => {
let tooltip = this.debug.actor_tooltip;
if (! tooltip)
return;
// FIXME this doesn't work so well for actors in motion :S
// TODO show bounding box for hovered actor?
let [x, y] = this.renderer.cell_coords_from_event(ev);
// TODO should update the tooltip if the game advances but the mouse doesn't move
let cell = this.level.cell(x, y);
let actor = cell.get_actor();
tooltip.element.classList.toggle('--visible', actor);
if (! actor)
return;
tooltip.element.style.left = `${ev.clientX}px`;
tooltip.element.style.top = `${ev.clientY}px`;
tooltip.header.textContent = actor.type.name;
for (let [key, element] of Object.entries(tooltip.props)) {
element.textContent = String(actor[key] ?? "—");
}
// TODO it would be cool to use icons and whatever for this, but that would be tricky to
// do without serious canvas churn
let inv = [];
if (actor.keyring) {
for (let [key, count] of Object.entries(actor.keyring)) {
inv.push(`${key} ×${count}`);
}
}
if (actor.toolbelt) {
for (let tool of actor.toolbelt) {
inv.push(tool);
}
}
tooltip.inventory.textContent = inv.join(', ');
});
this.renderer.canvas.addEventListener('mouseout', ev => {
if (this.debug.actor_tooltip) {
this.debug.actor_tooltip.element.classList.remove('--visible');
}
});
this.adjust_scale(); this.adjust_scale();
if (this.level) { if (this.level) {
this.update_ui(); this.update_ui();
@ -1869,8 +1936,7 @@ class Player extends PrimaryView {
confirm_game_interruption(question, action) { confirm_game_interruption(question, action) {
if (this.state === 'playing' || this.state === 'paused' || this.state === 'rewinding') { if (this.state === 'playing' || this.state === 'paused' || this.state === 'rewinding') {
new ConfirmOverlay(this.conductor, "Restart this level and watch the replay?", action) new ConfirmOverlay(this.conductor, question, action).open();
.open();
} }
else { else {
action(); action();

View File

@ -57,6 +57,7 @@ export class CanvasRenderer {
this.viewport_y = 0; this.viewport_y = 0;
this.viewport_dirty = false; this.viewport_dirty = false;
this.show_actor_bboxes = false; this.show_actor_bboxes = false;
this.show_actor_order = false;
this.use_rewind_effect = false; this.use_rewind_effect = false;
this.perception = 'normal'; // normal, xray, editor, palette this.perception = 'normal'; // normal, xray, editor, palette
this.active_player = null; this.active_player = null;
@ -264,6 +265,11 @@ export class CanvasRenderer {
} }
} }
if (this.use_rewind_effect) {
this.draw_rewind_effect(tic);
}
// Debug overlays
if (this.show_actor_bboxes) { if (this.show_actor_bboxes) {
this.ctx.fillStyle = '#f004'; this.ctx.fillStyle = '#f004';
for (let x = xf0; x <= x1; x++) { for (let x = xf0; x <= x1; x++) {
@ -277,9 +283,27 @@ export class CanvasRenderer {
} }
} }
} }
if (this.show_actor_order) {
this.ctx.fillStyle = '#fff';
this.ctx.strokeStyle = '#000';
this.ctx.lineWidth = 3;
this.ctx.font = '16px monospace';
this.ctx.textAlign = 'center';
this.ctx.textBaseline = 'middle';
for (let [n, actor] of this.level.actors.entries()) {
let cell = actor.cell;
if (! cell)
continue;
if (cell.x < xf0 || cell.x > x1 || cell.y < yf0 || cell.y > y1)
continue;
if (this.use_rewind_effect) { let [vx, vy] = actor.visual_position(tic_offset, packet.update_rate);
this.draw_rewind_effect(tic); let x = (vx + 0.5 - x0) * tw;
let y = (vy + 0.5 - y0) * th;
let label = String(this.level.actors.length - 1 - n);
this.ctx.strokeText(label, x, y);
this.ctx.fillText(label, x, y);
}
} }
} }

View File

@ -1490,7 +1490,7 @@ body.--debug #player-debug {
#player-debug .-inventory { #player-debug .-inventory {
display: grid; display: grid;
margin: 0 auto; margin: 0 auto;
grid: auto-fill var(--tile-height) / repeat(8, var(--tile-width)); grid: auto-flow min-content / repeat(8, min-content);
gap: 2px; gap: 2px;
} }
#player-debug > .-inventory > button { #player-debug > .-inventory > button {
@ -1500,7 +1500,7 @@ body.--debug #player-debug {
display: block; display: block;
} }
#player-debug > .-inventory > button.-wide { #player-debug > .-inventory > button.-wide {
grid-column: span 8; grid-column: span 5;
padding: 0.25em; padding: 0.25em;
} }
#player-debug .-buttons { #player-debug .-buttons {
@ -1604,6 +1604,45 @@ body.--debug #player-debug {
display: flex; display: flex;
} }
.player-debug-actor-tooltip {
position: absolute;
display: none;
/* similar to editor tooltip */
padding: 0.33em 0.75em;
border: 1px solid black;
color: #d8d8d8;
background: hsl(225, 10%, 20%);
box-shadow: 0 1px 2px 1px #0004;
opacity: 0.9;
font-family: monospace;
pointer-events: none;
}
.player-debug-actor-tooltip.--visible {
display: block;
}
.player-debug-actor-tooltip h3 {
font-size: 1.25em;
margin-bottom: 0.25rem;
border-bottom: 1px solid currentColor;
color: white;
}
.player-debug-actor-tooltip dl {
display: grid;
grid: auto-flow min-content / auto auto;
align-items: baseline;
gap: 0.25em 1em;
margin: 0;
}
.player-debug-actor-tooltip dl > dt {
grid-column: 1;
}
.player-debug-actor-tooltip dl > dd {
grid-column: 2;
margin: 0;
}
/**************************************************************************************************/ /**************************************************************************************************/
/* Editor */ /* Editor */