package mindustry.input; import arc.*; import arc.Graphics.*; import arc.Graphics.Cursor.*; import arc.graphics.*; import arc.graphics.g2d.*; import arc.input.*; import arc.input.KeyCode.*; import arc.math.*; import arc.math.geom.*; import arc.scene.*; import arc.scene.ui.layout.*; import arc.struct.*; import arc.util.*; import mindustry.*; import mindustry.core.*; import mindustry.entities.units.*; import mindustry.game.EventType.*; import mindustry.game.*; import mindustry.gen.*; import mindustry.graphics.*; import mindustry.ui.*; import mindustry.world.*; import static arc.Core.camera; import static arc.Core.*; import static mindustry.Vars.*; import static mindustry.input.PlaceMode.*; public class DesktopInput extends InputHandler{ public Vec2 movement = new Vec2(); /** Current cursor type. */ public Cursor cursorType = SystemCursor.arrow; /** Position where the player started dragging a line. */ public int selectX = -1, selectY = -1, schemX = -1, schemY = -1; /** Last known line positions.*/ public int lastLineX, lastLineY, schematicX, schematicY; /** Whether selecting mode is active. */ public PlaceMode mode; /** Animation scale for line. */ public float selectScale; /** Selected build plan for movement. */ public @Nullable BuildPlan splan; /** Whether player is currently deleting removal plans. */ public boolean deleting = false, shouldShoot = false, panning = false; /** Mouse pan speed. */ public float panScale = 0.005f, panSpeed = 4.5f, panBoostSpeed = 15f; /** Delta time between consecutive clicks. */ public long selectMillis = 0; /** Previously selected tile. */ public Tile prevSelected; /** Most recently selected control group by index */ public int lastCtrlGroup; /** Time of most recent control group selection */ public long lastCtrlGroupSelectMillis; boolean showHint(){ return ui.hudfrag.shown && Core.settings.getBool("hints") && selectPlans.isEmpty() && (!isBuilding && !Core.settings.getBool("buildautopause") || player.unit().isBuilding() || !player.dead() && !player.unit().spawnedByCore()); } @Override public void buildUI(Group group){ //building and respawn hints group.fill(t -> { t.color.a = 0f; t.visible(() -> (t.color.a = Mathf.lerpDelta(t.color.a, Mathf.num(showHint()), 0.15f)) > 0.001f); t.bottom(); t.table(Styles.black6, b -> { StringBuilder str = new StringBuilder(); b.defaults().left(); b.label(() -> { if(!showHint()) return str; str.setLength(0); if(!isBuilding && !Core.settings.getBool("buildautopause") && !player.unit().isBuilding()){ str.append(Core.bundle.format("enablebuilding", Core.keybinds.get(Binding.pause_building).key.toString())); }else if(player.unit().isBuilding()){ str.append(Core.bundle.format(isBuilding ? "pausebuilding" : "resumebuilding", Core.keybinds.get(Binding.pause_building).key.toString())) .append("\n").append(Core.bundle.format("cancelbuilding", Core.keybinds.get(Binding.clear_building).key.toString())) .append("\n").append(Core.bundle.format("selectschematic", Core.keybinds.get(Binding.schematic_select).key.toString())); } if(!player.dead() && !player.unit().spawnedByCore()){ str.append(str.length() != 0 ? "\n" : "").append(Core.bundle.format("respawn", Core.keybinds.get(Binding.respawn).key.toString())); } return str; }).style(Styles.outlineLabel); }).margin(10f); }); //schematic controls group.fill(t -> { t.visible(() -> ui.hudfrag.shown && lastSchematic != null && !selectPlans.isEmpty()); t.bottom(); t.table(Styles.black6, b -> { b.defaults().left(); b.label(() -> Core.bundle.format("schematic.flip", Core.keybinds.get(Binding.schematic_flip_x).key.toString(), Core.keybinds.get(Binding.schematic_flip_y).key.toString())).style(Styles.outlineLabel).visible(() -> Core.settings.getBool("hints")); b.row(); b.table(a -> { a.button("@schematic.add", Icon.save, this::showSchematicSave).colspan(2).size(250f, 50f).disabled(f -> lastSchematic == null || lastSchematic.file != null); }); }).margin(6f); }); } @Override public void drawTop(){ Lines.stroke(1f); int cursorX = tileX(Core.input.mouseX()); int cursorY = tileY(Core.input.mouseY()); //draw break selection if(mode == breaking){ drawBreakSelection(selectX, selectY, cursorX, cursorY, !Core.input.keyDown(Binding.schematic_select) ? maxLength : Vars.maxSchematicSize); } if(!Core.scene.hasKeyboard() && mode != breaking){ if(Core.input.keyDown(Binding.schematic_select)){ drawSelection(schemX, schemY, cursorX, cursorY, Vars.maxSchematicSize); }else if(Core.input.keyDown(Binding.rebuild_select)){ drawRebuildSelection(schemX, schemY, cursorX, cursorY); } } drawCommanded(); Draw.reset(); } @Override public void drawBottom(){ int cursorX = tileX(Core.input.mouseX()); int cursorY = tileY(Core.input.mouseY()); //draw plan being moved if(splan != null){ boolean valid = validPlace(splan.x, splan.y, splan.block, splan.rotation, splan); if(splan.block.rotate && splan.block.drawArrow){ drawArrow(splan.block, splan.x, splan.y, splan.rotation, valid); } splan.block.drawPlan(splan, allPlans(), valid); drawSelected(splan.x, splan.y, splan.block, getPlan(splan.x, splan.y, splan.block.size, splan) != null ? Pal.remove : Pal.accent); } //draw hover plans if(mode == none && !isPlacing()){ var plan = getPlan(cursorX, cursorY); if(plan != null){ drawSelected(plan.x, plan.y, plan.breaking ? plan.tile().block() : plan.block, Pal.accent); } } var items = selectPlans.items; int size = selectPlans.size; //draw schematic plans for(int i = 0; i < size; i++){ var plan = items[i]; plan.animScale = 1f; drawPlan(plan); } //draw schematic plans - over version, cached results for(int i = 0; i < size; i++){ var plan = items[i]; //use cached value from previous invocation drawOverPlan(plan, plan.cachedValid); } if(player.isBuilder()){ //draw things that may be placed soon if(mode == placing && block != null){ for(int i = 0; i < linePlans.size; i++){ var plan = linePlans.get(i); if(i == linePlans.size - 1 && plan.block.rotate && plan.block.drawArrow){ drawArrow(block, plan.x, plan.y, plan.rotation); } drawPlan(linePlans.get(i)); } linePlans.each(this::drawOverPlan); }else if(isPlacing()){ int rot = block == null ? rotation : block.planRotation(rotation); if(block.rotate && block.drawArrow){ drawArrow(block, cursorX, cursorY, rot); } Draw.color(); boolean valid = validPlace(cursorX, cursorY, block, rot); drawPlan(cursorX, cursorY, block, rot); block.drawPlace(cursorX, cursorY, rot, valid); if(block.saveConfig){ Draw.mixcol(!valid ? Pal.breakInvalid : Color.white, (!valid ? 0.4f : 0.24f) + Mathf.absin(Time.globalTime, 6f, 0.28f)); bplan.set(cursorX, cursorY, rot, block); bplan.config = block.lastConfig; block.drawPlanConfig(bplan, allPlans()); bplan.config = null; Draw.reset(); } drawOverlapCheck(block, cursorX, cursorY, valid); } } Draw.reset(); } @Override public void update(){ super.update(); if(net.active() && Core.input.keyTap(Binding.player_list) && (scene.getKeyboardFocus() == null || scene.getKeyboardFocus().isDescendantOf(ui.listfrag.content) || scene.getKeyboardFocus().isDescendantOf(ui.minimapfrag.elem))){ ui.listfrag.toggle(); } boolean locked = locked(); boolean panCam = false; float camSpeed = (!Core.input.keyDown(Binding.boost) ? panSpeed : panBoostSpeed) * Time.delta; if(input.keyDown(Binding.pan) && !scene.hasField() && !scene.hasDialog()){ panCam = true; panning = true; } if((Math.abs(Core.input.axis(Binding.move_x)) > 0 || Math.abs(Core.input.axis(Binding.move_y)) > 0 || input.keyDown(Binding.mouse_move)) && (!scene.hasField())){ panning = false; } if(!locked){ if(((player.dead() || state.isPaused()) && !ui.chatfrag.shown()) && !scene.hasField() && !scene.hasDialog()){ if(input.keyDown(Binding.mouse_move)){ panCam = true; } Core.camera.position.add(Tmp.v1.setZero().add(Core.input.axis(Binding.move_x), Core.input.axis(Binding.move_y)).nor().scl(camSpeed)); }else if(!player.dead() && !panning){ //TODO do not pan Team corePanTeam = state.won ? state.rules.waveTeam : player.team(); Position coreTarget = state.gameOver && !state.rules.pvp && corePanTeam.data().lastCore != null ? corePanTeam.data().lastCore : null; Core.camera.position.lerpDelta(coreTarget != null ? coreTarget : player, Core.settings.getBool("smoothcamera") ? 0.08f : 1f); } if(panCam){ Core.camera.position.x += Mathf.clamp((Core.input.mouseX() - Core.graphics.getWidth() / 2f) * panScale, -1, 1) * camSpeed; Core.camera.position.y += Mathf.clamp((Core.input.mouseY() - Core.graphics.getHeight() / 2f) * panScale, -1, 1) * camSpeed; } } shouldShoot = !scene.hasMouse() && !locked; if(!locked && block == null && !scene.hasField() && !scene.hasDialog() && //disable command mode when player unit can boost and command mode binding is the same !(!player.dead() && player.unit().type.canBoost && keybinds.get(Binding.command_mode).key == keybinds.get(Binding.boost).key)){ if(settings.getBool("commandmodehold")){ commandMode = input.keyDown(Binding.command_mode); }else if(input.keyTap(Binding.command_mode)){ commandMode = !commandMode; } }else{ commandMode = false; } //validate commanding units selectedUnits.removeAll(u -> !u.isCommandable() || !u.isValid()); if(commandMode && !scene.hasField() && !scene.hasDialog()){ if(input.keyTap(Binding.select_all_units)){ selectedUnits.clear(); commandBuildings.clear(); for(var unit : player.team().data().units){ if(unit.isCommandable()){ selectedUnits.add(unit); } } } if(input.keyTap(Binding.select_all_unit_factories)){ selectedUnits.clear(); commandBuildings.clear(); for(var build : player.team().data().buildings){ if(build.block.commandable){ commandBuildings.add(build); } } } for(int i = 0; i < controlGroupBindings.length; i++){ if(input.keyTap(controlGroupBindings[i])){ //create control group if it doesn't exist yet if(controlGroups[i] == null) controlGroups[i] = new IntSeq(); IntSeq group = controlGroups[i]; boolean creating = input.keyDown(Binding.create_control_group); //clear existing if making a new control group //if any of the control group edit buttons are pressed take the current selection if(creating){ group.clear(); IntSeq selectedUnitIds = selectedUnits.mapInt(u -> u.id); if(Core.settings.getBool("distinctcontrolgroups", true)){ for(IntSeq cg : controlGroups){ if(cg != null){ cg.removeAll(selectedUnitIds); } } } group.addAll(selectedUnitIds); } //remove invalid units for(int j = 0; j < group.size; j++){ Unit u = Groups.unit.getByID(group.get(j)); if(u == null || !u.isCommandable() || !u.isValid()){ group.removeIndex(j); j --; } } //replace the selected units with the current control group if(!group.isEmpty() && !creating){ selectedUnits.clear(); commandBuildings.clear(); group.each(id -> { var unit = Groups.unit.getByID(id); if(unit != null){ selectedUnits.addAll(unit); } }); //double tap to center camera if(lastCtrlGroup == i && Time.timeSinceMillis(lastCtrlGroupSelectMillis) < 400){ float totalX = 0, totalY = 0; for(Unit unit : selectedUnits){ totalX += unit.x; totalY += unit.y; } panning = true; Core.camera.position.set(totalX / selectedUnits.size, totalY / selectedUnits.size); } lastCtrlGroup = i; lastCtrlGroupSelectMillis = Time.millis(); } } } } if(!scene.hasMouse() && !locked && state.rules.possessionAllowed){ if(Core.input.keyDown(Binding.control) && Core.input.keyTap(Binding.select)){ Unit on = selectedUnit(); var build = selectedControlBuild(); if(on != null){ Call.unitControl(player, on); shouldShoot = false; recentRespawnTimer = 1f; }else if(build != null){ Call.buildingControlSelect(player, build); recentRespawnTimer = 1f; } } } if(!player.dead() && !state.isPaused() && !scene.hasField() && !locked){ updateMovement(player.unit()); if(Core.input.keyTap(Binding.respawn)){ controlledType = null; recentRespawnTimer = 1f; Call.unitClear(player); } } if(Core.input.keyRelease(Binding.select)){ player.shooting = false; } if(state.isGame() && !scene.hasDialog() && !scene.hasField()){ if(Core.input.keyTap(Binding.minimap)) ui.minimapfrag.toggle(); if(Core.input.keyTap(Binding.planet_map) && state.isCampaign()) ui.planet.toggle(); if(Core.input.keyTap(Binding.research) && state.isCampaign()) ui.research.toggle(); } if(state.isMenu() || Core.scene.hasDialog()) return; //zoom camera if((!Core.scene.hasScroll() || Core.input.keyDown(Binding.diagonal_placement)) && !ui.chatfrag.shown() && !ui.consolefrag.shown() && Math.abs(Core.input.axisTap(Binding.zoom)) > 0 && !Core.input.keyDown(Binding.rotateplaced) && (Core.input.keyDown(Binding.diagonal_placement) || !keybinds.get(Binding.zoom).equals(keybinds.get(Binding.rotate)) || ((!player.isBuilder() || !isPlacing() || !block.rotate) && selectPlans.isEmpty()))){ renderer.scaleCamera(Core.input.axisTap(Binding.zoom)); } if(Core.input.keyTap(Binding.select) && !Core.scene.hasMouse()){ Tile selected = world.tileWorld(input.mouseWorldX(), input.mouseWorldY()); if(selected != null){ Call.tileTap(player, selected); } } if(player.dead() || locked){ cursorType = SystemCursor.arrow; if(!Core.scene.hasMouse()){ Core.graphics.cursor(cursorType); } return; } pollInput(); //deselect if not placing if(!isPlacing() && mode == placing){ mode = none; } if(player.shooting && !canShoot()){ player.shooting = false; } if(isPlacing() && player.isBuilder()){ cursorType = SystemCursor.hand; selectScale = Mathf.lerpDelta(selectScale, 1f, 0.2f); }else{ selectScale = 0f; } if(!Core.input.keyDown(Binding.diagonal_placement) && Math.abs((int)Core.input.axisTap(Binding.rotate)) > 0){ rotation = Mathf.mod(rotation + (int)Core.input.axisTap(Binding.rotate), 4); if(splan != null){ splan.rotation = Mathf.mod(splan.rotation + (int)Core.input.axisTap(Binding.rotate), 4); } if(isPlacing() && mode == placing){ updateLine(selectX, selectY); }else if(!selectPlans.isEmpty() && !ui.chatfrag.shown()){ rotatePlans(selectPlans, Mathf.sign(Core.input.axisTap(Binding.rotate))); } } Tile cursor = tileAt(Core.input.mouseX(), Core.input.mouseY()); if(cursor != null){ if(cursor.build != null && cursor.build.interactable(player.team())){ cursorType = cursor.build.getCursor(); } if(cursor.build != null && cursor.build.team == Team.derelict && Build.validPlace(cursor.block(), player.team(), cursor.build.tileX(), cursor.build.tileY(), cursor.build.rotation)){ cursorType = ui.repairCursor; } if((isPlacing() && player.isBuilder()) || !selectPlans.isEmpty()){ cursorType = SystemCursor.hand; } if(!isPlacing() && canMine(cursor)){ cursorType = ui.drillCursor; } if(commandMode && selectedUnits.any()){ boolean canAttack = (cursor.build != null && !cursor.build.inFogTo(player.team()) && cursor.build.team != player.team()); if(!canAttack){ var unit = selectedEnemyUnit(input.mouseWorldX(), input.mouseWorldY()); if(unit != null){ canAttack = selectedUnits.contains(u -> u.canTarget(unit)); } } if(canAttack){ cursorType = ui.targetCursor; } if(input.keyTap(Binding.command_queue) && keybinds.get(Binding.command_queue).key.type != KeyType.mouse){ commandTap(input.mouseX(), input.mouseY(), true); } } if(getPlan(cursor.x, cursor.y) != null && mode == none){ cursorType = SystemCursor.hand; } if(canTapPlayer(Core.input.mouseWorld().x, Core.input.mouseWorld().y)){ cursorType = ui.unloadCursor; } if(cursor.build != null && cursor.interactable(player.team()) && !isPlacing() && Math.abs(Core.input.axisTap(Binding.rotate)) > 0 && Core.input.keyDown(Binding.rotateplaced) && cursor.block().rotate && cursor.block().quickRotate){ Call.rotateBlock(player, cursor.build, Core.input.axisTap(Binding.rotate) > 0); } } if(!Core.scene.hasMouse()){ Core.graphics.cursor(cursorType); } cursorType = SystemCursor.arrow; } @Override public void useSchematic(Schematic schem){ block = null; schematicX = tileX(getMouseX()); schematicY = tileY(getMouseY()); selectPlans.clear(); selectPlans.addAll(schematics.toPlans(schem, schematicX, schematicY)); mode = none; } @Override public boolean isBreaking(){ return mode == breaking; } @Override public void buildPlacementUI(Table table){ table.image().color(Pal.gray).height(4f).colspan(4).growX(); table.row(); table.left().margin(0f).defaults().size(48f).left(); table.button(Icon.paste, Styles.clearNonei, () -> { ui.schematics.show(); }).tooltip("@schematics"); table.button(Icon.book, Styles.clearNonei, () -> { ui.database.show(); }).tooltip("@database"); table.button(Icon.tree, Styles.clearNonei, () -> { ui.research.show(); }).visible(() -> state.isCampaign()).tooltip("@research"); table.button(Icon.map, Styles.clearNonei, () -> { ui.planet.show(); }).visible(() -> state.isCampaign()).tooltip("@planetmap"); } void pollInput(){ if(scene.hasField()) return; Tile selected = tileAt(Core.input.mouseX(), Core.input.mouseY()); int cursorX = tileX(Core.input.mouseX()); int cursorY = tileY(Core.input.mouseY()); int rawCursorX = World.toTile(Core.input.mouseWorld().x), rawCursorY = World.toTile(Core.input.mouseWorld().y); //automatically pause building if the current build queue is empty if(Core.settings.getBool("buildautopause") && isBuilding && !player.unit().isBuilding()){ isBuilding = false; buildWasAutoPaused = true; } if(!selectPlans.isEmpty()){ int shiftX = rawCursorX - schematicX, shiftY = rawCursorY - schematicY; selectPlans.each(s -> { s.x += shiftX; s.y += shiftY; }); schematicX += shiftX; schematicY += shiftY; } if(Core.input.keyTap(Binding.deselect) && !isPlacing() && player.unit().plans.isEmpty() && !commandMode){ player.unit().mineTile = null; } if(Core.input.keyTap(Binding.clear_building)){ player.unit().clearBuilding(); } if((Core.input.keyTap(Binding.schematic_select) || Core.input.keyTap(Binding.rebuild_select)) && !Core.scene.hasKeyboard() && mode != breaking){ schemX = rawCursorX; schemY = rawCursorY; } if(Core.input.keyTap(Binding.schematic_menu) && !Core.scene.hasKeyboard()){ if(ui.schematics.isShown()){ ui.schematics.hide(); }else{ ui.schematics.show(); } } if(Core.input.keyTap(Binding.clear_building) || isPlacing()){ lastSchematic = null; selectPlans.clear(); } if( !Core.scene.hasKeyboard() && selectX == -1 && selectY == -1 && schemX != -1 && schemY != -1){ if(Core.input.keyRelease(Binding.schematic_select)){ lastSchematic = schematics.create(schemX, schemY, rawCursorX, rawCursorY); useSchematic(lastSchematic); if(selectPlans.isEmpty()){ lastSchematic = null; } schemX = -1; schemY = -1; }else if(input.keyRelease(Binding.rebuild_select)){ rebuildArea(schemX, schemY, rawCursorX, rawCursorY); schemX = -1; schemY = -1; } } if(!selectPlans.isEmpty()){ if(Core.input.keyTap(Binding.schematic_flip_x)){ flipPlans(selectPlans, true); } if(Core.input.keyTap(Binding.schematic_flip_y)){ flipPlans(selectPlans, false); } } if(splan != null){ float offset = ((splan.block.size + 2) % 2) * tilesize / 2f; float x = Core.input.mouseWorld().x + offset; float y = Core.input.mouseWorld().y + offset; splan.x = (int)(x / tilesize); splan.y = (int)(y / tilesize); } if(block == null || mode != placing){ linePlans.clear(); } if(Core.input.keyTap(Binding.pause_building)){ isBuilding = !isBuilding; buildWasAutoPaused = false; if(isBuilding){ player.shooting = false; } } if((cursorX != lastLineX || cursorY != lastLineY) && isPlacing() && mode == placing){ updateLine(selectX, selectY); lastLineX = cursorX; lastLineY = cursorY; } //select some units if(Core.input.keyRelease(Binding.select) && commandRect){ selectUnitsRect(); } if(Core.input.keyTap(Binding.select) && !Core.scene.hasMouse()){ tappedOne = false; BuildPlan plan = getPlan(cursorX, cursorY); if(Core.input.keyDown(Binding.break_block)){ mode = none; }else if(!selectPlans.isEmpty()){ flushPlans(selectPlans); }else if(isPlacing()){ selectX = cursorX; selectY = cursorY; lastLineX = cursorX; lastLineY = cursorY; mode = placing; updateLine(selectX, selectY); }else if(plan != null && !plan.breaking && mode == none && !plan.initialized){ splan = plan; }else if(plan != null && plan.breaking){ deleting = true; }else if(commandMode){ commandRect = true; commandRectX = input.mouseWorldX(); commandRectY = input.mouseWorldY(); }else if(!checkConfigTap() && selected != null && !tryRepairDerelict(selected)){ //only begin shooting if there's no cursor event if(!tryTapPlayer(Core.input.mouseWorld().x, Core.input.mouseWorld().y) && !tileTapped(selected.build) && !player.unit().activelyBuilding() && !droppingItem && !(tryStopMine(selected) || (!settings.getBool("doubletapmine") || selected == prevSelected && Time.timeSinceMillis(selectMillis) < 500) && tryBeginMine(selected)) && !Core.scene.hasKeyboard()){ player.shooting = shouldShoot; } }else if(!Core.scene.hasKeyboard()){ //if it's out of bounds, shooting is just fine player.shooting = shouldShoot; } selectMillis = Time.millis(); prevSelected = selected; }else if(Core.input.keyTap(Binding.deselect) && isPlacing()){ block = null; mode = none; }else if(Core.input.keyTap(Binding.deselect) && !selectPlans.isEmpty()){ selectPlans.clear(); lastSchematic = null; }else if(Core.input.keyTap(Binding.break_block) && !Core.scene.hasMouse() && player.isBuilder() && !commandMode){ //is recalculated because setting the mode to breaking removes potential multiblock cursor offset deleting = false; mode = breaking; selectX = tileX(Core.input.mouseX()); selectY = tileY(Core.input.mouseY()); schemX = rawCursorX; schemY = rawCursorY; } if(Core.input.keyDown(Binding.select) && mode == none && !isPlacing() && deleting){ var plan = getPlan(cursorX, cursorY); if(plan != null && plan.breaking){ player.unit().plans().remove(plan); } }else{ deleting = false; } if(mode == placing && block != null){ if(!overrideLineRotation && !Core.input.keyDown(Binding.diagonal_placement) && (selectX != cursorX || selectY != cursorY) && ((int)Core.input.axisTap(Binding.rotate) != 0)){ rotation = ((int)((Angles.angle(selectX, selectY, cursorX, cursorY) + 45) / 90f)) % 4; overrideLineRotation = true; } }else{ overrideLineRotation = false; } if(Core.input.keyRelease(Binding.break_block) && Core.input.keyDown(Binding.schematic_select) && mode == breaking){ lastSchematic = schematics.create(schemX, schemY, rawCursorX, rawCursorY); schemX = -1; schemY = -1; } if(Core.input.keyRelease(Binding.break_block) || Core.input.keyRelease(Binding.select)){ if(mode == placing && block != null){ //touch up while placing, place everything in selection if(input.keyDown(Binding.boost)){ flushPlansReverse(linePlans); }else{ flushPlans(linePlans); } linePlans.clear(); Events.fire(new LineConfirmEvent()); }else if(mode == breaking){ //touch up while breaking, break everything in selection removeSelection(selectX, selectY, cursorX, cursorY, !Core.input.keyDown(Binding.schematic_select) ? maxLength : Vars.maxSchematicSize); if(lastSchematic != null){ useSchematic(lastSchematic); lastSchematic = null; } } selectX = -1; selectY = -1; tryDropItems(selected == null ? null : selected.build, Core.input.mouseWorld().x, Core.input.mouseWorld().y); if(splan != null){ if(getPlan(splan.x, splan.y, splan.block.size, splan) != null){ player.unit().plans().remove(splan, true); } splan = null; } mode = none; } if(Core.input.keyTap(Binding.toggle_block_status)){ Core.settings.put("blockstatus", !Core.settings.getBool("blockstatus")); } if(Core.input.keyTap(Binding.toggle_power_lines)){ if(Core.settings.getInt("lasersopacity") == 0){ Core.settings.put("lasersopacity", Core.settings.getInt("preferredlaseropacity", 100)); }else{ Core.settings.put("preferredlaseropacity", Core.settings.getInt("lasersopacity")); Core.settings.put("lasersopacity", 0); } } } @Override public boolean tap(float x, float y, int count, KeyCode button){ if(scene.hasMouse() || !commandMode) return false; tappedOne = true; //click: select a single unit if(button == KeyCode.mouseLeft){ if(count >= 2){ selectTypedUnits(); }else{ tapCommandUnit(); } } return super.tap(x, y, count, button); } @Override public boolean touchDown(float x, float y, int pointer, KeyCode button){ if(scene.hasMouse() || !commandMode) return false; if(button == KeyCode.mouseRight){ commandTap(x, y); } if(button == keybinds.get(Binding.command_queue).key){ commandTap(x, y, true); } return super.touchDown(x, y, pointer, button); } @Override public boolean selectedBlock(){ return isPlacing() && mode != breaking; } @Override public float getMouseX(){ return Core.input.mouseX(); } @Override public float getMouseY(){ return Core.input.mouseY(); } @Override public void updateState(){ super.updateState(); if(state.isMenu()){ lastSchematic = null; droppingItem = false; mode = none; block = null; splan = null; selectPlans.clear(); } } @Override public void panCamera(Vec2 position){ if(!locked()){ panning = true; camera.position.set(position); } } protected void updateMovement(Unit unit){ boolean omni = unit.type.omniMovement; float speed = unit.speed(); float xa = Core.input.axis(Binding.move_x); float ya = Core.input.axis(Binding.move_y); boolean boosted = (unit instanceof Mechc && unit.isFlying()); movement.set(xa, ya).nor().scl(speed); if(Core.input.keyDown(Binding.mouse_move)){ movement.add(input.mouseWorld().sub(player).scl(1f / 25f * speed)).limit(speed); } float mouseAngle = Angles.mouseAngle(unit.x, unit.y); boolean aimCursor = omni && player.shooting && unit.type.hasWeapons() && unit.type.faceTarget && !boosted; if(aimCursor){ unit.lookAt(mouseAngle); }else{ unit.lookAt(unit.prefRotation()); } unit.movePref(movement); unit.aim(Core.input.mouseWorld()); unit.controlWeapons(true, player.shooting && !boosted); player.boosting = Core.input.keyDown(Binding.boost); player.mouseX = unit.aimX(); player.mouseY = unit.aimY(); //update payload input if(unit instanceof Payloadc){ if(Core.input.keyTap(Binding.pickupCargo)){ tryPickupPayload(); } if(Core.input.keyTap(Binding.dropCargo)){ tryDropPayload(); } } } }