/******************************************************************************* * Copyright 2014 See AUTHORS file. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package com.badlogic.gdx.ai.tests.pfa.tests.tiled.hrchy; import com.badlogic.gdx.ai.pfa.Connection; import com.badlogic.gdx.ai.pfa.indexed.IndexedHierarchicalGraph; import com.badlogic.gdx.ai.tests.pfa.tests.tiled.DungeonUtils; import com.badlogic.gdx.ai.tests.pfa.tests.tiled.DungeonUtils.TwoLevelHierarchy; import com.badlogic.gdx.ai.tests.pfa.tests.tiled.flat.FlatTiledNode; import com.badlogic.gdx.ai.tests.pfa.tests.tiled.TiledGraph; import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.utils.Array; /** A random generated graph representing a hierarchical tiled map. * * @author davebaol */ public class HierarchicalTiledGraph extends IndexedHierarchicalGraph<HierarchicalTiledNode> implements TiledGraph<HierarchicalTiledNode> { private static final int LEVELS = 2; public static final int[] sizeX = {125, 3}; public static final int[] sizeY = {75, 2}; public static final int[] offset = {0, sizeX[0] * sizeY[0]}; protected Array<HierarchicalTiledNode> nodes; public boolean diagonal; public HierarchicalTiledNode startNode; public HierarchicalTiledGraph () { super(LEVELS); this.nodes = new Array<HierarchicalTiledNode>(calculateTotalCapacity()); this.diagonal = false; this.startNode = null; } @Override public void init (int roomCount, int roomMinSize, int roomMaxSize, int squashIterations) { int tilesX = sizeX[0]; int tilesY = sizeY[0]; int buildingsX = sizeX[1]; int buildingsY = sizeY[1]; TwoLevelHierarchy twoLevelHierarchy = DungeonUtils.generate2LevelHierarchy(tilesX, tilesY, buildingsX, buildingsY, (int)(roomCount - roomCount * .8f), (int)(roomCount + roomCount * .8f), roomMinSize, roomMaxSize, squashIterations); // Create nodes for level 0 (tiles) this.level = 0; int map[][] = twoLevelHierarchy.level0; for (int x = 0; x < tilesX; x++) { for (int y = 0; y < tilesY; y++) { nodes.add(new HierarchicalTiledNode(x, y, map[x][y], nodes.size, 4)); } } // Create connections for level 0 // Each node has up to 4 neighbors, therefore no diagonal movement is possible for (int x = 0; x < tilesX; x++) { int idx = x * tilesY; for (int y = 0; y < tilesY; y++) { HierarchicalTiledNode n = nodes.get(idx + y); if (x > 0) addLevel0Connection(n, -1, 0); if (y > 0) addLevel0Connection(n, 0, -1); if (x < tilesX - 1) addLevel0Connection(n, 1, 0); if (y < tilesY - 1) addLevel0Connection(n, 0, 1); } } // Create nodes for level 1 (buildings) this.level = 1; int xxx = tilesX / buildingsX; int yyy = tilesY / buildingsY; for (int x = 0; x < buildingsX; x++) { for (int y = 0; y < buildingsY; y++) { int x0 = x * xxx; int y0 = y * yyy; int x1 = x0 + xxx; int y1 = y0 + yyy; final HierarchicalTiledNode lowerLevelNode = findFloorTileClosestToCenterOfMass(this.level - 1, x0, y0, x1, y1); nodes.add(new HierarchicalTiledNode(x, y, map[x][y], nodes.size, 4) { @Override public HierarchicalTiledNode getLowerLevelNode () { return lowerLevelNode; } }); } } // System.out.println(DungeonUtils.mapToString(map)); // Create connections for level 1 // Each node has up to 2 neighbors for (int x = 0; x < buildingsX; x++) { for (int y = 0; y < buildingsY; y++) { HierarchicalTiledNode n = getNode(x, y); if (twoLevelHierarchy.level1Con1[x][y]) addLevel1BidirectionalConnection(n, 0, 1); if (twoLevelHierarchy.level1Con2[x][y]) addLevel1BidirectionalConnection(n, 1, 0); } } } private HierarchicalTiledNode getNodeAtLevel (int level, int x, int y) { return nodes.get(x * sizeY[level] + y + offset[level]); } private HierarchicalTiledNode findFloorTileClosestToCenterOfMass (int level, int x0, int y0, int x1, int y1) { // Calculate center of mass Vector2 centerOfMass = new Vector2(0, 0); int floorTiles = 0; for (int x = x0; x < x1; x++) { for (int y = y0; y < y1; y++) { HierarchicalTiledNode n = getNodeAtLevel(level, x, y); if (n.type == FlatTiledNode.TILE_FLOOR) { centerOfMass.add(n.x, n.y); floorTiles++; } } } int comx = (int)(centerOfMass.x / floorTiles); int comy = (int)(centerOfMass.y / floorTiles); HierarchicalTiledNode comTile = getNodeAtLevel(level, comx, comy); if (comTile.type == FlatTiledNode.TILE_FLOOR) return comTile; // Find floor tile closest to the center of mass float closetDist2 = Float.POSITIVE_INFINITY; HierarchicalTiledNode closestFloor = null; for (int x = x0; x < x1; x++) { for (int y = y0; y < y1; y++) { HierarchicalTiledNode n = getNodeAtLevel(level, x, y); if (n.type == FlatTiledNode.TILE_FLOOR) { float dist2 = Vector2.dst2(comTile.x, comTile.y, n.x, n.y); if (dist2 < closetDist2) { closetDist2 = dist2; closestFloor = n; } } } } return closestFloor; } @Override public HierarchicalTiledNode convertNodeBetweenLevels (int inputLevel, HierarchicalTiledNode node, int outputLevel) { if (inputLevel < outputLevel) { int newX = node.x / (sizeX[inputLevel] / sizeX[outputLevel]); int newY = node.y / (sizeY[inputLevel] / sizeY[outputLevel]); return nodes.get(newX * sizeY[outputLevel] + newY + offset[outputLevel]); } if (inputLevel > outputLevel) { return node.getLowerLevelNode(); } return node; } @Override public HierarchicalTiledNode getNode (int x, int y) { return nodes.get(x * sizeY[level] + y + offset[level]); } @Override public HierarchicalTiledNode getNode (int index) { return nodes.get(index); } @Override public int getIndex (HierarchicalTiledNode node) { return node.getIndex(); } @Override public int getNodeCount () { return nodes.size; } @Override public Array<Connection<HierarchicalTiledNode>> getConnections (HierarchicalTiledNode fromNode) { return fromNode.getConnections(); } private void addLevel0Connection (HierarchicalTiledNode n, int xOffset, int yOffset) { HierarchicalTiledNode target = getNode(n.x + xOffset, n.y + yOffset); if (target.type == FlatTiledNode.TILE_FLOOR) n.getConnections().add(new HierarchicalTiledConnection(this, n, target)); } private void addLevel1BidirectionalConnection (HierarchicalTiledNode n, int xOffset, int yOffset) { HierarchicalTiledNode target = getNode(n.x + xOffset, n.y + yOffset); n.getConnections().add(new HierarchicalTiledConnection(this, n, target)); target.getConnections().add(new HierarchicalTiledConnection(this, target, n)); } private static final int calculateTotalCapacity () { int capacity = 0; for (int i = 0; i < LEVELS; i++) capacity += sizeX[i] * sizeY[i]; return capacity; } }