diff --git a/js/game.js b/js/game.js index 40b78c9..6ca02c0 100644 --- a/js/game.js +++ b/js/game.js @@ -654,6 +654,10 @@ export class Level { if (! actor.cell) continue; + if (actor.type.on_tic) { + actor.type.on_tic(actor, this); + } + // Check this again, since an earlier pass may have caused us to start moving if (actor.movement_cooldown > 0) continue; @@ -1354,7 +1358,19 @@ export class Level { this.transmute_tile(terrain, 'teleport_yellow'); } else { - this.add_tile(new Tile(TILE_TYPES[name]), actor.cell); + let type = TILE_TYPES[name]; + if (type.on_drop) { + name = type.on_drop(this, actor); + if (name) { + type = TILE_TYPES[name]; + } + } + let tile = new Tile(type); + if (type.is_actor) { + tile.direction = actor.direction; + this.add_actor(tile); + } + this.add_tile(tile, actor.cell); } actor.toolbelt.shift(); @@ -1542,6 +1558,15 @@ export class Level { return (x >= 0 && x < this.width && y >= 0 && y < this.height); } + cell(x, y) { + if (this.is_point_within_bounds(x, y)) { + return this.cells[y][x]; + } + else { + return null; + } + } + get_neighboring_cell(cell, direction) { let move = DIRECTIONS[direction].movement; let goal_x = cell.x + move[0]; diff --git a/js/tileset.js b/js/tileset.js index 924667f..f4de817 100644 --- a/js/tileset.js +++ b/js/tileset.js @@ -131,8 +131,9 @@ export const CC2_TILESET_LAYOUT = { canopy: [14, 3], // canopy xray - // TODO lit dynamite: [0, 4], + // FIXME lit frames + dynamite_lit: [0, 4], bomb: [5, 4], green_bomb: [6, 4], // TODO bomb fuse tile, ugh @@ -349,7 +350,8 @@ export const CC2_TILESET_LAYOUT = { south: [[1, 17], [0, 17]], west: [[5, 17], [4, 17]], }, - bowling_ball: [6, 17], // TODO also +18 when rolling + bowling_ball: [6, 17], + rolling_ball: [[6, 17], [7, 17]], tank_yellow: { north: [[8, 17], [9, 17]], east: [[10, 17], [11, 17]], diff --git a/js/tiletypes.js b/js/tiletypes.js index b925f41..b9051d2 100644 --- a/js/tiletypes.js +++ b/js/tiletypes.js @@ -2036,14 +2036,129 @@ const TILE_TYPES = { is_item: true, is_tool: true, blocks_collision: COLLISION.block_cc1 | COLLISION.monster_solid, - // FIXME does a thing when dropped, but that isn't implemented at all yet + on_drop(level, owner) { + // FIXME not if the cell has a no sign + if (! owner.type.is_real_player) { + // Only players can activate dynamite + return; + } + return 'dynamite_lit'; + }, + }, + dynamite_lit: { + draw_layer: DRAW_LAYERS.item, + is_actor: true, + // FIXME collision? + // FIXME kills player on touch + // FIXME inherits a copy of player's inventory! + // FIXME holds down buttons, so needs an on_arrive + // FIXME speaking of buttons, destroyed actors should on_depart + on_tic(me, level) { + // FIXME When Chip or Melinda leaves a tile with a time bomb and no no sign on it, the + // time bomb will count down for about 4.3 seconds before exploding; it does not matter + // whether the player dropped the item (e.g. if the player teleported)???? + if (me.slide_mode || me.movement_cooldown) + return; + if (me.timer === undefined) { + me.timer = 86; // FIXME?? wiki just says about 4.3 seconds what + return; + } + + me.timer -= 1; + if (me.timer > 0) + return; + + // Kaboom! Blow up a 5x5 square + level.sfx.play_once('bomb', me.cell); + let x = me.cell.x, y = me.cell.y; + for (let dx = -2; dx <= 2; dx++) { + for (let dy = -2; dy <= 2; dy++) { + // Exclude the far corners + if (Math.abs(dx) + Math.abs(dy) >= 4) + continue; + + let cell = level.cell(x + dx, y + dy); + if (! cell) + continue; + + let tiles = Array.from(cell); + tiles.sort((a, b) => b.type.draw_layer - a.type.draw_layer); + let actor, terrain, removed_anything; + for (let tile of tiles) { + if (tile.type.name === 'canopy') { + // Canopy protects everything else + break; + } + if (tile.type.is_actor) { + actor = tile; + } + + if (tile.type.draw_layer === 0) { + // Terrain gets transmuted afterwards + terrain = tile; + } + else { + // Everything else is destroyed + level.remove_tile(tile); + removed_anything = true; + } + } + + if (actor) { + // Actors protect terrain, but floor becomes fire + if (terrain && terrain.type.name === 'floor' && terrain.wire_directions === 0 && terrain.wire_tunnel_directions === 0) { + if (actor.type.name === 'ice_block') { + level.transmute_tile(terrain, 'water'); + } + else { + level.transmute_tile(terrain, 'fire'); + } + } + } + else if (terrain) { + // Anything other than these babies gets blown up and turned into floor + if (!( + terrain.type.name === 'steel' || terrain.type.name === 'socket' || terrain.type.name === 'logic_gate' || terrain.type.name === 'floor')) + { + level.transmute_tile(terrain, 'floor'); + removed_anything = true; + } + } + + if (removed_anything) { + level.spawn_animation(cell, 'explosion'); + } + } + } + }, }, bowling_ball: { - // TODO not implemented, rolls when dropped, has an inventory, yadda yadda draw_layer: DRAW_LAYERS.item, is_item: true, is_tool: true, blocks_collision: COLLISION.block_cc1 | COLLISION.monster_solid, + on_drop(level, owner) { + return 'rolling_ball'; + }, + }, + rolling_ball: { + draw_layer: DRAW_LAYERS.actor, + is_actor: true, + has_inventory: true, + // FIXME collision...? + // FIXME do i start moving immediately when dropped, or next turn? + movement_speed: 4, + decide_movement(me, level) { + return [ + me.direction, + function() { + // FIXME lol this is incredibly stupid but i have no way to react when /i/ hit + // something /else/ yet + level.transmute_tile(me, 'explosion'); + return me.direction; + }, + ]; + }, }, xray_eye: { // TODO not implemented