diff --git a/js/game.js b/js/game.js index da1f886..609a316 100644 --- a/js/game.js +++ b/js/game.js @@ -473,13 +473,11 @@ export class Level { return; } - this.advance_tic_all(p1_actions); - - // Commit the undo state at the end of each tic (pass 2) - this.commit(); + this.begin_tic(p1_actions); + this.finish_tic(p1_actions); } - advance_tic_all(p1_actions) { + begin_tic(p1_actions) { // Store some current level state in the undo entry. (These will often not be modified, but // they only take a few bytes each so that's fine.) for (let key of [ @@ -493,6 +491,7 @@ export class Level { // Player's secondary direction is set immediately; it applies on arrival to cells even if // it wasn't held the last time the player started moving + // TODO this feels wrong to me but i'm not sure why if (p1_actions.secondary === this.player.direction) { this._set_tile_prop(this.player, 'secondary_direction', p1_actions.primary); } @@ -555,7 +554,9 @@ export class Level { this.update_wiring(); this.update_wiring(); this.update_wiring(); + } + finish_tic(p1_actions) { // SECOND PASS: actors decide their upcoming movement simultaneously for (let i = this.actors.length - 1; i >= 0; i--) { let actor = this.actors[i]; @@ -799,6 +800,8 @@ export class Level { this.sfx.play_once('tick'); } } + + this.commit(); } // Try to move the given actor one tile in the given direction and update their cooldown. diff --git a/js/main.js b/js/main.js index d8a921a..564709a 100644 --- a/js/main.js +++ b/js/main.js @@ -308,16 +308,22 @@ class Player extends PrimaryView { }); // 0: normal realtime mode - // 1: turn-based mode - // 2: turn-based mode, but we know the game is frozen waiting for input + // 1: turn-based mode, at the start of a tic + // 2: turn-based mode, in mid-tic, with the game frozen waiting for input this.turn_mode = 0; this.turn_based_checkbox = this.root.querySelector('.controls .control-turn-based'); this.turn_based_checkbox.checked = false; this.turn_based_checkbox.addEventListener('change', ev => { if (this.turn_based_checkbox.checked) { + // If we're leaving real-time mode then we're between tics this.turn_mode = 1; } else { + if (this.turn_mode === 2) { + // Finish up the tic with dummy input + this.level.finish_tic({primary: null, secondary: null}); + this.advance_by(1); + } this.turn_mode = 0; } }); @@ -1020,16 +1026,29 @@ class Player extends PrimaryView { } let has_input = input.has('wait') || Object.values(player_actions).some(x => x); + // Turn-based mode complicates this slightly; it aligns us to the middle of a tic + if (this.turn_mode === 2) { + if (has_input) { + // Finish the current tic, then continue as usual. This means the end of the + // tic doesn't count against the number of tics to advance -- because it already + // did, the first time we tried it + this.level.finish_tic(player_actions); + this.turn_mode = 1; + } + else { + continue; + } + } + + // We should now be at the start of a tic + this.level.begin_tic(player_actions); if (this.turn_mode > 0 && this.level.can_accept_input() && ! has_input) { // If we're in turn-based mode and could provide input here, but don't have any, // then wait until we do this.turn_mode = 2; } else { - this.level.advance_tic(player_actions); - if (this.turn_mode > 1) { - this.turn_mode = 1; - } + this.level.finish_tic(player_actions); } if (this.level.state !== 'playing') { @@ -1088,6 +1107,10 @@ class Player extends PrimaryView { undo() { this.level.undo(); + // Undo always returns to the start of a tic + if (this.turn_mode === 2) { + this.turn_mode = 1; + } } // Redraws every frame, unless the game isn't running