diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index 8ba184f1..7c04a4f6 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -2086,6 +2086,7 @@ Skybox reflects time of day Standardize data sourcing in MacroTemporalData Macro pathfinding scaffolding Macro pathfinding work +Actual macro pathfinding implementation diff --git a/src/main/java/electrosphere/server/ai/nodes/meta/collections/SequenceNode.java b/src/main/java/electrosphere/server/ai/nodes/meta/collections/SequenceNode.java index fb9c2eb3..402df6cb 100644 --- a/src/main/java/electrosphere/server/ai/nodes/meta/collections/SequenceNode.java +++ b/src/main/java/electrosphere/server/ai/nodes/meta/collections/SequenceNode.java @@ -15,13 +15,18 @@ public class SequenceNode implements CollectionNode { /** * The child nodes of the sequence */ - List children; + private List children; + + /** + * The name associated with this node + */ + private String name; /** * Constructor * @param children All the children of the sequence */ - public SequenceNode(List children){ + public SequenceNode(String name, List 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,14 +49,19 @@ 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){ - AITreeNodeResult result = child.evaluate(entity, blackboard); - if(result != AITreeNodeResult.SUCCESS){ - return result; + 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; diff --git a/src/main/java/electrosphere/server/ai/nodes/plan/MacroPathfindingNode.java b/src/main/java/electrosphere/server/ai/nodes/plan/MacroPathfindingNode.java index ee89adc4..978fa250 100644 --- a/src/main/java/electrosphere/server/ai/nodes/plan/MacroPathfindingNode.java +++ b/src/main/java/electrosphere/server/ai/nodes/plan/MacroPathfindingNode.java @@ -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); } diff --git a/src/main/java/electrosphere/server/ai/trees/character/StandardCharacterTree.java b/src/main/java/electrosphere/server/ai/trees/character/StandardCharacterTree.java index 656a7679..ceb17127 100644 --- a/src/main/java/electrosphere/server/ai/trees/character/StandardCharacterTree.java +++ b/src/main/java/electrosphere/server/ai/trees/character/StandardCharacterTree.java @@ -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( diff --git a/src/main/java/electrosphere/server/ai/trees/character/goals/CharacterGoalTree.java b/src/main/java/electrosphere/server/ai/trees/character/goals/CharacterGoalTree.java index 3a95ff37..426d6773 100644 --- a/src/main/java/electrosphere/server/ai/trees/character/goals/CharacterGoalTree.java +++ b/src/main/java/electrosphere/server/ai/trees/character/goals/CharacterGoalTree.java @@ -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) diff --git a/src/main/java/electrosphere/server/ai/trees/creature/AttackerAITree.java b/src/main/java/electrosphere/server/ai/trees/creature/AttackerAITree.java index 831ce060..be576729 100644 --- a/src/main/java/electrosphere/server/ai/trees/creature/AttackerAITree.java +++ b/src/main/java/electrosphere/server/ai/trees/creature/AttackerAITree.java @@ -21,6 +21,7 @@ public class AttackerAITree { */ public static AITreeNode create(AttackerTreeData attackerTreeData){ return new SequenceNode( + "AttackerAITree", MeleeAITree.create(attackerTreeData) ); } diff --git a/src/main/java/electrosphere/server/ai/trees/creature/MacroMoveToTree.java b/src/main/java/electrosphere/server/ai/trees/creature/MacroMoveToTree.java index b2d84985..437aeeec 100644 --- a/src/main/java/electrosphere/server/ai/trees/creature/MacroMoveToTree.java +++ b/src/main/java/electrosphere/server/ai/trees/creature/MacroMoveToTree.java @@ -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,8 +51,9 @@ public class MacroMoveToTree { } return new SelectorNode( new SequenceNode( + "MacroMoveToTree", //check if in range of target - new TargetRangeCheckNode(dist, targetKey), + new TargetRangeCheckNode(dist, targetKey), new DataDeleteNode(BlackboardKeys.PATHFINDING_POINT), new DataDeleteNode(BlackboardKeys.PATHFINDING_DATA), //if in range, stop moving fowards and return SUCCESS @@ -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)) ) diff --git a/src/main/java/electrosphere/server/ai/trees/creature/MoveToTree.java b/src/main/java/electrosphere/server/ai/trees/creature/MoveToTree.java index 0e1450b1..2b4c21be 100644 --- a/src/main/java/electrosphere/server/ai/trees/creature/MoveToTree.java +++ b/src/main/java/electrosphere/server/ai/trees/creature/MoveToTree.java @@ -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)) diff --git a/src/main/java/electrosphere/server/ai/trees/creature/explore/ExploreTree.java b/src/main/java/electrosphere/server/ai/trees/creature/explore/ExploreTree.java index 37263773..c5f7aec4 100644 --- a/src/main/java/electrosphere/server/ai/trees/creature/explore/ExploreTree.java +++ b/src/main/java/electrosphere/server/ai/trees/creature/explore/ExploreTree.java @@ -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), diff --git a/src/main/java/electrosphere/server/ai/trees/creature/inventory/EquipToolbarTree.java b/src/main/java/electrosphere/server/ai/trees/creature/inventory/EquipToolbarTree.java index 637c4717..c78edbd5 100644 --- a/src/main/java/electrosphere/server/ai/trees/creature/inventory/EquipToolbarTree.java +++ b/src/main/java/electrosphere/server/ai/trees/creature/inventory/EquipToolbarTree.java @@ -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), diff --git a/src/main/java/electrosphere/server/ai/trees/creature/melee/MeleeAITree.java b/src/main/java/electrosphere/server/ai/trees/creature/melee/MeleeAITree.java index 256dab70..cd888a8b 100644 --- a/src/main/java/electrosphere/server/ai/trees/creature/melee/MeleeAITree.java +++ b/src/main/java/electrosphere/server/ai/trees/creature/melee/MeleeAITree.java @@ -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)), diff --git a/src/main/java/electrosphere/server/ai/trees/creature/resource/AcquireItemTree.java b/src/main/java/electrosphere/server/ai/trees/creature/resource/AcquireItemTree.java index 5e3a453a..592f7e60 100644 --- a/src/main/java/electrosphere/server/ai/trees/creature/resource/AcquireItemTree.java +++ b/src/main/java/electrosphere/server/ai/trees/creature/resource/AcquireItemTree.java @@ -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() diff --git a/src/main/java/electrosphere/server/ai/trees/creature/resource/FellTree.java b/src/main/java/electrosphere/server/ai/trees/creature/resource/FellTree.java index 5d9fcb20..19447847 100644 --- a/src/main/java/electrosphere/server/ai/trees/creature/resource/FellTree.java +++ b/src/main/java/electrosphere/server/ai/trees/creature/resource/FellTree.java @@ -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(), diff --git a/src/main/java/electrosphere/server/ai/trees/hierarchy/MaslowTree.java b/src/main/java/electrosphere/server/ai/trees/hierarchy/MaslowTree.java index b0f3a7ee..1ded7660 100644 --- a/src/main/java/electrosphere/server/ai/trees/hierarchy/MaslowTree.java +++ b/src/main/java/electrosphere/server/ai/trees/hierarchy/MaslowTree.java @@ -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() diff --git a/src/main/java/electrosphere/server/ai/trees/hierarchy/safety/CombatTree.java b/src/main/java/electrosphere/server/ai/trees/hierarchy/safety/CombatTree.java index 9ee1399c..beaa5d54 100644 --- a/src/main/java/electrosphere/server/ai/trees/hierarchy/safety/CombatTree.java +++ b/src/main/java/electrosphere/server/ai/trees/hierarchy/safety/CombatTree.java @@ -21,6 +21,7 @@ public class CombatTree { */ public static AITreeNode create(){ return new SequenceNode( + "CombatTree", new PublishStatusNode("Engaged in mortal combat"), new SucceederNode(null) ); diff --git a/src/main/java/electrosphere/server/ai/trees/hierarchy/safety/FleeTree.java b/src/main/java/electrosphere/server/ai/trees/hierarchy/safety/FleeTree.java index 5df0bc95..3abd5ba0 100644 --- a/src/main/java/electrosphere/server/ai/trees/hierarchy/safety/FleeTree.java +++ b/src/main/java/electrosphere/server/ai/trees/hierarchy/safety/FleeTree.java @@ -21,6 +21,7 @@ public class FleeTree { */ public static AITreeNode create(){ return new SequenceNode( + "FleeTree", new PublishStatusNode("Flee!"), new SucceederNode(null) ); diff --git a/src/main/java/electrosphere/server/ai/trees/hierarchy/safety/MaslowSafetyTree.java b/src/main/java/electrosphere/server/ai/trees/hierarchy/safety/MaslowSafetyTree.java index bc087636..a3b748e1 100644 --- a/src/main/java/electrosphere/server/ai/trees/hierarchy/safety/MaslowSafetyTree.java +++ b/src/main/java/electrosphere/server/ai/trees/hierarchy/safety/MaslowSafetyTree.java @@ -21,6 +21,7 @@ public class MaslowSafetyTree { */ public static AITreeNode create(){ return new SequenceNode( + "MaslowSafetyTree", new PublishStatusNode("Evaluate safety"), new SelectorNode( FleeTree.create(), diff --git a/src/main/java/electrosphere/server/ai/trees/hierarchy/safety/shelter/ConstructShelterTree.java b/src/main/java/electrosphere/server/ai/trees/hierarchy/safety/shelter/ConstructShelterTree.java index d98d6a30..ab9d1b67 100644 --- a/src/main/java/electrosphere/server/ai/trees/hierarchy/safety/shelter/ConstructShelterTree.java +++ b/src/main/java/electrosphere/server/ai/trees/hierarchy/safety/shelter/ConstructShelterTree.java @@ -23,6 +23,7 @@ public class ConstructShelterTree { */ public static AITreeNode create(){ return new SequenceNode( + "ConstructShelter", new PublishStatusNode("Construct a shelter"), new BeginStructureNode(), BuildStructureTree.create(), diff --git a/src/main/java/electrosphere/server/ai/trees/hierarchy/safety/shelter/ShelterTree.java b/src/main/java/electrosphere/server/ai/trees/hierarchy/safety/shelter/ShelterTree.java index e6d4fb49..d302580e 100644 --- a/src/main/java/electrosphere/server/ai/trees/hierarchy/safety/shelter/ShelterTree.java +++ b/src/main/java/electrosphere/server/ai/trees/hierarchy/safety/shelter/ShelterTree.java @@ -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() ) diff --git a/src/main/java/electrosphere/server/ai/trees/jobs/ForageTree.java b/src/main/java/electrosphere/server/ai/trees/jobs/ForageTree.java index 6ce17b59..684fcd1c 100644 --- a/src/main/java/electrosphere/server/ai/trees/jobs/ForageTree.java +++ b/src/main/java/electrosphere/server/ai/trees/jobs/ForageTree.java @@ -19,6 +19,7 @@ public class ForageTree { */ public static AITreeNode create(){ return new SequenceNode( + "ForageTree" ); } diff --git a/src/main/java/electrosphere/server/ai/trees/struct/BuildStructureTree.java b/src/main/java/electrosphere/server/ai/trees/struct/BuildStructureTree.java index 3a56a731..501fa5bc 100644 --- a/src/main/java/electrosphere/server/ai/trees/struct/BuildStructureTree.java +++ b/src/main/java/electrosphere/server/ai/trees/struct/BuildStructureTree.java @@ -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")) ), diff --git a/src/main/java/electrosphere/server/ai/trees/test/BlockerAITree.java b/src/main/java/electrosphere/server/ai/trees/test/BlockerAITree.java index f9c0500f..6af4ceba 100644 --- a/src/main/java/electrosphere/server/ai/trees/test/BlockerAITree.java +++ b/src/main/java/electrosphere/server/ai/trees/test/BlockerAITree.java @@ -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) diff --git a/src/main/java/electrosphere/server/macro/spatial/path/MacroPathNode.java b/src/main/java/electrosphere/server/macro/spatial/path/MacroPathNode.java index 4c315098..8f46f1b5 100644 --- a/src/main/java/electrosphere/server/macro/spatial/path/MacroPathNode.java +++ b/src/main/java/electrosphere/server/macro/spatial/path/MacroPathNode.java @@ -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 getNeighborIds(){ + return this.neighborNodes; + } + /** * Adds a neighbor to this node * @param neighbor The neighbor diff --git a/src/main/java/electrosphere/server/service/MacroPathingService.java b/src/main/java/electrosphere/server/service/MacroPathingService.java index 0c8bf113..0f3e718d 100644 --- a/src/main/java/electrosphere/server/service/MacroPathingService.java +++ b/src/main/java/electrosphere/server/service/MacroPathingService.java @@ -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 findPath(MacroData macroData, MacroPathNode start, MacroPathNode end){ - throw new Error("Not implemented yet!"); + List rVal = null; + //tracks whether we've found the goal or not + boolean foundGoal = false; + int countConsidered = 0; + MacroPathCache pathCache = macroData.getPathCache(); + + //create sets + PriorityQueue openSet = new PriorityQueue(); + Map openSetLookup = new HashMap(); + Map closetSet = new HashMap(); + + //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(); + 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 { + + /** + * 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(); + } } } diff --git a/src/main/java/electrosphere/server/simulation/chara/CharaSimulation.java b/src/main/java/electrosphere/server/simulation/chara/CharaSimulation.java index 72d51367..a35720bf 100644 --- a/src/main/java/electrosphere/server/simulation/chara/CharaSimulation.java +++ b/src/main/java/electrosphere/server/simulation/chara/CharaSimulation.java @@ -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)); + } + } } /**