Add support for gray buttons to the adjust tool

This commit is contained in:
Eevee (Evelyn Woods) 2024-04-21 03:53:57 -06:00
parent 1170c5970e
commit abbda898c7
3 changed files with 153 additions and 38 deletions

View File

@ -1201,6 +1201,42 @@ const ADJUST_TOGGLES_CCW = {};
}
}
}
// Little wrapper that allows calling (simple) callbacks on tile types from the editor.
// Note that editor tiles don't even have .cell, so we have to fake that too
class DummyRunningLevel {
constructor(editor, tile, cell) {
this.editor = editor;
this.tile = tile;
this.cell = cell;
}
_set_tile_prop(tile, key, value) {
if (tile !== this.tile) {
console.error("DummyRunningLevel._set_tile_prop called on an unknown tile:", tile, "expected:", this.tile);
return;
}
this.editor.place_in_cell(this.cell, {...tile, key: value});
}
transmute_tile(tile, type_name) {
if (tile !== this.tile) {
console.error("DummyRunningLevel.transmute_tile called on an unknown tile:", tile, "expected:", this.tile);
return;
}
let type = TILE_TYPES[type_name];
if (! type) {
console.error("DummyRunningLevel.transmute_tile called with bad type:", type_name);
return;
}
if (tile.type.layer !== type.layer) {
console.error("DummyRunningLevel.transmute_tile refusing to change tile layers:", tile, type_name);
return;
}
this.editor.place_in_cell(this.cell, {...tile, type});
}
}
// Tiles with special behavior when clicked
const ADJUST_SPECIAL = {
button_green(editor) {
@ -1221,7 +1257,24 @@ const ADJUST_SPECIAL = {
}
}
},
button_gray(editor, tile, cell) {
// Toggle gray objects... er... objects affected by gray buttons
for (let dy = -2; dy <= 2; dy++) {
for (let dx = -2; dx <= 2; dx++) {
if (dx === 0 && dy === 0)
continue;
let other_cell = editor.cell(cell.x + dx, cell.y + dy);
if (! other_cell)
continue;
for (let other of other_cell) {
if (other && other.type.on_gray_button && other.type.is_gray_button_editor_safe) {
other.type.on_gray_button(other, new DummyRunningLevel(editor, other, other_cell));
}
}
}
}
},
};
// TODO maybe better visual feedback of what will happen when you click?
// - rotate terrain (cw, ccw)
@ -1229,6 +1282,59 @@ const ADJUST_SPECIAL = {
// - rotate actor (cw, ccw)
// - press button
export class AdjustOperation extends MouseOperation {
constructor(...args) {
super(...args);
this.gray_button_preview = mk_svg('g.overlay-transient', {'data-source': 'AdjustOperation'});
this.gray_button_preview.append(mk_svg('rect.overlay-adjust-gray-button-radius', {
x: -2,
y: -2,
width: 5,
height: 5,
}));
this.editor.svg_overlay.append(this.gray_button_preview);
}
handle_hover(client_x, client_y, frac_cell_x, frac_cell_y, cell_x, cell_y) {
let cell = this.cell(cell_x, cell_y);
let terrain = cell[LAYERS.terrain];
if (terrain.type.name === 'button_gray') {
this.gray_button_preview.classList.add('--visible');
this.gray_button_preview.setAttribute('transform', `translate(${cell_x} ${cell_y})`);
for (let el of this.gray_button_preview.querySelectorAll('rect.overlay-adjust-gray-button-shroud')) {
el.remove();
}
// The easiest way I can find to preview this is to slap an overlay on everything NOT
// affected by the button. Try to consolidate some of the resulting rectangles though
for (let dy = -2; dy <= 2; dy++) {
let last_rect, last_dx;
for (let dx = -2; dx <= 2; dx++) {
let target = this.cell(cell_x + dx, cell_y + dy);
if (target && target !== cell && target[LAYERS.terrain].type.on_gray_button)
continue;
if (last_rect && last_dx === dx - 1) {
last_rect.setAttribute('width', 1 + parseInt(last_rect.getAttribute('width'), 10));
}
else {
last_rect = mk_svg('rect.overlay-adjust-gray-button-shroud', {
x: dx,
y: dy,
width: 1,
height: 1,
});
this.gray_button_preview.append(last_rect);
}
last_dx = dx;
}
}
}
else {
this.gray_button_preview.classList.remove('--visible');
}
}
handle_press() {
let cell = this.cell(this.prev_cell_x, this.prev_cell_y);
if (this.ctrl) {
@ -1274,7 +1380,7 @@ export class AdjustOperation extends MouseOperation {
// Other special tile behavior
let special = ADJUST_SPECIAL[tile.type.name];
if (special) {
special(this.editor);
special(this.editor, tile, cell);
this.editor.commit_undo();
break;
}
@ -1283,6 +1389,10 @@ export class AdjustOperation extends MouseOperation {
// Adjust tool doesn't support dragging
// TODO should it?
// TODO if it does then it should end as soon as you spawn a popup
do_destroy() {
this.gray_button_preview.remove();
super.do_destroy();
}
}
// FIXME currently allows creating outside the map bounds and moving beyond the right/bottom, sigh

View File

@ -66,6 +66,7 @@ function _define_force_floor(direction, opposite_type) {
activate(me, level) {
level.transmute_tile(me, opposite_type);
},
is_gray_button_editor_safe: true,
on_gray_button: activate_me,
on_power: activate_me,
};
@ -505,6 +506,7 @@ const TILE_TYPES = {
activate(me, level) {
level.transmute_tile(me, 'swivel_se');
},
is_gray_button_editor_safe: true,
on_gray_button: activate_me,
on_power: activate_me,
},
@ -522,6 +524,7 @@ const TILE_TYPES = {
activate(me, level) {
level.transmute_tile(me, 'swivel_sw');
},
is_gray_button_editor_safe: true,
on_gray_button: activate_me,
on_power: activate_me,
},
@ -539,6 +542,7 @@ const TILE_TYPES = {
activate(me, level) {
level.transmute_tile(me, 'swivel_nw');
},
is_gray_button_editor_safe: true,
on_gray_button: activate_me,
on_power: activate_me,
},
@ -556,6 +560,7 @@ const TILE_TYPES = {
activate(me, level) {
level.transmute_tile(me, 'swivel_ne');
},
is_gray_button_editor_safe: true,
on_gray_button: activate_me,
on_power: activate_me,
},
@ -653,6 +658,7 @@ const TILE_TYPES = {
on_power(me, level) {
me.type._switch_track(me, level);
},
is_gray_button_editor_safe: true,
on_gray_button(me, level) {
me.type._switch_track(me, level);
},
@ -791,6 +797,7 @@ const TILE_TYPES = {
activate(me, level) {
level.transmute_tile(me, 'turntable_ccw');
},
is_gray_button_editor_safe: true,
on_gray_button: activate_me,
on_power: activate_me,
},
@ -812,6 +819,7 @@ const TILE_TYPES = {
activate(me, level) {
level.transmute_tile(me, 'turntable_cw');
},
is_gray_button_editor_safe: true,
on_gray_button: activate_me,
on_power: activate_me,
},
@ -1328,12 +1336,12 @@ const TILE_TYPES = {
level.pending_green_toggle &&
(other.type.collision_mask & COLLISION.all_but_ghost));
},
on_gray_button(me, level) {
activate(me, level) {
level.transmute_tile(me, 'green_wall');
},
on_power(me, level) {
me.type.on_gray_button(me, level);
},
is_gray_button_editor_safe: true,
on_gray_button: activate_me,
on_power: activate_me,
},
green_wall: {
layer: LAYERS.terrain,
@ -1344,12 +1352,12 @@ const TILE_TYPES = {
! level.pending_green_toggle &&
(other.type.collision_mask & COLLISION.all_but_ghost));
},
on_gray_button(me, level) {
activate(me, level) {
level.transmute_tile(me, 'green_floor');
},
on_power(me, level) {
me.type.on_gray_button(me, level);
},
is_gray_button_editor_safe: true,
on_gray_button: activate_me,
on_power: activate_me,
},
green_chip: {
layer: LAYERS.item,
@ -1377,28 +1385,24 @@ const TILE_TYPES = {
},
purple_floor: {
layer: LAYERS.terrain,
on_gray_button(me, level) {
activate(me, level) {
level.transmute_tile(me, 'purple_wall');
},
on_power(me, level) {
me.type.on_gray_button(me, level);
},
on_depower(me, level) {
me.type.on_gray_button(me, level);
},
is_gray_button_editor_safe: true,
on_gray_button: activate_me,
on_power: activate_me,
on_depower: activate_me,
},
purple_wall: {
layer: LAYERS.terrain,
blocks_collision: COLLISION.all_but_ghost,
on_gray_button(me, level) {
activate(me, level) {
level.transmute_tile(me, 'purple_floor');
},
on_power(me, level) {
me.type.on_gray_button(me, level);
},
on_depower(me, level) {
me.type.on_gray_button(me, level);
},
is_gray_button_editor_safe: true,
on_gray_button: activate_me,
on_power: activate_me,
on_depower: activate_me,
},
// Sokoban blocks, buttons, and walls -- they each come in four colors, the buttons can be
@ -1578,9 +1582,8 @@ const TILE_TYPES = {
on_power(me, level) {
me.type.activate(me, level, true);
},
on_gray_button(me, level) {
me.type.activate(me, level);
},
is_gray_button_editor_safe: false,
on_gray_button: activate_me,
},
trap: {
layer: LAYERS.terrain,
@ -2004,24 +2007,18 @@ const TILE_TYPES = {
// Do NOT immediately nuke anything on us, or it'd be impossible to push a block off an
// adjacent orange button; this is probably why flame jets kill on tics
},
on_gray_button(me, level) {
me.type.activate(me, level);
},
on_power(me, level) {
me.type.activate(me, level);
},
is_gray_button_editor_safe: true,
on_gray_button: activate_me,
on_power: activate_me,
},
flame_jet_on: {
layer: LAYERS.terrain,
activate(me, level) {
level.transmute_tile(me, 'flame_jet_off');
},
on_gray_button(me, level) {
me.type.activate(me, level);
},
on_power(me, level) {
me.type.activate(me, level);
},
is_gray_button_editor_safe: true,
on_gray_button: activate_me,
on_power: activate_me,
on_stand(me, level, other) {
// Note that (dirt?) blocks, fireballs, and anything with fire boots are immune
// TODO would be neat if this understood "ignores anything with fire immunity" but that

View File

@ -2290,6 +2290,14 @@ svg.level-editor-overlay g.overlay-connection.--implicit line.-arrow {
svg.level-editor-overlay g.overlay-connection line.-arrow {
marker-end: url(#overlay-arrowhead);
}
svg.level-editor-overlay .overlay-adjust-gray-button-radius {
stroke: #f4f4f4;
fill: hsla(10, 10%, 80%, 0.125);
}
svg.level-editor-overlay .overlay-adjust-gray-button-shroud {
stroke: none;
fill: hsla(10, 10%, 60%, 0.75);
}
svg.level-editor-overlay rect.overlay-camera {
stroke: #808080;
fill: #80808040;