From bd89c2cc9ead04111fda10648d20ef1905010e4d Mon Sep 17 00:00:00 2001 From: Tobias Springer Date: Sun, 10 May 2020 18:24:50 +0200 Subject: [PATCH] Mass deletion support --- res_built/atlas/atlas0_10.json | 10 +- res_built/atlas/atlas0_100.json | 10 +- res_built/atlas/atlas0_25.json | 10 +- res_built/atlas/atlas0_50.json | 10 +- res_built/atlas/atlas0_75.json | 10 +- res_raw/sprites/misc/deletion_marker.png | Bin 0 -> 4745 bytes src/css/ingame_hud/keybindings_overlay.scss | 4 +- src/css/ingame_hud/mass_selector.scss | 20 ++ src/css/main.scss | 3 +- src/js/game/hud/hud.js | 5 +- src/js/game/hud/parts/keybinding_overlay.js | 12 +- src/js/game/hud/parts/mass_selector.js | 195 +++++++++++++++++++- src/js/game/key_action_mapper.js | 2 + 13 files changed, 276 insertions(+), 15 deletions(-) create mode 100644 res_raw/sprites/misc/deletion_marker.png create mode 100644 src/css/ingame_hud/mass_selector.scss 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 0000000000000000000000000000000000000000..bb3a2b753c0d4326e577c8d4e9bf3536be21113a GIT binary patch literal 4745 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 literal 0 HcmV?d00001 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+ +
@@ -60,13 +62,13 @@ export class HUDKeybindingOverlay extends BaseHUDPart { -
- ⇧ SHIFT +
+ ⇧ SHIFT
-
- ALT +
+ ALT
` diff --git a/src/js/game/hud/parts/mass_selector.js b/src/js/game/hud/parts/mass_selector.js index 658eb8ee..f8b0cb83 100644 --- a/src/js/game/hud/parts/mass_selector.js +++ b/src/js/game/hud/parts/mass_selector.js @@ -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 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: {