diff --git a/js/format-c2m.js b/js/format-c2m.js index be25daa..f100717 100644 --- a/js/format-c2m.js +++ b/js/format-c2m.js @@ -24,28 +24,28 @@ const TILE_ENCODING = { //0x13: 'teleport_green', 0x14: 'exit', //0x15: 'slime', - 0x16: ['player', 'direction', 'next'], - 0x17: ['dirt_block', 'direction', 'next'], - 0x18: ['walker', 'direction', 'next'], - 0x19: ['glider', 'direction', 'next'], - 0x1a: ['ice_block', 'direction', 'next'], - 0x1b: ['thinwall_e', 'next'], - 0x1c: ['thinwall_s', 'next'], - 0x1d: ['thinwall_se', 'next'], + 0x16: ['player', '#direction', '#next'], + 0x17: ['dirt_block', '#direction', '#next'], + 0x18: ['walker', '#direction', '#next'], + 0x19: ['glider', '#direction', '#next'], + 0x1a: ['ice_block', '#direction', '#next'], + 0x1b: ['thinwall_e', '#next'], + 0x1c: ['thinwall_s', '#next'], + 0x1d: ['thinwall_se', '#next'], 0x1e: 'gravel', 0x1f: 'button_green', 0x20: 'button_blue', - 0x21: ['tank_blue', 'direction', 'next'], + 0x21: ['tank_blue', '#direction', '#next'], 0x22: 'door_red', 0x23: 'door_blue', 0x24: 'door_yellow', 0x25: 'door_green', - 0x26: ['key_red', 'next'], - 0x27: ['key_blue', 'next'], - 0x28: ['key_yellow', 'next'], - 0x29: ['key_green', 'next'], - 0x2a: ['chip', 'next'], - 0x2b: ['chip_extra', 'next'], + 0x26: ['key_red', '#next'], + 0x27: ['key_blue', '#next'], + 0x28: ['key_yellow', '#next'], + 0x29: ['key_green', '#next'], + 0x2a: ['chip', '#next'], + 0x2b: ['chip_extra', '#next'], 0x2c: 'socket', 0x2d: 'popwall', 0x2e: 'wall_appearing', @@ -53,20 +53,20 @@ const TILE_ENCODING = { 0x30: 'fake_wall', 0x31: 'fake_floor', 0x32: 'dirt', - 0x33: ['bug', 'direction', 'next'], - 0x34: ['paramecium', 'direction', 'next'], - 0x35: ['ball', 'direction', 'next'], - 0x36: ['blob', 'direction', 'next'], - 0x37: ['teeth', 'direction', 'next'], - 0x38: ['fireball', 'direction', 'next'], + 0x33: ['bug', '#direction', '#next'], + 0x34: ['paramecium', '#direction', '#next'], + 0x35: ['ball', '#direction', '#next'], + 0x36: ['blob', '#direction', '#next'], + 0x37: ['teeth', '#direction', '#next'], + 0x38: ['fireball', '#direction', '#next'], 0x39: 'button_red', 0x3a: 'button_brown', - 0x3b: ['cleats', 'next'], - 0x3c: ['suction_boots', 'next'], - 0x3d: ['fire_boots', 'next'], - 0x3e: ['flippers', 'next'], + 0x3b: ['cleats', '#next'], + 0x3c: ['suction_boots', '#next'], + 0x3d: ['fire_boots', '#next'], + 0x3e: ['flippers', '#next'], 0x3f: 'thief_keys', - 0x40: ['bomb', 'next'], + 0x40: ['bomb', '#next'], //0x41: Open trap (unused in main levels) : 0x42: 'trap', 0x43: 'cloner', @@ -74,46 +74,46 @@ const TILE_ENCODING = { 0x45: 'hint', //0x46: 'force_floor_all', // 0x47: 'button_gray', - 0x48: 'swivel_sw', - 0x49: 'swivel_nw', - 0x4a: 'swivel_ne', - 0x4b: 'swivel_se', - // 0x4c: Time bonus : 'next' - // 0x4d: Stopwatch : 'next' + 0x48: ['swivel_sw', 'swivel_floor'], + 0x49: ['swivel_nw', 'swivel_floor'], + 0x4a: ['swivel_ne', 'swivel_floor'], + 0x4b: ['swivel_se', 'swivel_floor'], + // 0x4c: Time bonus : '#next' + // 0x4d: Stopwatch : '#next' // 0x4e: Transmogrifier : // 0x4f: Railroad track (Modifier required, see section below) : // 0x50: Steel wall : - // 0x51: Time bomb : 'next' - // 0x52: Helmet : 'next' - // 0x53: (Unused) : 'direction', 'next' + // 0x51: Time bomb : '#next' + // 0x52: Helmet : '#next' + // 0x53: (Unused) : '#direction', '#next' // 0x54: (Unused) : // 0x55: (Unused) : - // 0x56: Melinda : 'direction', 'next' - // 0x57: Timid teeth : 'direction', 'next' - // 0x58: Explosion animation (unused in main levels) : 'direction', 'next' - // 0x59: Hiking boots : 'next' + // 0x56: Melinda : '#direction', '#next' + // 0x57: Timid teeth : '#direction', '#next' + // 0x58: Explosion animation (unused in main levels) : '#direction', '#next' + // 0x59: Hiking boots : '#next' // 0x5a: Male-only sign : // 0x5b: Female-only sign : // 0x5c: Inverter gate (N) : Modifier allows other gates, see below - // 0x5d: (Unused) : 'direction', 'next' + // 0x5d: (Unused) : '#direction', '#next' // 0x5e: Logic switch (ON) : // 0x5f: Flame jet (OFF) : // 0x60: Flame jet (ON) : // 0x61: Orange button : - // 0x62: Lightning bolt : 'next' - // 0x63: Yellow tank : 'direction', 'next' + // 0x62: Lightning bolt : '#next' + // 0x63: Yellow tank : '#direction', '#next' // 0x64: Yellow tank button : - // 0x65: Mirror Chip : 'direction', 'next' - // 0x66: Mirror Melinda : 'direction', 'next' + // 0x65: Mirror Chip : '#direction', '#next' + // 0x66: Mirror Melinda : '#direction', '#next' // 0x67: (Unused) : - // 0x68: Bowling ball : 'next' - // 0x69: Rover : 'direction', 'next' - // 0x6a: Time penalty : 'next' + // 0x68: Bowling ball : '#next' + // 0x69: Rover : '#direction', '#next' + // 0x6a: Time penalty : '#next' // 0x6b: Custom floor (green) : Modifier allows other styles, see below // 0x6c: (Unused) : - // 0x6d: Thin wall / Canopy : Panel/Canopy bitmask (see below), 'next' + // 0x6d: Thin wall / Canopy : Panel/Canopy bitmask (see below), '#next' // 0x6e: (Unused) : - // 0x6f: Railroad sign : 'next' + // 0x6f: Railroad sign : '#next' // 0x70: Custom wall (green) : Modifier allows other styles, see below // TODO needs a preceding modifier but that's not done yet (and should enforce that a modifier is followed by a modifiable tile?) 0x71: 'floor_letter', @@ -124,32 +124,32 @@ const TILE_ENCODING = { // 0x76: 8-bit Modifier (see Modifier section below) : 1 modifier byte, Tile Specification for affected tile // 0x77: 16-bit Modifier (see Modifier section below) : 2 modifier bytes, Tile Specification for affected tile // 0x78: 32-bit Modifier (see Modifier section below) : 4 modifier bytes, Tile Specification for affected tile - // 0x79: (Unused) : 'direction', 'next' - 0x7a: ['score_10', 'next'], - 0x7b: ['score_100', 'next'], - 0x7c: ['score_1000', 'next'], + // 0x79: (Unused) : '#direction', '#next' + 0x7a: ['score_10', '#next'], + 0x7b: ['score_100', '#next'], + 0x7c: ['score_1000', '#next'], // 0x7d: Solid green wall : // 0x7e: False green wall : - 0x7f: ['forbidden', 'next'], - 0x80: ['score_2x', 'next'], - // 0x81: Directional block : 'direction', Directional Arrows Bitmask, 'next' - // 0x82: Floor mimic : 'direction', 'next' - // 0x83: Green bomb : 'next' - // 0x84: Green chip : 'next' - // 0x85: (Unused) : 'next' - // 0x86: (Unused) : 'next' + 0x7f: ['forbidden', '#next'], + 0x80: ['score_2x', '#next'], + // 0x81: Directional block : '#direction', Directional Arrows Bitmask, '#next' + // 0x82: Floor mimic : '#direction', '#next' + // 0x83: Green bomb : '#next' + // 0x84: Green chip : '#next' + // 0x85: (Unused) : '#next' + // 0x86: (Unused) : '#next' // 0x87: Black button : // 0x88: ON/OFF switch (OFF) : // 0x89: ON/OFF switch (ON) : 0x8a: 'thief_tools', - // 0x8b: Ghost : 'direction', 'next' - // 0x8c: Steel foil : 'next' - 0x8d: 'turtle', - // 0x8e: Secret eye : 'next' - // 0x8f: Thief bribe : 'next' - // 0x90: Speed boots : 'next' + // 0x8b: Ghost : '#direction', '#next' + // 0x8c: Steel foil : '#next' + 0x8d: ['turtle', 'water'], + // 0x8e: Secret eye : '#next' + // 0x8f: Thief bribe : '#next' + // 0x90: Speed boots : '#next' // 0x91: (Unused) : - // 0x92: Hook : 'next' + // 0x92: Hook : '#next' }; // Decompress the little ad-hoc compression scheme used for both map data and @@ -354,7 +354,7 @@ export function parse_level(buf) { // Handle extra arguments let has_next = false; for (let arg of args) { - if (arg === 'direction') { + if (arg === '#direction') { let dirbyte = bytes[p]; p++; let direction = ['north', 'east', 'south', 'west'][dirbyte]; @@ -364,9 +364,14 @@ export function parse_level(buf) { } tile.direction = direction; } - else if (arg === 'next') { + else if (arg === '#next') { has_next = true; } + else { + // Anything else is an implicit next tile, e.g. + // turtles imply water underneath + cell.push({name: arg}); + } } if (! has_next) diff --git a/js/main.js b/js/main.js index 69a0f03..488c736 100644 --- a/js/main.js +++ b/js/main.js @@ -367,9 +367,28 @@ class Level { let blocked; if (goal_x >= 0 && goal_x < this.width && goal_y >= 0 && goal_y < this.height) { - let goal_cell = this.cells[goal_y][goal_x]; - goal_cell.each(tile => { - if (tile !== actor && tile.type.blocks) { + // Check for a thin wall in our current cell first + let original_cell = this.cells[actor.y][actor.x]; + original_cell.each(tile => { + if (tile !== actor && tile.type.thin_walls && + tile.type.thin_walls.has(direction)) + { + blocked = true; + return; + } + }); + + // Only bother touching the goal cell if we're not already trapped in this one + // FIXME actually, this prevents flicking! + if (! blocked) { + let goal_cell = this.cells[goal_y][goal_x]; + goal_cell.each(tile => { + if (! ( + tile.type.blocks || + (tile.type.thin_walls && tile.type.thin_walls.has(DIRECTIONS[direction].opposite)) + )) + return; + if (actor.type.pushes && actor.type.pushes[tile.type.name]) { if (this.attempt_step(tile, direction)) // It moved out of the way! @@ -383,8 +402,8 @@ class Level { } blocked = true; // XXX should i break here, or bump everything? - } - }); + }); + } } else { // Hit the edge diff --git a/js/tileset.js b/js/tileset.js index 2d9eec4..945ad02 100644 --- a/js/tileset.js +++ b/js/tileset.js @@ -30,8 +30,9 @@ export const CC2_TILESET_LAYOUT = { // TODO these guys don't have floor underneath. swivel_sw: [9, 11], swivel_nw: [10, 11], - swivel_ne: [12, 11], - swivel_se: [13, 11], + swivel_ne: [11, 11], + swivel_se: [12, 11], + swivel_floor: [13, 11], forbidden: [14, 5], turtle: [13, 12], // TODO also 14 + 15 for sinking popwall: [8, 10], diff --git a/js/tiletypes.js b/js/tiletypes.js index 0eec466..f40096b 100644 --- a/js/tiletypes.js +++ b/js/tiletypes.js @@ -47,17 +47,18 @@ const TILE_TYPES = { }, // Swivel doors + swivel_floor: {}, swivel_ne: { - thin_walls: new Set(['north'], ['east']), + thin_walls: new Set(['north', 'east']), }, swivel_se: { - thin_walls: new Set(['south'], ['east']), + thin_walls: new Set(['south', 'east']), }, swivel_sw: { - thin_walls: new Set(['south'], ['west']), + thin_walls: new Set(['south', 'west']), }, swivel_nw: { - thin_walls: new Set(['north'], ['west']), + thin_walls: new Set(['north', 'west']), }, // Locked doors @@ -140,10 +141,7 @@ const TILE_TYPES = { } }, ice_sw: { - thin_walls: { - south: true, - west: true, - }, + thin_walls: new Set(['south', 'west']), on_arrive(me, level, other) { if (other.direction === 'south') { other.direction = 'east'; @@ -155,10 +153,7 @@ const TILE_TYPES = { } }, ice_nw: { - thin_walls: { - north: true, - west: true, - }, + thin_walls: new Set(['north', 'west']), on_arrive(me, level, other) { if (other.direction === 'north') { other.direction = 'east'; @@ -170,10 +165,7 @@ const TILE_TYPES = { } }, ice_ne: { - thin_walls: { - north: true, - east: true, - }, + thin_walls: new Set(['north', 'east']), on_arrive(me, level, other) { if (other.direction === 'north') { other.direction = 'west'; @@ -185,10 +177,7 @@ const TILE_TYPES = { } }, ice_se: { - thin_walls: { - south: true, - east: true, - }, + thin_walls: new Set(['south', 'east']), on_arrive(me, level, other) { if (other.direction === 'south') { other.direction = 'west';