Add some more actor inspection to debug mode
This commit is contained in:
parent
134270e3e3
commit
54823f62bf
33
index.html
33
index.html
@ -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">1½× 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>
|
||||||
|
|||||||
106
js/main.js
106
js/main.js
@ -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();
|
||||||
|
|||||||
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
43
style.css
43
style.css
@ -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 */
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user