diff --git a/core/src/mindustry/entities/Units.java b/core/src/mindustry/entities/Units.java index b07022ef70..7775cb5f3f 100644 --- a/core/src/mindustry/entities/Units.java +++ b/core/src/mindustry/entities/Units.java @@ -24,6 +24,20 @@ public class Units{ private static int intResult; private static Building buildResult; + //prevents allocations in anyEntities + private static boolean anyEntityGround; + private static float aeX, aeY, aeW, aeH; + private static final Cons anyEntityLambda = unit -> { + if(boolResult) return; + if((unit.isGrounded() && !unit.hovering) == anyEntityGround){ + unit.hitboxTile(hitrect); + + if(hitrect.overlaps(aeX, aeY, aeW, aeH)){ + boolResult = true; + } + } + }; + @Remote(called = Loc.server) public static void unitCapDeath(Unit unit){ if(unit != null){ @@ -145,18 +159,13 @@ public class Units{ public static boolean anyEntities(float x, float y, float width, float height, boolean ground){ boolResult = false; + anyEntityGround = ground; + aeX = x; + aeY = y; + aeW = width; + aeH = height; - nearby(x, y, width, height, unit -> { - if(boolResult) return; - if((unit.isGrounded() && !unit.hovering) == ground){ - unit.hitboxTile(hitrect); - - if(hitrect.overlaps(x, y, width, height)){ - boolResult = true; - } - } - }); - + nearby(x, y, width, height, anyEntityLambda); return boolResult; } diff --git a/core/src/mindustry/entities/units/BuildPlan.java b/core/src/mindustry/entities/units/BuildPlan.java index 29dd7c324b..d4120fc24c 100644 --- a/core/src/mindustry/entities/units/BuildPlan.java +++ b/core/src/mindustry/entities/units/BuildPlan.java @@ -2,6 +2,7 @@ package mindustry.entities.units; import arc.func.*; import arc.math.geom.*; +import arc.math.geom.QuadTree.*; import arc.util.*; import mindustry.game.*; import mindustry.gen.*; @@ -10,7 +11,7 @@ import mindustry.world.*; import static mindustry.Vars.*; /** Class for storing build requests. Can be either a place or remove request. */ -public class BuildPlan implements Position{ +public class BuildPlan implements Position, QuadTreeObject{ /** Position and rotation of this request. */ public int x, y, rotation; /** Block being placed. If null, this is a breaking request.*/ @@ -157,6 +158,15 @@ public class BuildPlan implements Position{ return world.build(x, y); } + @Override + public void hitbox(Rect out){ + if(block != null){ + out.setCentered(x * tilesize + block.offset, y * tilesize + block.offset, block.size * tilesize); + }else{ + out.setCentered(x * tilesize, y * tilesize, tilesize); + } + } + @Override public float getX(){ return drawx(); diff --git a/core/src/mindustry/input/InputHandler.java b/core/src/mindustry/input/InputHandler.java index 1e501d7174..11feb7d635 100644 --- a/core/src/mindustry/input/InputHandler.java +++ b/core/src/mindustry/input/InputHandler.java @@ -70,12 +70,30 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ public Seq lineRequests = new Seq<>(); public Seq selectRequests = new Seq<>(); + private Seq plansOut = new Seq<>(BuildPlan.class); + private QuadTree playerPlanTree = new QuadTree<>(new Rect()); + + private final Eachable allRequests = cons -> { + player.unit().plans().each(cons); + selectRequests.each(cons); + lineRequests.each(cons); + }; + + private final Eachable allSelectLines = cons -> { + selectRequests.each(cons); + lineRequests.each(cons); + }; + public InputHandler(){ Events.on(UnitDestroyEvent.class, e -> { if(e.unit != null && e.unit.isPlayer() && e.unit.getPlayer().isLocal() && e.unit.type.weapons.contains(w -> w.bullet.killShooter)){ player.shooting = false; } }); + + Events.on(WorldLoadEvent.class, e -> { + playerPlanTree = new QuadTree<>(new Rect(0f, 0f, world.unitWidth(), world.unitHeight())); + }); } //methods to override @@ -439,7 +457,6 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ player.unit().clearCommand(); }else if(player.unit().type.commandLimit > 0){ - //TODO try out some other formations player.unit().commandNearby(new CircleFormation()); Fx.commandSend.at(player, player.unit().type.commandRadius); } @@ -456,11 +473,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ } public Eachable allRequests(){ - return cons -> { - for(BuildPlan request : player.unit().plans()) cons.get(request); - for(BuildPlan request : selectRequests) cons.get(request); - for(BuildPlan request : lineRequests) cons.get(request); - }; + return allRequests; } public boolean isUsingSchematic(){ @@ -468,6 +481,9 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ } public void update(){ + playerPlanTree.clear(); + player.unit().plans.each(playerPlanTree::insert); + player.typing = ui.chatfrag.shown(); if(player.dead()){ @@ -836,10 +852,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ Draw.reset(); Draw.mixcol(!valid ? Pal.breakInvalid : Color.white, (!valid ? 0.4f : 0.24f) + Mathf.absin(Time.globalTime, 6f, 0.28f)); Draw.alpha(1f); - request.block.drawRequestConfigTop(request, cons -> { - selectRequests.each(cons); - lineRequests.each(cons); - }); + request.block.drawRequestConfigTop(request, allSelectLines); Draw.reset(); } @@ -1209,15 +1222,22 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ } public boolean validPlace(int x, int y, Block type, int rotation, BuildPlan ignore){ - //TODO with many requests, this is O(n * m), very laggy - for(BuildPlan req : player.unit().plans()){ - if(req != ignore - && !req.breaking - && req.block.bounds(req.x, req.y, Tmp.r1).overlaps(type.bounds(x, y, Tmp.r2)) - && !(type.canReplace(req.block) && Tmp.r1.equals(Tmp.r2))){ - return false; + if(player.unit().plans.size > 0){ + Tmp.r1.setCentered(x * tilesize + type.offset, y * tilesize + type.offset, type.size * tilesize); + plansOut.clear(); + playerPlanTree.intersect(Tmp.r1, plansOut); + + for(int i = 0; i < plansOut.size; i++){ + var req = plansOut.items[i]; + if(req != ignore + && !req.breaking + && req.block.bounds(req.x, req.y, Tmp.r1).overlaps(type.bounds(x, y, Tmp.r2)) + && !(type.canReplace(req.block) && Tmp.r1.equals(Tmp.r2))){ + return false; + } } } + return Build.validPlace(type, player.team(), x, y, rotation); }