diff --git a/core/assets/baseparts/1605281306347.msch b/core/assets/baseparts/1605281306347.msch index a200060661..1ef95d9ea5 100644 Binary files a/core/assets/baseparts/1605281306347.msch and b/core/assets/baseparts/1605281306347.msch differ diff --git a/core/assets/baseparts/759638168080351252.msch b/core/assets/baseparts/759638168080351252.msch deleted file mode 100644 index 7ebed705a8..0000000000 Binary files a/core/assets/baseparts/759638168080351252.msch and /dev/null differ diff --git a/core/src/mindustry/ai/BaseAI.java b/core/src/mindustry/ai/BaseAI.java index a4fb6404e9..7900b6608c 100644 --- a/core/src/mindustry/ai/BaseAI.java +++ b/core/src/mindustry/ai/BaseAI.java @@ -1,5 +1,7 @@ package mindustry.ai; +import arc.*; +import arc.input.*; import arc.math.*; import arc.math.geom.*; import arc.struct.*; @@ -17,16 +19,18 @@ import mindustry.world.*; import mindustry.world.blocks.defense.*; import mindustry.world.blocks.production.*; import mindustry.world.blocks.storage.*; +import mindustry.world.blocks.storage.CoreBlock.*; import static mindustry.Vars.*; public class BaseAI{ private static final Vec2 axis = new Vec2(), rotator = new Vec2(); private static final float correctPercent = 0.5f; - private static final float step = 5; private static final int attempts = 4; private static final float emptyChance = 0.01f; - private static final int timerStep = 0, timerSpawn = 1; + private static final int timerStep = 0, timerSpawn = 1, timerRefreshPath = 2; + private static final int pathStep = 50; + private static final Seq tmpTiles = new Seq<>(); private static int correct = 0, incorrect = 0; @@ -36,6 +40,13 @@ public class BaseAI{ TeamData data; Interval timer = new Interval(4); + IntSet path = new IntSet(); + IntSet calcPath = new IntSet(); + @Nullable Tile calcTile; + boolean calculating, startedCalculating; + int calcCount = 0; + int totalCalcs = 0; + public BaseAI(TeamData data){ this.data = data; } @@ -53,8 +64,76 @@ public class BaseAI{ } } + //refresh path + if(!calculating && (timer.get(timerRefreshPath, 3f * Time.toMinutes) || !startedCalculating) && data.hasCore()){ + calculating = true; + startedCalculating = true; + calcPath.clear(); + } + + //didn't find tile in time + if(calculating && calcCount >= world.width() * world.height()){ + calculating = false; + calcCount = 0; + calcPath.clear(); + totalCalcs ++; + } + + //calculate path for units so schematics are not placed on it + if(calculating){ + if(calcTile == null){ + Vars.spawner.eachGroundSpawn((x, y) -> calcTile = world.tile(x, y)); + if(calcTile == null){ + calculating = false; + } + }else{ + var field = pathfinder.getField(state.rules.waveTeam, Pathfinder.costGround, Pathfinder.fieldCore); + + int[][] weights = field.weights; + for(int i = 0; i < pathStep; i++){ + int minCost = Integer.MAX_VALUE; + int cx = calcTile.x, cy = calcTile.y; + boolean foundAny = false; + for(Point2 p : Geometry.d4){ + int nx = cx + p.x, ny = cy + p.y; + + Tile other = world.tile(nx, ny); + if(other != null && weights[nx][ny] < minCost && weights[nx][ny] != -1){ + minCost = weights[nx][ny]; + calcTile = other; + foundAny = true; + } + } + + //didn't find anything, break out of loop, this will trigger a clear later + if(!foundAny){ + calcCount = Integer.MAX_VALUE; + break; + } + + calcPath.add(calcTile.pos()); + + //found the end. + if(calcTile.build instanceof CoreBuild b && b.team == state.rules.defaultTeam){ + //clean up calculations and flush results + calculating = false; + calcCount = 0; + path.clear(); + path.addAll(calcPath); + calcPath.clear(); + calcTile = null; + totalCalcs ++; + + break; + } + + calcCount ++; + } + } + } + //only schedule when there's something to build. - if(data.blocks.isEmpty() && timer.get(timerStep, Mathf.lerp(20f, 4f, data.team.rules().aiTier))){ + if(totalCalcs > 0 && data.blocks.isEmpty() && timer.get(timerStep, Mathf.lerp(20f, 4f, data.team.rules().aiTier))){ if(!triedWalls){ tryWalls(); triedWalls = true; @@ -123,6 +202,13 @@ public class BaseAI{ if(!Build.validPlace(tile.block, data.team, realX, realY, tile.rotation)){ return false; } + Tile wtile = world.tile(realX, realY); + + //may intersect AI path + tmpTiles.clear(); + if(tile.block.solid && wtile != null && wtile.getLinkedTilesAs(tile.block, tmpTiles).contains(t -> path.contains(t.pos()))){ + return false; + } } //make sure at least X% of resource requirements are met @@ -191,7 +277,8 @@ public class BaseAI{ } } - if(any && Build.validPlace(wall, data.team, tile.x, tile.y, 0)){ + tmpTiles.clear(); + if(any && Build.validPlace(wall, data.team, tile.x, tile.y, 0) && !tile.getLinkedTilesAs(wall, tmpTiles).contains(t -> path.contains(t.pos()))){ data.blocks.add(new BlockPlan(tile.x, tile.y, (short)0, wall.id, null)); } } diff --git a/core/src/mindustry/graphics/OverlayRenderer.java b/core/src/mindustry/graphics/OverlayRenderer.java index 6723f81c66..6cc1a1a851 100644 --- a/core/src/mindustry/graphics/OverlayRenderer.java +++ b/core/src/mindustry/graphics/OverlayRenderer.java @@ -171,7 +171,7 @@ public class OverlayRenderer{ Draw.reset(); Building tile = world.buildWorld(v.x, v.y); - if(tile != null && tile.interactable(player.team()) && tile.acceptStack(player.unit().item(), player.unit().stack.amount, player.unit()) > 0 && player.within(tile, itemTransferRange)){ + if(input.canDropItem() && tile != null && tile.interactable(player.team()) && tile.acceptStack(player.unit().item(), player.unit().stack.amount, player.unit()) > 0 && player.within(tile, itemTransferRange)){ Lines.stroke(3f, Pal.gray); Lines.square(tile.x, tile.y, tile.block.size * tilesize / 2f + 3 + Mathf.absin(Time.time(), 5f, 1f)); Lines.stroke(1f, Pal.place); diff --git a/core/src/mindustry/input/DesktopInput.java b/core/src/mindustry/input/DesktopInput.java index 7771b30fed..b106abf168 100644 --- a/core/src/mindustry/input/DesktopInput.java +++ b/core/src/mindustry/input/DesktopInput.java @@ -484,7 +484,7 @@ public class DesktopInput extends InputHandler{ deleting = true; }else if(selected != null){ //only begin shooting if there's no cursor event - if(!tileTapped(selected.build) && !tryTapPlayer(Core.input.mouseWorld().x, Core.input.mouseWorld().y) && !player.unit().activelyBuilding() && !droppingItem && + if(!tryTapPlayer(Core.input.mouseWorld().x, Core.input.mouseWorld().y) && !tileTapped(selected.build) && !player.unit().activelyBuilding() && !droppingItem && !tryBeginMine(selected) && player.unit().mineTile == null && !Core.scene.hasKeyboard()){ player.shooting = shouldShoot; } diff --git a/core/src/mindustry/input/InputHandler.java b/core/src/mindustry/input/InputHandler.java index ed219c026e..c2ab8423c9 100644 --- a/core/src/mindustry/input/InputHandler.java +++ b/core/src/mindustry/input/InputHandler.java @@ -1056,6 +1056,10 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ return droppingItem; } + public boolean canDropItem(){ + return droppingItem && !canTapPlayer(Core.input.mouseWorldX(), Core.input.mouseWorldY()); + } + public void tryDropItems(@Nullable Building tile, float x, float y){ if(!droppingItem || player.unit().stack.amount <= 0 || canTapPlayer(x, y) || state.isPaused() ){ droppingItem = false; diff --git a/core/src/mindustry/maps/generators/BaseGenerator.java b/core/src/mindustry/maps/generators/BaseGenerator.java index 5a1a72ad5e..8a5bf215ab 100644 --- a/core/src/mindustry/maps/generators/BaseGenerator.java +++ b/core/src/mindustry/maps/generators/BaseGenerator.java @@ -5,6 +5,7 @@ import arc.math.*; import arc.math.geom.*; import arc.struct.*; import arc.util.*; +import mindustry.ai.*; import mindustry.ai.BaseRegistry.*; import mindustry.content.*; import mindustry.game.*; @@ -146,6 +147,15 @@ public class BaseGenerator{ } }); } + + //clear path for ground units + for(Tile tile : cores){ + Astar.pathfind(tile, spawn, t -> t.team() == state.rules.waveTeam && !t.within(tile, 25f * 8) ? 100000 : t.floor().hasSurface() ? 1 : 10, t -> !t.block().isStatic()).each(t -> { + if(t.team() == state.rules.waveTeam && !t.within(tile, 25f * 8)){ + t.setBlock(Blocks.air); + } + }); + } } public void postGenerate(){ diff --git a/core/src/mindustry/ui/fragments/HintsFragment.java b/core/src/mindustry/ui/fragments/HintsFragment.java index eb7d8ca854..1bc8ad6f1e 100644 --- a/core/src/mindustry/ui/fragments/HintsFragment.java +++ b/core/src/mindustry/ui/fragments/HintsFragment.java @@ -154,7 +154,7 @@ public class HintsFragment extends Fragment{ respawn(visibleMobile, () -> !player.dead() && !player.unit().spawnedByCore, () -> !player.dead() && player.unit().spawnedByCore), launch(() -> isTutorial.get() && state.rules.sector.isCaptured(), () -> ui.planet.isShown()), schematicSelect(visibleDesktop, () -> ui.hints.placedBlocks.contains(Blocks.router), () -> Core.input.keyRelease(Binding.schematic_select) || Core.input.keyTap(Binding.pick)), - conveyorPathfind(() -> control.input.block == Blocks.titaniumConveyor, () -> Core.input.keyTap(Binding.diagonal_placement) || (mobile && Core.settings.getBool("swapdiagonal"))), + conveyorPathfind(() -> control.input.block == Blocks.titaniumConveyor, () -> Core.input.keyRelease(Binding.diagonal_placement) || (mobile && Core.settings.getBool("swapdiagonal"))), boost(visibleDesktop, () -> !player.dead() && player.unit().type.canBoost, () -> Core.input.keyDown(Binding.boost)), command(() -> state.rules.defaultTeam.data().units.size > 3 && !net.active(), () -> player.unit().isCommanding()), payloadPickup(() -> !player.unit().dead && player.unit() instanceof Payloadc p && p.payloads().isEmpty(), () -> player.unit() instanceof Payloadc p && p.payloads().any()), diff --git a/core/src/mindustry/ui/fragments/PlacementFragment.java b/core/src/mindustry/ui/fragments/PlacementFragment.java index 9115b16785..a42e1d6aa5 100644 --- a/core/src/mindustry/ui/fragments/PlacementFragment.java +++ b/core/src/mindustry/ui/fragments/PlacementFragment.java @@ -97,7 +97,7 @@ public class PlacementFragment extends Fragment{ boolean gridUpdate(InputHandler input){ scrollPositions.put(currentCategory, blockPane.getScrollY()); - if(Core.input.keyDown(Binding.pick) && player.isBuilder()){ //mouse eyedropper select + if(Core.input.keyTap(Binding.pick) && player.isBuilder()){ //mouse eyedropper select Building tile = world.buildWorld(Core.input.mouseWorld().x, Core.input.mouseWorld().y); Block tryRecipe = tile == null ? null : tile.block instanceof ConstructBlock ? ((ConstructBuild)tile).cblock : tile.block; Object tryConfig = tile == null ? null : tile.config(); diff --git a/core/src/mindustry/world/blocks/campaign/Accelerator.java b/core/src/mindustry/world/blocks/campaign/Accelerator.java index 419186fe56..9dd9040f8f 100644 --- a/core/src/mindustry/world/blocks/campaign/Accelerator.java +++ b/core/src/mindustry/world/blocks/campaign/Accelerator.java @@ -32,8 +32,10 @@ public class Accelerator extends Block{ @Override public void init(){ + itemCapacity = 0; for(ItemStack stack : launching.requirements){ capacities[stack.item.id] = stack.amount; + itemCapacity += stack.amount; } consumes.items(launching.requirements); super.init(); diff --git a/core/src/mindustry/world/blocks/distribution/StackConveyor.java b/core/src/mindustry/world/blocks/distribution/StackConveyor.java index 06d6f12068..b35f1630fb 100644 --- a/core/src/mindustry/world/blocks/distribution/StackConveyor.java +++ b/core/src/mindustry/world/blocks/distribution/StackConveyor.java @@ -106,6 +106,8 @@ public class StackConveyor extends Block implements Autotiler{ public float cooldown; public Item lastItem; + boolean proxUpdating = false; + @Override public void draw(){ Draw.rect(regions[state], x, y, rotdeg()); @@ -168,15 +170,15 @@ public class StackConveyor extends Block implements Autotiler{ //update other conveyor state when this conveyor's state changes if(state != lastState){ + proxUpdating = true; for(Building near : proximity){ - if(near instanceof StackConveyorBuild){ + if(!(near instanceof StackConveyorBuild b && b.proxUpdating && b.state != stateUnload)){ near.onProximityUpdate(); - for(Building other : near.proximity){ - if(!(other instanceof StackConveyorBuild)) other.onProximityUpdate(); - } } } + proxUpdating = false; } + } @Override diff --git a/core/src/mindustry/world/blocks/units/UnitFactory.java b/core/src/mindustry/world/blocks/units/UnitFactory.java index 1a257d9565..6b2156e566 100644 --- a/core/src/mindustry/world/blocks/units/UnitFactory.java +++ b/core/src/mindustry/world/blocks/units/UnitFactory.java @@ -53,12 +53,15 @@ public class UnitFactory extends UnitBlock{ @Override public void init(){ capacities = new int[Vars.content.items().size]; + itemCapacity = 0; for(UnitPlan plan : plans){ for(ItemStack stack : plan.requirements){ capacities[stack.item.id] = Math.max(capacities[stack.item.id], stack.amount * 2); - itemCapacity = Math.max(itemCapacity, stack.amount * 2); } } + for(int i : capacities){ + itemCapacity += i; + } super.init(); }