package vooga.rts.map; import java.awt.Graphics2D; import java.awt.Rectangle; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Observable; import java.util.Observer; import util.Location; import vooga.rts.gamedesign.sprite.gamesprites.GameSprite; import vooga.rts.gamedesign.sprite.gamesprites.interactive.InteractiveEntity; import vooga.rts.gamedesign.sprite.map.Tile; import vooga.rts.util.Camera; import vooga.rts.util.Location3D; /** * This class stores all the nodes that will be used for pathfinding. * * @author Jonathan Schmidt * @author Challen Herzberg-Brovold * */ public class NodeMap implements Observer { private int myWidth; private int myHeight; private Node[][] myMap; private Map<GameSprite, Node> myLookupMap; public NodeMap (int width, int height) { myMap = new Node[width][height]; myWidth = width; myHeight = height; myLookupMap = new HashMap<GameSprite, Node>(); } /** * Returns all the neighboring nodes of a specified node. * * @param current the node for which we want the neighbors * @return a list of neighbors of the node */ public List<Node> getNeighbors (Node current) { List<Node> neighbors = new ArrayList<Node>(); int x = current.getX(); int y = current.getY(); for (int i = -1; i < 2; i += 2) { neighbors.add(get(x + i, y)); neighbors.add(get(x, y + i)); } return neighbors; } /** * Returns the node at the specified coordinates * * @param x-coordinate * @param y-coordinate * @return node at the coordinates */ public Node get (int x, int y) { if (x >= 0 && y >= 0 && x < myMap.length && y < myMap[0].length) { return myMap[x][y]; } return null; } /** * * @return width of the map in nodes */ public int getWidth () { return myWidth; } /** * * @return height of the map in nodes. */ public int getHeight () { return myHeight; } public void put (Node node, int x, int y) { myMap[x][y] = node; } public void paint (Graphics2D pen) { Rectangle view = Camera.instance().getWorldVision().getBounds(); // Get the start index of what is visible by the cameras. int startX = (int) (view.getMinX() > 0 ? view.getMinX() : 0); startX /= Node.NODE_SIZE; int startY = (int) (view.getMinY() > 0 ? view.getMinY() : 0); startY /= Node.NODE_SIZE; // Get the end index of what is visible int endX = (int) (view.getMaxX() < Node.NODE_SIZE * myWidth ? view.getMaxX() : myWidth * Node.NODE_SIZE); endX /= Node.NODE_SIZE; endX = endX < myWidth ? endX : myWidth - 1; int endY = (int) (view.getMaxY() < Node.NODE_SIZE * myHeight ? view.getMaxY() : myHeight * Node.NODE_SIZE); endY /= Node.NODE_SIZE; endY = endY < myHeight ? endY : myHeight - 1; int depth = (endX - startX) + (endY - startY); for (int z = 0; z < depth; z++) { for (int y = 0; y <= z; y++) { int x = z - y; Node n = get(x + startX, y + startY); if (n != null) { n.paint(pen); } else { break; } } } } /** * Adds a Sprite to a Node. * * @param sprite The sprite to be added to the node. * @param node The node to add the sprite to. */ private void addToNode (GameSprite sprite, Node node) { myLookupMap.put(sprite, node); node.addSprite(sprite); } /** * Removes a Sprite from its current Node. * * @param sprite The sprite to be added to the node. * @param node The node to add the sprite to. */ private void removeFromNode (GameSprite sprite) { Node node = myLookupMap.get(sprite); if (node != null) { node.removeSprite(sprite); } } /** * Finds a node that a location should correspond to. * * @param world The world location. * @return The node that the location is inside. */ private Node findContainingNode (Location3D world) { // This should be the node for this location. Node potential = getNode(world); if (potential != null) { // && potential.contains(world)) { return potential; } return null; } /** * Returns a list of nodes less than a certain distance of the center. * * @param center The center * @param radius The distance to search from the center * @return */ public List<Node> getNodesinArea (Location3D center, double radius) { // generate the square int numTiles = (int) Math.ceil(radius / Node.NODE_SIZE); int nodeX = (int) Math.floor(center.getX() / Node.NODE_SIZE); int nodeY = (int) Math.floor(center.getY() / Node.NODE_SIZE); List<Node> nodeList = new ArrayList<Node>(); for (int x = nodeX - numTiles; x < nodeX + numTiles; x++) { for (int y = nodeY - numTiles; y < nodeY + numTiles; y++) { Node cur = get(x, y); if (cur != null) { if (cur.contains(new Location3D(x * Node.NODE_SIZE, y * Node.NODE_SIZE, center .getZ()))) { nodeList.add(cur); } } } } return nodeList; } /** * Returns a node that corresponds to a provided world location. * * @param location The location of * @return */ public Node getNode (Location3D location) { int x = (int) Math.floor(location.getX() / Node.NODE_SIZE); int y = (int) Math.floor(location.getY() / Node.NODE_SIZE); return get(x, y); } @Override public void update (Observable arg0, Object arg1) { // Map only worries about Game Sprite observables if (!(arg0 instanceof GameSprite)) { return; } GameSprite item = (GameSprite) arg0; Node cur = myLookupMap.get(item); // If the map doesn't know about it yet if (cur == null) { Node newNode = findContainingNode(item.getWorldLocation()); if (newNode != null) { addToNode(item, newNode); cur = newNode; } } if (cur == null) { return; } // if it's updating with its new location if (arg1 instanceof Location3D) { // hasn't moved outside of the current node if (cur.contains(item.getWorldLocation())) { return; } else { Node newNode = findContainingNode(item.getWorldLocation()); if (newNode != null) { removeFromNode(item); addToNode(item, newNode); } } } if (item instanceof InteractiveEntity) { InteractiveEntity ie = (InteractiveEntity) item; if (ie.isDead()) { if (cur != null) { removeFromNode(item); } } } if (arg1 instanceof String && ((String)arg1).equals("remove")) { removeFromNode(item); } } }