Finish CC1 parser; show hints; improve ice
This commit is contained in:
parent
3084ca7b49
commit
0dd190fc5a
@ -7,13 +7,19 @@ const CC1_TILE_ENCODING = {
|
|||||||
0x02: 'chip',
|
0x02: 'chip',
|
||||||
0x03: 'water',
|
0x03: 'water',
|
||||||
0x04: 'fire',
|
0x04: 'fire',
|
||||||
// invis wall
|
0x05: 'wall_invisible',
|
||||||
// thin walls...
|
0x06: 'thinwall_n',
|
||||||
|
0x07: 'thinwall_w',
|
||||||
|
0x08: 'thinwall_s',
|
||||||
|
0x09: 'thinwall_e',
|
||||||
0x0a: 'dirt_block',
|
0x0a: 'dirt_block',
|
||||||
0x0b: 'dirt',
|
0x0b: 'dirt',
|
||||||
0x0c: 'ice',
|
0x0c: 'ice',
|
||||||
0x0d: 'force_floor_s',
|
0x0d: 'force_floor_s',
|
||||||
// cloners
|
0x0e: ['clone_block', 'north'],
|
||||||
|
0x0f: ['clone_block', 'west'],
|
||||||
|
0x10: ['clone_block', 'south'],
|
||||||
|
0x11: ['clone_block', 'east'],
|
||||||
0x12: 'force_floor_n',
|
0x12: 'force_floor_n',
|
||||||
0x13: 'force_floor_e',
|
0x13: 'force_floor_e',
|
||||||
0x14: 'force_floor_w',
|
0x14: 'force_floor_w',
|
||||||
@ -22,24 +28,37 @@ const CC1_TILE_ENCODING = {
|
|||||||
0x17: 'door_red',
|
0x17: 'door_red',
|
||||||
0x18: 'door_green',
|
0x18: 'door_green',
|
||||||
0x19: 'door_yellow',
|
0x19: 'door_yellow',
|
||||||
0x1a: 'ice_se',
|
0x1a: 'ice_nw',
|
||||||
0x1b: 'ice_sw',
|
0x1b: 'ice_ne',
|
||||||
0x1c: 'ice_nw',
|
0x1c: 'ice_se',
|
||||||
0x1d: 'ice_nw',
|
0x1d: 'ice_sw',
|
||||||
// fake blocks
|
0x1e: 'fake_floor',
|
||||||
// 0x20 unused
|
0x1f: 'fake_wall',
|
||||||
// thief
|
0x20: 'wall_invisible', // unused
|
||||||
|
0x21: 'thief_tools',
|
||||||
0x22: 'socket',
|
0x22: 'socket',
|
||||||
// green button
|
0x23: 'button_green',
|
||||||
// red button
|
0x24: 'button_red',
|
||||||
// green tile
|
0x25: 'green_wall',
|
||||||
// more buttons, teleports, bombs, traps
|
0x26: 'green_floor',
|
||||||
0x2f: 'clue',
|
0x27: 'button_brown',
|
||||||
|
0x28: 'button_blue',
|
||||||
|
0x29: 'teleport_blue',
|
||||||
|
0x2a: 'bomb',
|
||||||
|
0x2b: 'trap',
|
||||||
|
0x2c: 'wall_appearing',
|
||||||
|
0x2d: 'gravel',
|
||||||
|
0x2e: 'popwall',
|
||||||
|
0x2f: 'hint',
|
||||||
|
0x30: 'thinwall_se',
|
||||||
|
0x31: 'cloner',
|
||||||
|
0x32: 'force_floor_all',
|
||||||
0x33: 'player_drowned',
|
0x33: 'player_drowned',
|
||||||
0x34: 'player_burned',
|
0x34: 'player_burned',
|
||||||
//0x35: player_burned, XXX is this burned off a tile or?
|
//0x35: player_burned, XXX is this burned off a tile or?
|
||||||
// 0x36 - 0x38 unused
|
0x36: 'wall_invisible', // unused
|
||||||
|
0x37: 'wall_invisible', // unused
|
||||||
|
0x38: 'wall_invisible', // unused
|
||||||
//0x39: exit_player,
|
//0x39: exit_player,
|
||||||
0x3a: 'exit',
|
0x3a: 'exit',
|
||||||
0x3b: 'exit', // i think this is for the second frame of the exit animation?
|
0x3b: 'exit', // i think this is for the second frame of the exit animation?
|
||||||
@ -48,7 +67,38 @@ const CC1_TILE_ENCODING = {
|
|||||||
0x41: ['bug', 'west'],
|
0x41: ['bug', 'west'],
|
||||||
0x42: ['bug', 'south'],
|
0x42: ['bug', 'south'],
|
||||||
0x43: ['bug', 'east'],
|
0x43: ['bug', 'east'],
|
||||||
|
0x44: ['fireball', 'north'],
|
||||||
|
0x45: ['fireball', 'west'],
|
||||||
|
0x46: ['fireball', 'south'],
|
||||||
|
0x47: ['fireball', 'east'],
|
||||||
|
0x48: ['ball', 'north'],
|
||||||
|
0x49: ['ball', 'west'],
|
||||||
|
0x4a: ['ball', 'south'],
|
||||||
|
0x4b: ['ball', 'east'],
|
||||||
|
0x4c: ['tank_blue', 'north'],
|
||||||
|
0x4d: ['tank_blue', 'west'],
|
||||||
|
0x4e: ['tank_blue', 'south'],
|
||||||
|
0x4f: ['tank_blue', 'east'],
|
||||||
|
0x50: ['glider', 'north'],
|
||||||
|
0x51: ['glider', 'west'],
|
||||||
|
0x52: ['glider', 'south'],
|
||||||
|
0x53: ['glider', 'east'],
|
||||||
|
0x54: ['teeth', 'north'],
|
||||||
|
0x55: ['teeth', 'west'],
|
||||||
|
0x56: ['teeth', 'south'],
|
||||||
|
0x57: ['teeth', 'east'],
|
||||||
|
0x58: ['walker', 'north'],
|
||||||
|
0x59: ['walker', 'west'],
|
||||||
|
0x5a: ['walker', 'south'],
|
||||||
|
0x5b: ['walker', 'east'],
|
||||||
|
0x5c: ['blob', 'north'],
|
||||||
|
0x5d: ['blob', 'west'],
|
||||||
|
0x5e: ['blob', 'south'],
|
||||||
|
0x5f: ['blob', 'east'],
|
||||||
|
0x60: ['paramecium', 'north'],
|
||||||
|
0x61: ['paramecium', 'west'],
|
||||||
|
0x62: ['paramecium', 'south'],
|
||||||
|
0x63: ['paramecium', 'east'],
|
||||||
0x64: 'key_blue',
|
0x64: 'key_blue',
|
||||||
0x65: 'key_red',
|
0x65: 'key_red',
|
||||||
0x66: 'key_green',
|
0x66: 'key_green',
|
||||||
@ -75,7 +125,6 @@ function parse_level(buf) {
|
|||||||
|
|
||||||
let view = new DataView(buf);
|
let view = new DataView(buf);
|
||||||
let bytes = new Uint8Array(buf);
|
let bytes = new Uint8Array(buf);
|
||||||
console.log(bytes);
|
|
||||||
|
|
||||||
// Header
|
// Header
|
||||||
let level_number = view.getUint16(0, true);
|
let level_number = view.getUint16(0, true);
|
||||||
@ -138,11 +187,11 @@ function parse_level(buf) {
|
|||||||
let meta_length = view.getUint16(p, true);
|
let meta_length = view.getUint16(p, true);
|
||||||
p += 2;
|
p += 2;
|
||||||
let end = p + meta_length;
|
let end = p + meta_length;
|
||||||
while (p < meta_length) {
|
while (p < end) {
|
||||||
// Common header
|
// Common header
|
||||||
let field_type = view.getUint16(p, true);
|
let field_type = view.getUint8(p, true);
|
||||||
let field_length = view.getUint16(p + 2, true);
|
let field_length = view.getUint8(p + 1, true);
|
||||||
p += 4;
|
p += 2;
|
||||||
if (field_type === 0x01) {
|
if (field_type === 0x01) {
|
||||||
// Level time; unnecessary since it's already in the level header
|
// Level time; unnecessary since it's already in the level header
|
||||||
// TODO check, compare, warn?
|
// TODO check, compare, warn?
|
||||||
@ -213,14 +262,12 @@ export function parse_game(buf) {
|
|||||||
// And now, the levels
|
// And now, the levels
|
||||||
let p = 6;
|
let p = 6;
|
||||||
for (let l = 1; l <= level_count; l++) {
|
for (let l = 1; l <= level_count; l++) {
|
||||||
console.log('level', l);
|
|
||||||
let length = full_view.getUint16(p, true);
|
let length = full_view.getUint16(p, true);
|
||||||
let level_buf = buf.slice(p + 2, p + 2 + length);
|
let level_buf = buf.slice(p + 2, p + 2 + length);
|
||||||
p += 2 + length;
|
p += 2 + length;
|
||||||
|
|
||||||
let level = parse_level(level_buf);
|
let level = parse_level(level_buf);
|
||||||
game.levels.push(level);
|
game.levels.push(level);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return game;
|
return game;
|
||||||
|
|||||||
@ -9,6 +9,7 @@ export class StoredLevel {
|
|||||||
constructor() {
|
constructor() {
|
||||||
this.title = '';
|
this.title = '';
|
||||||
this.password = null;
|
this.password = null;
|
||||||
|
this.hint = '';
|
||||||
this.chips_required = 0;
|
this.chips_required = 0;
|
||||||
this.time_limit = 0;
|
this.time_limit = 0;
|
||||||
this.viewport_size = 9;
|
this.viewport_size = 9;
|
||||||
|
|||||||
70
js/main.js
70
js/main.js
@ -64,7 +64,7 @@ class Tile {
|
|||||||
this.direction = 'south';
|
this.direction = 'south';
|
||||||
}
|
}
|
||||||
|
|
||||||
this.is_sliding = false;
|
this.slide_mode = null;
|
||||||
|
|
||||||
if (type.has_inventory) {
|
if (type.has_inventory) {
|
||||||
this.inventory = {};
|
this.inventory = {};
|
||||||
@ -204,6 +204,8 @@ class Level {
|
|||||||
this.actors = [];
|
this.actors = [];
|
||||||
this.chips_remaining = this.stored_level.chips_required;
|
this.chips_remaining = this.stored_level.chips_required;
|
||||||
|
|
||||||
|
this.hint_shown = null;
|
||||||
|
|
||||||
let n = 0;
|
let n = 0;
|
||||||
for (let y = 0; y < this.height; y++) {
|
for (let y = 0; y < this.height; y++) {
|
||||||
let row = [];
|
let row = [];
|
||||||
@ -240,7 +242,7 @@ class Level {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (let actor of this.actors) {
|
for (let actor of this.actors) {
|
||||||
if (actor.is_sliding) {
|
if (actor.slide_mode !== null) {
|
||||||
// TODO do we stop sliding if we hit something, too?
|
// TODO do we stop sliding if we hit something, too?
|
||||||
this.attempt_step(actor, actor.direction);
|
this.attempt_step(actor, actor.direction);
|
||||||
}
|
}
|
||||||
@ -258,6 +260,10 @@ class Level {
|
|||||||
|
|
||||||
for (let actor of this.actors) {
|
for (let actor of this.actors) {
|
||||||
// TODO skip doomed? strip them out? hm
|
// TODO skip doomed? strip them out? hm
|
||||||
|
if (actor.slide_mode === 'ice') {
|
||||||
|
// Actors can't make voluntary moves on ice
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (actor === this.player) {
|
if (actor === this.player) {
|
||||||
if (player_direction) {
|
if (player_direction) {
|
||||||
actor.direction = player_direction;
|
actor.direction = player_direction;
|
||||||
@ -310,12 +316,16 @@ class Level {
|
|||||||
}
|
}
|
||||||
blocks = true;
|
blocks = true;
|
||||||
// XXX should i break here, or bump everything?
|
// XXX should i break here, or bump everything?
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (blocks)
|
if (blocks) {
|
||||||
|
if (actor.slide_mode === 'ice') {
|
||||||
|
// Actors on ice turn around when they hit something
|
||||||
|
actor.direction = DIRECTIONS[DIRECTIONS[direction].left].left;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// We're clear!
|
// We're clear!
|
||||||
this.move_to(actor, goal_x, goal_y);
|
this.move_to(actor, goal_x, goal_y);
|
||||||
@ -329,7 +339,7 @@ class Level {
|
|||||||
let goal_cell = this.cells[y][x];
|
let goal_cell = this.cells[y][x];
|
||||||
let original_cell = this.cells[actor.y][actor.x];
|
let original_cell = this.cells[actor.y][actor.x];
|
||||||
original_cell._remove(actor);
|
original_cell._remove(actor);
|
||||||
actor.is_sliding = false;
|
actor.slide_mode = null;
|
||||||
goal_cell._add(actor);
|
goal_cell._add(actor);
|
||||||
actor.x = x;
|
actor.x = x;
|
||||||
actor.y = y;
|
actor.y = y;
|
||||||
@ -338,11 +348,19 @@ class Level {
|
|||||||
goal_cell.is_dirty = true;
|
goal_cell.is_dirty = true;
|
||||||
|
|
||||||
// Step on all the tiles in the new cell
|
// Step on all the tiles in the new cell
|
||||||
|
if (actor === this.player) {
|
||||||
|
this.hint_shown = null;
|
||||||
|
}
|
||||||
goal_cell.each(tile => {
|
goal_cell.each(tile => {
|
||||||
if (tile === actor)
|
if (tile === actor)
|
||||||
return;
|
return;
|
||||||
if (actor.ignores(tile.type.name))
|
if (actor.ignores(tile.type.name))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (actor === this.player && tile.type.name === 'hint') {
|
||||||
|
this.hint_shown = this.stored_level.hint;
|
||||||
|
}
|
||||||
|
|
||||||
if (tile.type.is_item && actor.type.has_inventory) {
|
if (tile.type.is_item && actor.type.has_inventory) {
|
||||||
actor.give_item(tile.type.name);
|
actor.give_item(tile.type.name);
|
||||||
tile.destroy();
|
tile.destroy();
|
||||||
@ -361,12 +379,20 @@ class Level {
|
|||||||
|
|
||||||
// TODO make a set of primitives for actually altering the level that also
|
// TODO make a set of primitives for actually altering the level that also
|
||||||
// record how to undo themselves
|
// record how to undo themselves
|
||||||
|
make_slide(actor, mode) {
|
||||||
|
actor.slide_mode = mode;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const GAME_UI_HTML = `
|
const GAME_UI_HTML = `
|
||||||
<main>
|
<main>
|
||||||
<div class="level"><!-- level canvas and any overlays go here --></div>
|
<div class="level"><!-- level canvas and any overlays go here --></div>
|
||||||
<div class="meta"></div>
|
<div class="meta"></div>
|
||||||
|
<div class="nav">
|
||||||
|
<button class="nav-prev" type="button">«</button>
|
||||||
|
<button class="nav-browse" type="button">Choose level...</button>
|
||||||
|
<button class="nav-next" type="button">»</button>
|
||||||
|
</div>
|
||||||
<div class="hint"></div>
|
<div class="hint"></div>
|
||||||
<div class="chips"></div>
|
<div class="chips"></div>
|
||||||
<div class="time"></div>
|
<div class="time"></div>
|
||||||
@ -375,7 +401,8 @@ const GAME_UI_HTML = `
|
|||||||
</main>
|
</main>
|
||||||
`;
|
`;
|
||||||
class Game {
|
class Game {
|
||||||
constructor(tileset, level) {
|
constructor(stored_game, tileset) {
|
||||||
|
this.stored_game = stored_game;
|
||||||
this.tileset = tileset;
|
this.tileset = tileset;
|
||||||
|
|
||||||
// TODO obey level options; allow overriding
|
// TODO obey level options; allow overriding
|
||||||
@ -386,13 +413,29 @@ class Game {
|
|||||||
this.container.innerHTML = GAME_UI_HTML;
|
this.container.innerHTML = GAME_UI_HTML;
|
||||||
this.level_el = this.container.querySelector('.level');
|
this.level_el = this.container.querySelector('.level');
|
||||||
this.meta_el = this.container.querySelector('.meta');
|
this.meta_el = this.container.querySelector('.meta');
|
||||||
|
this.nav_el = this.container.querySelector('.nav');
|
||||||
this.hint_el = this.container.querySelector('.hint');
|
this.hint_el = this.container.querySelector('.hint');
|
||||||
this.chips_el = this.container.querySelector('.chips');
|
this.chips_el = this.container.querySelector('.chips');
|
||||||
this.time_el = this.container.querySelector('.time');
|
this.time_el = this.container.querySelector('.time');
|
||||||
this.inventory_el = this.container.querySelector('.inventory');
|
this.inventory_el = this.container.querySelector('.inventory');
|
||||||
this.bummer_el = this.container.querySelector('.bummer');
|
this.bummer_el = this.container.querySelector('.bummer');
|
||||||
|
|
||||||
this.load_level(level);
|
this.nav_prev_button = this.nav_el.querySelector('.nav-prev');
|
||||||
|
this.nav_next_button = this.nav_el.querySelector('.nav-next');
|
||||||
|
this.nav_prev_button.addEventListener('click', ev => {
|
||||||
|
// TODO confirm
|
||||||
|
if (this.level_index > 0) {
|
||||||
|
this.load_level(this.level_index - 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.nav_next_button.addEventListener('click', ev => {
|
||||||
|
// TODO confirm
|
||||||
|
if (this.level_index < this.stored_game.levels.length - 1) {
|
||||||
|
this.load_level(this.level_index + 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.load_level(0);
|
||||||
|
|
||||||
this.level_canvas = mk('canvas', {width: tileset.size_x * this.camera_size_x, height: tileset.size_y * this.camera_size_y});
|
this.level_canvas = mk('canvas', {width: tileset.size_x * this.camera_size_x, height: tileset.size_y * this.camera_size_y});
|
||||||
this.level_el.append(this.level_canvas);
|
this.level_el.append(this.level_canvas);
|
||||||
@ -446,10 +489,14 @@ class Game {
|
|||||||
requestAnimationFrame(this.do_frame.bind(this));
|
requestAnimationFrame(this.do_frame.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
load_level(level) {
|
load_level(level_index) {
|
||||||
this.level = level;
|
this.level_index = level_index;
|
||||||
|
this.level = new Level(this.stored_game.levels[level_index]);
|
||||||
// FIXME do better
|
// FIXME do better
|
||||||
this.meta_el.textContent = this.level.stored_level.title;
|
this.meta_el.textContent = this.level.stored_level.title;
|
||||||
|
|
||||||
|
this.nav_prev_button.disabled = level_index <= 0;
|
||||||
|
this.nav_next_button.disabled = level_index >= this.stored_game.levels.length;
|
||||||
this.update_ui();
|
this.update_ui();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -476,7 +523,9 @@ class Game {
|
|||||||
}
|
}
|
||||||
|
|
||||||
update_ui() {
|
update_ui() {
|
||||||
|
// TODO can we do this only if they actually changed?
|
||||||
this.chips_el.textContent = this.level.chips_remaining;
|
this.chips_el.textContent = this.level.chips_remaining;
|
||||||
|
this.hint_el.textContent = this.level.hint_shown ?? '';
|
||||||
|
|
||||||
if (this.level.state === 'failure') {
|
if (this.level.state === 'failure') {
|
||||||
this.bummer_el.textContent = this.level.fail_message;
|
this.bummer_el.textContent = this.level.fail_message;
|
||||||
@ -555,8 +604,7 @@ async function main() {
|
|||||||
// TODO also support tile world's DAC when reading from local??
|
// TODO also support tile world's DAC when reading from local??
|
||||||
// TODO ah, there's more metadata in CCX, crapola
|
// TODO ah, there's more metadata in CCX, crapola
|
||||||
let stored_game = await load_game('levels/CCLP1.ccl');
|
let stored_game = await load_game('levels/CCLP1.ccl');
|
||||||
let level = new Level(stored_game.levels[0]);
|
let game = new Game(stored_game, tileset);
|
||||||
let game = new Game(tileset, level);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
main();
|
main();
|
||||||
|
|||||||
@ -62,18 +62,27 @@ export const CC2_TILESET_LAYOUT = {
|
|||||||
west: [[12, 7], [13, 7], [14, 7], [15, 7]],
|
west: [[12, 7], [13, 7], [14, 7], [15, 7]],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
ball: [[10, 10], [11, 10], [12, 10], [13, 10], [14, 10]],
|
||||||
|
|
||||||
|
fireball: [[12, 9], [13, 9], [14, 9], [15, 9]],
|
||||||
|
|
||||||
cleats: [2, 6],
|
cleats: [2, 6],
|
||||||
suction_boots: [3, 6],
|
suction_boots: [3, 6],
|
||||||
fire_boots: [1, 6],
|
fire_boots: [1, 6],
|
||||||
flippers: [0, 6],
|
flippers: [0, 6],
|
||||||
|
|
||||||
clue: [5, 2],
|
hint: [5, 2],
|
||||||
};
|
};
|
||||||
|
|
||||||
export const TILE_WORLD_TILESET_LAYOUT = {
|
export const TILE_WORLD_TILESET_LAYOUT = {
|
||||||
floor: [0, 0],
|
floor: [0, 0],
|
||||||
wall: [0, 1],
|
wall: [0, 1],
|
||||||
|
thinwall_n: [0, 6],
|
||||||
|
thinwall_w: [0, 7],
|
||||||
|
thinwall_s: [0, 8],
|
||||||
|
thinwall_e: [0, 9],
|
||||||
ice: [0, 12],
|
ice: [0, 12],
|
||||||
|
|
||||||
ice_sw: [1, 13],
|
ice_sw: [1, 13],
|
||||||
ice_nw: [1, 10],
|
ice_nw: [1, 10],
|
||||||
ice_ne: [1, 11],
|
ice_ne: [1, 11],
|
||||||
@ -84,6 +93,9 @@ export const TILE_WORLD_TILESET_LAYOUT = {
|
|||||||
force_floor_e: [1, 3],
|
force_floor_e: [1, 3],
|
||||||
force_floor_s: [0, 13],
|
force_floor_s: [0, 13],
|
||||||
force_floor_w: [1, 4],
|
force_floor_w: [1, 4],
|
||||||
|
// TODO there are two of these, which seems self-defeating??
|
||||||
|
fake_wall: [1, 14],
|
||||||
|
fake_floor: [1, 15],
|
||||||
|
|
||||||
exit: [[3, 10], [3, 11]],
|
exit: [[3, 10], [3, 11]],
|
||||||
|
|
||||||
@ -93,6 +105,7 @@ export const TILE_WORLD_TILESET_LAYOUT = {
|
|||||||
west: [6, 13],
|
west: [6, 13],
|
||||||
east: [6, 15],
|
east: [6, 15],
|
||||||
},
|
},
|
||||||
|
cloner: [3, 1],
|
||||||
player_drowned: [3, 3],
|
player_drowned: [3, 3],
|
||||||
player_burned: [3, 4],
|
player_burned: [3, 4],
|
||||||
// TODO the tileset has several of these...? why?
|
// TODO the tileset has several of these...? why?
|
||||||
@ -118,13 +131,62 @@ export const TILE_WORLD_TILESET_LAYOUT = {
|
|||||||
south: [4, 2],
|
south: [4, 2],
|
||||||
west: [4, 1],
|
west: [4, 1],
|
||||||
},
|
},
|
||||||
|
fireball: {
|
||||||
|
north: [4, 4],
|
||||||
|
east: [4, 7],
|
||||||
|
south: [4, 6],
|
||||||
|
west: [4, 5],
|
||||||
|
},
|
||||||
|
ball: {
|
||||||
|
north: [4, 8],
|
||||||
|
east: [4, 11],
|
||||||
|
south: [4, 10],
|
||||||
|
west: [4, 9],
|
||||||
|
},
|
||||||
|
tank_blue: {
|
||||||
|
north: [4, 12],
|
||||||
|
east: [4, 15],
|
||||||
|
south: [4, 14],
|
||||||
|
west: [4, 5],
|
||||||
|
},
|
||||||
|
glider: {
|
||||||
|
north: [5, 0],
|
||||||
|
east: [5, 3],
|
||||||
|
south: [5, 2],
|
||||||
|
west: [5, 1],
|
||||||
|
},
|
||||||
|
teeth: {
|
||||||
|
north: [5, 4],
|
||||||
|
east: [5, 7],
|
||||||
|
south: [5, 6],
|
||||||
|
west: [5, 5],
|
||||||
|
},
|
||||||
|
walker: {
|
||||||
|
north: [5, 8],
|
||||||
|
east: [5, 11],
|
||||||
|
south: [5, 10],
|
||||||
|
west: [5, 9],
|
||||||
|
},
|
||||||
|
blob: {
|
||||||
|
north: [5, 12],
|
||||||
|
east: [5, 15],
|
||||||
|
south: [5, 14],
|
||||||
|
west: [5, 5],
|
||||||
|
},
|
||||||
|
|
||||||
|
paramecium: {
|
||||||
|
north: [6, 0],
|
||||||
|
east: [6, 3],
|
||||||
|
south: [6, 2],
|
||||||
|
west: [6, 1],
|
||||||
|
},
|
||||||
|
|
||||||
cleats: [6, 10],
|
cleats: [6, 10],
|
||||||
suction_boots: [6, 11],
|
suction_boots: [6, 11],
|
||||||
fire_boots: [6, 9],
|
fire_boots: [6, 9],
|
||||||
flippers: [6, 8],
|
flippers: [6, 8],
|
||||||
|
|
||||||
clue: [2, 15],
|
hint: [2, 15],
|
||||||
};
|
};
|
||||||
|
|
||||||
export class Tileset {
|
export class Tileset {
|
||||||
|
|||||||
118
js/tiletypes.js
118
js/tiletypes.js
@ -1,4 +1,10 @@
|
|||||||
export const TILE_TYPES = {
|
export const TILE_TYPES = {
|
||||||
|
cloner: {
|
||||||
|
blocks: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
floor: {
|
floor: {
|
||||||
cc2_byte: 0x01,
|
cc2_byte: 0x01,
|
||||||
},
|
},
|
||||||
@ -6,8 +12,42 @@ export const TILE_TYPES = {
|
|||||||
cc2_byte: 0x02,
|
cc2_byte: 0x02,
|
||||||
blocks: true,
|
blocks: true,
|
||||||
},
|
},
|
||||||
|
wall_invisible: {
|
||||||
|
blocks: true,
|
||||||
|
},
|
||||||
|
wall_appearing: {
|
||||||
|
blocks: true,
|
||||||
|
},
|
||||||
|
thinwall_n: {
|
||||||
|
thin_walls: new Set(['north']),
|
||||||
|
},
|
||||||
|
thinwall_s: {
|
||||||
|
thin_walls: new Set(['south']),
|
||||||
|
},
|
||||||
|
thinwall_e: {
|
||||||
|
thin_walls: new Set(['east']),
|
||||||
|
},
|
||||||
|
thinwall_w: {
|
||||||
|
thin_walls: new Set(['west']),
|
||||||
|
},
|
||||||
|
fake_wall: {
|
||||||
|
blocks: true,
|
||||||
|
on_bump(me, level, other) {
|
||||||
|
me.become('wall');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fake_floor: {
|
||||||
|
blocks: true,
|
||||||
|
on_bump(me, level, other) {
|
||||||
|
me.become('floor');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
ice: {
|
ice: {
|
||||||
cc2_byte: 0x03,
|
cc2_byte: 0x03,
|
||||||
|
on_arrive(me, level, other) {
|
||||||
|
level.make_slide(other, 'ice');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
ice_sw: {
|
ice_sw: {
|
||||||
cc2_byte: 0x04,
|
cc2_byte: 0x04,
|
||||||
@ -15,6 +55,15 @@ export const TILE_TYPES = {
|
|||||||
south: true,
|
south: true,
|
||||||
west: true,
|
west: true,
|
||||||
},
|
},
|
||||||
|
on_arrive(me, level, other) {
|
||||||
|
if (other.direction === 'south') {
|
||||||
|
other.direction = 'east';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
other.direction = 'north';
|
||||||
|
}
|
||||||
|
level.make_slide(other, 'ice');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
ice_nw: {
|
ice_nw: {
|
||||||
cc2_byte: 0x05,
|
cc2_byte: 0x05,
|
||||||
@ -22,6 +71,15 @@ export const TILE_TYPES = {
|
|||||||
north: true,
|
north: true,
|
||||||
west: true,
|
west: true,
|
||||||
},
|
},
|
||||||
|
on_arrive(me, level, other) {
|
||||||
|
if (other.direction === 'north') {
|
||||||
|
other.direction = 'east';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
other.direction = 'south';
|
||||||
|
}
|
||||||
|
level.make_slide(other, 'ice');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
ice_ne: {
|
ice_ne: {
|
||||||
cc2_byte: 0x06,
|
cc2_byte: 0x06,
|
||||||
@ -29,6 +87,15 @@ export const TILE_TYPES = {
|
|||||||
north: true,
|
north: true,
|
||||||
east: true,
|
east: true,
|
||||||
},
|
},
|
||||||
|
on_arrive(me, level, other) {
|
||||||
|
if (other.direction === 'north') {
|
||||||
|
other.direction = 'west';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
other.direction = 'south';
|
||||||
|
}
|
||||||
|
level.make_slide(other, 'ice');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
ice_se: {
|
ice_se: {
|
||||||
cc2_byte: 0x07,
|
cc2_byte: 0x07,
|
||||||
@ -36,6 +103,15 @@ export const TILE_TYPES = {
|
|||||||
south: true,
|
south: true,
|
||||||
east: true,
|
east: true,
|
||||||
},
|
},
|
||||||
|
on_arrive(me, level, other) {
|
||||||
|
if (other.direction === 'south') {
|
||||||
|
other.direction = 'west';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
other.direction = 'north';
|
||||||
|
}
|
||||||
|
level.make_slide(other, 'ice');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
water: {
|
water: {
|
||||||
cc2_byte: 0x08,
|
cc2_byte: 0x08,
|
||||||
@ -70,28 +146,28 @@ export const TILE_TYPES = {
|
|||||||
cc2_byte: 0x0a,
|
cc2_byte: 0x0a,
|
||||||
on_arrive(me, level, other) {
|
on_arrive(me, level, other) {
|
||||||
other.direction = 'north';
|
other.direction = 'north';
|
||||||
other.is_sliding = true;
|
level.make_slide(other, 'push');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
force_floor_e: {
|
force_floor_e: {
|
||||||
cc2_byte: 0x0b,
|
cc2_byte: 0x0b,
|
||||||
on_arrive(me, level, other) {
|
on_arrive(me, level, other) {
|
||||||
other.direction = 'east';
|
other.direction = 'east';
|
||||||
other.is_sliding = true;
|
level.make_slide(other, 'push');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
force_floor_s: {
|
force_floor_s: {
|
||||||
cc2_byte: 0x0c,
|
cc2_byte: 0x0c,
|
||||||
on_arrive(me, level, other) {
|
on_arrive(me, level, other) {
|
||||||
other.direction = 'south';
|
other.direction = 'south';
|
||||||
other.is_sliding = true;
|
level.make_slide(other, 'push');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
force_floor_w: {
|
force_floor_w: {
|
||||||
cc2_byte: 0x0d,
|
cc2_byte: 0x0d,
|
||||||
on_arrive(me, level, other) {
|
on_arrive(me, level, other) {
|
||||||
other.direction = 'west';
|
other.direction = 'west';
|
||||||
other.is_sliding = true;
|
level.make_slide(other, 'push');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -222,6 +298,36 @@ export const TILE_TYPES = {
|
|||||||
has_direction: true,
|
has_direction: true,
|
||||||
is_top_layer: true,
|
is_top_layer: true,
|
||||||
},
|
},
|
||||||
|
paramecium: {
|
||||||
|
cc2_byte: 0x34,
|
||||||
|
is_actor: true,
|
||||||
|
has_direction: true,
|
||||||
|
is_top_layer: true,
|
||||||
|
},
|
||||||
|
ball: {
|
||||||
|
cc2_byte: 0x35,
|
||||||
|
is_actor: true,
|
||||||
|
has_direction: true,
|
||||||
|
is_top_layer: true,
|
||||||
|
},
|
||||||
|
blob: {
|
||||||
|
cc2_byte: 0x36,
|
||||||
|
is_actor: true,
|
||||||
|
has_direction: true,
|
||||||
|
is_top_layer: true,
|
||||||
|
},
|
||||||
|
teeth: {
|
||||||
|
cc2_byte: 0x37,
|
||||||
|
is_actor: true,
|
||||||
|
has_direction: true,
|
||||||
|
is_top_layer: true,
|
||||||
|
},
|
||||||
|
fireball: {
|
||||||
|
cc2_byte: 0x38,
|
||||||
|
is_actor: true,
|
||||||
|
has_direction: true,
|
||||||
|
is_top_layer: true,
|
||||||
|
},
|
||||||
|
|
||||||
cleats: {
|
cleats: {
|
||||||
cc2_byte: 0x3b,
|
cc2_byte: 0x3b,
|
||||||
@ -253,7 +359,7 @@ export const TILE_TYPES = {
|
|||||||
item_ignores: new Set(['water']),
|
item_ignores: new Set(['water']),
|
||||||
},
|
},
|
||||||
|
|
||||||
clue: {
|
hint: {
|
||||||
cc2_byte: 0x45,
|
cc2_byte: 0x45,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -264,7 +370,7 @@ CC2_TILE_TYPES.fill(null);
|
|||||||
for (let [name, tiledef] of Object.entries(TILE_TYPES)) {
|
for (let [name, tiledef] of Object.entries(TILE_TYPES)) {
|
||||||
tiledef.name = name;
|
tiledef.name = name;
|
||||||
|
|
||||||
if (tiledef.cc2_byte === null)
|
if (tiledef.cc2_byte === null || tiledef.cc2_byte === undefined)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
let existing = CC2_TILE_TYPES[tiledef.cc2_byte];
|
let existing = CC2_TILE_TYPES[tiledef.cc2_byte];
|
||||||
|
|||||||
@ -16,6 +16,7 @@ main {
|
|||||||
display: grid;
|
display: grid;
|
||||||
grid:
|
grid:
|
||||||
"level meta" min-content
|
"level meta" min-content
|
||||||
|
"level nav" min-content
|
||||||
"level chips" min-content
|
"level chips" min-content
|
||||||
"level time" min-content
|
"level time" min-content
|
||||||
"level hint" 1fr
|
"level hint" 1fr
|
||||||
@ -43,6 +44,14 @@ main {
|
|||||||
background: black;
|
background: black;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
.nav {
|
||||||
|
grid-area: nav;
|
||||||
|
display: flex;
|
||||||
|
gap: 1em;
|
||||||
|
}
|
||||||
|
.nav .nav-browse {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
.chips {
|
.chips {
|
||||||
grid-area: chips;
|
grid-area: chips;
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user