Editor: More tiles; more metadata; save hints, more or less

This commit is contained in:
Eevee (Evelyn Woods) 2020-12-03 21:19:47 -07:00
parent 7a710ee5dc
commit 87ac6f94a3
4 changed files with 120 additions and 11 deletions

View File

@ -8,6 +8,7 @@ export class StoredLevel {
// TODO still not sure this belongs here
this.number = number; // one-based
this.title = '';
this.author = '';
this.password = null;
this.hint = '';
this.chips_required = 0;

View File

@ -843,6 +843,7 @@ export function parse_level(buf, number = 1) {
level.time_limit = view.getUint16(0, true);
// TODO 0 - 10x10, 1 - 9x9, 2 - split, otherwise unknown which needs handling
// FIXME does this default to 0 if no OPTN block is present?
let viewport = view.getUint8(2, true);
if (viewport === 0) {
level.viewport_size = 10;
@ -1219,6 +1220,22 @@ export function synthesize_level(stored_level) {
if (stored_level.title) {
c2m.add_section('TITL', stored_level.title);
}
if (stored_level.author) {
c2m.add_section('AUTH', stored_level.author);
}
// Options block
let options = new Uint8Array(3);
new DataView(options.buffer).setUint16(0, stored_level.time_limit, true);
if (stored_level.viewport_size === 10) {
options[2] = 0;
}
else if (stored_level.viewport_size === 9) {
options[2] = 1;
}
// TODO split
// TODO for size purposes, omit the block entirely if all options are defaults?
c2m.add_section('OPTN', options);
// Store camera regions
// TODO LL feature, should be distinguished somehow
@ -1240,6 +1257,7 @@ export function synthesize_level(stored_level) {
let map_view = new DataView(map_bytes.buffer);
map_bytes[0] = stored_level.size_x;
map_bytes[1] = stored_level.size_y;
let hints = [];
let p = 2;
for (let cell of stored_level.linear_cells) {
for (let i = cell.length - 1; i >= 0; i--) {
@ -1283,10 +1301,20 @@ export function synthesize_level(stored_level) {
}
}
if (tile.type.name === 'hint') {
hints.push(tile.hint_text);
}
// TODO assert that the bottom tile has no next, and all the others do
}
}
// Collect hints first so we can put them in the comment field
// FIXME this does not respect global hint, but then, neither does the editor.
hints = hints.map(hint => hint ?? '');
hints.push('');
c2m.add_section('NOTE', hints.join('\n[CLUE]\n'));
// FIXME ack, ArrayBuffer.slice makes a copy actually! and i use it a lot in this file i think!!
let map_buf = map_bytes.buffer.slice(0, p);
let compressed_map = compress(map_buf);

View File

@ -30,9 +30,18 @@ class EditorLevelMetaOverlay extends DialogOverlay {
update_time_limit();
time_limit_input.addEventListener('input', update_time_limit);
let make_radio_set = (name, options) => {
let elements = [];
for (let [label, value] of options) {
elements.push();
}
};
dl.append(
mk('dt', "Title"),
mk('dd', mk('input', {name: 'title', type: 'text', value: stored_level.title})),
mk('dt', "Author"),
mk('dd', mk('input', {name: 'author', type: 'text', value: stored_level.author})),
mk('dt', "Time limit"),
mk('dd', time_limit_input, " ", time_limit_output),
mk('dt', "Size"),
@ -43,7 +52,37 @@ class EditorLevelMetaOverlay extends DialogOverlay {
"Height: ",
mk('input', {name: 'size_y', type: 'number', min: 10, max: 100, value: stored_level.size_y}),
),
mk('dt', "Viewport"),
mk('dd',
mk('label',
mk('input', {name: 'viewport', type: 'radio', value: '10'}),
" 10×10 (Chip's Challenge 2 size)"),
mk('br'),
mk('label',
mk('input', {name: 'viewport', type: 'radio', value: '9'}),
" 9×9 (Chip's Challenge 1 size)"),
mk('br'),
mk('label',
mk('input', {name: 'viewport', type: 'radio', value: '', disabled: 'disabled'}),
" Split 10×10 (not yet supported)"),
),
mk('dt', "Blob behavior"),
mk('dd',
mk('label',
mk('input', {name: 'blob_behavior', type: 'radio', value: '0'}),
" Deterministic (PRNG + simple convolution)"),
mk('br'),
mk('label',
mk('input', {name: 'blob_behavior', type: 'radio', value: '1'}),
" 4 patterns (default; PRNG + rotating offset)"),
mk('br'),
mk('label',
mk('input', {name: 'blob_behavior', type: 'radio', value: '2'}),
" Extra random (initial seed is truly random)"),
),
);
this.root.elements['viewport'].value = stored_level.viewport_size;
this.root.elements['blob_behavior'].value = stored_level.blob_behavior;
// TODO:
// - author
// - chips?
@ -64,6 +103,10 @@ class EditorLevelMetaOverlay extends DialogOverlay {
stored_level.title = title;
this.conductor.update_level_title();
}
let author = els.author.value;
if (author !== stored_level.author) {
stored_level.author = author;
}
stored_level.time_limit = parseInt(els.time_limit.value, 10);
@ -73,6 +116,10 @@ class EditorLevelMetaOverlay extends DialogOverlay {
this.conductor.editor.resize_level(size_x, size_y);
}
stored_level.blob_behavior = parseInt(els.blob_behavior.value, 10);
stored_level.viewport_size = parseInt(els.viewport.value, 10);
this.conductor.player.update_viewport_size();
this.close();
});
this.footer.append(ok);
@ -409,8 +456,8 @@ const ADJUST_TOGGLES_CCW = {};
['thief_keys', 'thief_tools'],
['swivel_nw', 'swivel_ne', 'swivel_se', 'swivel_sw'],
['ice_nw', 'ice_ne', 'ice_se', 'ice_sw'],
['ff_north', 'ff_east', 'ff_south', 'ff_west'],
['ice', 'ff_all'],
['force_floor_n', 'force_floor_e', 'force_floor_s', 'force_floor_w'],
['ice', 'force_floor_all'],
['water', 'turtle'],
['no_player1_sign', 'no_player2_sign'],
['flame_jet_off', 'flame_jet_on'],
@ -743,11 +790,22 @@ const EDITOR_PALETTE = [{
title: "Terrain",
tiles: [
'popwall',
'fake_floor', 'fake_wall',
'wall_invisible', 'wall_appearing',
'steel',
'wall_invisible',
'wall_appearing',
'fake_floor',
'fake_wall',
'popdown_floor',
'popdown_wall',
'floor_letter',
'gravel',
'dirt',
'dirt',
'slime',
'thief_keys',
'thief_tools',
'no_player1_sign',
'no_player2_sign',
'floor_custom_green', 'floor_custom_pink', 'floor_custom_yellow', 'floor_custom_blue',
'wall_custom_green', 'wall_custom_pink', 'wall_custom_yellow', 'wall_custom_blue',
@ -765,36 +823,52 @@ const EDITOR_PALETTE = [{
'bribe', 'railroad_sign', 'hiking_boots', 'speed_boots',
'xray_eye', 'helmet', 'foil', 'lightning_bolt',
'bowling_ball', 'dynamite', 'no_sign', 'bestowal_bow',
'score_10', 'score_100', 'score_1000', 'score_2x',
],
}, {
title: "Creatures",
tiles: [
'tank_blue',
'tank_yellow',
'ball',
'walker',
'fireball',
'glider',
'bug',
'paramecium',
'walker',
'teeth',
'blob',
'teeth',
],
}, {
title: "Mechanisms",
tiles: [
'bomb',
'dirt_block',
'ice_block',
/*
* FIXME this won't work for all kinds of reasons
{ name: 'directional_block', arrows: new Set },
{ name: 'directional_block', arrows: new Set(['north']) },
{ name: 'directional_block', arrows: new Set(['north', 'east']) },
{ name: 'directional_block', arrows: new Set(['north', 'south']) },
{ name: 'directional_block', arrows: new Set(['north', 'east', 'south']) },
{ name: 'directional_block', arrows: new Set(['north', 'east', 'south', 'west']) },
*/
'bomb',
'button_gray',
'button_green',
'green_floor',
'green_wall',
'green_chip',
'green_bomb',
'button_yellow',
'button_blue',
'button_red', 'cloner',
'button_brown', 'trap',
'button_orange', 'flame_jet_off', 'flame_jet_on',
'button_pink',
'button_black',
'purple_floor',
'purple_wall',
'teleport_blue',
'teleport_red',
'teleport_green',
@ -1025,6 +1099,7 @@ export class Editor extends PrimaryView {
let stored_level = new format_base.StoredLevel(number);
stored_level.size_x = size_x;
stored_level.size_y = size_y;
stored_level.viewport_size = 10;
for (let i = 0; i < size_x * size_y; i++) {
stored_level.linear_cells.push(this._make_cell());
}

View File

@ -676,15 +676,20 @@ class Player extends PrimaryView {
this.level = new Level(stored_level, this.gather_compat_options(stored_level));
this.level.sfx = this.sfx_player;
this.renderer.set_level(this.level);
this.renderer.set_viewport_size(stored_level.viewport_size, stored_level.viewport_size);
this.renderer.canvas.style.setProperty('--viewport-width', stored_level.viewport_size);
this.renderer.canvas.style.setProperty('--viewport-height', stored_level.viewport_size);
this.update_viewport_size();
this.root.classList.toggle('--has-demo', !!this.level.stored_level.demo);
// TODO base this on a hash of the UA + some identifier for the pack + the level index. StoredLevel doesn't know its own index atm...
this.change_music(this.conductor.level_index % SOUNDTRACK.length);
this._clear_state();
}
update_viewport_size() {
let size = this.conductor.stored_level.viewport_size;
this.renderer.set_viewport_size(size, size);
this.renderer.canvas.style.setProperty('--viewport-width', size);
this.renderer.canvas.style.setProperty('--viewport-height', size);
}
restart_level() {
this.level.restart(this.gather_compat_options(this.level.stored_level));
this._clear_state();