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