Mass deletion support
This commit is contained in:
parent
eb46b45c9a
commit
bd89c2cc9e
|
@ -400,6 +400,14 @@
|
|||
"spriteSourceSize": {"x":0,"y":0,"w":3,"h":3},
|
||||
"sourceSize": {"w":3,"h":3}
|
||||
},
|
||||
"sprites/misc/deletion_marker.png":
|
||||
{
|
||||
"frame": {"x":107,"y":95,"w":10,"h":10},
|
||||
"rotated": false,
|
||||
"trimmed": false,
|
||||
"spriteSourceSize": {"x":0,"y":0,"w":10,"h":10},
|
||||
"sourceSize": {"w":10,"h":10}
|
||||
},
|
||||
"sprites/misc/slot_bad_arrow.png":
|
||||
{
|
||||
"frame": {"x":107,"y":95,"w":10,"h":10},
|
||||
|
@ -423,6 +431,6 @@
|
|||
"format": "RGBA8888",
|
||||
"size": {"w":121,"h":240},
|
||||
"scale": "0.1",
|
||||
"smartupdate": "$TexturePacker:SmartUpdate:80df730947c1d83df455c7645ebd4dbd:29979cacc85be7fc1c890b96110d9771:f159918d23e5952766c6d23ab52278c6$"
|
||||
"smartupdate": "$TexturePacker:SmartUpdate:e1aa43844bd41a62665cbb0c503ebb65:4482f83c3403490091eed64b07ce6591:f159918d23e5952766c6d23ab52278c6$"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -400,6 +400,14 @@
|
|||
"spriteSourceSize": {"x":4,"y":4,"w":28,"h":28},
|
||||
"sourceSize": {"w":32,"h":32}
|
||||
},
|
||||
"sprites/misc/deletion_marker.png":
|
||||
{
|
||||
"frame": {"x":237,"y":1710,"w":82,"h":82},
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": {"x":7,"y":7,"w":82,"h":82},
|
||||
"sourceSize": {"w":96,"h":96}
|
||||
},
|
||||
"sprites/misc/slot_bad_arrow.png":
|
||||
{
|
||||
"frame": {"x":237,"y":1710,"w":82,"h":82},
|
||||
|
@ -423,6 +431,6 @@
|
|||
"format": "RGBA8888",
|
||||
"size": {"w":1007,"h":1968},
|
||||
"scale": "1",
|
||||
"smartupdate": "$TexturePacker:SmartUpdate:80df730947c1d83df455c7645ebd4dbd:29979cacc85be7fc1c890b96110d9771:f159918d23e5952766c6d23ab52278c6$"
|
||||
"smartupdate": "$TexturePacker:SmartUpdate:e1aa43844bd41a62665cbb0c503ebb65:4482f83c3403490091eed64b07ce6591:f159918d23e5952766c6d23ab52278c6$"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -400,6 +400,14 @@
|
|||
"spriteSourceSize": {"x":0,"y":0,"w":8,"h":8},
|
||||
"sourceSize": {"w":8,"h":8}
|
||||
},
|
||||
"sprites/misc/deletion_marker.png":
|
||||
{
|
||||
"frame": {"x":217,"y":159,"w":22,"h":22},
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": {"x":1,"y":1,"w":22,"h":22},
|
||||
"sourceSize": {"w":24,"h":24}
|
||||
},
|
||||
"sprites/misc/slot_bad_arrow.png":
|
||||
{
|
||||
"frame": {"x":217,"y":159,"w":22,"h":22},
|
||||
|
@ -423,6 +431,6 @@
|
|||
"format": "RGBA8888",
|
||||
"size": {"w":591,"h":252},
|
||||
"scale": "0.25",
|
||||
"smartupdate": "$TexturePacker:SmartUpdate:80df730947c1d83df455c7645ebd4dbd:29979cacc85be7fc1c890b96110d9771:f159918d23e5952766c6d23ab52278c6$"
|
||||
"smartupdate": "$TexturePacker:SmartUpdate:e1aa43844bd41a62665cbb0c503ebb65:4482f83c3403490091eed64b07ce6591:f159918d23e5952766c6d23ab52278c6$"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -400,6 +400,14 @@
|
|||
"spriteSourceSize": {"x":1,"y":1,"w":15,"h":15},
|
||||
"sourceSize": {"w":16,"h":16}
|
||||
},
|
||||
"sprites/misc/deletion_marker.png":
|
||||
{
|
||||
"frame": {"x":456,"y":460,"w":42,"h":42},
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": {"x":3,"y":3,"w":42,"h":42},
|
||||
"sourceSize": {"w":48,"h":48}
|
||||
},
|
||||
"sprites/misc/slot_bad_arrow.png":
|
||||
{
|
||||
"frame": {"x":456,"y":460,"w":42,"h":42},
|
||||
|
@ -423,6 +431,6 @@
|
|||
"format": "RGBA8888",
|
||||
"size": {"w":1023,"h":509},
|
||||
"scale": "0.5",
|
||||
"smartupdate": "$TexturePacker:SmartUpdate:80df730947c1d83df455c7645ebd4dbd:29979cacc85be7fc1c890b96110d9771:f159918d23e5952766c6d23ab52278c6$"
|
||||
"smartupdate": "$TexturePacker:SmartUpdate:e1aa43844bd41a62665cbb0c503ebb65:4482f83c3403490091eed64b07ce6591:f159918d23e5952766c6d23ab52278c6$"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -400,6 +400,14 @@
|
|||
"spriteSourceSize": {"x":2,"y":2,"w":22,"h":22},
|
||||
"sourceSize": {"w":24,"h":24}
|
||||
},
|
||||
"sprites/misc/deletion_marker.png":
|
||||
{
|
||||
"frame": {"x":1050,"y":79,"w":62,"h":62},
|
||||
"rotated": false,
|
||||
"trimmed": true,
|
||||
"spriteSourceSize": {"x":5,"y":5,"w":62,"h":62},
|
||||
"sourceSize": {"w":72,"h":72}
|
||||
},
|
||||
"sprites/misc/slot_bad_arrow.png":
|
||||
{
|
||||
"frame": {"x":1050,"y":79,"w":62,"h":62},
|
||||
|
@ -423,6 +431,6 @@
|
|||
"format": "RGBA8888",
|
||||
"size": {"w":1125,"h":999},
|
||||
"scale": "0.75",
|
||||
"smartupdate": "$TexturePacker:SmartUpdate:80df730947c1d83df455c7645ebd4dbd:29979cacc85be7fc1c890b96110d9771:f159918d23e5952766c6d23ab52278c6$"
|
||||
"smartupdate": "$TexturePacker:SmartUpdate:e1aa43844bd41a62665cbb0c503ebb65:4482f83c3403490091eed64b07ce6591:f159918d23e5952766c6d23ab52278c6$"
|
||||
}
|
||||
}
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 4.6 KiB |
|
@ -64,7 +64,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
.shift .keybinding {
|
||||
.keybinding.shift {
|
||||
transition: all 0.1s ease-in-out;
|
||||
transition-property: background-color, color, border-color;
|
||||
background: $colorRedBright;
|
||||
|
@ -72,7 +72,7 @@
|
|||
color: #fff;
|
||||
}
|
||||
|
||||
&.shiftDown .shift .keybinding {
|
||||
&.shiftDown .keybinding.shift {
|
||||
border-color: darken($colorRedBright, 40);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
#ingame_HUD_MassSelector {
|
||||
position: absolute;
|
||||
@include S(top, 50px);
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
@include S(width, 300px);
|
||||
background: #f77;
|
||||
@include S(border-radius, 4px);
|
||||
@include S(padding, 10px);
|
||||
@include PlainText;
|
||||
color: #fff;
|
||||
|
||||
.keybinding {
|
||||
position: relative;
|
||||
top: unset;
|
||||
left: unset;
|
||||
right: unset;
|
||||
bottom: unset;
|
||||
}
|
||||
}
|
|
@ -33,11 +33,12 @@
|
|||
@import "ingame_hud/game_menu";
|
||||
@import "ingame_hud/blur_overlay";
|
||||
@import "ingame_hud/dialogs";
|
||||
@import "ingame_hud/mass_selector";
|
||||
|
||||
// Z-Index
|
||||
$elements: ingame_Canvas, ingame_HUD_building_placer_overlay, ingame_HUD_building_placer,
|
||||
ingame_HUD_buildings_toolbar, ingame_HUD_GameMenu, ingame_HUD_KeybindingOverlay, ingame_HUD_Shop,
|
||||
ingame_HUD_BetaOverlay, ingame_HUD_UnlockNotification;
|
||||
ingame_HUD_BetaOverlay, ingame_HUD_MassSelector, ingame_HUD_UnlockNotification;
|
||||
|
||||
$zindex: 100;
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ import { HUDUnlockNotification } from "./parts/unlock_notification";
|
|||
import { HUDGameMenu } from "./parts/game_menu";
|
||||
import { HUDShop } from "./parts/shop";
|
||||
import { IS_MOBILE } from "../../core/config";
|
||||
import { HUDMassSelector } from "./parts/mass_selector";
|
||||
|
||||
export class GameHUD {
|
||||
/**
|
||||
|
@ -40,6 +41,8 @@ export class GameHUD {
|
|||
|
||||
gameMenu: new HUDGameMenu(this.root),
|
||||
|
||||
massSelector: new HUDMassSelector(this.root),
|
||||
|
||||
shop: new HUDShop(this.root),
|
||||
|
||||
// betaOverlay: new HUDBetaOverlay(this.root),
|
||||
|
@ -149,7 +152,7 @@ export class GameHUD {
|
|||
* @param {DrawParameters} parameters
|
||||
*/
|
||||
draw(parameters) {
|
||||
const partsOrder = ["buildingPlacer"];
|
||||
const partsOrder = ["massSelector", "buildingPlacer"];
|
||||
|
||||
for (let i = 0; i < partsOrder.length; ++i) {
|
||||
if (this.parts[partsOrder[i]]) {
|
||||
|
|
|
@ -44,7 +44,9 @@ export class HUDKeybindingOverlay extends BaseHUDPart {
|
|||
</div>
|
||||
|
||||
<div class="binding noPlacementOnly">
|
||||
<code class="keybinding rightMouse"></code>
|
||||
<code class="keybinding rightMouse"></code><i></i>
|
||||
<code class="keybinding shift">ALT</code>+
|
||||
<code class="keybinding leftMouse"></code>
|
||||
<label>Delete</label>
|
||||
</div>
|
||||
|
||||
|
@ -60,13 +62,13 @@ export class HUDKeybindingOverlay extends BaseHUDPart {
|
|||
<label>Rotate Building</label>
|
||||
</div>
|
||||
|
||||
<div class="binding placementOnly shift">
|
||||
<code class="keybinding">⇧ SHIFT</code>
|
||||
<div class="binding placementOnly">
|
||||
<code class="keybinding shift">⇧ SHIFT</code>
|
||||
<label>Place Multiple</label>
|
||||
</div>
|
||||
|
||||
<div class="binding placementOnly shift">
|
||||
<code class="keybinding">ALT</code>
|
||||
<div class="binding placementOnly">
|
||||
<code class="keybinding shift">ALT</code>
|
||||
<label>Reverse orientation</label>
|
||||
</div>
|
||||
`
|
||||
|
|
|
@ -1,3 +1,196 @@
|
|||
import { BaseHUDPart } from "../base_hud_part";
|
||||
import { Vector } from "../../../core/vector";
|
||||
import { STOP_PROPAGATION } from "../../../core/signal";
|
||||
import { DrawParameters } from "../../../core/draw_parameters";
|
||||
import { Entity } from "../../entity";
|
||||
import { Loader } from "../../../core/loader";
|
||||
import { globalConfig } from "../../../core/config";
|
||||
import { makeDiv } from "../../../core/utils";
|
||||
import { DynamicDomAttach } from "../dynamic_dom_attach";
|
||||
import { createLogger } from "../../../core/logging";
|
||||
|
||||
export class HUDMassSelector extends BaseHUDPart {}
|
||||
const logger = createLogger("hud/mass_selector");
|
||||
|
||||
export class HUDMassSelector extends BaseHUDPart {
|
||||
createElements(parent) {
|
||||
this.element = makeDiv(
|
||||
parent,
|
||||
"ingame_HUD_MassSelector",
|
||||
[],
|
||||
`
|
||||
Press <code class="keybinding">DEL</code> to remove selected buildings
|
||||
and <code class="keybinding">ESCAPE</code> to cancel.
|
||||
`
|
||||
);
|
||||
}
|
||||
|
||||
initialize() {
|
||||
this.deletionMarker = Loader.getSprite("sprites/misc/deletion_marker.png");
|
||||
|
||||
this.currentSelectionStart = null;
|
||||
this.currentSelectionEnd = null;
|
||||
this.entityUidsMarkedForDeletion = new Set();
|
||||
|
||||
this.root.signals.entityQueuedForDestroy.add(this.onEntityDestroyed, this);
|
||||
|
||||
this.root.camera.downPreHandler.add(this.onMouseDown, this);
|
||||
this.root.camera.movePreHandler.add(this.onMouseMove, this);
|
||||
this.root.camera.upPostHandler.add(this.onMouseUp, this);
|
||||
|
||||
this.root.gameState.keyActionMapper.getBinding("back").add(this.onBack, this);
|
||||
this.root.gameState.keyActionMapper.getBinding("confirm_mass_delete").add(this.confirmDelete, this);
|
||||
|
||||
this.domAttach = new DynamicDomAttach(this.root, this.element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the destroy callback and makes sure we clean our list
|
||||
* @param {Entity} entity
|
||||
*/
|
||||
onEntityDestroyed(entity) {
|
||||
this.entityUidsMarkedForDeletion.delete(entity.uid);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
onBack() {
|
||||
// Clear entities on escape
|
||||
if (this.entityUidsMarkedForDeletion) {
|
||||
this.entityUidsMarkedForDeletion = new Set();
|
||||
return STOP_PROPAGATION;
|
||||
}
|
||||
}
|
||||
|
||||
confirmDelete() {
|
||||
const entityUids = Array.from(this.entityUidsMarkedForDeletion);
|
||||
for (let i = 0; i < entityUids.length; ++i) {
|
||||
const uid = entityUids[i];
|
||||
const entity = this.root.entityMgr.findByUid(uid);
|
||||
if (!this.root.logic.tryDeleteBuilding(entity)) {
|
||||
logger.error("Error in mass delete, could not remove building");
|
||||
this.entityUidsMarkedForDeletion.delete(uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* mouse down pre handler
|
||||
* @param {Vector} pos
|
||||
*/
|
||||
onMouseDown(pos) {
|
||||
if (!this.root.app.inputMgr.altIsDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.root.app.inputMgr.shiftIsDown) {
|
||||
// Start new selection
|
||||
this.entityUidsMarkedForDeletion = new Set();
|
||||
}
|
||||
|
||||
this.currentSelectionStart = pos.copy();
|
||||
this.currentSelectionEnd = pos.copy();
|
||||
return STOP_PROPAGATION;
|
||||
}
|
||||
|
||||
/**
|
||||
* mouse move pre handler
|
||||
* @param {Vector} pos
|
||||
*/
|
||||
onMouseMove(pos) {
|
||||
if (this.currentSelectionStart) {
|
||||
this.currentSelectionEnd = pos.copy();
|
||||
}
|
||||
}
|
||||
|
||||
onMouseUp() {
|
||||
if (this.currentSelectionStart) {
|
||||
const worldStart = this.root.camera.screenToWorld(this.currentSelectionStart);
|
||||
const worldEnd = this.root.camera.screenToWorld(this.currentSelectionEnd);
|
||||
|
||||
const tileStart = worldStart.toTileSpace();
|
||||
const tileEnd = worldEnd.toTileSpace();
|
||||
|
||||
const realTileStart = tileStart.min(tileEnd);
|
||||
const realTileEnd = tileStart.max(tileEnd);
|
||||
|
||||
for (let x = realTileStart.x; x <= realTileEnd.x; ++x) {
|
||||
for (let y = realTileStart.y; y <= realTileEnd.y; ++y) {
|
||||
const contents = this.root.map.getTileContentXY(x, y);
|
||||
if (contents && this.root.logic.canDeleteBuilding(contents)) {
|
||||
this.entityUidsMarkedForDeletion.add(contents.uid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.currentSelectionStart = null;
|
||||
this.currentSelectionEnd = null;
|
||||
}
|
||||
}
|
||||
|
||||
update() {
|
||||
this.domAttach.update(this.entityUidsMarkedForDeletion.size > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {DrawParameters} parameters
|
||||
*/
|
||||
draw(parameters) {
|
||||
if (this.currentSelectionStart) {
|
||||
const worldStart = this.root.camera.screenToWorld(this.currentSelectionStart);
|
||||
const worldEnd = this.root.camera.screenToWorld(this.currentSelectionEnd);
|
||||
|
||||
const realWorldStart = worldStart.min(worldEnd);
|
||||
const realWorldEnd = worldStart.max(worldEnd);
|
||||
|
||||
const tileStart = worldStart.toTileSpace();
|
||||
const tileEnd = worldEnd.toTileSpace();
|
||||
|
||||
const realTileStart = tileStart.min(tileEnd);
|
||||
const realTileEnd = tileStart.max(tileEnd);
|
||||
|
||||
parameters.context.lineWidth = 1;
|
||||
parameters.context.fillStyle = "rgba(255, 127, 127, 0.2)";
|
||||
parameters.context.strokeStyle = "rgba(255, 127, 127, 0.5)";
|
||||
parameters.context.beginPath();
|
||||
parameters.context.rect(
|
||||
realWorldStart.x,
|
||||
realWorldStart.y,
|
||||
realWorldEnd.x - realWorldStart.x,
|
||||
realWorldEnd.y - realWorldStart.y
|
||||
);
|
||||
parameters.context.fill();
|
||||
parameters.context.stroke();
|
||||
|
||||
for (let x = realTileStart.x; x <= realTileEnd.x; ++x) {
|
||||
for (let y = realTileStart.y; y <= realTileEnd.y; ++y) {
|
||||
const contents = this.root.map.getTileContentXY(x, y);
|
||||
if (contents && this.root.logic.canDeleteBuilding(contents)) {
|
||||
const staticComp = contents.components.StaticMapEntity;
|
||||
const center = staticComp.getTileSpaceBounds().getCenter().toWorldSpace();
|
||||
this.deletionMarker.drawCachedCentered(
|
||||
parameters,
|
||||
center.x,
|
||||
center.y,
|
||||
globalConfig.tileSize * 0.5
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.entityUidsMarkedForDeletion.forEach(uid => {
|
||||
const entity = this.root.entityMgr.findByUid(uid);
|
||||
const staticComp = entity.components.StaticMapEntity;
|
||||
const center = staticComp.getTileSpaceBounds().getCenter().toWorldSpace();
|
||||
|
||||
this.deletionMarker.drawCachedCentered(
|
||||
parameters,
|
||||
center.x,
|
||||
center.y,
|
||||
globalConfig.tileSize * 0.5
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,8 @@ export const defaultKeybindings = {
|
|||
|
||||
menu_open_shop: { keyCode: key("F") },
|
||||
menu_open_stats: { keyCode: key("G") },
|
||||
|
||||
confirm_mass_delete: { keyCode: 46 }, // DEL
|
||||
},
|
||||
|
||||
toolbar: {
|
||||
|
|
Reference in New Issue