diff --git a/src/resources/objects/creature/CreatureObject.java b/src/resources/objects/creature/CreatureObject.java index 32521beb..2245768d 100644 --- a/src/resources/objects/creature/CreatureObject.java +++ b/src/resources/objects/creature/CreatureObject.java @@ -389,7 +389,7 @@ public class CreatureObject extends TangibleObject implements IPersistent { } public void setHeight(float height) { - height = (((height < 0.7) || (height > 1.5)) ? 1 : height); + // height = (((height < 0.7) || (height > 1.5)) ? 1 : height); Babies need height values smaller than that notifyObservers(getBaseline(3).set("height", height), true); } diff --git a/src/services/ai/LairActor.java b/src/services/ai/LairActor.java index 44e7e1be..11a9fd2b 100644 --- a/src/services/ai/LairActor.java +++ b/src/services/ai/LairActor.java @@ -21,15 +21,24 @@ ******************************************************************************/ package services.ai; +import java.util.Collections; +import java.util.Map; import java.util.Random; import java.util.Vector; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; import engine.resources.scene.Point3D; import main.NGECore; import net.engio.mbassy.listener.Handler; import resources.common.SpawnPoint; +import resources.datatables.Posture; import resources.objects.creature.CreatureObject; import resources.objects.tangible.TangibleObject; +import services.ai.states.RetreatState; import services.combat.CombatEvents.DamageTaken; import tools.DevLog; @@ -45,11 +54,13 @@ public class LairActor { private short level; private String bossTemplate; private boolean bossSpawned = false; + private Map attackerMap = new ConcurrentHashMap(); public LairActor(TangibleObject lairObject, String creatureTemplate) { this.lairObject = lairObject; this.creatureTemplate = creatureTemplate; lairObject.getEventBus().subscribe(this); + checkAttackerTask(); } public LairActor(TangibleObject lairObject, String creatureTemplate, int maxSpawns, short level) { @@ -58,6 +69,7 @@ public class LairActor { this.maxSpawns = maxSpawns; this.level = level; lairObject.getEventBus().subscribe(this); + checkAttackerTask(); } public LairActor(TangibleObject lairObject, Vector creatureTemplates, int maxSpawns, short level) { @@ -66,6 +78,7 @@ public class LairActor { this.maxSpawns = maxSpawns; this.level = level; lairObject.getEventBus().subscribe(this); + checkAttackerTask(); } @@ -90,40 +103,55 @@ public class LairActor { ai.addDefender((CreatureObject)event.attacker); ((CreatureObject)event.attacker).addDefender(ai.getCreature()); } - - + attackerMap.put(((CreatureObject)event.attacker), System.currentTimeMillis()); } public void spawnNewCreatures() { + // remove dead creatures from collection + creatures.removeAll(Collections.singleton(null)); + Vector removers = new Vector(); + for (AIActor act : creatures){ + if (act!=null ){ + if (act.getCreature().getPosture()==Posture.Dead) + removers.add(act); + else if (lairObject.getPosition().getDistance2D(act.getCreature().getPosition())>80) + removers.add(act); + } + } + creatures.removeAll(removers); + if(creatures.size() >= maxSpawns) return; int currentCondition = lairObject.getConditionDamage(); int maxCondition = lairObject.getMaximumCondition(); - + //System.out.println("spawnWave " + spawnWave + " currentCondition " + currentCondition + " maxCondition " + maxCondition + "quot " + ((float)currentCondition / (float)maxCondition)); switch(spawnWave) { // TODO: play damage effect case 0: spawnWave++; break; case 1: - if((currentCondition / maxCondition) < 0.7) { + if(((float)currentCondition / (float)maxCondition) < 0.7) { spawnWave++; + break; } else { return; } case 2: - if((currentCondition / maxCondition) < 0.3) { + if(((float)currentCondition / (float)maxCondition) < 0.3) { spawnWave++; + break; } else { return; } - break; + case 3: if (bossTemplate!=null && ! bossSpawned){ - //CreatureObject boss = (CreatureObject)NGECore.getInstance().spawnService.spawnCreature(bossTemplate, lairObject.getPlanet().getName(), 0L, lairObject.getPosition().x + 3, lairObject.getPosition().y, lairObject.getPosition().z, lairObject.getOrientation().w, lairObject.getOrientation().x, lairObject.getOrientation().y,lairObject.getOrientation().z,-1); + CreatureObject boss = (CreatureObject)NGECore.getInstance().spawnService.spawnCreature(bossTemplate, lairObject.getPlanet().getName(), 0L, lairObject.getPosition().x + 3, lairObject.getPosition().y, lairObject.getPosition().z, lairObject.getOrientation().w, lairObject.getOrientation().x, lairObject.getOrientation().y,lairObject.getOrientation().z,-1); bossSpawned = true; + break; } return; @@ -138,7 +166,9 @@ public class LairActor { creatureAmount = new Random().nextInt(4) + (maxSpawns / 5); tries++; } - while(creatureAmount > maxSpawns && tries < 10); + while(tries < 10); + + //while(creatureAmount > maxSpawns && tries < 10); for(int i = 0; i < creatureAmount; i++) { Point3D position = SpawnPoint.getRandomPosition(lairObject.getPosition(), 5, 30, lairObject.getPlanetId()); @@ -211,4 +241,48 @@ public class LairActor { public void setBossTemplate(String bossTemplate) { this.bossTemplate = bossTemplate; } + + private void checkAttackerTask(){ + final Future[] fut1 = {null}; + final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); + fut1[0] = scheduler.scheduleAtFixedRate(new Runnable() { + @Override public void run() { + try { + if (lairObject==null){ + Thread.yield(); + fut1[0].cancel(false); + } else { + int currentCondition = lairObject.getConditionDamage(); + int maxCondition = lairObject.getMaximumCondition(); + if (currentCondition>maxCondition){ + Thread.yield(); + fut1[0].cancel(false); + } + } + checkAttackers(); + } catch (Exception e) { + System.err.println("Exception in checkAttackerTask->scheduleAtFixedRate->checkAttackers() " + e.getMessage()); + } + } + }, 0, 10, TimeUnit.SECONDS); + } + + private void checkAttackers(){ + Map attackerMapClone = new ConcurrentHashMap(attackerMap); + for (CreatureObject attacker : attackerMapClone.keySet()) { + Long lastAttackTime = attackerMapClone.get(attacker); + if (attacker.getWorldPosition().getDistance2D(lairObject.getWorldPosition())>100 || System.currentTimeMillis()-lastAttackTime>10000) + removeDefender(attacker); + } + attackerMapClone = null; + } + + private void removeDefender(TangibleObject defender) { + if (defender==null){ + return; + } + lairObject.removeDefender(defender); + if (attackerMap.containsKey(defender)) attackerMap.remove(defender); + defender.removeDefender(lairObject); + } } diff --git a/src/services/combat/CombatService.java b/src/services/combat/CombatService.java index f251144f..98438d95 100644 --- a/src/services/combat/CombatService.java +++ b/src/services/combat/CombatService.java @@ -1533,8 +1533,13 @@ public class CombatService implements INetworkDispatch { public boolean areInDuel(CreatureObject creature1, CreatureObject creature2) { - if(creature1.getDuelList().contains(creature2) && creature2.getDuelList().contains(creature1)) - return true; + // Vehicles are going through this too, but dont have duellists, so null checks must be done or NPE + if (creature1.getDuelList()!=null && creature2.getDuelList()!=null) { + if(creature1.getDuelList().contains(creature2) && creature2.getDuelList().contains(creature1)) + return true; + } else { + return false; + } return false; } diff --git a/src/services/spawn/MixedSpawnArea.java b/src/services/spawn/MixedSpawnArea.java index 8a4a5d43..c7cc66f7 100644 --- a/src/services/spawn/MixedSpawnArea.java +++ b/src/services/spawn/MixedSpawnArea.java @@ -35,6 +35,7 @@ import resources.common.collidables.AbstractCollidable.ExitEvent; import resources.objects.creature.CreatureObject; import services.TerrainService; import services.SimulationService.MoveEvent; +import services.ai.AIActor; import services.ai.LairActor; import engine.resources.config.Config; import engine.resources.objects.SWGObject; @@ -213,13 +214,15 @@ public class MixedSpawnArea extends SpawnArea { } } if (this.spawnGroup instanceof LairGroupTemplate){ + object = event.object; //if(object == null || !(object instanceof CreatureObject) || object.getContainer() != null) // return; - if(object == null || !(object instanceof CreatureObject)) + if(object == null || !(object instanceof CreatureObject)){ return; + } creature = (CreatureObject) object; @@ -386,23 +389,26 @@ public class MixedSpawnArea extends SpawnArea { // LairSpawnArea class methods public void spawnLair(CreatureObject object) { - + NGECore core = NGECore.getInstance(); LairGroupTemplate lairGroup = (LairGroupTemplate)this.spawnGroup; Vector lairTemplates = lairGroup.getLairSpawnTemplates(); - if(lairGroup == null || lairTemplates.isEmpty()) + if(lairGroup == null || lairTemplates.isEmpty()){ return; + } - if((System.currentTimeMillis() - lastSpawnTime) < 10000) + if((System.currentTimeMillis() - lastSpawnTime) < 10000){ return; + } Point3D randomPosition = getRandomPosition(object.getWorldPosition(), 32.f, 200.f); - if(randomPosition == null) + if(randomPosition == null){ return; + } TerrainService terrainSvc = core.terrainService; @@ -410,29 +416,38 @@ public class MixedSpawnArea extends SpawnArea { randomPosition.y = height; for(LairActor otherLair : lairs) { - if(otherLair.getLairObject().getWorldPosition().getDistance(randomPosition) < 30) + if(otherLair.getLairObject().getWorldPosition().getDistance(randomPosition) < 30){ return; + } } - if(!terrainSvc.canBuildAtPosition(object, randomPosition.x, randomPosition.z)) + if(!terrainSvc.canBuildAtPosition(object, randomPosition.x, randomPosition.z)){ return; + } Random random = new Random(); - + LairSpawnTemplate lairSpawn = lairTemplates.get(random.nextInt(lairTemplates.size())); - + int level = -1; // If level equals -1 then the mobile template CL will be used! if (lairSpawn.getMinLevel() != -1 && lairSpawn.getMaxLevel()!=-1) level = random.nextInt((int) (lairSpawn.getMaxLevel() - lairSpawn.getMinLevel()) + 1) + lairSpawn.getMinLevel(); + AIActor actor = (AIActor) object.getAttachment("AI"); + + if (actor!=null) + level = actor.getMobileTemplate().getLevel(); - + if (level<0) + level = 10; + LairActor lairActor = core.spawnService.spawnLair(lairSpawn.getLairTemplate(), getPlanet(), randomPosition, (short) level); - if(lairActor == null) + + if(lairActor == null){ return; + } lairs.add(lairActor); lastSpawnTime = System.currentTimeMillis(); - } } diff --git a/src/services/spawn/SpawnService.java b/src/services/spawn/SpawnService.java index feeeb648..fbda0ebb 100644 --- a/src/services/spawn/SpawnService.java +++ b/src/services/spawn/SpawnService.java @@ -459,18 +459,23 @@ public class SpawnService { } public LairActor spawnLair(String lairSpawnTemplate, Planet planet, Point3D position, short level) { - + LairTemplate lairTemplate = lairTemplates.get(lairSpawnTemplate); if(lairTemplate == null) return null; + // adapt to terrain + float positionY = core.terrainService.getHeight(planet.getID(), position.x, position.z); + Point3D adaptedPosition = new Point3D(position.x, positionY, position.z); + TangibleObject lairObject = null; if (lairTemplate.getLairCRC()==null){ String lairCRC = lairTemplate.getLairCRCs().get(new Random().nextInt(lairTemplate.getLairCRCs().size())); - System.out.println("Lair CRC " + lairCRC); - lairObject = (TangibleObject) core.objectService.createObject(lairCRC, 0, planet, position, new Quaternion(1, 0, 0, 0)); + //System.out.println("Lair CRC " + lairCRC); + + lairObject = (TangibleObject) core.objectService.createObject(lairCRC, 0, planet, adaptedPosition, new Quaternion(1, 0, 0, 0)); } else { - lairObject = (TangibleObject) core.objectService.createObject(lairTemplate.getLairCRC().trim(), 0, planet, position, new Quaternion(1, 0, 0, 0)); + lairObject = (TangibleObject) core.objectService.createObject(lairTemplate.getLairCRC().trim(), 0, planet, adaptedPosition, new Quaternion(1, 0, 0, 0)); } if(lairObject == null) @@ -478,8 +483,17 @@ public class SpawnService { lairObject.setOptionsBitmask(Options.ATTACKABLE); lairObject.setPvpBitmask(PvpStatus.Attackable); - lairObject.setMaximumCondition(1000 * level); + + + + if (lairTemplate.getMobiles()!=null){ + MobileTemplate mobileTemplate = mobileTemplates.get(lairTemplate.getMobiles().get(0)); + level = mobileTemplate.getLevel(); + } + + lairObject.setMaximumCondition(1000 * level); + LairActor lairActor = new LairActor(lairObject, lairTemplate.getMobileName(), 10, level); if (lairTemplate.getMobiles()!=null)