macro pathfinding implementation

This commit is contained in:
austin 2025-05-30 22:09:10 -04:00
parent 57de3d5d81
commit 414707f6d5
25 changed files with 210 additions and 20 deletions

View File

@ -2086,6 +2086,7 @@ Skybox reflects time of day
Standardize data sourcing in MacroTemporalData Standardize data sourcing in MacroTemporalData
Macro pathfinding scaffolding Macro pathfinding scaffolding
Macro pathfinding work Macro pathfinding work
Actual macro pathfinding implementation

View File

@ -15,13 +15,18 @@ public class SequenceNode implements CollectionNode {
/** /**
* The child nodes of the sequence * The child nodes of the sequence
*/ */
List<AITreeNode> children; private List<AITreeNode> children;
/**
* The name associated with this node
*/
private String name;
/** /**
* Constructor * Constructor
* @param children All the children of the sequence * @param children All the children of the sequence
*/ */
public SequenceNode(List<AITreeNode> children){ public SequenceNode(String name, List<AITreeNode> children){
if(children == null){ if(children == null){
throw new IllegalArgumentException("Trying to create sequence node with no children!"); 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!"); throw new IllegalArgumentException("Trying to create sequence node with no children!");
} }
this.children = children; this.children = children;
this.name = name;
} }
/** /**
* Constructor * Constructor
* @param children All the children of the sequence * @param children All the children of the sequence
*/ */
public SequenceNode(AITreeNode ... children){ public SequenceNode(String name, AITreeNode ... children){
if(children == null){ if(children == null){
throw new IllegalArgumentException("Trying to create sequence node with no children!"); 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!"); throw new IllegalArgumentException("Trying to create sequence node with no children!");
} }
this.children = Arrays.asList(children); this.children = Arrays.asList(children);
this.name = name;
} }
@Override @Override
public AITreeNodeResult evaluate(Entity entity, Blackboard blackboard) { public AITreeNodeResult evaluate(Entity entity, Blackboard blackboard) {
for(AITreeNode child : children){ for(AITreeNode child : children){
AITreeNodeResult result = child.evaluate(entity, blackboard); try {
if(result != AITreeNodeResult.SUCCESS){ AITreeNodeResult result = child.evaluate(entity, blackboard);
return result; if(result != AITreeNodeResult.SUCCESS){
return result;
}
} catch(Throwable e){
throw new Error(this.name, e);
} }
} }
return AITreeNodeResult.SUCCESS; return AITreeNodeResult.SUCCESS;

View File

@ -10,6 +10,7 @@ import electrosphere.server.ai.blackboard.Blackboard;
import electrosphere.server.ai.nodes.AITreeNode; import electrosphere.server.ai.nodes.AITreeNode;
import electrosphere.server.datacell.Realm; import electrosphere.server.datacell.Realm;
import electrosphere.server.macro.MacroData; import electrosphere.server.macro.MacroData;
import electrosphere.server.macro.region.MacroRegion;
import electrosphere.server.macro.spatial.MacroAreaObject; import electrosphere.server.macro.spatial.MacroAreaObject;
import electrosphere.server.macro.spatial.path.MacroPathNode; import electrosphere.server.macro.spatial.path.MacroPathNode;
import electrosphere.server.macro.structure.VirtualStructure; import electrosphere.server.macro.structure.VirtualStructure;
@ -40,8 +41,8 @@ public class MacroPathfindingNode implements AITreeNode {
* *
* @param targetEntityKey * @param targetEntityKey
*/ */
public static PathfindingNode createPathEntity(String targetEntityKey){ public static MacroPathfindingNode createPathEntity(String targetEntityKey){
PathfindingNode rVal = new PathfindingNode(); MacroPathfindingNode rVal = new MacroPathfindingNode();
rVal.targetEntityKey = targetEntityKey; rVal.targetEntityKey = targetEntityKey;
return rVal; return rVal;
} }
@ -58,8 +59,8 @@ public class MacroPathfindingNode implements AITreeNode {
if(targetRaw == null){ if(targetRaw == null){
throw new Error("Target undefined!"); throw new Error("Target undefined!");
} }
if(targetRaw instanceof MacroPathNode macroNode){ if(targetRaw instanceof MacroRegion macroRegion){
targetPos = macroNode.getPosition(); targetPos = macroRegion.getPos();
} else { } else {
throw new Error("Unsupported target type " + targetRaw); throw new Error("Unsupported target type " + targetRaw);
} }

View File

@ -25,6 +25,7 @@ public class StandardCharacterTree {
*/ */
public static AITreeNode create(StandardCharacterTreeData data){ public static AITreeNode create(StandardCharacterTreeData data){
return new SequenceNode( return new SequenceNode(
"StandardCharacter",
new PublishStatusNode("StandardCharacter"), new PublishStatusNode("StandardCharacter"),
//check that dependencies exist //check that dependencies exist
new SelectorNode( new SelectorNode(

View File

@ -27,6 +27,7 @@ public class CharacterGoalTree {
*/ */
public static AITreeNode create(){ public static AITreeNode create(){
return new SequenceNode( return new SequenceNode(
"CharacterGoalTree",
//check if we have goals //check if we have goals
MacroCharacterGoalNode.createAny(), MacroCharacterGoalNode.createAny(),
//select based on the type of goals the character has //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 //character's goal is to leave sim range
new SequenceNode( new SequenceNode(
"CharacterGoalTree",
MacroCharacterGoalNode.create(CharacterGoalType.LEAVE_SIM_RANGE), MacroCharacterGoalNode.create(CharacterGoalType.LEAVE_SIM_RANGE),
new PublishStatusNode("Leaving simulation range"), new PublishStatusNode("Leaving simulation range"),
new FaceTargetNode(BlackboardKeys.POINT_TARGET), new FaceTargetNode(BlackboardKeys.POINT_TARGET),
@ -42,6 +44,7 @@ public class CharacterGoalTree {
//character's goal is to build a structure //character's goal is to build a structure
new SequenceNode( new SequenceNode(
"CharacterGoalTree",
MacroCharacterGoalNode.create(CharacterGoalType.BUILD_STRUCTURE), MacroCharacterGoalNode.create(CharacterGoalType.BUILD_STRUCTURE),
new PublishStatusNode("Construct a shelter"), new PublishStatusNode("Construct a shelter"),
new BeginStructureNode(), new BeginStructureNode(),
@ -50,6 +53,7 @@ public class CharacterGoalTree {
//character's goal is to acquire an item //character's goal is to acquire an item
new SequenceNode( new SequenceNode(
"CharacterGoalTree",
MacroCharacterGoalNode.create(CharacterGoalType.ACQUIRE_ITEM), MacroCharacterGoalNode.create(CharacterGoalType.ACQUIRE_ITEM),
new PublishStatusNode("Acquire building material"), new PublishStatusNode("Acquire building material"),
//try to find building materials //try to find building materials
@ -58,6 +62,7 @@ public class CharacterGoalTree {
//character's goal is to move to a macro virtual structure //character's goal is to move to a macro virtual structure
new SequenceNode( new SequenceNode(
"CharacterGoalTree",
MacroCharacterGoalNode.create(CharacterGoalType.MOVE_TO_MACRO_STRUCT), MacroCharacterGoalNode.create(CharacterGoalType.MOVE_TO_MACRO_STRUCT),
new PublishStatusNode("Move to macro structure"), new PublishStatusNode("Move to macro structure"),
MacroMoveToTree.create(BlackboardKeys.MACRO_TARGET) MacroMoveToTree.create(BlackboardKeys.MACRO_TARGET)

View File

@ -21,6 +21,7 @@ public class AttackerAITree {
*/ */
public static AITreeNode create(AttackerTreeData attackerTreeData){ public static AITreeNode create(AttackerTreeData attackerTreeData){
return new SequenceNode( return new SequenceNode(
"AttackerAITree",
MeleeAITree.create(attackerTreeData) MeleeAITree.create(attackerTreeData)
); );
} }

View File

@ -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.collections.SequenceNode;
import electrosphere.server.ai.nodes.meta.decorators.RunnerNode; import electrosphere.server.ai.nodes.meta.decorators.RunnerNode;
import electrosphere.server.ai.nodes.meta.decorators.SucceederNode; import electrosphere.server.ai.nodes.meta.decorators.SucceederNode;
import electrosphere.server.ai.nodes.plan.MacroPathfindingNode;
import electrosphere.server.ai.nodes.plan.PathfindingNode; import electrosphere.server.ai.nodes.plan.PathfindingNode;
/** /**
@ -50,8 +51,9 @@ public class MacroMoveToTree {
} }
return new SelectorNode( return new SelectorNode(
new SequenceNode( new SequenceNode(
"MacroMoveToTree",
//check if in range of target //check if in range of target
new TargetRangeCheckNode(dist, targetKey), new TargetRangeCheckNode(dist, targetKey),
new DataDeleteNode(BlackboardKeys.PATHFINDING_POINT), new DataDeleteNode(BlackboardKeys.PATHFINDING_POINT),
new DataDeleteNode(BlackboardKeys.PATHFINDING_DATA), new DataDeleteNode(BlackboardKeys.PATHFINDING_DATA),
//if in range, stop moving fowards and return SUCCESS //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 //not in range of target, keep moving towards it
new SequenceNode( new SequenceNode(
PathfindingNode.createPathEntity(targetKey), "MacroMoveToTree",
MacroPathfindingNode.createPathEntity(targetKey),
new FaceTargetNode(BlackboardKeys.PATHFINDING_POINT), new FaceTargetNode(BlackboardKeys.PATHFINDING_POINT),
new RunnerNode(new MoveStartNode(MovementRelativeFacing.FORWARD)) new RunnerNode(new MoveStartNode(MovementRelativeFacing.FORWARD))
) )

View File

@ -50,6 +50,7 @@ public class MoveToTree {
} }
return new SelectorNode( return new SelectorNode(
new SequenceNode( new SequenceNode(
"MoveToTree",
//check if in range of target //check if in range of target
new TargetRangeCheckNode(dist, targetKey), new TargetRangeCheckNode(dist, targetKey),
new DataDeleteNode(BlackboardKeys.PATHFINDING_POINT), new DataDeleteNode(BlackboardKeys.PATHFINDING_POINT),
@ -60,6 +61,7 @@ public class MoveToTree {
//not in range of target, keep moving towards it //not in range of target, keep moving towards it
new SequenceNode( new SequenceNode(
"MoveToTree",
PathfindingNode.createPathEntity(targetKey), PathfindingNode.createPathEntity(targetKey),
new FaceTargetNode(BlackboardKeys.PATHFINDING_POINT), new FaceTargetNode(BlackboardKeys.PATHFINDING_POINT),
new RunnerNode(new MoveStartNode(MovementRelativeFacing.FORWARD)) new RunnerNode(new MoveStartNode(MovementRelativeFacing.FORWARD))

View File

@ -24,6 +24,7 @@ public class ExploreTree {
*/ */
public static AITreeNode create(){ public static AITreeNode create(){
return new SequenceNode( return new SequenceNode(
"Explore",
new PublishStatusNode("Explore"), new PublishStatusNode("Explore"),
//resolve point to explore towards //resolve point to explore towards
new TargetExploreNode(BlackboardKeys.MOVE_TO_TARGET), new TargetExploreNode(BlackboardKeys.MOVE_TO_TARGET),

View File

@ -25,6 +25,7 @@ public class EquipToolbarTree {
*/ */
public static AITreeNode create(String targetKey){ public static AITreeNode create(String targetKey){
return new SequenceNode( return new SequenceNode(
"EquipToolbar",
new PublishStatusNode("Equip an item"), new PublishStatusNode("Equip an item"),
//check that we have this type of item //check that we have this type of item
new DataTransferNode(targetKey, BlackboardKeys.INVENTORY_CHECK_TYPE), new DataTransferNode(targetKey, BlackboardKeys.INVENTORY_CHECK_TYPE),

View File

@ -44,6 +44,7 @@ public class MeleeAITree {
public static AITreeNode create(AttackerTreeData attackerTreeData){ public static AITreeNode create(AttackerTreeData attackerTreeData){
return new SequenceNode( return new SequenceNode(
"MeleeAITree",
//preconditions here //preconditions here
new HasWeaponNode(), new HasWeaponNode(),
new MeleeTargetingNode(attackerTreeData.getAggroRange()), new MeleeTargetingNode(attackerTreeData.getAggroRange()),
@ -53,6 +54,7 @@ public class MeleeAITree {
//in attack range //in attack range
new SequenceNode( new SequenceNode(
"MeleeAITree",
//check prior to performing action //check prior to performing action
new MeleeRangeCheckNode(attackerTreeData,MeleeRangeCheckType.ATTACK), new MeleeRangeCheckNode(attackerTreeData,MeleeRangeCheckType.ATTACK),
@ -66,18 +68,21 @@ public class MeleeAITree {
new RandomizerNode( new RandomizerNode(
//wait //wait
new SequenceNode( new SequenceNode(
"MeleeAITree",
new PublishStatusNode("Waiting"), new PublishStatusNode("Waiting"),
new FaceTargetNode(BlackboardKeys.ENTITY_TARGET), new FaceTargetNode(BlackboardKeys.ENTITY_TARGET),
new TimerNode(new SucceederNode(null), 600) new TimerNode(new SucceederNode(null), 600)
), ),
//wait //wait
new SequenceNode( new SequenceNode(
"MeleeAITree",
new PublishStatusNode("Waiting"), new PublishStatusNode("Waiting"),
new FaceTargetNode(BlackboardKeys.ENTITY_TARGET), new FaceTargetNode(BlackboardKeys.ENTITY_TARGET),
new TimerNode(new SucceederNode(null), 300) new TimerNode(new SucceederNode(null), 300)
), ),
//attack //attack
new SequenceNode( new SequenceNode(
"MeleeAITree",
new PublishStatusNode("Attacking"), new PublishStatusNode("Attacking"),
new FaceTargetNode(BlackboardKeys.ENTITY_TARGET), new FaceTargetNode(BlackboardKeys.ENTITY_TARGET),
new AttackStartNode(), new AttackStartNode(),
@ -88,6 +93,7 @@ public class MeleeAITree {
//in aggro range //in aggro range
new SequenceNode( new SequenceNode(
"MeleeAITree",
//check prior to performing action //check prior to performing action
new MeleeRangeCheckNode(attackerTreeData,MeleeRangeCheckType.AGGRO), new MeleeRangeCheckNode(attackerTreeData,MeleeRangeCheckType.AGGRO),
@ -96,6 +102,7 @@ public class MeleeAITree {
//wait //wait
new SequenceNode( new SequenceNode(
"MeleeAITree",
new PublishStatusNode("Waiting"), new PublishStatusNode("Waiting"),
new FaceTargetNode(BlackboardKeys.ENTITY_TARGET), new FaceTargetNode(BlackboardKeys.ENTITY_TARGET),
new TimerNode(new SucceederNode(null), 1200) new TimerNode(new SucceederNode(null), 1200)
@ -103,6 +110,7 @@ public class MeleeAITree {
//strafe to the right //strafe to the right
new SequenceNode( new SequenceNode(
"MeleeAITree",
new PublishStatusNode("Strafing right"), new PublishStatusNode("Strafing right"),
new WalkStartNode(), new WalkStartNode(),
new InverterNode(new OnFailureNode( new InverterNode(new OnFailureNode(
@ -117,6 +125,7 @@ public class MeleeAITree {
//strafe to the left //strafe to the left
new SequenceNode( new SequenceNode(
"MeleeAITree",
new PublishStatusNode("Strafing left"), new PublishStatusNode("Strafing left"),
new WalkStartNode(), new WalkStartNode(),
new InverterNode(new OnFailureNode( new InverterNode(new OnFailureNode(
@ -132,6 +141,7 @@ public class MeleeAITree {
//approach target //approach target
//move towards target and attack //move towards target and attack
new SequenceNode( new SequenceNode(
"MeleeAITree",
new PublishStatusNode("Move into attack range"), new PublishStatusNode("Move into attack range"),
new FaceTargetNode(BlackboardKeys.ENTITY_TARGET), new FaceTargetNode(BlackboardKeys.ENTITY_TARGET),
new SucceederNode(new MoveStartNode(MovementRelativeFacing.FORWARD)), new SucceederNode(new MoveStartNode(MovementRelativeFacing.FORWARD)),

View File

@ -35,11 +35,13 @@ public class AcquireItemTree {
*/ */
public static AITreeNode create(String blackboardKey){ public static AITreeNode create(String blackboardKey){
return new SequenceNode( return new SequenceNode(
"AcquireItemTree",
new PublishStatusNode("Acquire an item"), new PublishStatusNode("Acquire an item"),
//solve how we're going to get this top level item //solve how we're going to get this top level item
new SolveSourcingTreeNode(blackboardKey), new SolveSourcingTreeNode(blackboardKey),
new SelectorNode( new SelectorNode(
new SequenceNode( new SequenceNode(
"AcquireItemTree",
new PublishStatusNode("Pick up an item"), new PublishStatusNode("Pick up an item"),
//check if we should be sourcing this item by picking it up //check if we should be sourcing this item by picking it up
new SourcingTypeNode(SourcingType.PICKUP, BlackboardKeys.ITEM_TARGET_CATEGORY), new SourcingTypeNode(SourcingType.PICKUP, BlackboardKeys.ITEM_TARGET_CATEGORY),
@ -50,6 +52,7 @@ public class AcquireItemTree {
new RunnerNode(null) new RunnerNode(null)
), ),
new SequenceNode( new SequenceNode(
"AcquireItemTree",
new PublishStatusNode("Craft an item"), new PublishStatusNode("Craft an item"),
//check if we should be sourcing this from a recipe //check if we should be sourcing this from a recipe
new SourcingTypeNode(SourcingType.RECIPE, blackboardKey), new SourcingTypeNode(SourcingType.RECIPE, blackboardKey),
@ -57,6 +60,7 @@ public class AcquireItemTree {
new RunnerNode(null) new RunnerNode(null)
), ),
new SequenceNode( new SequenceNode(
"AcquireItemTree",
new PublishStatusNode("Harvest an item"), new PublishStatusNode("Harvest an item"),
//check if we should be sourcing this from harvesting foliage //check if we should be sourcing this from harvesting foliage
new SourcingTypeNode(SourcingType.HARVEST, blackboardKey), new SourcingTypeNode(SourcingType.HARVEST, blackboardKey),
@ -66,6 +70,7 @@ public class AcquireItemTree {
new RunnerNode(null) new RunnerNode(null)
), ),
new SequenceNode( new SequenceNode(
"AcquireItemTree",
new PublishStatusNode("Fell a tree"), new PublishStatusNode("Fell a tree"),
//check if we should be sourcing this from felling a tree //check if we should be sourcing this from felling a tree
new SourcingTypeNode(SourcingType.TREE, blackboardKey), new SourcingTypeNode(SourcingType.TREE, blackboardKey),
@ -74,6 +79,7 @@ public class AcquireItemTree {
new RunnerNode(null) new RunnerNode(null)
), ),
new SequenceNode( new SequenceNode(
"AcquireItemTree",
new PublishStatusNode("Explore new chunks for resources"), new PublishStatusNode("Explore new chunks for resources"),
//Failed to find sources of material in existing chunks, must move for new chunks //Failed to find sources of material in existing chunks, must move for new chunks
ExploreTree.create() ExploreTree.create()

View File

@ -42,6 +42,7 @@ public class FellTree {
public static AITreeNode create(String targetKey){ public static AITreeNode create(String targetKey){
return new SequenceNode( return new SequenceNode(
"FellTree",
//preconditions here //preconditions here
new DataStorageNode(BlackboardKeys.INVENTORY_CHECK_TYPE, ItemIdStrings.ITEM_STONE_AXE), new DataStorageNode(BlackboardKeys.INVENTORY_CHECK_TYPE, ItemIdStrings.ITEM_STONE_AXE),
EquipToolbarTree.create(BlackboardKeys.INVENTORY_CHECK_TYPE), EquipToolbarTree.create(BlackboardKeys.INVENTORY_CHECK_TYPE),
@ -51,6 +52,7 @@ public class FellTree {
//in attack range //in attack range
new SequenceNode( new SequenceNode(
"FellTree",
//check if in range of target //check if in range of target
new TargetRangeCheckNode(FellTree.FELL_RANGE, targetKey), new TargetRangeCheckNode(FellTree.FELL_RANGE, targetKey),
//stop walking now that we're in range //stop walking now that we're in range
@ -60,6 +62,7 @@ public class FellTree {
//attack //attack
new SequenceNode( new SequenceNode(
"FellTree",
new PublishStatusNode("Attacking"), new PublishStatusNode("Attacking"),
new FaceTargetNode(BlackboardKeys.ENTITY_TARGET), new FaceTargetNode(BlackboardKeys.ENTITY_TARGET),
new AttackStartNode(), new AttackStartNode(),

View File

@ -23,8 +23,10 @@ public class MaslowTree {
*/ */
public static AITreeNode create(){ public static AITreeNode create(){
return new SequenceNode( return new SequenceNode(
"MaslowTree",
//check that dependencies exist //check that dependencies exist
new SequenceNode( new SequenceNode(
"MaslowTree",
new PublishStatusNode("Checking dependencies for maslow tree.."), new PublishStatusNode("Checking dependencies for maslow tree.."),
new MacroDataExists(), new MacroDataExists(),
new IsCharacterNode() new IsCharacterNode()

View File

@ -21,6 +21,7 @@ public class CombatTree {
*/ */
public static AITreeNode create(){ public static AITreeNode create(){
return new SequenceNode( return new SequenceNode(
"CombatTree",
new PublishStatusNode("Engaged in mortal combat"), new PublishStatusNode("Engaged in mortal combat"),
new SucceederNode(null) new SucceederNode(null)
); );

View File

@ -21,6 +21,7 @@ public class FleeTree {
*/ */
public static AITreeNode create(){ public static AITreeNode create(){
return new SequenceNode( return new SequenceNode(
"FleeTree",
new PublishStatusNode("Flee!"), new PublishStatusNode("Flee!"),
new SucceederNode(null) new SucceederNode(null)
); );

View File

@ -21,6 +21,7 @@ public class MaslowSafetyTree {
*/ */
public static AITreeNode create(){ public static AITreeNode create(){
return new SequenceNode( return new SequenceNode(
"MaslowSafetyTree",
new PublishStatusNode("Evaluate safety"), new PublishStatusNode("Evaluate safety"),
new SelectorNode( new SelectorNode(
FleeTree.create(), FleeTree.create(),

View File

@ -23,6 +23,7 @@ public class ConstructShelterTree {
*/ */
public static AITreeNode create(){ public static AITreeNode create(){
return new SequenceNode( return new SequenceNode(
"ConstructShelter",
new PublishStatusNode("Construct a shelter"), new PublishStatusNode("Construct a shelter"),
new BeginStructureNode(), new BeginStructureNode(),
BuildStructureTree.create(), BuildStructureTree.create(),

View File

@ -23,22 +23,27 @@ public class ShelterTree {
*/ */
public static AITreeNode create(){ public static AITreeNode create(){
return new SequenceNode( return new SequenceNode(
"ShelterTree",
new PublishStatusNode("Evaluate shelter"), new PublishStatusNode("Evaluate shelter"),
//make sure that this entity actually cares about shelter //make sure that this entity actually cares about shelter
new SequenceNode( new SequenceNode(
"ShelterTree",
//if this is a character //if this is a character
new IsCharacterNode() new IsCharacterNode()
), ),
//now that we know the entity cares about shelter, check if they have shelter //now that we know the entity cares about shelter, check if they have shelter
new SequenceNode( new SequenceNode(
"ShelterTree",
//if has shelter.. //if has shelter..
new SelectorNode( new SelectorNode(
new SequenceNode( new SequenceNode(
"ShelterTree",
new HasShelter() new HasShelter()
//already has shelter //already has shelter
//TODO: check environment (ie time of day) to see if we should return to shelter //TODO: check environment (ie time of day) to see if we should return to shelter
), ),
new SequenceNode( new SequenceNode(
"ShelterTree",
//does not have shelter //does not have shelter
ConstructShelterTree.create() ConstructShelterTree.create()
) )

View File

@ -19,6 +19,7 @@ public class ForageTree {
*/ */
public static AITreeNode create(){ public static AITreeNode create(){
return new SequenceNode( return new SequenceNode(
"ForageTree"
); );
} }

View File

@ -32,18 +32,22 @@ public class BuildStructureTree {
*/ */
public static AITreeNode create(){ public static AITreeNode create(){
return new SequenceNode( return new SequenceNode(
"BuildStructureTree",
new PublishStatusNode("Construct a structure"), new PublishStatusNode("Construct a structure"),
//figure out current task //figure out current task
new SelectorNode( new SelectorNode(
//make sure we know what material we need to build with currently //make sure we know what material we need to build with currently
new SequenceNode( new SequenceNode(
"BuildStructureTree",
new SolveBuildMaterialNode(), new SolveBuildMaterialNode(),
new PublishStatusNode("Trying to place block in structure"), new PublishStatusNode("Trying to place block in structure"),
//if has building materials //if has building materials
new SequenceNode( new SequenceNode(
"BuildStructureTree",
new InventoryContainsNode(BlackboardKeys.BUILDING_MATERIAL_CURRENT), new InventoryContainsNode(BlackboardKeys.BUILDING_MATERIAL_CURRENT),
//if we're within range to place the material //if we're within range to place the material
new SequenceNode( new SequenceNode(
"BuildStructureTree",
//not in range, move to within range //not in range, move to within range
MoveToTree.create(CollisionEngine.DEFAULT_INTERACT_DISTANCE, BlackboardKeys.STRUCTURE_TARGET), MoveToTree.create(CollisionEngine.DEFAULT_INTERACT_DISTANCE, BlackboardKeys.STRUCTURE_TARGET),
//equip the type of block to place //equip the type of block to place
@ -55,6 +59,7 @@ public class BuildStructureTree {
), ),
//does not have building materials //does not have building materials
new SequenceNode( new SequenceNode(
"BuildStructureTree",
new InverterNode(new InventoryContainsNode(BlackboardKeys.BUILDING_MATERIAL_CURRENT)), 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")) new RunnerNode(new PublishStatusNode("Waiting on macro character to set goal to find materials to build with"))
), ),

View File

@ -24,6 +24,7 @@ public class BlockerAITree {
*/ */
public static AITreeNode create(BlockerTreeData data){ public static AITreeNode create(BlockerTreeData data){
return new SequenceNode( return new SequenceNode(
"BlockerAITree",
new BlockStartNode(), new BlockStartNode(),
new MeleeTargetingNode(5.0f), new MeleeTargetingNode(5.0f),
new FaceTargetNode(BlackboardKeys.ENTITY_TARGET) new FaceTargetNode(BlackboardKeys.ENTITY_TARGET)

View File

@ -200,6 +200,14 @@ public class MacroPathNode {
return this.neighborNodes.stream().map((Long neighborId) -> cache.getNodeById(neighborId)).collect(Collectors.toList()); 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 * Adds a neighbor to this node
* @param neighbor The neighbor * @param neighbor The neighbor

View File

@ -1,6 +1,10 @@
package electrosphere.server.service; package electrosphere.server.service;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import org.joml.Vector3d; import org.joml.Vector3d;
@ -11,6 +15,7 @@ import electrosphere.engine.signal.SignalServiceImpl;
import electrosphere.engine.threads.ThreadCounts; import electrosphere.engine.threads.ThreadCounts;
import electrosphere.server.datacell.Realm; import electrosphere.server.datacell.Realm;
import electrosphere.server.macro.MacroData; import electrosphere.server.macro.MacroData;
import electrosphere.server.macro.spatial.path.MacroPathCache;
import electrosphere.server.macro.spatial.path.MacroPathNode; import electrosphere.server.macro.spatial.path.MacroPathNode;
import electrosphere.server.pathfinding.recast.PathingProgressiveData; import electrosphere.server.pathfinding.recast.PathingProgressiveData;
@ -75,7 +80,120 @@ public class MacroPathingService extends SignalServiceImpl {
* @return The path * @return The path
*/ */
private List<Vector3d> findPath(MacroData macroData, MacroPathNode start, MacroPathNode end){ 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();
}
} }
} }

View File

@ -42,13 +42,13 @@ public class CharaSimulation {
return; return;
} }
//send a character on a walk //send a character on a walk
// if(chara.getId() == 3){ if(chara.getId() == 3){
// Town hometown = CharacterUtils.getHometown(realm.getMacroData(), chara); Town hometown = CharacterUtils.getHometown(realm.getMacroData(), chara);
// if(hometown != null){ if(hometown != null){
// MacroRegion target = hometown.getFarmPlots(realm.getMacroData()).get(0); MacroRegion target = hometown.getFarmPlots(realm.getMacroData()).get(0);
// CharacterGoal.setCharacterGoal(chara, new CharacterGoal(CharacterGoalType.MOVE_TO_MACRO_STRUCT, target)); CharacterGoal.setCharacterGoal(chara, new CharacterGoal(CharacterGoalType.MOVE_TO_MACRO_STRUCT, target));
// } }
// } }
} }
/** /**