Finally add and wire up most of the debug panel
This commit is contained in:
parent
c1ba299e9f
commit
aeac5c285b
118
index.html
118
index.html
@ -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">1½× — 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>
|
||||
|
||||
156
js/main.js
156
js/main.js
@ -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';
|
||||
|
||||
@ -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);
|
||||
},
|
||||
|
||||
82
style.css
82
style.css
@ -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;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user