Ai rearchitecture for real btrees
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good
This commit is contained in:
parent
d44fdc5673
commit
bdec668fb8
@ -564,6 +564,7 @@ Fix katana is frustum culled incorrectly
|
|||||||
Fix rendering pipelines being broken when the katana is not drawn
|
Fix rendering pipelines being broken when the katana is not drawn
|
||||||
Fix deleting entities on server not properly deleting the whole entity
|
Fix deleting entities on server not properly deleting the whole entity
|
||||||
Windup and Cooldown animations on 2h block
|
Windup and Cooldown animations on 2h block
|
||||||
|
AI rearchitecture to actually do behavior trees now that I know what they are
|
||||||
|
|
||||||
|
|
||||||
# TODO
|
# TODO
|
||||||
|
|||||||
@ -155,6 +155,14 @@ public class ServerBlockTree implements BehaviorTree {
|
|||||||
return this.state == BlockState.BLOCKING;
|
return this.state == BlockState.BLOCKING;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if this tree is in any active state or not (ie is it playing an animation, blocking, etc)
|
||||||
|
* @return true if no animation is happening, false otherwise
|
||||||
|
*/
|
||||||
|
public boolean isIdle(){
|
||||||
|
return this.state == BlockState.NOT_BLOCKING;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the block system data for this tree
|
* Gets the block system data for this tree
|
||||||
* @return the data if it exists, otherwise null
|
* @return the data if it exists, otherwise null
|
||||||
|
|||||||
@ -8,8 +8,8 @@ import com.google.gson.JsonElement;
|
|||||||
import com.google.gson.JsonParseException;
|
import com.google.gson.JsonParseException;
|
||||||
|
|
||||||
import electrosphere.logger.LoggerInterface;
|
import electrosphere.logger.LoggerInterface;
|
||||||
import electrosphere.server.ai.creature.Attacker;
|
import electrosphere.server.ai.trees.test.BlockerAITree;
|
||||||
import electrosphere.server.ai.creature.Blocker;
|
import electrosphere.server.ai.trees.creature.AttackerAITree;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deserializes ai tree data types
|
* Deserializes ai tree data types
|
||||||
@ -20,9 +20,9 @@ public class AITreeDataSerializer implements JsonDeserializer<AITreeData> {
|
|||||||
public AITreeData deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
public AITreeData deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||||
|
|
||||||
switch(json.getAsJsonObject().get("name").getAsString()){
|
switch(json.getAsJsonObject().get("name").getAsString()){
|
||||||
case Attacker.TREE_NAME:
|
case AttackerAITree.TREE_NAME:
|
||||||
return context.deserialize(json, AttackerTreeData.class);
|
return context.deserialize(json, AttackerTreeData.class);
|
||||||
case Blocker.TREE_NAME:
|
case BlockerAITree.TREE_NAME:
|
||||||
return context.deserialize(json, BlockerTreeData.class);
|
return context.deserialize(json, BlockerTreeData.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,6 @@ package electrosphere.menu.debug;
|
|||||||
import electrosphere.engine.Globals;
|
import electrosphere.engine.Globals;
|
||||||
import electrosphere.renderer.ui.imgui.ImGuiWindow;
|
import electrosphere.renderer.ui.imgui.ImGuiWindow;
|
||||||
import electrosphere.renderer.ui.imgui.ImGuiWindow.ImGuiWindowCallback;
|
import electrosphere.renderer.ui.imgui.ImGuiWindow.ImGuiWindowCallback;
|
||||||
import electrosphere.server.ai.AI;
|
|
||||||
import imgui.ImGui;
|
import imgui.ImGui;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -38,14 +37,6 @@ public class ImGuiAI {
|
|||||||
Globals.aiManager.setActive(!Globals.aiManager.isActive());
|
Globals.aiManager.setActive(!Globals.aiManager.isActive());
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ImGui.collapsingHeader("AI Statuses")){
|
|
||||||
for(AI ai : Globals.aiManager.getAIList()){
|
|
||||||
if(ai.getCurrentTree() != null){
|
|
||||||
ImGui.text("Entity ID " + ai.getParent().getId() + " - Active Tree: " + ai.getCurrentTree().getCurrentStateName() + " - Priority: " + ai.getCurrentTree().getCurrentStatePriority());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
aiWindow.setOpen(false);
|
aiWindow.setOpen(false);
|
||||||
|
|||||||
@ -3,19 +3,13 @@ package electrosphere.server.ai;
|
|||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.joml.Quaterniond;
|
|
||||||
import org.joml.Vector3d;
|
|
||||||
|
|
||||||
import electrosphere.entity.Entity;
|
import electrosphere.entity.Entity;
|
||||||
import electrosphere.entity.EntityDataStrings;
|
import electrosphere.entity.EntityDataStrings;
|
||||||
import electrosphere.entity.EntityUtils;
|
|
||||||
import electrosphere.entity.types.creature.CreatureUtils;
|
import electrosphere.entity.types.creature.CreatureUtils;
|
||||||
import electrosphere.game.data.creature.type.ai.AITreeData;
|
import electrosphere.game.data.creature.type.ai.AITreeData;
|
||||||
import electrosphere.game.data.creature.type.ai.AttackerTreeData;
|
|
||||||
import electrosphere.game.data.creature.type.ai.BlockerTreeData;
|
import electrosphere.game.data.creature.type.ai.BlockerTreeData;
|
||||||
import electrosphere.logger.LoggerInterface;
|
import electrosphere.logger.LoggerInterface;
|
||||||
import electrosphere.server.ai.creature.Attacker;
|
import electrosphere.server.ai.trees.test.BlockerAITree;
|
||||||
import electrosphere.server.ai.creature.Blocker;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A collection of AITrees that are attached to a single entity.
|
* A collection of AITrees that are attached to a single entity.
|
||||||
@ -34,14 +28,19 @@ public class AI {
|
|||||||
Entity parent;
|
Entity parent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The list of trees for this ai in particular
|
* The root node of the behavior tree
|
||||||
*/
|
*/
|
||||||
List<AITree> trees = new LinkedList<AITree>();
|
AITreeNode rootNode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The currently active tree
|
* The blackboard for the tree
|
||||||
*/
|
*/
|
||||||
AITree currentTree = null;
|
Blackboard blackboard = new Blackboard();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal usage, used to track the nodes that have been evaluated and ensure we're not looping
|
||||||
|
*/
|
||||||
|
List<AITreeNode> evaluatedNodes = new LinkedList<AITreeNode>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs an AI from a list of trees that should be present on the ai
|
* Constructs an AI from a list of trees that should be present on the ai
|
||||||
@ -54,11 +53,8 @@ public class AI {
|
|||||||
//attach all trees
|
//attach all trees
|
||||||
for(AITreeData aiData : treeData){
|
for(AITreeData aiData : treeData){
|
||||||
switch(aiData.getName()){
|
switch(aiData.getName()){
|
||||||
case Attacker.TREE_NAME:
|
case BlockerAITree.TREE_NAME: {
|
||||||
rVal.trees.add(Attacker.construct(parent, (AttackerTreeData) aiData));
|
rVal.rootNode = BlockerAITree.create((BlockerTreeData) aiData);
|
||||||
break;
|
|
||||||
case Blocker.TREE_NAME: {
|
|
||||||
rVal.trees.add(Blocker.construct(parent, (BlockerTreeData) aiData));
|
|
||||||
} break;
|
} break;
|
||||||
default: {
|
default: {
|
||||||
LoggerInterface.loggerEngine.ERROR(new IllegalArgumentException("Trying to construct ai tree with undefined data type! " + aiData.getName()));
|
LoggerInterface.loggerEngine.ERROR(new IllegalArgumentException("Trying to construct ai tree with undefined data type! " + aiData.getName()));
|
||||||
@ -80,48 +76,23 @@ public class AI {
|
|||||||
* Simulates a single frame of this ai. Only runs simulation logic for the highest priority tree
|
* Simulates a single frame of this ai. Only runs simulation logic for the highest priority tree
|
||||||
*/
|
*/
|
||||||
protected void simulate(){
|
protected void simulate(){
|
||||||
//
|
if(this.shouldExecute()){
|
||||||
//Check basic values of entity to make sure the ai trees aren't changing state when updating state and priority
|
AITreeNode currentNode = this.rootNode;
|
||||||
Vector3d currentPos = new Vector3d(EntityUtils.getPosition(parent));
|
evaluatedNodes.clear();
|
||||||
Quaterniond currentRot = new Quaterniond(EntityUtils.getRotation(parent));
|
while(currentNode != null){
|
||||||
|
//
|
||||||
|
//loop checking
|
||||||
|
if(evaluatedNodes.contains(currentNode)){
|
||||||
|
LoggerInterface.loggerEngine.ERROR(new IllegalStateException("AI tree looped!"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
//
|
//evaluate node
|
||||||
//Update state and find the highest priority tree
|
evaluatedNodes.add(currentNode);
|
||||||
for(AITree tree : trees){
|
currentNode = currentNode.evaluate(this.parent, this.blackboard);
|
||||||
tree.updateStateAndPriority();
|
|
||||||
if(currentTree == null){
|
|
||||||
currentTree = tree;
|
|
||||||
} else if(currentTree.getCurrentStatePriority() < tree.getCurrentStatePriority()){
|
|
||||||
currentTree = tree;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
//Actual check to see if state has changed (throw error if it does)
|
|
||||||
Vector3d postUpdatePos = EntityUtils.getPosition(parent);
|
|
||||||
Quaterniond postUpdateRot = EntityUtils.getRotation(parent);
|
|
||||||
if(postUpdatePos.distance(currentPos) > 0){
|
|
||||||
String message = "An AI tree changed entity state while it was updating its priority!\n" +
|
|
||||||
"This is an illegal operation!\n" +
|
|
||||||
"All entity updates should be in the simulate method!" +
|
|
||||||
"(The position changed)";
|
|
||||||
LoggerInterface.loggerEngine.ERROR(new IllegalStateException(message));
|
|
||||||
}
|
|
||||||
if(postUpdateRot.dot(currentRot) < ERROR_CHECK_ROT_THRESHOLD){
|
|
||||||
String message = "An AI tree changed entity state while it was updating its priority!\n" +
|
|
||||||
"This is an illegal operation!\n" +
|
|
||||||
"All entity updates should be in the simulate method!" +
|
|
||||||
"(The rotation changed) " + postUpdateRot.dot(currentRot);
|
|
||||||
LoggerInterface.loggerEngine.ERROR(new IllegalStateException(message));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
//Simulate the highest priority tree
|
|
||||||
if(currentTree != null && !CreatureUtils.hasControllerPlayerId(this.parent)){
|
|
||||||
currentTree.simulate();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -155,11 +126,19 @@ public class AI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the current highest priority tree
|
* Gets the root node of the behavior tree
|
||||||
* @return The highest priority tree
|
* @return The root node
|
||||||
*/
|
*/
|
||||||
public AITree getCurrentTree(){
|
public AITreeNode getRootNode(){
|
||||||
return this.currentTree;
|
return this.rootNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the ai should simulate or not
|
||||||
|
* @return true if should simulate, false otherwise
|
||||||
|
*/
|
||||||
|
private boolean shouldExecute(){
|
||||||
|
return !CreatureUtils.hasControllerPlayerId(this.parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,40 +0,0 @@
|
|||||||
package electrosphere.server.ai;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A behavior tree encapsulating some type of
|
|
||||||
*/
|
|
||||||
public interface AITree {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simulates a single frame of logic for the ai.
|
|
||||||
* This is where the ai actually makes the entity do something
|
|
||||||
*/
|
|
||||||
public void simulate();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the name of the tree
|
|
||||||
* @return The tree's name
|
|
||||||
*/
|
|
||||||
public String getTreeName();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the state and priority of this tree.
|
|
||||||
* This should calculate the goal of this tree every frame.
|
|
||||||
* All state transitions should be in here.
|
|
||||||
*/
|
|
||||||
public void updateStateAndPriority();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the name of the current state of this ai
|
|
||||||
* @return The name
|
|
||||||
*/
|
|
||||||
public String getCurrentStateName();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the priority of the current state of this ai
|
|
||||||
* The priority increases with the value of the int. IE, a priority of 1 is of greater importance than a priority of 0.
|
|
||||||
* @return The priority
|
|
||||||
*/
|
|
||||||
public int getCurrentStatePriority();
|
|
||||||
|
|
||||||
}
|
|
||||||
18
src/main/java/electrosphere/server/ai/AITreeNode.java
Normal file
18
src/main/java/electrosphere/server/ai/AITreeNode.java
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package electrosphere.server.ai;
|
||||||
|
|
||||||
|
import electrosphere.entity.Entity;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A node in a behavior tree
|
||||||
|
*/
|
||||||
|
public interface AITreeNode {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Evaluates the node
|
||||||
|
* @param entity The entity to evaluate this node on
|
||||||
|
* @param blackboard The blackboard for the tree
|
||||||
|
* @return The next node to run, or null if no more nodes should be run
|
||||||
|
*/
|
||||||
|
public AITreeNode evaluate(Entity entity, Blackboard blackboard);
|
||||||
|
|
||||||
|
}
|
||||||
57
src/main/java/electrosphere/server/ai/Blackboard.java
Normal file
57
src/main/java/electrosphere/server/ai/Blackboard.java
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package electrosphere.server.ai;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Blackboard for storing data with the tree
|
||||||
|
*/
|
||||||
|
public class Blackboard {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The values in the blackboard
|
||||||
|
*/
|
||||||
|
Map<String,Object> values = new HashMap<String,Object>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Puts a value into the blackboard
|
||||||
|
* @param key The key
|
||||||
|
* @param value The value
|
||||||
|
*/
|
||||||
|
public void put(String key, Object value){
|
||||||
|
values.put(key,value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a value from the blackboard
|
||||||
|
* @param key The key of the value
|
||||||
|
* @return The value if it exists, null otherwise
|
||||||
|
*/
|
||||||
|
public Object get(String key){
|
||||||
|
if(this.has(key)){
|
||||||
|
return values.get(key);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the blackboard contains a value at a given key
|
||||||
|
* @param key The key
|
||||||
|
* @return true if the blackboard has a value for that key, false otherwise
|
||||||
|
*/
|
||||||
|
public boolean has(String key){
|
||||||
|
return values.containsKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a value from the blackboard
|
||||||
|
* @param key The key to delete the value from
|
||||||
|
*/
|
||||||
|
public void delete(String key){
|
||||||
|
if(this.has(key)){
|
||||||
|
values.remove(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -1,344 +0,0 @@
|
|||||||
package electrosphere.server.ai.creature;
|
|
||||||
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
import org.joml.Vector3d;
|
|
||||||
|
|
||||||
import electrosphere.engine.Globals;
|
|
||||||
import electrosphere.entity.Entity;
|
|
||||||
import electrosphere.entity.EntityTags;
|
|
||||||
import electrosphere.entity.EntityUtils;
|
|
||||||
import electrosphere.entity.state.attack.ServerAttackTree;
|
|
||||||
import electrosphere.entity.state.block.ServerBlockTree;
|
|
||||||
import electrosphere.entity.state.movement.groundmove.ClientGroundMovementTree;
|
|
||||||
import electrosphere.entity.state.movement.groundmove.ServerGroundMovementTree;
|
|
||||||
import electrosphere.entity.state.movement.groundmove.ClientGroundMovementTree.MovementRelativeFacing;
|
|
||||||
import electrosphere.entity.types.creature.CreatureUtils;
|
|
||||||
import electrosphere.game.data.creature.type.ai.AttackerTreeData;
|
|
||||||
import electrosphere.logger.LoggerInterface;
|
|
||||||
import electrosphere.server.ai.AITree;
|
|
||||||
import electrosphere.server.datacell.Realm;
|
|
||||||
import electrosphere.server.datacell.utils.DataCellSearchUtils;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Keeps track of targets and instructs the ai to attack
|
|
||||||
*/
|
|
||||||
public class Attacker implements AITree {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The name of the tree type
|
|
||||||
*/
|
|
||||||
public static final String TREE_NAME = "Attacker";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Different states available to the Attacker tree
|
|
||||||
*/
|
|
||||||
public enum AttackerState {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Not doing anything
|
|
||||||
*/
|
|
||||||
IDLE,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Trying to close the distance between itself and its target
|
|
||||||
*/
|
|
||||||
MOVING_INTO_ATTACKING_RANGE,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Moving around the target. If the target is a player, this is intended to give the player openings to attack during
|
|
||||||
*/
|
|
||||||
DANCING,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Actually going in for an attack
|
|
||||||
*/
|
|
||||||
ATTACKING,
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The configuring tree data for this tree
|
|
||||||
*/
|
|
||||||
AttackerTreeData treeData;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The current state of this instance of this tree
|
|
||||||
*/
|
|
||||||
AttackerState state = AttackerState.IDLE;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The parent to this tree
|
|
||||||
*/
|
|
||||||
Entity parent;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The current target of this tree
|
|
||||||
*/
|
|
||||||
Entity target = null;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The number of frames the tree has been in its current state
|
|
||||||
*/
|
|
||||||
int currentStateFrameCount = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The random used for rolls on behavior transitions
|
|
||||||
*/
|
|
||||||
Random random = new Random();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getTreeName(){
|
|
||||||
return TREE_NAME;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
* @param parent The parent to this tree
|
|
||||||
*/
|
|
||||||
private Attacker(Entity parent){
|
|
||||||
this.parent = parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs an attacker tree
|
|
||||||
* @param parent The parent entity
|
|
||||||
* @param treeData The tree data
|
|
||||||
* @return The Attacker tree
|
|
||||||
*/
|
|
||||||
public static AITree construct(Entity parent, AttackerTreeData treeData) {
|
|
||||||
Attacker rVal = new Attacker(parent);
|
|
||||||
rVal.treeData = treeData;
|
|
||||||
return rVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void simulate() {
|
|
||||||
switch(state){
|
|
||||||
case IDLE: {
|
|
||||||
ServerBlockTree serverBlockTree = ServerBlockTree.getServerBlockTree(parent);
|
|
||||||
if(serverBlockTree.isBlocking()){
|
|
||||||
serverBlockTree.stop();
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case MOVING_INTO_ATTACKING_RANGE: {
|
|
||||||
//make sure we're blocking
|
|
||||||
ServerBlockTree serverBlockTree = ServerBlockTree.getServerBlockTree(parent);
|
|
||||||
if(!serverBlockTree.isBlocking()){
|
|
||||||
serverBlockTree.start();
|
|
||||||
}
|
|
||||||
//close distance
|
|
||||||
moveToTarget();
|
|
||||||
} break;
|
|
||||||
case DANCING: {
|
|
||||||
ServerBlockTree serverBlockTree = ServerBlockTree.getServerBlockTree(parent);
|
|
||||||
if(!serverBlockTree.isBlocking()){
|
|
||||||
serverBlockTree.start();
|
|
||||||
}
|
|
||||||
//TODO: move parallel
|
|
||||||
} break;
|
|
||||||
case ATTACKING: {
|
|
||||||
ServerBlockTree serverBlockTree = ServerBlockTree.getServerBlockTree(parent);
|
|
||||||
ServerAttackTree serverAttackTree = ServerAttackTree.getServerAttackTree(parent);
|
|
||||||
if(serverBlockTree.isBlocking()){
|
|
||||||
serverBlockTree.stop();
|
|
||||||
} else if(serverAttackTree.isAttacking()){
|
|
||||||
|
|
||||||
} else {
|
|
||||||
serverAttackTree.start();
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateStateAndPriority() {
|
|
||||||
switch(state){
|
|
||||||
case IDLE: {
|
|
||||||
if(target != null){
|
|
||||||
setState(AttackerState.MOVING_INTO_ATTACKING_RANGE);
|
|
||||||
} else {
|
|
||||||
searchForTarget();
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case MOVING_INTO_ATTACKING_RANGE: {
|
|
||||||
if(inAttackRange()){
|
|
||||||
//should change to dancing or attacking
|
|
||||||
if(this.random.nextInt(2) > 0){
|
|
||||||
setState(AttackerState.ATTACKING);
|
|
||||||
} else {
|
|
||||||
setState(AttackerState.DANCING);
|
|
||||||
}
|
|
||||||
} else if(
|
|
||||||
inAggroRange() &&
|
|
||||||
!inAttackRange() &&
|
|
||||||
currentStateFrameCount > this.treeData.getStateChangeTimeout()
|
|
||||||
){
|
|
||||||
//should change to dancing or moving into attack range
|
|
||||||
if(this.random.nextInt(2) > 0){
|
|
||||||
setState(AttackerState.MOVING_INTO_ATTACKING_RANGE);
|
|
||||||
} else {
|
|
||||||
setState(AttackerState.DANCING);
|
|
||||||
}
|
|
||||||
} else if(!inAggroRange()) {
|
|
||||||
this.target = null;
|
|
||||||
setState(AttackerState.IDLE);
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case DANCING: {
|
|
||||||
if(inAttackRange()){
|
|
||||||
//should change to dancing or attacking
|
|
||||||
if(this.random.nextInt(2) > 0){
|
|
||||||
setState(AttackerState.ATTACKING);
|
|
||||||
} else {
|
|
||||||
setState(AttackerState.DANCING);
|
|
||||||
}
|
|
||||||
} else if(
|
|
||||||
inAggroRange() &&
|
|
||||||
!inAttackRange() &&
|
|
||||||
currentStateFrameCount > this.treeData.getStateChangeTimeout()
|
|
||||||
){
|
|
||||||
//should change to dancing or moving into attack range
|
|
||||||
if(this.random.nextInt(2) > 0){
|
|
||||||
setState(AttackerState.MOVING_INTO_ATTACKING_RANGE);
|
|
||||||
} else {
|
|
||||||
setState(AttackerState.DANCING);
|
|
||||||
}
|
|
||||||
} else if(!inAggroRange()) {
|
|
||||||
this.target = null;
|
|
||||||
setState(AttackerState.IDLE);
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
case ATTACKING: {
|
|
||||||
if(inAttackRange()){
|
|
||||||
//should change to dancing or attacking
|
|
||||||
if(this.random.nextInt(2) > 0){
|
|
||||||
setState(AttackerState.ATTACKING);
|
|
||||||
} else {
|
|
||||||
setState(AttackerState.DANCING);
|
|
||||||
}
|
|
||||||
} else if(
|
|
||||||
inAggroRange() &&
|
|
||||||
!inAttackRange() &&
|
|
||||||
currentStateFrameCount > this.treeData.getStateChangeTimeout()
|
|
||||||
){
|
|
||||||
//should change to dancing or moving into attack range
|
|
||||||
if(this.random.nextInt(2) > 0){
|
|
||||||
setState(AttackerState.MOVING_INTO_ATTACKING_RANGE);
|
|
||||||
} else {
|
|
||||||
setState(AttackerState.DANCING);
|
|
||||||
}
|
|
||||||
} else if(!inAggroRange()) {
|
|
||||||
this.target = null;
|
|
||||||
setState(AttackerState.IDLE);
|
|
||||||
}
|
|
||||||
} break;
|
|
||||||
}
|
|
||||||
currentStateFrameCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getCurrentStateName() {
|
|
||||||
switch(state){
|
|
||||||
case IDLE:
|
|
||||||
return "IDLE";
|
|
||||||
case MOVING_INTO_ATTACKING_RANGE:
|
|
||||||
return "MOVING INTO ATTACKING RANGE";
|
|
||||||
case DANCING:
|
|
||||||
return "DANCING";
|
|
||||||
case ATTACKING:
|
|
||||||
return "ATTACKING";
|
|
||||||
}
|
|
||||||
LoggerInterface.loggerEngine.ERROR(new IllegalStateException("Unaccounted for state name in ai tree"));
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getCurrentStatePriority() {
|
|
||||||
switch(state){
|
|
||||||
case IDLE:
|
|
||||||
return 0;
|
|
||||||
case MOVING_INTO_ATTACKING_RANGE:
|
|
||||||
return 3;
|
|
||||||
case DANCING:
|
|
||||||
return 2;
|
|
||||||
case ATTACKING:
|
|
||||||
return 8;
|
|
||||||
}
|
|
||||||
LoggerInterface.loggerEngine.ERROR(new IllegalStateException("Unaccounted for state name in ai tree"));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the state of the tree
|
|
||||||
* @param state The state
|
|
||||||
*/
|
|
||||||
private void setState(AttackerState state){
|
|
||||||
this.state = state;
|
|
||||||
this.currentStateFrameCount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Searches for a valid target
|
|
||||||
*/
|
|
||||||
private void searchForTarget(){
|
|
||||||
Vector3d position = EntityUtils.getPosition(parent);
|
|
||||||
Realm realm = Globals.realmManager.getEntityRealm(parent);
|
|
||||||
for(Entity current : DataCellSearchUtils.getEntitiesWithTagAroundLocation(realm,position,EntityTags.LIFE_STATE)){
|
|
||||||
if(current != parent){
|
|
||||||
Vector3d potentialTargetPosition = EntityUtils.getPosition(current);
|
|
||||||
if(position.distance(potentialTargetPosition) < treeData.getAggroRange()){
|
|
||||||
target = current;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the target is within the aggression range
|
|
||||||
*/
|
|
||||||
private boolean inAggroRange(){
|
|
||||||
boolean rVal = false;
|
|
||||||
Vector3d position = EntityUtils.getPosition(parent);
|
|
||||||
Vector3d targetPosition = EntityUtils.getPosition(target);
|
|
||||||
if(new Vector3d(position).distance(targetPosition) < treeData.getAggroRange()){
|
|
||||||
rVal = true;
|
|
||||||
}
|
|
||||||
return rVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the target is within range of an attack animation
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
private boolean inAttackRange(){
|
|
||||||
boolean rVal = false;
|
|
||||||
Vector3d position = EntityUtils.getPosition(parent);
|
|
||||||
Vector3d targetPosition = EntityUtils.getPosition(target);
|
|
||||||
if(new Vector3d(position).distance(targetPosition) < treeData.getAttackRange()){
|
|
||||||
rVal = true;
|
|
||||||
}
|
|
||||||
return rVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempts to move towards the target
|
|
||||||
*/
|
|
||||||
private void moveToTarget(){
|
|
||||||
Vector3d targetPosition = EntityUtils.getPosition(target);
|
|
||||||
Vector3d characterPosition = EntityUtils.getPosition(parent);
|
|
||||||
Vector3d moveVector = new Vector3d(targetPosition).sub(characterPosition).normalize();
|
|
||||||
CreatureUtils.setFacingVector(parent, new Vector3d((float)moveVector.x,(float)moveVector.y,(float)moveVector.z));
|
|
||||||
ServerGroundMovementTree characterMoveTree = (ServerGroundMovementTree)CreatureUtils.serverGetEntityMovementTree(parent);
|
|
||||||
if(characterMoveTree.getState()==ClientGroundMovementTree.MovementTreeState.IDLE || characterMoveTree.getState()==ClientGroundMovementTree.MovementTreeState.SLOWDOWN){
|
|
||||||
characterMoveTree.start(MovementRelativeFacing.FORWARD);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,127 +0,0 @@
|
|||||||
package electrosphere.server.ai.creature;
|
|
||||||
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
import org.joml.Quaterniond;
|
|
||||||
import org.joml.Vector3d;
|
|
||||||
|
|
||||||
import electrosphere.engine.Globals;
|
|
||||||
import electrosphere.entity.Entity;
|
|
||||||
import electrosphere.entity.EntityTags;
|
|
||||||
import electrosphere.entity.EntityUtils;
|
|
||||||
import electrosphere.entity.state.block.ServerBlockTree;
|
|
||||||
import electrosphere.entity.types.creature.CreatureUtils;
|
|
||||||
import electrosphere.game.data.creature.type.ai.BlockerTreeData;
|
|
||||||
import electrosphere.server.ai.AITree;
|
|
||||||
import electrosphere.server.datacell.Realm;
|
|
||||||
import electrosphere.server.datacell.utils.DataCellSearchUtils;
|
|
||||||
import electrosphere.util.math.MathUtils;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AI that just continuously blocks
|
|
||||||
*/
|
|
||||||
public class Blocker implements AITree {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The name of the tree type
|
|
||||||
*/
|
|
||||||
public static final String TREE_NAME = "Blocker";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The configuring tree data for this tree
|
|
||||||
*/
|
|
||||||
BlockerTreeData treeData;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The parent to this tree
|
|
||||||
*/
|
|
||||||
Entity parent;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The current target of this tree
|
|
||||||
*/
|
|
||||||
Entity target = null;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The number of frames the tree has been in its current state
|
|
||||||
*/
|
|
||||||
int currentStateFrameCount = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The random used for rolls on behavior transitions
|
|
||||||
*/
|
|
||||||
Random random = new Random();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Private constructor
|
|
||||||
* @param parent The parent entity
|
|
||||||
*/
|
|
||||||
private Blocker(Entity parent){
|
|
||||||
this.parent = parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs an attacker tree
|
|
||||||
* @param parent The parent entity
|
|
||||||
* @param treeData The tree data
|
|
||||||
* @return The Attacker tree
|
|
||||||
*/
|
|
||||||
public static AITree construct(Entity parent, BlockerTreeData treeData) {
|
|
||||||
Blocker rVal = new Blocker(parent);
|
|
||||||
rVal.treeData = treeData;
|
|
||||||
return rVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void simulate() {
|
|
||||||
if(target == null){
|
|
||||||
searchForTarget();
|
|
||||||
}
|
|
||||||
if(ServerBlockTree.getServerBlockTree(parent) != null){
|
|
||||||
ServerBlockTree serverBlockTree = ServerBlockTree.getServerBlockTree(parent);
|
|
||||||
if(!serverBlockTree.isBlocking()){
|
|
||||||
serverBlockTree.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(target != null){
|
|
||||||
Vector3d targetPos = EntityUtils.getPosition(target);
|
|
||||||
Quaterniond rotation = MathUtils.calculateRotationFromPointToPoint(EntityUtils.getPosition(parent), targetPos);
|
|
||||||
EntityUtils.getRotation(parent).set(rotation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getTreeName() {
|
|
||||||
return TREE_NAME;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateStateAndPriority() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getCurrentStateName() {
|
|
||||||
return "BLCOKING";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getCurrentStatePriority() {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Searches for a valid target
|
|
||||||
*/
|
|
||||||
private void searchForTarget(){
|
|
||||||
Vector3d position = EntityUtils.getPosition(parent);
|
|
||||||
Realm realm = Globals.realmManager.getEntityRealm(parent);
|
|
||||||
for(Entity current : DataCellSearchUtils.getEntitiesWithTagAroundLocation(realm,position,EntityTags.LIFE_STATE)){
|
|
||||||
if(current != parent && CreatureUtils.isCreature(current)){
|
|
||||||
target = current;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,123 +0,0 @@
|
|||||||
package electrosphere.server.ai.creature;
|
|
||||||
|
|
||||||
import electrosphere.entity.Entity;
|
|
||||||
import electrosphere.entity.EntityUtils;
|
|
||||||
import electrosphere.entity.state.movement.groundmove.ClientGroundMovementTree;
|
|
||||||
import electrosphere.entity.state.movement.groundmove.ClientGroundMovementTree.MovementRelativeFacing;
|
|
||||||
import electrosphere.entity.types.creature.CreatureUtils;
|
|
||||||
import electrosphere.server.ai.AITree;
|
|
||||||
|
|
||||||
import java.util.Random;
|
|
||||||
import org.joml.Vector3d;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An ai whose goal is to wander sporadically
|
|
||||||
*/
|
|
||||||
public class MillAbout implements AITree {
|
|
||||||
|
|
||||||
Entity character;
|
|
||||||
|
|
||||||
boolean millAbout = true;
|
|
||||||
int millMoveTimer = 0;
|
|
||||||
int millMoveTimerTimeout = 100;
|
|
||||||
int MILL_MOVE_TIMEOUT_LOWER = 1500;
|
|
||||||
int MILL_MOVE_TIMEOUT_UPPER = 2000;
|
|
||||||
float millTargetMaxDist = 3;
|
|
||||||
|
|
||||||
boolean moveToTarget = false;
|
|
||||||
Vector3d moveTargetPosition;
|
|
||||||
|
|
||||||
private MillAbout(Entity character){
|
|
||||||
this.character = character;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void simulate(){
|
|
||||||
Vector3d position = EntityUtils.getPosition(character);
|
|
||||||
//mill aboud
|
|
||||||
if(millAbout){
|
|
||||||
if(millMoveTimer >= millMoveTimerTimeout){
|
|
||||||
Random rand = new Random();
|
|
||||||
millMoveTimer = 0;
|
|
||||||
millMoveTimerTimeout = rand.nextInt(MILL_MOVE_TIMEOUT_UPPER - MILL_MOVE_TIMEOUT_LOWER) + MILL_MOVE_TIMEOUT_LOWER;
|
|
||||||
Vector3d moveVector = new Vector3d();
|
|
||||||
//search for a spot to mill to
|
|
||||||
// while(true){
|
|
||||||
moveVector.set(
|
|
||||||
rand.nextFloat() - 0.5,
|
|
||||||
rand.nextFloat() - 0.5,
|
|
||||||
rand.nextFloat() - 0.5
|
|
||||||
).normalize().mul(millTargetMaxDist);
|
|
||||||
moveVector.y = position.y; //Globals.commonWorldData.getElevationAtPoint(new Vector3d(position).add(moveVector));
|
|
||||||
|
|
||||||
//TODO: search in navmeshmanager to make sure navigable, otherwise generate new pos
|
|
||||||
// }
|
|
||||||
moveTargetPosition = new Vector3d(position).add(moveVector);
|
|
||||||
millAbout = false;
|
|
||||||
moveToTarget = true;
|
|
||||||
} else {
|
|
||||||
millMoveTimer++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(moveToTarget){
|
|
||||||
if(moveTargetPosition.distance(position) > 0.4){
|
|
||||||
Vector3d moveVector = new Vector3d(moveTargetPosition).sub(position).normalize();
|
|
||||||
CreatureUtils.setFacingVector(character, new Vector3d((float)moveVector.x,(float)moveVector.y,(float)moveVector.z));
|
|
||||||
ClientGroundMovementTree characterMoveTree = (ClientGroundMovementTree)CreatureUtils.serverGetEntityMovementTree(character);
|
|
||||||
if(characterMoveTree.getState()==ClientGroundMovementTree.MovementTreeState.IDLE || characterMoveTree.getState()==ClientGroundMovementTree.MovementTreeState.SLOWDOWN){
|
|
||||||
characterMoveTree.start(MovementRelativeFacing.FORWARD);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ClientGroundMovementTree characterMoveTree = (ClientGroundMovementTree)CreatureUtils.serverGetEntityMovementTree(character);
|
|
||||||
characterMoveTree.slowdown();
|
|
||||||
// System.out.println("Made it to destination");
|
|
||||||
moveToTarget = false;
|
|
||||||
millAbout = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// if(target != null){
|
|
||||||
// if(inAttackRange()){
|
|
||||||
// attack();
|
|
||||||
// } else {
|
|
||||||
// if(inAggroRange()){
|
|
||||||
// moveToTarget();
|
|
||||||
// } else {
|
|
||||||
// target = null;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// searchForTarget();
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
public static AITree construct(Entity parent) {
|
|
||||||
MillAbout rVal = new MillAbout(parent);
|
|
||||||
return rVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getTreeName() {
|
|
||||||
return "MillAbout";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateStateAndPriority() {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
throw new UnsupportedOperationException("Unimplemented method 'updateStateAndPriority'");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getCurrentStateName() {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
throw new UnsupportedOperationException("Unimplemented method 'getCurrentStateName'");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getCurrentStatePriority() {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
throw new UnsupportedOperationException("Unimplemented method 'getCurrentStatePriority'");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,152 +0,0 @@
|
|||||||
package electrosphere.server.ai.creature;
|
|
||||||
|
|
||||||
import electrosphere.engine.Globals;
|
|
||||||
import electrosphere.entity.Entity;
|
|
||||||
import electrosphere.entity.EntityTags;
|
|
||||||
import electrosphere.entity.EntityUtils;
|
|
||||||
import electrosphere.entity.state.attack.ServerAttackTree;
|
|
||||||
import electrosphere.entity.state.movement.groundmove.ClientGroundMovementTree;
|
|
||||||
import electrosphere.entity.state.movement.groundmove.ClientGroundMovementTree.MovementRelativeFacing;
|
|
||||||
import electrosphere.entity.types.creature.CreatureUtils;
|
|
||||||
import electrosphere.server.ai.AITree;
|
|
||||||
import electrosphere.server.datacell.Realm;
|
|
||||||
import electrosphere.server.datacell.utils.DataCellSearchUtils;
|
|
||||||
|
|
||||||
import org.joml.Vector3d;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An ai whose goal is to find a weapon and try to kill nearby enemies
|
|
||||||
*/
|
|
||||||
public class MindlessAttacker implements AITree {
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The parent entity to this ai tree
|
|
||||||
*/
|
|
||||||
Entity parent;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The current target of this tree
|
|
||||||
*/
|
|
||||||
Entity target;
|
|
||||||
|
|
||||||
//aggression ranges
|
|
||||||
float aggroRange = 10.0f;
|
|
||||||
float attackRange = 1.0f;
|
|
||||||
|
|
||||||
//cooldown controls
|
|
||||||
int attackCooldownMax = 250;
|
|
||||||
int attackCooldown = 0;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void simulate(){
|
|
||||||
if(target != null){
|
|
||||||
if(inAttackRange()){
|
|
||||||
attack();
|
|
||||||
} else {
|
|
||||||
if(inAggroRange()){
|
|
||||||
moveToTarget();
|
|
||||||
} else {
|
|
||||||
target = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
searchForTarget();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void attack(){
|
|
||||||
if(attackCooldown == 0){
|
|
||||||
attackCooldown = attackCooldownMax;
|
|
||||||
ServerAttackTree attackTree = CreatureUtils.serverGetAttackTree(parent);
|
|
||||||
attackTree.start();
|
|
||||||
} else {
|
|
||||||
attackCooldown--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void moveToTarget(){
|
|
||||||
Vector3d targetPosition = EntityUtils.getPosition(target);
|
|
||||||
Vector3d characterPosition = EntityUtils.getPosition(parent);
|
|
||||||
Vector3d moveVector = new Vector3d(targetPosition).sub(characterPosition).normalize();
|
|
||||||
CreatureUtils.setFacingVector(parent, new Vector3d((float)moveVector.x,(float)moveVector.y,(float)moveVector.z));
|
|
||||||
ClientGroundMovementTree characterMoveTree = (ClientGroundMovementTree)CreatureUtils.serverGetEntityMovementTree(parent);
|
|
||||||
if(characterMoveTree.getState()==ClientGroundMovementTree.MovementTreeState.IDLE || characterMoveTree.getState()==ClientGroundMovementTree.MovementTreeState.SLOWDOWN){
|
|
||||||
characterMoveTree.start(MovementRelativeFacing.FORWARD);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean inAttackRange(){
|
|
||||||
boolean rVal = false;
|
|
||||||
Vector3d position = EntityUtils.getPosition(parent);
|
|
||||||
Vector3d targetPosition = EntityUtils.getPosition(target);
|
|
||||||
if(new Vector3d(position).distance(targetPosition) < attackRange){
|
|
||||||
rVal = true;
|
|
||||||
}
|
|
||||||
return rVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean inAggroRange(){
|
|
||||||
boolean rVal = false;
|
|
||||||
Vector3d position = EntityUtils.getPosition(parent);
|
|
||||||
Vector3d targetPosition = EntityUtils.getPosition(target);
|
|
||||||
if(new Vector3d(position).distance(targetPosition) < aggroRange){
|
|
||||||
rVal = true;
|
|
||||||
}
|
|
||||||
return rVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void searchForTarget(){
|
|
||||||
Vector3d position = EntityUtils.getPosition(parent);
|
|
||||||
Realm realm = Globals.realmManager.getEntityRealm(parent);
|
|
||||||
for(Entity current : DataCellSearchUtils.getEntitiesWithTagAroundLocation(realm,position,EntityTags.LIFE_STATE)){
|
|
||||||
if(current != parent){
|
|
||||||
Vector3d potentialTargetPosition = EntityUtils.getPosition(current);
|
|
||||||
if(position.distance(potentialTargetPosition) < aggroRange){
|
|
||||||
target = current;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static AITree construct(Entity parent) {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
throw new UnsupportedOperationException("Unimplemented method 'construct'");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateStateAndPriority() {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
throw new UnsupportedOperationException("Unimplemented method 'updateStateAndPriority'");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getCurrentStateName() {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
throw new UnsupportedOperationException("Unimplemented method 'getCurrentStateName'");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getCurrentStatePriority() {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
throw new UnsupportedOperationException("Unimplemented method 'getCurrentStatePriority'");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getTreeName(){
|
|
||||||
return "MindlessAttacker";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,315 +0,0 @@
|
|||||||
package electrosphere.server.ai.creature;
|
|
||||||
|
|
||||||
import electrosphere.engine.Globals;
|
|
||||||
import electrosphere.entity.Entity;
|
|
||||||
import electrosphere.entity.EntityTags;
|
|
||||||
import electrosphere.entity.EntityUtils;
|
|
||||||
import electrosphere.entity.state.attack.ServerAttackTree;
|
|
||||||
import electrosphere.entity.state.equip.ClientEquipState;
|
|
||||||
import electrosphere.entity.state.movement.groundmove.ClientGroundMovementTree;
|
|
||||||
import electrosphere.entity.state.movement.groundmove.ClientGroundMovementTree.MovementRelativeFacing;
|
|
||||||
import electrosphere.entity.types.creature.CreatureUtils;
|
|
||||||
import electrosphere.entity.types.item.ItemUtils;
|
|
||||||
import electrosphere.server.ai.AITree;
|
|
||||||
import electrosphere.server.datacell.Realm;
|
|
||||||
import electrosphere.server.datacell.utils.DataCellSearchUtils;
|
|
||||||
import electrosphere.util.math.MathUtils;
|
|
||||||
|
|
||||||
import org.joml.Quaterniond;
|
|
||||||
import org.joml.Vector3d;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An ai whose goal is to try to kill enemies; however, if it has no access to a weapon it will try to run away
|
|
||||||
*/
|
|
||||||
public class OpportunisticAttacker implements AITree {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The parent of this ai tree
|
|
||||||
*/
|
|
||||||
Entity parent;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The target for this ai
|
|
||||||
*/
|
|
||||||
Entity target;
|
|
||||||
|
|
||||||
float aggroRange = 10.0f;
|
|
||||||
float attackRange = 1.0f;
|
|
||||||
|
|
||||||
float weaponSeekRange = 10.0f;
|
|
||||||
float pickupRange = 2.0f;
|
|
||||||
|
|
||||||
int attackCooldownMax = 250;
|
|
||||||
int attackCooldown = 0;
|
|
||||||
|
|
||||||
enum OpportunisticAttackerGoal {
|
|
||||||
ATTACK,
|
|
||||||
SEEK_WEAPON,
|
|
||||||
FLEE,
|
|
||||||
IDLE,
|
|
||||||
}
|
|
||||||
|
|
||||||
OpportunisticAttackerGoal goal;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getTreeName(){
|
|
||||||
return "OpportunisticAttacker";
|
|
||||||
}
|
|
||||||
|
|
||||||
OpportunisticAttacker(Entity character){
|
|
||||||
this.parent = character;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void attachToCreature(Entity creature){
|
|
||||||
OpportunisticAttacker ai = new OpportunisticAttacker(creature);
|
|
||||||
ai.goal = OpportunisticAttackerGoal.IDLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void simulate(){
|
|
||||||
switch(goal){
|
|
||||||
case ATTACK:
|
|
||||||
if(target != null){
|
|
||||||
if(inAttackRange()){
|
|
||||||
if(hasWeapon()){
|
|
||||||
faceTarget();
|
|
||||||
attack();
|
|
||||||
} else {
|
|
||||||
if(weaponInRange()){
|
|
||||||
setGoal(OpportunisticAttackerGoal.SEEK_WEAPON);
|
|
||||||
} else {
|
|
||||||
setGoal(OpportunisticAttackerGoal.IDLE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if(inAggroRange()){
|
|
||||||
if(hasWeapon()){
|
|
||||||
faceTarget();
|
|
||||||
moveToTarget();
|
|
||||||
} else {
|
|
||||||
if(weaponInRange()){
|
|
||||||
setGoal(OpportunisticAttackerGoal.SEEK_WEAPON);
|
|
||||||
} else {
|
|
||||||
setGoal(OpportunisticAttackerGoal.FLEE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
target = null;
|
|
||||||
setGoal(OpportunisticAttackerGoal.IDLE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setGoal(OpportunisticAttackerGoal.IDLE);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case SEEK_WEAPON:
|
|
||||||
if(target != null){
|
|
||||||
if(weaponInRange()){
|
|
||||||
if(weaponInPickupRange()){
|
|
||||||
pickupWeapon();
|
|
||||||
searchForTarget();
|
|
||||||
if(target != null){
|
|
||||||
setGoal(OpportunisticAttackerGoal.ATTACK);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
moveToTarget();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setGoal(OpportunisticAttackerGoal.FLEE);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if(inAggroRange()){
|
|
||||||
if(hasWeapon()){
|
|
||||||
moveToTarget();
|
|
||||||
} else {
|
|
||||||
if(!weaponInRange()){
|
|
||||||
setGoal(OpportunisticAttackerGoal.FLEE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
target = null;
|
|
||||||
setGoal(OpportunisticAttackerGoal.IDLE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case FLEE:
|
|
||||||
if(target != null){
|
|
||||||
if(weaponInRange()){
|
|
||||||
setGoal(OpportunisticAttackerGoal.SEEK_WEAPON);
|
|
||||||
} else {
|
|
||||||
if(inAggroRange()){
|
|
||||||
//find direction opposite target and move there
|
|
||||||
} else {
|
|
||||||
target = null;
|
|
||||||
setGoal(OpportunisticAttackerGoal.IDLE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setGoal(OpportunisticAttackerGoal.IDLE);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case IDLE:
|
|
||||||
searchForTarget();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void attack(){
|
|
||||||
if(attackCooldown == 0){
|
|
||||||
attackCooldown = attackCooldownMax;
|
|
||||||
ServerAttackTree attackTree = CreatureUtils.serverGetAttackTree(parent);
|
|
||||||
attackTree.start();
|
|
||||||
} else {
|
|
||||||
attackCooldown--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void moveToTarget(){
|
|
||||||
Vector3d targetPosition = EntityUtils.getPosition(target);
|
|
||||||
Vector3d characterPosition = EntityUtils.getPosition(parent);
|
|
||||||
Vector3d moveVector = new Vector3d(targetPosition).sub(characterPosition).normalize();
|
|
||||||
CreatureUtils.setFacingVector(parent, new Vector3d((float)moveVector.x,(float)moveVector.y,(float)moveVector.z));
|
|
||||||
ClientGroundMovementTree characterMoveTree = (ClientGroundMovementTree)CreatureUtils.serverGetEntityMovementTree(parent);
|
|
||||||
if(characterMoveTree.getState()==ClientGroundMovementTree.MovementTreeState.IDLE || characterMoveTree.getState()==ClientGroundMovementTree.MovementTreeState.SLOWDOWN){
|
|
||||||
characterMoveTree.start(MovementRelativeFacing.FORWARD);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean inAttackRange(){
|
|
||||||
boolean rVal = false;
|
|
||||||
Vector3d position = EntityUtils.getPosition(parent);
|
|
||||||
Vector3d targetPosition = EntityUtils.getPosition(target);
|
|
||||||
if(new Vector3d(position).distance(targetPosition) < attackRange){
|
|
||||||
rVal = true;
|
|
||||||
}
|
|
||||||
return rVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean inAggroRange(){
|
|
||||||
boolean rVal = false;
|
|
||||||
Vector3d position = EntityUtils.getPosition(parent);
|
|
||||||
Vector3d targetPosition = EntityUtils.getPosition(target);
|
|
||||||
if(new Vector3d(position).distance(targetPosition) < aggroRange){
|
|
||||||
rVal = true;
|
|
||||||
}
|
|
||||||
return rVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void searchForTarget(){
|
|
||||||
Vector3d position = EntityUtils.getPosition(parent);
|
|
||||||
Realm realm = Globals.realmManager.getEntityRealm(parent);
|
|
||||||
for(Entity current : DataCellSearchUtils.getEntitiesWithTagAroundLocation(realm,position,EntityTags.LIFE_STATE)){
|
|
||||||
if(current != parent){
|
|
||||||
Vector3d potentialTargetPosition = EntityUtils.getPosition(current);
|
|
||||||
if(position.distance(potentialTargetPosition) < aggroRange){
|
|
||||||
target = current;
|
|
||||||
setGoal(OpportunisticAttackerGoal.ATTACK);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean hasWeapon(){
|
|
||||||
boolean rVal = false;
|
|
||||||
if(ClientEquipState.hasEquipState(parent)){
|
|
||||||
//TODO:fix hasWeapon
|
|
||||||
ClientEquipState equipState = ClientEquipState.getEquipState(parent);
|
|
||||||
// if(equipState.hasEquipPrimary()){
|
|
||||||
// rVal = true;
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
return rVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean weaponInRange(){
|
|
||||||
boolean rVal = false;
|
|
||||||
Vector3d position = EntityUtils.getPosition(parent);
|
|
||||||
Realm realm = Globals.realmManager.getEntityRealm(parent);
|
|
||||||
for(Entity current : DataCellSearchUtils.getEntitiesWithTagAroundLocation(realm,position,EntityTags.ITEM)){
|
|
||||||
if(current != parent && ItemUtils.isItem(current) && ItemUtils.isWeapon(current)){
|
|
||||||
Vector3d potentialTargetPosition = EntityUtils.getPosition(current);
|
|
||||||
if(position.distance(potentialTargetPosition) < weaponSeekRange){
|
|
||||||
target = current;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return rVal;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean weaponInPickupRange(){
|
|
||||||
Vector3d position = EntityUtils.getPosition(parent);
|
|
||||||
if(target != null){
|
|
||||||
Vector3d potentialTargetPosition = EntityUtils.getPosition(target);
|
|
||||||
if(position.distance(potentialTargetPosition) < pickupRange){
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void pickupWeapon(){
|
|
||||||
if(ClientEquipState.hasEquipState(parent)){
|
|
||||||
ClientEquipState equipState = ClientEquipState.getEquipState(parent);
|
|
||||||
// if(!equipState.hasEquipPrimary()){
|
|
||||||
// equipState.attemptEquip(target);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void faceTarget(){
|
|
||||||
Vector3d position = EntityUtils.getPosition(parent);
|
|
||||||
Vector3d targetPosition = EntityUtils.getPosition(target);
|
|
||||||
Vector3d movementVector = new Vector3d(targetPosition).sub(position).normalize();
|
|
||||||
Quaterniond movementQuaternion = new Quaterniond().rotationTo(MathUtils.getOriginVector(), new Vector3d(movementVector.x,0,movementVector.z)).normalize();
|
|
||||||
CreatureUtils.setFacingVector(parent, movementVector);
|
|
||||||
EntityUtils.getRotation(parent).set(movementQuaternion);
|
|
||||||
}
|
|
||||||
|
|
||||||
void setGoal(OpportunisticAttackerGoal goal){
|
|
||||||
this.goal = goal;
|
|
||||||
switch(goal){
|
|
||||||
case IDLE:
|
|
||||||
// System.out.println("Set goal idle");
|
|
||||||
break;
|
|
||||||
case FLEE:
|
|
||||||
// System.out.println("Set goal flee");
|
|
||||||
break;
|
|
||||||
case SEEK_WEAPON:
|
|
||||||
// System.out.println("Set goal seek weapon");
|
|
||||||
break;
|
|
||||||
case ATTACK:
|
|
||||||
// System.out.println("Set goal attack");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static AITree construct(Entity parent) {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
throw new UnsupportedOperationException("Unimplemented method 'construct'");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateStateAndPriority() {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
throw new UnsupportedOperationException("Unimplemented method 'updateStateAndPriority'");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getCurrentStateName() {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
throw new UnsupportedOperationException("Unimplemented method 'getCurrentStateName'");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getCurrentStatePriority() {
|
|
||||||
// TODO Auto-generated method stub
|
|
||||||
throw new UnsupportedOperationException("Unimplemented method 'getCurrentStatePriority'");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
package electrosphere.server.ai.nodes.combat;
|
||||||
|
|
||||||
|
import electrosphere.entity.Entity;
|
||||||
|
import electrosphere.entity.state.block.ServerBlockTree;
|
||||||
|
import electrosphere.server.ai.AITreeNode;
|
||||||
|
import electrosphere.server.ai.Blackboard;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to block
|
||||||
|
*/
|
||||||
|
public class BlockNode implements AITreeNode {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The next node to execute
|
||||||
|
*/
|
||||||
|
AITreeNode next = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param next (Optional) The next node to execute
|
||||||
|
*/
|
||||||
|
public BlockNode(AITreeNode next){
|
||||||
|
this.next = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AITreeNode evaluate(Entity entity, Blackboard blackboard) {
|
||||||
|
if(ServerBlockTree.getServerBlockTree(entity) != null){
|
||||||
|
ServerBlockTree serverBlockTree = ServerBlockTree.getServerBlockTree(entity);
|
||||||
|
if(serverBlockTree.isIdle()){
|
||||||
|
serverBlockTree.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,60 @@
|
|||||||
|
package electrosphere.server.ai.nodes.combat;
|
||||||
|
|
||||||
|
import org.joml.Vector3d;
|
||||||
|
|
||||||
|
import electrosphere.engine.Globals;
|
||||||
|
import electrosphere.entity.Entity;
|
||||||
|
import electrosphere.entity.EntityTags;
|
||||||
|
import electrosphere.entity.EntityUtils;
|
||||||
|
import electrosphere.game.data.creature.type.ai.AttackerTreeData;
|
||||||
|
import electrosphere.server.ai.AITreeNode;
|
||||||
|
import electrosphere.server.ai.Blackboard;
|
||||||
|
import electrosphere.server.datacell.Realm;
|
||||||
|
import electrosphere.server.datacell.utils.DataCellSearchUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* BTree node to seek targets for melee attacks
|
||||||
|
*/
|
||||||
|
public class MeleeTargetingNode implements AITreeNode {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The next node to execute
|
||||||
|
*/
|
||||||
|
AITreeNode next = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The attacker tree data
|
||||||
|
*/
|
||||||
|
AttackerTreeData treeData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param next (Optional) The next node to execute
|
||||||
|
*/
|
||||||
|
public MeleeTargetingNode(AITreeNode next){
|
||||||
|
this.next = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AITreeNode evaluate(Entity entity, Blackboard blackboard) {
|
||||||
|
throw new UnsupportedOperationException("Unimplemented!");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Searches for a valid target
|
||||||
|
*/
|
||||||
|
private void searchForTarget(Entity parent){
|
||||||
|
Vector3d position = EntityUtils.getPosition(parent);
|
||||||
|
Realm realm = Globals.realmManager.getEntityRealm(parent);
|
||||||
|
for(Entity current : DataCellSearchUtils.getEntitiesWithTagAroundLocation(realm,position,EntityTags.LIFE_STATE)){
|
||||||
|
if(current != parent){
|
||||||
|
Vector3d potentialTargetPosition = EntityUtils.getPosition(current);
|
||||||
|
if(position.distance(potentialTargetPosition) < treeData.getAggroRange()){
|
||||||
|
// target = current;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,84 @@
|
|||||||
|
package electrosphere.server.ai.nodes.movement;
|
||||||
|
|
||||||
|
import org.joml.Vector3d;
|
||||||
|
|
||||||
|
import electrosphere.entity.Entity;
|
||||||
|
import electrosphere.entity.EntityUtils;
|
||||||
|
import electrosphere.entity.state.movement.groundmove.ClientGroundMovementTree;
|
||||||
|
import electrosphere.entity.state.movement.groundmove.ClientGroundMovementTree.MovementRelativeFacing;
|
||||||
|
import electrosphere.entity.state.movement.groundmove.ServerGroundMovementTree;
|
||||||
|
import electrosphere.entity.types.creature.CreatureUtils;
|
||||||
|
import electrosphere.game.data.creature.type.ai.AttackerTreeData;
|
||||||
|
import electrosphere.server.ai.AITreeNode;
|
||||||
|
import electrosphere.server.ai.Blackboard;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves the ai into optimial positioning for melee combat
|
||||||
|
*/
|
||||||
|
public class MeleeMovement implements AITreeNode {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The next node to execute
|
||||||
|
*/
|
||||||
|
AITreeNode next = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The attacker tree data
|
||||||
|
*/
|
||||||
|
AttackerTreeData treeData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
* @param next (Optional) The next node to execute
|
||||||
|
*/
|
||||||
|
public MeleeMovement(AITreeNode next){
|
||||||
|
this.next = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AITreeNode evaluate(Entity entity, Blackboard blackboard) {
|
||||||
|
throw new UnsupportedOperationException("Unimplemented!");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the target is within the aggression range
|
||||||
|
*/
|
||||||
|
private boolean inAggroRange(Entity parent, Entity target){
|
||||||
|
boolean rVal = false;
|
||||||
|
Vector3d position = EntityUtils.getPosition(parent);
|
||||||
|
Vector3d targetPosition = EntityUtils.getPosition(target);
|
||||||
|
if(new Vector3d(position).distance(targetPosition) < treeData.getAggroRange()){
|
||||||
|
rVal = true;
|
||||||
|
}
|
||||||
|
return rVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the target is within range of an attack animation
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private boolean inAttackRange(Entity parent, Entity target){
|
||||||
|
boolean rVal = false;
|
||||||
|
Vector3d position = EntityUtils.getPosition(parent);
|
||||||
|
Vector3d targetPosition = EntityUtils.getPosition(target);
|
||||||
|
if(new Vector3d(position).distance(targetPosition) < treeData.getAttackRange()){
|
||||||
|
rVal = true;
|
||||||
|
}
|
||||||
|
return rVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to move towards the target
|
||||||
|
*/
|
||||||
|
private void moveToTarget(Entity parent, Entity target){
|
||||||
|
Vector3d targetPosition = EntityUtils.getPosition(target);
|
||||||
|
Vector3d characterPosition = EntityUtils.getPosition(parent);
|
||||||
|
Vector3d moveVector = new Vector3d(targetPosition).sub(characterPosition).normalize();
|
||||||
|
CreatureUtils.setFacingVector(parent, new Vector3d((float)moveVector.x,(float)moveVector.y,(float)moveVector.z));
|
||||||
|
ServerGroundMovementTree characterMoveTree = (ServerGroundMovementTree)CreatureUtils.serverGetEntityMovementTree(parent);
|
||||||
|
if(characterMoveTree.getState()==ClientGroundMovementTree.MovementTreeState.IDLE || characterMoveTree.getState()==ClientGroundMovementTree.MovementTreeState.SLOWDOWN){
|
||||||
|
characterMoveTree.start(MovementRelativeFacing.FORWARD);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
package electrosphere.server.ai.trees.creature;
|
||||||
|
|
||||||
|
import electrosphere.server.ai.AITreeNode;
|
||||||
|
import electrosphere.server.ai.nodes.combat.BlockNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Figures out how if it should attack and how it can, then executes that subtree
|
||||||
|
*/
|
||||||
|
public class AttackerAITree {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of the tree
|
||||||
|
*/
|
||||||
|
public static final String TREE_NAME = "Attacker";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an attacker ai tree
|
||||||
|
* @return The root node of the tree
|
||||||
|
*/
|
||||||
|
public static AITreeNode create(){
|
||||||
|
return new BlockNode(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
package electrosphere.server.ai.trees.test;
|
||||||
|
|
||||||
|
import electrosphere.game.data.creature.type.ai.BlockerTreeData;
|
||||||
|
import electrosphere.server.ai.AITreeNode;
|
||||||
|
import electrosphere.server.ai.nodes.combat.BlockNode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a blocker test tree
|
||||||
|
*/
|
||||||
|
public class BlockerAITree {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Name of the tree
|
||||||
|
*/
|
||||||
|
public static final String TREE_NAME = "Blocker";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a blocker tree
|
||||||
|
* @return The root node of the tree
|
||||||
|
*/
|
||||||
|
public static AITreeNode create(BlockerTreeData data){
|
||||||
|
return new BlockNode(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user