[WIP] Switch to a more accurate frame-based model
This seems to match how CC2 actually works, and it fixes the replays for the CC1 levels BLOCK BUSTER, THE PRISONER, CATACOMBS, and GOLDKEY. Unfortunately, it also regresses ON THE ROCKS, GRAIL, and ALPHABET SOUP, and I do not know why. I'm not even sure why it fixes CATACOMBS and GOLDKEY. It makes a mess of force floor handling with mixed results. It's also some 40% slower, which is not ideal. I doubt I'll revisit this particular branch, since it's the sort of mass code rearrangement that breaks everything, but I'm keeping it for future reference in case I try to revisit this idea.
This commit is contained in:
parent
eec7ab2e1a
commit
86764612d3
508
js/game.js
508
js/game.js
@ -557,23 +557,91 @@ export class Level {
|
||||
this.pending_undo.level_props[key] = this[key];
|
||||
}
|
||||
|
||||
this.p1_input = p1_input;
|
||||
|
||||
// Used for various tic-local effects; don't need to be undoable
|
||||
// TODO maybe this should be undone anyway so rewind looks better?
|
||||
this.player.is_blocked = false;
|
||||
|
||||
this.sfx.set_player_position(this.player.cell);
|
||||
|
||||
// CC2 wiring runs every frame, not every tic, so we need to do it three times, but dealing
|
||||
// with it is delicate. We want the result of a button press to draw, but not last longer
|
||||
// than intended, so we only want one update between the end of the cooldown pass and the
|
||||
// end of the tic. That means the other two have to go here. When a level starts, there
|
||||
// are only two wiring updates before everything gets its first chance to move, so we skip
|
||||
// the very first one here.
|
||||
if (this.tic_counter !== 0) {
|
||||
this._do_decision_pass(false);
|
||||
this._do_action_pass();
|
||||
this._do_cooldown_pass();
|
||||
this.update_wiring();
|
||||
this._do_decision_pass(false);
|
||||
this._do_action_pass();
|
||||
this._do_cooldown_pass();
|
||||
this.update_wiring();
|
||||
}
|
||||
|
||||
finish_tic(p1_input) {
|
||||
this.p1_input = p1_input;
|
||||
this._do_decision_pass(true);
|
||||
|
||||
// In the event that the player is sliding (and thus not deliberately moving) or has
|
||||
// stopped, remember their current movement direction here, too.
|
||||
// This is hokey, and doing it here is even hokier, but it seems to match CC2 behavior.
|
||||
if (this.player.movement_cooldown > 0) {
|
||||
this.remember_player_move(this.player.direction);
|
||||
}
|
||||
|
||||
this._do_action_pass();
|
||||
this._do_cooldown_pass();
|
||||
this.update_wiring();
|
||||
|
||||
// Strip out any destroyed actors from the acting order
|
||||
// FIXME this is O(n), where n is /usually/ small, but i still don't love it. not strictly
|
||||
// necessary, either; maybe only do it every few tics?
|
||||
let p = 0;
|
||||
for (let i = 0, l = this.actors.length; i < l; i++) {
|
||||
let actor = this.actors[i];
|
||||
// While we're here, delete this guy
|
||||
delete actor.cooldown_delay_hack;
|
||||
|
||||
if (actor.cell) {
|
||||
if (p !== i) {
|
||||
this.actors[p] = actor;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
else {
|
||||
let local_p = p;
|
||||
this.pending_undo.push(() => this.actors.splice(local_p, 0, actor));
|
||||
}
|
||||
}
|
||||
this.actors.length = p;
|
||||
|
||||
// Possibly switch players
|
||||
// FIXME not correct
|
||||
/*
|
||||
if (swap_player1) {
|
||||
this.player_index += 1;
|
||||
this.player_index %= this.players.length;
|
||||
this.player = this.players[this.player_index];
|
||||
}
|
||||
*/
|
||||
|
||||
this.previous_input = this.p1_input;
|
||||
|
||||
// Advance the clock
|
||||
// TODO i suspect cc2 does this at the beginning of the tic, but even if you've won? if you
|
||||
// step on a penalty + exit you win, but you see the clock flicker 1 for a single frame
|
||||
this.tic_counter += 1;
|
||||
if (this.time_remaining !== null && ! this.timer_paused) {
|
||||
this.time_remaining -= 1;
|
||||
if (this.time_remaining <= 0) {
|
||||
this.fail('time');
|
||||
}
|
||||
else if (this.time_remaining % 20 === 0 && this.time_remaining < 30 * 20) {
|
||||
this.sfx.play_once('tick');
|
||||
}
|
||||
}
|
||||
|
||||
this.commit();
|
||||
}
|
||||
|
||||
_do_cooldown_pass() {
|
||||
// FIRST PASS: actors tick their cooldowns, finish their movement, and possibly step on
|
||||
// cells they were moving into. This has a few advantages: it makes rendering interpolation
|
||||
// much easier, and doing it as a separate pass from /starting/ movement (unlike Lynx)
|
||||
@ -631,146 +699,9 @@ export class Level {
|
||||
continue;
|
||||
|
||||
if (actor.just_stepped_on_teleporter) {
|
||||
this.attempt_teleport(actor, actor === this.player ? p1_input : null);
|
||||
this.attempt_teleport(actor, actor === this.player ? this.p1_input : null);
|
||||
}
|
||||
}
|
||||
|
||||
// Here's the third.
|
||||
this.update_wiring();
|
||||
}
|
||||
|
||||
finish_tic(p1_input) {
|
||||
// SECOND PASS: actors decide their upcoming movement simultaneously
|
||||
for (let i = this.actors.length - 1; i >= 0; i--) {
|
||||
let actor = this.actors[i];
|
||||
|
||||
// Clear any old decisions ASAP. Note that this prop is only used internally within a
|
||||
// single tic, so it doesn't need to be undoable
|
||||
actor.decision = null;
|
||||
|
||||
if (! actor.cell)
|
||||
continue;
|
||||
|
||||
if (actor.movement_cooldown > 0)
|
||||
continue;
|
||||
|
||||
if (actor === this.player) {
|
||||
this.make_player_decision(actor, p1_input);
|
||||
}
|
||||
else {
|
||||
this.make_actor_decision(actor);
|
||||
}
|
||||
}
|
||||
|
||||
// THIRD PASS: everyone actually moves
|
||||
let swap_player1 = false;
|
||||
for (let i = this.actors.length - 1; i >= 0; i--) {
|
||||
let actor = this.actors[i];
|
||||
if (! actor.cell)
|
||||
continue;
|
||||
|
||||
if (actor.type.on_tic) {
|
||||
actor.type.on_tic(actor, this);
|
||||
}
|
||||
|
||||
// Check this again, since an earlier pass may have caused us to start moving
|
||||
if (actor.movement_cooldown > 0)
|
||||
continue;
|
||||
|
||||
// Check for special player actions, which can only happen when not moving
|
||||
// FIXME if you press a key while moving it should happen as soon as you stop (assuming
|
||||
// the key is still held down)
|
||||
// FIXME cc2 seems to rely on key repeat for this; the above is true, but also, if you
|
||||
// have four bowling balls and hold Q, you'll throw the first, wait a second or so, then
|
||||
// release the rest rapid-fire. absurd
|
||||
if (actor === this.player) {
|
||||
let new_input = p1_input & ~this.previous_input;
|
||||
if (new_input & INPUT_BITS.cycle) {
|
||||
this.cycle_inventory(this.player);
|
||||
}
|
||||
if (new_input & INPUT_BITS.drop) {
|
||||
this.drop_item(this.player);
|
||||
}
|
||||
if (new_input & INPUT_BITS.swap) {
|
||||
// This is delayed until the end of the tic to avoid screwing up anything
|
||||
// checking this.player
|
||||
swap_player1 = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (! actor.decision)
|
||||
continue;
|
||||
|
||||
// Actor is allowed to move, so do so
|
||||
let old_cell = actor.cell;
|
||||
let success = this.attempt_step(actor, actor.decision);
|
||||
|
||||
if (! success && actor.slide_mode === 'ice') {
|
||||
this._handle_slide_bonk(actor);
|
||||
success = this.attempt_step(actor, actor.decision);
|
||||
}
|
||||
|
||||
// Track whether the player is blocked, for visual effect
|
||||
if (actor === this.player && actor.decision && ! success) {
|
||||
this.sfx.play_once('blocked');
|
||||
actor.is_blocked = true;
|
||||
}
|
||||
}
|
||||
|
||||
// In the event that the player is sliding (and thus not deliberately moving) or has
|
||||
// stopped, remember their current movement direction here, too.
|
||||
// This is hokey, and doing it here is even hokier, but it seems to match CC2 behavior.
|
||||
if (this.player.movement_cooldown > 0) {
|
||||
this.remember_player_move(this.player.direction);
|
||||
}
|
||||
|
||||
// Strip out any destroyed actors from the acting order
|
||||
// FIXME this is O(n), where n is /usually/ small, but i still don't love it. not strictly
|
||||
// necessary, either; maybe only do it every few tics?
|
||||
let p = 0;
|
||||
for (let i = 0, l = this.actors.length; i < l; i++) {
|
||||
let actor = this.actors[i];
|
||||
// While we're here, delete this guy
|
||||
delete actor.cooldown_delay_hack;
|
||||
|
||||
if (actor.cell) {
|
||||
if (p !== i) {
|
||||
this.actors[p] = actor;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
else {
|
||||
let local_p = p;
|
||||
this.pending_undo.push(() => this.actors.splice(local_p, 0, actor));
|
||||
}
|
||||
}
|
||||
this.actors.length = p;
|
||||
|
||||
// Possibly switch players
|
||||
// FIXME not correct
|
||||
if (swap_player1) {
|
||||
this.player_index += 1;
|
||||
this.player_index %= this.players.length;
|
||||
this.player = this.players[this.player_index];
|
||||
}
|
||||
|
||||
this.previous_input = p1_input;
|
||||
|
||||
// Advance the clock
|
||||
// TODO i suspect cc2 does this at the beginning of the tic, but even if you've won? if you
|
||||
// step on a penalty + exit you win, but you see the clock flicker 1 for a single frame
|
||||
this.tic_counter += 1;
|
||||
if (this.time_remaining !== null && ! this.timer_paused) {
|
||||
this.time_remaining -= 1;
|
||||
if (this.time_remaining <= 0) {
|
||||
this.fail('time');
|
||||
}
|
||||
else if (this.time_remaining % 20 === 0 && this.time_remaining < 30 * 20) {
|
||||
this.sfx.play_once('tick');
|
||||
}
|
||||
}
|
||||
|
||||
this.commit();
|
||||
}
|
||||
|
||||
_extract_player_directions(input) {
|
||||
@ -834,6 +765,15 @@ export class Level {
|
||||
|
||||
if (actor.slide_mode === 'force') {
|
||||
this._set_tile_prop(actor, 'last_move_was_force', true);
|
||||
if (! try_direction(actor.direction, 'move') && actor.cell.get_terrain().type.name === 'force_floor_all') {
|
||||
for (let i = 0; i < 3; i++) {
|
||||
let direction = this.get_force_floor_direction();
|
||||
if (try_direction(direction, 'move') || i === 2) {
|
||||
actor.decision = direction;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (actor.slide_mode === 'ice') {
|
||||
// A sliding player that bonks into a wall still needs to turn around, but in this
|
||||
@ -901,11 +841,23 @@ export class Level {
|
||||
// If we're overriding a force floor but the direction we're moving in is blocked, the
|
||||
// force floor takes priority (and we've already bumped the wall(s))
|
||||
if (actor.slide_mode === 'force' && ! open) {
|
||||
actor.decision = actor.direction;
|
||||
this._set_tile_prop(actor, 'last_move_was_force', true);
|
||||
// Be sure to invoke this hack if we're standing on an RFF
|
||||
|
||||
if (actor.cell.get_terrain().type.name === 'force_floor_all') {
|
||||
for (let i = 0; i < 3; i++) {
|
||||
let direction = this.get_force_floor_direction();
|
||||
if (try_direction(direction, 'move') || i === 2) {
|
||||
actor.decision = direction;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
actor.decision = actor.direction;
|
||||
this._handle_slide_bonk(actor);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Otherwise this is 100% a conscious move so we lose our override power next tic
|
||||
this._set_tile_prop(actor, 'last_move_was_force', false);
|
||||
@ -978,6 +930,262 @@ export class Level {
|
||||
}
|
||||
}
|
||||
|
||||
_do_decision_pass(tic_aligned) {
|
||||
// SECOND PASS: actors decide their upcoming movement simultaneously
|
||||
for (let i = this.actors.length - 1; i >= 0; i--) {
|
||||
let actor = this.actors[i];
|
||||
|
||||
// Clear any old decisions ASAP. Note that this prop is only used internally within a
|
||||
// single tic, so it doesn't need to be undoable
|
||||
actor.decision = null;
|
||||
|
||||
if (! actor.cell)
|
||||
continue;
|
||||
|
||||
if (actor.movement_cooldown > 0)
|
||||
continue;
|
||||
|
||||
if (! tic_aligned && ! actor.slide_mode)
|
||||
continue;
|
||||
|
||||
if (actor.slide_mode && actor.cell.get_terrain().type.name === 'force_floor_all') {
|
||||
this.set_actor_direction(actor, this.get_force_floor_direction());
|
||||
}
|
||||
if (actor === this.player) {
|
||||
this.make_player_decision(actor, this.p1_input);
|
||||
}
|
||||
else {
|
||||
this.make_actor_decision(actor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_do_action_pass() {
|
||||
// THIRD PASS: everyone actually moves
|
||||
let swap_player1 = false;
|
||||
for (let i = this.actors.length - 1; i >= 0; i--) {
|
||||
let actor = this.actors[i];
|
||||
if (! actor.cell)
|
||||
continue;
|
||||
|
||||
if (actor.type.on_tic) {
|
||||
actor.type.on_tic(actor, this);
|
||||
}
|
||||
|
||||
// Check this again, since an earlier pass may have caused us to start moving
|
||||
if (actor.movement_cooldown > 0)
|
||||
continue;
|
||||
|
||||
// Check for special player actions, which can only happen when not moving
|
||||
// FIXME if you press a key while moving it should happen as soon as you stop (assuming
|
||||
// the key is still held down)
|
||||
// FIXME cc2 seems to rely on key repeat for this; the above is true, but also, if you
|
||||
// have four bowling balls and hold Q, you'll throw the first, wait a second or so, then
|
||||
// release the rest rapid-fire. absurd
|
||||
if (actor === this.player) {
|
||||
let new_input = this.p1_input & ~this.previous_input;
|
||||
if (new_input & INPUT_BITS.cycle) {
|
||||
this.cycle_inventory(this.player);
|
||||
}
|
||||
if (new_input & INPUT_BITS.drop) {
|
||||
this.drop_item(this.player);
|
||||
}
|
||||
if (new_input & INPUT_BITS.swap) {
|
||||
// This is delayed until the end of the tic to avoid screwing up anything
|
||||
// checking this.player
|
||||
swap_player1 = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (! actor.decision)
|
||||
continue;
|
||||
|
||||
// Actor is allowed to move, so do so
|
||||
let old_cell = actor.cell;
|
||||
let success = this.attempt_step(actor, actor.decision);
|
||||
|
||||
if (! success && actor.slide_mode === 'ice') {
|
||||
this._handle_slide_bonk(actor);
|
||||
success = this.attempt_step(actor, actor.decision);
|
||||
}
|
||||
|
||||
// Track whether the player is blocked, for visual effect
|
||||
if (actor === this.player && actor.decision && ! success) {
|
||||
this.sfx.play_once('blocked');
|
||||
actor.is_blocked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_do_cooldown_pass() {
|
||||
// FIRST PASS: actors tick their cooldowns, finish their movement, and possibly step on
|
||||
// cells they were moving into. This has a few advantages: it makes rendering interpolation
|
||||
// much easier, and doing it as a separate pass from /starting/ movement (unlike Lynx)
|
||||
// improves the illusion that everything is happening simultaneously.
|
||||
// Note that, as far as I can tell, CC2 actually runs this pass every /frame/. We do not!
|
||||
// Also Note that we iterate in reverse order, DESPITE keeping dead actors around with null
|
||||
// cells, to match the Lynx and CC2 behavior. This is actually important in some cases;
|
||||
// check out the start of CCLP3 #54, where the gliders will eat the blue key immediately if
|
||||
// they act in forward order! (More subtly, even the decision pass does things like
|
||||
// advance the RNG, so for replay compatibility it needs to be in reverse order too.)
|
||||
for (let i = this.actors.length - 1; i >= 0; i--) {
|
||||
let actor = this.actors[i];
|
||||
// Actors with no cell were destroyed
|
||||
if (! actor.cell)
|
||||
continue;
|
||||
|
||||
if (actor.movement_cooldown <= 0)
|
||||
continue;
|
||||
|
||||
if (actor.cooldown_delay_hack) {
|
||||
// See the extensive comment in attempt_out_of_turn_step
|
||||
actor.cooldown_delay_hack += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
this._set_tile_prop(actor, 'movement_cooldown', Math.max(0, actor.movement_cooldown - 1));
|
||||
|
||||
if (actor.movement_cooldown <= 0) {
|
||||
if (actor.type.ttl) {
|
||||
// This is an animation that just finished, so destroy it
|
||||
this.remove_tile(actor);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! this.compat.tiles_react_instantly) {
|
||||
this.step_on_cell(actor, actor.cell);
|
||||
}
|
||||
// Erase any trace of being in mid-movement, however:
|
||||
// - This has to happen after stepping on cells, because some effects care about
|
||||
// the cell we're arriving from
|
||||
// - Don't do it if stepping on the cell caused us to move again
|
||||
if (actor.movement_cooldown <= 0) {
|
||||
this._set_tile_prop(actor, 'previous_cell', null);
|
||||
this._set_tile_prop(actor, 'movement_speed', null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mini extra pass: deal with teleporting separately. Otherwise, actors may have been in
|
||||
// the way of the teleporter but finished moving away during the above loop; this is
|
||||
// particularly bad when it happens with a block you're pushing.
|
||||
for (let i = this.actors.length - 1; i >= 0; i--) {
|
||||
let actor = this.actors[i];
|
||||
if (! actor.cell)
|
||||
continue;
|
||||
|
||||
if (actor.just_stepped_on_teleporter) {
|
||||
this.attempt_teleport(actor, actor === this.player ? this.p1_input : null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_do_turn_phase(allow_decision) {
|
||||
for (let i = this.actors.length - 1; i >= 0; i--) {
|
||||
let actor = this.actors[i];
|
||||
|
||||
// Clear any old decisions ASAP. Note that this prop is only used internally within a
|
||||
// single tic, so it doesn't need to be undoable
|
||||
actor.decision = null;
|
||||
|
||||
if (! actor.cell)
|
||||
continue;
|
||||
|
||||
if (actor.movement_cooldown <= 0 && (allow_decision || actor.slide_mode)) {
|
||||
if (actor === this.player) {
|
||||
this.make_player_decision(actor, this.p1_input);
|
||||
}
|
||||
else {
|
||||
this.make_actor_decision(actor);
|
||||
}
|
||||
}
|
||||
|
||||
this.take_actor_turn(actor);
|
||||
}
|
||||
}
|
||||
|
||||
take_actor_turn(actor) {
|
||||
let success = false;
|
||||
|
||||
if (! actor.cell)
|
||||
return;
|
||||
|
||||
// FIXME obviously this becomes on_frame
|
||||
if (actor.type.on_tic) {
|
||||
actor.type.on_tic(actor, this);
|
||||
}
|
||||
|
||||
// Check this again, since an earlier pass may have caused us to start moving
|
||||
if (actor.movement_cooldown <= 0) {
|
||||
|
||||
// Check for special player actions, which can only happen when not moving
|
||||
// FIXME if you press a key while moving it should happen as soon as you stop (assuming
|
||||
// the key is still held down)
|
||||
// FIXME cc2 seems to rely on key repeat for this; the above is true, but also, if you
|
||||
// have four bowling balls and hold Q, you'll throw the first, wait a second or so, then
|
||||
// release the rest rapid-fire. absurd
|
||||
if (actor === this.player) {
|
||||
let new_input = this.p1_input & ~this.previous_input;
|
||||
if (new_input & INPUT_BITS.cycle) {
|
||||
this.cycle_inventory(this.player);
|
||||
}
|
||||
if (new_input & INPUT_BITS.drop) {
|
||||
this.drop_item(this.player);
|
||||
}
|
||||
if (new_input & INPUT_BITS.swap) {
|
||||
// This is delayed until the end of the tic to avoid screwing up anything
|
||||
// checking this.player
|
||||
swap_player1 = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (actor.decision) {
|
||||
// Actor is allowed to move, so do so
|
||||
let old_cell = actor.cell;
|
||||
success = this.attempt_step(actor, actor.decision);
|
||||
|
||||
if (! success && actor.slide_mode === 'ice') {
|
||||
this._handle_slide_bonk(actor);
|
||||
success = this.attempt_step(actor, actor.decision);
|
||||
}
|
||||
|
||||
// Track whether the player is blocked, for visual effect
|
||||
if (actor === this.player && actor.decision && ! success) {
|
||||
this.sfx.play_once('blocked');
|
||||
actor.is_blocked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (actor.movement_cooldown > 0) {
|
||||
|
||||
this._set_tile_prop(actor, 'movement_cooldown', Math.max(0, actor.movement_cooldown - 1));
|
||||
|
||||
if (actor.movement_cooldown <= 0) {
|
||||
if (actor.type.ttl) {
|
||||
// This is an animation that just finished, so destroy it
|
||||
this.remove_tile(actor);
|
||||
return;
|
||||
}
|
||||
|
||||
if (! this.compat.tiles_react_instantly) {
|
||||
this.step_on_cell(actor, actor.cell);
|
||||
}
|
||||
// Erase any trace of being in mid-movement, however:
|
||||
// - This has to happen after stepping on cells, because some effects care about
|
||||
// the cell we're arriving from
|
||||
// - Don't do it if stepping on the cell caused us to move again
|
||||
if (actor.movement_cooldown <= 0) {
|
||||
this._set_tile_prop(actor, 'previous_cell', null);
|
||||
this._set_tile_prop(actor, 'movement_speed', null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
// FIXME can probably clean this up a decent bit now
|
||||
_handle_slide_bonk(actor) {
|
||||
if (actor.slide_mode === 'ice') {
|
||||
@ -1119,14 +1327,16 @@ export class Level {
|
||||
}
|
||||
|
||||
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._set_tile_prop(actor, 'movement_cooldown', speed * 3);
|
||||
this._set_tile_prop(actor, 'movement_speed', speed * 3);
|
||||
this.move_to(actor, goal_cell, speed);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
attempt_out_of_turn_step(actor, direction) {
|
||||
actor.decision = direction;
|
||||
return this.take_actor_turn(actor);
|
||||
if (this.attempt_step(actor, direction)) {
|
||||
// Here's the problem.
|
||||
// In CC2, cooldown is measured in frames, not tics, and it decrements every frame, not
|
||||
@ -1149,7 +1359,7 @@ export class Level {
|
||||
// XXX that's still not perfect; if actor X is tic-misaligned, like if there's a chain
|
||||
// of 3 or more actors cloning directly onto red buttons for other cloners, then this
|
||||
// cannot possibly work
|
||||
actor.cooldown_delay_hack = 1;
|
||||
//actor.cooldown_delay_hack = 1;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
|
||||
@ -781,7 +781,7 @@ const TILE_TYPES = {
|
||||
// TODO ms: this is random, and an acting wall to monsters (!)
|
||||
// TODO lynx/cc2 check this at decision time, which may affect ordering
|
||||
on_arrive(me, level, other) {
|
||||
level.set_actor_direction(other, level.get_force_floor_direction());
|
||||
//level.set_actor_direction(other, level.get_force_floor_direction());
|
||||
},
|
||||
},
|
||||
slime: {
|
||||
@ -2496,7 +2496,7 @@ const TILE_TYPES = {
|
||||
is_actor: true,
|
||||
collision_mask: 0,
|
||||
blocks_collision: COLLISION.player,
|
||||
ttl: 6,
|
||||
ttl: 16,
|
||||
// If anything else even begins to step on an animation, it's erased
|
||||
// FIXME possibly erased too fast; cc2 shows it briefly? could i get away with on_arrive here?
|
||||
on_approach(me, level, other) {
|
||||
@ -2508,7 +2508,7 @@ const TILE_TYPES = {
|
||||
is_actor: true,
|
||||
collision_mask: 0,
|
||||
blocks_collision: COLLISION.player,
|
||||
ttl: 6,
|
||||
ttl: 16,
|
||||
on_approach(me, level, other) {
|
||||
level.remove_tile(me);
|
||||
},
|
||||
@ -2520,7 +2520,7 @@ const TILE_TYPES = {
|
||||
collision_mask: 0,
|
||||
blocks_collision: 0,
|
||||
// determined experimentally
|
||||
ttl: 12,
|
||||
ttl: 36,
|
||||
},
|
||||
// Custom VFX (identical function, but different aesthetic)
|
||||
splash_slime: {
|
||||
@ -2528,7 +2528,7 @@ const TILE_TYPES = {
|
||||
is_actor: true,
|
||||
collision_mask: 0,
|
||||
blocks_collision: COLLISION.player,
|
||||
ttl: 6,
|
||||
ttl: 16,
|
||||
on_approach(me, level, other) {
|
||||
level.remove_tile(me);
|
||||
},
|
||||
|
||||
Loading…
Reference in New Issue
Block a user