Support CC2's multiple hints

This commit is contained in:
Eevee (Evelyn Woods) 2020-08-29 00:58:04 -06:00
parent a50de91195
commit a9b1af8e62
3 changed files with 68 additions and 23 deletions

View File

@ -194,6 +194,8 @@ export function parse_level(buf) {
let level = new util.StoredLevel; let level = new util.StoredLevel;
let full_view = new DataView(buf); let full_view = new DataView(buf);
let next_section_start = 0; let next_section_start = 0;
let extra_hints;
let hint_tiles = [];
while (next_section_start < buf.byteLength) { while (next_section_start < buf.byteLength) {
// Read section header and length // Read section header and length
let section_start = next_section_start; let section_start = next_section_start;
@ -203,23 +205,43 @@ export function parse_level(buf) {
if (next_section_start > buf.byteLength) if (next_section_start > buf.byteLength)
throw new Error(`Section at byte ${section_start} of type '${section_type}' extends ${buf.length - next_section_start} bytes past the end of the file`); throw new Error(`Section at byte ${section_start} of type '${section_type}' extends ${buf.length - next_section_start} bytes past the end of the file`);
if (section_type === 'CC2M' || section_type === 'LOCK' || section_type === 'TITL' || section_type === 'AUTH' || section_type === 'VERS' || section_type === 'CLUE' || section_type === 'NOTE') { if (section_type === 'CC2M' || section_type === 'LOCK' || section_type === 'VERS' ||
section_type === 'TITL' || section_type === 'AUTH' ||
section_type === 'CLUE' || section_type === 'NOTE')
{
// These are all singular strings (with a terminating NUL, for some reason) // These are all singular strings (with a terminating NUL, for some reason)
// XXX character encoding?? // XXX character encoding??
// FIXME assign to appropriate fields let str = util.string_from_buffer_ascii(buf.slice(section_start + 8, next_section_start - 1)).replace(/\r\n/g, "\n");
let field = section_type;
if (section_type === 'TITL') { // TODO store more of this, at least for idempotence, maybe
field = 'title'; if (section_type === 'CC2M') {
// File version, doesn't seem interesting
}
else if (section_type === 'LOCK') {
// Unclear, seems to be a comment about the editor...?
}
else if (section_type === 'VERS') {
// Editor version which created this level
}
else if (section_type === 'TITL') {
// Level title
level.title = str;
} }
else if (section_type === 'AUTH') { else if (section_type === 'AUTH') {
field = 'author'; // Author's name
level.author = str;
} }
/*
else if (section_type === 'CLUE') { else if (section_type === 'CLUE') {
field = 'hint'; // Level hint
level.hint = str;
}
else if (section_type === 'NOTE') {
// Author's comments... but might also include multiple hints
// for levels with multiple hint tiles, delineated by [CLUE].
// For my purposes, extra hints are associated with the
// individual tiles, so we'll map those later
[level.comment, ...extra_hints] = str.split(/(?<=^|\n)\[CLUE\]\n/g);
} }
*/
level[field] = util.string_from_buffer_ascii(buf.slice(section_start + 8, next_section_start - 1)).replace(/\r\n/g, "\n");
continue; continue;
} }
@ -311,16 +333,23 @@ export function parse_level(buf) {
} }
let tile = {name}; let tile = {name};
cell.push(tile); cell.push(tile);
let tiledef = TILE_TYPES[name]; let type = TILE_TYPES[name];
if (!tiledef) console.error(name); if (!type) console.error(name);
if (tiledef.is_required_chip) { if (type.is_required_chip) {
level.chips_required++; level.chips_required++;
} }
if (tiledef.is_player) { if (type.is_player) {
// TODO handle multiple starts // TODO handle multiple starts
level.player_start_x = n % width; level.player_start_x = n % width;
level.player_start_y = Math.floor(n / width); level.player_start_y = Math.floor(n / width);
} }
if (type.is_hint) {
// Remember all the hint tiles (in reading order) so we
// can map extra hints to them later. Don't do it now,
// since the format doesn't technically guarantee that
// the metadata sections appear before the map data!
hint_tiles.push(tile);
}
// Handle extra arguments // Handle extra arguments
let has_next = false; let has_next = false;
@ -361,6 +390,16 @@ export function parse_level(buf) {
// TODO save it, persist when editing level // TODO save it, persist when editing level
} }
} }
console.log(level);
// Connect extra hints
let h = 0;
for (let tile of hint_tiles) {
if (h > extra_hints.length)
break;
tile.specific_hint = extra_hints[h];
h++;
}
return level; return level;
} }

View File

@ -82,8 +82,9 @@ class Tile {
} }
static from_template(tile_template, x, y) { static from_template(tile_template, x, y) {
if (! TILE_TYPES[tile_template.name]) console.error(tile_template.name); let type = TILE_TYPES[tile_template.name];
return new this(TILE_TYPES[tile_template.name], x, y, tile_template.direction); if (! type) console.error(tile_template.name);
return new this(type, x, y, tile_template.direction);
} }
ignores(name) { ignores(name) {
@ -231,16 +232,20 @@ class Level {
let cell = new Cell; let cell = new Cell;
row.push(cell); row.push(cell);
let template_cell = this.stored_level.linear_cells[n]; let stored_cell = this.stored_level.linear_cells[n];
n++; n++;
for (let template_tile of template_cell) { for (let template_tile of stored_cell) {
let tile = Tile.from_template(template_tile, x, y); let tile = Tile.from_template(template_tile, x, y);
if (tile.type.is_player) { if (tile.type.is_player) {
// TODO handle multiple players, also chip and melinda both // TODO handle multiple players, also chip and melinda both
// TODO complain if no chip // TODO complain if no chip
this.player = tile; this.player = tile;
} }
if (tile.type.is_hint) {
// Copy over the tile-specific hint, if any
tile.specific_hint = template_tile.specific_hint ?? null;
}
if (tile.type.is_actor) { if (tile.type.is_actor) {
this.actors.push(tile); this.actors.push(tile);
} }
@ -436,8 +441,8 @@ class Level {
if (actor.ignores(tile.type.name)) if (actor.ignores(tile.type.name))
return; return;
if (actor === this.player && tile.type.name === 'hint') { if (actor === this.player && tile.type.is_hint) {
this.hint_shown = this.stored_level.hint; this.hint_shown = tile.specific_hint ?? this.stored_level.hint;
} }
if (tile.type.is_item && actor.type.has_inventory) { if (tile.type.is_item && actor.type.has_inventory) {
@ -485,8 +490,8 @@ class Game {
this.tileset = tileset; this.tileset = tileset;
// TODO obey level options; allow overriding // TODO obey level options; allow overriding
this.viewport_size_x = 19; this.viewport_size_x = 9;
this.viewport_size_y = 19; this.viewport_size_y = 9;
document.body.innerHTML = GAME_UI_HTML; document.body.innerHTML = GAME_UI_HTML;
this.container = document.body.querySelector('main'); this.container = document.body.querySelector('main');

View File

@ -392,6 +392,7 @@ const TILE_TYPES = {
}, },
hint: { hint: {
is_hint: true,
}, },
socket: { socket: {
blocks: true, blocks: true,