Editor: More tiles; more metadata; save hints, more or less
This commit is contained in:
parent
7a710ee5dc
commit
87ac6f94a3
@ -8,6 +8,7 @@ export class StoredLevel {
|
|||||||
// TODO still not sure this belongs here
|
// TODO still not sure this belongs here
|
||||||
this.number = number; // one-based
|
this.number = number; // one-based
|
||||||
this.title = '';
|
this.title = '';
|
||||||
|
this.author = '';
|
||||||
this.password = null;
|
this.password = null;
|
||||||
this.hint = '';
|
this.hint = '';
|
||||||
this.chips_required = 0;
|
this.chips_required = 0;
|
||||||
|
|||||||
@ -843,6 +843,7 @@ export function parse_level(buf, number = 1) {
|
|||||||
level.time_limit = view.getUint16(0, true);
|
level.time_limit = view.getUint16(0, true);
|
||||||
|
|
||||||
// TODO 0 - 10x10, 1 - 9x9, 2 - split, otherwise unknown which needs handling
|
// 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);
|
let viewport = view.getUint8(2, true);
|
||||||
if (viewport === 0) {
|
if (viewport === 0) {
|
||||||
level.viewport_size = 10;
|
level.viewport_size = 10;
|
||||||
@ -1219,6 +1220,22 @@ export function synthesize_level(stored_level) {
|
|||||||
if (stored_level.title) {
|
if (stored_level.title) {
|
||||||
c2m.add_section('TITL', 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
|
// Store camera regions
|
||||||
// TODO LL feature, should be distinguished somehow
|
// 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);
|
let map_view = new DataView(map_bytes.buffer);
|
||||||
map_bytes[0] = stored_level.size_x;
|
map_bytes[0] = stored_level.size_x;
|
||||||
map_bytes[1] = stored_level.size_y;
|
map_bytes[1] = stored_level.size_y;
|
||||||
|
let hints = [];
|
||||||
let p = 2;
|
let p = 2;
|
||||||
for (let cell of stored_level.linear_cells) {
|
for (let cell of stored_level.linear_cells) {
|
||||||
for (let i = cell.length - 1; i >= 0; i--) {
|
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
|
// 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!!
|
// 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 map_buf = map_bytes.buffer.slice(0, p);
|
||||||
let compressed_map = compress(map_buf);
|
let compressed_map = compress(map_buf);
|
||||||
|
|||||||
@ -30,9 +30,18 @@ class EditorLevelMetaOverlay extends DialogOverlay {
|
|||||||
update_time_limit();
|
update_time_limit();
|
||||||
time_limit_input.addEventListener('input', 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(
|
dl.append(
|
||||||
mk('dt', "Title"),
|
mk('dt', "Title"),
|
||||||
mk('dd', mk('input', {name: 'title', type: 'text', value: stored_level.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('dt', "Time limit"),
|
||||||
mk('dd', time_limit_input, " ", time_limit_output),
|
mk('dd', time_limit_input, " ", time_limit_output),
|
||||||
mk('dt', "Size"),
|
mk('dt', "Size"),
|
||||||
@ -43,7 +52,37 @@ class EditorLevelMetaOverlay extends DialogOverlay {
|
|||||||
"Height: ",
|
"Height: ",
|
||||||
mk('input', {name: 'size_y', type: 'number', min: 10, max: 100, value: stored_level.size_y}),
|
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:
|
// TODO:
|
||||||
// - author
|
// - author
|
||||||
// - chips?
|
// - chips?
|
||||||
@ -64,6 +103,10 @@ class EditorLevelMetaOverlay extends DialogOverlay {
|
|||||||
stored_level.title = title;
|
stored_level.title = title;
|
||||||
this.conductor.update_level_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);
|
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);
|
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.close();
|
||||||
});
|
});
|
||||||
this.footer.append(ok);
|
this.footer.append(ok);
|
||||||
@ -409,8 +456,8 @@ const ADJUST_TOGGLES_CCW = {};
|
|||||||
['thief_keys', 'thief_tools'],
|
['thief_keys', 'thief_tools'],
|
||||||
['swivel_nw', 'swivel_ne', 'swivel_se', 'swivel_sw'],
|
['swivel_nw', 'swivel_ne', 'swivel_se', 'swivel_sw'],
|
||||||
['ice_nw', 'ice_ne', 'ice_se', 'ice_sw'],
|
['ice_nw', 'ice_ne', 'ice_se', 'ice_sw'],
|
||||||
['ff_north', 'ff_east', 'ff_south', 'ff_west'],
|
['force_floor_n', 'force_floor_e', 'force_floor_s', 'force_floor_w'],
|
||||||
['ice', 'ff_all'],
|
['ice', 'force_floor_all'],
|
||||||
['water', 'turtle'],
|
['water', 'turtle'],
|
||||||
['no_player1_sign', 'no_player2_sign'],
|
['no_player1_sign', 'no_player2_sign'],
|
||||||
['flame_jet_off', 'flame_jet_on'],
|
['flame_jet_off', 'flame_jet_on'],
|
||||||
@ -743,11 +790,22 @@ const EDITOR_PALETTE = [{
|
|||||||
title: "Terrain",
|
title: "Terrain",
|
||||||
tiles: [
|
tiles: [
|
||||||
'popwall',
|
'popwall',
|
||||||
'fake_floor', 'fake_wall',
|
'steel',
|
||||||
'wall_invisible', 'wall_appearing',
|
'wall_invisible',
|
||||||
|
'wall_appearing',
|
||||||
|
'fake_floor',
|
||||||
|
'fake_wall',
|
||||||
|
'popdown_floor',
|
||||||
|
'popdown_wall',
|
||||||
|
|
||||||
|
'floor_letter',
|
||||||
'gravel',
|
'gravel',
|
||||||
'dirt',
|
'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',
|
'floor_custom_green', 'floor_custom_pink', 'floor_custom_yellow', 'floor_custom_blue',
|
||||||
'wall_custom_green', 'wall_custom_pink', 'wall_custom_yellow', 'wall_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',
|
'bribe', 'railroad_sign', 'hiking_boots', 'speed_boots',
|
||||||
'xray_eye', 'helmet', 'foil', 'lightning_bolt',
|
'xray_eye', 'helmet', 'foil', 'lightning_bolt',
|
||||||
'bowling_ball', 'dynamite', 'no_sign', 'bestowal_bow',
|
'bowling_ball', 'dynamite', 'no_sign', 'bestowal_bow',
|
||||||
|
'score_10', 'score_100', 'score_1000', 'score_2x',
|
||||||
],
|
],
|
||||||
}, {
|
}, {
|
||||||
title: "Creatures",
|
title: "Creatures",
|
||||||
tiles: [
|
tiles: [
|
||||||
'tank_blue',
|
'tank_blue',
|
||||||
|
'tank_yellow',
|
||||||
'ball',
|
'ball',
|
||||||
|
'walker',
|
||||||
'fireball',
|
'fireball',
|
||||||
'glider',
|
'glider',
|
||||||
'bug',
|
'bug',
|
||||||
'paramecium',
|
'paramecium',
|
||||||
'walker',
|
|
||||||
'teeth',
|
|
||||||
'blob',
|
'blob',
|
||||||
|
'teeth',
|
||||||
],
|
],
|
||||||
}, {
|
}, {
|
||||||
title: "Mechanisms",
|
title: "Mechanisms",
|
||||||
tiles: [
|
tiles: [
|
||||||
'bomb',
|
|
||||||
'dirt_block',
|
'dirt_block',
|
||||||
'ice_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_gray',
|
||||||
'button_green',
|
'button_green',
|
||||||
'green_floor',
|
'green_floor',
|
||||||
'green_wall',
|
'green_wall',
|
||||||
'green_chip',
|
'green_chip',
|
||||||
'green_bomb',
|
'green_bomb',
|
||||||
|
'button_yellow',
|
||||||
'button_blue',
|
'button_blue',
|
||||||
'button_red', 'cloner',
|
'button_red', 'cloner',
|
||||||
'button_brown', 'trap',
|
'button_brown', 'trap',
|
||||||
'button_orange', 'flame_jet_off', 'flame_jet_on',
|
'button_orange', 'flame_jet_off', 'flame_jet_on',
|
||||||
|
'button_pink',
|
||||||
|
'button_black',
|
||||||
|
'purple_floor',
|
||||||
|
'purple_wall',
|
||||||
'teleport_blue',
|
'teleport_blue',
|
||||||
'teleport_red',
|
'teleport_red',
|
||||||
'teleport_green',
|
'teleport_green',
|
||||||
@ -1025,6 +1099,7 @@ export class Editor extends PrimaryView {
|
|||||||
let stored_level = new format_base.StoredLevel(number);
|
let stored_level = new format_base.StoredLevel(number);
|
||||||
stored_level.size_x = size_x;
|
stored_level.size_x = size_x;
|
||||||
stored_level.size_y = size_y;
|
stored_level.size_y = size_y;
|
||||||
|
stored_level.viewport_size = 10;
|
||||||
for (let i = 0; i < size_x * size_y; i++) {
|
for (let i = 0; i < size_x * size_y; i++) {
|
||||||
stored_level.linear_cells.push(this._make_cell());
|
stored_level.linear_cells.push(this._make_cell());
|
||||||
}
|
}
|
||||||
|
|||||||
11
js/main.js
11
js/main.js
@ -676,15 +676,20 @@ class Player extends PrimaryView {
|
|||||||
this.level = new Level(stored_level, this.gather_compat_options(stored_level));
|
this.level = new Level(stored_level, this.gather_compat_options(stored_level));
|
||||||
this.level.sfx = this.sfx_player;
|
this.level.sfx = this.sfx_player;
|
||||||
this.renderer.set_level(this.level);
|
this.renderer.set_level(this.level);
|
||||||
this.renderer.set_viewport_size(stored_level.viewport_size, stored_level.viewport_size);
|
this.update_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.root.classList.toggle('--has-demo', !!this.level.stored_level.demo);
|
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...
|
// 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.change_music(this.conductor.level_index % SOUNDTRACK.length);
|
||||||
this._clear_state();
|
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() {
|
restart_level() {
|
||||||
this.level.restart(this.gather_compat_options(this.level.stored_level));
|
this.level.restart(this.gather_compat_options(this.level.stored_level));
|
||||||
this._clear_state();
|
this._clear_state();
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user