/*******************************************************************************
* Copyright 2015 Maximilian Stark | Dakror <mail@dakror.de>
*
* 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 de.dakror.vloxlands.ai.path;
import java.util.ArrayDeque;
import com.badlogic.gdx.math.Vector3;
import com.badlogic.gdx.utils.Array;
import de.dakror.vloxlands.ai.path.node.BFSNode;
import de.dakror.vloxlands.game.voxel.Voxel;
/**
* @author Dakror
*/
public class BFS {
public static Array<BFSNode> visited = new Array<BFSNode>();
static ArrayDeque<BFSNode> queue = new ArrayDeque<BFSNode>();
final static Vector3 start = new Vector3();
static BFSConfig cfg;
public synchronized static Path findClosestVoxel(Vector3 pos, BFSConfig cfg) {
BFS.cfg = cfg;
start.set(pos);
visited.clear();
queue.clear();
queue.add(new BFSNode(pos.x, pos.y, pos.z, cfg.creature.getIsland().get(pos.x, pos.y, pos.z), cfg.creature.getIsland().getMeta(pos.x, pos.y, pos.z), null));
while (queue.size() > 0) {
BFSNode v = queue.poll();
boolean done = v.voxel == cfg.voxel;
if (cfg.meta != 0 && done) {
boolean sameMeta = cfg.meta == v.meta;
done &= sameMeta != cfg.notMeta;
}
if (cfg.neighborMeta != 0 && done) {
done &= checkNeighbors(v);
}
if (done) {
Path p = AStar.findPath(pos, new Vector3(v.x, v.y, v.z), cfg.creature, cfg.maxRange, true);
if (p != null) return p;
}
addNeighbors(v);
}
return null;
}
static boolean checkNeighbors(BFSNode node) {
for (int i = -cfg.neighborRangeX; i <= cfg.neighborRangeX; i++) {
for (int j = -cfg.neighborRangeY; j <= cfg.neighborRangeY; j++) {
for (int k = -cfg.neighborRangeZ; k <= cfg.neighborRangeZ; k++) {
if ((cfg.creature.getIsland().getMeta(i + node.x, j + node.y, k + node.z) == cfg.neighborMeta) == cfg.notNeighbor) return false;
}
}
}
return true;
}
static void addNeighbors(BFSNode selected) {
byte air = Voxel.get("AIR").getId();
final Vector3 v = new Vector3();
for (int x = -1; x < 2; x++) {
for (int y = -1; y < 3; y++) {
for (int z = -1; z < 2; z++) {
if (x != 0 && z != 0 && y != 0) continue;
v.set(selected.x + x, selected.y + y, selected.z + z);
byte vxl = cfg.creature.getIsland().get(v.x, v.y, v.z);
// don't need parents here
BFSNode node = new BFSNode(v.x, v.y, v.z, vxl, cfg.creature.getIsland().getMeta(v.x, v.y, v.z), null);
if (queue.contains(node) || visited.contains(node, false)) continue;
if (vxl == air && !cfg.creature.canFly()) continue;
if (cfg.maxRange > 0 && Math.sqrt((v.x - start.x) * (v.x - start.x) + (v.z - start.z) * (v.z - start.z)) > cfg.maxRange) continue;
if (!cfg.creature.canFly() && (!cfg.creature.getIsland().hasNeighbors(v.x, v.y - 1, v.z) || !cfg.creature.getIsland().hasNeighbors(v.x, v.y - 2, v.z) || !cfg.creature.getIsland().hasNeighbors(v.x, v.y - 3, v.z))) continue;
if (!cfg.creature.getIsland().isTargetable(v.x, v.y, v.z)) continue;
queue.add(node);
visited.add(node);
}
}
}
}
}