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.renderer.RenderUtils;
|
||||||
import electrosphere.engine.assetmanager.AssetDataStrings;
|
import electrosphere.engine.assetmanager.AssetDataStrings;
|
||||||
import electrosphere.game.client.targeting.crosshair.Crosshair;
|
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.NavMeshPathfinder;
|
||||||
import electrosphere.game.server.pathfinding.navmesh.NavCube;
|
import electrosphere.game.server.pathfinding.navmesh.NavCube;
|
||||||
import electrosphere.game.server.pathfinding.navmesh.NavMesh;
|
import electrosphere.game.server.pathfinding.navmesh.NavMesh;
|
||||||
@ -621,7 +622,8 @@ public class LoadingThread extends Thread {
|
|||||||
// Entity goblinSword = ItemUtils.spawnBasicItem("Katana");
|
// Entity goblinSword = ItemUtils.spawnBasicItem("Katana");
|
||||||
// AttachUtils.attachEntityToEntityAtBone(goblin, goblinSword, "Bone.031");
|
// AttachUtils.attachEntityToEntityAtBone(goblin, goblinSword, "Bone.031");
|
||||||
//attach ai to evil goblin
|
//attach ai to evil goblin
|
||||||
MindlessAttacker.attachToCreature(goblin);
|
// MindlessAttacker.attachToCreature(goblin);
|
||||||
|
OpportunisticAttacker.attachToCreature(goblin);
|
||||||
|
|
||||||
// goblin = CreatureUtils.spawnBasicCreature("Goblin");
|
// goblin = CreatureUtils.spawnBasicCreature("Goblin");
|
||||||
// CollisionObjUtils.positionCharacter(goblin, new Vector3f(3, 0, 4));
|
// 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_IS_ITEM = "itemIsItem";
|
||||||
public static final String ITEM_TYPE = "itemType";
|
public static final String ITEM_TYPE = "itemType";
|
||||||
|
public static final String ITEM_IS_WEAPON = "itemIsWeapon";
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@ -89,6 +89,9 @@ public class ItemUtils {
|
|||||||
case "TARGETABLE":
|
case "TARGETABLE":
|
||||||
Globals.entityManager.registerTargetableEntity(rVal);
|
Globals.entityManager.registerTargetableEntity(rVal);
|
||||||
break;
|
break;
|
||||||
|
case "WEAPON":
|
||||||
|
rVal.putData(EntityDataStrings.ITEM_IS_WEAPON, true);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rVal.putData(EntityDataStrings.DRAW_CAST_SHADOW, true);
|
rVal.putData(EntityDataStrings.DRAW_CAST_SHADOW, true);
|
||||||
@ -160,4 +163,8 @@ public class ItemUtils {
|
|||||||
public static String getType(Entity item){
|
public static String getType(Entity item){
|
||||||
return (String)item.getData(EntityDataStrings.ITEM_TYPE);
|
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