Opportunistic Attacker

This commit is contained in:
austin 2021-11-20 12:55:13 -05:00
parent 84534e3ceb
commit b2fe119348
4 changed files with 275 additions and 1 deletions

View File

@ -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));

View File

@ -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";
/*

View File

@ -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);
}
}

View File

@ -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;
}
}
}