This commit is contained in:
parent
ed7c7461b6
commit
4a5f0e36c6
193
js/game.js
193
js/game.js
@ -481,6 +481,8 @@ export class Level extends LevelInterface {
|
|||||||
// Note that this clock counts *up*, even on untimed levels, and is unaffected by CC2's
|
// Note that this clock counts *up*, even on untimed levels, and is unaffected by CC2's
|
||||||
// clock alteration shenanigans
|
// clock alteration shenanigans
|
||||||
this.tic_counter = 0;
|
this.tic_counter = 0;
|
||||||
|
// 0 to 2, counting which frame within a tic we're on in CC2
|
||||||
|
this.frame_offset = 0;
|
||||||
// 0 to 7, indicating the first tic that teeth can move on.
|
// 0 to 7, indicating the first tic that teeth can move on.
|
||||||
// 0 is equivalent to even step; 4 is equivalent to odd step.
|
// 0 is equivalent to even step; 4 is equivalent to odd step.
|
||||||
// 5 is the default in CC2. Lynx can use any of the 8. MSCC uses
|
// 5 is the default in CC2. Lynx can use any of the 8. MSCC uses
|
||||||
@ -818,12 +820,10 @@ export class Level extends LevelInterface {
|
|||||||
|
|
||||||
can_accept_input() {
|
can_accept_input() {
|
||||||
// We can accept input anytime the player can move, i.e. when they're not already moving and
|
// We can accept input anytime the player can move, i.e. when they're not already moving and
|
||||||
// not in an un-overrideable slide.
|
// not in an un-overrideable slide
|
||||||
// Note that this only makes sense in the middle of a tic; at the beginning of one, the
|
return this.player.movement_cooldown === 0 &&
|
||||||
// player's movement cooldown may very well be 1, but it'll be decremented before they
|
(this.player.slide_mode === null || (
|
||||||
// attempt to move
|
this.player.slide_mode === 'force' && this.player.last_move_was_force));
|
||||||
return this.player.movement_cooldown === 0 && (this.player.slide_mode === null || (
|
|
||||||
this.player.slide_mode === 'force' && this.player.last_move_was_force));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lynx PRNG, used unchanged in CC2
|
// Lynx PRNG, used unchanged in CC2
|
||||||
@ -874,81 +874,33 @@ export class Level extends LevelInterface {
|
|||||||
// Input is a bit mask of INPUT_BITS.
|
// Input is a bit mask of INPUT_BITS.
|
||||||
advance_tic(p1_input) {
|
advance_tic(p1_input) {
|
||||||
if (this.state !== 'playing') {
|
if (this.state !== 'playing') {
|
||||||
console.warn(`Level.advance_tic() called when state is ${this.state}`);
|
console.warn(`Attempting to advance game when state is ${this.state}`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.begin_tic(p1_input);
|
this._do_init_phase();
|
||||||
this.finish_tic(p1_input);
|
this._set_p1_input(p1_input);
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME a whole bunch of these comments are gonna be wrong or confusing now
|
|
||||||
begin_tic(p1_input) {
|
|
||||||
// At the beginning of the very first tic, some tiles want to do initialization that's not
|
|
||||||
// appropriate to do before the game begins. (For example, bombs blow up anything that
|
|
||||||
// starts on them in CC2, but we don't want to do that before the game has run at all. We
|
|
||||||
// DEFINITELY don't want to blow the PLAYER up before the game starts!)
|
|
||||||
if (! this.done_on_begin) {
|
|
||||||
// Run backwards, to match actor order
|
|
||||||
for (let i = this.linear_cells.length - 1; i >= 0; i--) {
|
|
||||||
let cell = this.linear_cells[i];
|
|
||||||
for (let tile of cell) {
|
|
||||||
if (tile && tile.type.on_begin) {
|
|
||||||
tile.type.on_begin(tile, this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// It's not possible to rewind to before this happened, so clear undo and permanently
|
|
||||||
// set a flag
|
|
||||||
this.pending_undo = this.create_undo_entry();
|
|
||||||
this.done_on_begin = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.undo_enabled) {
|
|
||||||
// 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 [
|
|
||||||
'_rng1', '_rng2', '_blob_modifier', '_tw_rng', 'force_floor_direction',
|
|
||||||
'tic_counter', 'time_remaining', 'timer_paused',
|
|
||||||
'chips_remaining', 'bonus_points', 'state',
|
|
||||||
'player1_move', 'player2_move', 'remaining_players', 'player',
|
|
||||||
]) {
|
|
||||||
this.pending_undo.level_props[key] = this[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.p1_input = p1_input;
|
|
||||||
this.p1_released |= ~p1_input; // Action keys released since we last checked them
|
|
||||||
this.swap_player1 = false;
|
|
||||||
|
|
||||||
this.sfx.set_player_position(this.player.cell);
|
|
||||||
|
|
||||||
if (this.compat.use_lynx_loop) {
|
if (this.compat.use_lynx_loop) {
|
||||||
if (this.compat.emulate_60fps) {
|
if (this.compat.emulate_60fps) {
|
||||||
this._begin_tic_lynx60();
|
this._advance_tic_lynx60();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this._begin_tic_lynx();
|
this._advance_tic_lynx();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this._begin_tic_lexy();
|
this._advance_tic_lexy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME merge this a bit more with the lynx loop, which should be more in finish_tic anyway
|
// Default loop: run at 20 tics per second, split things into some more loops
|
||||||
// FIXME fix turn-based mode
|
_advance_tic_lexy() {
|
||||||
// FIXME you are now not synched with something coming out of a trap or cloner, but i don't know
|
// Under CC2 rules, there are two wire updates at the very beginning of the game before the
|
||||||
// how to fix that with this loop
|
// player can actually move. That means the first tic has five wire phases total.
|
||||||
// Finish a tic, i.e., apply input just before the player can make a decision and then do it
|
if (this.tic_counter === 0) {
|
||||||
finish_tic(p1_input) {
|
this._do_wire_phase();
|
||||||
this.p1_input = p1_input;
|
this._do_wire_phase();
|
||||||
this.p1_released |= ~p1_input; // Action keys released since we last checked them
|
|
||||||
|
|
||||||
if (this.compat.use_lynx_loop) {
|
|
||||||
if (this.compat.emulate_60fps) {
|
|
||||||
this._finish_tic_lynx60();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this._do_decision_phase();
|
this._do_decision_phase();
|
||||||
@ -996,6 +948,7 @@ export class Level extends LevelInterface {
|
|||||||
|
|
||||||
this._swap_players();
|
this._swap_players();
|
||||||
|
|
||||||
|
// Wire updates every frame, which means thrice per tic
|
||||||
this._do_wire_phase();
|
this._do_wire_phase();
|
||||||
this._do_wire_phase();
|
this._do_wire_phase();
|
||||||
this._do_wire_phase();
|
this._do_wire_phase();
|
||||||
@ -1003,50 +956,110 @@ export class Level extends LevelInterface {
|
|||||||
this._do_cleanup_phase();
|
this._do_cleanup_phase();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lexy-style loop, similar to Lynx but with some things split out into separate phases
|
// Lynx loop: everyone decides, then everyone moves/cools in a single pass
|
||||||
_begin_tic_lexy() {
|
_advance_tic_lynx() {
|
||||||
// CC2 wiring runs every frame, not every tic, so we need to do it three times, but dealing
|
|
||||||
// with it is delicate. Ideally the player would see the current state of the game when
|
|
||||||
// they move, so all the wire updates should be at the end, BUT under CC2 rules, there are
|
|
||||||
// two wire updates at the start of the game before any movement can actually happen.
|
|
||||||
// Do those here, then otherwise do three wire phases when finishing.
|
|
||||||
if (this.tic_counter === 0) {
|
|
||||||
this._do_wire_phase();
|
|
||||||
this._do_wire_phase();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lynx-style loop: everyone decides, then everyone moves/cools.
|
|
||||||
_begin_tic_lynx() {
|
|
||||||
// FIXME this should have three wire passes too, chief
|
|
||||||
this._do_decision_phase();
|
this._do_decision_phase();
|
||||||
this._do_combined_action_phase(3);
|
this._do_combined_action_phase(3);
|
||||||
this._do_wire_phase();
|
this._do_wire_phase();
|
||||||
|
this._do_wire_phase();
|
||||||
|
this._do_wire_phase();
|
||||||
|
|
||||||
this._do_cleanup_phase();
|
this._do_cleanup_phase();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Same as above, but split up to run at 60fps, where only every third frame allows for
|
// CC2 loop: similar to the Lynx loop, but run three times per tic, and non-forced decisions can
|
||||||
// decisions. This is how CC2 works.
|
// only be made every third frame
|
||||||
_begin_tic_lynx60() {
|
_advance_tic_lynx60() {
|
||||||
this._do_decision_phase(true);
|
this._do_decision_phase(true);
|
||||||
this._do_combined_action_phase(1, true);
|
this._do_combined_action_phase(1, true);
|
||||||
this._do_wire_phase();
|
this._do_wire_phase();
|
||||||
|
|
||||||
|
this.frame_offset = 1;
|
||||||
this._do_decision_phase(true);
|
this._do_decision_phase(true);
|
||||||
this._do_combined_action_phase(1, true);
|
this._do_combined_action_phase(1, true);
|
||||||
this._do_wire_phase();
|
this._do_wire_phase();
|
||||||
}
|
|
||||||
// This is in the "finish" part to preserve the property turn-based mode expects, where "finish"
|
this.frame_offset = 2;
|
||||||
// picks up right when the player could provide input
|
|
||||||
_finish_tic_lynx60() {
|
|
||||||
this._do_decision_phase();
|
this._do_decision_phase();
|
||||||
this._do_combined_action_phase(1);
|
this._do_combined_action_phase(1);
|
||||||
this._do_wire_phase();
|
this._do_wire_phase();
|
||||||
|
|
||||||
|
this.frame_offset = 0;
|
||||||
this._do_cleanup_phase();
|
this._do_cleanup_phase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Attempt to advance by one FRAME at a time. Primarily useful for running 60 FPS mode at,
|
||||||
|
// well, 60 FPS.
|
||||||
|
advance_frame(p1_input) {
|
||||||
|
if (this.compat.use_lynx_loop && this.compat.emulate_60fps) {
|
||||||
|
// Lynx 60, i.e. CC2
|
||||||
|
if (this.frame_offset === 0) {
|
||||||
|
this._do_init_phase(p1_input);
|
||||||
|
}
|
||||||
|
this._set_p1_input(p1_input);
|
||||||
|
let is_decision_frame = this.frame_offset === 2;
|
||||||
|
|
||||||
|
this._do_decision_phase(! is_decision_frame);
|
||||||
|
this._do_combined_action_phase(1, ! is_decision_frame);
|
||||||
|
this._do_wire_phase();
|
||||||
|
|
||||||
|
if (this.frame_offset === 2) {
|
||||||
|
this._do_cleanup_phase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// This is either Lexy mode or Lynx mode, and either way we run at 20 tps
|
||||||
|
if (this.frame_offset === 0) {
|
||||||
|
this.advance_tic(p1_input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.frame_offset = (this.frame_offset + 1) % 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
_set_p1_input(p1_input) {
|
||||||
|
this.p1_input = p1_input;
|
||||||
|
this.p1_released |= ~p1_input; // Action keys released since we last checked them
|
||||||
|
}
|
||||||
|
|
||||||
|
_do_init_phase() {
|
||||||
|
// At the beginning of the very first tic, some tiles want to do initialization that's not
|
||||||
|
// appropriate to do before the game begins. (For example, bombs blow up anything that
|
||||||
|
// starts on them in CC2, but we don't want to do that before the game has run at all. We
|
||||||
|
// DEFINITELY don't want to blow the PLAYER up before the game starts!)
|
||||||
|
if (! this.done_on_begin) {
|
||||||
|
// Run backwards, to match actor order
|
||||||
|
for (let i = this.linear_cells.length - 1; i >= 0; i--) {
|
||||||
|
let cell = this.linear_cells[i];
|
||||||
|
for (let tile of cell) {
|
||||||
|
if (tile && tile.type.on_begin) {
|
||||||
|
tile.type.on_begin(tile, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// It's not possible to rewind to before this happened, so clear undo and permanently
|
||||||
|
// set a flag
|
||||||
|
this.pending_undo = this.create_undo_entry();
|
||||||
|
this.done_on_begin = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.undo_enabled) {
|
||||||
|
// 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 [
|
||||||
|
'_rng1', '_rng2', '_blob_modifier', '_tw_rng', 'force_floor_direction',
|
||||||
|
'tic_counter', 'frame_offset', 'time_remaining', 'timer_paused',
|
||||||
|
'chips_remaining', 'bonus_points', 'state',
|
||||||
|
'player1_move', 'player2_move', 'remaining_players', 'player',
|
||||||
|
]) {
|
||||||
|
this.pending_undo.level_props[key] = this[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.swap_player1 = false;
|
||||||
|
|
||||||
|
this.sfx.set_player_position(this.player.cell);
|
||||||
|
}
|
||||||
|
|
||||||
// Decision phase: all actors decide on their movement "simultaneously"
|
// Decision phase: all actors decide on their movement "simultaneously"
|
||||||
_do_decision_phase(forced_only = false) {
|
_do_decision_phase(forced_only = false) {
|
||||||
// Before decisions happen, remember the player's /current/ direction, which may be affected
|
// Before decisions happen, remember the player's /current/ direction, which may be affected
|
||||||
|
|||||||
116
js/main.js
116
js/main.js
@ -444,25 +444,12 @@ class Player extends PrimaryView {
|
|||||||
this.music_audio_el = this.music_el.querySelector('audio');
|
this.music_audio_el = this.music_el.querySelector('audio');
|
||||||
this.music_index = null;
|
this.music_index = null;
|
||||||
|
|
||||||
// 0: normal realtime mode
|
this.turn_based_mode = false;
|
||||||
// 1: turn-based mode, at the start of a tic
|
this.turn_based_mode_waiting = false;
|
||||||
// 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('.control-turn-based');
|
this.turn_based_checkbox = this.root.querySelector('.control-turn-based');
|
||||||
this.turn_based_checkbox.checked = false;
|
this.turn_based_checkbox.checked = false;
|
||||||
this.turn_based_checkbox.addEventListener('change', ev => {
|
this.turn_based_checkbox.addEventListener('change', ev => {
|
||||||
if (this.turn_based_checkbox.checked) {
|
this.turn_based_mode = 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(0);
|
|
||||||
this.advance_by(1);
|
|
||||||
}
|
|
||||||
this.turn_mode = 0;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Bind buttons
|
// Bind buttons
|
||||||
@ -592,7 +579,7 @@ class Player extends PrimaryView {
|
|||||||
|
|
||||||
// Per-tic navigation; only useful if the game isn't running
|
// Per-tic navigation; only useful if the game isn't running
|
||||||
if (ev.key === ',') {
|
if (ev.key === ',') {
|
||||||
if (this.state === 'stopped' || this.state === 'paused' || this.turn_mode > 0) {
|
if (this.state === 'stopped' || this.state === 'paused' || this.turn_based_mode) {
|
||||||
this.set_state('paused');
|
this.set_state('paused');
|
||||||
this.undo();
|
this.undo();
|
||||||
this.update_ui();
|
this.update_ui();
|
||||||
@ -601,11 +588,16 @@ class Player extends PrimaryView {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (ev.key === '.') {
|
if (ev.key === '.') {
|
||||||
if (this.state === 'waiting' || this.state === 'paused' || this.turn_mode > 0) {
|
if (this.state === 'waiting' || this.state === 'paused' || this.turn_based_mode) {
|
||||||
if (this.state === 'waiting' || this.turn_mode === 1) {
|
if (this.state === 'waiting') {
|
||||||
this.set_state('paused');
|
if (this.turn_based_mode) {
|
||||||
|
this.set_state('playing');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.set_state('paused');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.advance_by(1, true);
|
this.advance_by(1, true, ev.altKey && this.level.compat.emulate_60fps);
|
||||||
this._redraw();
|
this._redraw();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@ -1301,7 +1293,7 @@ class Player extends PrimaryView {
|
|||||||
_clear_state() {
|
_clear_state() {
|
||||||
this.set_state('waiting');
|
this.set_state('waiting');
|
||||||
|
|
||||||
this.turn_mode = this.turn_based_checkbox.checked ? 1 : 0;
|
this.turn_based_mode_waiting = false;
|
||||||
this.last_advance = 0;
|
this.last_advance = 0;
|
||||||
this.current_keyring = {};
|
this.current_keyring = {};
|
||||||
this.current_toolbelt = [];
|
this.current_toolbelt = [];
|
||||||
@ -1382,7 +1374,8 @@ class Player extends PrimaryView {
|
|||||||
return input;
|
return input;
|
||||||
}
|
}
|
||||||
|
|
||||||
advance_by(tics, force = false) {
|
advance_by(tics, force = false, use_frames = false) {
|
||||||
|
let crossed_tic_boundary = false;
|
||||||
for (let i = 0; i < tics; i++) {
|
for (let i = 0; i < tics; i++) {
|
||||||
// FIXME turn-based mode should be disabled during a replay
|
// FIXME turn-based mode should be disabled during a replay
|
||||||
let input = this.get_input();
|
let input = this.get_input();
|
||||||
@ -1394,38 +1387,32 @@ class Player extends PrimaryView {
|
|||||||
this.debug.replay.set(this.level.tic_counter, input);
|
this.debug.replay.set(this.level.tic_counter, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Turn-based mode is considered assistance, but only if the game actually attempts to
|
if (this.turn_based_mode) {
|
||||||
// progress while it's enabled
|
// Turn-based mode is considered assistance, but only if the game actually attempts
|
||||||
if (this.turn_mode > 0) {
|
// to progress while it's enabled
|
||||||
this.level.aid = Math.max(1, this.level.aid);
|
this.level.aid = Math.max(1, this.level.aid);
|
||||||
}
|
|
||||||
|
|
||||||
let has_input = wait || input;
|
// If we're in turn-based mode and could provide input here, but don't have any,
|
||||||
// Turn-based mode complicates this slightly; it aligns us to the middle of a tic
|
// then wait until we do
|
||||||
if (this.turn_mode === 2) {
|
if (this.level.can_accept_input() && ! input && ! wait && ! force) {
|
||||||
if (has_input || force) {
|
this.turn_based_mode_waiting = true;
|
||||||
// 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(input);
|
|
||||||
this.turn_mode = 1;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We should now be at the start of a tic
|
this.turn_based_mode_waiting = false;
|
||||||
this.level.begin_tic(input);
|
if (use_frames) {
|
||||||
if (this.turn_mode > 0 && this.level.can_accept_input() && ! has_input) {
|
this.level.advance_frame(input);
|
||||||
// If we're in turn-based mode and could provide input here, but don't have any,
|
if (this.level.frame_offset === 0) {
|
||||||
// then wait until we do
|
crossed_tic_boundary = true;
|
||||||
this.turn_mode = 2;
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.level.finish_tic(input);
|
this.level.advance_tic(input);
|
||||||
|
crossed_tic_boundary = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME don't do this til we would next advance? or some other way let it play out
|
||||||
if (this.level.state !== 'playing') {
|
if (this.level.state !== 'playing') {
|
||||||
// We either won or lost!
|
// We either won or lost!
|
||||||
this.set_state('stopped');
|
this.set_state('stopped');
|
||||||
@ -1455,6 +1442,10 @@ class Player extends PrimaryView {
|
|||||||
// tracking fractional updates, but asking to run at 10× and only getting 2× would suck)
|
// tracking fractional updates, but asking to run at 10× and only getting 2× would suck)
|
||||||
let num_advances = 1;
|
let num_advances = 1;
|
||||||
let dt = 1000 / (TICS_PER_SECOND * this.play_speed);
|
let dt = 1000 / (TICS_PER_SECOND * this.play_speed);
|
||||||
|
let use_frames = this.level.compat.emulate_60fps && this.state === 'playing';
|
||||||
|
if (use_frames) {
|
||||||
|
dt /= 3;
|
||||||
|
}
|
||||||
if (dt < 10) {
|
if (dt < 10) {
|
||||||
num_advances = Math.ceil(10 / dt);
|
num_advances = Math.ceil(10 / dt);
|
||||||
dt = 10;
|
dt = 10;
|
||||||
@ -1469,7 +1460,7 @@ class Player extends PrimaryView {
|
|||||||
this._advance_handle = window.setTimeout(this._advance_bound, dt);
|
this._advance_handle = window.setTimeout(this._advance_bound, dt);
|
||||||
|
|
||||||
if (this.state === 'playing') {
|
if (this.state === 'playing') {
|
||||||
this.advance_by(num_advances);
|
this.advance_by(num_advances, false, use_frames);
|
||||||
}
|
}
|
||||||
else if (this.state === 'rewinding') {
|
else if (this.state === 'rewinding') {
|
||||||
if (this.level.has_undo()) {
|
if (this.level.has_undo()) {
|
||||||
@ -1488,10 +1479,6 @@ class Player extends PrimaryView {
|
|||||||
|
|
||||||
undo() {
|
undo() {
|
||||||
this.level.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
|
// Redraws every frame, unless the game isn't running
|
||||||
@ -1500,29 +1487,25 @@ class Player extends PrimaryView {
|
|||||||
// TODO i'm not sure it'll be right when rewinding either
|
// TODO i'm not sure it'll be right when rewinding either
|
||||||
// TODO or if the game's speed changes. wow!
|
// TODO or if the game's speed changes. wow!
|
||||||
let tic_offset;
|
let tic_offset;
|
||||||
if (this.turn_mode === 2) {
|
if (this.turn_based_mode_waiting || this.state === 'stopped' || ! this.use_interpolation) {
|
||||||
// We're dawdling between tics, so nothing is actually animating, but the clock hasn't
|
// We're dawdling between tics, so nothing is actually animating, but the clock hasn't
|
||||||
// advanced yet; pretend whatever's currently animating has finished
|
// advanced yet; pretend whatever's currently animating has finished
|
||||||
// FIXME this creates bizarre side effects like actors making a huge first step when
|
// FIXME this creates bizarre side effects like actors making a huge first step when
|
||||||
// stepping forwards one tic at a time, but without it you get force floors animating
|
// stepping forwards one tic at a time, but without it you get force floors animating
|
||||||
// and then abruptly reversing in turn-based mode (maybe we should just not interpolate
|
// and then abruptly reversing in turn-based mode (maybe we should just not interpolate
|
||||||
// at all in that case??)
|
// at all in that case??)
|
||||||
tic_offset = 0.999;
|
|
||||||
}
|
|
||||||
else if (this.state === 'stopped') {
|
|
||||||
// Once the game is over, interpolating backwards makes less sense
|
// Once the game is over, interpolating backwards makes less sense
|
||||||
// FIXME this /appears/ to skip a whole tic of movement though. hm.
|
// FIXME this /appears/ to skip a whole tic of movement though. hm.
|
||||||
tic_offset = 0.999;
|
tic_offset = this.level.compat.emulate_60fps ? 0.333 : 0.999;
|
||||||
}
|
}
|
||||||
else if (this.use_interpolation) {
|
else {
|
||||||
|
// Note that, conveniently, when running at 60 FPS this ranges from 0 to 1/3, so nothing
|
||||||
|
// actually needs to change
|
||||||
tic_offset = Math.min(0.9999, (performance.now() - this.last_advance) / 1000 * TICS_PER_SECOND * this.play_speed);
|
tic_offset = Math.min(0.9999, (performance.now() - this.last_advance) / 1000 * TICS_PER_SECOND * this.play_speed);
|
||||||
if (this.state === 'rewinding') {
|
if (this.state === 'rewinding') {
|
||||||
tic_offset = 1 - tic_offset;
|
tic_offset = 1 - tic_offset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
tic_offset = 0.999;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._redraw(tic_offset);
|
this._redraw(tic_offset);
|
||||||
|
|
||||||
@ -1661,12 +1644,14 @@ class Player extends PrimaryView {
|
|||||||
|
|
||||||
if (this.debug.enabled) {
|
if (this.debug.enabled) {
|
||||||
let t = this.level.tic_counter;
|
let t = this.level.tic_counter;
|
||||||
if (this.turn_mode === 2) {
|
let current_tic = String(t);
|
||||||
this.debug.time_tics_el.textContent = `${t}½`;
|
if (this.level.frame_offset === 1) {
|
||||||
|
current_tic += "⅓";
|
||||||
}
|
}
|
||||||
else {
|
else if (this.level.frame_offset === 2) {
|
||||||
this.debug.time_tics_el.textContent = `${t}`;
|
current_tic += "⅔";
|
||||||
}
|
}
|
||||||
|
this.debug.time_tics_el.textContent = current_tic;
|
||||||
this.debug.time_moves_el.textContent = `${Math.floor(t/4)}`;
|
this.debug.time_moves_el.textContent = `${Math.floor(t/4)}`;
|
||||||
this.debug.time_secs_el.textContent = (t / 20).toFixed(2);
|
this.debug.time_secs_el.textContent = (t / 20).toFixed(2);
|
||||||
|
|
||||||
@ -1687,10 +1672,9 @@ class Player extends PrimaryView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
autopause() {
|
autopause() {
|
||||||
if (this.turn_mode > 0) {
|
// Turn-based mode doesn't need this
|
||||||
// Turn-based mode doesn't need this
|
if (this.turn_based_mode)
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
this.set_state('paused');
|
this.set_state('paused');
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user