Finally add and wire up most of the debug panel

This commit is contained in:
Eevee (Evelyn Woods) 2020-12-06 18:51:12 -07:00
parent c1ba299e9f
commit aeac5c285b
4 changed files with 303 additions and 69 deletions

View File

@ -109,6 +109,85 @@
</div>
<div class="inventory"></div>
</section>
<form id="player-debug">
<div class="-time">
<table class="-time-controls">
<tr>
<td><button type="button" class="-time-button" data-dt="-1">← 1 tic</button></td>
<td id="player-debug-time-tics">0</td>
<td>tics</td>
<td><button type="button" class="-time-button" data-dt="1">1 tic →</button></td>
</tr>
<tr>
<td><button type="button" class="-time-button" data-dt="-4">← 1 move</button></td>
<td id="player-debug-time-moves">0</td>
<td>moves</td>
<td><button type="button" class="-time-button" data-dt="4">1 move →</button></td>
</tr>
<tr>
<td><button type="button" class="-time-button" data-dt="-20">← 1 s</button></td>
<td id="player-debug-time-secs">0</td>
<td>seconds</td>
<td><button type="button" class="-time-button" data-dt="20">1 s →</button></td>
</tr>
</table>
<p>Game speed:
<select name="speed">
<option value="6">6× — 120 tics per second</option>
<option value="3">3× — 60 tics per second</option>
<option value="2">2× — 40 tics per second</option>
<option value="3/2">× — 30 tics per second</option>
<option value="1" selected>1× — 20 tics per second</option>
<option value="1/2">½× — 10 tics per second</option>
<option value="1/3">⅓× — 6⅔ tics per second</option>
<option value="1/4">¼× — 5 tics per second</option>
</select>
</p>
</div>
<div class="input"></div>
<div class="-options">
<p> Viewport size:
<select id="player-debug-viewport" disabled>
<option value="default" selected>Level default (9 or 10)</option>
<option value="12">12 × 12</option>
<option value="16">16 × 16</option>
<option value="24">24 × 24</option>
<option value="32">32 × 32</option>
<option value="max">Entire level</option>
</select>
</p>
<ul>
<li><input type="checkbox" disabled> Show actor info</li>
<li><input type="checkbox" disabled> Show actor bounding boxes</li>
<li><input type="checkbox" disabled> Freeze time for everything else</li>
<li><input type="checkbox" disabled> Player is immortal</li>
<li><input type="checkbox" disabled> Player ignores collision</li>
<li><input type="checkbox" disabled> Player levitates</li>
</ul>
<p>(Middle-click to teleport.)</p>
</div>
<div class="-inventory">
<!-- populated in js -->
</div>
<div class="-buttons">
<!-- populated in js -->
</div>
<!-- TODO?
- inspect with mouse
- list of actors, or currently pointed-to actor?
- activate something manually?
- click a button ingame?
- pan viewport (like editor)
- show connections, directions, other editor features
- look under anything
- other game info?
- count tiles?
- total hearts?
- total bonus flags?
-->
</form>
<div id="player-music">
<div id="player-music-left">
🎵 <a id="player-music-title">title</a> by <a id="player-music-author">author</a>
@ -157,45 +236,6 @@
<button class="demo-play" type="button">View replay</button>
</div>
</div>
<div id="player-debug">
<p>Play time: A tics, B moves, C seconds</p>
<p>
<button class="demo-step-1" type="button">Step 1 tic</button>
<button class="demo-step-4" type="button">Step 1 move</button>
<button>« 4 tics</button>
<button>« 1 tic</button>
<button>1 tic »</button>
<button>4 tics »</button>
</p>
<p>Speed: 6× (0.5 frame), 3× (1 frame), 2× (???), 1.5× (2 frames), 1× (3 frames), ½× (6 frames), ⅓× (9 frames), ¼× (12 frames)</p>
<p>Viewport: 9/10, 12, 16, 24, 32, map size</p>
<div class="input"></div>
<p><input type="checkbox"> Show actor info</p>
<p><input type="checkbox"> Show actor bounding boxes</p>
<p><input type="checkbox"> Freeze time for everything else</p>
<p><input type="checkbox"> Immortal / intangible?</p>
<p><input type="checkbox"> Ignore collisions</p>
<p><input type="checkbox"> Levitate</p>
<p><input type="checkbox"> Stop clock</p> <!-- TODO toggle clock like the ingame tile? -->
<p>Give item: <img src="icons/tool-bg-selected.png"> (Click inventory to destroy items)</p>
<p>
<button>Green button</button>
<button>Blue button</button>
</p>
<!-- TODO?
- inspect with mouse
- list of actors, or currently pointed-to actor?
- activate something manually?
- click a button ingame?
- pan viewport (like editor)
- show connections, directions, other editor features
- look under anything
-->
</div>
</main>
<main id="editor" hidden>
<header>

View File

