diff --git a/core/src/mindustry/entities/comp/UnitComp.java b/core/src/mindustry/entities/comp/UnitComp.java index e9e15b85fd..ce1775e29a 100644 --- a/core/src/mindustry/entities/comp/UnitComp.java +++ b/core/src/mindustry/entities/comp/UnitComp.java @@ -473,6 +473,12 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I return controller instanceof AIController; } + /** @return whether the unit *can* be commanded, even if its controller is not currently CommandAI. */ + public boolean allowCommand(){ + return controller instanceof CommandAI || (controller instanceof LogicAI && type.allowChangeCommands); + } + + /** @return whether the unit has a CommandAI controller */ public boolean isCommandable(){ return controller instanceof CommandAI; } diff --git a/core/src/mindustry/input/DesktopInput.java b/core/src/mindustry/input/DesktopInput.java index 146ab5a75d..dc8c61b7ec 100644 --- a/core/src/mindustry/input/DesktopInput.java +++ b/core/src/mindustry/input/DesktopInput.java @@ -288,14 +288,14 @@ public class DesktopInput extends InputHandler{ } //validate commanding units - selectedUnits.removeAll(u -> !u.isCommandable() || !u.isValid() || u.team != player.team()); + selectedUnits.removeAll(u -> !u.allowCommand() || !u.isValid() || u.team != player.team()); 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()){ + if(unit.allowCommand()){ selectedUnits.add(unit); } } diff --git a/core/src/mindustry/input/InputHandler.java b/core/src/mindustry/input/InputHandler.java index 7174f82d27..0279c542bc 100644 --- a/core/src/mindustry/input/InputHandler.java +++ b/core/src/mindustry/input/InputHandler.java @@ -261,43 +261,50 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ for(int id : unitIds){ Unit unit = Groups.unit.getByID(id); - if(unit != null && unit.team == player.team() && unit.controller() instanceof CommandAI ai){ + if(unit != null && unit.team == player.team()){ - //implicitly order it to move - if(ai.command == null || ai.command.switchToMove){ - ai.command(UnitCommand.moveCommand); + if(unit.controller() instanceof LogicAI){ + //reset to commandAI if applicable + unit.resetController(); } - if(teamTarget != null && teamTarget.team() != player.team() && - !(teamTarget instanceof Unit u && !unit.canTarget(u)) && !(teamTarget instanceof Building && !unit.type.targetGround)){ - - anyCommandedTarget = true; - if(queueCommand){ - ai.commandQueue(teamTarget); - }else{ - ai.commandQueue.clear(); - ai.commandTarget(teamTarget); + if(unit.controller() instanceof CommandAI ai){ + //implicitly order it to move + if(ai.command == null || ai.command.switchToMove){ + ai.command(UnitCommand.moveCommand); } - }else if(posTarget != null){ - if(queueCommand){ - ai.commandQueue(posTarget); - }else{ - ai.commandQueue.clear(); - ai.commandPosition(posTarget); + + if(teamTarget != null && teamTarget.team() != player.team() && + !(teamTarget instanceof Unit u && !unit.canTarget(u)) && !(teamTarget instanceof Building && !unit.type.targetGround)){ + + anyCommandedTarget = true; + if(queueCommand){ + ai.commandQueue(teamTarget); + }else{ + ai.commandQueue.clear(); + ai.commandTarget(teamTarget); + } + }else if(posTarget != null){ + if(queueCommand){ + ai.commandQueue(posTarget); + }else{ + ai.commandQueue.clear(); + ai.commandPosition(posTarget); + } } - } - unit.lastCommanded = player.coloredName(); - if(ai.commandQueue.size <= 0){ - ai.group = null; - } + unit.lastCommanded = player.coloredName(); + if(ai.commandQueue.size <= 0){ + ai.group = null; + } - //remove when other player command - if(!headless && player != Vars.player){ - control.input.selectedUnits.remove(unit); - } + //remove when other player command + if(!headless && player != Vars.player){ + control.input.selectedUnits.remove(unit); + } - toAdd.add(unit); + toAdd.add(unit); + } } } @@ -1093,38 +1100,42 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ public void drawCommanded(boolean flying){ float lineLimit = 6.5f; - Color color = Pal.accent; int sides = 6; float alpha = 0.5f; if(commandMode){ //happens sometimes - selectedUnits.removeAll(u -> !u.isCommandable()); + selectedUnits.removeAll(u -> !u.allowCommand()); //draw command overlay UI for(Unit unit : selectedUnits){ - CommandAI ai = unit.command(); - Position lastPos = ai.attackTarget != null ? ai.attackTarget : ai.targetPos; + Color color = unit.controller() instanceof LogicAI ? Team.malis.color : Pal.accent; - if(flying && ai.attackTarget != null && ai.currentCommand().drawTarget){ - Drawf.target(ai.attackTarget.getX(), ai.attackTarget.getY(), 6f, Pal.remove); - } + Position lastPos = null; - if(unit.isFlying() != flying) continue; + if(unit.controller() instanceof CommandAI ai){ + lastPos = ai.attackTarget != null ? ai.attackTarget : ai.targetPos; - //draw target line - if(ai.targetPos != null && ai.currentCommand().drawTarget){ - Position lineDest = ai.attackTarget != null ? ai.attackTarget : ai.targetPos; - Drawf.limitLine(unit, lineDest, unit.hitSize / unitSelectRadScl + 1f, lineLimit, color.write(Tmp.c1).a(alpha)); + if(flying && ai.attackTarget != null && ai.currentCommand().drawTarget){ + Drawf.target(ai.attackTarget.getX(), ai.attackTarget.getY(), 6f, Pal.remove); + } - if(ai.attackTarget == null){ - Drawf.square(lineDest.getX(), lineDest.getY(), 3.5f, color.write(Tmp.c1).a(alpha)); + if(unit.isFlying() != flying) continue; - if(ai.currentCommand() == UnitCommand.enterPayloadCommand){ - var build = world.buildWorld(lineDest.getX(), lineDest.getY()); - if(build != null && build.block.acceptsUnitPayloads && build.team == unit.team){ - Drawf.selected(build, color); + //draw target line + if(ai.targetPos != null && ai.currentCommand().drawTarget){ + Position lineDest = ai.attackTarget != null ? ai.attackTarget : ai.targetPos; + Drawf.limitLine(unit, lineDest, unit.hitSize / unitSelectRadScl + 1f, lineLimit, color.write(Tmp.c1).a(alpha)); + + if(ai.attackTarget == null){ + Drawf.square(lineDest.getX(), lineDest.getY(), 3.5f, color.write(Tmp.c1).a(alpha)); + + if(ai.currentCommand() == UnitCommand.enterPayloadCommand){ + var build = world.buildWorld(lineDest.getX(), lineDest.getY()); + if(build != null && build.block.acceptsUnitPayloads && build.team == unit.team){ + Drawf.selected(build, color); + } } } } @@ -1148,42 +1159,43 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ //Lines.poly(unit.x, unit.y, sides, rad + 1.5f); Draw.reset(); - - if(lastPos == null){ lastPos = unit; } - //draw command queue - if(ai.currentCommand().drawTarget && ai.commandQueue.size > 0){ - for(var next : ai.commandQueue){ - Drawf.limitLine(lastPos, next, lineLimit, lineLimit, color.write(Tmp.c1).a(alpha)); - lastPos = next; + if(unit.controller() instanceof CommandAI ai){ + //draw command queue + if(ai.currentCommand().drawTarget && ai.commandQueue.size > 0){ + for(var next : ai.commandQueue){ + Drawf.limitLine(lastPos, next, lineLimit, lineLimit, color.write(Tmp.c1).a(alpha)); + lastPos = next; - if(next instanceof Vec2 vec){ - Drawf.square(vec.x, vec.y, 3.5f, color.write(Tmp.c1).a(alpha)); - }else{ - Drawf.target(next.getX(), next.getY(), 6f, Pal.remove); + if(next instanceof Vec2 vec){ + Drawf.square(vec.x, vec.y, 3.5f, color.write(Tmp.c1).a(alpha)); + }else{ + Drawf.target(next.getX(), next.getY(), 6f, Pal.remove); + } } } - } - if(ai.targetPos != null && ai.currentCommand() == UnitCommand.loopPayloadCommand && unit instanceof Payloadc pay){ - Draw.color(color, 0.4f + Mathf.absin(5f, 0.5f)); - TextureRegion region = pay.hasPayload() ? Icon.download.getRegion() : Icon.upload.getRegion(); - float offset = 11f; - float size = 8f; - Draw.rect(region, ai.targetPos.x, ai.targetPos.y + offset, size, size / region.ratio()); + if(ai.targetPos != null && ai.currentCommand() == UnitCommand.loopPayloadCommand && unit instanceof Payloadc pay){ + Draw.color(color, 0.4f + Mathf.absin(5f, 0.5f)); + TextureRegion region = pay.hasPayload() ? Icon.download.getRegion() : Icon.upload.getRegion(); + float offset = 11f; + float size = 8f; + Draw.rect(region, ai.targetPos.x, ai.targetPos.y + offset, size, size / region.ratio()); - if(ai.commandQueue.size > 0){ - region = !pay.hasPayload() ? Icon.download.getRegion() : Icon.upload.getRegion(); - Draw.rect(region, ai.commandQueue.first().getX(), ai.commandQueue.first().getY() + offset, size, size / region.ratio()); + if(ai.commandQueue.size > 0){ + region = !pay.hasPayload() ? Icon.download.getRegion() : Icon.upload.getRegion(); + Draw.rect(region, ai.commandQueue.first().getX(), ai.commandQueue.first().getY() + offset, size, size / region.ratio()); + } + Draw.color(); } - Draw.color(); } } if(flying){ + Color color = Pal.accent; for(var commandBuild : commandBuildings){ if(commandBuild != null){ Drawf.square(commandBuild.x, commandBuild.y, commandBuild.hitSize() / 1.4f + 1f); @@ -1897,7 +1909,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ tmpUnits.clear(); float rad = 4f; tree.intersect(x - rad/2f, y - rad/2f, rad, rad, tmpUnits); - return tmpUnits.min(u -> u.isCommandable(), u -> u.dst(x, y) - u.hitSize/2f); + return tmpUnits.min(u -> u.allowCommand(), u -> u.dst(x, y) - u.hitSize/2f); } public @Nullable Unit selectedEnemyUnit(float x, float y){ @@ -1919,7 +1931,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ tmpUnits.clear(); float rad = 4f; tree.intersect(Tmp.r1.set(x - rad/2f, y - rad/2f, rad*2f + w, rad*2f + h).normalize(), tmpUnits); - tmpUnits.removeAll(u -> !u.isCommandable() || !predicate.get(u)); + tmpUnits.removeAll(u -> !u.allowCommand() || !predicate.get(u)); return tmpUnits; } diff --git a/core/src/mindustry/input/MobileInput.java b/core/src/mindustry/input/MobileInput.java index d69d5112a1..40c2be507e 100644 --- a/core/src/mindustry/input/MobileInput.java +++ b/core/src/mindustry/input/MobileInput.java @@ -770,7 +770,7 @@ public class MobileInput extends InputHandler implements GestureListener{ } //validate commanding units - selectedUnits.removeAll(u -> !u.isCommandable() || !u.isValid() || u.team != player.team()); + selectedUnits.removeAll(u -> !u.allowCommand() || !u.isValid() || u.team != player.team()); if(!commandMode){ commandBuildings.clear(); diff --git a/core/src/mindustry/world/blocks/campaign/LandingPad.java b/core/src/mindustry/world/blocks/campaign/LandingPad.java index 180825e9b3..e9e44418a6 100644 --- a/core/src/mindustry/world/blocks/campaign/LandingPad.java +++ b/core/src/mindustry/world/blocks/campaign/LandingPad.java @@ -109,6 +109,13 @@ public class LandingPad extends Block{ addBar("cooldown", (LandingPadBuild entity) -> new Bar("bar.cooldown", Pal.lightOrange, () -> entity.cooldown)); } + @Override + public void setStats(){ + super.setStats(); + + stats.add(Stat.cooldownTime, (cooldownTime+arrivalDuration)/60f, StatUnit.seconds); + } + @Override public boolean outputsItems(){ return true; diff --git a/desktop/src/mindustry/desktop/DesktopLauncher.java b/desktop/src/mindustry/desktop/DesktopLauncher.java index e0c2158c1c..486d041d91 100644 --- a/desktop/src/mindustry/desktop/DesktopLauncher.java +++ b/desktop/src/mindustry/desktop/DesktopLauncher.java @@ -41,8 +41,7 @@ public class DesktopLauncher extends ClientLauncher{ maximized = true; width = 900; height = 700; - //request 3.1, which has instancing - gl30Minor = 1; + gl30Minor = 2; gl30 = true; for(int i = 0; i < arg.length; i++){ if(arg[i].charAt(0) == '-'){