From ed8d9459b22040efcdedc26ce279315aeb41b3f8 Mon Sep 17 00:00:00 2001 From: NewGarbo Date: Tue, 12 Jan 2016 07:02:19 +0000 Subject: [PATCH] goes with the commit below VVVV --- .../clans/clans/outpost/OutpostBuilder.java | 130 ++++++ .../clans/clans/outpost/pathing/AStar.java | 391 ++++++++++++++++++ .../clans/outpost/pathing/PathingResult.java | 23 ++ .../clans/clans/outpost/pathing/Tile.java | 183 ++++++++ 4 files changed, 727 insertions(+) create mode 100644 Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/outpost/OutpostBuilder.java create mode 100644 Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/outpost/pathing/AStar.java create mode 100644 Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/outpost/pathing/PathingResult.java create mode 100644 Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/outpost/pathing/Tile.java diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/outpost/OutpostBuilder.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/outpost/OutpostBuilder.java new file mode 100644 index 000000000..51b0724ad --- /dev/null +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/outpost/OutpostBuilder.java @@ -0,0 +1,130 @@ +package mineplex.game.clans.clans.outpost; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.entity.Skeleton; +import org.bukkit.inventory.ItemStack; + +import com.mojang.authlib.GameProfile; + +import mineplex.core.common.util.UtilEnt; +import mineplex.core.common.util.UtilMath; +import mineplex.core.common.util.UtilPlayer; +import mineplex.core.disguise.disguises.DisguisePlayer; +import mineplex.game.clans.clans.outpost.pathing.AStar; +import mineplex.game.clans.clans.outpost.pathing.AStar.InvalidPathException; +import mineplex.game.clans.clans.outpost.pathing.PathingResult; +import mineplex.game.clans.clans.outpost.pathing.Tile; + +public class OutpostBuilder +{ + private Skeleton _entity; + + private List _path; + private int _pathPos; + private List _moveLocations; + private int _nextLocation; + private long _arrivedTime; + private long _lastMovement; + + private Outpost _host; + + public OutpostBuilder(Location pos, String name, List locations, Outpost host) + { + _host = host; + + Location spawnLoc = pos; + Skeleton skel = pos.getWorld().spawn(spawnLoc, Skeleton.class); + skel.teleport(spawnLoc); + skel.setHealth(20); + UtilEnt.Vegetate(skel); + UtilEnt.silence(skel, true); + + skel.getEquipment().setHelmet(new ItemStack(Material.NETHER_BRICK_ITEM, 1)); + skel.getEquipment().setItemInHand(new ItemStack(Material.IRON_PICKAXE, 1)); + + // Get in range + List inRange = UtilPlayer.getNearby(spawnLoc, 75d); + + // Disguise + DisguisePlayer disguise = new DisguisePlayer(skel, new GameProfile(Bukkit.getOfflinePlayer("Chiss").getUniqueId(), "Chiss")); + host.getClan().Clans.getDisguiseManager().disguise(disguise, inRange.toArray(new Player[inRange.size()])); + + _entity = skel; + + _moveLocations = locations; + } + + public void tick() + { + if (_path == null || _arrivedTime != -1) + { + _nextLocation = UtilMath.random.nextInt(_moveLocations.size()); + _path = calculatePath(); + _pathPos = 0; + return; + } + + if (System.currentTimeMillis() - _lastMovement >= 500) + { + if (_pathPos == _path.size() - 1) + { + _arrivedTime = 1; + } + else + { + _entity.teleport(_path.get(++_pathPos)); + _arrivedTime = -1; + } + + _lastMovement = System.currentTimeMillis(); + } + } + + private List calculatePath() + { + Location start = _entity.getLocation(); + Location end = _moveLocations.get(_nextLocation); + int range = _host._type._size * 2; + + try + { + AStar pathFinder = new AStar(start, end, range); + ArrayList route = pathFinder.iterate(); + PathingResult res = pathFinder.getPathingResult(); + + if (res == PathingResult.NO_PATH) + { + return Arrays.asList(_moveLocations.get(_nextLocation)); + } + + List list = new ArrayList<>(); + + for (Tile tile : route) + { + list.add(tile.getLocation(start)); + } + + return list; + } + catch (InvalidPathException e) + { + cleanup(); + + return null; + } + } + + private void cleanup() + { + _entity.remove(); + _host.queueForRemoval(this); + } + +} diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/outpost/pathing/AStar.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/outpost/pathing/AStar.java new file mode 100644 index 000000000..b3da829eb --- /dev/null +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/outpost/pathing/AStar.java @@ -0,0 +1,391 @@ +/* + * By @Adamki11s + */ + +package mineplex.game.clans.clans.outpost.pathing; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; + +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.block.Block; +import org.bukkit.material.Gate; + +/** + * Not made by meee (NewGarbo) + * https://bukkit.org/threads/lib-a-pathfinding-algorithm.129786/ + */ +public class AStar +{ + private final int _sx; + private final int _sy; + private final int _sz; + private final int _ex; + private final int _ey; + private final int _ez; + private final World _world; + + private PathingResult _result; + + private HashMap _open = new HashMap(); + private HashMap _closed = new HashMap(); + + private void addToOpenList(Tile t, boolean modify) + { + if (_open.containsKey(t.getUID())) + { + if (modify) + { + _open.put(t.getUID(), t); + } + } + else + { + _open.put(t.getUID(), t); + } + } + + private void addToClosedList(Tile t) + { + if (!_closed.containsKey(t.getUID())) + { + _closed.put(t.getUID(), t); + } + } + + private final int _range; + private final String _endUID; + + public AStar(Location start, Location end, int range) throws InvalidPathException + { + boolean s = true, e = true; + + if (!(s = isLocationWalkable(start)) || !(e = isLocationWalkable(end))) + { + throw new InvalidPathException(s, e); + } + + _world = start.getWorld(); + _sx = start.getBlockX(); + _sy = start.getBlockY(); + _sz = start.getBlockZ(); + _ex = end.getBlockX(); + _ey = end.getBlockY(); + _ez = end.getBlockZ(); + + _range = range; + + short sh = 0; + Tile t = new Tile(sh, sh, sh, null); + t.calculateBoth(_sx, _sy, _sz, _ex, _ey, _ez, true); + _open.put(t.getUID(), t); + processAdjacentTiles(t); + + StringBuilder b = new StringBuilder(); + b.append(_ex - _sx).append(_ey - _sy).append(_ez - _sz); + _endUID = b.toString(); + } + + public Location getEndLocation() + { + return new Location(_world, _ex, _ey, _ez); + } + + public PathingResult getPathingResult() + { + return _result; + } + + protected boolean _checkOnce = false; + + private int abs(int i) + { + return (i < 0 ? -i : i); + } + + public ArrayList iterate() + { + if (!_checkOnce) + { + // invert the boolean flag + _checkOnce ^= true; + if ((abs(_sx - _ex) > _range) || (abs(_sy - _ey) > _range) || (abs(_sz - _ez) > _range)) + { + _result = PathingResult.NO_PATH; + return null;// jump out + } + } + // while not at end + Tile current = null; + + while (canContinue()) + { + + // get lowest F cost square on open list + current = getLowestFTile(); + + // process tiles + processAdjacentTiles(current); + } + + if (_result != PathingResult.SUCCESS) + { + return null; + } + else + { + // path found + LinkedList routeTrace = new LinkedList(); + Tile parent; + + routeTrace.add(current); + + while ((parent = current.getParent()) != null) + { + routeTrace.add(parent); + current = parent; + } + + Collections.reverse(routeTrace); + + return new ArrayList(routeTrace); + } + } + + private boolean canContinue() + { + // check if open list is empty, if it is no path has been found + if (_open.size() == 0) + { + _result = PathingResult.NO_PATH; + return false; + } + else + { + if (_closed.containsKey(_endUID)) + { + _result = PathingResult.SUCCESS; + return false; + } + else + { + return true; + } + } + } + + private Tile getLowestFTile() + { + double f = 0; + Tile drop = null; + + // get lowest F cost square + for (Tile t : _open.values()) + { + if (f == 0) + { + t.calculateBoth(_sx, _sy, _sz, _ex, _ey, _ez, true); + f = t.getF(); + drop = t; + } + else + { + t.calculateBoth(_sx, _sy, _sz, _ex, _ey, _ez, true); + double posF = t.getF(); + if (posF < f) + { + f = posF; + drop = t; + } + } + } + + // drop from open list and add to closed + + _open.remove(drop.getUID()); + addToClosedList(drop); + + return drop; + } + + private boolean isOnClosedList(Tile t) + { + return _closed.containsKey(t.getUID()); + } + + // pass in the current tile as the parent + private void processAdjacentTiles(Tile current) + { + + // set of possible walk to locations adjacent to current tile + HashSet possible = new HashSet(26); + + for (byte x = -1; x <= 1; x++) + { + for (byte y = -1; y <= 1; y++) + { + for (byte z = -1; z <= 1; z++) + { + + if (x == 0 && y == 0 && z == 0) + { + continue;// don't check current square + } + + Tile t = new Tile((short) (current.getX() + x), (short) (current.getY() + y), (short) (current.getZ() + z), current); + + if (!t.isInRange(_range)) + { + // if block is out of bounds continue + continue; + } + + if (x != 0 && z != 0 && (y == 0 || y == 1)) + { + // check to stop jumping through diagonal blocks + Tile xOff = new Tile((short) (current.getX() + x), (short) (current.getY() + y), (short) (current.getZ()), current), zOff = new Tile((short) (current.getX()), (short) (current.getY() + y), (short) (current.getZ() + z), current); + if (!isTileWalkable(xOff) && !isTileWalkable(zOff)) + { + continue; + } + } + + if (isOnClosedList(t)) + { + // ignore tile + continue; + } + + // only process the tile if it can be walked on + if (isTileWalkable(t)) + { + t.calculateBoth(_sx, _sy, _sz, _ex, _ey, _ez, true); + possible.add(t); + } + + } + } + } + + for (Tile t : possible) + { + // get the reference of the object in the array + Tile openRef = null; + if ((openRef = isOnOpenList(t)) == null) + { + // not on open list, so add + addToOpenList(t, false); + } + else + { + // is on open list, check if path to that square is better using + // G cost + if (t.getG() < openRef.getG()) + { + // if current path is better, change parent + openRef.setParent(current); + // force updates of F, G and H values. + openRef.calculateBoth(_sx, _sy, _sz, _ex, _ey, _ez, true); + } + + } + } + + } + + private Tile isOnOpenList(Tile t) + { + return (_open.containsKey(t.getUID()) ? _open.get(t.getUID()) : null); + /* + * for (Tile o : open) { if (o.equals(t)) { return o; } } return null; + */ + } + + private boolean isTileWalkable(Tile t) + { + Location l = new Location(_world, (_sx + t.getX()), (_sy + t.getY()), (_sz + t.getZ())); + Block b = l.getBlock(); + int i = b.getTypeId(); + + // lava, fire, wheat and ladders cannot be walked on, and of course air + // 85, 107 and 113 stops npcs climbing fences and fence gates + if (i != 10 && i != 11 && i != 51 && i != 59 && i != 65 && i != 0 && i != 85 && i != 107 && i != 113 && !canBlockBeWalkedThrough(i)) + { + // make sure the blocks above are air + + if (b.getRelative(0, 1, 0).getTypeId() == 107) + { + // fench gate check, if closed continue + Gate g = new Gate(b.getRelative(0, 1, 0).getData()); + return (g.isOpen() ? (b.getRelative(0, 2, 0).getTypeId() == 0) : false); + } + return (canBlockBeWalkedThrough(b.getRelative(0, 1, 0).getTypeId()) && b.getRelative(0, 2, 0).getTypeId() == 0); + + } + else + { + return false; + } + } + + private boolean isLocationWalkable(Location l) + { + Block b = l.getBlock(); + int i = b.getTypeId(); + + if (i != 10 && i != 11 && i != 51 && i != 59 && i != 65 && i != 0 && !canBlockBeWalkedThrough(i)) + { + // make sure the blocks above are air or can be walked through + return (canBlockBeWalkedThrough(b.getRelative(0, 1, 0).getTypeId()) && b.getRelative(0, 2, 0).getTypeId() == 0); + } + else + { + return false; + } + } + + private boolean canBlockBeWalkedThrough(int id) + { + return (id == 0 || id == 6 || id == 50 || id == 63 || id == 30 || id == 31 || id == 32 || id == 37 || id == 38 || id == 39 || id == 40 || id == 55 || id == 66 || id == 75 || id == 76 || id == 78); + } + + @SuppressWarnings("serial") + public class InvalidPathException extends Exception + { + private final boolean _s, _e; + + public InvalidPathException(boolean s, boolean e) + { + _s = s; + _e = e; + } + + public String getErrorReason() + { + StringBuilder sb = new StringBuilder(); + if (!_s) + { + sb.append("Start Location was air. "); + } + if (!_e) + { + sb.append("End Location was air."); + } + return sb.toString(); + } + + public boolean isStartNotSolid() + { + return (!_s); + } + + public boolean isEndNotSolid() + { + return (!_e); + } + } +} diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/outpost/pathing/PathingResult.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/outpost/pathing/PathingResult.java new file mode 100644 index 000000000..06e0084dc --- /dev/null +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/outpost/pathing/PathingResult.java @@ -0,0 +1,23 @@ +package mineplex.game.clans.clans.outpost.pathing; + +/** + * Not made by meee (NewGarbo) + * https://bukkit.org/threads/lib-a-pathfinding-algorithm.129786/ + */ +public enum PathingResult +{ + SUCCESS(0), + NO_PATH(-1); + + private final int _ec; + + PathingResult(int ec) + { + _ec = ec; + } + + public int getEndCode() + { + return _ec; + } +} diff --git a/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/outpost/pathing/Tile.java b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/outpost/pathing/Tile.java new file mode 100644 index 000000000..133d67278 --- /dev/null +++ b/Plugins/Mineplex.Game.Clans/src/mineplex/game/clans/clans/outpost/pathing/Tile.java @@ -0,0 +1,183 @@ +/* + * By @Adamki11s + */ + +package mineplex.game.clans.clans.outpost.pathing; + +import org.bukkit.Location; + +/** + * Not made by meee (NewGarbo) + * https://bukkit.org/threads/lib-a-pathfinding-algorithm.129786/ + */ +public class Tile +{ + // as offset from starting point + private final short _x; + private final short _y; + private final short _z; + + private double _g = -1; + private double _h = -1; + + private Tile _parent = null; + + private final String _uid; + + public Tile(short x, short y, short z, Tile parent) + { + _x = x; + _y = y; + _z = z; + _parent = parent; + + StringBuilder b = new StringBuilder(); + b.append(x); + b.append(y); + b.append(z); + _uid = b.toString(); + + } + + public boolean isInRange(int range) + { + return ((range - abs(_x) >= 0) && (range - abs(_y) >= 0) && (range - abs(_z) >= 0)); + } + + public void setParent(Tile parent) + { + _parent = parent; + } + + public Location getLocation(Location start) + { + return new Location(start.getWorld(), start.getBlockX() + _x, start.getBlockY() + _y, start.getBlockZ() + _z); + } + + public Tile getParent() + { + return _parent; + } + + public short getX() + { + return _x; + } + + public int getX(Location i) + { + return (i.getBlockX() + _x); + } + + public short getY() + { + return _y; + } + + public int getY(Location i) + { + return (i.getBlockY() + _y); + } + + public short getZ() + { + return _z; + } + + public int getZ(Location i) + { + return (i.getBlockZ() + _z); + } + + public String getUID() + { + return _uid; + } + + public boolean equals(Tile t) + { + return (t.getX() == _x && t.getY() == _y && t.getZ() == _z); + } + + public void calculateBoth(int sx, int sy, int sz, int ex, int ey, int ez, boolean update) + { + calculateG(sx, sy, sz, update); + calculateH(sx, sy, sz, ex, ey, ez, update); + } + + public void calculateH(int sx, int sy, int sz, int ex, int ey, int ez, boolean update) + { + // only update if h hasn't been calculated or if forced + if ((!update && _h == -1) || update) + { + int hx = sx + _x, hy = sy + _y, hz = sz + _z; + _h = getEuclideanDistance(hx, hy, hz, ex, ey, ez); + } + } + + // G = the movement cost to move from the starting point A to a given square + // on the grid, following the path generated to get there. + public void calculateG(int sx, int sy, int sz, boolean update) + { + if ((!update && _g == -1) || update) + { + // only update if g hasn't been calculated or if forced + Tile currentParent = getParent(), currentTile = this; + int gCost = 0; + // follow path back to start + while ((currentParent = currentTile.getParent()) != null) + { + int dx = currentTile.getX() - currentParent.getX(), dy = currentTile.getY() - currentParent.getY(), dz = currentTile.getZ() - currentParent.getZ(); + + dx = abs(dx); + dy = abs(dy); + dz = abs(dz); + + if (dx == 1 && dy == 1 && dz == 1) + { + gCost += 1.7; + } else if (((dx == 1 || dz == 1) && dy == 1) || ((dx == 1 || dz == 1) && dy == 0)) + { + gCost += 1.4; + } + else + { + gCost += 1.0; + } + + // move backwards a tile + currentTile = currentParent; + } + _g = gCost; + } + + } + + public double getG() + { + return _g; + } + + public double getH() + { + return _h; + } + + public double getF() + { + // f = h + g + return (_h + _g); + } + + private double getEuclideanDistance(int sx, int sy, int sz, int ex, int ey, int ez) + { + double dx = sx - ex, dy = sy - ey, dz = sz - ez; + return Math.sqrt((dx * dx) + (dy * dy) + (dz * dz)); + } + + private int abs(int i) + { + return (i < 0 ? -i : i); + } + +}