From e7c9bbe846297e4ce9727be9c857e76058847c8f Mon Sep 17 00:00:00 2001 From: "Eevee (Evelyn Woods)" Date: Mon, 30 Nov 2020 09:08:55 -0700 Subject: [PATCH] Implement most of the railroad behavior --- js/game.js | 17 +++++- js/tiletypes.js | 135 ++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 148 insertions(+), 4 deletions(-) diff --git a/js/game.js b/js/game.js index 6d726e6..f769897 100644 --- a/js/game.js +++ b/js/game.js @@ -44,6 +44,8 @@ export class Tile { } } + // TODO don't love that the arg order is different here vs tile type, but also don't love that + // the name is the same? blocks(other, direction, level) { // Extremely awkward special case: items don't block monsters if the cell also contains an // item modifier (i.e. "no" sign) or a player @@ -60,7 +62,7 @@ export class Tile { return true; if (this.type.blocks) - return this.type.blocks(this, level, other); + return this.type.blocks(this, level, other, direction); return false; } @@ -189,6 +191,16 @@ export class Cell extends Array { } return false; } + + // Special railroad ability: change the direction we attempt to leave + redirect_exit(actor, direction) { + for (let tile of this) { + if (tile.type.redirect_exit) { + return tile.type.redirect_exit(tile, actor, direction); + } + } + return direction; + } } Cell.prototype.prev_powered_edges = 0; Cell.prototype.powered_edges = 0; @@ -731,6 +743,8 @@ export class Level { } fallback_direction = direction; + direction = actor.cell.redirect_exit(actor, direction); + let dest_cell = this.get_neighboring_cell(actor.cell, direction); if (! dest_cell) continue; @@ -836,6 +850,7 @@ export class Level { if (actor.movement_cooldown > 0) return false; + direction = actor.cell.redirect_exit(actor, direction); this.set_actor_direction(actor, direction); // Record our speed, and halve it below if we're stepping onto a sliding tile diff --git a/js/tiletypes.js b/js/tiletypes.js index d85d6a5..0c17472 100644 --- a/js/tiletypes.js +++ b/js/tiletypes.js @@ -316,7 +316,138 @@ const TILE_TYPES = { // Railroad railroad: { draw_layer: DRAW_LAYERS.terrain, - // TODO a lot!! + _track_order: [ + ['north', 'east'], + ['south', 'east'], + ['south', 'west'], + ['north', 'west'], + ['east', 'west'], + ['north', 'south'], + ], + // FIXME railroad_bits sucks, split into some useful variables + on_ready(me) { + // If there's already an actor on top of us, assume it entered the way it's already + // facing (which may be illegal, in which case it can't leave) + let actor = me.cell.get_actor(); + if (actor) { + me.entered_direction = actor.direction; + } + }, + // TODO feel like "ignores" was the wrong idea and there should just be some magic flags for + // particular objects that can be immune to. or maybe those objects should have their own + // implementations of immunity + _is_affected(me, other) { + if (other.type.name === 'ghost') + return false; + if (other.has_item('railroad_sign')) + return false; + return true; + }, + *_iter_tracks(me) { + let order = me.type._track_order; + if (me.railroad_bits & 0x40) { + // FIXME what happens if the "top" track is not actually a valid track??? + yield order[me.railroad_bits >> 8]; + } + else { + for (let [i, track] of order.entries()) { + if (me.railroad_bits & (1 << i)) { + yield track; + } + } + } + }, + _switch_track(me, level) { + if (me.railroad_bits & 0x40) { + let current = me.railroad_bits >> 8; + for (let i = 0, l = me.type._track_order.length; i < l; i++) { + current = (current + 1) % l; + if (me.railroad_bits & (1 << current)) + break; + } + level._set_tile_prop(me, 'railroad_bits', (current << 8) | (me.railroad_bits & 0xff)); + } + }, + has_opening(me, direction) { + for (let track of me.type._iter_tracks(me)) { + if (track.indexOf(direction) >= 0) { + return true; + } + } + return false; + }, + blocks(me, level, other, direction) { + return me.type._is_affected(me, other) && + ! me.type.has_opening(me, DIRECTIONS[direction].opposite); + }, + blocks_leaving(me, other, direction) { + // FIXME needs the same logic as redirect_exit, so that an illegal entrance can't leave + // at all + return me.type._is_affected(me, other) && ! me.type.has_opening(me, direction); + }, + on_arrive(me, level, other) { + // FIXME actually this happens even if you have the sign so it shouldn't ignore!! + level._set_tile_prop(me, 'entered_direction', other.direction); + }, + on_depart(me, level, other) { + if (other.type.name === 'directional_block') { + // Directional blocks are rotated when they leave + if (other.direction === DIRECTIONS[me.entered_direction].right) { + let new_arrows = new Set; + for (let arrow of other.arrows) { + new_arrows.add(DIRECTIONS[arrow].right); + } + level._set_tile_prop(other, 'arrows', new_arrows); + } + else if (other.direction === DIRECTIONS[me.entered_direction].left) { + let new_arrows = new Set; + for (let arrow of other.arrows) { + new_arrows.add(DIRECTIONS[arrow].left); + } + level._set_tile_prop(other, 'arrows', new_arrows); + } + } + + // FIXME this only happens if we're unwired, but i don't have a way to check that atm + me.type._switch_track(me, level); + }, + on_power(me, level) { + me.type._switch_track(me, level); + }, + on_gray_button(me, level) { + me.type._switch_track(me, level); + }, + redirect_exit(me, other, direction) { + if (! me.type._is_affected(me, other)) + return direction; + + let legal_exits = new Set; + let entered_from = DIRECTIONS[me.entered_direction].opposite; + for (let track of me.type._iter_tracks(me)) { + if (track[0] === entered_from) { + legal_exits.add(track[1]); + } + else if (track[1] === entered_from) { + legal_exits.add(track[0]); + } + } + if (legal_exits.has(direction)) { + return direction; + } + if (legal_exits.has(DIRECTIONS[direction].right)) { + return DIRECTIONS[direction].right; + } + if (legal_exits.has(DIRECTIONS[direction].left)) { + return DIRECTIONS[direction].left; + } + if (legal_exits.has(DIRECTIONS[direction].opposite)) { + return DIRECTIONS[direction].opposite; + } + // FIXME i think in this case the actor gets stuck, but, facing which way? + return direction; + }, + // FIXME track switches on depart or power or gray button + // FIXME rotate dir blocks }, // Locked doors @@ -1549,8 +1680,6 @@ const TILE_TYPES = { is_item: true, is_tool: true, blocks_collision: COLLISION.block_cc1 | COLLISION.monster_solid, - // FIXME this doesn't work any more, need to put it in railroad blocks impl - item_ignores: new Set(['railroad']), }, foil: { // TODO not implemented