Auto-pathfinding diagonals
This commit is contained in:
@@ -590,6 +590,7 @@ category.shooting = Shooting
|
|||||||
category.optional = Optional Enhancements
|
category.optional = Optional Enhancements
|
||||||
setting.landscape.name = Lock Landscape
|
setting.landscape.name = Lock Landscape
|
||||||
setting.shadows.name = Shadows
|
setting.shadows.name = Shadows
|
||||||
|
setting.blockreplace.name = Automatic Block Suggestions
|
||||||
setting.linear.name = Linear Filtering
|
setting.linear.name = Linear Filtering
|
||||||
setting.hints.name = Hints
|
setting.hints.name = Hints
|
||||||
setting.animatedwater.name = Animated Water
|
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.EventType.*;
|
||||||
import io.anuke.mindustry.game.Schematic.*;
|
import io.anuke.mindustry.game.Schematic.*;
|
||||||
import io.anuke.mindustry.input.*;
|
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.type.*;
|
||||||
import io.anuke.mindustry.world.*;
|
import io.anuke.mindustry.world.*;
|
||||||
import io.anuke.mindustry.world.blocks.*;
|
import io.anuke.mindustry.world.blocks.*;
|
||||||
@@ -258,7 +258,7 @@ public class Schematics implements Loadable{
|
|||||||
|
|
||||||
/** Creates a schematic from a world selection. */
|
/** Creates a schematic from a world selection. */
|
||||||
public Schematic create(int x, int y, int x2, int y2){
|
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;
|
x = result.x;
|
||||||
y = result.y;
|
y = result.y;
|
||||||
x2 = result.x2;
|
x2 = result.x2;
|
||||||
|
|||||||
@@ -182,13 +182,14 @@ public class DesktopInput extends InputHandler{
|
|||||||
selectScale = 0f;
|
selectScale = 0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
rotation = Mathf.mod(rotation + (int)Core.input.axisTap(Binding.rotate), 4);
|
||||||
|
|
||||||
if(sreq != null){
|
if(sreq != null){
|
||||||
sreq.rotation = Mathf.mod(sreq.rotation + (int)Core.input.axisTap(Binding.rotate), 4);
|
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){
|
|
||||||
if(isPlacing() && mode == placing){
|
if(isPlacing() && mode == placing){
|
||||||
updateLine(selectX, selectY);
|
updateLine(selectX, selectY);
|
||||||
}else if(!selectRequests.isEmpty()){
|
}else if(!selectRequests.isEmpty()){
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import io.anuke.mindustry.game.*;
|
|||||||
import io.anuke.mindustry.game.Teams.*;
|
import io.anuke.mindustry.game.Teams.*;
|
||||||
import io.anuke.mindustry.gen.*;
|
import io.anuke.mindustry.gen.*;
|
||||||
import io.anuke.mindustry.graphics.*;
|
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.net.*;
|
||||||
import io.anuke.mindustry.type.*;
|
import io.anuke.mindustry.type.*;
|
||||||
import io.anuke.mindustry.ui.fragments.*;
|
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){
|
protected void drawBreakSelection(int x1, int y1, int x2, int y2){
|
||||||
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);
|
||||||
NormalizeResult dresult = PlaceUtils.normalizeArea(x1, y1, x2, y2, rotation, false, maxLength);
|
NormalizeResult dresult = Placement.normalizeArea(x1, y1, x2, y2, rotation, false, maxLength);
|
||||||
|
|
||||||
for(int x = dresult.x; x <= dresult.x2; x++){
|
for(int x = dresult.x; x <= dresult.x2; x++){
|
||||||
for(int y = dresult.y; y <= dresult.y2; y++){
|
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){
|
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);
|
Lines.stroke(2f);
|
||||||
|
|
||||||
@@ -469,7 +469,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
|||||||
|
|
||||||
/** Remove everything from the queue in a selection. */
|
/** Remove everything from the queue in a selection. */
|
||||||
protected void removeSelection(int x1, int y1, int x2, int y2, boolean flush){
|
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 x = 0; x <= Math.abs(result.x2 - result.x); x++){
|
||||||
for(int y = 0; y <= Math.abs(result.y2 - result.y); y++){
|
for(int y = 0; y <= Math.abs(result.y2 - result.y); y++){
|
||||||
int wx = x1 + x * Mathf.sign(x2 - x1);
|
int wx = x1 + x * Mathf.sign(x2 - x1);
|
||||||
@@ -526,6 +526,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
|||||||
lineRequests.add(req);
|
lineRequests.add(req);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if(Core.settings.getBool("blockreplace")){
|
||||||
lineRequests.each(req -> {
|
lineRequests.each(req -> {
|
||||||
Block replace = req.block.getReplacement(req, lineRequests);
|
Block replace = req.block.getReplacement(req, lineRequests);
|
||||||
if(replace.unlockedCur()){
|
if(replace.unlockedCur()){
|
||||||
@@ -533,6 +534,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected void updateLine(int x1, int y1){
|
protected void updateLine(int x1, int y1){
|
||||||
updateLine(x1, y1, tileX(getMouseX()), tileY(getMouseY()));
|
updateLine(x1, y1, tileX(getMouseX()), tileY(getMouseY()));
|
||||||
@@ -809,9 +811,9 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(diagonal){
|
if(diagonal){
|
||||||
points = PlaceUtils.pathfindLine(startX, startY, endX, endY);
|
points = Placement.pathfindLine(block != null && block.conveyorPlacement, startX, startY, endX, endY);
|
||||||
}else{
|
}else{
|
||||||
points = PlaceUtils.normalizeLine(startX, startY, endX, endY);
|
points = Placement.normalizeLine(startX, startY, endX, endY);
|
||||||
}
|
}
|
||||||
|
|
||||||
float angle = Angles.angle(startX, startY, endX, endY);
|
float angle = Angles.angle(startX, startY, endX, endY);
|
||||||
|
|||||||
@@ -1,26 +1,41 @@
|
|||||||
package io.anuke.mindustry.input;
|
package io.anuke.mindustry.input;
|
||||||
|
|
||||||
import io.anuke.arc.collection.Array;
|
import io.anuke.arc.collection.*;
|
||||||
import io.anuke.arc.math.Mathf;
|
import io.anuke.arc.math.*;
|
||||||
import io.anuke.arc.math.geom.Bresenham2;
|
import io.anuke.arc.math.geom.*;
|
||||||
import io.anuke.arc.math.geom.Point2;
|
import io.anuke.arc.util.pooling.*;
|
||||||
import io.anuke.arc.util.pooling.Pools;
|
import io.anuke.mindustry.world.*;
|
||||||
import io.anuke.mindustry.world.Block;
|
|
||||||
|
|
||||||
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 NormalizeResult result = new NormalizeResult();
|
||||||
private static final NormalizeDrawResult drawResult = new NormalizeDrawResult();
|
private static final NormalizeDrawResult drawResult = new NormalizeDrawResult();
|
||||||
private static Bresenham2 bres = new Bresenham2();
|
private static Bresenham2 bres = new Bresenham2();
|
||||||
private static Array<Point2> points = new Array<>();
|
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. */
|
/** 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);
|
Pools.freeAll(points);
|
||||||
|
|
||||||
points.clear();
|
points.clear();
|
||||||
|
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);
|
return bres.lineNoDiagonal(startX, startY, endX, endY, Pools.get(Point2.class, Point2::new), points);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Normalize two points into one straight line, no diagonals. */
|
/** Normalize two points into one straight line, no diagonals. */
|
||||||
public static Array<Point2> normalizeLine(int startX, int startY, int endX, int endY){
|
public static Array<Point2> normalizeLine(int startX, int startY, int endX, int endY){
|
||||||
@@ -40,6 +55,92 @@ public class PlaceUtils{
|
|||||||
return points;
|
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.
|
* 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.
|
* 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);
|
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("savecreate", true);
|
||||||
|
|
||||||
|
game.checkPref("blockreplace", true);
|
||||||
|
|
||||||
game.checkPref("hints", true);
|
game.checkPref("hints", true);
|
||||||
|
|
||||||
if(steam && !Version.modifier.contains("beta")){
|
if(steam && !Version.modifier.contains("beta")){
|
||||||
|
|||||||
@@ -91,6 +91,8 @@ public class Block extends BlockStorage{
|
|||||||
public boolean consumesTap;
|
public boolean consumesTap;
|
||||||
/** Whether the config is positional and needs to be shifted. */
|
/** Whether the config is positional and needs to be shifted. */
|
||||||
public boolean posConfig;
|
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.
|
* 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.
|
* 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;
|
rotate = true;
|
||||||
solid = false;
|
solid = false;
|
||||||
floating = true;
|
floating = true;
|
||||||
|
conveyorPlacement = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ public class Conveyor extends Block implements Autotiler{
|
|||||||
group = BlockGroup.transportation;
|
group = BlockGroup.transportation;
|
||||||
hasItems = true;
|
hasItems = true;
|
||||||
itemCapacity = 4;
|
itemCapacity = 4;
|
||||||
|
conveyorPlacement = true;
|
||||||
|
|
||||||
idleSound = Sounds.conveyor;
|
idleSound = Sounds.conveyor;
|
||||||
idleSoundVolume = 0.004f;
|
idleSoundVolume = 0.004f;
|
||||||
|
|||||||
Reference in New Issue
Block a user