Remove animation_{progress,speed} and fix interpolation
This commit is contained in:
parent
5572b3e692
commit
2f130861d6
76
js/game.js
76
js/game.js
@ -32,11 +32,16 @@ export class Tile {
|
|||||||
visual_position(tic_offset = 0) {
|
visual_position(tic_offset = 0) {
|
||||||
let x = this.cell.x;
|
let x = this.cell.x;
|
||||||
let y = this.cell.y;
|
let y = this.cell.y;
|
||||||
if (! this.previous_cell || ! this.animation_speed) {
|
if (! this.previous_cell || this.movement_speed === null) {
|
||||||
return [x, y];
|
return [x, y];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
let p = (this.animation_progress + tic_offset) / this.animation_speed;
|
// For a movement speed of N, the cooldown is set to N during the tic an actor starts
|
||||||
|
// moving, then advanced to N - 1 before the tic ends. When movement finishes, the
|
||||||
|
// cooldown becomes 0 at the end of a tic, but the actor doesn't decide to move again
|
||||||
|
// until the start of the next tic. Thus, actors interpolate "back in time" by up to a
|
||||||
|
// full tic; if the cooldown is N - 1 then they interpolate from N to N - 1, and so on.
|
||||||
|
let p = ((this.movement_speed - 1 - this.movement_cooldown) + tic_offset) / this.movement_speed;
|
||||||
return [
|
return [
|
||||||
(1 - p) * this.previous_cell.x + p * x,
|
(1 - p) * this.previous_cell.x + p * x,
|
||||||
(1 - p) * this.previous_cell.y + p * y,
|
(1 - p) * this.previous_cell.y + p * y,
|
||||||
@ -538,6 +543,15 @@ export class Level {
|
|||||||
if (actor.movement_cooldown > 0)
|
if (actor.movement_cooldown > 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (actor.type.ttl) {
|
||||||
|
// This is purely an animation and it's finished now, so remove it
|
||||||
|
this.remove_tile(actor);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Our cooldown is zero, so erase any trace of being in mid-movement
|
||||||
|
this._set_tile_prop(actor, 'previous_cell', null);
|
||||||
|
this._set_tile_prop(actor, 'movement_speed', null);
|
||||||
|
|
||||||
// Teeth can only move the first 4 of every 8 tics, and mimics only the first 4 of every
|
// Teeth can only move the first 4 of every 8 tics, and mimics only the first 4 of every
|
||||||
// 16, though "first" can be adjusted
|
// 16, though "first" can be adjusted
|
||||||
if (actor.slide_mode === null && actor.type.movement_parity &&
|
if (actor.slide_mode === null && actor.type.movement_parity &&
|
||||||
@ -672,14 +686,6 @@ export class Level {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.take_actor_turn(actor, actor.decision);
|
this.take_actor_turn(actor, actor.decision);
|
||||||
/*
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = this.actors.length - 1; i >= 0; i--) {
|
|
||||||
let actor = this.actors[i];
|
|
||||||
if (! actor.cell)
|
|
||||||
continue;
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.toggle_green_objects) {
|
if (this.toggle_green_objects) {
|
||||||
@ -790,28 +796,14 @@ export class Level {
|
|||||||
|
|
||||||
if (actor.movement_cooldown > 0) {
|
if (actor.movement_cooldown > 0) {
|
||||||
this._set_tile_prop(actor, 'movement_cooldown', actor.movement_cooldown - 1);
|
this._set_tile_prop(actor, 'movement_cooldown', actor.movement_cooldown - 1);
|
||||||
}
|
if (actor.movement_cooldown <= 0) {
|
||||||
|
// Note that we don't clear movement_speed and previous_cell (or remove animations)
|
||||||
if (actor.animation_speed) {
|
// until the start of the next tic, because animation interpolation still needs to
|
||||||
// Deal with movement animation
|
// know that we're finishing animating here, AND a couple effects (like blobs
|
||||||
// FIXME this is super hokey and was introduced because blocks didn't have a movement
|
// spreading slime) need to know about the previous cell in on_arrive
|
||||||
// cooldown, but it turns out they do actually, so, delete me?
|
if (! actor.type.ttl && ! this.compat.tiles_react_instantly) {
|
||||||
this._set_tile_prop(actor, 'animation_progress', actor.animation_progress + 1);
|
|
||||||
if (actor.animation_progress >= actor.animation_speed) {
|
|
||||||
if (actor.type.ttl) {
|
|
||||||
// This is purely an animation so it disappears once it's played
|
|
||||||
this.remove_tile(actor);
|
|
||||||
console.log(this.tic_counter, 'poof!');
|
|
||||||
return moved;
|
|
||||||
}
|
|
||||||
this._set_tile_prop(actor, 'animation_progress', null);
|
|
||||||
this._set_tile_prop(actor, 'animation_speed', null);
|
|
||||||
// Step on the cell before erasing the previous one, for behavior like blobs
|
|
||||||
// spreading slime
|
|
||||||
if (! this.compat.tiles_react_instantly) {
|
|
||||||
this.step_on_cell(actor, actor.cell);
|
this.step_on_cell(actor, actor.cell);
|
||||||
}
|
}
|
||||||
this._set_tile_prop(actor, 'previous_cell', null);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -937,10 +929,12 @@ export class Level {
|
|||||||
if (double_speed || actor.has_item('speed_boots')) {
|
if (double_speed || actor.has_item('speed_boots')) {
|
||||||
speed /= 2;
|
speed /= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._set_tile_prop(actor, 'previous_cell', actor.cell);
|
||||||
|
this._set_tile_prop(actor, 'movement_cooldown', speed);
|
||||||
|
this._set_tile_prop(actor, 'movement_speed', speed);
|
||||||
this.move_to(actor, goal_cell, speed);
|
this.move_to(actor, goal_cell, speed);
|
||||||
|
|
||||||
// Set movement cooldown since we just moved
|
|
||||||
this._set_tile_prop(actor, 'movement_cooldown', speed);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -951,10 +945,6 @@ export class Level {
|
|||||||
if (actor.cell === goal_cell)
|
if (actor.cell === goal_cell)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this._set_tile_prop(actor, 'previous_cell', actor.cell);
|
|
||||||
this._set_tile_prop(actor, 'animation_speed', speed);
|
|
||||||
this._set_tile_prop(actor, 'animation_progress', 0);
|
|
||||||
|
|
||||||
let original_cell = actor.cell;
|
let original_cell = actor.cell;
|
||||||
this.remove_tile(actor);
|
this.remove_tile(actor);
|
||||||
this.make_slide(actor, null);
|
this.make_slide(actor, null);
|
||||||
@ -1598,8 +1588,12 @@ export class Level {
|
|||||||
spawn_animation(cell, name) {
|
spawn_animation(cell, name) {
|
||||||
let type = TILE_TYPES[name];
|
let type = TILE_TYPES[name];
|
||||||
let tile = new Tile(type);
|
let tile = new Tile(type);
|
||||||
this._set_tile_prop(tile, 'animation_speed', tile.type.ttl);
|
// Co-opt movement_cooldown/speed for these despite that they aren't moving, since they're
|
||||||
this._set_tile_prop(tile, 'animation_progress', 0);
|
// also used to animate everything else. Decrement the cooldown immediately, to match the
|
||||||
|
// normal actor behavior of decrementing one's own cooldown at the end of one's turn
|
||||||
|
this._set_tile_prop(tile, 'movement_speed', tile.type.ttl);
|
||||||
|
this._set_tile_prop(tile, 'movement_cooldown', tile.type.ttl - 1);
|
||||||
|
this._set_tile_prop(tile, 'forced_turn_tic', this.tic_counter);
|
||||||
cell._add(tile);
|
cell._add(tile);
|
||||||
this.actors.push(tile);
|
this.actors.push(tile);
|
||||||
this.pending_undo.push(() => {
|
this.pending_undo.push(() => {
|
||||||
@ -1618,8 +1612,10 @@ export class Level {
|
|||||||
if (! TILE_TYPES[current].is_actor) {
|
if (! TILE_TYPES[current].is_actor) {
|
||||||
console.warn("Transmuting a non-actor into an animation!");
|
console.warn("Transmuting a non-actor into an animation!");
|
||||||
}
|
}
|
||||||
this._set_tile_prop(tile, 'animation_speed', tile.type.ttl);
|
this._set_tile_prop(tile, 'previous_cell', null);
|
||||||
this._set_tile_prop(tile, 'animation_progress', 0);
|
this._set_tile_prop(tile, 'movement_speed', tile.type.ttl);
|
||||||
|
this._set_tile_prop(tile, 'movement_cooldown', tile.type.ttl - 1);
|
||||||
|
this._set_tile_prop(tile, 'forced_turn_tic', this.tic_counter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -907,7 +907,7 @@ export class Tileset {
|
|||||||
// Deal with animation
|
// Deal with animation
|
||||||
if (coords[0] instanceof Array) {
|
if (coords[0] instanceof Array) {
|
||||||
if (tic !== null) {
|
if (tic !== null) {
|
||||||
if (tile && tile.animation_speed) {
|
if (tile && tile.movement_speed) {
|
||||||
// This tile reports its own animation timing (in tics), so trust that, and just
|
// This tile reports its own animation timing (in tics), so trust that, and just
|
||||||
// use the current tic's fraction.
|
// use the current tic's fraction.
|
||||||
// That said: adjusting animation speed complicates this slightly. Consider the
|
// That said: adjusting animation speed complicates this slightly. Consider the
|
||||||
@ -919,7 +919,7 @@ export class Tileset {
|
|||||||
// first half; if it started on tics 5-8, play the second half. They could get
|
// first half; if it started on tics 5-8, play the second half. They could get
|
||||||
// out of sync if the player hesitates, but no one will notice that, and this
|
// out of sync if the player hesitates, but no one will notice that, and this
|
||||||
// approach minimizes storing extra state.
|
// approach minimizes storing extra state.
|
||||||
let i = (tile.animation_progress + tic % 1) / tile.animation_speed;
|
let i = ((tile.movement_speed - 1 - tile.movement_cooldown) + tic % 1) / tile.movement_speed;
|
||||||
// But do NOT do this for explosions or splashes, which have a fixed duration
|
// But do NOT do this for explosions or splashes, which have a fixed duration
|
||||||
// and only play once
|
// and only play once
|
||||||
if (this.animation_slowdown > 1 && ! tile.type.ttl) {
|
if (this.animation_slowdown > 1 && ! tile.type.ttl) {
|
||||||
@ -927,7 +927,7 @@ export class Tileset {
|
|||||||
// 1/N of it before the game ends (or loops) the animation.
|
// 1/N of it before the game ends (or loops) the animation.
|
||||||
// So increase by [0..N-1] to get it in some other range, then divide by N
|
// So increase by [0..N-1] to get it in some other range, then divide by N
|
||||||
// to scale back down to [0, 1)
|
// to scale back down to [0, 1)
|
||||||
i += Math.floor(tic / tile.animation_speed % this.animation_slowdown);
|
i += Math.floor(tic / tile.movement_speed % this.animation_slowdown);
|
||||||
i /= this.animation_slowdown;
|
i /= this.animation_slowdown;
|
||||||
}
|
}
|
||||||
coords = coords[Math.floor(i * coords.length)];
|
coords = coords[Math.floor(i * coords.length)];
|
||||||
@ -1212,7 +1212,7 @@ export class Tileset {
|
|||||||
// Deal with animation
|
// Deal with animation
|
||||||
if (coords[0] instanceof Array) {
|
if (coords[0] instanceof Array) {
|
||||||
if (tic !== null) {
|
if (tic !== null) {
|
||||||
if (tile && tile.animation_speed) {
|
if (tile && tile.movement_speed) {
|
||||||
// This tile reports its own animation timing (in tics), so trust that, and just
|
// This tile reports its own animation timing (in tics), so trust that, and just
|
||||||
// use the current tic's fraction.
|
// use the current tic's fraction.
|
||||||
// That said: adjusting animation speed complicates this slightly. Consider the
|
// That said: adjusting animation speed complicates this slightly. Consider the
|
||||||
@ -1224,7 +1224,7 @@ export class Tileset {
|
|||||||
// first half; if it started on tics 5-8, play the second half. They could get
|
// first half; if it started on tics 5-8, play the second half. They could get
|
||||||
// out of sync if the player hesitates, but no one will notice that, and this
|
// out of sync if the player hesitates, but no one will notice that, and this
|
||||||
// approach minimizes storing extra state.
|
// approach minimizes storing extra state.
|
||||||
let i = (tile.animation_progress + tic % 1) / tile.animation_speed;
|
let i = ((tile.movement_speed - 1 - tile.movement_cooldown) + tic % 1) / tile.movement_speed;
|
||||||
// But do NOT do this for explosions or splashes, which have a fixed duration
|
// But do NOT do this for explosions or splashes, which have a fixed duration
|
||||||
// and only play once
|
// and only play once
|
||||||
if (this.animation_slowdown > 1 && ! tile.type.ttl) {
|
if (this.animation_slowdown > 1 && ! tile.type.ttl) {
|
||||||
@ -1232,7 +1232,7 @@ export class Tileset {
|
|||||||
// 1/N of it before the game ends (or loops) the animation.
|
// 1/N of it before the game ends (or loops) the animation.
|
||||||
// So increase by [0..N-1] to get it in some other range, then divide by N
|
// So increase by [0..N-1] to get it in some other range, then divide by N
|
||||||
// to scale back down to [0, 1)
|
// to scale back down to [0, 1)
|
||||||
i += Math.floor(tic / tile.animation_speed % this.animation_slowdown);
|
i += Math.floor(tic / tile.movement_speed % this.animation_slowdown);
|
||||||
i /= this.animation_slowdown;
|
i /= this.animation_slowdown;
|
||||||
}
|
}
|
||||||
coords = coords[Math.floor(i * coords.length)];
|
coords = coords[Math.floor(i * coords.length)];
|
||||||
|
|||||||
@ -82,7 +82,7 @@ function player_visual_state(me) {
|
|||||||
else if (me.is_pushing) {
|
else if (me.is_pushing) {
|
||||||
return 'pushing';
|
return 'pushing';
|
||||||
}
|
}
|
||||||
else if (me.animation_speed) {
|
else if (me.movement_speed) {
|
||||||
return 'moving';
|
return 'moving';
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -96,8 +96,8 @@ function pursue_player(me, level) {
|
|||||||
let target_cell = player.cell;
|
let target_cell = player.cell;
|
||||||
// CC2 behavior (not Lynx (TODO compat?)): pursue the cell the player is leaving, if
|
// CC2 behavior (not Lynx (TODO compat?)): pursue the cell the player is leaving, if
|
||||||
// they're still mostly in it
|
// they're still mostly in it
|
||||||
if (player.previous_cell && player.animation_speed &&
|
if (player.previous_cell && player.movement_speed &&
|
||||||
player.animation_progress <= player.animation_speed / 2)
|
player.movement_cooldown >= player.movement_speed / 2)
|
||||||
{
|
{
|
||||||
target_cell = player.previous_cell;
|
target_cell = player.previous_cell;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user