Basic ai framework
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
c776dae868
commit
b10b7e8407
@ -408,7 +408,15 @@
|
|||||||
"cameraViewDirOffsetY" : -0.3,
|
"cameraViewDirOffsetY" : -0.3,
|
||||||
"cameraViewDirOffsetZ" : 0.0,
|
"cameraViewDirOffsetZ" : 0.0,
|
||||||
"firstPersonModelPath" : "Models/creatures/viewmodel.glb"
|
"firstPersonModelPath" : "Models/creatures/viewmodel.glb"
|
||||||
}
|
},
|
||||||
|
"aiTrees" : [
|
||||||
|
{
|
||||||
|
"name" : "Attacker",
|
||||||
|
"aggroRange" : 10,
|
||||||
|
"attackRange" : 2,
|
||||||
|
"stateChangeTimeout" : "240"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"files" : []
|
"files" : []
|
||||||
|
|||||||
@ -25,7 +25,5 @@ export const ENGINE_onInit = () => {
|
|||||||
engine.sceneLoader.engine = engine
|
engine.sceneLoader.engine = engine
|
||||||
engine.hookManager.engine = engine
|
engine.hookManager.engine = engine
|
||||||
|
|
||||||
console.log(Object.keys(engine.classes))
|
|
||||||
|
|
||||||
loggerScripts.INFO('Script Engine Initialized')
|
loggerScripts.INFO('Script Engine Initialized')
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,15 +31,26 @@ public class LevelEditorLoading {
|
|||||||
if(params.length < 1){
|
if(params.length < 1){
|
||||||
throw new IllegalStateException("Trying to load level editor with insufficient params");
|
throw new IllegalStateException("Trying to load level editor with insufficient params");
|
||||||
}
|
}
|
||||||
LevelDescription description = (LevelDescription)params[0];
|
|
||||||
String saveName = description.getName();
|
String saveName = null;
|
||||||
SceneFile sceneFile = description.getSceneFile();
|
SceneFile sceneFile = null;
|
||||||
|
//figure out scene stuff
|
||||||
|
if(params[0] instanceof LevelDescription){
|
||||||
|
//fires when creating a level for the first time
|
||||||
|
LevelDescription description = (LevelDescription)params[0];
|
||||||
|
saveName = description.getName();
|
||||||
|
sceneFile= description.getSceneFile();
|
||||||
|
} else {
|
||||||
|
//fires when subsequently editing
|
||||||
|
saveName = (String)params[0];
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
//Set params we would expect to run with this thread
|
//Set params we would expect to run with this thread
|
||||||
//
|
//
|
||||||
Globals.RUN_CLIENT = true;
|
Globals.RUN_CLIENT = true;
|
||||||
Globals.RUN_SERVER = true;
|
Globals.RUN_SERVER = true;
|
||||||
|
Globals.aiManager.setActive(false);
|
||||||
|
|
||||||
|
|
||||||
Window loadingWindow = (Window)Globals.elementManager.getWindow(WindowStrings.WINDOW_LOADING);
|
Window loadingWindow = (Window)Globals.elementManager.getWindow(WindowStrings.WINDOW_LOADING);
|
||||||
|
|||||||
@ -281,6 +281,7 @@ public class EntityDataStrings {
|
|||||||
AI stuff
|
AI stuff
|
||||||
*/
|
*/
|
||||||
public static final String VIEW_PITCH = "aiViewPitch";
|
public static final String VIEW_PITCH = "aiViewPitch";
|
||||||
|
public static final String AI = "ai";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pose actor
|
* Pose actor
|
||||||
|
|||||||
@ -454,6 +454,14 @@ public class ServerAttackTree implements BehaviorTree {
|
|||||||
parent.putData(EntityDataStrings.ATTACK_MOVE_TYPE_ACTIVE, attackType);
|
parent.putData(EntityDataStrings.ATTACK_MOVE_TYPE_ACTIVE, attackType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the attack tree is active or not
|
||||||
|
* @return true if active, false otherwise
|
||||||
|
*/
|
||||||
|
public boolean isAttacking(){
|
||||||
|
return this.state != AttackTreeState.IDLE;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the current moveset
|
* Gets the current moveset
|
||||||
* @param attackType the attack type
|
* @param attackType the attack type
|
||||||
|
|||||||
@ -103,6 +103,14 @@ public class ServerBlockTree implements BehaviorTree {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets whether the tree is blocking or not
|
||||||
|
* @return true if blocking, false otherwise
|
||||||
|
*/
|
||||||
|
public boolean isBlocking(){
|
||||||
|
return this.state == BlockState.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
|
||||||
|
|||||||
@ -650,6 +650,16 @@ public class CreatureUtils {
|
|||||||
rVal.putData(EntityDataStrings.SERVER_ROTATOR_TREE, rotatorTree);
|
rVal.putData(EntityDataStrings.SERVER_ROTATOR_TREE, rotatorTree);
|
||||||
ServerBehaviorTreeUtils.attachBTreeToEntity(rVal, rotatorTree);
|
ServerBehaviorTreeUtils.attachBTreeToEntity(rVal, rotatorTree);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// AI (This SHOULD only be applied on the server with the way AI architected currently)
|
||||||
|
///
|
||||||
|
///
|
||||||
|
if(rawType.getAITrees() != null){
|
||||||
|
Globals.aiManager.attachAI(rVal, rawType.getAITrees());
|
||||||
|
}
|
||||||
|
|
||||||
//add health system
|
//add health system
|
||||||
rVal.putData(EntityDataStrings.LIFE_STATE, new LifeState(rVal, rawType.getHealthSystem()));
|
rVal.putData(EntityDataStrings.LIFE_STATE, new LifeState(rVal, rawType.getHealthSystem()));
|
||||||
ServerEntityTagUtils.attachTagToEntity(rVal, EntityTags.LIFE_STATE);
|
ServerEntityTagUtils.attachTagToEntity(rVal, EntityTags.LIFE_STATE);
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package electrosphere.game.data.creature.type;
|
|||||||
|
|
||||||
import electrosphere.game.data.collidable.CollidableTemplate;
|
import electrosphere.game.data.collidable.CollidableTemplate;
|
||||||
import electrosphere.game.data.collidable.HitboxData;
|
import electrosphere.game.data.collidable.HitboxData;
|
||||||
|
import electrosphere.game.data.creature.type.ai.AITreeData;
|
||||||
import electrosphere.game.data.creature.type.attack.AttackMove;
|
import electrosphere.game.data.creature.type.attack.AttackMove;
|
||||||
import electrosphere.game.data.creature.type.attack.AttackMoveResolver;
|
import electrosphere.game.data.creature.type.attack.AttackMoveResolver;
|
||||||
import electrosphere.game.data.creature.type.block.BlockSystem;
|
import electrosphere.game.data.creature.type.block.BlockSystem;
|
||||||
@ -16,90 +17,234 @@ import java.util.List;
|
|||||||
* A given type of creature
|
* A given type of creature
|
||||||
*/
|
*/
|
||||||
public class CreatureType {
|
public class CreatureType {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The id of the creature
|
||||||
|
*/
|
||||||
String creatureId;
|
String creatureId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of hitboxes on the creature
|
||||||
|
*/
|
||||||
List<HitboxData> hitboxes;
|
List<HitboxData> hitboxes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Various tokens
|
||||||
|
*/
|
||||||
List<String> tokens;
|
List<String> tokens;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The visual attributes that can be configured on this creature type
|
||||||
|
*/
|
||||||
List<VisualAttribute> visualAttributes;
|
List<VisualAttribute> visualAttributes;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The movement systems available to this creature type
|
||||||
|
*/
|
||||||
List<MovementSystem> movementSystems;
|
List<MovementSystem> movementSystems;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rotator systems available to this creature type
|
||||||
|
*/
|
||||||
RotatorSystem rotatorSystem;
|
RotatorSystem rotatorSystem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of equip points on this creature
|
||||||
|
*/
|
||||||
List<EquipPoint> equipPoints;
|
List<EquipPoint> equipPoints;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The collidable used for this creature type
|
||||||
|
*/
|
||||||
CollidableTemplate collidable;
|
CollidableTemplate collidable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of attack moves available to this creature
|
||||||
|
*/
|
||||||
List<AttackMove> attackMoves;
|
List<AttackMove> attackMoves;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The health system available to this creature
|
||||||
|
*/
|
||||||
HealthSystem healthSystem;
|
HealthSystem healthSystem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The look at system available for this creature
|
||||||
|
*/
|
||||||
LookAtSystem lookAtSystem;
|
LookAtSystem lookAtSystem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The model path for this creature
|
||||||
|
*/
|
||||||
String modelPath;
|
String modelPath;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The view model data for this creature
|
||||||
|
*/
|
||||||
ViewModelData viewModelData;
|
ViewModelData viewModelData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The idle data for this creature
|
||||||
|
*/
|
||||||
IdleData idleData;
|
IdleData idleData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The block system for this creature
|
||||||
|
*/
|
||||||
BlockSystem blockSystem;
|
BlockSystem blockSystem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The configuration data for the ai trees associated with this creature
|
||||||
|
*/
|
||||||
|
List<AITreeData> aiTrees;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The attack move resolver for this creature type
|
||||||
|
*/
|
||||||
AttackMoveResolver attackMoveResolver;
|
AttackMoveResolver attackMoveResolver;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the id for this creature type
|
||||||
|
* @return The id
|
||||||
|
*/
|
||||||
public String getCreatureId() {
|
public String getCreatureId() {
|
||||||
return creatureId;
|
return creatureId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the list of hitboxes for this creature
|
||||||
|
* @return The list of hitbox data
|
||||||
|
*/
|
||||||
public List<HitboxData> getHitboxes() {
|
public List<HitboxData> getHitboxes() {
|
||||||
return hitboxes;
|
return hitboxes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the tokens for this creature
|
||||||
|
* @return The tokens
|
||||||
|
*/
|
||||||
public List<String> getTokens() {
|
public List<String> getTokens() {
|
||||||
return tokens;
|
return tokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the configurable visual attributes for this creature type
|
||||||
|
* @return The list of visual attribute data
|
||||||
|
*/
|
||||||
public List<VisualAttribute> getVisualAttributes(){
|
public List<VisualAttribute> getVisualAttributes(){
|
||||||
return visualAttributes;
|
return visualAttributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the path for the model for this creature
|
||||||
|
* @return The model path
|
||||||
|
*/
|
||||||
public String getModelPath() {
|
public String getModelPath() {
|
||||||
return modelPath;
|
return modelPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the list of data of movement types available to this creature
|
||||||
|
* @return The list of movement type data
|
||||||
|
*/
|
||||||
public List<MovementSystem> getMovementSystems() {
|
public List<MovementSystem> getMovementSystems() {
|
||||||
return movementSystems;
|
return movementSystems;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the list of attack moves available to this creature type
|
||||||
|
* @return The list of attack moves
|
||||||
|
*/
|
||||||
public List<AttackMove> getAttackMoves() {
|
public List<AttackMove> getAttackMoves() {
|
||||||
return attackMoves;
|
return attackMoves;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the health system data for this creature type
|
||||||
|
* @return The health system data
|
||||||
|
*/
|
||||||
public HealthSystem getHealthSystem() {
|
public HealthSystem getHealthSystem() {
|
||||||
return healthSystem;
|
return healthSystem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the collidable data for this creature type
|
||||||
|
* @return The collidable data
|
||||||
|
*/
|
||||||
public CollidableTemplate getCollidable() {
|
public CollidableTemplate getCollidable() {
|
||||||
return collidable;
|
return collidable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the look at system configuration for this creature type
|
||||||
|
* @return The look at system data
|
||||||
|
*/
|
||||||
public LookAtSystem getLookAtSystem() {
|
public LookAtSystem getLookAtSystem() {
|
||||||
return lookAtSystem;
|
return lookAtSystem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the rotator data for this creature type
|
||||||
|
* @return The rotator data
|
||||||
|
*/
|
||||||
public RotatorSystem getRotatorSystem() {
|
public RotatorSystem getRotatorSystem() {
|
||||||
return rotatorSystem;
|
return rotatorSystem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the list of equip points for this creature type
|
||||||
|
* @return The list of equip points
|
||||||
|
*/
|
||||||
public List<EquipPoint> getEquipPoints(){
|
public List<EquipPoint> getEquipPoints(){
|
||||||
return equipPoints;
|
return equipPoints;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the attack move resolver for this creature type
|
||||||
|
* @param resolver The resolver
|
||||||
|
*/
|
||||||
public void setAttackMoveResolver(AttackMoveResolver resolver){
|
public void setAttackMoveResolver(AttackMoveResolver resolver){
|
||||||
attackMoveResolver = resolver;
|
attackMoveResolver = resolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the attack move resolver for this creature type
|
||||||
|
* @return The attack move resolver
|
||||||
|
*/
|
||||||
public AttackMoveResolver getAttackMoveResolver(){
|
public AttackMoveResolver getAttackMoveResolver(){
|
||||||
return attackMoveResolver;
|
return attackMoveResolver;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the first-person view model data for this creature type
|
||||||
|
* @return The first-person view model data
|
||||||
|
*/
|
||||||
public ViewModelData getViewModelData(){
|
public ViewModelData getViewModelData(){
|
||||||
return viewModelData;
|
return viewModelData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the idle data for this creature type (ie animation data)
|
||||||
|
* @return The idle data
|
||||||
|
*/
|
||||||
public IdleData getIdleData(){
|
public IdleData getIdleData(){
|
||||||
return idleData;
|
return idleData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the block system data for this creature type
|
||||||
|
* @return The block system data
|
||||||
|
*/
|
||||||
public BlockSystem getBlockSystem(){
|
public BlockSystem getBlockSystem(){
|
||||||
return blockSystem;
|
return blockSystem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the AI tree data associated with this creature type
|
||||||
|
* @return The list of ai tree data
|
||||||
|
*/
|
||||||
|
public List<AITreeData> getAITrees(){
|
||||||
|
return aiTrees;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,13 @@
|
|||||||
|
package electrosphere.game.data.creature.type.ai;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration data for an ai tree
|
||||||
|
*/
|
||||||
|
public interface AITreeData {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the name of the tree type
|
||||||
|
*/
|
||||||
|
public String getName();
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
package electrosphere.game.data.creature.type.ai;
|
||||||
|
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
|
||||||
|
import com.google.gson.JsonDeserializationContext;
|
||||||
|
import com.google.gson.JsonDeserializer;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonParseException;
|
||||||
|
|
||||||
|
import electrosphere.logger.LoggerInterface;
|
||||||
|
import electrosphere.server.ai.creature.Attacker;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deserializes ai tree data types
|
||||||
|
*/
|
||||||
|
public class AITreeDataSerializer implements JsonDeserializer<AITreeData> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AITreeData deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
|
||||||
|
|
||||||
|
switch(json.getAsJsonObject().get("name").getAsString()){
|
||||||
|
case Attacker.TREE_NAME:
|
||||||
|
return context.deserialize(json, AttackerTreeData.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
LoggerInterface.loggerEngine.ERROR(new IllegalArgumentException("JSON Object provided to AITreeDataSerializer that cannot deserialize into a tree data type cleanly"));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,52 @@
|
|||||||
|
package electrosphere.game.data.creature.type.ai;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration data for an attacker tree
|
||||||
|
*/
|
||||||
|
public class AttackerTreeData implements AITreeData {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The range at which this entity will locate enemies
|
||||||
|
*/
|
||||||
|
float aggroRange;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The range at which attack animations will be attempted
|
||||||
|
*/
|
||||||
|
float attackRange;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number of frames to wait before changing between states
|
||||||
|
*/
|
||||||
|
int stateChangeTimeout;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the range at which this entity will locate enemies
|
||||||
|
* @return The range
|
||||||
|
*/
|
||||||
|
public float getAggroRange(){
|
||||||
|
return aggroRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the range at which attack animations will be attempted
|
||||||
|
* @return The range
|
||||||
|
*/
|
||||||
|
public float getAttackRange(){
|
||||||
|
return attackRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the number of frames to wait before changing between states
|
||||||
|
* @return The number of frames
|
||||||
|
*/
|
||||||
|
public int getStateChangeTimeout(){
|
||||||
|
return stateChangeTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return "Attacker";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -95,10 +95,21 @@ public class MenuGeneratorsLevelEditor {
|
|||||||
loadingThread.start();
|
loadingThread.start();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
//
|
||||||
|
//button (edit Level)
|
||||||
|
//
|
||||||
|
Button editButton = Button.createButton("Edit", () -> {
|
||||||
|
//launch level editor
|
||||||
|
LoadingThread loadingThread = new LoadingThread(LoadingThreadType.LEVEL_EDITOR, saveName);
|
||||||
|
Globals.loadingThreadsList.add(loadingThread);
|
||||||
|
loadingThread.start();
|
||||||
|
});
|
||||||
|
|
||||||
//create row
|
//create row
|
||||||
Div row = Div.createRow(
|
Div row = Div.createRow(
|
||||||
deleteButton,
|
deleteButton,
|
||||||
launchButton
|
launchButton,
|
||||||
|
editButton
|
||||||
);
|
);
|
||||||
row.setMaxHeight(30);
|
row.setMaxHeight(30);
|
||||||
existingLevelColumn.addChild(row);
|
existingLevelColumn.addChild(row);
|
||||||
|
|||||||
@ -1,10 +1,137 @@
|
|||||||
package electrosphere.server.ai;
|
package electrosphere.server.ai;
|
||||||
|
|
||||||
/**
|
import java.util.LinkedList;
|
||||||
* An ai
|
import java.util.List;
|
||||||
*/
|
|
||||||
public abstract class AI {
|
|
||||||
|
|
||||||
public abstract void simulate();
|
import org.joml.Quaterniond;
|
||||||
|
import org.joml.Vector3d;
|
||||||
|
|
||||||
|
import electrosphere.entity.Entity;
|
||||||
|
import electrosphere.entity.EntityDataStrings;
|
||||||
|
import electrosphere.entity.EntityUtils;
|
||||||
|
import electrosphere.entity.types.creature.CreatureUtils;
|
||||||
|
import electrosphere.game.data.creature.type.ai.AITreeData;
|
||||||
|
import electrosphere.game.data.creature.type.ai.AttackerTreeData;
|
||||||
|
import electrosphere.logger.LoggerInterface;
|
||||||
|
import electrosphere.server.ai.creature.Attacker;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A collection of AITrees that are attached to a single entity.
|
||||||
|
* Maintains which
|
||||||
|
*/
|
||||||
|
public class AI {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The error checking threshold for ai mutation
|
||||||
|
*/
|
||||||
|
static final float ERROR_CHECK_ROT_THRESHOLD = 0.999f;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The entity this ai is associated with
|
||||||
|
*/
|
||||||
|
Entity parent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of trees for this ai in particular
|
||||||
|
*/
|
||||||
|
List<AITree> trees = new LinkedList<AITree>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an AI from a list of trees that should be present on the ai
|
||||||
|
* @param treeData The list of data on trees to be provided
|
||||||
|
* @return The AI
|
||||||
|
*/
|
||||||
|
protected static AI constructAI(Entity parent, List<AITreeData> treeData){
|
||||||
|
AI rVal = new AI(parent);
|
||||||
|
|
||||||
|
//attach all trees
|
||||||
|
for(AITreeData aiData : treeData){
|
||||||
|
switch(aiData.getName()){
|
||||||
|
case Attacker.TREE_NAME:
|
||||||
|
rVal.trees.add(Attacker.construct(parent, (AttackerTreeData) aiData));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private constructor
|
||||||
|
*/
|
||||||
|
private AI(Entity parent){
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simulates a single frame of this ai. Only runs simulation logic for the highest priority tree
|
||||||
|
*/
|
||||||
|
protected void simulate(){
|
||||||
|
//
|
||||||
|
//Check basic values of entity to make sure the ai trees aren't changing state when updating state and priority
|
||||||
|
Vector3d currentPos = new Vector3d(EntityUtils.getPosition(parent));
|
||||||
|
Quaterniond currentRot = new Quaterniond(EntityUtils.getRotation(parent));
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
//Update state and find the highest priority tree
|
||||||
|
AITree currentTree = null;
|
||||||
|
for(AITree tree : trees){
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the ai key on the entity
|
||||||
|
* @param entity The entity
|
||||||
|
* @param ai The ai
|
||||||
|
*/
|
||||||
|
public static void setAI(Entity entity, AI ai){
|
||||||
|
entity.putData(EntityDataStrings.AI, ai);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the ai on the key of the entity
|
||||||
|
* @param entity The entity
|
||||||
|
* @return The AI if it exists, null otherwise
|
||||||
|
*/
|
||||||
|
public static AI getAI(Entity entity){
|
||||||
|
if(entity.containsKey(EntityDataStrings.AI)){
|
||||||
|
return (AI)entity.getData(EntityDataStrings.AI);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,35 +1,97 @@
|
|||||||
package electrosphere.server.ai;
|
package electrosphere.server.ai;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import electrosphere.entity.Entity;
|
||||||
|
import electrosphere.game.data.creature.type.ai.AITreeData;
|
||||||
|
import electrosphere.logger.LoggerInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Server manager for all entity AIs
|
* Server manager for all entity AIs
|
||||||
*/
|
*/
|
||||||
public class AIManager {
|
public class AIManager {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of ais
|
||||||
|
*/
|
||||||
List<AI> aiList = new LinkedList<AI>();
|
List<AI> aiList = new LinkedList<AI>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The map of ai to associated entity
|
||||||
|
*/
|
||||||
|
Map<AI,Entity> aiEntityMap = new HashMap<AI,Entity>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The map of entity to associated ai
|
||||||
|
*/
|
||||||
|
Map<Entity,AI> entityAIMap = new HashMap<Entity,AI>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controls whether the ai manager should simulate each frame or not
|
||||||
|
*/
|
||||||
|
boolean active = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor
|
||||||
|
*/
|
||||||
public AIManager(){
|
public AIManager(){
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simulates all AIs currently available
|
||||||
|
*/
|
||||||
public void simulate(){
|
public void simulate(){
|
||||||
for(AI ai : aiList){
|
if(isActive()){
|
||||||
ai.simulate();
|
for(AI ai : aiList){
|
||||||
|
ai.simulate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerAI(AI ai){
|
/**
|
||||||
if(!aiList.contains(ai)){
|
* Sets the active status of the ai manager
|
||||||
aiList.add(ai);
|
* @param isActive true to simulate ai each frame, false otherwise
|
||||||
}
|
*/
|
||||||
|
public void setActive(boolean isActive){
|
||||||
|
this.active = isActive;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deregisterAI(AI ai){
|
/**
|
||||||
if(aiList.contains(ai)){
|
* Gets whether the ai manager is active or not
|
||||||
aiList.remove(ai);
|
* @return true if simulating each frame, false otherwise
|
||||||
|
*/
|
||||||
|
public boolean isActive(){
|
||||||
|
return active;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attaches an AI to an entity
|
||||||
|
* @param entity The entity
|
||||||
|
* @param treeData The list of data on trees to be provided
|
||||||
|
*/
|
||||||
|
public void attachAI(Entity entity, List<AITreeData> treeData){
|
||||||
|
if(entity == null){
|
||||||
|
LoggerInterface.loggerEngine.ERROR(new IllegalArgumentException("Entity provided is null!"));
|
||||||
}
|
}
|
||||||
|
AI ai = AI.constructAI(entity, treeData);
|
||||||
|
aiList.add(ai);
|
||||||
|
entityAIMap.put(entity,ai);
|
||||||
|
aiEntityMap.put(ai,entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the ai for this entity from the manager
|
||||||
|
* @param entity The entity
|
||||||
|
*/
|
||||||
|
public void removeAI(Entity entity){
|
||||||
|
AI targetAI = entityAIMap.get(entity);
|
||||||
|
aiList.remove(targetAI);
|
||||||
|
aiEntityMap.remove(targetAI);
|
||||||
|
entityAIMap.remove(entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
40
src/main/java/electrosphere/server/ai/AITree.java
Normal file
40
src/main/java/electrosphere/server/ai/AITree.java
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
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();
|
||||||
|
|
||||||
|
}
|
||||||
344
src/main/java/electrosphere/server/ai/creature/Attacker.java
Normal file
344
src/main/java/electrosphere/server/ai/creature/Attacker.java
Normal file
@ -0,0 +1,344 @@
|
|||||||
|
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,12 +1,11 @@
|
|||||||
package electrosphere.server.ai.creature;
|
package electrosphere.server.ai.creature;
|
||||||
|
|
||||||
import electrosphere.engine.Globals;
|
|
||||||
import electrosphere.entity.Entity;
|
import electrosphere.entity.Entity;
|
||||||
import electrosphere.entity.EntityUtils;
|
import electrosphere.entity.EntityUtils;
|
||||||
import electrosphere.entity.state.movement.groundmove.ClientGroundMovementTree;
|
import electrosphere.entity.state.movement.groundmove.ClientGroundMovementTree;
|
||||||
import electrosphere.entity.state.movement.groundmove.ClientGroundMovementTree.MovementRelativeFacing;
|
import electrosphere.entity.state.movement.groundmove.ClientGroundMovementTree.MovementRelativeFacing;
|
||||||
import electrosphere.entity.types.creature.CreatureUtils;
|
import electrosphere.entity.types.creature.CreatureUtils;
|
||||||
import electrosphere.server.ai.AI;
|
import electrosphere.server.ai.AITree;
|
||||||
|
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import org.joml.Vector3d;
|
import org.joml.Vector3d;
|
||||||
@ -14,16 +13,10 @@ import org.joml.Vector3d;
|
|||||||
/**
|
/**
|
||||||
* An ai whose goal is to wander sporadically
|
* An ai whose goal is to wander sporadically
|
||||||
*/
|
*/
|
||||||
public class MillAbout extends AI {
|
public class MillAbout implements AITree {
|
||||||
|
|
||||||
Entity character;
|
Entity character;
|
||||||
|
|
||||||
// float aggroRange = 10.0f;
|
|
||||||
// float attackRange = 1.0f;
|
|
||||||
//
|
|
||||||
// int attackCooldownMax = 250;
|
|
||||||
// int attackCooldown = 0;
|
|
||||||
|
|
||||||
boolean millAbout = true;
|
boolean millAbout = true;
|
||||||
int millMoveTimer = 0;
|
int millMoveTimer = 0;
|
||||||
int millMoveTimerTimeout = 100;
|
int millMoveTimerTimeout = 100;
|
||||||
@ -34,15 +27,10 @@ public class MillAbout extends AI {
|
|||||||
boolean moveToTarget = false;
|
boolean moveToTarget = false;
|
||||||
Vector3d moveTargetPosition;
|
Vector3d moveTargetPosition;
|
||||||
|
|
||||||
public MillAbout(Entity character){
|
private MillAbout(Entity character){
|
||||||
this.character = character;
|
this.character = character;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void attachToCreature(Entity creature){
|
|
||||||
MillAbout ai = new MillAbout(creature);
|
|
||||||
Globals.aiManager.registerAI(ai);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -104,59 +92,32 @@ public class MillAbout extends AI {
|
|||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
// void attack(){
|
public static AITree construct(Entity parent) {
|
||||||
// if(attackCooldown == 0){
|
MillAbout rVal = new MillAbout(parent);
|
||||||
// attackCooldown = attackCooldownMax;
|
return rVal;
|
||||||
// AttackTree attackTree = CreatureUtils.getAttackTree(character);
|
}
|
||||||
// attackTree.start(EntityDataStrings.ATTACK_MOVE_TYPE_MELEE_SWING_ONE_HAND);
|
|
||||||
// } else {
|
|
||||||
// attackCooldown--;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// void moveToTarget(){
|
@Override
|
||||||
// Vector3d targetPosition = EntityUtils.getPosition(target);
|
public String getTreeName() {
|
||||||
// Vector3d characterPosition = EntityUtils.getPosition(character);
|
return "MillAbout";
|
||||||
// Vector3d moveVector = new Vector3d(targetPosition).sub(characterPosition).normalize();
|
}
|
||||||
// CreatureUtils.setMovementVector(character, new Vector3f((float)moveVector.x,(float)moveVector.y,(float)moveVector.z));
|
|
||||||
// GroundMovementTree characterMoveTree = CreatureUtils.getEntityMovementTree(character);
|
|
||||||
// if(characterMoveTree.getState()==GroundMovementTree.MovementTreeState.IDLE || characterMoveTree.getState()==GroundMovementTree.MovementTreeState.SLOWDOWN){
|
|
||||||
// characterMoveTree.start();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// boolean inAttackRange(){
|
@Override
|
||||||
// boolean rVal = false;
|
public void updateStateAndPriority() {
|
||||||
// Vector3d position = EntityUtils.getPosition(character);
|
// TODO Auto-generated method stub
|
||||||
// Vector3d targetPosition = EntityUtils.getPosition(target);
|
throw new UnsupportedOperationException("Unimplemented method 'updateStateAndPriority'");
|
||||||
// if(new Vector3d(position).distance(targetPosition) < attackRange){
|
}
|
||||||
// rVal = true;
|
|
||||||
// }
|
|
||||||
// return rVal;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// boolean inAggroRange(){
|
@Override
|
||||||
// boolean rVal = false;
|
public String getCurrentStateName() {
|
||||||
// Vector3d position = EntityUtils.getPosition(character);
|
// TODO Auto-generated method stub
|
||||||
// Vector3d targetPosition = EntityUtils.getPosition(target);
|
throw new UnsupportedOperationException("Unimplemented method 'getCurrentStateName'");
|
||||||
// if(new Vector3d(position).distance(targetPosition) < aggroRange){
|
}
|
||||||
// rVal = true;
|
|
||||||
// }
|
|
||||||
// return rVal;
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
@Override
|
||||||
// void searchForTarget(){
|
public int getCurrentStatePriority() {
|
||||||
// Vector3d position = EntityUtils.getPosition(character);
|
// TODO Auto-generated method stub
|
||||||
// for(Entity current : Globals.entityManager.getLifeStateEntities()){
|
throw new UnsupportedOperationException("Unimplemented method 'getCurrentStatePriority'");
|
||||||
// if(current != character){
|
}
|
||||||
// Vector3d potentialTargetPosition = EntityUtils.getPosition(current);
|
|
||||||
// if(position.distance(potentialTargetPosition) < aggroRange){
|
|
||||||
// target = current;
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,7 @@ import electrosphere.entity.state.attack.ServerAttackTree;
|
|||||||
import electrosphere.entity.state.movement.groundmove.ClientGroundMovementTree;
|
import electrosphere.entity.state.movement.groundmove.ClientGroundMovementTree;
|
||||||
import electrosphere.entity.state.movement.groundmove.ClientGroundMovementTree.MovementRelativeFacing;
|
import electrosphere.entity.state.movement.groundmove.ClientGroundMovementTree.MovementRelativeFacing;
|
||||||
import electrosphere.entity.types.creature.CreatureUtils;
|
import electrosphere.entity.types.creature.CreatureUtils;
|
||||||
import electrosphere.server.ai.AI;
|
import electrosphere.server.ai.AITree;
|
||||||
import electrosphere.server.datacell.Realm;
|
import electrosphere.server.datacell.Realm;
|
||||||
import electrosphere.server.datacell.utils.DataCellSearchUtils;
|
import electrosphere.server.datacell.utils.DataCellSearchUtils;
|
||||||
|
|
||||||
@ -17,27 +17,27 @@ import org.joml.Vector3d;
|
|||||||
/**
|
/**
|
||||||
* An ai whose goal is to find a weapon and try to kill nearby enemies
|
* An ai whose goal is to find a weapon and try to kill nearby enemies
|
||||||
*/
|
*/
|
||||||
public class MindlessAttacker extends AI{
|
public class MindlessAttacker implements AITree {
|
||||||
|
|
||||||
Entity character;
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The parent entity to this ai tree
|
||||||
|
*/
|
||||||
|
Entity parent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current target of this tree
|
||||||
|
*/
|
||||||
Entity target;
|
Entity target;
|
||||||
|
|
||||||
|
//aggression ranges
|
||||||
float aggroRange = 10.0f;
|
float aggroRange = 10.0f;
|
||||||
float attackRange = 1.0f;
|
float attackRange = 1.0f;
|
||||||
|
|
||||||
|
//cooldown controls
|
||||||
int attackCooldownMax = 250;
|
int attackCooldownMax = 250;
|
||||||
int attackCooldown = 0;
|
int attackCooldown = 0;
|
||||||
|
|
||||||
public MindlessAttacker(Entity character){
|
|
||||||
this.character = character;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void attachToCreature(Entity creature){
|
|
||||||
MindlessAttacker ai = new MindlessAttacker(creature);
|
|
||||||
Globals.aiManager.registerAI(ai);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -60,7 +60,7 @@ public class MindlessAttacker extends AI{
|
|||||||
void attack(){
|
void attack(){
|
||||||
if(attackCooldown == 0){
|
if(attackCooldown == 0){
|
||||||
attackCooldown = attackCooldownMax;
|
attackCooldown = attackCooldownMax;
|
||||||
ServerAttackTree attackTree = CreatureUtils.serverGetAttackTree(character);
|
ServerAttackTree attackTree = CreatureUtils.serverGetAttackTree(parent);
|
||||||
attackTree.start();
|
attackTree.start();
|
||||||
} else {
|
} else {
|
||||||
attackCooldown--;
|
attackCooldown--;
|
||||||
@ -69,10 +69,10 @@ public class MindlessAttacker extends AI{
|
|||||||
|
|
||||||
void moveToTarget(){
|
void moveToTarget(){
|
||||||
Vector3d targetPosition = EntityUtils.getPosition(target);
|
Vector3d targetPosition = EntityUtils.getPosition(target);
|
||||||
Vector3d characterPosition = EntityUtils.getPosition(character);
|
Vector3d characterPosition = EntityUtils.getPosition(parent);
|
||||||
Vector3d moveVector = new Vector3d(targetPosition).sub(characterPosition).normalize();
|
Vector3d moveVector = new Vector3d(targetPosition).sub(characterPosition).normalize();
|
||||||
CreatureUtils.setFacingVector(character, new Vector3d((float)moveVector.x,(float)moveVector.y,(float)moveVector.z));
|
CreatureUtils.setFacingVector(parent, new Vector3d((float)moveVector.x,(float)moveVector.y,(float)moveVector.z));
|
||||||
ClientGroundMovementTree characterMoveTree = (ClientGroundMovementTree)CreatureUtils.serverGetEntityMovementTree(character);
|
ClientGroundMovementTree characterMoveTree = (ClientGroundMovementTree)CreatureUtils.serverGetEntityMovementTree(parent);
|
||||||
if(characterMoveTree.getState()==ClientGroundMovementTree.MovementTreeState.IDLE || characterMoveTree.getState()==ClientGroundMovementTree.MovementTreeState.SLOWDOWN){
|
if(characterMoveTree.getState()==ClientGroundMovementTree.MovementTreeState.IDLE || characterMoveTree.getState()==ClientGroundMovementTree.MovementTreeState.SLOWDOWN){
|
||||||
characterMoveTree.start(MovementRelativeFacing.FORWARD);
|
characterMoveTree.start(MovementRelativeFacing.FORWARD);
|
||||||
}
|
}
|
||||||
@ -80,7 +80,7 @@ public class MindlessAttacker extends AI{
|
|||||||
|
|
||||||
boolean inAttackRange(){
|
boolean inAttackRange(){
|
||||||
boolean rVal = false;
|
boolean rVal = false;
|
||||||
Vector3d position = EntityUtils.getPosition(character);
|
Vector3d position = EntityUtils.getPosition(parent);
|
||||||
Vector3d targetPosition = EntityUtils.getPosition(target);
|
Vector3d targetPosition = EntityUtils.getPosition(target);
|
||||||
if(new Vector3d(position).distance(targetPosition) < attackRange){
|
if(new Vector3d(position).distance(targetPosition) < attackRange){
|
||||||
rVal = true;
|
rVal = true;
|
||||||
@ -90,7 +90,7 @@ public class MindlessAttacker extends AI{
|
|||||||
|
|
||||||
boolean inAggroRange(){
|
boolean inAggroRange(){
|
||||||
boolean rVal = false;
|
boolean rVal = false;
|
||||||
Vector3d position = EntityUtils.getPosition(character);
|
Vector3d position = EntityUtils.getPosition(parent);
|
||||||
Vector3d targetPosition = EntityUtils.getPosition(target);
|
Vector3d targetPosition = EntityUtils.getPosition(target);
|
||||||
if(new Vector3d(position).distance(targetPosition) < aggroRange){
|
if(new Vector3d(position).distance(targetPosition) < aggroRange){
|
||||||
rVal = true;
|
rVal = true;
|
||||||
@ -100,10 +100,10 @@ public class MindlessAttacker extends AI{
|
|||||||
|
|
||||||
|
|
||||||
void searchForTarget(){
|
void searchForTarget(){
|
||||||
Vector3d position = EntityUtils.getPosition(character);
|
Vector3d position = EntityUtils.getPosition(parent);
|
||||||
Realm realm = Globals.realmManager.getEntityRealm(character);
|
Realm realm = Globals.realmManager.getEntityRealm(parent);
|
||||||
for(Entity current : DataCellSearchUtils.getEntitiesWithTagAroundLocation(realm,position,EntityTags.LIFE_STATE)){
|
for(Entity current : DataCellSearchUtils.getEntitiesWithTagAroundLocation(realm,position,EntityTags.LIFE_STATE)){
|
||||||
if(current != character){
|
if(current != parent){
|
||||||
Vector3d potentialTargetPosition = EntityUtils.getPosition(current);
|
Vector3d potentialTargetPosition = EntityUtils.getPosition(current);
|
||||||
if(position.distance(potentialTargetPosition) < aggroRange){
|
if(position.distance(potentialTargetPosition) < aggroRange){
|
||||||
target = current;
|
target = current;
|
||||||
@ -113,4 +113,40 @@ public class MindlessAttacker extends AI{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import electrosphere.entity.state.movement.groundmove.ClientGroundMovementTree;
|
|||||||
import electrosphere.entity.state.movement.groundmove.ClientGroundMovementTree.MovementRelativeFacing;
|
import electrosphere.entity.state.movement.groundmove.ClientGroundMovementTree.MovementRelativeFacing;
|
||||||
import electrosphere.entity.types.creature.CreatureUtils;
|
import electrosphere.entity.types.creature.CreatureUtils;
|
||||||
import electrosphere.entity.types.item.ItemUtils;
|
import electrosphere.entity.types.item.ItemUtils;
|
||||||
import electrosphere.server.ai.AI;
|
import electrosphere.server.ai.AITree;
|
||||||
import electrosphere.server.datacell.Realm;
|
import electrosphere.server.datacell.Realm;
|
||||||
import electrosphere.server.datacell.utils.DataCellSearchUtils;
|
import electrosphere.server.datacell.utils.DataCellSearchUtils;
|
||||||
import electrosphere.util.MathUtils;
|
import electrosphere.util.MathUtils;
|
||||||
@ -21,11 +21,16 @@ 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
|
* 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 extends AI {
|
public class OpportunisticAttacker implements AITree {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The parent of this ai tree
|
||||||
|
*/
|
||||||
|
Entity parent;
|
||||||
|
|
||||||
Entity character;
|
/**
|
||||||
|
* The target for this ai
|
||||||
|
*/
|
||||||
Entity target;
|
Entity target;
|
||||||
|
|
||||||
float aggroRange = 10.0f;
|
float aggroRange = 10.0f;
|
||||||
@ -46,14 +51,18 @@ public class OpportunisticAttacker extends AI {
|
|||||||
|
|
||||||
OpportunisticAttackerGoal goal;
|
OpportunisticAttackerGoal goal;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTreeName(){
|
||||||
|
return "OpportunisticAttacker";
|
||||||
|
}
|
||||||
|
|
||||||
OpportunisticAttacker(Entity character){
|
OpportunisticAttacker(Entity character){
|
||||||
this.character = character;
|
this.parent = character;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void attachToCreature(Entity creature){
|
public static void attachToCreature(Entity creature){
|
||||||
OpportunisticAttacker ai = new OpportunisticAttacker(creature);
|
OpportunisticAttacker ai = new OpportunisticAttacker(creature);
|
||||||
ai.goal = OpportunisticAttackerGoal.IDLE;
|
ai.goal = OpportunisticAttackerGoal.IDLE;
|
||||||
Globals.aiManager.registerAI(ai);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -151,7 +160,7 @@ public class OpportunisticAttacker extends AI {
|
|||||||
void attack(){
|
void attack(){
|
||||||
if(attackCooldown == 0){
|
if(attackCooldown == 0){
|
||||||
attackCooldown = attackCooldownMax;
|
attackCooldown = attackCooldownMax;
|
||||||
ServerAttackTree attackTree = CreatureUtils.serverGetAttackTree(character);
|
ServerAttackTree attackTree = CreatureUtils.serverGetAttackTree(parent);
|
||||||
attackTree.start();
|
attackTree.start();
|
||||||
} else {
|
} else {
|
||||||
attackCooldown--;
|
attackCooldown--;
|
||||||
@ -160,10 +169,10 @@ public class OpportunisticAttacker extends AI {
|
|||||||
|
|
||||||
void moveToTarget(){
|
void moveToTarget(){
|
||||||
Vector3d targetPosition = EntityUtils.getPosition(target);
|
Vector3d targetPosition = EntityUtils.getPosition(target);
|
||||||
Vector3d characterPosition = EntityUtils.getPosition(character);
|
Vector3d characterPosition = EntityUtils.getPosition(parent);
|
||||||
Vector3d moveVector = new Vector3d(targetPosition).sub(characterPosition).normalize();
|
Vector3d moveVector = new Vector3d(targetPosition).sub(characterPosition).normalize();
|
||||||
CreatureUtils.setFacingVector(character, new Vector3d((float)moveVector.x,(float)moveVector.y,(float)moveVector.z));
|
CreatureUtils.setFacingVector(parent, new Vector3d((float)moveVector.x,(float)moveVector.y,(float)moveVector.z));
|
||||||
ClientGroundMovementTree characterMoveTree = (ClientGroundMovementTree)CreatureUtils.serverGetEntityMovementTree(character);
|
ClientGroundMovementTree characterMoveTree = (ClientGroundMovementTree)CreatureUtils.serverGetEntityMovementTree(parent);
|
||||||
if(characterMoveTree.getState()==ClientGroundMovementTree.MovementTreeState.IDLE || characterMoveTree.getState()==ClientGroundMovementTree.MovementTreeState.SLOWDOWN){
|
if(characterMoveTree.getState()==ClientGroundMovementTree.MovementTreeState.IDLE || characterMoveTree.getState()==ClientGroundMovementTree.MovementTreeState.SLOWDOWN){
|
||||||
characterMoveTree.start(MovementRelativeFacing.FORWARD);
|
characterMoveTree.start(MovementRelativeFacing.FORWARD);
|
||||||
}
|
}
|
||||||
@ -171,7 +180,7 @@ public class OpportunisticAttacker extends AI {
|
|||||||
|
|
||||||
boolean inAttackRange(){
|
boolean inAttackRange(){
|
||||||
boolean rVal = false;
|
boolean rVal = false;
|
||||||
Vector3d position = EntityUtils.getPosition(character);
|
Vector3d position = EntityUtils.getPosition(parent);
|
||||||
Vector3d targetPosition = EntityUtils.getPosition(target);
|
Vector3d targetPosition = EntityUtils.getPosition(target);
|
||||||
if(new Vector3d(position).distance(targetPosition) < attackRange){
|
if(new Vector3d(position).distance(targetPosition) < attackRange){
|
||||||
rVal = true;
|
rVal = true;
|
||||||
@ -181,7 +190,7 @@ public class OpportunisticAttacker extends AI {
|
|||||||
|
|
||||||
boolean inAggroRange(){
|
boolean inAggroRange(){
|
||||||
boolean rVal = false;
|
boolean rVal = false;
|
||||||
Vector3d position = EntityUtils.getPosition(character);
|
Vector3d position = EntityUtils.getPosition(parent);
|
||||||
Vector3d targetPosition = EntityUtils.getPosition(target);
|
Vector3d targetPosition = EntityUtils.getPosition(target);
|
||||||
if(new Vector3d(position).distance(targetPosition) < aggroRange){
|
if(new Vector3d(position).distance(targetPosition) < aggroRange){
|
||||||
rVal = true;
|
rVal = true;
|
||||||
@ -191,10 +200,10 @@ public class OpportunisticAttacker extends AI {
|
|||||||
|
|
||||||
|
|
||||||
void searchForTarget(){
|
void searchForTarget(){
|
||||||
Vector3d position = EntityUtils.getPosition(character);
|
Vector3d position = EntityUtils.getPosition(parent);
|
||||||
Realm realm = Globals.realmManager.getEntityRealm(character);
|
Realm realm = Globals.realmManager.getEntityRealm(parent);
|
||||||
for(Entity current : DataCellSearchUtils.getEntitiesWithTagAroundLocation(realm,position,EntityTags.LIFE_STATE)){
|
for(Entity current : DataCellSearchUtils.getEntitiesWithTagAroundLocation(realm,position,EntityTags.LIFE_STATE)){
|
||||||
if(current != character){
|
if(current != parent){
|
||||||
Vector3d potentialTargetPosition = EntityUtils.getPosition(current);
|
Vector3d potentialTargetPosition = EntityUtils.getPosition(current);
|
||||||
if(position.distance(potentialTargetPosition) < aggroRange){
|
if(position.distance(potentialTargetPosition) < aggroRange){
|
||||||
target = current;
|
target = current;
|
||||||
@ -207,9 +216,9 @@ public class OpportunisticAttacker extends AI {
|
|||||||
|
|
||||||
boolean hasWeapon(){
|
boolean hasWeapon(){
|
||||||
boolean rVal = false;
|
boolean rVal = false;
|
||||||
if(ClientEquipState.hasEquipState(character)){
|
if(ClientEquipState.hasEquipState(parent)){
|
||||||
//TODO:fix hasWeapon
|
//TODO:fix hasWeapon
|
||||||
ClientEquipState equipState = ClientEquipState.getEquipState(character);
|
ClientEquipState equipState = ClientEquipState.getEquipState(parent);
|
||||||
// if(equipState.hasEquipPrimary()){
|
// if(equipState.hasEquipPrimary()){
|
||||||
// rVal = true;
|
// rVal = true;
|
||||||
// }
|
// }
|
||||||
@ -219,10 +228,10 @@ public class OpportunisticAttacker extends AI {
|
|||||||
|
|
||||||
boolean weaponInRange(){
|
boolean weaponInRange(){
|
||||||
boolean rVal = false;
|
boolean rVal = false;
|
||||||
Vector3d position = EntityUtils.getPosition(character);
|
Vector3d position = EntityUtils.getPosition(parent);
|
||||||
Realm realm = Globals.realmManager.getEntityRealm(character);
|
Realm realm = Globals.realmManager.getEntityRealm(parent);
|
||||||
for(Entity current : DataCellSearchUtils.getEntitiesWithTagAroundLocation(realm,position,EntityTags.ITEM)){
|
for(Entity current : DataCellSearchUtils.getEntitiesWithTagAroundLocation(realm,position,EntityTags.ITEM)){
|
||||||
if(current != character && ItemUtils.isItem(current) && ItemUtils.isWeapon(current)){
|
if(current != parent && ItemUtils.isItem(current) && ItemUtils.isWeapon(current)){
|
||||||
Vector3d potentialTargetPosition = EntityUtils.getPosition(current);
|
Vector3d potentialTargetPosition = EntityUtils.getPosition(current);
|
||||||
if(position.distance(potentialTargetPosition) < weaponSeekRange){
|
if(position.distance(potentialTargetPosition) < weaponSeekRange){
|
||||||
target = current;
|
target = current;
|
||||||
@ -234,7 +243,7 @@ public class OpportunisticAttacker extends AI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
boolean weaponInPickupRange(){
|
boolean weaponInPickupRange(){
|
||||||
Vector3d position = EntityUtils.getPosition(character);
|
Vector3d position = EntityUtils.getPosition(parent);
|
||||||
if(target != null){
|
if(target != null){
|
||||||
Vector3d potentialTargetPosition = EntityUtils.getPosition(target);
|
Vector3d potentialTargetPosition = EntityUtils.getPosition(target);
|
||||||
if(position.distance(potentialTargetPosition) < pickupRange){
|
if(position.distance(potentialTargetPosition) < pickupRange){
|
||||||
@ -245,8 +254,8 @@ public class OpportunisticAttacker extends AI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void pickupWeapon(){
|
void pickupWeapon(){
|
||||||
if(ClientEquipState.hasEquipState(character)){
|
if(ClientEquipState.hasEquipState(parent)){
|
||||||
ClientEquipState equipState = ClientEquipState.getEquipState(character);
|
ClientEquipState equipState = ClientEquipState.getEquipState(parent);
|
||||||
// if(!equipState.hasEquipPrimary()){
|
// if(!equipState.hasEquipPrimary()){
|
||||||
// equipState.attemptEquip(target);
|
// equipState.attemptEquip(target);
|
||||||
// }
|
// }
|
||||||
@ -254,12 +263,12 @@ public class OpportunisticAttacker extends AI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void faceTarget(){
|
void faceTarget(){
|
||||||
Vector3d position = EntityUtils.getPosition(character);
|
Vector3d position = EntityUtils.getPosition(parent);
|
||||||
Vector3d targetPosition = EntityUtils.getPosition(target);
|
Vector3d targetPosition = EntityUtils.getPosition(target);
|
||||||
Vector3d movementVector = new Vector3d(targetPosition).sub(position).normalize();
|
Vector3d movementVector = new Vector3d(targetPosition).sub(position).normalize();
|
||||||
Quaterniond movementQuaternion = new Quaterniond().rotationTo(MathUtils.getOriginVector(), new Vector3d(movementVector.x,0,movementVector.z)).normalize();
|
Quaterniond movementQuaternion = new Quaterniond().rotationTo(MathUtils.getOriginVector(), new Vector3d(movementVector.x,0,movementVector.z)).normalize();
|
||||||
CreatureUtils.setFacingVector(character, movementVector);
|
CreatureUtils.setFacingVector(parent, movementVector);
|
||||||
EntityUtils.getRotation(character).set(movementQuaternion);
|
EntityUtils.getRotation(parent).set(movementQuaternion);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setGoal(OpportunisticAttackerGoal goal){
|
void setGoal(OpportunisticAttackerGoal goal){
|
||||||
@ -280,4 +289,27 @@ public class OpportunisticAttacker extends AI {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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'");
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,58 +0,0 @@
|
|||||||
package electrosphere.server.ai.creature.adventurer;
|
|
||||||
|
|
||||||
import org.joml.Vector3d;
|
|
||||||
|
|
||||||
import electrosphere.engine.Globals;
|
|
||||||
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.game.server.town.Town;
|
|
||||||
import electrosphere.server.ai.AI;
|
|
||||||
|
|
||||||
public class SeekTown extends AI {
|
|
||||||
|
|
||||||
Entity character;
|
|
||||||
|
|
||||||
Town target = null;
|
|
||||||
Vector3d targetPos = null;
|
|
||||||
|
|
||||||
public SeekTown(Entity character){
|
|
||||||
this.character = character;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void attachToCreature(Entity creature){
|
|
||||||
SeekTown ai = new SeekTown(creature);
|
|
||||||
Globals.aiManager.registerAI(ai);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void simulate() {
|
|
||||||
// if(target == null){
|
|
||||||
// if(Globals.macroData.getTowns().size() > 0){
|
|
||||||
// target = Globals.macroData.getTowns().get(0);
|
|
||||||
// targetPos = new Vector3d(
|
|
||||||
// Globals.commonWorldData.convertWorldToReal(target.getPositions().get(0).x),
|
|
||||||
// 0,
|
|
||||||
// Globals.commonWorldData.convertWorldToReal(target.getPositions().get(0).y)
|
|
||||||
// );
|
|
||||||
// targetPos.y = Globals.commonWorldData.getElevationAtPoint(targetPos);
|
|
||||||
// System.out.println("Target pos: " + targetPos);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
moveToTarget();
|
|
||||||
}
|
|
||||||
|
|
||||||
void moveToTarget(){
|
|
||||||
Vector3d targetPosition = targetPos;
|
|
||||||
Vector3d characterPosition = EntityUtils.getPosition(character);
|
|
||||||
Vector3d moveVector = new Vector3d(targetPosition).sub(characterPosition).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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -1,51 +0,0 @@
|
|||||||
package electrosphere.server.ai.creature.party;
|
|
||||||
|
|
||||||
import org.joml.Vector3d;
|
|
||||||
|
|
||||||
import electrosphere.engine.Globals;
|
|
||||||
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.AI;
|
|
||||||
|
|
||||||
public class PartyFollower extends AI {
|
|
||||||
|
|
||||||
Entity character;
|
|
||||||
Entity target;
|
|
||||||
double threshold = 0;
|
|
||||||
|
|
||||||
public PartyFollower(Entity character, Entity target, double threshold){
|
|
||||||
this.character = character;
|
|
||||||
this.target = target;
|
|
||||||
this.threshold = threshold;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void attachToCreature(Entity creature, Entity target, double threshold){
|
|
||||||
PartyFollower ai = new PartyFollower(creature, target, threshold);
|
|
||||||
Globals.aiManager.registerAI(ai);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void simulate() {
|
|
||||||
if(target == null){
|
|
||||||
Vector3d characterPosition = EntityUtils.getPosition(character);
|
|
||||||
Vector3d targetPosition = EntityUtils.getPosition(target);
|
|
||||||
if(targetPosition.distance(characterPosition) > threshold){
|
|
||||||
moveToTarget();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void moveToTarget(){
|
|
||||||
Vector3d targetPosition = EntityUtils.getPosition(target);
|
|
||||||
Vector3d characterPosition = EntityUtils.getPosition(character);
|
|
||||||
Vector3d moveVector = new Vector3d(targetPosition).sub(characterPosition).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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -3,6 +3,8 @@ package electrosphere.util;
|
|||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.GsonBuilder;
|
import com.google.gson.GsonBuilder;
|
||||||
|
|
||||||
|
import electrosphere.game.data.creature.type.ai.AITreeData;
|
||||||
|
import electrosphere.game.data.creature.type.ai.AITreeDataSerializer;
|
||||||
import electrosphere.game.data.creature.type.movement.MovementSystem;
|
import electrosphere.game.data.creature.type.movement.MovementSystem;
|
||||||
import electrosphere.game.data.creature.type.movement.MovementSystemSerializer;
|
import electrosphere.game.data.creature.type.movement.MovementSystemSerializer;
|
||||||
import electrosphere.logger.LoggerInterface;
|
import electrosphere.logger.LoggerInterface;
|
||||||
@ -35,6 +37,7 @@ public class FileUtils {
|
|||||||
//init gson
|
//init gson
|
||||||
GsonBuilder gsonBuilder = new GsonBuilder();
|
GsonBuilder gsonBuilder = new GsonBuilder();
|
||||||
gsonBuilder.registerTypeAdapter(MovementSystem.class, new MovementSystemSerializer());
|
gsonBuilder.registerTypeAdapter(MovementSystem.class, new MovementSystemSerializer());
|
||||||
|
gsonBuilder.registerTypeAdapter(AITreeData.class, new AITreeDataSerializer());
|
||||||
gsonBuilder.addDeserializationExclusionStrategy(new AnnotationExclusionStrategy());
|
gsonBuilder.addDeserializationExclusionStrategy(new AnnotationExclusionStrategy());
|
||||||
gsonBuilder.addSerializationExclusionStrategy(new AnnotationExclusionStrategy());
|
gsonBuilder.addSerializationExclusionStrategy(new AnnotationExclusionStrategy());
|
||||||
gson = gsonBuilder.create();
|
gson = gsonBuilder.create();
|
||||||
|
|||||||
10
src/main/java/electrosphere/util/RandomUtils.java
Normal file
10
src/main/java/electrosphere/util/RandomUtils.java
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package electrosphere.util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utilities for random numbers
|
||||||
|
*/
|
||||||
|
public class RandomUtils {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user