Mostly implement orange buttons and flame jets

This commit is contained in:
Eevee (Evelyn Woods) 2020-10-24 21:33:11 -06:00
parent 0c9a7e3d07
commit 368e4676fd
3 changed files with 90 additions and 12 deletions

View File

@ -291,22 +291,25 @@ export class Level {
let cell = connectable.cell; let cell = connectable.cell;
let x = cell.x; let x = cell.x;
let y = cell.y; let y = cell.y;
let goal = connectable.type.connects_to; // FIXME this is a single string for red/brown buttons (to match iter_tiles_in_RO) but a
// set for orange buttons (because flame jet states are separate tiles), which sucks ass
let goals = connectable.type.connects_to;
// Check for custom wiring, for MSCC .DAT levels // Check for custom wiring, for MSCC .DAT levels
// TODO would be neat if this applied to orange buttons too
if (this.stored_level.has_custom_connections) { if (this.stored_level.has_custom_connections) {
let n = this.stored_level.coords_to_scalar(x, y); let n = this.stored_level.coords_to_scalar(x, y);
let target_cell_n = null; let target_cell_n = null;
if (goal === 'trap') { if (connectable.type.name === 'button_brown') {
target_cell_n = this.stored_level.custom_trap_wiring[n] ?? null; target_cell_n = this.stored_level.custom_trap_wiring[n] ?? null;
} }
else if (goal === 'cloner') { else if (connectable.type.name === 'button_red') {
target_cell_n = this.stored_level.custom_cloner_wiring[n] ?? null; target_cell_n = this.stored_level.custom_cloner_wiring[n] ?? null;
} }
if (target_cell_n && target_cell_n < this.width * this.height) { if (target_cell_n && target_cell_n < this.width * this.height) {
let [tx, ty] = this.stored_level.scalar_to_coords(target_cell_n); let [tx, ty] = this.stored_level.scalar_to_coords(target_cell_n);
for (let tile of this.cells[ty][tx]) { for (let tile of this.cells[ty][tx]) {
if (tile.type.name === goal) { if (goals === tile.type.name) {
connectable.connection = tile; connectable.connection = tile;
break; break;
} }
@ -315,8 +318,26 @@ export class Level {
continue; continue;
} }
// Orange buttons do a really weird diamond search
if (connectable.type.connect_order === 'diamond') {
for (let cell of this.iter_cells_in_diamond(connectable.cell)) {
let target = null;
for (let tile of cell) {
if (goals.has(tile.type.name)) {
target = tile;
break;
}
}
if (target !== null) {
connectable.connection = target;
break;
}
}
continue;
}
// Otherwise, look in reading order // Otherwise, look in reading order
for (let tile of this.iter_tiles_in_reading_order(cell, goal)) { for (let tile of this.iter_tiles_in_reading_order(cell, goals)) {
// TODO ideally this should be a weak connection somehow, since dynamite can destroy // TODO ideally this should be a weak connection somehow, since dynamite can destroy
// empty cloners and probably traps too // empty cloners and probably traps too
connectable.connection = tile; connectable.connection = tile;
@ -1189,6 +1210,26 @@ export class Level {
} }
} }
// Iterates over the grid in a diamond pattern, spreading out from the given start cell (but not
// including it). Only used for connecting orange buttons.
*iter_cells_in_diamond(start_cell) {
let max_search_radius = Math.max(this.size_x, this.size_y);
for (let dist = 1; dist <= max_search_radius; dist++) {
// Start east and move counterclockwise
let sx = start_cell.x + dist;
let sy = start_cell.y;
for (let direction of [[-1, -1], [-1, 1], [1, 1], [1, -1]]) {
for (let i = 0; i < dist; i++) {
if (this.is_point_within_bounds(sx, sy)) {
yield this.cells[sy][sx];
}
sx += direction[0];
sy += direction[1];
}
}
}
}
// ------------------------------------------------------------------------- // -------------------------------------------------------------------------
// Undo handling // Undo handling

View File

@ -524,6 +524,7 @@ const EDITOR_PALETTE = [{
'button_blue', 'button_blue',
'button_red', 'cloner', 'button_red', 'cloner',
'button_brown', 'trap', 'button_brown', 'trap',
'button_orange', 'flame_jet_off', 'flame_jet_on',
'teleport_blue', 'teleport_blue',
'teleport_red', 'teleport_red',
'teleport_green', 'teleport_green',

View File

@ -565,7 +565,7 @@ const TILE_TYPES = {
blocks_all: true, blocks_all: true,
is_actor: true, is_actor: true,
is_block: true, is_block: true,
ignores: new Set(['fire']), ignores: new Set(['fire', 'flame_jet_on']),
movement_speed: 4, movement_speed: 4,
}, },
ice_block: { ice_block: {
@ -583,14 +583,13 @@ const TILE_TYPES = {
directional_block: { directional_block: {
// TODO directional, obviously // TODO directional, obviously
// TODO floor in water // TODO floor in water
// TODO destroyed in fire, flame jet, slime // TODO destroyed in slime
// TODO rotate on train tracks // TODO rotate on train tracks
draw_layer: LAYER_ACTOR, draw_layer: LAYER_ACTOR,
blocks_all: true, blocks_all: true,
is_actor: true, is_actor: true,
is_block: true, is_block: true,
can_reveal_walls: true, can_reveal_walls: true,
ignores: new Set(['fire']),
movement_speed: 4, movement_speed: 4,
pushes: { pushes: {
dirt_block: true, dirt_block: true,
@ -857,6 +856,10 @@ const TILE_TYPES = {
return level.iter_tiles_in_reading_order(me.cell, 'teleport_yellow', true); return level.iter_tiles_in_reading_order(me.cell, 'teleport_yellow', true);
}, },
}, },
// Flame jet rules:
// - State toggles /while/ an orange button is held or wire current is received
// - Multiple such inputs cancel each other out
// - Gray button toggles it permanently
flame_jet_off: { flame_jet_off: {
draw_layer: LAYER_TERRAIN, draw_layer: LAYER_TERRAIN,
activate(me, level) { activate(me, level) {
@ -871,7 +874,6 @@ const TILE_TYPES = {
}, },
flame_jet_on: { flame_jet_on: {
draw_layer: LAYER_TERRAIN, draw_layer: LAYER_TERRAIN,
// FIXME every tic, kills every actor in the cell
activate(me, level) { activate(me, level) {
level.transmute_tile(me, 'flame_jet_off'); level.transmute_tile(me, 'flame_jet_off');
}, },
@ -881,6 +883,21 @@ const TILE_TYPES = {
on_power(me, level) { on_power(me, level) {
me.type.activate(me, level); me.type.activate(me, level);
}, },
// Kill anything that shows up
// FIXME every tic, also kills every actor in the cell (mostly matters if you step on with
// fire boots and then drop them)
on_arrive(me, level, other) {
// Note that blocks, fireballs, and anything with fire boots are immune
// TODO would be neat if this understood "ignores anything with fire immunity" but that
// might be a bit too high-level for this game
if (other.type.is_player) {
level.fail('burned');
}
else {
// TODO should this play a sound?
level.transmute_tile(other, 'explosion');
}
},
}, },
// Buttons // Buttons
button_blue: { button_blue: {
@ -999,7 +1016,26 @@ const TILE_TYPES = {
}, },
button_orange: { button_orange: {
draw_layer: LAYER_TERRAIN, draw_layer: LAYER_TERRAIN,
// FIXME toggles flame jets, connected somehow, ??? connects_to: new Set(['flame_jet_off', 'flame_jet_on']),
connect_order: 'diamond',
// Both stepping on and leaving the button have the same effect: toggle the state of the
// connected flame jet
_toggle_flame_jet(me, level, other) {
let jet = me.connection;
if (jet && jet.cell) {
jet.type.activate(jet, level);
}
},
on_arrive(me, level, other) {
level.sfx.play_once('button-press', me.cell);
me.type._toggle_flame_jet(me, level, other);
},
on_depart(me, level, other) {
level.sfx.play_once('button-release', me.cell);
me.type._toggle_flame_jet(me, level, other);
},
}, },
button_pink: { button_pink: {
draw_layer: LAYER_TERRAIN, draw_layer: LAYER_TERRAIN,
@ -1164,7 +1200,7 @@ const TILE_TYPES = {
blocks_blocks: true, blocks_blocks: true,
movement_mode: 'turn-right', movement_mode: 'turn-right',
movement_speed: 4, movement_speed: 4,
ignores: new Set(['fire']), ignores: new Set(['fire', 'flame_jet_on']),
}, },
glider: { glider: {
draw_layer: LAYER_ACTOR, draw_layer: LAYER_ACTOR,
@ -1279,7 +1315,7 @@ const TILE_TYPES = {
is_tool: true, is_tool: true,
blocks_monsters: true, blocks_monsters: true,
blocks_blocks: true, blocks_blocks: true,
item_ignores: new Set(['fire']), item_ignores: new Set(['fire', 'flame_jet_on']),
}, },
flippers: { flippers: {
draw_layer: LAYER_ITEM, draw_layer: LAYER_ITEM,