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
|
||||
Skybox reflects time of day
|
||||
Standardize data sourcing in MacroTemporalData
|
||||
Macro pathfinding scaffolding
|
||||
|
||||
|
||||
|
||||
|
||||
@ -45,6 +45,11 @@ public class BlackboardKeys {
|
||||
*/
|
||||
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
|
||||
*/
|
||||
|
||||
@ -6,7 +6,7 @@ import electrosphere.entity.Entity;
|
||||
import electrosphere.entity.EntityUtils;
|
||||
import electrosphere.server.ai.blackboard.Blackboard;
|
||||
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
|
||||
@ -44,8 +44,8 @@ public class TargetRangeCheckNode implements AITreeNode {
|
||||
targetPos = (Vector3d)targetRaw;
|
||||
} else if(targetRaw instanceof Entity){
|
||||
targetPos = EntityUtils.getPosition((Entity)targetRaw);
|
||||
} else if(targetRaw instanceof VirtualStructure){
|
||||
targetPos = ((VirtualStructure)targetRaw).getPos();
|
||||
} else if(targetRaw instanceof MacroObject macroObject){
|
||||
targetPos = macroObject.getPos();
|
||||
} else {
|
||||
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.goal.CharacterGoal;
|
||||
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;
|
||||
|
||||
/**
|
||||
@ -84,6 +86,13 @@ public class MacroCharacterGoalNode implements AITreeNode {
|
||||
}
|
||||
blackboard.put(BlackboardKeys.GOAL_ITEM_ACQUISITION_TARGET, targetRaw);
|
||||
} 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()){
|
||||
return AITreeNodeResult.SUCCESS;
|
||||
@ -91,4 +100,30 @@ public class MacroCharacterGoalNode implements AITreeNode {
|
||||
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.debug.PublishStatusNode;
|
||||
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.struct.BuildStructureTree;
|
||||
import electrosphere.server.macro.character.goal.CharacterGoal.CharacterGoalType;
|
||||
@ -53,6 +54,13 @@ public class CharacterGoalTree {
|
||||
new PublishStatusNode("Acquire building material"),
|
||||
//try to find building materials
|
||||
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_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.CharacterGoalType;
|
||||
import electrosphere.server.macro.civilization.town.Town;
|
||||
import electrosphere.server.macro.region.MacroRegion;
|
||||
import electrosphere.server.macro.structure.VirtualStructure;
|
||||
import electrosphere.server.macro.utils.StructurePlacementUtils;
|
||||
import electrosphere.server.macro.utils.StructureRepairUtils;
|
||||
@ -40,6 +41,14 @@ public class CharaSimulation {
|
||||
if(CharaSimulation.checkTownGoals(realm, chara)){
|
||||
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