From 1156ead143f7919b3630076a9df6014c80100d37 Mon Sep 17 00:00:00 2001 From: Anuken Date: Fri, 12 Jan 2018 14:50:29 -0500 Subject: [PATCH] Implemented different heuristics for different difficulties --- .../src/io/anuke/mindustry/ai/Heuristics.java | 12 ++++++- core/src/io/anuke/mindustry/ai/Pathfind.java | 34 +++---------------- .../io/anuke/mindustry/core/NetClient.java | 1 + .../io/anuke/mindustry/game/Difficulty.java | 27 +++++++++++---- .../mindustry/ui/dialogs/JoinDialog.java | 3 +- 5 files changed, 40 insertions(+), 37 deletions(-) diff --git a/core/src/io/anuke/mindustry/ai/Heuristics.java b/core/src/io/anuke/mindustry/ai/Heuristics.java index db28f29ef1..9df327eff7 100644 --- a/core/src/io/anuke/mindustry/ai/Heuristics.java +++ b/core/src/io/anuke/mindustry/ai/Heuristics.java @@ -2,12 +2,14 @@ package io.anuke.mindustry.ai; import com.badlogic.gdx.ai.pfa.Heuristic; import io.anuke.mindustry.Vars; +import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.blocks.types.defense.Turret; import io.anuke.mindustry.world.blocks.types.production.Drill; import io.anuke.mindustry.world.blocks.types.production.Generator; import io.anuke.mindustry.world.blocks.types.production.Pump; import io.anuke.mindustry.world.blocks.types.production.Smelter; +import io.anuke.ucore.function.Predicate; public class Heuristics { /**How many times more it costs to go through a destructible block than an empty block.*/ @@ -36,6 +38,11 @@ public class Heuristics { } public static class DestrutiveHeuristic implements Heuristic { + private final Predicate frees; + + public DestrutiveHeuristic(Predicate frees){ + this.frees = frees; + } @Override public float estimate(Tile node, Tile other){ @@ -51,8 +58,11 @@ public class Heuristics { //if this block has solid blocks near it, increase the cost, as we don't want enemies hugging walls if(node.occluded) cost += Vars.tilesize*occludedMultiplier; + if(other.getLinked() != null) other = other.getLinked(); + if(node.getLinked() != null) node = node.getLinked(); + //generators are free! - if(generator(other) || generator(node)) cost = 0; + if(frees.test(other.block()) || frees.test(node.block())) cost = 0; return cost; } diff --git a/core/src/io/anuke/mindustry/ai/Pathfind.java b/core/src/io/anuke/mindustry/ai/Pathfind.java index 55481fc7b5..6d81452a5f 100644 --- a/core/src/io/anuke/mindustry/ai/Pathfind.java +++ b/core/src/io/anuke/mindustry/ai/Pathfind.java @@ -1,12 +1,10 @@ package io.anuke.mindustry.ai; -import com.badlogic.gdx.ai.pfa.Heuristic; import com.badlogic.gdx.ai.pfa.PathFinderRequest; import com.badlogic.gdx.ai.pfa.PathSmoother; import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.math.Vector2; import io.anuke.mindustry.Vars; -import io.anuke.mindustry.ai.Heuristics.DestrutiveHeuristic; import io.anuke.mindustry.entities.enemies.Enemy; import io.anuke.mindustry.game.SpawnPoint; import io.anuke.mindustry.world.Tile; @@ -20,8 +18,6 @@ public class Pathfind{ /**Maximum time taken per frame on pathfinding for a single path.*/ private static final long maxTime = 1000000 * 5; - /**Heuristic for determining cost between two tiles*/ - Heuristic heuristic = new DestrutiveHeuristic(); /**Tile graph, for determining conenctions between two tiles*/ TileGraph graph = new TileGraph(); /**Smoother that removes extra nodes from a path.*/ @@ -53,38 +49,17 @@ public class Pathfind{ enemy.node = -1; return vector.set(enemy.x, enemy.y); } - - //if an enemy is idle for a while, it's probably stuck - //if(enemy.idletime > EnemyType.maxIdle){ - - //Tile target = path[enemy.node]; - //if(Vars.world.raycastWorld(enemy.x, enemy.y, target.worldx(), target.worldy()) != null) { - // if (enemy.node > 1) - // enemy.node = enemy.node - 1; - // enemy.idletime = 0; - //} - - //else, must be blocked by a playermade block, do nothing - //} - - //-1 is only possible here if both pathfindings failed, which should NOT happen - //check graph code if(enemy.node <= -1){ return vector.set(enemy.x, enemy.y); } - if(enemy.node >= path.length){ - enemy.node = -1; - return vector.set(enemy.x, enemy.y); - } - //TODO documentation on what this does Tile prev = path[enemy.node - 1]; Tile target = path[enemy.node]; - //a bridge has broken + //a bridge has been broken, re-path if(!Vars.world.passable(target.x, target.y)){ remakePath(); return vector.set(enemy.x, enemy.y); @@ -131,6 +106,7 @@ public class Pathfind{ } + /**Re-calculate paths for all enemies. Runs when a path changes while moving.*/ private void remakePath(){ for(int i = 0; i < Vars.control.enemyGroup.amount(); i ++){ Enemy enemy = Vars.control.enemyGroup.all().get(i); @@ -169,13 +145,13 @@ public class Pathfind{ //warmup for(int i = 0; i < 100; i ++){ - point.finder.searchNodePath(point.start, Vars.control.getCore(), heuristic, point.path); + point.finder.searchNodePath(point.start, Vars.control.getCore(), Vars.control.getDifficulty().heuristic, point.path); point.path.clear(); } Timers.mark(); for(int i = 0; i < amount; i ++){ - point.finder.searchNodePath(point.start, Vars.control.getCore(), heuristic, point.path); + point.finder.searchNodePath(point.start, Vars.control.getCore(), Vars.control.getDifficulty().heuristic, point.path); point.path.clear(); } UCore.log("Time elapsed: " + Timers.elapsed() + "ms\nAverage MS per path: " + Timers.elapsed()/amount); @@ -190,7 +166,7 @@ public class Pathfind{ point.pathTiles = null; - point.request = new PathFinderRequest<>(point.start, Vars.control.getCore(), heuristic, point.path); + point.request = new PathFinderRequest<>(point.start, Vars.control.getCore(), Vars.control.getDifficulty().heuristic, point.path); point.request.statusChanged = true; //IMPORTANT! } } diff --git a/core/src/io/anuke/mindustry/core/NetClient.java b/core/src/io/anuke/mindustry/core/NetClient.java index 27c967af4e..92c356c13b 100644 --- a/core/src/io/anuke/mindustry/core/NetClient.java +++ b/core/src/io/anuke/mindustry/core/NetClient.java @@ -254,6 +254,7 @@ public class NetClient extends Module { Gdx.app.postRunnable(() -> { //duplicates. if(Vars.control.enemyGroup.getByID(player.id) != null) return; + player.getInterpolator().last.set(player.x, player.y); player.getInterpolator().target.set(player.x, player.y); player.add(); diff --git a/core/src/io/anuke/mindustry/game/Difficulty.java b/core/src/io/anuke/mindustry/game/Difficulty.java index 447f1467cc..e5b05d934b 100644 --- a/core/src/io/anuke/mindustry/game/Difficulty.java +++ b/core/src/io/anuke/mindustry/game/Difficulty.java @@ -1,13 +1,25 @@ package io.anuke.mindustry.game; +import com.badlogic.gdx.ai.pfa.Heuristic; +import io.anuke.mindustry.ai.Heuristics.DestrutiveHeuristic; +import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.blocks.types.LiquidBlock; +import io.anuke.mindustry.world.blocks.types.PowerBlock; +import io.anuke.mindustry.world.blocks.types.defense.Turret; +import io.anuke.mindustry.world.blocks.types.distribution.Conveyor; +import io.anuke.mindustry.world.blocks.types.distribution.Router; +import io.anuke.mindustry.world.blocks.types.production.Drill; +import io.anuke.mindustry.world.blocks.types.production.Generator; +import io.anuke.mindustry.world.blocks.types.production.Smelter; import io.anuke.ucore.util.Bundles; public enum Difficulty { - easy(4f, 2f), - normal(2f, 1f), - hard(1.5f, 0.5f), - insane(0.5f, 0.25f), - purge(0.35f, 0.01f); + easy(4f, 2f, new DestrutiveHeuristic(b -> b instanceof Generator)), + normal(2f, 1f, new DestrutiveHeuristic(b -> b instanceof Smelter || b instanceof Generator)), + hard(1.5f, 0.5f, new DestrutiveHeuristic(b -> b instanceof Turret || b instanceof Generator || b instanceof Drill || b instanceof Smelter)), + insane(0.5f, 0.25f, new DestrutiveHeuristic(b -> b instanceof Turret || b instanceof Generator || b instanceof Drill || b instanceof Smelter || b instanceof Router)), + purge(0.35f, 0.01f, new DestrutiveHeuristic(b -> b instanceof Turret || b instanceof Generator || b instanceof Drill || b instanceof Router + || b instanceof Smelter || b instanceof Conveyor || b instanceof LiquidBlock || b instanceof PowerBlock)); /**The scaling of how many waves it takes for one more enemy of a type to appear. * For example: with enemeyScaling = 2 and the default scaling being 2, it would take 4 waves for @@ -16,9 +28,12 @@ public enum Difficulty { /**Multiplier of the time between waves.*/ public final float timeScaling; - Difficulty(float enemyScaling, float timeScaling){ + public final Heuristic heuristic; + + Difficulty(float enemyScaling, float timeScaling, Heuristic heuristic){ this.enemyScaling = enemyScaling; this.timeScaling = timeScaling; + this.heuristic = heuristic; } @Override diff --git a/core/src/io/anuke/mindustry/ui/dialogs/JoinDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/JoinDialog.java index 197836940d..1018408287 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/JoinDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/JoinDialog.java @@ -117,6 +117,7 @@ public class JoinDialog extends FloatingDialog { while(t.getCause() != null){ t = t.getCause(); } + //TODO localize String error = t.getMessage() == null ? "" : t.getMessage().toLowerCase(); if(error.contains("connection refused")) { error = "connection refused"; @@ -125,7 +126,7 @@ public class JoinDialog extends FloatingDialog { }else if(error.contains("invalid argument")) { error = "invalid IP or port!"; }else if(t.getClass().toString().toLowerCase().contains("sockettimeout")){ - error = "timed out!"; + error = "timed out!\nmake sure the host has port forwarding set up,\nand that the address is correct!"; }else{ error = Strings.parseException(e, false); }