From aa0e254f0e018206aa173299bc8b15490a7b1138 Mon Sep 17 00:00:00 2001 From: Anuken Date: Sat, 7 May 2022 00:30:25 -0400 Subject: [PATCH] Mobile unit command support --- core/assets/bundles/bundle.properties | 1 + core/src/mindustry/content/SectorPresets.java | 2 +- core/src/mindustry/input/DesktopInput.java | 104 +--------------- core/src/mindustry/input/InputHandler.java | 115 ++++++++++++++++++ core/src/mindustry/input/MobileInput.java | 84 +++++++++---- 5 files changed, 181 insertions(+), 125 deletions(-) diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index ce027b37b3..d0ac2818fa 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -305,6 +305,7 @@ ok = OK open = Open customize = Customize Rules cancel = Cancel +command = Command openlink = Open Link copylink = Copy Link back = Back diff --git a/core/src/mindustry/content/SectorPresets.java b/core/src/mindustry/content/SectorPresets.java index d2a8321c5c..efdd1cb896 100644 --- a/core/src/mindustry/content/SectorPresets.java +++ b/core/src/mindustry/content/SectorPresets.java @@ -123,7 +123,7 @@ public class SectorPresets{ new ShapeTextMarker("Click to mine [accent]resources[] from walls.", 290f * 8f, 106f * 8f) ), new BuildCountObjective(Blocks.turbineCondenser, 1).withMarkers( - new ShapeTextMarker("Place a [accent]turbine condenser[] on the vent.\nThis will generate [accent]power[].", 289f * 8f, 116f * 8f, 8f * 2.6f, 0f, 9f) + new ShapeTextMarker("Open the tech tree.\nResearch, then place a [accent]turbine condenser[] on the vent.\nThis will generate [accent]power[].", 289f * 8f, 116f * 8f, 8f * 2.6f, 0f, 9f) ), new BuildCountObjective(Blocks.plasmaBore, 1).withMarkers( new ShapeTextMarker("Research and place a [accent]plasma bore[]. \nThis automatically mines resources from walls.", 293.5f * 8f, 113.5f * 8f, 4f * 2.6f, 45f, 60f) diff --git a/core/src/mindustry/input/DesktopInput.java b/core/src/mindustry/input/DesktopInput.java index 3916240be5..7f53983c52 100644 --- a/core/src/mindustry/input/DesktopInput.java +++ b/core/src/mindustry/input/DesktopInput.java @@ -12,7 +12,6 @@ import arc.scene.*; import arc.scene.ui.layout.*; import arc.util.*; import mindustry.*; -import mindustry.ai.types.*; import mindustry.core.*; import mindustry.entities.units.*; import mindustry.game.EventType.*; @@ -114,68 +113,11 @@ public class DesktopInput extends InputHandler{ drawSelection(schemX, schemY, cursorX, cursorY, Vars.maxSchematicSize); } - if(commandMode){ - //happens sometimes - selectedUnits.removeAll(u -> !u.isCommandable()); - - //draw command overlay UI - for(Unit unit : selectedUnits){ - CommandAI ai = unit.command(); - //draw target line - if(ai.targetPos != null){ - Position lineDest = ai.attackTarget != null ? ai.attackTarget : ai.targetPos; - Drawf.limitLine(unit, lineDest, unit.hitSize / 2f, 3.5f); - - 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); - } - } - - if(commandBuild != null){ - Drawf.square(commandBuild.x, commandBuild.y, commandBuild.hitSize() / 1.4f + 1f); - var cpos = commandBuild.getCommandPosition(); - - if(cpos != null){ - Drawf.limitLine(commandBuild, cpos, commandBuild.hitSize() / 2f, 3.5f); - Drawf.square(cpos.x, cpos.y, 3.5f); - } - } - - 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); - } - } - + drawCommanded(); Draw.reset(); } - public void drawCommand(Unit sel){ - Drawf.square(sel.x, sel.y, sel.hitSize / 1.4f + Mathf.absin(4f, 1f), selectedUnits.contains(sel) ? Pal.remove : Pal.accent); - } - @Override public void drawBottom(){ int cursorX = tileX(Core.input.mouseX()); @@ -575,19 +517,7 @@ public class DesktopInput extends InputHandler{ //select some units if(Core.input.keyRelease(Binding.select) && commandRect){ - if(!tappedOne){ - var units = selectedCommandUnits(commandRectX, commandRectY, input.mouseWorldX() - commandRectX, input.mouseWorldY() - commandRectY); - if(multiSelect()){ - //tiny brain method of unique addition - selectedUnits.removeAll(units); - }else{ - //nothing selected, clear units - selectedUnits.clear(); - } - selectedUnits.addAll(units); - commandBuild = null; - } - commandRect = false; + selectUnitsRect(); } if(Core.input.keyTap(Binding.select) && !Core.scene.hasMouse()){ @@ -711,11 +641,6 @@ public class DesktopInput extends InputHandler{ } } - //TODO when shift is held? ctrl? - public boolean multiSelect(){ - return false; - } - @Override public boolean tap(float x, float y, int count, KeyCode button){ if(scene.hasMouse() || !commandMode) return false; @@ -754,30 +679,7 @@ public class DesktopInput extends InputHandler{ if(scene.hasMouse() || !commandMode) return false; if(button == KeyCode.mouseRight){ - //right click: move to position - - //move to location - TODO right click instead? - Vec2 target = input.mouseWorld().cpy(); - - if(selectedUnits.size > 0){ - - 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(commandBuild != null){ - Call.commandBuilding(player, commandBuild, target); - } + commandTap(x, y); } return super.touchDown(x, y, pointer, button); diff --git a/core/src/mindustry/input/InputHandler.java b/core/src/mindustry/input/InputHandler.java index d646485236..6e4b3fe53d 100644 --- a/core/src/mindustry/input/InputHandler.java +++ b/core/src/mindustry/input/InputHandler.java @@ -698,6 +698,121 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ } } + //TODO when shift is held? ctrl? + public boolean multiUnitSelect(){ + return false; + } + + public void selectUnitsRect(){ + if(commandMode && commandRect){ + if(!tappedOne){ + var units = selectedCommandUnits(commandRectX, commandRectY, input.mouseWorldX() - commandRectX, input.mouseWorldY() - commandRectY); + if(multiUnitSelect()){ + //tiny brain method of unique addition + selectedUnits.removeAll(units); + }else{ + //nothing selected, clear units + selectedUnits.clear(); + } + selectedUnits.addAll(units); + commandBuild = null; + } + commandRect = false; + } + } + + public void commandTap(float screenX, float screenY){ + if(commandMode){ + //right click: move to position + + //move to location - TODO right click instead? + Vec2 target = input.mouseWorld(screenX, screenY).cpy(); + + if(selectedUnits.size > 0){ + + 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(commandBuild != null){ + Call.commandBuilding(player, commandBuild, target); + } + } + } + + public void drawCommand(Unit sel){ + Drawf.square(sel.x, sel.y, sel.hitSize / 1.4f + Mathf.absin(4f, 1f), selectedUnits.contains(sel) ? Pal.remove : Pal.accent); + } + + public void drawCommanded(){ + if(commandMode){ + //happens sometimes + selectedUnits.removeAll(u -> !u.isCommandable()); + + //draw command overlay UI + for(Unit unit : selectedUnits){ + CommandAI ai = unit.command(); + //draw target line + if(ai.targetPos != null){ + Position lineDest = ai.attackTarget != null ? ai.attackTarget : ai.targetPos; + Drawf.limitLine(unit, lineDest, unit.hitSize / 2f, 3.5f); + + 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); + } + } + + if(commandBuild != null){ + Drawf.square(commandBuild.x, commandBuild.y, commandBuild.hitSize() / 1.4f + 1f); + var cpos = commandBuild.getCommandPosition(); + + if(cpos != null){ + Drawf.limitLine(commandBuild, cpos, commandBuild.hitSize() / 2f, 3.5f); + Drawf.square(cpos.x, cpos.y, 3.5f); + } + } + + if(commandMode && !commandRect){ + Unit sel = selectedCommandUnit(input.mouseWorldX(), input.mouseWorldY()); + + if(sel != null && !(!multiUnitSelect() && 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(); + + } + public void drawBottom(){ } diff --git a/core/src/mindustry/input/MobileInput.java b/core/src/mindustry/input/MobileInput.java index bd0aecb493..f708412ef5 100644 --- a/core/src/mindustry/input/MobileInput.java +++ b/core/src/mindustry/input/MobileInput.java @@ -1,7 +1,6 @@ package mindustry.input; import arc.*; -import arc.func.*; import arc.graphics.g2d.*; import arc.input.GestureDetector.*; import arc.input.*; @@ -26,6 +25,7 @@ import mindustry.ui.*; import mindustry.world.*; import mindustry.world.blocks.*; +import static arc.Core.*; import static mindustry.Vars.*; import static mindustry.input.PlaceMode.*; @@ -252,12 +252,19 @@ public class MobileInput extends InputHandler implements GestureListener{ }).visible(() -> !selectPlans.isEmpty()).name("confirmplace"); } + boolean showCancel(){ + return (player.unit().isBuilding() || block != null || mode == breaking || !selectPlans.isEmpty()) && !hasSchem(); + } + + boolean hasSchem(){ + return lastSchematic != null && !selectPlans.isEmpty(); + } + @Override public void buildUI(Group group){ - Boolp schem = () -> lastSchematic != null && !selectPlans.isEmpty(); group.fill(t -> { - t.visible(() -> (player.unit().isBuilding() || block != null || mode == breaking || !selectPlans.isEmpty()) && !schem.get()); + t.visible(this::showCancel); t.bottom().left(); t.button("@cancel", Icon.cancel, () -> { player.unit().clearBuilding(); @@ -268,7 +275,15 @@ public class MobileInput extends InputHandler implements GestureListener{ }); group.fill(t -> { - t.visible(schem); + t.visible(() -> !showCancel() && block == null); + t.bottom().left(); + t.button("@command", Icon.units, Styles.squareTogglet, () -> { + commandMode = !commandMode; + }).width(155f).height(50f).margin(12f).checked(b -> commandMode); + }); + + group.fill(t -> { + t.visible(this::hasSchem); t.bottom().left(); t.table(Tex.pane, b -> { b.defaults().size(50f); @@ -342,6 +357,8 @@ public class MobileInput extends InputHandler implements GestureListener{ if(mode == schematicSelect){ drawSelection(lineStartX, lineStartY, lastLineX, lastLineY, Vars.maxSchematicSize); } + + drawCommanded(); } @Override @@ -522,6 +539,10 @@ public class MobileInput extends InputHandler implements GestureListener{ tryDropItems(tile == null ? null : tile.build, Core.input.mouseWorld(screenX, screenY).x, Core.input.mouseWorld(screenX, screenY).y); } + + //select some units + selectUnitsRect(); + return false; } @@ -538,26 +559,36 @@ public class MobileInput extends InputHandler implements GestureListener{ if(mode == none){ Vec2 pos = Core.input.mouseWorld(x, y); - if(player.unit() instanceof Payloadc pay){ - Unit target = Units.closest(player.team(), pos.x, pos.y, 8f, u -> u.isAI() && u.isGrounded() && pay.canPickup(u) && u.within(pos, u.hitSize + 8f)); - if(target != null){ - payloadTarget = target; - }else{ - Building build = world.buildWorld(pos.x, pos.y); + if(commandMode){ + + //long press begins rect selection. + commandRect = true; + commandRectX = input.mouseWorldX(); + commandRectY = input.mouseWorldY(); - if(build != null && build.team == player.team() && (pay.canPickup(build) || build.getPayload() != null && pay.canPickupPayload(build.getPayload()))){ - payloadTarget = build; - }else if(pay.hasPayload()){ - //drop off at position - payloadTarget = new Vec2(pos); - }else{ - manualShooting = true; - this.target = null; - } - } }else{ - manualShooting = true; - this.target = null; + + if(player.unit() instanceof Payloadc pay){ + Unit target = Units.closest(player.team(), pos.x, pos.y, 8f, u -> u.isAI() && u.isGrounded() && pay.canPickup(u) && u.within(pos, u.hitSize + 8f)); + if(target != null){ + payloadTarget = target; + }else{ + Building build = world.buildWorld(pos.x, pos.y); + + if(build != null && build.team == player.team() && (pay.canPickup(build) || build.getPayload() != null && pay.canPickupPayload(build.getPayload()))){ + payloadTarget = build; + }else if(pay.hasPayload()){ + //drop off at position + payloadTarget = new Vec2(pos); + }else{ + manualShooting = true; + this.target = null; + } + } + }else{ + manualShooting = true; + this.target = null; + } } if(!state.isPaused()) Fx.select.at(pos); @@ -615,6 +646,9 @@ public class MobileInput extends InputHandler implements GestureListener{ }else if(mode == breaking && validBreak(linked.x,linked.y) && !hasPlan(linked)){ //add to selection queue if it's a valid BREAK position selectPlans.add(new BuildPlan(linked.x, linked.y)); + }else if(commandMode && selectedUnits.size > 0){ + //handle selecting units with command mode + commandTap(x, y); }else{ //control units if(count == 2){ @@ -671,6 +705,10 @@ public class MobileInput extends InputHandler implements GestureListener{ payloadTarget = null; } + if(locked || block != null || scene.hasField() || hasSchem() || selectPlans.size > 0){ + commandMode = false; + } + //zoom camera if(!locked && Math.abs(Core.input.axisTap(Binding.zoom)) > 0 && !Core.input.keyDown(Binding.rotateplaced) && (Core.input.keyDown(Binding.diagonal_placement) || ((!player.isBuilder() || !isPlacing() || !block.rotate) && selectPlans.isEmpty()))){ renderer.scaleCamera(Core.input.axisTap(Binding.zoom)); @@ -801,7 +839,7 @@ public class MobileInput extends InputHandler implements GestureListener{ @Override public boolean pan(float x, float y, float deltaX, float deltaY){ - if(Core.scene == null || Core.scene.hasDialog() || Core.settings.getBool("keyboard") || locked()) return false; + if(Core.scene == null || Core.scene.hasDialog() || Core.settings.getBool("keyboard") || locked() || commandRect) return false; float scale = Core.camera.width / Core.graphics.getWidth(); deltaX *= scale;