Move teleporter overriding to decision time; treat teleporting as a kind of slide; decouple speed from sliding
This commit is contained in:
parent
adb0c4c869
commit
c7815ba841
106
js/game.js
106
js/game.js
@ -277,7 +277,7 @@ export class Cell extends Array {
|
||||
level._set_tile_prop(actor, 'is_pushing', true);
|
||||
}
|
||||
if (! level.attempt_out_of_turn_step(tile, direction)) {
|
||||
if (tile.slide_mode !== null && tile.movement_cooldown !== 0) {
|
||||
if (tile.slide_mode !== null && tile.movement_cooldown > 0) {
|
||||
// If the push failed and the obstacle is in the middle of a slide,
|
||||
// remember this as the next move it'll make
|
||||
level._set_tile_prop(tile, 'pending_push', direction);
|
||||
@ -1087,7 +1087,10 @@ export class Level extends LevelInterface {
|
||||
// succeed, even if overriding in the same direction we're already moving, that does count
|
||||
// as an override.
|
||||
let terrain = actor.cell.get_terrain();
|
||||
let may_move = ! forced_only && (! actor.slide_mode || (actor.slide_mode === 'force' && actor.last_move_was_force));
|
||||
let may_move = ! forced_only && (
|
||||
! actor.slide_mode ||
|
||||
(actor.slide_mode === 'force' && actor.last_move_was_force) ||
|
||||
(actor.slide_mode === 'teleport' && actor.cell.get_terrain().type.teleport_allow_override));
|
||||
let [dir1, dir2] = this._extract_player_directions(input);
|
||||
|
||||
// Check for special player actions, which can only happen at decision time. Dropping can
|
||||
@ -1181,12 +1184,13 @@ export class Level extends LevelInterface {
|
||||
|
||||
// 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 && ! open) {
|
||||
if (actor.slide_mode === 'force' && ! open) {
|
||||
this._set_tile_prop(actor, 'last_move_was_force', true);
|
||||
actor.decision = actor.direction;
|
||||
}
|
||||
else {
|
||||
// Otherwise this is 100% a conscious move so we lose our override power next tic
|
||||
// TODO how does this interact with teleports
|
||||
this._set_tile_prop(actor, 'last_move_was_force', false);
|
||||
}
|
||||
}
|
||||
@ -1292,9 +1296,10 @@ export class Level extends LevelInterface {
|
||||
}
|
||||
this.set_actor_direction(actor, direction);
|
||||
|
||||
// Record our speed, and halve it below if we're stepping onto a sliding tile
|
||||
// Grab speed /first/, in case the movement or on_blocked turns us into an animation
|
||||
// immediately (and then we won't have a speed!)
|
||||
// FIXME that's a weird case actually since the explosion ends up still moving
|
||||
let speed = actor.type.movement_speed;
|
||||
let double_speed = false;
|
||||
|
||||
let move = DIRECTIONS[direction].movement;
|
||||
if (! this.check_movement(actor, actor.cell, direction, 'push')) {
|
||||
@ -1304,17 +1309,16 @@ export class Level extends LevelInterface {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We're clear! Compute our speed and move us
|
||||
// FIXME this feels clunky
|
||||
let goal_cell = this.get_neighboring_cell(actor.cell, direction);
|
||||
let terrain = goal_cell.get_terrain();
|
||||
if (terrain && terrain.type.slide_mode && ! actor.ignores(terrain.type.name)) {
|
||||
double_speed = true;
|
||||
}
|
||||
|
||||
// We're clear!
|
||||
if (double_speed || actor.has_item('speed_boots')) {
|
||||
if (actor.has_item('speed_boots')) {
|
||||
speed /= 2;
|
||||
}
|
||||
else if (terrain && terrain.type.speed_factor && ! actor.ignores(terrain.type.name)) {
|
||||
speed /= terrain.type.speed_factor;
|
||||
}
|
||||
|
||||
let orig_cell = actor.cell;
|
||||
this._set_tile_prop(actor, 'previous_cell', orig_cell);
|
||||
@ -1418,8 +1422,6 @@ export class Level extends LevelInterface {
|
||||
}
|
||||
|
||||
// Announce we're approaching
|
||||
// Clear the slide here since slide mode is important for knowing our speed
|
||||
this.make_slide(actor, null);
|
||||
for (let tile of Array.from(actor.cell)) {
|
||||
if (tile === actor)
|
||||
continue;
|
||||
@ -1445,9 +1447,6 @@ export class Level extends LevelInterface {
|
||||
if (tile.type.on_approach) {
|
||||
tile.type.on_approach(tile, this, actor);
|
||||
}
|
||||
if (tile.type.slide_mode) {
|
||||
this.make_slide(actor, tile.type.slide_mode);
|
||||
}
|
||||
}
|
||||
|
||||
// If we're a monster stepping on the player's tail, that also kills her immediately; the
|
||||
@ -1508,11 +1507,10 @@ export class Level extends LevelInterface {
|
||||
}
|
||||
}
|
||||
|
||||
attempt_teleport(actor, input) {
|
||||
attempt_teleport(actor) {
|
||||
let teleporter = actor.just_stepped_on_teleporter;
|
||||
actor.just_stepped_on_teleporter = null;
|
||||
delete actor.just_stepped_on_teleporter;
|
||||
|
||||
let push_mode = actor === this.player ? 'push' : 'bump';
|
||||
let original_direction = actor.direction;
|
||||
let success = false;
|
||||
let dest, direction;
|
||||
@ -1528,7 +1526,15 @@ export class Level extends LevelInterface {
|
||||
if (dest === teleporter && teleporter.type.name === 'teleport_yellow') {
|
||||
break;
|
||||
}
|
||||
if (this.check_movement(actor, dest.cell, direction, push_mode)) {
|
||||
// Note that this uses 'bump' even for players; it would be very bad if we could
|
||||
// initiate movement in this pass (in Lexy rules, anyway), because we might try to push
|
||||
// something that's still waiting to teleport itself!
|
||||
// XXX is this correct? it does mean you won't try to teleport to a teleporter that's
|
||||
// "blocked" by a block that won't be there anyway by the time you try to move, but that
|
||||
// seems very obscure and i haven't run into a case with it yet. offhand i don't think
|
||||
// it can even come up under cc2 rules, since teleporting is done after an actor cools
|
||||
// down and before the next actor even gets a chance to act
|
||||
if (this.check_movement(actor, dest.cell, direction, 'bump')) {
|
||||
success = true;
|
||||
// Sound plays from the origin cell simply because that's where the sfx player
|
||||
// thinks the player is currently; position isn't updated til next turn
|
||||
@ -1538,59 +1544,22 @@ export class Level extends LevelInterface {
|
||||
}
|
||||
|
||||
if (success) {
|
||||
if (teleporter.type.teleport_allow_override && actor === this.player) {
|
||||
// Red and yellow teleporters allow players to override the exit direction. This
|
||||
// can only happen after we've found a suitable destination. As with normal player
|
||||
// decisions, we aggressively check each direction first (meaning we might bump the
|
||||
// same cell twice here!), and then figure out what to do afterwards.
|
||||
// Note that it's possible to bump a direction multiple times during this process,
|
||||
// and also possible to perform a three-way block slap: the direction she leaves,
|
||||
// the other direction she was holding, and the original exit direction we found.
|
||||
let [dir1, dir2] = this._extract_player_directions(this.p1_input);
|
||||
let open1 = false, open2 = false;
|
||||
if (dir1) {
|
||||
open1 = this.check_movement(actor, dest.cell, dir1, push_mode);
|
||||
}
|
||||
if (dir2) {
|
||||
open2 = this.check_movement(actor, dest.cell, dir2, push_mode);
|
||||
}
|
||||
this.set_actor_direction(actor, direction);
|
||||
this.make_slide(actor, 'teleport');
|
||||
|
||||
// If the player didn't even try to override, do nothing
|
||||
if (! dir1 && ! dir2) {
|
||||
}
|
||||
// If only one direction is available, whether because she only held one direction
|
||||
// or because one of them was blocked, use that one
|
||||
else if ((open1 && ! open2) || (dir1 && ! dir2)) {
|
||||
direction = dir1;
|
||||
}
|
||||
else if (! open1 && open2) {
|
||||
direction = dir2;
|
||||
}
|
||||
// Otherwise, we have a tie. If either direction is the exit we found (which
|
||||
// can only happen if both are open), prefer that one...
|
||||
else if (dir1 === direction || dir2 === direction) {
|
||||
}
|
||||
// ...otherwise, prefer the horizontal one.
|
||||
else if (dir1 === 'west' || dir1 === 'east') {
|
||||
direction = dir1;
|
||||
}
|
||||
else {
|
||||
direction = dir2;
|
||||
}
|
||||
}
|
||||
|
||||
// Now physically move the actor and have them take a turn
|
||||
// Now physically move the actor, but their movement waits until next decision phase
|
||||
this.remove_tile(actor);
|
||||
this.add_tile(actor, dest.cell);
|
||||
// FIXME i think the cc2 approach might be to handle this at decision time, hence why
|
||||
// overriding works at all; it happens to work for me because this happens immediately
|
||||
// before decision time as a separate pass! for now, simulate by undoing the cooldown
|
||||
this.attempt_out_of_turn_step(actor, direction);
|
||||
if (this.compat.use_lynx_loop && actor.movement_cooldown) {
|
||||
this._set_tile_prop(actor, 'movement_cooldown', actor.movement_cooldown + 1);
|
||||
}
|
||||
else {
|
||||
// Be sure to remove the slide_mode or they'll be stuck forever
|
||||
// TODO: in cc2, if you're on a /disabled/ red teleporter, you'll continue to be pushed
|
||||
// in this direction?? is this a bug, is this the only such case?
|
||||
if (! (teleporter.type.name === 'teleport_red' && ! teleporter.type._is_active(teleporter, this))) {
|
||||
this.make_slide(actor, null);
|
||||
}
|
||||
if (! success && actor.type.has_inventory && teleporter.type.name === 'teleport_yellow') {
|
||||
|
||||
if (actor.type.has_inventory && teleporter.type.name === 'teleport_yellow') {
|
||||
// Super duper special yellow teleporter behavior: you pick it the fuck up
|
||||
// FIXME not if there's only one in the level?
|
||||
this.attempt_take(actor, teleporter);
|
||||
@ -1599,6 +1568,7 @@ export class Level extends LevelInterface {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cycle_inventory(actor) {
|
||||
if (this.stored_level.use_cc1_boots)
|
||||
|
||||
@ -679,11 +679,13 @@ const TILE_TYPES = {
|
||||
ice: {
|
||||
draw_layer: DRAW_LAYERS.terrain,
|
||||
slide_mode: 'ice',
|
||||
speed_factor: 2,
|
||||
},
|
||||
ice_sw: {
|
||||
draw_layer: DRAW_LAYERS.terrain,
|
||||
thin_walls: new Set(['south', 'west']),
|
||||
slide_mode: 'ice',
|
||||
speed_factor: 2,
|
||||
blocks_leaving: blocks_leaving_thin_walls,
|
||||
on_arrive(me, level, other) {
|
||||
if (other.direction === 'south') {
|
||||
@ -698,6 +700,7 @@ const TILE_TYPES = {
|
||||
draw_layer: DRAW_LAYERS.terrain,
|
||||
thin_walls: new Set(['north', 'west']),
|
||||
slide_mode: 'ice',
|
||||
speed_factor: 2,
|
||||
blocks_leaving: blocks_leaving_thin_walls,
|
||||
on_arrive(me, level, other) {
|
||||
if (other.direction === 'north') {
|
||||
@ -712,6 +715,7 @@ const TILE_TYPES = {
|
||||
draw_layer: DRAW_LAYERS.terrain,
|
||||
thin_walls: new Set(['north', 'east']),
|
||||
slide_mode: 'ice',
|
||||
speed_factor: 2,
|
||||
blocks_leaving: blocks_leaving_thin_walls,
|
||||
on_arrive(me, level, other) {
|
||||
if (other.direction === 'north') {
|
||||
@ -726,6 +730,7 @@ const TILE_TYPES = {
|
||||
draw_layer: DRAW_LAYERS.terrain,
|
||||
thin_walls: new Set(['south', 'east']),
|
||||
slide_mode: 'ice',
|
||||
speed_factor: 2,
|
||||
blocks_leaving: blocks_leaving_thin_walls,
|
||||
on_arrive(me, level, other) {
|
||||
if (other.direction === 'south') {
|
||||
@ -739,6 +744,7 @@ const TILE_TYPES = {
|
||||
force_floor_n: {
|
||||
draw_layer: DRAW_LAYERS.terrain,
|
||||
slide_mode: 'force',
|
||||
speed_factor: 2,
|
||||
on_ready: on_ready_force_floor,
|
||||
on_arrive(me, level, other) {
|
||||
level.set_actor_direction(other, 'north');
|
||||
@ -756,6 +762,7 @@ const TILE_TYPES = {
|
||||
force_floor_e: {
|
||||
draw_layer: DRAW_LAYERS.terrain,
|
||||
slide_mode: 'force',
|
||||
speed_factor: 2,
|
||||
on_ready: on_ready_force_floor,
|
||||
on_arrive(me, level, other) {
|
||||
level.set_actor_direction(other, 'east');
|
||||
@ -773,6 +780,7 @@ const TILE_TYPES = {
|
||||
force_floor_s: {
|
||||
draw_layer: DRAW_LAYERS.terrain,
|
||||
slide_mode: 'force',
|
||||
speed_factor: 2,
|
||||
on_ready: on_ready_force_floor,
|
||||
on_arrive(me, level, other) {
|
||||
level.set_actor_direction(other, 'south');
|
||||
@ -790,6 +798,7 @@ const TILE_TYPES = {
|
||||
force_floor_w: {
|
||||
draw_layer: DRAW_LAYERS.terrain,
|
||||
slide_mode: 'force',
|
||||
speed_factor: 2,
|
||||
on_ready: on_ready_force_floor,
|
||||
on_arrive(me, level, other) {
|
||||
level.set_actor_direction(other, 'west');
|
||||
@ -807,6 +816,7 @@ const TILE_TYPES = {
|
||||
force_floor_all: {
|
||||
draw_layer: DRAW_LAYERS.terrain,
|
||||
slide_mode: 'force',
|
||||
speed_factor: 2,
|
||||
on_ready: on_ready_force_floor,
|
||||
// TODO ms: this is random, and an acting wall to monsters (!)
|
||||
on_arrive(me, level, other) {
|
||||
@ -1194,6 +1204,7 @@ const TILE_TYPES = {
|
||||
},
|
||||
teleport_blue: {
|
||||
draw_layer: DRAW_LAYERS.terrain,
|
||||
slide_mode: 'teleport',
|
||||
wire_propagation_mode: 'all',
|
||||
*teleport_dest_order(me, level, other) {
|
||||
let exit_direction = other.direction;
|
||||
@ -1274,6 +1285,7 @@ const TILE_TYPES = {
|
||||
},
|
||||
teleport_red: {
|
||||
draw_layer: DRAW_LAYERS.terrain,
|
||||
slide_mode: 'teleport',
|
||||
wire_propagation_mode: 'none',
|
||||
teleport_allow_override: true,
|
||||
_is_active(me, level) {
|
||||
@ -1319,6 +1331,7 @@ const TILE_TYPES = {
|
||||
},
|
||||
teleport_green: {
|
||||
draw_layer: DRAW_LAYERS.terrain,
|
||||
slide_mode: 'teleport',
|
||||
teleport_dest_order(me, level, other) {
|
||||
let all = Array.from(level.iter_tiles_in_reading_order(me.cell, 'teleport_green'));
|
||||
if (all.length <= 1) {
|
||||
@ -1345,6 +1358,7 @@ const TILE_TYPES = {
|
||||
},
|
||||
teleport_yellow: {
|
||||
draw_layer: DRAW_LAYERS.terrain,
|
||||
slide_mode: 'teleport',
|
||||
teleport_allow_override: true,
|
||||
*teleport_dest_order(me, level, other) {
|
||||
let exit_direction = other.direction;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user