@ -256,6 +256,7 @@ class Player extends PrimaryView {
this.scale = 1;
this.play_speed = 1;
this.compat = {
popwalls_react_on_arrive: false,
auto_convert_ccl_popwalls: true,
@ -417,12 +418,14 @@ class Player extends PrimaryView {
this.renderer.canvas.addEventListener('auxclick', ev => {
if (ev.button !== 1)
return;
// TODO make a real debug flag? allow enabling this but consider it aid level 3?
if (! location.host.match(/localhost/))
if (! this.debug.enabled)
return;
let [x, y] = this.renderer.cell_coords_from_event(ev);
this.level.move_to(this.level.player, this.level.cells[y][x], 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 inventory
@ -459,11 +462,20 @@ class Player extends PrimaryView {
if (! this.active)
return;
if (ev.key === 'p' || ev.key === 'Pause') {
if (ev.key === 'p') {
this.toggle_pause();
return;
}
if (ev.key === 'Pause' && ! this.debug.enabled) {
new ConfirmOverlay(this.conductor,
"Enable debug mode? This will disable all saving of scores until you reload!",
() => {
this.setup_debug();
},
).open();
}
if (ev.key === ' ') {
if (this.state === 'waiting') {
// Start without moving
@ -618,6 +630,7 @@ class Player extends PrimaryView {
});
// Populate input debugger
this.debug = { enabled: false };
this.input_el = this.root.querySelector('.input');
this.input_action_elements = {};
for (let [action, label] of Object.entries(ACTION_LABELS)) {
@ -645,6 +658,102 @@ class Player extends PrimaryView {
setup() {
}
// Link up the debug panel and enable debug features
setup_debug() {
this.root.classList.add('--debug');
let debug_el = this.root.querySelector('#player-debug');
this.debug = {
enabled: true,
time_tics_el: this.root.querySelector('#player-debug-time-tics'),
time_moves_el: this.root.querySelector('#player-debug-time-moves'),
time_secs_el: this.root.querySelector('#player-debug-time-secs'),
};
// 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);
debug_el.querySelector('.-time-controls').addEventListener('click', ev => {
let button = ev.target.closest('button.-time-button');
if (! button)
return;
let dt = parseInt(button.getAttribute('data-dt'));
if (dt > 0) {
this.advance_by(dt);
}
else if (dt < 0) {
for (let i = 0; i < -dt; i++) {
if (! this.level.has_undo())
break;
this.undo();
}
}
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);
});
debug_el.querySelector('.-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();
}),
make_button("toggle clock", () => {
this.level.pause_timer();
this.update_ui();
}),
make_button("+10s", () => {
this.level.adjust_timer(+10);
this.update_ui();
}),
make_button("10s", () => {
this.level.adjust_timer(-10);
this.update_ui();
}),
make_button("stop clock", () => {
this.level.time_remaining = null;
this.update_ui();
}),
);
this.update_ui();
}
activate() {
// We can't resize when we're not visible, so do it now
super.activate();
@ -907,7 +1016,7 @@ class Player extends PrimaryView {
// buffer dry) and change to 'waiting' instead?
}
let dt = 1000 / TICS_PER_SECOND;
let dt = 1000 / (TICS_PER_SECOND * this.play_speed);
if (this.state === 'rewinding') {
// Rewind faster than normal time
dt *= 0.5;
@ -936,7 +1045,7 @@ class Player extends PrimaryView {
this.tic_offset = 1;
}
else {
this.tic_offset = Math.min(0.9999, (performance.now() - this.last_advance) / 1000 / (1 / TICS_PER_SECOND));
this.tic_offset = Math.min(0.9999, (performance.now() - this.last_advance) / 1000 * TICS_PER_SECOND * this.play_speed);
if (this.state === 'rewinding') {
this.tic_offset = 1 - this.tic_offset;
}
@ -1034,6 +1143,13 @@ class Player extends PrimaryView {
for (let action of Object.keys(ACTION_LABELS)) {
this.input_action_elements[action].classList.toggle('--pressed', this.previous_input.has(action));
}
if (this.debug.enabled) {
let t = this.level.tic_counter;
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);
}
}
toggle_pause() {
@ -1108,22 +1224,24 @@ class Player extends PrimaryView {
let scorecard = this.level.get_scorecard();
let savefile = this.conductor.current_pack_savefile;
let old_scorecard;
if (! savefile.scorecards[level_index] ||
savefile.scorecards[level_index].score < scorecard.score ||
(savefile.scorecards[level_index].score === scorecard.score &&
savefile.scorecards[level_index].aid > scorecard.aid))
{
old_scorecard = savefile.scorecards[level_index];
if (! this.debug.enabled) {
if (! savefile.scorecards[level_index] ||
savefile.scorecards[level_index].score < scorecard.score ||
(savefile.scorecards[level_index].score === scorecard.score &&
savefile.scorecards[level_index].aid > scorecard.aid))
{
old_scorecard = savefile.scorecards[level_index];
// Adjust the total score
savefile.total_score = savefile.total_score ?? 0;
if (old_scorecard) {
savefile.total_score -= old_scorecard.score;
// Adjust the total score
savefile.total_score = savefile.total_score ?? 0;
if (old_scorecard) {
savefile.total_score -= old_scorecard.score;
}
savefile.total_score += scorecard.score;
savefile.scorecards[level_index] = scorecard;
this.conductor.save_savefile();
}
savefile.total_score += scorecard.score;
savefile.scorecards[level_index] = scorecard;
this.conductor.save_savefile();
}
overlay_reason = 'success';

View File

@ -1174,9 +1174,7 @@ const TILE_TYPES = {
// Buttons
button_blue: {
draw_layer: DRAW_LAYERS.terrain,
on_arrive(me, level, other) {
level.sfx.play_once('button-press', me.cell);
do_button(level) {
// Flip direction of all blue tanks
for (let actor of level.actors) {
// TODO generify somehow??
@ -1185,6 +1183,10 @@ const TILE_TYPES = {
}
}
},
on_arrive(me, level, other) {
level.sfx.play_once('button-press', me.cell);
me.type.do_button(level);
},
on_depart(me, level, other) {
level.sfx.play_once('button-release', me.cell);
},
@ -1209,9 +1211,7 @@ const TILE_TYPES = {
},
button_green: {
draw_layer: DRAW_LAYERS.terrain,
on_arrive(me, level, other) {
level.sfx.play_once('button-press', me.cell);
do_button(level) {
// Swap green floors and walls
// TODO could probably make this more compact for undo purposes
for (let row of level.cells) {
@ -1233,6 +1233,10 @@ const TILE_TYPES = {
}
}
},
on_arrive(me, level, other) {
level.sfx.play_once('button-press', me.cell);
me.type.do_button(level);
},
on_depart(me, level, other) {
level.sfx.play_once('button-release', me.cell);
},

View File

@ -202,10 +202,15 @@ svg.svg-icon {
dl.formgrid {
display: grid;
grid: auto-flow min-content / 1fr 4fr;
align-items: baseline;
gap: 1em;
margin: 0;
}
dl.formgrid > dt {
grid-column: 1;
}
dl.formgrid > dd {
grid-column: 2;
margin: 0;
}
@ -561,7 +566,7 @@ button.level-pack-button p {
"level inventory" min-content
"level actions" min-content
/* Need explicit min-content to force the hint to wrap */
/ min-content min-content
/ min-content min-content min-content
;
column-gap: 2em;
row-gap: 0.5em;
@ -603,6 +608,10 @@ button.level-pack-button p {
text-align: center;
text-shadow: 0 2px 1px black;
}
/* Allow clicking through the overlay in debug mode */
#player.--debug .overlay-message {
pointer-events: none;
}
#player .overlay-message p {
margin: 0;
}
@ -840,13 +849,76 @@ main.--has-demo .demo-controls {
}
/* Debug stuff */
#player.--debug #player-debug {
display: grid;
}
#player-debug {
display: none;
grid:
"time inventory options"
"buttons buttons buttons"
/ 2fr 1fr 2fr
;
gap: 0.25em;
padding: 0.5em;
background-image: repeating-linear-gradient(135deg,
hsl(0, 0%, 12.5%) 0em,
hsl(0, 0%, 12.5%) 1em,
hsl(45, 25%, 12.5%) 1em,
hsl(45, 25%, 12.5%) 2em);
hsl(0, 0%, 4%) 0em,
hsl(0, 0%, 4%) 1em,
hsl(15, 25%, 8%) 1em,
hsl(15, 25%, 8%) 2em);
border: 0.25em solid hsla(30, 100%, 50%, 0.5);
box-shadow: inset 0 0 0.25em 0.25em #0004;
}
#player-debug > .-time {
grid-area: time;
}
#player-debug > .-time table.-time-controls {
}
#player-debug > .-time table.-time-controls button {
width: 100%;
text-align: inherit;
}
#player-debug > .-time table.-time-controls td:nth-child(1) {
/* go backwards button */
text-align: left;
}
#player-debug > .-time table.-time-controls td:nth-child(2) {
/* value */
width: 6em;
text-align: right;
}
#player-debug > .-time table.-time-controls td:nth-child(3) {
/* label */
}
#player-debug > .-time table.-time-controls td:nth-child(4) {
/* go forward button */
text-align: right;
}
#player-debug > .-options {
grid-area: options;
}
#player-debug > .-inventory {
grid-area: inventory;
display: grid;
margin: auto;
grid: auto-fill var(--tile-height) / repeat(4, var(--tile-width));
gap: 2px;
}
#player-debug > .-inventory > button {
padding: 0;
}
#player-debug > .-inventory > button > img {
display: block;
}
#player-debug > .-inventory > button.-wide {
grid-column: span 4;
padding: 0.25em;
}
#player-debug > .-buttons {
grid-area: buttons;
display: flex;
gap: 0.25em;
}
.input {
display: grid;