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
|
||||
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--) {
|
||||
let tile = cell[i];
|
||||
// FIXME does not yet support canopy or thin walls >:S
|
||||
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
|
||||
// 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),
|
||||
|
||||
@ -423,18 +423,7 @@ class PencilOperation extends DrawOperation {
|
||||
}
|
||||
else if (template) {
|
||||
// Erase whatever's on the same layer
|
||||
// TODO this seems like the wrong place for this
|
||||
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);
|
||||
this.editor.erase_tile(cell);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@ -1124,9 +1113,9 @@ const EDITOR_TOOL_ORDER = ['pencil', 'adjust', 'force-floors', 'tracks', 'wire',
|
||||
const EDITOR_PALETTE = [{
|
||||
title: "Basics",
|
||||
tiles: [
|
||||
'player', 'player2',
|
||||
'chip', 'chip_extra',
|
||||
'floor', 'wall', 'hint', 'socket', 'exit',
|
||||
'player', 'player2', 'hint',
|
||||
'floor', 'wall', 'thin_walls/south',
|
||||
'chip', 'chip_extra', 'socket', 'exit',
|
||||
],
|
||||
}, {
|
||||
title: "Terrain",
|
||||
@ -1161,6 +1150,7 @@ const EDITOR_PALETTE = [{
|
||||
'water', 'turtle', 'fire',
|
||||
'ice', 'ice_nw',
|
||||
'force_floor_n', 'force_floor_all',
|
||||
'canopy',
|
||||
],
|
||||
}, {
|
||||
title: "Items",
|
||||
@ -1266,13 +1256,14 @@ const EDITOR_PALETTE = [{
|
||||
}];
|
||||
|
||||
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/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/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/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/curve': { name: 'railroad', tracks: 1 << 0, track_switch: null, 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_RIGHT = [1, 2, 3, 0, 5, 4];
|
||||
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: {
|
||||
pick_palette_entry(tile) {
|
||||
if (tile.arrows.size === 2) {
|
||||
@ -1378,6 +1394,61 @@ const SPECIAL_PALETTE_BEHAVIOR = {
|
||||
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: {
|
||||
pick_palette_entry(tile) {
|
||||
@ -1693,15 +1764,15 @@ export class Editor extends PrimaryView {
|
||||
let tile = Object.assign({}, SPECIAL_PALETTE_ENTRIES[key]);
|
||||
tile.type = TILE_TYPES[tile.name];
|
||||
delete tile.name;
|
||||
this.select_palette(tile);
|
||||
this.select_palette(tile, true);
|
||||
}
|
||||
else {
|
||||
// Regular tile name
|
||||
this.select_palette(key);
|
||||
this.select_palette(key, true);
|
||||
}
|
||||
});
|
||||
this.palette_selection = null;
|
||||
this.select_palette('floor');
|
||||
this.select_palette('floor', true);
|
||||
}
|
||||
|
||||
activate() {
|
||||
@ -1911,7 +1982,7 @@ export class Editor extends PrimaryView {
|
||||
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;
|
||||
if (typeof name_or_tile === 'string') {
|
||||
name = name_or_tile;
|
||||
@ -1936,6 +2007,7 @@ export class Editor extends PrimaryView {
|
||||
|
||||
// Store the tile
|
||||
this.palette_selection = tile;
|
||||
this.palette_selection_from_palette = from_palette;
|
||||
|
||||
// Select it in the palette, if possible
|
||||
let key = name;
|
||||
@ -2028,18 +2100,61 @@ export class Editor extends PrimaryView {
|
||||
|
||||
let cell = this.cell(x, y);
|
||||
// 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
|
||||
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) {
|
||||
cell.splice(i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
cell.push(Object.assign({}, tile));
|
||||
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) {
|
||||
this.cancel_mouse_operation();
|
||||
// FIXME keep these around, don't recreate them constantly
|
||||
|
||||
Loading…
Reference in New Issue
Block a user