Opportunistic Attacker
This commit is contained in:
parent
84534e3ceb
commit
b2fe119348
@ -43,6 +43,7 @@ import electrosphere.renderer.Model;
|
||||
import electrosphere.renderer.RenderUtils;
|
||||
import electrosphere.engine.assetmanager.AssetDataStrings;
|
||||
import electrosphere.game.client.targeting.crosshair.Crosshair;
|
||||
import electrosphere.game.server.ai.creature.OpportunisticAttacker;
|
||||
import electrosphere.game.server.pathfinding.NavMeshPathfinder;
|
||||
import electrosphere.game.server.pathfinding.navmesh.NavCube;
|
||||
import electrosphere.game.server.pathfinding.navmesh.NavMesh;
|
||||
@ -621,7 +622,8 @@ public class LoadingThread extends Thread {
|
||||
// Entity goblinSword = ItemUtils.spawnBasicItem("Katana");
|
||||
// AttachUtils.attachEntityToEntityAtBone(goblin, goblinSword, "Bone.031");
|
||||
//attach ai to evil goblin
|
||||
MindlessAttacker.attachToCreature(goblin);
|
||||
// MindlessAttacker.attachToCreature(goblin);
|
||||
OpportunisticAttacker.attachToCreature(goblin);
|
||||
|
||||
// goblin = CreatureUtils.spawnBasicCreature("Goblin");
|
||||
// CollisionObjUtils.positionCharacter(goblin, new Vector3f(3, 0, 4));
|
||||
|
||||
@ -134,6 +134,7 @@ public class EntityDataStrings {
|
||||
*/
|
||||
public static final String ITEM_IS_ITEM = "itemIsItem";
|
||||
public static final String ITEM_TYPE = "itemType";
|
||||
public static final String ITEM_IS_WEAPON = "itemIsWeapon";
|
||||
|
||||
|
||||
/*
|
||||
|
||||
@ -89,6 +89,9 @@ public class ItemUtils {
|
||||
case "TARGETABLE":
|
||||
Globals.entityManager.registerTargetableEntity(rVal);
|
||||
break;
|
||||
case "WEAPON":
|
||||
rVal.putData(EntityDataStrings.ITEM_IS_WEAPON, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
rVal.putData(EntityDataStrings.DRAW_CAST_SHADOW, true);
|
||||
@ -160,4 +163,8 @@ public class ItemUtils {
|
||||
public static String getType(Entity item){
|
||||
return (String)item.getData(EntityDataStrings.ITEM_TYPE);
|
||||
}
|
||||
|
||||
public static boolean isWeapon(Entity item){
|
||||
return item.getDataKeys().contains(EntityDataStrings.ITEM_IS_WEAPON);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,264 @@
|
||||
package electrosphere.game.server.ai.creature;
|
||||
|
||||
import electrosphere.entity.Entity;
|
||||
import electrosphere.entity.EntityDataStrings;
|
||||
import electrosphere.entity.EntityUtils;
|
||||
import electrosphere.entity.state.AttackTree;
|
||||
import electrosphere.entity.state.equip.EquipState;
|
||||
import electrosphere.entity.state.movement.GroundMovementTree;
|
||||
import electrosphere.entity.types.creature.CreatureUtils;
|
||||
import electrosphere.entity.types.item.ItemUtils;
|
||||
import electrosphere.game.server.ai.AI;
|
||||
import electrosphere.main.Globals;
|
||||
import org.joml.Vector3d;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author amaterasu
|
||||
*/
|
||||
public class OpportunisticAttacker extends AI {
|
||||
|
||||
|
||||
Entity character;
|
||||
|
||||
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;
|
||||
|
||||
OpportunisticAttacker(Entity character){
|
||||
this.character = character;
|
||||
}
|
||||
|
||||
public static void attachToCreature(Entity creature){
|
||||
OpportunisticAttacker ai = new OpportunisticAttacker(creature);
|
||||
ai.goal = OpportunisticAttackerGoal.IDLE;
|
||||
Globals.aiManager.registerAI(ai);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void simulate(){
|
||||
switch(goal){
|
||||
case ATTACK:
|
||||
if(target != null){
|
||||
if(inAttackRange()){
|
||||
if(hasWeapon()){
|
||||
attack();
|
||||
} else {
|
||||
if(weaponInRange()){
|
||||
setGoal(OpportunisticAttackerGoal.SEEK_WEAPON);
|
||||
} else {
|
||||
setGoal(OpportunisticAttackerGoal.IDLE);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if(inAggroRange()){
|
||||
if(hasWeapon()){
|
||||
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;
|
||||
AttackTree attackTree = CreatureUtils.getAttackTree(character);
|
||||
attackTree.start(EntityDataStrings.ATTACK_MOVE_TYPE_MELEE_SWING_ONE_HAND);
|
||||
} else {
|
||||
attackCooldown--;
|
||||
}
|
||||
}
|
||||
|
||||
void moveToTarget(){
|
||||
Vector3d targetPosition = EntityUtils.getPosition(target);
|
||||
Vector3d characterPosition = EntityUtils.getPosition(character);
|
||||
Vector3d moveVector = new Vector3d(targetPosition).sub(characterPosition).normalize();
|
||||
CreatureUtils.setMovementVector(character, new Vector3d((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(){
|
||||
boolean rVal = false;
|
||||
Vector3d position = EntityUtils.getPosition(character);
|
||||
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(character);
|
||||
Vector3d targetPosition = EntityUtils.getPosition(target);
|
||||
if(new Vector3d(position).distance(targetPosition) < aggroRange){
|
||||
rVal = true;
|
||||
}
|
||||
return rVal;
|
||||
}
|
||||
|
||||
|
||||
void searchForTarget(){
|
||||
Vector3d position = EntityUtils.getPosition(character);
|
||||
for(Entity current : Globals.entityManager.getLifeStateEntities()){
|
||||
if(current != character){
|
||||
Vector3d potentialTargetPosition = EntityUtils.getPosition(current);
|
||||
if(position.distance(potentialTargetPosition) < aggroRange){
|
||||
target = current;
|
||||
setGoal(OpportunisticAttackerGoal.ATTACK);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean hasWeapon(){
|
||||
boolean rVal = false;
|
||||
if(character.getDataKeys().contains(EntityDataStrings.EQUIP_STATE)){
|
||||
EquipState equipState = (EquipState)character.getData(EntityDataStrings.EQUIP_STATE);
|
||||
if(equipState.hasEquipPrimary()){
|
||||
rVal = true;
|
||||
}
|
||||
}
|
||||
return rVal;
|
||||
}
|
||||
|
||||
boolean weaponInRange(){
|
||||
boolean rVal = false;
|
||||
Vector3d position = EntityUtils.getPosition(character);
|
||||
for(Entity current : Globals.entityManager.getItemEntities()){
|
||||
if(current != character && 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(character);
|
||||
if(target != null){
|
||||
Vector3d potentialTargetPosition = EntityUtils.getPosition(target);
|
||||
if(position.distance(potentialTargetPosition) < pickupRange){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void pickupWeapon(){
|
||||
if(character.getDataKeys().contains(EntityDataStrings.EQUIP_STATE)){
|
||||
EquipState equipState = (EquipState)character.getData(EntityDataStrings.EQUIP_STATE);
|
||||
if(!equipState.hasEquipPrimary()){
|
||||
equipState.attemptEquip(target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user