Files
NGECore2/src/services/ai/TurretAIActor.java
T
2014-08-10 15:17:01 +02:00

600 lines
20 KiB
Java

/*******************************************************************************
* Copyright (c) 2013 <Project SWG>
*
* This File is part of NGECore2.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Using NGEngine to work with NGECore2 is making a combined work based on NGEngine.
* Therefore all terms and conditions of the GNU Lesser General Public License cover the combination.
******************************************************************************/
package services.ai;
import java.util.Map;
import java.util.Vector;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import main.NGECore;
import net.engio.mbassy.listener.Handler;
import engine.clients.Client;
import engine.resources.objects.SWGObject;
import engine.resources.scene.Point3D;
import engine.resources.scene.Quaternion;
import resources.datatables.Options;
import resources.objects.cell.CellObject;
import resources.objects.creature.CreatureObject;
import resources.objects.installation.InstallationObject;
import resources.objects.tangible.TangibleObject;
import services.ai.states.TurretAIState;
import services.ai.states.TurretAIState.StateResult;
import services.ai.states.AIState;
import services.ai.states.TurretAttackState;
import services.ai.states.TurretIdleState;
import services.combat.CombatEvents.DamageTaken;
import services.spawn.MobileTemplate;
import tools.DevLog;
import resources.datatables.FactionStatus;
import java.util.Random;
@SuppressWarnings("unused")
public class TurretAIActor {
private TangibleObject creature;
private Point3D spawnPosition;
private volatile TurretAIState currentState;
private TangibleObject followObject;
private Vector<Point3D> movementPoints = new Vector<Point3D>();
private MobileTemplate mobileTemplate;
private ScheduledExecutorService scheduler;
private Map<CreatureObject, Integer> damageMap = new ConcurrentHashMap<CreatureObject, Integer>();
private volatile boolean hasReachedPosition;
private long lastAttackTimestamp;
private ScheduledFuture<?> regenTask;
private ScheduledFuture<?> aggroCheckTask;
private ScheduledFuture<?> factionCheckTask;
private boolean isStalking = false;
private byte milkState = 0;
private boolean hasBeenHarvested = false;
private Vector<Point3D> patrolPoints = new Vector<Point3D>();
private int patrolPointIndex = 0;
private String loiterDestType = "LOITER";
private Point3D loiterDestination;
private Point3D originPosition;
private float minLoiterDist;
private float maxLoiterDist;
private String waitState = "NO";
private long waitStartTime = 0L;
private TurretAIState intendedPrimaryTurretAIState;
private Point3D lastPositionBeforeStateChange;
private boolean patrolLoop = true; // default
private ScheduledFuture<?> recoveryFuture;
public TurretAIActor(TangibleObject creature, Point3D spawnPosition, ScheduledExecutorService scheduler) {
this.creature = creature;
this.spawnPosition = spawnPosition;
setLastPositionBeforeStateChange(spawnPosition); // just to make sure it's initialized
this.scheduler = scheduler;
creature.getEventBus().subscribe(this);
this.currentState = new TurretIdleState();
setIntendedPrimaryTurretAIState(this.currentState); // to switch back to after aggro
if(creature.getOption(Options.AGGRESSIVE)) {
aggroCheckTask = scheduler.scheduleAtFixedRate(() -> {
try {
if(creature == null || creature.getObservers().isEmpty() || creature.isInCombat() || isStalking)
return;
if (creature.getAttachment("tamed")!=null){
DevLog.debugoutai(this, "Charon", "Pet AI", "aggroCheckTask tamed==1");
return;
}
if (creature.getCustomName().contains("baby"))
DevLog.debugoutai(this, "Charon", "Pet AI", "baby aggroCheckTask should not be here " +creature.getAttachment("tamed"));
creature.getObservers().stream().map(Client::getParent).filter(obj -> obj.inRange(creature.getWorldPosition(), 60)).forEach((obj) -> {
if(new Random().nextFloat() <= 0.5 || creature.isInCombat() || isStalking) {
/*if(mobileTemplate.isStalker()) {
setFollowObject((CreatureObject) obj);
setCurrentState(new StalkState());
} else */
if (creature.getAttachment("IsBeingTamed")!=null)
return;
if (obj instanceof CreatureObject){
CreatureObject addedObject = (CreatureObject) obj;
if (addedObject.getCalledPet()!=null){
CreatureObject calledPet = addedObject.getCalledPet();
if (calledPet.getPosture() != 13 && calledPet.getPosture() != 14){
addDefender(calledPet);
}
}
if (addedObject.getPosture() != 13 && addedObject.getPosture() != 14){
addDefender(addedObject);
}
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}, 0, 2000, TimeUnit.MILLISECONDS);
}
if(creature.getFaction().length()>0 || !creature.getOption(Options.AGGRESSIVE)) {
factionCheckTask = scheduler.scheduleAtFixedRate(() -> {
//System.out.println("Turret faction thread");
try {
if(creature == null || creature.getFactionStatus()!=FactionStatus.Combatant || creature.getObservers().isEmpty() || creature.isInCombat())
return;
creature.getObservers().stream().map(Client::getParent).filter(obj -> obj.inRange(creature.getWorldPosition(), 60)).forEach((obj) -> {
if(new Random().nextFloat() <= 0.5 || creature.isInCombat()) {
// DevLog.debugout("Charon", "TURRET CHECK faction creature ", "added " + creature.getFaction());
// DevLog.debugout("Charon", "TURRET CHECK faction obj", "added " + ((TangibleObject)obj).getFaction());
// DevLog.debugout("Charon", "TURRET CHECK factionCheckTask", "added " + obj.getCustomName());
// DevLog.debugout("Charon", "TURRET CHECK isFactionEnemy", "res " + NGECore.getInstance().factionService.isFactionEnemy((TangibleObject)creature, (TangibleObject)obj));
if (obj instanceof CreatureObject && NGECore.getInstance().factionService.isFactionEnemy((TangibleObject)creature, (TangibleObject)obj)){
CreatureObject addedObject = (CreatureObject) obj;
if (addedObject.getCalledPet()!=null){
CreatureObject calledPet = addedObject.getCalledPet();
if (calledPet.getPosture() != 13 && calledPet.getPosture() != 14){
addDefender(calledPet);
}
}
if (addedObject.getPosture() != 13 && addedObject.getPosture() != 14 && ! addedObject.getOption(Options.INVULNERABLE)){
addDefender(addedObject);
// DevLog.debugout("Charon", "TURRET faction creature ", "added " + creature.getFaction());
// DevLog.debugout("Charon", "TURRET faction obj", "added " + ((TangibleObject)obj).getFaction());
// DevLog.debugout("Charon", "TURRET factionCheckTask", "added " + obj.getCustomName());
}
}
}
});
NGECore.getInstance().simulationService.getAllNearNonSameFactionNPCs(60, creature).forEach((obj) -> {
if(new Random().nextFloat() <= 0.5 || creature.isInCombat()) {
// DevLog.debugout("Charon", "TURRET CHECK faction creature ", "added " + creature.getFaction());
// DevLog.debugout("Charon", "TURRET CHECK faction obj", "added " + ((TangibleObject)obj).getFaction());
// DevLog.debugout("Charon", "TURRET CHECK factionCheckTask", "added " + obj.getCustomName());
// DevLog.debugout("Charon", "TURRET CHECK isFactionEnemy", "res " + NGECore.getInstance().factionService.isFactionEnemy((TangibleObject)creature, (TangibleObject)obj));
if (obj instanceof CreatureObject && NGECore.getInstance().factionService.isFactionEnemy((TangibleObject)creature, (TangibleObject)obj)){
CreatureObject addedObject = (CreatureObject) obj;
if (addedObject.getCalledPet()!=null){
CreatureObject calledPet = addedObject.getCalledPet();
if (calledPet.getPosture() != 13 && calledPet.getPosture() != 14 & ! calledPet.getOption(Options.INVULNERABLE)){
addDefender(calledPet);
}
}
if (addedObject.getPosture() != 13 && addedObject.getPosture() != 14 & ! addedObject.getOption(Options.INVULNERABLE)){
addDefender(addedObject);
// DevLog.debugout("Charon", "TURRET faction creature ", "added " + creature.getFaction());
// DevLog.debugout("Charon", "TURRET faction obj", "added " + ((TangibleObject)obj).getFaction());
// DevLog.debugout("Charon", "TURRET factionCheckTask", "added " + obj.getCustomName());
}
}
}
});
NGECore.getInstance().simulationService.getAllNearNonSameFactionTANOs(60, creature).forEach((obj) -> {
if(new Random().nextFloat() <= 0.5 || creature.isInCombat()) {
// DevLog.debugout("Charon", "TANO CHECK faction creature ", "added " + creature.getFaction());
// DevLog.debugout("Charon", "TANO CHECK faction obj", "added " + ((TangibleObject)obj).getFaction());
// DevLog.debugout("Charon", "TANO CHECK factionCheckTask", "added " + obj.getCustomName());
// DevLog.debugout("Charon", "TANO CHECK isFactionEnemy", "res " + NGECore.getInstance().factionService.isFactionEnemy((TangibleObject)creature, (TangibleObject)obj));
if (obj instanceof TangibleObject && !(obj instanceof CreatureObject) && NGECore.getInstance().factionService.isFactionEnemy((TangibleObject)creature, (TangibleObject)obj)){
TangibleObject addedObject = (TangibleObject) obj;
addDefender(addedObject);
DevLog.debugoutai(this, "Charon", "TANO faction creature ", "added " + creature.getFaction());
DevLog.debugoutai(this, "Charon", "TANO faction obj", "added " + ((TangibleObject)obj).getFaction());
DevLog.debugoutai(this, "Charon", "TANO factionCheckTask", "added " + obj.getCustomName());
}
}
});
} catch (Exception e) {
e.printStackTrace();
}
}, 0, 1000, TimeUnit.MILLISECONDS);
}
}
public TangibleObject getCreature() {
return creature;
}
public void setCreature(CreatureObject creature) {
this.creature = creature;
}
public Point3D getSpawnPosition() {
return spawnPosition;
}
public void setSpawnPosition(Point3D spawnPosition) {
this.spawnPosition = spawnPosition;
}
// public void addDefender(CreatureObject defender) {
// addDefender(defender, false);
// }
public void addDefender(TangibleObject defender) {
addDefender(defender, false);
}
public void addDefender(TangibleObject defender, boolean isAssisting) {
creature.addDefender(defender);
if(followObject == null)
setFollowObject(defender);
setCurrentState(new TurretAttackState());
// if(!isAssisting) {
// NGECore.getInstance().simulationService.get(creature.getPlanet(), creature.getWorldPosition().x, creature.getWorldPosition().z, 38).stream().filter((obj) ->
// obj instanceof CreatureObject &&
// obj.getAttachment("AI") != null &&
// ((TurretAIActor) obj.getAttachment("AI")).getMobileTemplate().getSocialGroup().equals(getMobileTemplate().getSocialGroup()) &&
// obj.inRange(creature.getWorldPosition(), ((TurretAIActor) obj.getAttachment("AI")).getMobileTemplate().getAssistRange())
// ).forEach((obj) -> ((TurretAIActor) obj.getAttachment("AI")).addDefender(defender, true));
// }
}
public void removeDefender(TangibleObject defender) {
creature.removeDefender(defender);
damageMap.remove(defender);
defender.removeDefender(creature);
if(followObject == defender) {
setFollowObject(getHighestDamageDealer());
if(creature.getDefendersList().size() == 0)
setCurrentState(new TurretIdleState());
}
}
public CreatureObject getHighestDamageDealer() {
CreatureObject highestDamageDealer = null;
highestDamageDealer = damageMap.keySet().stream().max((c1, c2) -> damageMap.get(c1) - damageMap.get(c2)).orElse(null);
// return first defender if no damage has been dealt
if(highestDamageDealer == null) {
for(TangibleObject tangible : creature.getDefendersList().toArray(new TangibleObject[]{})) {
if(tangible instanceof CreatureObject)
return (CreatureObject) tangible;
}
}
return highestDamageDealer;
}
public TurretAIState getCurrentState() {
return currentState;
}
public void setCurrentState(TurretAIState currentState) {
try {
if(currentState.getClass() == this.currentState.getClass())
return;
if(this.currentState != null)
doStateAction(this.currentState.onExit(this));
this.currentState = currentState;
doStateAction(currentState.onEnter(this));
} catch (Exception e) {
e.printStackTrace();
}
}
public TangibleObject getFollowObject() {
return followObject;
}
public void setFollowObject(TangibleObject followObject) {
this.followObject = followObject;
}
public Vector<Point3D> getMovementPoints() {
return movementPoints;
}
public void setMovementPoints(Vector<Point3D> movementPoints) {
this.movementPoints = movementPoints;
}
public MobileTemplate getMobileTemplate() {
return mobileTemplate;
}
public void setMobileTemplate(MobileTemplate mobileTemplate) {
this.mobileTemplate = mobileTemplate;
}
public void scheduleRecovery() {
TurretAIState caughtAIState = currentState;
recoveryFuture = scheduler.schedule(() -> {
try {
if (caughtAIState.getClass().equals(currentState.getClass()))
doStateAction(currentState.recover(TurretAIActor.this));
} catch (Exception e) {
e.printStackTrace();
}
}, 2000, TimeUnit.MILLISECONDS);
}
public void setNextPosition(Point3D position) {
movementPoints.add(0, position);
}
@Handler
public void handleDamage(DamageTaken event) {
CreatureObject attacker = (CreatureObject)event.attacker;
if(damageMap.containsKey(attacker))
damageMap.put(attacker, damageMap.get(attacker) + event.damage);
else
damageMap.put(attacker, event.damage);
}
public Map<CreatureObject, Integer> getDamageMap() {
return damageMap;
}
public boolean hasReachedPosition() {
return hasReachedPosition;
}
public void setHasReachedPosition(boolean hasReachedPosition) {
this.hasReachedPosition = hasReachedPosition;
}
public void faceObject(SWGObject object) {
// Null checks due to a null error at: float direction =
if (object == null) DevLog.debugout("Charon", "AI Actor", "faceObject object is NULL");
if (creature == null) DevLog.debugout("Charon", "AI Actor", "faceObject creature is NULL");
if (object.getWorldPosition() == null) DevLog.debugout("Charon", "AI Actor", "faceObject object's position is NULL");
if (creature.getWorldPosition() == null) DevLog.debugout("Charon", "AI Actor", "faceObject creature's position is NULL");
float direction = (float) Math.atan2(object.getWorldPosition().x - creature.getWorldPosition().x, object.getWorldPosition().z - creature.getWorldPosition().z);
if(direction < 0)
direction = (float) (2 * Math.PI + direction);
if(Math.abs(direction - creature.getRadians()) < 0.05)
return;
Quaternion quaternion = new Quaternion((float) Math.cos(direction / 2), 0, (float) Math.sin(direction / 2), 0);
if (quaternion.y < 0.0f && quaternion.w > 0.0f) {
quaternion.y *= -1;
quaternion.w *= -1;
}
if(creature.getContainer() instanceof CellObject)
NGECore.getInstance().simulationService.moveObject(creature, creature.getPosition(), quaternion, creature.getMovementCounter(), 0, (CellObject) creature.getContainer());
else
NGECore.getInstance().simulationService.moveObject(creature, creature.getPosition(), quaternion, creature.getMovementCounter(), 0, null);
}
public long getLastAttackTimestamp() {
return lastAttackTimestamp;
}
public void setLastAttackTimestamp(long lastAttackTimestamp) {
this.lastAttackTimestamp = lastAttackTimestamp;
}
public long getTimeSinceLastAttack() {
return System.currentTimeMillis() - getLastAttackTimestamp();
}
public void doStateAction(byte result) {
if (creature==null){
destroyActor();
return;
}
switch(result) {
case StateResult.DEAD:
destroyActor();
break;
case StateResult.FINISHED:
case StateResult.UNFINISHED:
return;
case StateResult.IDLE:
setCurrentState(new TurretIdleState());
return;
//case StateResult.NONE:
// System.out.println("State action returned error result");
}
}
public void scheduleDespawn() {
// Sometimes these tasks are null?
try {
if (aggroCheckTask!=null)
aggroCheckTask.cancel(true);
if (factionCheckTask!=null)
factionCheckTask.cancel(true);
} catch(Exception e) {
}
try {
regenTask.cancel(true);
} catch(Exception e) {
}
}
public void destroyActor(){
creature.getEventBus().unsubscribe(this);
// Make sure to kill all AI helper threads
if (aggroCheckTask!=null)
aggroCheckTask.cancel(true);
if (factionCheckTask!=null)
factionCheckTask.cancel(true);
if (regenTask!=null)
regenTask.cancel(true);
}
public void cancelAggro(){
try {
aggroCheckTask.cancel(true);
} catch(Exception e) {
}
}
public ScheduledFuture<?> getRegenTask() {
return regenTask;
}
public void setRegenTask(ScheduledFuture<?> regenTask) {
this.regenTask = regenTask;
}
public boolean isStalking() {
return isStalking;
}
public void setStalking(boolean isStalking) {
this.isStalking = isStalking;
}
public byte getMilkState() {
synchronized(creature.getMutex()) {
return milkState;
}
}
public void setMilkState(byte milkState) {
synchronized(creature.getMutex()) {
this.milkState = milkState;
}
}
public boolean hasBeenHarvested() {
synchronized(creature.getMutex()) {
return hasBeenHarvested;
}
}
public void setHasBeenHarvested(boolean hasBeenHarvested) {
synchronized(creature.getMutex()) {
this.hasBeenHarvested = hasBeenHarvested;
}
}
public void setPatrolPoints(Vector<Point3D> patrolPoints){
this.patrolPoints = patrolPoints;
//setMovementPoints(patrolPoints);
}
public int getPatrolPointIndex() {
return patrolPointIndex;
}
public void setPatrolPointIndex(int patrolPointIndex) {
this.patrolPointIndex = patrolPointIndex;
}
public Point3D getLoiterDestination() {
return loiterDestination;
}
public void setLoiterDestination(Point3D loiterDestination) {
this.loiterDestination = loiterDestination;
}
public Point3D getOriginPosition() {
return originPosition;
}
public void setOriginPosition(Point3D originPosition) {
this.originPosition = originPosition;
}
public String getLoiterDestType() {
return loiterDestType;
}
public void setLoiterDestType(String loiterDestType) {
this.loiterDestType = loiterDestType;
}
public float getMinLoiterDist() {
return minLoiterDist;
}
public void setMinLoiterDist(float minLoiterDist) {
this.minLoiterDist = minLoiterDist;
}
public float getMaxLoiterDist() {
return maxLoiterDist;
}
public void setMaxLoiterDist(float maxLoiterDist) {
this.maxLoiterDist = maxLoiterDist;
}
public String getWaitState() {
return waitState;
}
public void setWaitState(String waitState) {
this.waitState = waitState;
}
public long getWaitStartTime() {
return waitStartTime;
}
public void setWaitStartTime(long waitStartTime) {
this.waitStartTime = waitStartTime;
}
public Vector<Point3D> getPatrolPoints() {
return patrolPoints;
}
public TurretAIState getIntendedPrimaryTurretAIState() {
return intendedPrimaryTurretAIState;
}
public void setIntendedPrimaryTurretAIState(TurretAIState intendedPrimaryTurretAIState) {
this.intendedPrimaryTurretAIState = intendedPrimaryTurretAIState;
}
public Point3D getLastPositionBeforeStateChange() {
return lastPositionBeforeStateChange;
}
public void setLastPositionBeforeStateChange(
Point3D lastPositionBeforeStateChange) {
this.lastPositionBeforeStateChange = lastPositionBeforeStateChange;
}
public boolean isPatrolLoop() {
return patrolLoop;
}
public void setPatrolLoop(boolean patrolLoop) {
this.patrolLoop = patrolLoop;
}
}