Allow blocks to reverse on railroads; partially implement teleporter wiring
This commit is contained in:
parent
6063ea9fba
commit
422c702777
117
js/tiletypes.js
117
js/tiletypes.js
@ -463,6 +463,8 @@ const TILE_TYPES = {
|
|||||||
on_depart(me, level, other) {
|
on_depart(me, level, other) {
|
||||||
if (other.type.name === 'directional_block') {
|
if (other.type.name === 'directional_block') {
|
||||||
// Directional blocks are rotated when they leave
|
// Directional blocks are rotated when they leave
|
||||||
|
// FIXME this isn't right, they rotate by the difference between their attempted
|
||||||
|
// move and their redirected move
|
||||||
if (other.direction === DIRECTIONS[me.entered_direction].right) {
|
if (other.direction === DIRECTIONS[me.entered_direction].right) {
|
||||||
let new_arrows = new Set;
|
let new_arrows = new Set;
|
||||||
for (let arrow of other.arrows) {
|
for (let arrow of other.arrows) {
|
||||||
@ -495,6 +497,9 @@ const TILE_TYPES = {
|
|||||||
|
|
||||||
let legal_exits = new Set;
|
let legal_exits = new Set;
|
||||||
let entered_from = DIRECTIONS[me.entered_direction].opposite;
|
let entered_from = DIRECTIONS[me.entered_direction].opposite;
|
||||||
|
if (other.type.can_reverse_on_railroad) {
|
||||||
|
legal_exits.add(entered_from);
|
||||||
|
}
|
||||||
for (let track of me.type._iter_tracks(me)) {
|
for (let track of me.type._iter_tracks(me)) {
|
||||||
if (track[0] === entered_from) {
|
if (track[0] === entered_from) {
|
||||||
legal_exits.add(track[1]);
|
legal_exits.add(track[1]);
|
||||||
@ -871,6 +876,7 @@ const TILE_TYPES = {
|
|||||||
is_actor: true,
|
is_actor: true,
|
||||||
is_block: true,
|
is_block: true,
|
||||||
ignores: new Set(['fire', 'flame_jet_on']),
|
ignores: new Set(['fire', 'flame_jet_on']),
|
||||||
|
can_reverse_on_railroad: true,
|
||||||
movement_speed: 4,
|
movement_speed: 4,
|
||||||
},
|
},
|
||||||
ice_block: {
|
ice_block: {
|
||||||
@ -880,6 +886,7 @@ const TILE_TYPES = {
|
|||||||
is_actor: true,
|
is_actor: true,
|
||||||
is_block: true,
|
is_block: true,
|
||||||
can_reveal_walls: true,
|
can_reveal_walls: true,
|
||||||
|
can_reverse_on_railroad: true,
|
||||||
movement_speed: 4,
|
movement_speed: 4,
|
||||||
pushes: {
|
pushes: {
|
||||||
ice_block: true,
|
ice_block: true,
|
||||||
@ -897,6 +904,7 @@ const TILE_TYPES = {
|
|||||||
is_actor: true,
|
is_actor: true,
|
||||||
is_block: true,
|
is_block: true,
|
||||||
can_reveal_walls: true,
|
can_reveal_walls: true,
|
||||||
|
can_reverse_on_railroad: true,
|
||||||
movement_speed: 4,
|
movement_speed: 4,
|
||||||
allows_push(me, direction) {
|
allows_push(me, direction) {
|
||||||
return me.arrows && me.arrows.has(direction);
|
return me.arrows && me.arrows.has(direction);
|
||||||
@ -1112,31 +1120,127 @@ const TILE_TYPES = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
// FIXME blue teleporters transmit current 4 ways. red don't transmit it at all
|
||||||
teleport_blue: {
|
teleport_blue: {
|
||||||
draw_layer: DRAW_LAYERS.terrain,
|
draw_layer: DRAW_LAYERS.terrain,
|
||||||
teleport_dest_order(me, level, other) {
|
teleport_dest_order(me, level, other) {
|
||||||
// FIXME wired teleports form a network, which is complicated, and logic gates
|
if (! me.wire_directions) {
|
||||||
// complicate it further
|
// TODO cc2 has a bug where, once it wraps around to the bottom right, it seems to
|
||||||
|
// forget that it was ever looking for an unwired teleport and will just grab the
|
||||||
|
// first one it sees
|
||||||
return level.iter_tiles_in_reading_order(me.cell, 'teleport_blue', true);
|
return level.iter_tiles_in_reading_order(me.cell, 'teleport_blue', true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wired blue teleports form a network, which means we have to walk all wires from this
|
||||||
|
// point, collect a list of all possible blue teleports, and then sort them so we can
|
||||||
|
// try them in the right order.
|
||||||
|
// Complicating this somewhat, logic gates act as diodes: we can walk through a logic
|
||||||
|
// gate if we're connected to one of its inputs AND its output is enabled, but we can't
|
||||||
|
// walk "backwards" through it.
|
||||||
|
// (In CC2, this is even worse; if the game searches the circuit and ONLY finds a logic
|
||||||
|
// gate, it seems to recurse from there, breaking the expected order. Worse, if it then
|
||||||
|
// can't find a destination teleporter, it teleports the actor "INTO" the logic gate
|
||||||
|
// itself; if a destination later presents itself, the actor will immediately appear,
|
||||||
|
// but if not, it might linger THROUGH A RESTART OR EVEN EDIT OF THE LEVEL, possibly
|
||||||
|
// appearing on a later playthrough or possibly crashing the game. Suffice to say, this
|
||||||
|
// behavior is not and will never be emulated. No level in CC2 or even CC2LP1 uses blue
|
||||||
|
// teleporters wired into logic gates, so even the ordering is not interesting imo.)
|
||||||
|
// Anyway, let's do a breadth-first search for teleporters.
|
||||||
|
let seeds = [me.cell];
|
||||||
|
let found = [];
|
||||||
|
let seen = new Set;
|
||||||
|
while (seeds.length > 0) {
|
||||||
|
console.log(seeds);
|
||||||
|
let next_seeds = [];
|
||||||
|
for (let cell of seeds) {
|
||||||
|
if (seen.has(cell))
|
||||||
|
continue;
|
||||||
|
seen.add(cell);
|
||||||
|
|
||||||
|
let wired = cell.get_wired_tile();
|
||||||
|
if (! wired || wired.wire_directions === 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Check for a blue teleporter
|
||||||
|
let dest;
|
||||||
|
for (let tile of cell) {
|
||||||
|
if (tile.type.name === 'teleport_blue') {
|
||||||
|
found.push(tile);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search our neighbors
|
||||||
|
for (let direction of Object.keys(DIRECTIONS)) {
|
||||||
|
if (! (wired.wire_directions & DIRECTIONS[direction].bit))
|
||||||
|
continue;
|
||||||
|
let neighbor = level.get_neighboring_cell(cell, direction);
|
||||||
|
if (! neighbor || seen.has(neighbor))
|
||||||
|
continue;
|
||||||
|
let neighbor_wired = neighbor.get_wired_tile();
|
||||||
|
if (! neighbor_wired)
|
||||||
|
continue;
|
||||||
|
if (! (neighbor_wired.wire_directions & DIRECTIONS[DIRECTIONS[direction].opposite].bit))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// TODO check for logic gate
|
||||||
|
// TODO need to know the direction we came in so we can get the right ones
|
||||||
|
// going out!
|
||||||
|
// FIXME this needs to understand crossings, and both this and basic wiring
|
||||||
|
// need to understand how blue/red teleports convey current
|
||||||
|
|
||||||
|
next_seeds.push(neighbor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
seeds = next_seeds;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now that we have a list of candidate exits, sort it in reverse reading order,
|
||||||
|
// starting from ourselves. Easiest way to do this is to make a map of cell indices,
|
||||||
|
// shifted so that we're at zero, then sort in reverse
|
||||||
|
let dest_indices = new Map;
|
||||||
|
let our_index = me.cell.x + me.cell.y * level.size_x;
|
||||||
|
let level_size = level.size_x * level.size_y;
|
||||||
|
for (let dest of found) {
|
||||||
|
dest_indices.set(dest, (
|
||||||
|
(dest.cell.x + dest.cell.y * level.size_x)
|
||||||
|
- our_index + level_size
|
||||||
|
) % level_size);
|
||||||
|
}
|
||||||
|
found.sort((a, b) => dest_indices.get(b) - dest_indices.get(a));
|
||||||
|
return found;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
teleport_red: {
|
teleport_red: {
|
||||||
draw_layer: DRAW_LAYERS.terrain,
|
draw_layer: DRAW_LAYERS.terrain,
|
||||||
teleport_try_all_directions: true,
|
teleport_try_all_directions: true,
|
||||||
teleport_allow_override: true,
|
teleport_allow_override: true,
|
||||||
|
_is_active(me) {
|
||||||
|
return ! (me.wire_directions && (me.cell.powered_edges & me.wire_directions) === 0);
|
||||||
|
},
|
||||||
*teleport_dest_order(me, level, other) {
|
*teleport_dest_order(me, level, other) {
|
||||||
// Wired red teleporters can be turned off, which disconnects them from every other red
|
// Wired red teleporters can be turned off, which disconnects them from every other red
|
||||||
// teleporter (but they still teleport to themselves)
|
// teleporter (but they still teleport to themselves).
|
||||||
if (level.is_cell_wired(me.cell) && me.cell.powered_edges === 0) {
|
// A red teleporter is considered wired only if it has wires itself. However, CC2 also
|
||||||
|
// has the bizarre behavior of NOT considering a red teleporter wired if none of its
|
||||||
|
// wires are directly connected to another neighboring wire.
|
||||||
|
// FIXME implement that, merge current code with is_cell_wired
|
||||||
|
if (! this._is_active(me)) {
|
||||||
yield me;
|
yield me;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (let tile of level.iter_tiles_in_reading_order(me.cell, 'teleport_red')) {
|
for (let tile of level.iter_tiles_in_reading_order(me.cell, 'teleport_red')) {
|
||||||
if (tile !== me && level.is_cell_wired(tile.cell) && tile.cell.powered_edges === 0)
|
if (tile === me || this._is_active(tile)) {
|
||||||
continue;
|
|
||||||
yield tile;
|
yield tile;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
// TODO inactive ones don't animate
|
||||||
|
/*
|
||||||
|
visual_state(me) {
|
||||||
|
return this._is_active(me) ? 'active' : 'inactive';
|
||||||
|
},
|
||||||
|
*/
|
||||||
},
|
},
|
||||||
teleport_green: {
|
teleport_green: {
|
||||||
draw_layer: DRAW_LAYERS.terrain,
|
draw_layer: DRAW_LAYERS.terrain,
|
||||||
@ -1555,6 +1659,7 @@ const TILE_TYPES = {
|
|||||||
blocks_collision: COLLISION.all,
|
blocks_collision: COLLISION.all,
|
||||||
is_actor: true,
|
is_actor: true,
|
||||||
is_block: true,
|
is_block: true,
|
||||||
|
can_reverse_on_railroad: true,
|
||||||
movement_speed: 4,
|
movement_speed: 4,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user