Implement most of the railroad behavior

This commit is contained in:
Eevee (Evelyn Woods) 2020-11-30 09:08:55 -07:00
parent 8d26de6915
commit e7c9bbe846
2 changed files with 148 additions and 4 deletions

View File

@ -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

View File

@ -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