macro pathfinding implementation
This commit is contained in:
parent
57de3d5d81
commit
414707f6d5
@ -2086,6 +2086,7 @@ Skybox reflects time of day
|
||||
Standardize data sourcing in MacroTemporalData
|
||||
Macro pathfinding scaffolding
|
||||
Macro pathfinding work
|
||||
Actual macro pathfinding implementation
|
||||
|
||||
|
||||
|
||||
|
||||
@ -15,13 +15,18 @@ public class SequenceNode implements CollectionNode {
|
||||
/**
|
||||
* The child nodes of the sequence
|
||||
*/
|
||||
List<AITreeNode> children;
|
||||
private List<AITreeNode> children;
|
||||
|
||||
/**
|
||||
* The name associated with this node
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param children All the children of the sequence
|
||||
*/
|
||||
public SequenceNode(List<AITreeNode> children){
|
||||
public SequenceNode(String name, List<AITreeNode> children){
|
||||
if(children == null){
|
||||
throw new IllegalArgumentException("Trying to create sequence node with no children!");
|
||||
}
|
||||
@ -29,13 +34,14 @@ public class SequenceNode implements CollectionNode {
|
||||
throw new IllegalArgumentException("Trying to create sequence node with no children!");
|
||||
}
|
||||
this.children = children;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param children All the children of the sequence
|
||||
*/
|
||||
public SequenceNode(AITreeNode ... children){
|
||||
public SequenceNode(String name, AITreeNode ... children){
|
||||
if(children == null){
|
||||
throw new IllegalArgumentException("Trying to create sequence node with no children!");
|
||||
}
|
||||
@ -43,15 +49,20 @@ public class SequenceNode implements CollectionNode {
|
||||
throw new IllegalArgumentException("Trying to create sequence node with no children!");
|
||||
}
|
||||
this.children = Arrays.asList(children);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AITreeNodeResult evaluate(Entity entity, Blackboard blackboard) {
|
||||
for(AITreeNode child : children){
|
||||
try {
|
||||
AITreeNodeResult result = child.evaluate(entity, blackboard);
|
||||
if(result != AITreeNodeResult.SUCCESS){
|
||||
return result;
|
||||
}
|
||||
} catch(Throwable e){
|
||||
throw new Error(this.name, e);
|
||||
}
|
||||
}
|
||||
return AITreeNodeResult.SUCCESS;
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ import electrosphere.server.ai.blackboard.Blackboard;
|
||||
import electrosphere.server.ai.nodes.AITreeNode;
|
||||
import electrosphere.server.datacell.Realm;
|
||||
import electrosphere.server.macro.MacroData;
|
||||
import electrosphere.server.macro.region.MacroRegion;
|
||||
import electrosphere.server.macro.spatial.MacroAreaObject;
|
||||
import electrosphere.server.macro.spatial.path.MacroPathNode;
|
||||
import electrosphere.server.macro.structure.VirtualStructure;
|
||||
@ -40,8 +41,8 @@ public class MacroPathfindingNode implements AITreeNode {
|
||||
*
|
||||
* @param targetEntityKey
|
||||
*/
|
||||
public static PathfindingNode createPathEntity(String targetEntityKey){
|
||||
PathfindingNode rVal = new PathfindingNode();
|
||||
public static MacroPathfindingNode createPathEntity(String targetEntityKey){
|
||||
MacroPathfindingNode rVal = new MacroPathfindingNode();
|
||||
rVal.targetEntityKey = targetEntityKey;
|
||||
return rVal;
|
||||
}
|
||||
@ -58,8 +59,8 @@ public class MacroPathfindingNode implements AITreeNode {
|
||||
if(targetRaw == null){
|
||||
throw new Error("Target undefined!");
|
||||
}
|
||||
if(targetRaw instanceof MacroPathNode macroNode){
|
||||
targetPos = macroNode.getPosition();
|
||||
if(targetRaw instanceof MacroRegion macroRegion){
|
||||
targetPos = macroRegion.getPos();
|
||||
} else {
|
||||
throw new Error("Unsupported target type " + targetRaw);
|
||||
}
|
||||
|
||||
@ -25,6 +25,7 @@ public class StandardCharacterTree {
|
||||
*/
|
||||
public static AITreeNode create(StandardCharacterTreeData data){
|
||||
return new SequenceNode(
|
||||
"StandardCharacter",
|
||||
new PublishStatusNode("StandardCharacter"),
|
||||
//check that dependencies exist
|
||||
new SelectorNode(
|
||||
|
||||
@ -27,6 +27,7 @@ public class CharacterGoalTree {
|
||||
*/
|
||||
public static AITreeNode create(){
|
||||
return new SequenceNode(
|
||||
"CharacterGoalTree",
|
||||
//check if we have goals
|
||||
MacroCharacterGoalNode.createAny(),
|
||||
//select based on the type of goals the character has
|
||||
@ -34,6 +35,7 @@ public class CharacterGoalTree {
|
||||
|
||||
//character's goal is to leave sim range
|
||||
new SequenceNode(
|
||||
"CharacterGoalTree",
|
||||
MacroCharacterGoalNode.create(CharacterGoalType.LEAVE_SIM_RANGE),
|
||||
new PublishStatusNode("Leaving simulation range"),
|
||||
new FaceTargetNode(BlackboardKeys.POINT_TARGET),
|
||||
@ -42,6 +44,7 @@ public class CharacterGoalTree {
|
||||
|
||||
//character's goal is to build a structure
|
||||
new SequenceNode(
|
||||
"CharacterGoalTree",
|
||||
MacroCharacterGoalNode.create(CharacterGoalType.BUILD_STRUCTURE),
|
||||
new PublishStatusNode("Construct a shelter"),
|
||||
new BeginStructureNode(),
|
||||
@ -50,6 +53,7 @@ public class CharacterGoalTree {
|
||||
|
||||
//character's goal is to acquire an item
|
||||
new SequenceNode(
|
||||
"CharacterGoalTree",
|
||||
MacroCharacterGoalNode.create(CharacterGoalType.ACQUIRE_ITEM),
|
||||
new PublishStatusNode("Acquire building material"),
|
||||
//try to find building materials
|
||||
@ -58,6 +62,7 @@ public class CharacterGoalTree {
|
||||
|
||||
//character's goal is to move to a macro virtual structure
|
||||
new SequenceNode(
|
||||
"CharacterGoalTree",
|
||||
MacroCharacterGoalNode.create(CharacterGoalType.MOVE_TO_MACRO_STRUCT),
|
||||
new PublishStatusNode("Move to macro structure"),
|
||||
MacroMoveToTree.create(BlackboardKeys.MACRO_TARGET)
|
||||
|
||||
@ -21,6 +21,7 @@ public class AttackerAITree {
|
||||
*/
|
||||
public static AITreeNode create(AttackerTreeData attackerTreeData){
|
||||
return new SequenceNode(
|
||||
"AttackerAITree",
|
||||
MeleeAITree.create(attackerTreeData)
|
||||
);
|
||||
}
|
||||
|
||||
@ -12,6 +12,7 @@ import electrosphere.server.ai.nodes.meta.collections.SelectorNode;
|
||||
import electrosphere.server.ai.nodes.meta.collections.SequenceNode;
|
||||
import electrosphere.server.ai.nodes.meta.decorators.RunnerNode;
|
||||
import electrosphere.server.ai.nodes.meta.decorators.SucceederNode;
|
||||
import electrosphere.server.ai.nodes.plan.MacroPathfindingNode;
|
||||
import electrosphere.server.ai.nodes.plan.PathfindingNode;
|
||||
|
||||
/**
|
||||
@ -50,6 +51,7 @@ public class MacroMoveToTree {
|
||||
}
|
||||
return new SelectorNode(
|
||||
new SequenceNode(
|
||||
"MacroMoveToTree",
|
||||
//check if in range of target
|
||||
new TargetRangeCheckNode(dist, targetKey),
|
||||
new DataDeleteNode(BlackboardKeys.PATHFINDING_POINT),
|
||||
@ -60,7 +62,8 @@ public class MacroMoveToTree {
|
||||
|
||||
//not in range of target, keep moving towards it
|
||||
new SequenceNode(
|
||||
PathfindingNode.createPathEntity(targetKey),
|
||||
"MacroMoveToTree",
|
||||
MacroPathfindingNode.createPathEntity(targetKey),
|
||||
new FaceTargetNode(BlackboardKeys.PATHFINDING_POINT),
|
||||
new RunnerNode(new MoveStartNode(MovementRelativeFacing.FORWARD))
|
||||
)
|
||||
|
||||
@ -50,6 +50,7 @@ public class MoveToTree {
|
||||
}
|
||||
return new SelectorNode(
|
||||
new SequenceNode(
|
||||
"MoveToTree",
|
||||
//check if in range of target
|
||||
new TargetRangeCheckNode(dist, targetKey),
|
||||
new DataDeleteNode(BlackboardKeys.PATHFINDING_POINT),
|
||||
@ -60,6 +61,7 @@ public class MoveToTree {
|
||||
|
||||
//not in range of target, keep moving towards it
|
||||
new SequenceNode(
|
||||
"MoveToTree",
|
||||
PathfindingNode.createPathEntity(targetKey),
|
||||
new FaceTargetNode(BlackboardKeys.PATHFINDING_POINT),
|
||||
new RunnerNode(new MoveStartNode(MovementRelativeFacing.FORWARD))
|
||||
|
||||
@ -24,6 +24,7 @@ public class ExploreTree {
|
||||
*/
|
||||
public static AITreeNode create(){
|
||||
return new SequenceNode(
|
||||
"Explore",
|
||||
new PublishStatusNode("Explore"),
|
||||
//resolve point to explore towards
|
||||
new TargetExploreNode(BlackboardKeys.MOVE_TO_TARGET),
|
||||
|
||||
@ -25,6 +25,7 @@ public class EquipToolbarTree {
|
||||
*/
|
||||
public static AITreeNode create(String targetKey){
|
||||
return new SequenceNode(
|
||||
"EquipToolbar",
|
||||
new PublishStatusNode("Equip an item"),
|
||||
//check that we have this type of item
|
||||
new DataTransferNode(targetKey, BlackboardKeys.INVENTORY_CHECK_TYPE),
|
||||
|
||||
@ -44,6 +44,7 @@ public class MeleeAITree {
|
||||
public static AITreeNode create(AttackerTreeData attackerTreeData){
|
||||
|
||||
return new SequenceNode(
|
||||
"MeleeAITree",
|
||||
//preconditions here
|
||||
new HasWeaponNode(),
|
||||
new MeleeTargetingNode(attackerTreeData.getAggroRange()),
|
||||
@ -53,6 +54,7 @@ public class MeleeAITree {
|
||||
|
||||
//in attack range
|
||||
new SequenceNode(
|
||||
"MeleeAITree",
|
||||
//check prior to performing action
|
||||
new MeleeRangeCheckNode(attackerTreeData,MeleeRangeCheckType.ATTACK),
|
||||
|
||||
@ -66,18 +68,21 @@ public class MeleeAITree {
|
||||
new RandomizerNode(
|
||||
//wait
|
||||
new SequenceNode(
|
||||
"MeleeAITree",
|
||||
new PublishStatusNode("Waiting"),
|
||||
new FaceTargetNode(BlackboardKeys.ENTITY_TARGET),
|
||||
new TimerNode(new SucceederNode(null), 600)
|
||||
),
|
||||
//wait
|
||||
new SequenceNode(
|
||||
"MeleeAITree",
|
||||
new PublishStatusNode("Waiting"),
|
||||
new FaceTargetNode(BlackboardKeys.ENTITY_TARGET),
|
||||
new TimerNode(new SucceederNode(null), 300)
|
||||
),
|
||||
//attack
|
||||
new SequenceNode(
|
||||
"MeleeAITree",
|
||||
new PublishStatusNode("Attacking"),
|
||||
new FaceTargetNode(BlackboardKeys.ENTITY_TARGET),
|
||||
new AttackStartNode(),
|
||||
@ -88,6 +93,7 @@ public class MeleeAITree {
|
||||
|
||||
//in aggro range
|
||||
new SequenceNode(
|
||||
"MeleeAITree",
|
||||
//check prior to performing action
|
||||
new MeleeRangeCheckNode(attackerTreeData,MeleeRangeCheckType.AGGRO),
|
||||
|
||||
@ -96,6 +102,7 @@ public class MeleeAITree {
|
||||
|
||||
//wait
|
||||
new SequenceNode(
|
||||
"MeleeAITree",
|
||||
new PublishStatusNode("Waiting"),
|
||||
new FaceTargetNode(BlackboardKeys.ENTITY_TARGET),
|
||||
new TimerNode(new SucceederNode(null), 1200)
|
||||
@ -103,6 +110,7 @@ public class MeleeAITree {
|
||||
|
||||
//strafe to the right
|
||||
new SequenceNode(
|
||||
"MeleeAITree",
|
||||
new PublishStatusNode("Strafing right"),
|
||||
new WalkStartNode(),
|
||||
new InverterNode(new OnFailureNode(
|
||||
@ -117,6 +125,7 @@ public class MeleeAITree {
|
||||
|
||||
//strafe to the left
|
||||
new SequenceNode(
|
||||
"MeleeAITree",
|
||||
new PublishStatusNode("Strafing left"),
|
||||
new WalkStartNode(),
|
||||
new InverterNode(new OnFailureNode(
|
||||
@ -132,6 +141,7 @@ public class MeleeAITree {
|
||||
//approach target
|
||||
//move towards target and attack
|
||||
new SequenceNode(
|
||||
"MeleeAITree",
|
||||
new PublishStatusNode("Move into attack range"),
|
||||
new FaceTargetNode(BlackboardKeys.ENTITY_TARGET),
|
||||
new SucceederNode(new MoveStartNode(MovementRelativeFacing.FORWARD)),
|
||||
|
||||
@ -35,11 +35,13 @@ public class AcquireItemTree {
|
||||
*/
|
||||
public static AITreeNode create(String blackboardKey){
|
||||
return new SequenceNode(
|
||||
"AcquireItemTree",
|
||||
new PublishStatusNode("Acquire an item"),
|
||||
//solve how we're going to get this top level item
|
||||
new SolveSourcingTreeNode(blackboardKey),
|
||||
new SelectorNode(
|
||||
new SequenceNode(
|
||||
"AcquireItemTree",
|
||||
new PublishStatusNode("Pick up an item"),
|
||||
//check if we should be sourcing this item by picking it up
|
||||
new SourcingTypeNode(SourcingType.PICKUP, BlackboardKeys.ITEM_TARGET_CATEGORY),
|
||||
@ -50,6 +52,7 @@ public class AcquireItemTree {
|
||||
new RunnerNode(null)
|
||||
),
|
||||
new SequenceNode(
|
||||
"AcquireItemTree",
|
||||
new PublishStatusNode("Craft an item"),
|
||||
//check if we should be sourcing this from a recipe
|
||||
new SourcingTypeNode(SourcingType.RECIPE, blackboardKey),
|
||||
@ -57,6 +60,7 @@ public class AcquireItemTree {
|
||||
new RunnerNode(null)
|
||||
),
|
||||
new SequenceNode(
|
||||
"AcquireItemTree",
|
||||
new PublishStatusNode("Harvest an item"),
|
||||
//check if we should be sourcing this from harvesting foliage
|
||||
new SourcingTypeNode(SourcingType.HARVEST, blackboardKey),
|
||||
@ -66,6 +70,7 @@ public class AcquireItemTree {
|
||||
new RunnerNode(null)
|
||||
),
|
||||
new SequenceNode(
|
||||
"AcquireItemTree",
|
||||
new PublishStatusNode("Fell a tree"),
|
||||
//check if we should be sourcing this from felling a tree
|
||||
new SourcingTypeNode(SourcingType.TREE, blackboardKey),
|
||||
@ -74,6 +79,7 @@ public class AcquireItemTree {
|
||||
new RunnerNode(null)
|
||||
),
|
||||
new SequenceNode(
|
||||
"AcquireItemTree",
|
||||
new PublishStatusNode("Explore new chunks for resources"),
|
||||
//Failed to find sources of material in existing chunks, must move for new chunks
|
||||
ExploreTree.create()
|
||||
|
||||
@ -42,6 +42,7 @@ public class FellTree {
|
||||
public static AITreeNode create(String targetKey){
|
||||
|
||||
return new SequenceNode(
|
||||
"FellTree",
|
||||
//preconditions here
|
||||
new DataStorageNode(BlackboardKeys.INVENTORY_CHECK_TYPE, ItemIdStrings.ITEM_STONE_AXE),
|
||||
EquipToolbarTree.create(BlackboardKeys.INVENTORY_CHECK_TYPE),
|
||||
@ -51,6 +52,7 @@ public class FellTree {
|
||||
|
||||
//in attack range
|
||||
new SequenceNode(
|
||||
"FellTree",
|
||||
//check if in range of target
|
||||
new TargetRangeCheckNode(FellTree.FELL_RANGE, targetKey),
|
||||
//stop walking now that we're in range
|
||||
@ -60,6 +62,7 @@ public class FellTree {
|
||||
|
||||
//attack
|
||||
new SequenceNode(
|
||||
"FellTree",
|
||||
new PublishStatusNode("Attacking"),
|
||||
new FaceTargetNode(BlackboardKeys.ENTITY_TARGET),
|
||||
new AttackStartNode(),
|
||||
|
||||
@ -23,8 +23,10 @@ public class MaslowTree {
|
||||
*/
|
||||
public static AITreeNode create(){
|
||||
return new SequenceNode(
|
||||
"MaslowTree",
|
||||
//check that dependencies exist
|
||||
new SequenceNode(
|
||||
"MaslowTree",
|
||||
new PublishStatusNode("Checking dependencies for maslow tree.."),
|
||||
new MacroDataExists(),
|
||||
new IsCharacterNode()
|
||||
|
||||
@ -21,6 +21,7 @@ public class CombatTree {
|
||||
*/
|
||||
public static AITreeNode create(){
|
||||
return new SequenceNode(
|
||||
"CombatTree",
|
||||
new PublishStatusNode("Engaged in mortal combat"),
|
||||
new SucceederNode(null)
|
||||
);
|
||||
|
||||
@ -21,6 +21,7 @@ public class FleeTree {
|
||||
*/
|
||||
public static AITreeNode create(){
|
||||
return new SequenceNode(
|
||||
"FleeTree",
|
||||
new PublishStatusNode("Flee!"),
|
||||
new SucceederNode(null)
|
||||
);
|
||||
|
||||
@ -21,6 +21,7 @@ public class MaslowSafetyTree {
|
||||
*/
|
||||
public static AITreeNode create(){
|
||||
return new SequenceNode(
|
||||
"MaslowSafetyTree",
|
||||
new PublishStatusNode("Evaluate safety"),
|
||||
new SelectorNode(
|
||||
FleeTree.create(),
|
||||
|
||||
@ -23,6 +23,7 @@ public class ConstructShelterTree {
|
||||
*/
|
||||
public static AITreeNode create(){
|
||||
return new SequenceNode(
|
||||
"ConstructShelter",
|
||||
new PublishStatusNode("Construct a shelter"),
|
||||
new BeginStructureNode(),
|
||||
BuildStructureTree.create(),
|
||||
|
||||
@ -23,22 +23,27 @@ public class ShelterTree {
|
||||
*/
|
||||
public static AITreeNode create(){
|
||||
return new SequenceNode(
|
||||
"ShelterTree",
|
||||
new PublishStatusNode("Evaluate shelter"),
|
||||
//make sure that this entity actually cares about shelter
|
||||
new SequenceNode(
|
||||
"ShelterTree",
|
||||
//if this is a character
|
||||
new IsCharacterNode()
|
||||
),
|
||||
//now that we know the entity cares about shelter, check if they have shelter
|
||||
new SequenceNode(
|
||||
"ShelterTree",
|
||||
//if has shelter..
|
||||
new SelectorNode(
|
||||
new SequenceNode(
|
||||
"ShelterTree",
|
||||
new HasShelter()
|
||||
//already has shelter
|
||||
//TODO: check environment (ie time of day) to see if we should return to shelter
|
||||
),
|
||||
new SequenceNode(
|
||||
"ShelterTree",
|
||||
//does not have shelter
|
||||
ConstructShelterTree.create()
|
||||
)
|
||||
|
||||
@ -19,6 +19,7 @@ public class ForageTree {
|
||||
*/
|
||||
public static AITreeNode create(){
|
||||
return new SequenceNode(
|
||||
"ForageTree"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -32,18 +32,22 @@ public class BuildStructureTree {
|
||||
*/
|
||||
public static AITreeNode create(){
|
||||
return new SequenceNode(
|
||||
"BuildStructureTree",
|
||||
new PublishStatusNode("Construct a structure"),
|
||||
//figure out current task
|
||||
new SelectorNode(
|
||||
//make sure we know what material we need to build with currently
|
||||
new SequenceNode(
|
||||
"BuildStructureTree",
|
||||
new SolveBuildMaterialNode(),
|
||||
new PublishStatusNode("Trying to place block in structure"),
|
||||
//if has building materials
|
||||
new SequenceNode(
|
||||
"BuildStructureTree",
|
||||
new InventoryContainsNode(BlackboardKeys.BUILDING_MATERIAL_CURRENT),
|
||||
//if we're within range to place the material
|
||||
new SequenceNode(
|
||||
"BuildStructureTree",
|
||||
//not in range, move to within range
|
||||
MoveToTree.create(CollisionEngine.DEFAULT_INTERACT_DISTANCE, BlackboardKeys.STRUCTURE_TARGET),
|
||||
//equip the type of block to place
|
||||
@ -55,6 +59,7 @@ public class BuildStructureTree {
|
||||
),
|
||||
//does not have building materials
|
||||
new SequenceNode(
|
||||
"BuildStructureTree",
|
||||
new InverterNode(new InventoryContainsNode(BlackboardKeys.BUILDING_MATERIAL_CURRENT)),
|
||||
new RunnerNode(new PublishStatusNode("Waiting on macro character to set goal to find materials to build with"))
|
||||
),
|
||||
|
||||
@ -24,6 +24,7 @@ public class BlockerAITree {
|
||||
*/
|
||||
public static AITreeNode create(BlockerTreeData data){
|
||||
return new SequenceNode(
|
||||
"BlockerAITree",
|
||||
new BlockStartNode(),
|
||||
new MeleeTargetingNode(5.0f),
|
||||
new FaceTargetNode(BlackboardKeys.ENTITY_TARGET)
|
||||
|
||||
@ -200,6 +200,14 @@ public class MacroPathNode {
|
||||
return this.neighborNodes.stream().map((Long neighborId) -> cache.getNodeById(neighborId)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of neighbor ids
|
||||
* @return The list of neighbor ids
|
||||
*/
|
||||
public List<Long> getNeighborIds(){
|
||||
return this.neighborNodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a neighbor to this node
|
||||
* @param neighbor The neighbor
|
||||
|
||||
@ -1,6 +1,10 @@
|
||||
package electrosphere.server.service;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.PriorityQueue;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
import org.joml.Vector3d;
|
||||
@ -11,6 +15,7 @@ import electrosphere.engine.signal.SignalServiceImpl;
|
||||
import electrosphere.engine.threads.ThreadCounts;
|
||||
import electrosphere.server.datacell.Realm;
|
||||
import electrosphere.server.macro.MacroData;
|
||||
import electrosphere.server.macro.spatial.path.MacroPathCache;
|
||||
import electrosphere.server.macro.spatial.path.MacroPathNode;
|
||||
import electrosphere.server.pathfinding.recast.PathingProgressiveData;
|
||||
|
||||
@ -75,7 +80,120 @@ public class MacroPathingService extends SignalServiceImpl {
|
||||
* @return The path
|
||||
*/
|
||||
private List<Vector3d> findPath(MacroData macroData, MacroPathNode start, MacroPathNode end){
|
||||
throw new Error("Not implemented yet!");
|
||||
List<Vector3d> rVal = null;
|
||||
//tracks whether we've found the goal or not
|
||||
boolean foundGoal = false;
|
||||
int countConsidered = 0;
|
||||
MacroPathCache pathCache = macroData.getPathCache();
|
||||
|
||||
//create sets
|
||||
PriorityQueue<PathfinderNode> openSet = new PriorityQueue<PathfinderNode>();
|
||||
Map<Long,PathfinderNode> openSetLookup = new HashMap<Long,PathfinderNode>();
|
||||
Map<Long,PathfinderNode> closetSet = new HashMap<Long,PathfinderNode>();
|
||||
|
||||
//add start node
|
||||
PathfinderNode node = new PathfinderNode(start, 0, start.getId());
|
||||
openSet.add(node);
|
||||
openSetLookup.put(node.graphNode.getId(),node);
|
||||
|
||||
//search
|
||||
while(openSet.size() > 0 && !foundGoal){
|
||||
|
||||
//pull from open set
|
||||
PathfinderNode currentNode = openSet.poll();
|
||||
long currentCost = currentNode.cost;
|
||||
openSetLookup.remove(currentNode.graphNode.getId());
|
||||
closetSet.put(currentNode.graphNode.getId(), currentNode);
|
||||
countConsidered++;
|
||||
|
||||
|
||||
//iterate along neighbors
|
||||
for(Long neighborId : currentNode.graphNode.getNeighborIds()){
|
||||
//goal check
|
||||
if(end.getId() == neighborId){
|
||||
foundGoal = true;
|
||||
break;
|
||||
}
|
||||
//add-to-set check
|
||||
if(!closetSet.containsKey(neighborId) && !openSetLookup.containsKey(neighborId)){
|
||||
MacroPathNode neighborGraphNode = pathCache.getNodeById(neighborId);
|
||||
long newCost = currentCost + neighborGraphNode.getCost();
|
||||
PathfinderNode newNode = new PathfinderNode(
|
||||
neighborGraphNode,
|
||||
newCost, currentNode.graphNode.getId()
|
||||
);
|
||||
openSet.add(newNode);
|
||||
openSetLookup.put(neighborGraphNode.getId(), newNode);
|
||||
}
|
||||
}
|
||||
|
||||
//if found goal
|
||||
if(foundGoal){
|
||||
//reverse up the chain from here
|
||||
rVal = new LinkedList<Vector3d>();
|
||||
rVal.add(end.getPosition());
|
||||
while(currentNode.prevNode != currentNode.graphNode.getId()){
|
||||
rVal.add(0,currentNode.getPosition());
|
||||
currentNode = closetSet.get(currentNode.prevNode);
|
||||
}
|
||||
rVal.add(0,start.getPosition());
|
||||
break;
|
||||
}
|
||||
|
||||
//error check
|
||||
if(openSet.size() < 1){
|
||||
throw new Error("Open set ran out of nodes! " + countConsidered);
|
||||
}
|
||||
}
|
||||
|
||||
if(!foundGoal){
|
||||
throw new Error("Failed to find goal " + countConsidered);
|
||||
}
|
||||
return rVal;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A node to use during searching
|
||||
*/
|
||||
protected static class PathfinderNode implements Comparable<PathfinderNode> {
|
||||
|
||||
/**
|
||||
* The corresponding graph node
|
||||
*/
|
||||
MacroPathNode graphNode;
|
||||
|
||||
/**
|
||||
* Cost to get to this node
|
||||
*/
|
||||
long cost = 0;
|
||||
|
||||
/**
|
||||
* The previous node
|
||||
*/
|
||||
long prevNode = 0;
|
||||
|
||||
public PathfinderNode(
|
||||
MacroPathNode graphNode,
|
||||
long cost, long prevNode
|
||||
){
|
||||
this.graphNode = graphNode;
|
||||
this.cost = cost;
|
||||
this.prevNode = prevNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(PathfinderNode o) {
|
||||
return (int)(this.cost - o.cost);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the position of the node
|
||||
* @return The position of the node
|
||||
*/
|
||||
public Vector3d getPosition(){
|
||||
return graphNode.getPosition();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -42,13 +42,13 @@ public class CharaSimulation {
|
||||
return;
|
||||
}
|
||||
//send a character on a walk
|
||||
// if(chara.getId() == 3){
|
||||
// Town hometown = CharacterUtils.getHometown(realm.getMacroData(), chara);
|
||||
// if(hometown != null){
|
||||
// MacroRegion target = hometown.getFarmPlots(realm.getMacroData()).get(0);
|
||||
// CharacterGoal.setCharacterGoal(chara, new CharacterGoal(CharacterGoalType.MOVE_TO_MACRO_STRUCT, target));
|
||||
// }
|
||||
// }
|
||||
if(chara.getId() == 3){
|
||||
Town hometown = CharacterUtils.getHometown(realm.getMacroData(), chara);
|
||||
if(hometown != null){
|
||||
MacroRegion target = hometown.getFarmPlots(realm.getMacroData()).get(0);
|
||||
CharacterGoal.setCharacterGoal(chara, new CharacterGoal(CharacterGoalType.MOVE_TO_MACRO_STRUCT, target));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Loading…
Reference in New Issue
Block a user