macro pathfinding scaffolding
Some checks failed
studiorailgun/Renderer/pipeline/head There was a failure building this commit
Some checks failed
studiorailgun/Renderer/pipeline/head There was a failure building this commit
This commit is contained in:
parent
6883e75874
commit
0d01a4c214
@ -2084,6 +2084,7 @@ Remove potential collision engine footgun
|
|||||||
Synchronized time-of-day between server and client
|
Synchronized time-of-day between server and client
|
||||||
Skybox reflects time of day
|
Skybox reflects time of day
|
||||||
Standardize data sourcing in MacroTemporalData
|
Standardize data sourcing in MacroTemporalData
|
||||||
|
Macro pathfinding scaffolding
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -45,6 +45,11 @@ public class BlackboardKeys {
|
|||||||
*/
|
*/
|
||||||
public static final String BUILDING_MATERIAL_CURRENT = "buildingMaterialCurrent";
|
public static final String BUILDING_MATERIAL_CURRENT = "buildingMaterialCurrent";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The macro object that is being targeted
|
||||||
|
*/
|
||||||
|
public static final String MACRO_TARGET = "macroTarget";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type of item to try to acquire
|
* The type of item to try to acquire
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import electrosphere.entity.Entity;
|
|||||||
import electrosphere.entity.EntityUtils;
|
import electrosphere.entity.EntityUtils;
|
||||||
import electrosphere.server.ai.blackboard.Blackboard;
|
import electrosphere.server.ai.blackboard.Blackboard;
|
||||||
import electrosphere.server.ai.nodes.AITreeNode;
|
import electrosphere.server.ai.nodes.AITreeNode;
|
||||||
import electrosphere.server.macro.structure.VirtualStructure;
|
import electrosphere.server.macro.spatial.MacroObject;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the target is inside a given range of the entity
|
* Checks if the target is inside a given range of the entity
|
||||||
@ -44,8 +44,8 @@ public class TargetRangeCheckNode implements AITreeNode {
|
|||||||
targetPos = (Vector3d)targetRaw;
|
targetPos = (Vector3d)targetRaw;
|
||||||
} else if(targetRaw instanceof Entity){
|
} else if(targetRaw instanceof Entity){
|
||||||
targetPos = EntityUtils.getPosition((Entity)targetRaw);
|
targetPos = EntityUtils.getPosition((Entity)targetRaw);
|
||||||
} else if(targetRaw instanceof VirtualStructure){
|
} else if(targetRaw instanceof MacroObject macroObject){
|
||||||
targetPos = ((VirtualStructure)targetRaw).getPos();
|
targetPos = macroObject.getPos();
|
||||||
} else {
|
} else {
|
||||||
throw new Error("Unsupported target type " + targetRaw);
|
throw new Error("Unsupported target type " + targetRaw);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,6 +12,8 @@ import electrosphere.server.ai.nodes.checks.spatial.BeginStructureNode;
|
|||||||
import electrosphere.server.macro.character.Character;
|
import electrosphere.server.macro.character.Character;
|
||||||
import electrosphere.server.macro.character.goal.CharacterGoal;
|
import electrosphere.server.macro.character.goal.CharacterGoal;
|
||||||
import electrosphere.server.macro.character.goal.CharacterGoal.CharacterGoalType;
|
import electrosphere.server.macro.character.goal.CharacterGoal.CharacterGoalType;
|
||||||
|
import electrosphere.server.macro.region.MacroRegion;
|
||||||
|
import electrosphere.server.macro.spatial.MacroObject;
|
||||||
import electrosphere.server.macro.structure.VirtualStructure;
|
import electrosphere.server.macro.structure.VirtualStructure;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -84,6 +86,13 @@ public class MacroCharacterGoalNode implements AITreeNode {
|
|||||||
}
|
}
|
||||||
blackboard.put(BlackboardKeys.GOAL_ITEM_ACQUISITION_TARGET, targetRaw);
|
blackboard.put(BlackboardKeys.GOAL_ITEM_ACQUISITION_TARGET, targetRaw);
|
||||||
} break;
|
} break;
|
||||||
|
case MOVE_TO_MACRO_STRUCT: {
|
||||||
|
Object targetRaw = goal.getTarget();
|
||||||
|
if(!(targetRaw instanceof VirtualStructure) && !(targetRaw instanceof MacroRegion)){
|
||||||
|
throw new Error("Unsupported type! " + targetRaw);
|
||||||
|
}
|
||||||
|
MacroCharacterGoalNode.setMacroTarget(blackboard, (MacroObject)goal.getTarget());
|
||||||
|
} break;
|
||||||
}
|
}
|
||||||
if(type == goal.getType()){
|
if(type == goal.getType()){
|
||||||
return AITreeNodeResult.SUCCESS;
|
return AITreeNodeResult.SUCCESS;
|
||||||
@ -91,4 +100,30 @@ public class MacroCharacterGoalNode implements AITreeNode {
|
|||||||
return AITreeNodeResult.FAILURE;
|
return AITreeNodeResult.FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the macro object target for the entity
|
||||||
|
* @param blackboard The blackboard
|
||||||
|
* @param object The macro object to target
|
||||||
|
*/
|
||||||
|
public static void setMacroTarget(Blackboard blackboard, MacroObject object){
|
||||||
|
blackboard.put(BlackboardKeys.MACRO_TARGET, object);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the blackboard has a object target
|
||||||
|
* @param blackboard The blackboard
|
||||||
|
*/
|
||||||
|
public static boolean hasMacroTarget(Blackboard blackboard){
|
||||||
|
return blackboard.has(BlackboardKeys.MACRO_TARGET);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the object target in the blackboard
|
||||||
|
* @param blackboard The blackboard
|
||||||
|
* @return The object if it exists, null otherwise
|
||||||
|
*/
|
||||||
|
public static MacroObject getMacroTarget(Blackboard blackboard){
|
||||||
|
return (MacroObject)blackboard.get(BlackboardKeys.MACRO_TARGET);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,155 @@
|
|||||||
|
package electrosphere.server.ai.nodes.plan;
|
||||||
|
|
||||||
|
import org.joml.Vector3d;
|
||||||
|
|
||||||
|
import electrosphere.engine.Globals;
|
||||||
|
import electrosphere.entity.Entity;
|
||||||
|
import electrosphere.entity.EntityUtils;
|
||||||
|
import electrosphere.server.ai.AI;
|
||||||
|
import electrosphere.server.ai.blackboard.Blackboard;
|
||||||
|
import electrosphere.server.ai.nodes.AITreeNode;
|
||||||
|
import electrosphere.server.datacell.Realm;
|
||||||
|
import electrosphere.server.datacell.interfaces.PathfindingManager;
|
||||||
|
import electrosphere.server.macro.spatial.path.MacroPathNode;
|
||||||
|
import electrosphere.server.macro.structure.VirtualStructure;
|
||||||
|
import electrosphere.server.pathfinding.recast.PathingProgressiveData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A node that uses macro pathfinding structures to accelerate calculating the pathfinding
|
||||||
|
*/
|
||||||
|
public class MacroPathfindingNode implements AITreeNode {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The value used to check if the entity is close to a pathing point horizontally
|
||||||
|
*/
|
||||||
|
public static final double CLOSENESS_CHECK_BOUND_HORIZONTAL = 0.3f;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The value used to check if the entity is close to a pathing point vertically
|
||||||
|
*/
|
||||||
|
public static final double CLOSENESS_CHECK_BOUND_VERTICAL = 0.7f;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The blackboard key to lookup the target entity under
|
||||||
|
*/
|
||||||
|
private String targetEntityKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param targetEntityKey
|
||||||
|
*/
|
||||||
|
public static PathfindingNode createPathEntity(String targetEntityKey){
|
||||||
|
PathfindingNode rVal = new PathfindingNode();
|
||||||
|
rVal.targetEntityKey = targetEntityKey;
|
||||||
|
return rVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AITreeNodeResult evaluate(Entity entity, Blackboard blackboard){
|
||||||
|
//make sure that the solved pathfinding data is for the point we want
|
||||||
|
if(PathfindingNode.hasPathfindingData(blackboard)){
|
||||||
|
PathingProgressiveData pathingProgressiveData = PathfindingNode.getPathfindingData(blackboard);
|
||||||
|
Vector3d actualPoint = pathingProgressiveData.getGoal();
|
||||||
|
Object targetRaw = blackboard.get(this.targetEntityKey);
|
||||||
|
Vector3d targetPos = null;
|
||||||
|
if(targetRaw == null){
|
||||||
|
throw new Error("Target undefined!");
|
||||||
|
}
|
||||||
|
if(targetRaw instanceof MacroPathNode macroNode){
|
||||||
|
targetPos = macroNode.getPosition();
|
||||||
|
} else {
|
||||||
|
throw new Error("Unsupported target type " + targetRaw);
|
||||||
|
}
|
||||||
|
if(actualPoint.distance(targetPos) > CLOSENESS_CHECK_BOUND_HORIZONTAL){
|
||||||
|
PathfindingNode.clearPathfindingData(blackboard);
|
||||||
|
PathfindingNode.clearPathfindingPoint(blackboard);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//create a path if we don't already have one
|
||||||
|
if(!PathfindingNode.hasPathfindingData(blackboard)){
|
||||||
|
Object targetRaw = blackboard.get(this.targetEntityKey);
|
||||||
|
Vector3d targetPos = null;
|
||||||
|
if(targetRaw == null){
|
||||||
|
throw new Error("Target undefined!");
|
||||||
|
}
|
||||||
|
if(targetRaw instanceof Vector3d){
|
||||||
|
targetPos = (Vector3d)targetRaw;
|
||||||
|
} else if(targetRaw instanceof Entity){
|
||||||
|
targetPos = EntityUtils.getPosition((Entity)targetRaw);
|
||||||
|
} else if(targetRaw instanceof VirtualStructure){
|
||||||
|
targetPos = ((VirtualStructure)targetRaw).getPos();
|
||||||
|
} else {
|
||||||
|
throw new Error("Unsupported target type " + targetRaw);
|
||||||
|
}
|
||||||
|
|
||||||
|
Realm realm = Globals.serverState.realmManager.getEntityRealm(entity);
|
||||||
|
PathfindingManager pathfindingManager = realm.getPathfindingManager();
|
||||||
|
|
||||||
|
Vector3d entityPos = EntityUtils.getPosition(entity);
|
||||||
|
|
||||||
|
PathingProgressiveData pathingProgressiveData = pathfindingManager.findPathAsync(entityPos, targetPos);
|
||||||
|
PathfindingNode.setPathfindingData(blackboard, pathingProgressiveData);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!PathfindingNode.hasPathfindingData(blackboard)){
|
||||||
|
throw new Error("Failed to find path! Unhandled");
|
||||||
|
}
|
||||||
|
|
||||||
|
//check if the path has been found
|
||||||
|
PathingProgressiveData pathingProgressiveData = PathfindingNode.getPathfindingData(blackboard);
|
||||||
|
if(!pathingProgressiveData.isReady()){
|
||||||
|
AI.getAI(entity).setStatus("Thinking about pathing");
|
||||||
|
return AITreeNodeResult.RUNNING;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Vector3d entityPos = EntityUtils.getPosition(entity);
|
||||||
|
|
||||||
|
Vector3d currentPathPos = null;
|
||||||
|
if(pathingProgressiveData.getCurrentPoint() < pathingProgressiveData.getPoints().size()){
|
||||||
|
currentPathPos = pathingProgressiveData.getPoints().get(pathingProgressiveData.getCurrentPoint());
|
||||||
|
}
|
||||||
|
double vertDist = Math.abs(currentPathPos.y - entityPos.y);
|
||||||
|
double horizontalDist = Math.sqrt((currentPathPos.x - entityPos.x) * (currentPathPos.x - entityPos.x) + (currentPathPos.z - entityPos.z) * (currentPathPos.z - entityPos.z));
|
||||||
|
while(
|
||||||
|
currentPathPos != null &&
|
||||||
|
vertDist < CLOSENESS_CHECK_BOUND_VERTICAL &&
|
||||||
|
horizontalDist < CLOSENESS_CHECK_BOUND_HORIZONTAL &&
|
||||||
|
pathingProgressiveData.getCurrentPoint() < pathingProgressiveData.getPoints().size() - 1
|
||||||
|
){
|
||||||
|
pathingProgressiveData.setCurrentPoint(pathingProgressiveData.getCurrentPoint() + 1);
|
||||||
|
currentPathPos = pathingProgressiveData.getPoints().get(pathingProgressiveData.getCurrentPoint());
|
||||||
|
}
|
||||||
|
|
||||||
|
//if we're close enough to the final pathing point, always path to actual final point
|
||||||
|
if(
|
||||||
|
vertDist < CLOSENESS_CHECK_BOUND_VERTICAL &&
|
||||||
|
horizontalDist < CLOSENESS_CHECK_BOUND_HORIZONTAL &&
|
||||||
|
pathingProgressiveData.getCurrentPoint() == pathingProgressiveData.getPoints().size() - 1
|
||||||
|
){
|
||||||
|
Object targetRaw = blackboard.get(this.targetEntityKey);
|
||||||
|
Vector3d targetPos = null;
|
||||||
|
if(targetRaw == null){
|
||||||
|
throw new Error("Target undefined!");
|
||||||
|
}
|
||||||
|
if(targetRaw instanceof Vector3d){
|
||||||
|
targetPos = (Vector3d)targetRaw;
|
||||||
|
} else if(targetRaw instanceof Entity){
|
||||||
|
targetPos = EntityUtils.getPosition((Entity)targetRaw);
|
||||||
|
} else if(targetRaw instanceof VirtualStructure){
|
||||||
|
targetPos = ((VirtualStructure)targetRaw).getPos();
|
||||||
|
} else {
|
||||||
|
throw new Error("Unsupported target type " + targetRaw);
|
||||||
|
}
|
||||||
|
currentPathPos = targetPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
PathfindingNode.setPathfindingPoint(blackboard, currentPathPos);
|
||||||
|
|
||||||
|
return AITreeNodeResult.SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -11,6 +11,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.debug.PublishStatusNode;
|
import electrosphere.server.ai.nodes.meta.debug.PublishStatusNode;
|
||||||
import electrosphere.server.ai.nodes.meta.decorators.RunnerNode;
|
import electrosphere.server.ai.nodes.meta.decorators.RunnerNode;
|
||||||
|
import electrosphere.server.ai.trees.creature.MacroMoveToTree;
|
||||||
import electrosphere.server.ai.trees.creature.resource.AcquireItemTree;
|
import electrosphere.server.ai.trees.creature.resource.AcquireItemTree;
|
||||||
import electrosphere.server.ai.trees.struct.BuildStructureTree;
|
import electrosphere.server.ai.trees.struct.BuildStructureTree;
|
||||||
import electrosphere.server.macro.character.goal.CharacterGoal.CharacterGoalType;
|
import electrosphere.server.macro.character.goal.CharacterGoal.CharacterGoalType;
|
||||||
@ -53,6 +54,13 @@ public class CharacterGoalTree {
|
|||||||
new PublishStatusNode("Acquire building material"),
|
new PublishStatusNode("Acquire building material"),
|
||||||
//try to find building materials
|
//try to find building materials
|
||||||
AcquireItemTree.create(BlackboardKeys.GOAL_ITEM_ACQUISITION_TARGET)
|
AcquireItemTree.create(BlackboardKeys.GOAL_ITEM_ACQUISITION_TARGET)
|
||||||
|
),
|
||||||
|
|
||||||
|
//character's goal is to move to a macro virtual structure
|
||||||
|
new SequenceNode(
|
||||||
|
MacroCharacterGoalNode.create(CharacterGoalType.MOVE_TO_MACRO_STRUCT),
|
||||||
|
new PublishStatusNode("Move to macro structure"),
|
||||||
|
MacroMoveToTree.create(BlackboardKeys.MACRO_TARGET)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|||||||
@ -0,0 +1,69 @@
|
|||||||
|
package electrosphere.server.ai.trees.creature;
|
||||||
|
|
||||||
|
import electrosphere.entity.state.movement.groundmove.ClientGroundMovementTree.MovementRelativeFacing;
|
||||||
|
import electrosphere.server.ai.blackboard.BlackboardKeys;
|
||||||
|
import electrosphere.server.ai.nodes.AITreeNode;
|
||||||
|
import electrosphere.server.ai.nodes.actions.move.FaceTargetNode;
|
||||||
|
import electrosphere.server.ai.nodes.actions.move.MoveStartNode;
|
||||||
|
import electrosphere.server.ai.nodes.actions.move.MoveStopNode;
|
||||||
|
import electrosphere.server.ai.nodes.checks.spatial.TargetRangeCheckNode;
|
||||||
|
import electrosphere.server.ai.nodes.meta.DataDeleteNode;
|
||||||
|
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.PathfindingNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tree that moves an entity to a macro structure
|
||||||
|
*/
|
||||||
|
public class MacroMoveToTree {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of the tree
|
||||||
|
*/
|
||||||
|
public static final String TREE_NAME = "MacroMoveTo";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default distance to be within
|
||||||
|
*/
|
||||||
|
static final double DEFAULT_DIST = 0.5f;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a move-to-target tree
|
||||||
|
* @param targetKey The key to lookup the target under
|
||||||
|
* @return The root node of the move-to-target tree
|
||||||
|
*/
|
||||||
|
public static AITreeNode create(String targetKey){
|
||||||
|
return MacroMoveToTree.create(DEFAULT_DIST, targetKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a move-to-target tree
|
||||||
|
* @param dist The target distance to be within
|
||||||
|
* @param targetKey The key to lookup the target under
|
||||||
|
* @return The root node of the move-to-target tree
|
||||||
|
*/
|
||||||
|
public static AITreeNode create(double dist, String targetKey){
|
||||||
|
if(dist < PathfindingNode.CLOSENESS_CHECK_BOUND_HORIZONTAL){
|
||||||
|
throw new Error("Dist less than minimal amount! " + dist);
|
||||||
|
}
|
||||||
|
return new SelectorNode(
|
||||||
|
new SequenceNode(
|
||||||
|
//check if in range of target
|
||||||
|
new TargetRangeCheckNode(dist, targetKey),
|
||||||
|
new DataDeleteNode(BlackboardKeys.PATHFINDING_POINT),
|
||||||
|
new DataDeleteNode(BlackboardKeys.PATHFINDING_DATA),
|
||||||
|
//if in range, stop moving fowards and return SUCCESS
|
||||||
|
new SucceederNode(new MoveStopNode())
|
||||||
|
),
|
||||||
|
|
||||||
|
//not in range of target, keep moving towards it
|
||||||
|
new SequenceNode(
|
||||||
|
PathfindingNode.createPathEntity(targetKey),
|
||||||
|
new FaceTargetNode(BlackboardKeys.PATHFINDING_POINT),
|
||||||
|
new RunnerNode(new MoveStartNode(MovementRelativeFacing.FORWARD))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -26,6 +26,10 @@ public class CharacterGoal extends CharacterData {
|
|||||||
* Acquire an item
|
* Acquire an item
|
||||||
*/
|
*/
|
||||||
ACQUIRE_ITEM,
|
ACQUIRE_ITEM,
|
||||||
|
/**
|
||||||
|
* Move to a macro structure
|
||||||
|
*/
|
||||||
|
MOVE_TO_MACRO_STRUCT,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import electrosphere.server.macro.character.data.CharacterDataStrings;
|
|||||||
import electrosphere.server.macro.character.goal.CharacterGoal;
|
import electrosphere.server.macro.character.goal.CharacterGoal;
|
||||||
import electrosphere.server.macro.character.goal.CharacterGoal.CharacterGoalType;
|
import electrosphere.server.macro.character.goal.CharacterGoal.CharacterGoalType;
|
||||||
import electrosphere.server.macro.civilization.town.Town;
|
import electrosphere.server.macro.civilization.town.Town;
|
||||||
|
import electrosphere.server.macro.region.MacroRegion;
|
||||||
import electrosphere.server.macro.structure.VirtualStructure;
|
import electrosphere.server.macro.structure.VirtualStructure;
|
||||||
import electrosphere.server.macro.utils.StructurePlacementUtils;
|
import electrosphere.server.macro.utils.StructurePlacementUtils;
|
||||||
import electrosphere.server.macro.utils.StructureRepairUtils;
|
import electrosphere.server.macro.utils.StructureRepairUtils;
|
||||||
@ -40,6 +41,14 @@ public class CharaSimulation {
|
|||||||
if(CharaSimulation.checkTownGoals(realm, chara)){
|
if(CharaSimulation.checkTownGoals(realm, chara)){
|
||||||
return;
|
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));
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user