From 907d2a46b2c636e0191ea0273398b4755fd45807 Mon Sep 17 00:00:00 2001 From: Anuken Date: Thu, 10 Feb 2022 17:36:34 -0500 Subject: [PATCH] Unit control mode --- core/src/mindustry/input/DesktopInput.java | 142 ++++++++------ core/src/mindustry/ui/ItemImage.java | 1 + .../ui/fragments/PlacementFragment.java | 183 ++++++++++++------ 3 files changed, 210 insertions(+), 116 deletions(-) diff --git a/core/src/mindustry/input/DesktopInput.java b/core/src/mindustry/input/DesktopInput.java index 9369c74deb..70fe231b88 100644 --- a/core/src/mindustry/input/DesktopInput.java +++ b/core/src/mindustry/input/DesktopInput.java @@ -115,49 +115,53 @@ public class DesktopInput extends InputHandler{ drawSelection(schemX, schemY, cursorX, cursorY, Vars.maxSchematicSize); } - //draw command overlay UI + if(commandMode){ - for(Unit unit : selectedUnits){ - CommandAI ai = (CommandAI)unit.controller(); - //draw target line - if(ai.targetPos != null){ - Position lineDest = ai.attackTarget != null ? ai.attackTarget : ai.targetPos; + //draw command overlay UI - Tmp.v1.set(lineDest).sub(unit).setLength(unit.hitSize / 2f); - Tmp.v2.set(Tmp.v1).scl(-1f).setLength(3.5f); + for(Unit unit : selectedUnits){ + CommandAI ai = (CommandAI)unit.controller(); + //draw target line + if(ai.targetPos != null){ + Position lineDest = ai.attackTarget != null ? ai.attackTarget : ai.targetPos; - Drawf.line(Pal.accent, unit.x + Tmp.v1.x, unit.y + Tmp.v1.y, lineDest.getX() + Tmp.v2.x, lineDest.getY() + Tmp.v2.y); + Tmp.v1.set(lineDest).sub(unit).setLength(unit.hitSize / 2f); + Tmp.v2.set(Tmp.v1).scl(-1f).setLength(3.5f); - if(ai.attackTarget == null){ - Drawf.square(lineDest.getX(), lineDest.getY(), 3.5f); + Drawf.line(Pal.accent, unit.x + Tmp.v1.x, unit.y + Tmp.v1.y, lineDest.getX() + Tmp.v2.x, lineDest.getY() + Tmp.v2.y); + + if(ai.attackTarget == null){ + Drawf.square(lineDest.getX(), lineDest.getY(), 3.5f); + } + } + + Drawf.square(unit.x, unit.y, unit.hitSize / 1.4f + 1f); + + if(ai.attackTarget != null){ + Drawf.target(ai.attackTarget.getX(), ai.attackTarget.getY(), 6f, Pal.remove); } } - Drawf.square(unit.x, unit.y, unit.hitSize / 1.4f + 1f); + if(commandMode && !commandRect){ + Unit sel = selectedCommandUnit(input.mouseWorldX(), input.mouseWorldY()); - if(ai.attackTarget != null){ - Drawf.target(ai.attackTarget.getX(), ai.attackTarget.getY(), 6f, Pal.remove); + if(sel != null && !(!multiSelect() && selectedUnits.size == 1 && selectedUnits.contains(sel))){ + drawCommand(sel); + } + } + + if(commandRect){ + float x2 = input.mouseWorldX(), y2 = input.mouseWorldY(); + var units = selectedCommandUnits(commandRectX, commandRectY, x2 - commandRectX, y2 - commandRectY); + for(var unit : units){ + drawCommand(unit); + } + + Draw.color(Pal.accent, 0.3f); + Fill.crect(commandRectX, commandRectY, x2 - commandRectX, y2 - commandRectY); } } - if(commandMode && !commandRect){ - Unit sel = selectedCommandUnit(input.mouseWorldX(), input.mouseWorldY()); - - if(sel != null && !(!multiSelect() && selectedUnits.size == 1 && selectedUnits.contains(sel))){ - drawCommand(sel); - } - } - - if(commandRect){ - float x2 = input.mouseWorldX(), y2 = input.mouseWorldY(); - var units = selectedCommandUnits(commandRectX, commandRectY, x2 - commandRectX, y2 - commandRectY); - for(var unit : units){ - drawCommand(unit); - } - - Draw.color(Pal.accent, 0.3f); - Fill.crect(commandRectX, commandRectY, x2 - commandRectX, y2 - commandRectY); - } Draw.reset(); } @@ -271,12 +275,19 @@ public class DesktopInput extends InputHandler{ } } - commandMode = input.keyDown(Binding.commandMode) && !locked && state.rules.unitCommand && block == null; shouldShoot = !scene.hasMouse() && !locked; + if(!locked && state.rules.unitCommand && block == null){ + if(input.keyTap(Binding.commandMode)){ + commandMode = !commandMode; + } + }else{ + commandMode = false; + } + //TODO should selected units be cleared out of command mode? if(!commandMode){ - selectedUnits.clear(); + //selectedUnits.clear(); } //validate commanding units @@ -549,7 +560,8 @@ public class DesktopInput extends InputHandler{ if(multiSelect()){ //tiny brain method of unique addition selectedUnits.removeAll(units); - }else if(units.size > 0){ + }else{ + //nothing selected, clear units selectedUnits.clear(); } selectedUnits.addAll(units); @@ -673,7 +685,7 @@ public class DesktopInput extends InputHandler{ } } - //TODO + //TODO when shift is held? ctrl? public boolean multiSelect(){ return false; } @@ -684,37 +696,49 @@ public class DesktopInput extends InputHandler{ tappedOne = true; - Unit unit = selectedCommandUnit(input.mouseWorldX(), input.mouseWorldY()); - if(unit != null){ - if(!multiSelect()){ - selectedUnits.clear(); - selectedUnits.add(unit); - }else{ - if(selectedUnits.contains(unit)){ - selectedUnits.remove(unit); - }else{ + //click: select a single unit + if(button == KeyCode.mouseLeft){ + Unit unit = selectedCommandUnit(input.mouseWorldX(), input.mouseWorldY()); + if(unit != null){ + if(!multiSelect()){ + selectedUnits.clear(); selectedUnits.add(unit); + }else{ + if(selectedUnits.contains(unit)){ + selectedUnits.remove(unit); + }else{ + selectedUnits.add(unit); + } } + }else{ + //deselect + selectedUnits.clear(); } - }else if(selectedUnits.size > 0){ - //move to location - TODO right click instead? - Vec2 target = input.mouseWorld().cpy(); + }else if(button == KeyCode.mouseRight){ + //right click: move to position - Teamc attack = world.buildWorld(target.x, target.y); + if(selectedUnits.size > 0){ + //move to location - TODO right click instead? + Vec2 target = input.mouseWorld().cpy(); + + Teamc attack = world.buildWorld(target.x, target.y); + + if(attack == null || attack.team() == player.team()){ + attack = selectedEnemyUnit(target.x, target.y); + } + + int[] ids = new int[selectedUnits.size]; + for(int i = 0; i < ids.length; i++){ + ids[i] = selectedUnits.get(i).id; + } + + Call.commandUnits(player, ids, attack instanceof Building b ? b : null, attack instanceof Unit u ? u : null, target); - if(attack == null || attack.team() == player.team()){ - attack = selectedEnemyUnit(target.x, target.y); } - - int[] ids = new int[selectedUnits.size]; - for(int i = 0; i < ids.length; i++){ - ids[i] = selectedUnits.get(i).id; - } - - Call.commandUnits(player, ids, attack instanceof Building b ? b : null, attack instanceof Unit u ? u : null, target); - } + + return super.tap(x, y, count, button); } diff --git a/core/src/mindustry/ui/ItemImage.java b/core/src/mindustry/ui/ItemImage.java index e80b8804a5..20829c071b 100644 --- a/core/src/mindustry/ui/ItemImage.java +++ b/core/src/mindustry/ui/ItemImage.java @@ -17,6 +17,7 @@ public class ItemImage extends Stack{ add(new Table(t -> { t.left().bottom(); + //TODO outline? .style(Styles.outlineLabel) t.add(amount > 1000 ? UI.formatAmount(amount) : amount + ""); t.pack(); })); diff --git a/core/src/mindustry/ui/fragments/PlacementFragment.java b/core/src/mindustry/ui/fragments/PlacementFragment.java index cb43e89fc2..91f2656d9b 100644 --- a/core/src/mindustry/ui/fragments/PlacementFragment.java +++ b/core/src/mindustry/ui/fragments/PlacementFragment.java @@ -42,9 +42,10 @@ public class PlacementFragment extends Fragment{ Object lastDisplayState; Team lastTeam; boolean wasHovered; - Table blockTable, toggler, topTable; + Table blockTable, toggler, topTable, blockCatTable, commandTable; + Stack mainStack; ScrollPane blockPane; - boolean blockSelectEnd; + boolean blockSelectEnd, wasCommandMode; int blockSelectSeq; long blockSelectSeqMillis; Binding[] blockSelect = { @@ -366,68 +367,136 @@ public class PlacementFragment extends Fragment{ hovered.display(topTable); } }); - }).colspan(3).fillX().visible(this::hasInfoBox).touchable(Touchable.enabled); - frame.row(); - frame.image().color(Pal.gray).colspan(3).height(4).growX(); - frame.row(); - frame.table(Tex.pane2, blocksSelect -> { - blocksSelect.margin(4).marginTop(0); - blockPane = blocksSelect.pane(blocks -> blockTable = blocks).height(194f).update(pane -> { - if(pane.hasScroll()){ - Element result = Core.scene.hit(Core.input.mouseX(), Core.input.mouseY(), true); - if(result == null || !result.isDescendantOf(pane)){ - Core.scene.setScrollFocus(null); - } - } - }).grow().get(); - blockPane.setStyle(Styles.smallPane); - blocksSelect.row(); - blocksSelect.table(control.input::buildPlacementUI).name("inputTable").growX(); - }).fillY().bottom().touchable(Touchable.enabled); - frame.table(categories -> { - categories.bottom(); - categories.add(new Image(Styles.black6){ - @Override - public void draw(){ - if(height <= Scl.scl(3f)) return; - getDrawable().draw(x, y, width, height - Scl.scl(3f)); - } - }).colspan(2).growX().growY().padTop(-3f).row(); - categories.defaults().size(50f); + }).colspan(3).fillX().visible(this::hasInfoBox).touchable(Touchable.enabled).row(); - ButtonGroup group = new ButtonGroup<>(); + frame.image().color(Pal.gray).colspan(3).height(4).growX().row(); - //update category empty values - for(Category cat : Category.all){ - Seq blocks = getUnlockedByCategory(cat); - categoryEmpty[cat.ordinal()] = blocks.isEmpty(); + blockCatTable = new Table(); + commandTable = new Table(Tex.pane2); + mainStack = new Stack(); + + mainStack.update(() -> { + if(control.input.commandMode != wasCommandMode){ + mainStack.clearChildren(); + mainStack.addChild(control.input.commandMode ? commandTable : blockCatTable); + wasCommandMode = control.input.commandMode; + + //hacky, but forces command table to be same width as blocks + if(wasCommandMode){ + commandTable.getCells().peek().width(blockCatTable.getWidth()); + } } - - boolean needsAssign = categoryEmpty[currentCategory.ordinal()]; + }); - int f = 0; - for(Category cat : getCategories()){ - if(f++ % 2 == 0) categories.row(); + frame.add(mainStack).colspan(3).fill(); - if(categoryEmpty[cat.ordinal()]){ - categories.image(Styles.black6); - continue; - } - - if(needsAssign){ - currentCategory = cat; - needsAssign = false; - } + //commandTable: commanded units + { + commandTable.touchable = Touchable.enabled; + commandTable.add("[accent]Command Mode").fill().center().labelAlign(Align.center).row(); + commandTable.image().color(Pal.accent).growX().pad(20f).padTop(0f).padBottom(4f).row(); + commandTable.table(u -> { + u.left(); + int[] curCount = {0}; - categories.button(ui.getIcon(cat.name()), Styles.clearToggleTransi, () -> { - currentCategory = cat; - if(control.input.block != null){ - control.input.block = getSelectedBlock(currentCategory); + Runnable rebuildCommand = () -> { + u.clearChildren(); + var units = control.input.selectedUnits; + if(units.size > 0){ + int[] counts = new int[content.units().size]; + for(var unit : units){ + counts[unit.type.id] ++; + } + int col = 0; + for(int i = 0; i < counts.length; i++){ + if(counts[i] > 0){ + var type = content.unit(i); + u.add(new ItemImage(type.uiIcon, counts[i])).tooltip(type.localizedName).pad(4); + + if(++col % 7 == 0){ + u.row(); + } + } + } + }else{ + u.add("[no units]").color(Color.lightGray).growX().center().labelAlign(Align.center).pad(6); } - rebuildCategory.run(); - }).group(group).update(i -> i.setChecked(currentCategory == cat)).name("category-" + cat.name()); - } - }).fillY().bottom().touchable(Touchable.enabled); + }; + + u.update(() -> { + int size = control.input.selectedUnits.size; + if(curCount[0] != size){ + curCount[0] = size; + rebuildCommand.run(); + } + }); + rebuildCommand.run(); + }).grow(); + } + + //blockCatTable: all blocks | all categories + { + blockCatTable.table(Tex.pane2, blocksSelect -> { + blocksSelect.margin(4).marginTop(0); + blockPane = blocksSelect.pane(blocks -> blockTable = blocks).height(194f).update(pane -> { + if(pane.hasScroll()){ + Element result = Core.scene.hit(Core.input.mouseX(), Core.input.mouseY(), true); + if(result == null || !result.isDescendantOf(pane)){ + Core.scene.setScrollFocus(null); + } + } + }).grow().get(); + blockPane.setStyle(Styles.smallPane); + blocksSelect.row(); + blocksSelect.table(control.input::buildPlacementUI).name("inputTable").growX(); + }).fillY().bottom().touchable(Touchable.enabled); + blockCatTable.table(categories -> { + categories.bottom(); + categories.add(new Image(Styles.black6){ + @Override + public void draw(){ + if(height <= Scl.scl(3f)) return; + getDrawable().draw(x, y, width, height - Scl.scl(3f)); + } + }).colspan(2).growX().growY().padTop(-3f).row(); + categories.defaults().size(50f); + + ButtonGroup group = new ButtonGroup<>(); + + //update category empty values + for(Category cat : Category.all){ + Seq blocks = getUnlockedByCategory(cat); + categoryEmpty[cat.ordinal()] = blocks.isEmpty(); + } + + boolean needsAssign = categoryEmpty[currentCategory.ordinal()]; + + int f = 0; + for(Category cat : getCategories()){ + if(f++ % 2 == 0) categories.row(); + + if(categoryEmpty[cat.ordinal()]){ + categories.image(Styles.black6); + continue; + } + + if(needsAssign){ + currentCategory = cat; + needsAssign = false; + } + + categories.button(ui.getIcon(cat.name()), Styles.clearToggleTransi, () -> { + currentCategory = cat; + if(control.input.block != null){ + control.input.block = getSelectedBlock(currentCategory); + } + rebuildCategory.run(); + }).group(group).update(i -> i.setChecked(currentCategory == cat)).name("category-" + cat.name()); + } + }).fillY().bottom().touchable(Touchable.enabled); + } + + mainStack.add(blockCatTable); rebuildCategory.run(); frame.update(() -> {