276 lines
6.2 KiB
JavaScript
276 lines
6.2 KiB
JavaScript
export const TILE_TYPES = {
|
|
floor: {
|
|
cc2_byte: 0x01,
|
|
},
|
|
wall: {
|
|
cc2_byte: 0x02,
|
|
blocks: true,
|
|
},
|
|
ice: {
|
|
cc2_byte: 0x03,
|
|
},
|
|
ice_sw: {
|
|
cc2_byte: 0x04,
|
|
thin_walls: {
|
|
south: true,
|
|
west: true,
|
|
},
|
|
},
|
|
ice_nw: {
|
|
cc2_byte: 0x05,
|
|
thin_walls: {
|
|
north: true,
|
|
west: true,
|
|
},
|
|
},
|
|
ice_ne: {
|
|
cc2_byte: 0x06,
|
|
thin_walls: {
|
|
north: true,
|
|
east: true,
|
|
},
|
|
},
|
|
ice_se: {
|
|
cc2_byte: 0x07,
|
|
thin_walls: {
|
|
south: true,
|
|
east: true,
|
|
},
|
|
},
|
|
water: {
|
|
cc2_byte: 0x08,
|
|
on_arrive(me, level, other) {
|
|
// TODO cc1 allows items under water, i think; water was on the upper layer
|
|
if (other.type.name == 'dirt_block') {
|
|
other.destroy();
|
|
me.become('dirt');
|
|
}
|
|
else if (other.type.is_player) {
|
|
level.fail("Oops! You can't swim without flippers!");
|
|
other.become('player_drowned');
|
|
}
|
|
else {
|
|
other.destroy();
|
|
}
|
|
}
|
|
},
|
|
fire: {
|
|
cc2_byte: 0x09,
|
|
on_arrive(me, level, other) {
|
|
if (other.type.is_player) {
|
|
level.fail("Oops! You can't walk on fire without fire boots!");
|
|
other.become('player_burned');
|
|
}
|
|
else {
|
|
other.destroy();
|
|
}
|
|
}
|
|
},
|
|
force_floor_n: {
|
|
cc2_byte: 0x0a,
|
|
on_arrive(me, level, other) {
|
|
other.direction = 'north';
|
|
other.is_sliding = true;
|
|
}
|
|
},
|
|
force_floor_e: {
|
|
cc2_byte: 0x0b,
|
|
on_arrive(me, level, other) {
|
|
other.direction = 'east';
|
|
other.is_sliding = true;
|
|
}
|
|
},
|
|
force_floor_s: {
|
|
cc2_byte: 0x0c,
|
|
on_arrive(me, level, other) {
|
|
other.direction = 'south';
|
|
other.is_sliding = true;
|
|
}
|
|
},
|
|
force_floor_w: {
|
|
cc2_byte: 0x0d,
|
|
on_arrive(me, level, other) {
|
|
other.direction = 'west';
|
|
other.is_sliding = true;
|
|
}
|
|
},
|
|
|
|
exit: {
|
|
cc2_byte: 0x14,
|
|
},
|
|
|
|
player: {
|
|
cc2_byte: 0x16,
|
|
is_actor: true,
|
|
is_player: true,
|
|
has_inventory: true,
|
|
has_direction: true,
|
|
is_top_layer: true,
|
|
pushes: {
|
|
dirt_block: true,
|
|
},
|
|
infinite_items: {
|
|
key_green: true,
|
|
},
|
|
},
|
|
player_drowned: {
|
|
cc2_byte: null,
|
|
},
|
|
player_burned: {
|
|
cc2_byte: null,
|
|
},
|
|
dirt_block: {
|
|
cc2_byte: 0x17,
|
|
blocks: true,
|
|
has_direction: true,
|
|
is_top_layer: true,
|
|
},
|
|
|
|
door_red: {
|
|
cc2_byte: 0x22,
|
|
blocks: true,
|
|
on_bump(me, level, other) {
|
|
if (other.type.has_inventory && other.take_item('key_red')) {
|
|
me.type = TILE_TYPES.floor;
|
|
}
|
|
}
|
|
},
|
|
door_blue: {
|
|
cc2_byte: 0x23,
|
|
blocks: true,
|
|
on_bump(me, level, other) {
|
|
if (other.type.has_inventory && other.take_item('key_blue')) {
|
|
me.type = TILE_TYPES.floor;
|
|
}
|
|
}
|
|
},
|
|
door_yellow: {
|
|
cc2_byte: 0x24,
|
|
blocks: true,
|
|
on_bump(me, level, other) {
|
|
if (other.type.has_inventory && other.take_item('key_yellow')) {
|
|
me.type = TILE_TYPES.floor;
|
|
}
|
|
}
|
|
},
|
|
door_green: {
|
|
cc2_byte: 0x25,
|
|
blocks: true,
|
|
on_bump(me, level, other) {
|
|
if (other.type.has_inventory && other.take_item('key_green')) {
|
|
me.type = TILE_TYPES.floor;
|
|
}
|
|
}
|
|
},
|
|
key_red: {
|
|
cc2_byte: 0x26,
|
|
is_top_layer: true,
|
|
is_item: true,
|
|
},
|
|
key_blue: {
|
|
cc2_byte: 0x27,
|
|
is_top_layer: true,
|
|
is_item: true,
|
|
},
|
|
key_yellow: {
|
|
cc2_byte: 0x28,
|
|
is_top_layer: true,
|
|
is_item: true,
|
|
},
|
|
key_green: {
|
|
cc2_byte: 0x29,
|
|
is_top_layer: true,
|
|
is_item: true,
|
|
},
|
|
chip: {
|
|
cc2_byte: 0x2a,
|
|
is_top_layer: true,
|
|
is_chip: true,
|
|
is_required_chip: true,
|
|
on_arrive(me, level, other) {
|
|
if (other.type.is_player) {
|
|
level.collect_chip();
|
|
me.destroy();
|
|
}
|
|
}
|
|
},
|
|
chip_extra: {
|
|
cc2_byte: 0x2b,
|
|
is_chip: true,
|
|
is_top_layer: true,
|
|
},
|
|
socket: {
|
|
cc2_byte: 0x2c,
|
|
blocks: true,
|
|
on_bump(me, level, other) {
|
|
if (other.type.is_player && level.chips_remaining === 0) {
|
|
me.type = TILE_TYPES.floor;
|
|
}
|
|
}
|
|
},
|
|
|
|
dirt: {
|
|
cc2_byte: 0x32,
|
|
// TODO block monsters, and melinda only without the hiking boots
|
|
on_arrive(me, level, other) {
|
|
me.become('floor');
|
|
}
|
|
},
|
|
bug: {
|
|
cc2_byte: 0x33,
|
|
is_actor: true,
|
|
has_direction: true,
|
|
is_top_layer: true,
|
|
},
|
|
|
|
cleats: {
|
|
cc2_byte: 0x3b,
|
|
is_top_layer: true,
|
|
is_item: true,
|
|
item_ignores: new Set(['ice']),
|
|
},
|
|
suction_boots: {
|
|
cc2_byte: 0x3c,
|
|
is_top_layer: true,
|
|
is_item: true,
|
|
item_ignores: new Set([
|
|
'force_floor_n',
|
|
'force_floor_s',
|
|
'force_floor_e',
|
|
'force_floor_w',
|
|
]),
|
|
},
|
|
fire_boots: {
|
|
cc2_byte: 0x3d,
|
|
is_top_layer: true,
|
|
is_item: true,
|
|
item_ignores: new Set(['fire']),
|
|
},
|
|
flippers: {
|
|
cc2_byte: 0x3e,
|
|
is_top_layer: true,
|
|
is_item: true,
|
|
item_ignores: new Set(['water']),
|
|
},
|
|
|
|
clue: {
|
|
cc2_byte: 0x45,
|
|
},
|
|
};
|
|
|
|
|
|
export const CC2_TILE_TYPES = new Array(256);
|
|
CC2_TILE_TYPES.fill(null);
|
|
for (let [name, tiledef] of Object.entries(TILE_TYPES)) {
|
|
tiledef.name = name;
|
|
|
|
if (tiledef.cc2_byte === null)
|
|
continue;
|
|
|
|
let existing = CC2_TILE_TYPES[tiledef.cc2_byte];
|
|
if (existing)
|
|
throw new Error(`Duplicate CC2 byte: ${tiledef.cc2_byte} is both '${existing}' and '${name}'`);
|
|
|
|
CC2_TILE_TYPES[tiledef.cc2_byte] = name;
|
|
}
|