Add support for encoding thin walls/canopies; add them to the editor; add support for additive drawing
This commit is contained in:
parent
b9a311a18c
commit
746300a514
@ -1391,11 +1391,47 @@ export function synthesize_level(stored_level) {
|
|||||||
|
|
||||||
// TODO complain if duplicates on a layer
|
// TODO complain if duplicates on a layer
|
||||||
let dummy_terrain_tile = null;
|
let dummy_terrain_tile = null;
|
||||||
|
let handled_thin_walls = false;
|
||||||
|
// FIXME sort first, otherwise the canopy assumption that thin walls are immediately below
|
||||||
|
// breaks! maybe just fix that also
|
||||||
for (let i = cell.length - 1; i >= 0; i--) {
|
for (let i = cell.length - 1; i >= 0; i--) {
|
||||||
let tile = cell[i];
|
let tile = cell[i];
|
||||||
// FIXME does not yet support canopy or thin walls >:S
|
// FIXME does not yet support canopy or thin walls >:S
|
||||||
let spec = REVERSE_TILE_ENCODING[tile.type.name];
|
let spec = REVERSE_TILE_ENCODING[tile.type.name];
|
||||||
|
|
||||||
|
if (tile.type.name === 'canopy' || tile.type.name === 'thin_walls') {
|
||||||
|
// These two tiles are encoded together despite being on different layers. If we
|
||||||
|
// see the canopy first, then find the thin wall tile (if any) and set a flag so we
|
||||||
|
// don't try to encode it again
|
||||||
|
if (handled_thin_walls)
|
||||||
|
continue;
|
||||||
|
handled_thin_walls = true;
|
||||||
|
|
||||||
|
let canopy, thin_walls;
|
||||||
|
if (tile.type.name === 'canopy') {
|
||||||
|
canopy = tile;
|
||||||
|
if (i > 0 && cell[i - 1].type.name === 'thin_walls') {
|
||||||
|
thin_walls = cell[i - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
thin_walls = tile;
|
||||||
|
}
|
||||||
|
|
||||||
|
let arg = 0;
|
||||||
|
if (canopy) {
|
||||||
|
arg |= 0x10;
|
||||||
|
}
|
||||||
|
if (thin_walls) {
|
||||||
|
arg |= thin_walls.edges;
|
||||||
|
}
|
||||||
|
|
||||||
|
map_bytes[p] = REVERSE_TILE_ENCODING['#thinwall/canopy'].tile_byte;
|
||||||
|
map_bytes[p + 1] = arg;
|
||||||
|
p += 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Handle the swivel, a tile that draws as an overlay but is stored like terrain. In a
|
// Handle the swivel, a tile that draws as an overlay but is stored like terrain. In a
|
||||||
// level, it has two parts: the swivel itself, and a dummy swivel_floor terrain which is
|
// level, it has two parts: the swivel itself, and a dummy swivel_floor terrain which is
|
||||||
// unencodable. To encode that, we notice when we hit a swivel (which happens first),
|
// unencodable. To encode that, we notice when we hit a swivel (which happens first),
|
||||||
|
|||||||
@ -423,18 +423,7 @@ class PencilOperation extends DrawOperation {
|
|||||||
}
|
}
|
||||||
else if (template) {
|
else if (template) {
|
||||||
// Erase whatever's on the same layer
|
// Erase whatever's on the same layer
|
||||||
// TODO this seems like the wrong place for this
|
this.editor.erase_tile(cell);
|
||||||
let layer = template.type.draw_layer;
|
|
||||||
for (let i = cell.length - 1; i >= 0; i--) {
|
|
||||||
if (cell[i].type.draw_layer === layer) {
|
|
||||||
cell.splice(i, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Don't allow erasing the floor entirely
|
|
||||||
if (layer === 0) {
|
|
||||||
cell.unshift({type: TILE_TYPES.floor});
|
|
||||||
}
|
|
||||||
this.editor.mark_cell_dirty(cell);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -1124,9 +1113,9 @@ const EDITOR_TOOL_ORDER = ['pencil', 'adjust', 'force-floors', 'tracks', 'wire',
|
|||||||
const EDITOR_PALETTE = [{
|
const EDITOR_PALETTE = [{
|
||||||
title: "Basics",
|
title: "Basics",
|
||||||
tiles: [
|
tiles: [
|
||||||
'player', 'player2',
|
'player', 'player2', 'hint',
|
||||||
'chip', 'chip_extra',
|
'floor', 'wall', 'thin_walls/south',
|
||||||
'floor', 'wall', 'hint', 'socket', 'exit',
|
'chip', 'chip_extra', 'socket', 'exit',
|
||||||
],
|
],
|
||||||
}, {
|
}, {
|
||||||
title: "Terrain",
|
title: "Terrain",
|
||||||
@ -1161,6 +1150,7 @@ const EDITOR_PALETTE = [{
|
|||||||
'water', 'turtle', 'fire',
|
'water', 'turtle', 'fire',
|
||||||
'ice', 'ice_nw',
|
'ice', 'ice_nw',
|
||||||
'force_floor_n', 'force_floor_all',
|
'force_floor_n', 'force_floor_all',
|
||||||
|
'canopy',
|
||||||
],
|
],
|
||||||
}, {
|
}, {
|
||||||
title: "Items",
|
title: "Items",
|
||||||
@ -1266,13 +1256,14 @@ const EDITOR_PALETTE = [{
|
|||||||
}];
|
}];
|
||||||
|
|
||||||
const SPECIAL_PALETTE_ENTRIES = {
|
const SPECIAL_PALETTE_ENTRIES = {
|
||||||
|
'thin_walls/south': { name: 'thin_walls', edges: DIRECTIONS['south'].bit },
|
||||||
'frame_block/0': { name: 'frame_block', direction: 'south', arrows: new Set },
|
'frame_block/0': { name: 'frame_block', direction: 'south', arrows: new Set },
|
||||||
'frame_block/1': { name: 'frame_block', direction: 'north', arrows: new Set(['north']) },
|
'frame_block/1': { name: 'frame_block', direction: 'north', arrows: new Set(['north']) },
|
||||||
'frame_block/2a': { name: 'frame_block', direction: 'north', arrows: new Set(['north', 'east']) },
|
'frame_block/2a': { name: 'frame_block', direction: 'north', arrows: new Set(['north', 'east']) },
|
||||||
'frame_block/2o': { name: 'frame_block', direction: 'south', arrows: new Set(['north', 'south']) },
|
'frame_block/2o': { name: 'frame_block', direction: 'south', arrows: new Set(['north', 'south']) },
|
||||||
'frame_block/3': { name: 'frame_block', direction: 'south', arrows: new Set(['north', 'east', 'south']) },
|
'frame_block/3': { name: 'frame_block', direction: 'south', arrows: new Set(['north', 'east', 'south']) },
|
||||||
'frame_block/4': { name: 'frame_block', direction: 'south', arrows: new Set(['north', 'east', 'south', 'west']) },
|
'frame_block/4': { name: 'frame_block', direction: 'south', arrows: new Set(['north', 'east', 'south', 'west']) },
|
||||||
// FIXME these should be additive/subtractive, but a track picked up from the level should replace
|
// FIXME need to handle entered_direction intelligently, but also allow setting it explicitly
|
||||||
'railroad/straight': { name: 'railroad', tracks: 1 << 5, track_switch: null, entered_direction: 'north' },
|
'railroad/straight': { name: 'railroad', tracks: 1 << 5, track_switch: null, entered_direction: 'north' },
|
||||||
'railroad/curve': { name: 'railroad', tracks: 1 << 0, track_switch: null, entered_direction: 'north' },
|
'railroad/curve': { name: 'railroad', tracks: 1 << 0, track_switch: null, entered_direction: 'north' },
|
||||||
'railroad/switch': { name: 'railroad', tracks: 0, track_switch: 0, entered_direction: 'north' },
|
'railroad/switch': { name: 'railroad', tracks: 0, track_switch: 0, entered_direction: 'north' },
|
||||||
@ -1289,6 +1280,31 @@ const SPECIAL_PALETTE_ENTRIES = {
|
|||||||
const _RAILROAD_ROTATED_LEFT = [3, 0, 1, 2, 5, 4];
|
const _RAILROAD_ROTATED_LEFT = [3, 0, 1, 2, 5, 4];
|
||||||
const _RAILROAD_ROTATED_RIGHT = [1, 2, 3, 0, 5, 4];
|
const _RAILROAD_ROTATED_RIGHT = [1, 2, 3, 0, 5, 4];
|
||||||
const SPECIAL_PALETTE_BEHAVIOR = {
|
const SPECIAL_PALETTE_BEHAVIOR = {
|
||||||
|
thin_walls: {
|
||||||
|
pick_palette_entry(tile) {
|
||||||
|
return 'thin_walls/south';
|
||||||
|
},
|
||||||
|
rotate_left(tile) {
|
||||||
|
if (tile.edges & 0x01) {
|
||||||
|
tile.edges |= 0x10;
|
||||||
|
}
|
||||||
|
tile.edges >>= 1;
|
||||||
|
},
|
||||||
|
rotate_right(tile) {
|
||||||
|
tile.edges <<= 1;
|
||||||
|
if (tile.edges & 0x10) {
|
||||||
|
tile.edges = (tile.edges & ~0x10) | 0x01;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
combine_draw(palette_tile, existing_tile) {
|
||||||
|
existing_tile.edges |= palette_tile.edges;
|
||||||
|
},
|
||||||
|
combine_erase(palette_tile, existing_tile) {
|
||||||
|
existing_tile.edges &= ~palette_tile.edges;
|
||||||
|
if (existing_tile.edges === 0)
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
},
|
||||||
frame_block: {
|
frame_block: {
|
||||||
pick_palette_entry(tile) {
|
pick_palette_entry(tile) {
|
||||||
if (tile.arrows.size === 2) {
|
if (tile.arrows.size === 2) {
|
||||||
@ -1378,6 +1394,61 @@ const SPECIAL_PALETTE_BEHAVIOR = {
|
|||||||
tile.entered_direction = DIRECTIONS[tile.entered_direction].right;
|
tile.entered_direction = DIRECTIONS[tile.entered_direction].right;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
combine_draw(palette_tile, existing_tile) {
|
||||||
|
existing_tile.tracks |= palette_tile.tracks;
|
||||||
|
// If we have a switch already, the just-placed track becomes the current one
|
||||||
|
if (existing_tile.track_switch !== null) {
|
||||||
|
for (let i = 0; i < 6; i++) {
|
||||||
|
if (palette_tile.tracks & (1 << i)) {
|
||||||
|
existing_tile.track_switch = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (palette_tile.track_switch !== null && existing_tile.track_switch === null) {
|
||||||
|
// The palette's switch is just an indication that we should have one, not what it
|
||||||
|
// ought to be
|
||||||
|
existing_tile.track_switch = palette_tile.track_switch;
|
||||||
|
for (let i = 0; i < 6; i++) {
|
||||||
|
if (existing_tile.tracks & (1 << i)) {
|
||||||
|
existing_tile.track_switch = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
combine_erase(palette_tile, existing_tile) {
|
||||||
|
existing_tile.tracks &= ~palette_tile.tracks;
|
||||||
|
|
||||||
|
// If there are no track pieces left, remove the railroad. It's technically possible to
|
||||||
|
// have a railroad with no tracks, but you're very unlikely to want it, and if you
|
||||||
|
// really do then you can do it yourself
|
||||||
|
if (existing_tile.tracks === 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (palette_tile.track_switch !== null) {
|
||||||
|
existing_tile.track_switch = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fix the track switch if necessary
|
||||||
|
if (existing_tile.track_switch !== null) {
|
||||||
|
let num_tracks = 0;
|
||||||
|
for (let i = 0; i < 6; i++) {
|
||||||
|
if (existing_tile.tracks & (1 << i)) {
|
||||||
|
num_tracks += 1;
|
||||||
|
if (! (existing_tile.tracks & (1 << existing_tile.track_switch))) {
|
||||||
|
existing_tile.track_switch = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the switch if there's nothing to switch
|
||||||
|
if (num_tracks <= 1) {
|
||||||
|
existing_tile.track_switch = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
circuit_block: {
|
circuit_block: {
|
||||||
pick_palette_entry(tile) {
|
pick_palette_entry(tile) {
|
||||||
@ -1693,15 +1764,15 @@ export class Editor extends PrimaryView {
|
|||||||
let tile = Object.assign({}, SPECIAL_PALETTE_ENTRIES[key]);
|
let tile = Object.assign({}, SPECIAL_PALETTE_ENTRIES[key]);
|
||||||
tile.type = TILE_TYPES[tile.name];
|
tile.type = TILE_TYPES[tile.name];
|
||||||
delete tile.name;
|
delete tile.name;
|
||||||
this.select_palette(tile);
|
this.select_palette(tile, true);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Regular tile name
|
// Regular tile name
|
||||||
this.select_palette(key);
|
this.select_palette(key, true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.palette_selection = null;
|
this.palette_selection = null;
|
||||||
this.select_palette('floor');
|
this.select_palette('floor', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
activate() {
|
activate() {
|
||||||
@ -1911,7 +1982,7 @@ export class Editor extends PrimaryView {
|
|||||||
this.tool_button_els[this.current_tool].classList.add('-selected');
|
this.tool_button_els[this.current_tool].classList.add('-selected');
|
||||||
}
|
}
|
||||||
|
|
||||||
select_palette(name_or_tile) {
|
select_palette(name_or_tile, from_palette = false) {
|
||||||
let name, tile;
|
let name, tile;
|
||||||
if (typeof name_or_tile === 'string') {
|
if (typeof name_or_tile === 'string') {
|
||||||
name = name_or_tile;
|
name = name_or_tile;
|
||||||
@ -1936,6 +2007,7 @@ export class Editor extends PrimaryView {
|
|||||||
|
|
||||||
// Store the tile
|
// Store the tile
|
||||||
this.palette_selection = tile;
|
this.palette_selection = tile;
|
||||||
|
this.palette_selection_from_palette = from_palette;
|
||||||
|
|
||||||
// Select it in the palette, if possible
|
// Select it in the palette, if possible
|
||||||
let key = name;
|
let key = name;
|
||||||
@ -2028,18 +2100,61 @@ export class Editor extends PrimaryView {
|
|||||||
|
|
||||||
let cell = this.cell(x, y);
|
let cell = this.cell(x, y);
|
||||||
// Replace whatever's on the same layer
|
// Replace whatever's on the same layer
|
||||||
// TODO probably not the best heuristic yet, since i imagine you can
|
|
||||||
// combine e.g. the tent with thin walls
|
|
||||||
// TODO should preserve wiring if possible too
|
// TODO should preserve wiring if possible too
|
||||||
for (let i = cell.length - 1; i >= 0; i--) {
|
for (let i = cell.length - 1; i >= 0; i--) {
|
||||||
|
// If we find a tile of the same type as the one being drawn, see if it has custom
|
||||||
|
// combine behavior (only the case if it came from the palette)
|
||||||
|
if (cell[i].type === tile.type &&
|
||||||
|
// FIXME this is hacky garbage
|
||||||
|
tile === this.palette_selection && this.palette_selection_from_palette &&
|
||||||
|
SPECIAL_PALETTE_BEHAVIOR[tile.type.name] &&
|
||||||
|
SPECIAL_PALETTE_BEHAVIOR[tile.type.name].combine_draw)
|
||||||
|
{
|
||||||
|
SPECIAL_PALETTE_BEHAVIOR[tile.type.name].combine_draw(tile, cell[i]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (cell[i].type.draw_layer === tile.type.draw_layer) {
|
if (cell[i].type.draw_layer === tile.type.draw_layer) {
|
||||||
cell.splice(i, 1);
|
cell.splice(i, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cell.push(Object.assign({}, tile));
|
cell.push(Object.assign({}, tile));
|
||||||
cell.sort((a, b) => a.type.draw_layer - b.type.draw_layer);
|
cell.sort((a, b) => a.type.draw_layer - b.type.draw_layer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
erase_tile(cell, tile = null) {
|
||||||
|
if (tile === null) {
|
||||||
|
tile = this.palette_selection;
|
||||||
|
}
|
||||||
|
|
||||||
|
let layer = tile.type.draw_layer;
|
||||||
|
for (let i = cell.length - 1; i >= 0; i--) {
|
||||||
|
// If we find a tile of the same type as the one being drawn, see if it has custom
|
||||||
|
// combine behavior (only the case if it came from the palette)
|
||||||
|
if (cell[i].type === tile.type &&
|
||||||
|
// FIXME this is hacky garbage
|
||||||
|
tile === this.palette_selection && this.palette_selection_from_palette &&
|
||||||
|
SPECIAL_PALETTE_BEHAVIOR[tile.type.name] &&
|
||||||
|
SPECIAL_PALETTE_BEHAVIOR[tile.type.name].combine_erase)
|
||||||
|
{
|
||||||
|
let remove = SPECIAL_PALETTE_BEHAVIOR[tile.type.name].combine_erase(tile, cell[i]);
|
||||||
|
if (! remove)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cell[i].type.draw_layer === layer) {
|
||||||
|
cell.splice(i, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't allow erasing the floor entirely
|
||||||
|
if (layer === 0) {
|
||||||
|
cell.unshift({type: TILE_TYPES.floor});
|
||||||
|
}
|
||||||
|
this.mark_cell_dirty(cell);
|
||||||
|
}
|
||||||
|
|
||||||
open_tile_prop_overlay(tile, x0, y0) {
|
open_tile_prop_overlay(tile, x0, y0) {
|
||||||
this.cancel_mouse_operation();
|
this.cancel_mouse_operation();
|
||||||
// FIXME keep these around, don't recreate them constantly
|
// FIXME keep these around, don't recreate them constantly
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user