diff --git a/res_built/atlas/atlas0_10.json b/res_built/atlas/atlas0_10.json index 93e62d33..676d8bb1 100644 --- a/res_built/atlas/atlas0_10.json +++ b/res_built/atlas/atlas0_10.json @@ -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$" } } diff --git a/res_built/atlas/atlas0_100.json b/res_built/atlas/atlas0_100.json index 398253b9..d15c9c64 100644 --- a/res_built/atlas/atlas0_100.json +++ b/res_built/atlas/atlas0_100.json @@ -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$" } } diff --git a/res_built/atlas/atlas0_25.json b/res_built/atlas/atlas0_25.json index 6ab93d35..68ff1c35 100644 --- a/res_built/atlas/atlas0_25.json +++ b/res_built/atlas/atlas0_25.json @@ -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$" } } diff --git a/res_built/atlas/atlas0_50.json b/res_built/atlas/atlas0_50.json index 64bf7ac2..737bce47 100644 --- a/res_built/atlas/atlas0_50.json +++ b/res_built/atlas/atlas0_50.json @@ -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$" } } diff --git a/res_built/atlas/atlas0_75.json b/res_built/atlas/atlas0_75.json index 9d8ee63a..6ecb53d1 100644 --- a/res_built/atlas/atlas0_75.json +++ b/res_built/atlas/atlas0_75.json @@ -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$" } } diff --git a/res_raw/sprites/misc/deletion_marker.png b/res_raw/sprites/misc/deletion_marker.png new file mode 100644 index 00000000..bb3a2b75 Binary files /dev/null and b/res_raw/sprites/misc/deletion_marker.png differ diff --git a/src/css/ingame_hud/keybindings_overlay.scss b/src/css/ingame_hud/keybindings_overlay.scss index db05aacf..e345955b 100644 --- a/src/css/ingame_hud/keybindings_overlay.scss +++ b/src/css/ingame_hud/keybindings_overlay.scss @@ -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); } } diff --git a/src/css/ingame_hud/mass_selector.scss b/src/css/ingame_hud/mass_selector.scss new file mode 100644 index 00000000..3ac4b763 --- /dev/null +++ b/src/css/ingame_hud/mass_selector.scss @@ -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; + } +} diff --git a/src/css/main.scss b/src/css/main.scss index 6d99f6a5..58c75b45 100644 --- a/src/css/main.scss +++ b/src/css/main.scss @@ -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; diff --git a/src/js/game/hud/hud.js b/src/js/game/hud/hud.js index deae4591..3e4102d4 100644 --- a/src/js/game/hud/hud.js +++ b/src/js/game/hud/hud.js @@ -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]]) { diff --git a/src/js/game/hud/parts/keybinding_overlay.js b/src/js/game/hud/parts/keybinding_overlay.js index a83ef1ce..edf4d88c 100644 --- a/src/js/game/hud/parts/keybinding_overlay.js +++ b/src/js/game/hud/parts/keybinding_overlay.js @@ -44,7 +44,9 @@ export class HUDKeybindingOverlay extends BaseHUDPart {
+
+ ALT
+
+
⇧ SHIFT
+ ⇧ SHIFT
ALT
+ ALT
DEL
to remove selected buildings
+ and ESCAPE
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
+ );
+ });
+ }
+}
diff --git a/src/js/game/key_action_mapper.js b/src/js/game/key_action_mapper.js
index dae0c57e..55adba47 100644
--- a/src/js/game/key_action_mapper.js
+++ b/src/js/game/key_action_mapper.js
@@ -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: {