From 5b9d3029a51552f65e79d5070f36eb7d503eece1 Mon Sep 17 00:00:00 2001 From: Anuken Date: Thu, 20 Jun 2024 17:26:10 -0400 Subject: [PATCH] Loop unit transfer command --- core/assets/bundles/bundle.properties | 2 + core/src/mindustry/ai/UnitCommand.java | 7 ++- core/src/mindustry/ai/types/CommandAI.java | 61 ++++++++++++++++--- .../mindustry/entities/comp/PayloadComp.java | 3 +- core/src/mindustry/input/Binding.java | 1 + core/src/mindustry/input/InputHandler.java | 14 +++++ core/src/mindustry/io/TypeIO.java | 4 +- core/src/mindustry/type/UnitType.java | 2 +- .../ui/fragments/PlacementFragment.java | 4 ++ 9 files changed, 86 insertions(+), 12 deletions(-) diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index 549651a197..0f88f8d18d 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -359,6 +359,7 @@ command.enterPayload = Enter Payload Block command.loadUnits = Load Units command.loadBlocks = Load Blocks command.unloadPayload = Unload Payload +command.loopPayload = Loop Unit Transfer stance.stop = Cancel Orders stance.shoot = Stance: Shoot stance.holdfire = Stance: Hold Fire @@ -1274,6 +1275,7 @@ keybind.unit_command_load_units.name = Unit Command: Load Units keybind.unit_command_load_blocks.name = Unit Command: Load Blocks keybind.unit_command_unload_payload.name = Unit Command: Unload Payload keybind.unit_command_enter_payload.name = Unit Command: Enter Payload +keybind.unit_command_loop_payload.name = Unit Command: Loop Unit Transfer keybind.rebuild_select.name = Rebuild Region keybind.schematic_select.name = Select Region diff --git a/core/src/mindustry/ai/UnitCommand.java b/core/src/mindustry/ai/UnitCommand.java index 51a0edfcf8..85eff6be00 100644 --- a/core/src/mindustry/ai/UnitCommand.java +++ b/core/src/mindustry/ai/UnitCommand.java @@ -17,7 +17,7 @@ public class UnitCommand extends MappableContent{ @Deprecated public static final Seq all = new Seq<>(); - public static UnitCommand moveCommand, repairCommand, rebuildCommand, assistCommand, mineCommand, boostCommand, enterPayloadCommand, loadUnitsCommand, loadBlocksCommand, unloadPayloadCommand; + public static UnitCommand moveCommand, repairCommand, rebuildCommand, assistCommand, mineCommand, boostCommand, enterPayloadCommand, loadUnitsCommand, loadBlocksCommand, unloadPayloadCommand, loopPayloadCommand; /** Name of UI icon (from Icon class). */ public final String icon; @@ -110,5 +110,10 @@ public class UnitCommand extends MappableContent{ drawTarget = true; resetTarget = false; }}; + loopPayloadCommand = new UnitCommand("loopPayload", "resize", Binding.unit_command_loop_payload, null){{ + switchToMove = false; + drawTarget = true; + resetTarget = false; + }}; } } diff --git a/core/src/mindustry/ai/types/CommandAI.java b/core/src/mindustry/ai/types/CommandAI.java index d7b5d480fd..8ba3e24be1 100644 --- a/core/src/mindustry/ai/types/CommandAI.java +++ b/core/src/mindustry/ai/types/CommandAI.java @@ -20,11 +20,12 @@ public class CommandAI extends AIController{ protected static final Vec2 vecOut = new Vec2(), vecMovePos = new Vec2(); protected static final boolean[] noFound = {false}; protected static final UnitPayload tmpPayload = new UnitPayload(null); + protected static final int transferStateNone = 0, transferStateLoad = 1, transferStateUnload = 2; public Seq commandQueue = new Seq<>(5); public @Nullable Vec2 targetPos; public @Nullable Teamc attackTarget; - /** Group of units that were all commanded to reach the same point.. */ + /** Group of units that were all commanded to reach the same point. */ public @Nullable UnitGroup group; public int groupIndex = 0; /** All encountered unreachable buildings of this AI. Why a sequence? Because contains() is very rarely called on it. */ @@ -36,6 +37,7 @@ public class CommandAI extends AIController{ protected Vec2 lastTargetPos; protected boolean blockingUnit; protected float timeSpentBlocked; + protected int transferState = transferStateNone; /** Stance, usually related to firing mode. */ public UnitStance stance = UnitStance.shoot; @@ -113,6 +115,13 @@ public class CommandAI extends AIController{ attackTarget = null; } + void tryPickupUnit(Payloadc pay){ + Unit target = Units.closest(unit.team, unit.x, unit.y, unit.type.hitSize * 2f, u -> u.isAI() && u != unit && u.isGrounded() && pay.canPickup(u) && u.within(unit, u.hitSize + unit.hitSize)); + if(target != null){ + Call.pickedUnitPayload(unit, target); + } + } + public void defaultBehavior(){ if(!net.client() && unit instanceof Payloadc pay){ @@ -123,10 +132,7 @@ public class CommandAI extends AIController{ //try to pick up what's under it if(command == UnitCommand.loadUnitsCommand){ - Unit target = Units.closest(unit.team, unit.x, unit.y, unit.type.hitSize * 2f, u -> u.isAI() && u != unit && u.isGrounded() && pay.canPickup(u) && u.within(unit, u.hitSize + unit.hitSize)); - if(target != null){ - Call.pickedUnitPayload(unit, target); - } + tryPickupUnit(pay); } //try to pick up a block @@ -223,7 +229,8 @@ public class CommandAI extends AIController{ //TODO: should the unit stop when it finds a target? if( (stance == UnitStance.patrol && target != null && unit.within(target, unit.type.range - 2f) && !unit.type.circleTarget) || - (command == UnitCommand.enterPayloadCommand && unit.within(targetPos, 4f) || (targetBuild != null && unit.within(targetBuild, targetBuild.block.size * tilesize/2f * 0.9f))) + (command == UnitCommand.enterPayloadCommand && unit.within(targetPos, 4f) || (targetBuild != null && unit.within(targetBuild, targetBuild.block.size * tilesize/2f * 0.9f))) || + (command == UnitCommand.loopPayloadCommand && unit.within(targetPos, 10f)) ){ move = false; } @@ -330,6 +337,46 @@ public class CommandAI extends AIController{ return; } + if(!net.client() && command == UnitCommand.loopPayloadCommand && unit instanceof Payloadc pay){ + + if(transferState == transferStateNone){ + transferState = pay.hasPayload() ? transferStateUnload : transferStateLoad; + } + + if(transferState == transferStateUnload){ + //drop until there's a failure + int prev = -1; + while(pay.hasPayload() && prev != pay.payloads().size){ + prev = pay.payloads().size; + Call.payloadDropped(unit, unit.x, unit.y); + } + + //wait for everything to unload before running code below + if(pay.hasPayload()){ + return; + } + }else if(transferState == transferStateLoad){ + //pick up units until there's a failure + int prev = -1; + while(prev != pay.payloads().size){ + prev = pay.payloads().size; + tryPickupUnit(pay); + } + + //wait to load things before running code below + if(!pay.hasPayload()){ + return; + } + } + + //it will never finish + if(commandQueue.size == 0){ + return; + } + } + + transferState = transferStateNone; + Vec2 prev = targetPos; targetPos = null; @@ -341,7 +388,7 @@ public class CommandAI extends AIController{ commandPosition(position); } - if(prev != null && stance == UnitStance.patrol){ + if(prev != null && (stance == UnitStance.patrol || command == UnitCommand.loopPayloadCommand)){ commandQueue.add(prev.cpy()); } diff --git a/core/src/mindustry/entities/comp/PayloadComp.java b/core/src/mindustry/entities/comp/PayloadComp.java index 9b9d6e6405..9cf23876d2 100644 --- a/core/src/mindustry/entities/comp/PayloadComp.java +++ b/core/src/mindustry/entities/comp/PayloadComp.java @@ -147,7 +147,8 @@ abstract class PayloadComp implements Posc, Rotc, Hitboxc, Unitc{ Unit u = payload.unit; //can't drop ground units - if(!u.canPass(tileX(), tileY()) || Units.count(x, y, u.physicSize(), o -> o.isGrounded()) > 1){ + //allow stacking for small units for now - otherwise, unit transfer would get annoying + if(!u.canPass(tileX(), tileY()) || Units.count(x, y, u.physicSize(), o -> o.isGrounded() && o.hitSize > 14f) > 1){ return false; } diff --git a/core/src/mindustry/input/Binding.java b/core/src/mindustry/input/Binding.java index 039829ebd4..973e2e961e 100644 --- a/core/src/mindustry/input/Binding.java +++ b/core/src/mindustry/input/Binding.java @@ -60,6 +60,7 @@ public enum Binding implements KeyBind{ unit_command_load_units(KeyCode.unset), unit_command_load_blocks(KeyCode.unset), unit_command_unload_payload(KeyCode.unset), + unit_command_loop_payload(KeyCode.unset), category_prev(KeyCode.comma, "blocks"), category_next(KeyCode.period), diff --git a/core/src/mindustry/input/InputHandler.java b/core/src/mindustry/input/InputHandler.java index fcf7aedfb9..f3d7fa84cf 100644 --- a/core/src/mindustry/input/InputHandler.java +++ b/core/src/mindustry/input/InputHandler.java @@ -1088,6 +1088,20 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ } } } + + if(ai.targetPos != null && ai.currentCommand() == UnitCommand.loopPayloadCommand && unit instanceof Payloadc pay){ + Draw.color(Pal.accent, 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()); + } + Draw.color(); + } } for(var commandBuild : commandBuildings){ diff --git a/core/src/mindustry/io/TypeIO.java b/core/src/mindustry/io/TypeIO.java index c02ceb4ae6..5be1d54403 100644 --- a/core/src/mindustry/io/TypeIO.java +++ b/core/src/mindustry/io/TypeIO.java @@ -246,7 +246,7 @@ public class TypeIO{ //this is irrelevant. static final WeaponMount[] noMounts = {}; - + public static WeaponMount[] readMounts(Reads read){ read.skip(read.b() * (1 + 4 + 4)); @@ -581,7 +581,7 @@ public class TypeIO{ if(ai.command == null) ai.command = UnitCommand.moveCommand; } - //command queue only in type 7 + //command queue only in type 7/8 if(type == 7 || type == 8){ ai.commandQueue.clear(); int length = read.ub(); diff --git a/core/src/mindustry/type/UnitType.java b/core/src/mindustry/type/UnitType.java index 4466ecd536..1eb43fec67 100644 --- a/core/src/mindustry/type/UnitType.java +++ b/core/src/mindustry/type/UnitType.java @@ -853,7 +853,7 @@ public class UnitType extends UnlockableContent implements Senseable{ cmds.add(UnitCommand.mineCommand); } if(example instanceof Payloadc){ - cmds.addAll(UnitCommand.loadUnitsCommand, UnitCommand.loadBlocksCommand, UnitCommand.unloadPayloadCommand); + cmds.addAll(UnitCommand.loadUnitsCommand, UnitCommand.loadBlocksCommand, UnitCommand.unloadPayloadCommand, UnitCommand.loopPayloadCommand); } } diff --git a/core/src/mindustry/ui/fragments/PlacementFragment.java b/core/src/mindustry/ui/fragments/PlacementFragment.java index 8bbc475022..b0986bffa7 100644 --- a/core/src/mindustry/ui/fragments/PlacementFragment.java +++ b/core/src/mindustry/ui/fragments/PlacementFragment.java @@ -529,6 +529,10 @@ public class PlacementFragment{ if(stances.size > 1){ u.row(); + if(commands.size > 1){ + u.add(new Image(Tex.whiteui)).height(3f).color(Pal.gray).pad(7f).growX().row(); + } + u.table(coms -> { coms.left(); int scol = 0;