mirror of
https://github.com/Anuken/Mindustry.git
synced 2026-02-06 07:21:31 -08:00
Auto-pathfinding diagonals
This commit is contained in:
parent
b8ea0f3aa6
commit
2209968963
9 changed files with 150 additions and 31 deletions
|
|
@ -590,6 +590,7 @@ category.shooting = Shooting
|
|||
category.optional = Optional Enhancements
|
||||
setting.landscape.name = Lock Landscape
|
||||
setting.shadows.name = Shadows
|
||||
setting.blockreplace.name = Automatic Block Suggestions
|
||||
setting.linear.name = Linear Filtering
|
||||
setting.hints.name = Hints
|
||||
setting.animatedwater.name = Animated Water
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import io.anuke.mindustry.entities.traits.BuilderTrait.*;
|
|||
import io.anuke.mindustry.game.EventType.*;
|
||||
import io.anuke.mindustry.game.Schematic.*;
|
||||
import io.anuke.mindustry.input.*;
|
||||
import io.anuke.mindustry.input.PlaceUtils.*;
|
||||
import io.anuke.mindustry.input.Placement.*;
|
||||
import io.anuke.mindustry.type.*;
|
||||
import io.anuke.mindustry.world.*;
|
||||
import io.anuke.mindustry.world.blocks.*;
|
||||
|
|
@ -258,7 +258,7 @@ public class Schematics implements Loadable{
|
|||
|
||||
/** Creates a schematic from a world selection. */
|
||||
public Schematic create(int x, int y, int x2, int y2){
|
||||
NormalizeResult result = PlaceUtils.normalizeArea(x, y, x2, y2, 0, false, maxSchematicSize);
|
||||
NormalizeResult result = Placement.normalizeArea(x, y, x2, y2, 0, false, maxSchematicSize);
|
||||
x = result.x;
|
||||
y = result.y;
|
||||
x2 = result.x2;
|
||||
|
|
|
|||
|
|
@ -182,13 +182,14 @@ public class DesktopInput extends InputHandler{
|
|||
selectScale = 0f;
|
||||
}
|
||||
|
||||
rotation = Mathf.mod(rotation + (int)Core.input.axisTap(Binding.rotate), 4);
|
||||
|
||||
if(sreq != null){
|
||||
sreq.rotation = Mathf.mod(sreq.rotation + (int)Core.input.axisTap(Binding.rotate), 4);
|
||||
}
|
||||
|
||||
if(!Core.input.keyDown(Binding.zoom_hold) && Math.abs((int)Core.input.axisTap(Binding.rotate)) > 0){
|
||||
|
||||
rotation = Mathf.mod(rotation + (int)Core.input.axisTap(Binding.rotate), 4);
|
||||
|
||||
if(sreq != null){
|
||||
sreq.rotation = Mathf.mod(sreq.rotation + (int)Core.input.axisTap(Binding.rotate), 4);
|
||||
}
|
||||
|
||||
if(isPlacing() && mode == placing){
|
||||
updateLine(selectX, selectY);
|
||||
}else if(!selectRequests.isEmpty()){
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ import io.anuke.mindustry.game.*;
|
|||
import io.anuke.mindustry.game.Teams.*;
|
||||
import io.anuke.mindustry.gen.*;
|
||||
import io.anuke.mindustry.graphics.*;
|
||||
import io.anuke.mindustry.input.PlaceUtils.*;
|
||||
import io.anuke.mindustry.input.Placement.*;
|
||||
import io.anuke.mindustry.net.*;
|
||||
import io.anuke.mindustry.type.*;
|
||||
import io.anuke.mindustry.ui.fragments.*;
|
||||
|
|
@ -368,8 +368,8 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
|||
}
|
||||
|
||||
protected void drawBreakSelection(int x1, int y1, int x2, int y2){
|
||||
NormalizeDrawResult result = PlaceUtils.normalizeDrawArea(Blocks.air, x1, y1, x2, y2, false, maxLength, 1f);
|
||||
NormalizeResult dresult = PlaceUtils.normalizeArea(x1, y1, x2, y2, rotation, false, maxLength);
|
||||
NormalizeDrawResult result = Placement.normalizeDrawArea(Blocks.air, x1, y1, x2, y2, false, maxLength, 1f);
|
||||
NormalizeResult dresult = Placement.normalizeArea(x1, y1, x2, y2, rotation, false, maxLength);
|
||||
|
||||
for(int x = dresult.x; x <= dresult.x2; x++){
|
||||
for(int y = dresult.y; y <= dresult.y2; y++){
|
||||
|
|
@ -415,7 +415,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
|||
}
|
||||
|
||||
protected void drawSelection(int x1, int y1, int x2, int y2, int maxLength){
|
||||
NormalizeDrawResult result = PlaceUtils.normalizeDrawArea(Blocks.air, x1, y1, x2, y2, false, maxLength, 1f);
|
||||
NormalizeDrawResult result = Placement.normalizeDrawArea(Blocks.air, x1, y1, x2, y2, false, maxLength, 1f);
|
||||
|
||||
Lines.stroke(2f);
|
||||
|
||||
|
|
@ -469,7 +469,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
|||
|
||||
/** Remove everything from the queue in a selection. */
|
||||
protected void removeSelection(int x1, int y1, int x2, int y2, boolean flush){
|
||||
NormalizeResult result = PlaceUtils.normalizeArea(x1, y1, x2, y2, rotation, false, maxLength);
|
||||
NormalizeResult result = Placement.normalizeArea(x1, y1, x2, y2, rotation, false, maxLength);
|
||||
for(int x = 0; x <= Math.abs(result.x2 - result.x); x++){
|
||||
for(int y = 0; y <= Math.abs(result.y2 - result.y); y++){
|
||||
int wx = x1 + x * Mathf.sign(x2 - x1);
|
||||
|
|
@ -526,12 +526,14 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
|||
lineRequests.add(req);
|
||||
});
|
||||
|
||||
lineRequests.each(req -> {
|
||||
Block replace = req.block.getReplacement(req, lineRequests);
|
||||
if(replace.unlockedCur()){
|
||||
req.block = replace;
|
||||
}
|
||||
});
|
||||
if(Core.settings.getBool("blockreplace")){
|
||||
lineRequests.each(req -> {
|
||||
Block replace = req.block.getReplacement(req, lineRequests);
|
||||
if(replace.unlockedCur()){
|
||||
req.block = replace;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected void updateLine(int x1, int y1){
|
||||
|
|
@ -809,9 +811,9 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
|||
}
|
||||
|
||||
if(diagonal){
|
||||
points = PlaceUtils.pathfindLine(startX, startY, endX, endY);
|
||||
points = Placement.pathfindLine(block != null && block.conveyorPlacement, startX, startY, endX, endY);
|
||||
}else{
|
||||
points = PlaceUtils.normalizeLine(startX, startY, endX, endY);
|
||||
points = Placement.normalizeLine(startX, startY, endX, endY);
|
||||
}
|
||||
|
||||
float angle = Angles.angle(startX, startY, endX, endY);
|
||||
|
|
|
|||
|
|
@ -1,25 +1,40 @@
|
|||
package io.anuke.mindustry.input;
|
||||
|
||||
import io.anuke.arc.collection.Array;
|
||||
import io.anuke.arc.math.Mathf;
|
||||
import io.anuke.arc.math.geom.Bresenham2;
|
||||
import io.anuke.arc.math.geom.Point2;
|
||||
import io.anuke.arc.util.pooling.Pools;
|
||||
import io.anuke.mindustry.world.Block;
|
||||
import io.anuke.arc.collection.*;
|
||||
import io.anuke.arc.math.*;
|
||||
import io.anuke.arc.math.geom.*;
|
||||
import io.anuke.arc.util.pooling.*;
|
||||
import io.anuke.mindustry.world.*;
|
||||
|
||||
import static io.anuke.mindustry.Vars.tilesize;
|
||||
import java.util.*;
|
||||
|
||||
public class PlaceUtils{
|
||||
import static io.anuke.mindustry.Vars.*;
|
||||
|
||||
public class Placement{
|
||||
private static final NormalizeResult result = new NormalizeResult();
|
||||
private static final NormalizeDrawResult drawResult = new NormalizeDrawResult();
|
||||
private static Bresenham2 bres = new Bresenham2();
|
||||
private static Array<Point2> points = new Array<>();
|
||||
|
||||
//for pathfinding
|
||||
private static IntFloatMap costs = new IntFloatMap();
|
||||
private static IntIntMap parents = new IntIntMap();
|
||||
private static IntSet closed = new IntSet();
|
||||
|
||||
/** Normalize a diagonal line into points. */
|
||||
public static Array<Point2> pathfindLine(int startX, int startY, int endX, int endY){
|
||||
public static Array<Point2> pathfindLine(boolean conveyors, int startX, int startY, int endX, int endY){
|
||||
Pools.freeAll(points);
|
||||
|
||||
points.clear();
|
||||
return bres.lineNoDiagonal(startX, startY, endX, endY, Pools.get(Point2.class, Point2::new), points);
|
||||
if(conveyors){
|
||||
if(astar(startX, startY, endX, endY)){
|
||||
return points;
|
||||
}else{
|
||||
return normalizeLine(startX, startY, endX, endY);
|
||||
}
|
||||
}else{
|
||||
return bres.lineNoDiagonal(startX, startY, endX, endY, Pools.get(Point2.class, Point2::new), points);
|
||||
}
|
||||
}
|
||||
|
||||
/** Normalize two points into one straight line, no diagonals. */
|
||||
|
|
@ -40,6 +55,92 @@ public class PlaceUtils{
|
|||
return points;
|
||||
}
|
||||
|
||||
private static float tileHeuristic(Tile tile, Tile other){
|
||||
Block block = control.input.block;
|
||||
|
||||
if(!other.block().alwaysReplace && !(block != null && block.canReplace(other.block()))){
|
||||
return 20;
|
||||
}else{
|
||||
if(parents.containsKey(tile.pos())){
|
||||
Tile prev = world.tile(parents.get(tile.pos(), 0));
|
||||
if(tile.relativeTo(prev) != other.relativeTo(tile)){
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
private static float distanceHeuristic(int x1, int y1, int x2, int y2){
|
||||
return Math.abs(x1 - x2) + Math.abs(y1 - y2);
|
||||
}
|
||||
|
||||
private static boolean validNode(Tile tile, Tile other){
|
||||
Block block = control.input.block;
|
||||
if(block != null && block.canReplace(other.block())){
|
||||
return true;
|
||||
}else{
|
||||
return other.block().alwaysReplace;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean astar(int startX, int startY, int endX, int endY){
|
||||
Tile start = world.tile(startX, startY);
|
||||
Tile end = world.tile(endX, endY);
|
||||
if(start == end || start == null || end == null) return false;
|
||||
|
||||
costs.clear();
|
||||
closed.clear();
|
||||
parents.clear();
|
||||
|
||||
int nodeLimit = 1000;
|
||||
int totalNodes = 0;
|
||||
|
||||
PriorityQueue<Tile> queue = new PriorityQueue<>(10, (a, b) -> Float.compare(costs.get(a.pos(), 0f) + distanceHeuristic(a.x, a.y, end.x, end.y), costs.get(b.pos(), 0f) + distanceHeuristic(b.x, b.y, end.x, end.y)));
|
||||
queue.add(start);
|
||||
boolean found = false;
|
||||
while(!queue.isEmpty() && totalNodes++ < nodeLimit){
|
||||
Tile next = queue.poll();
|
||||
float baseCost = costs.get(next.pos(), 0f);
|
||||
if(next == end){
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
closed.add(Pos.get(next.x, next.y));
|
||||
for(Point2 point : Geometry.d4){
|
||||
int newx = next.x + point.x, newy = next.y + point.y;
|
||||
Tile child = world.tile(newx, newy);
|
||||
if(child != null && validNode(next, child)){
|
||||
if(closed.add(child.pos())){
|
||||
parents.put(child.pos(), next.pos());
|
||||
costs.put(child.pos(), tileHeuristic(next, child) + baseCost);
|
||||
queue.add(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!found) return false;
|
||||
int total = 0;
|
||||
|
||||
points.add(Pools.obtain(Point2.class, Point2::new).set(endX, endY));
|
||||
|
||||
Tile current = end;
|
||||
while(current != start && total++ < nodeLimit){
|
||||
if(current == null) return false;
|
||||
int newPos = parents.get(current.pos(), Pos.invalid);
|
||||
|
||||
if(newPos == Pos.invalid) return false;
|
||||
|
||||
points.add(Pools.obtain(Point2.class, Point2::new).set(Pos.x(newPos), Pos.y(newPos)));
|
||||
current = world.tile(newPos);
|
||||
}
|
||||
|
||||
points.reverse();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalizes a placement area and returns the result, ready to be used for drawing a rectangle.
|
||||
* Returned x2 and y2 will <i>always</i> be greater than x and y.
|
||||
|
|
@ -173,4 +274,12 @@ public class PlaceUtils{
|
|||
return y + (x2 - x > y2 - y ? 0 : i);
|
||||
}
|
||||
}
|
||||
|
||||
public interface DistanceHeuristic{
|
||||
float cost(int x1, int y1, int x2, int y2);
|
||||
}
|
||||
|
||||
public interface TileHueristic{
|
||||
float cost(Tile tile, Tile other);
|
||||
}
|
||||
}
|
||||
|
|
@ -226,6 +226,8 @@ public class SettingsMenuDialog extends SettingsDialog{
|
|||
|
||||
game.checkPref("savecreate", true);
|
||||
|
||||
game.checkPref("blockreplace", true);
|
||||
|
||||
game.checkPref("hints", true);
|
||||
|
||||
if(steam && !Version.modifier.contains("beta")){
|
||||
|
|
|
|||
|
|
@ -91,6 +91,8 @@ public class Block extends BlockStorage{
|
|||
public boolean consumesTap;
|
||||
/** Whether the config is positional and needs to be shifted. */
|
||||
public boolean posConfig;
|
||||
/** Whether this block uses conveyor-type placement mode.*/
|
||||
public boolean conveyorPlacement;
|
||||
/**
|
||||
* The color of this block when displayed on the minimap or map preview.
|
||||
* Do not set manually! This is overriden when loading for most blocks.
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ public class Conduit extends LiquidBlock implements Autotiler{
|
|||
rotate = true;
|
||||
solid = false;
|
||||
floating = true;
|
||||
conveyorPlacement = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ public class Conveyor extends Block implements Autotiler{
|
|||
group = BlockGroup.transportation;
|
||||
hasItems = true;
|
||||
itemCapacity = 4;
|
||||
conveyorPlacement = true;
|
||||
|
||||
idleSound = Sounds.conveyor;
|
||||
idleSoundVolume = 0.004f;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue