diff --git a/scripts/commands/combat/droid_attack.py b/scripts/commands/combat/droid_attack.py new file mode 100644 index 00000000..39356bbb --- /dev/null +++ b/scripts/commands/combat/droid_attack.py @@ -0,0 +1,10 @@ +import sys + +def setup(core, actor, target, command): + + return + +def run(core, actor, target, commandString): + pet = actor.getCalledPet() + pet.addDefender(target); + return diff --git a/scripts/commands/setgodmode.py b/scripts/commands/setgodmode.py index a7da7dbc..5f67bb5e 100644 --- a/scripts/commands/setgodmode.py +++ b/scripts/commands/setgodmode.py @@ -165,6 +165,13 @@ def run(core, actor, target, commandString): clipboard = toolkit.getSystemClipboard() clipboard.setContents(StringSelection(str), None) actor.sendSystemMessage('Patrolpoint copied to clipboard', 0) + + elif command == 'jesus': + actor.setHealth(actor.getMaxHealth()) + actor.setAction(actor.getMaxAction()) + actor.setPosture(0) + actor.setSpeedMultiplierBase(1) + actor.setTurnRadius(1) elif command == 'unknownAbilityPacket' and arg1: packet = UnknownAbilityPacket(arg1) diff --git a/scripts/mobiles/pets/pet_droideka.py b/scripts/mobiles/pets/pet_droideka.py new file mode 100644 index 00000000..b234326f --- /dev/null +++ b/scripts/mobiles/pets/pet_droideka.py @@ -0,0 +1,38 @@ +import sys +from services.spawn import MobileTemplate +from services.spawn import WeaponTemplate +from resources.datatables import WeaponType +from resources.datatables import Difficulty +from resources.datatables import Options +from java.util import Vector + + +def addTemplate(core): + mobileTemplate = MobileTemplate() + mobileTemplate.setCreatureName('droideka') + mobileTemplate.setLevel(90) + mobileTemplate.setDifficulty(Difficulty.NORMAL) + + mobileTemplate.setMinSpawnDistance(5) + mobileTemplate.setMaxSpawnDistance(10) + mobileTemplate.setDeathblow(True) + mobileTemplate.setSocialGroup("droideka") + mobileTemplate.setAssistRange(12) + mobileTemplate.setStalker(False) + mobileTemplate.setOptionsBitmask(Options.ATTACKABLE) + + templates = Vector() + templates.add('object/mobile/shared_droideka.iff') + mobileTemplate.setTemplates(templates) + + weaponTemplates = Vector() + weapontemplate = WeaponTemplate('object/weapon/ranged/droid/shared_droid_droideka_ranged.iff', WeaponType.CARBINE, 1.0, 15, 'energy') + weaponTemplates.add(weapontemplate) + mobileTemplate.setWeaponTemplateVector(weaponTemplates) + + attacks = Vector() + mobileTemplate.setDefaultAttack('rangedShot') + mobileTemplate.setAttacks(attacks) + + core.spawnService.addMobileTemplate('pet_droideka', mobileTemplate) + return \ No newline at end of file diff --git a/scripts/mobiles/pets/pet_gurreck.py b/scripts/mobiles/pets/pet_gurreck.py new file mode 100644 index 00000000..431ba9e3 --- /dev/null +++ b/scripts/mobiles/pets/pet_gurreck.py @@ -0,0 +1,38 @@ +import sys +from services.spawn import MobileTemplate +from services.spawn import WeaponTemplate +from resources.datatables import WeaponType +from resources.datatables import Difficulty +from resources.datatables import Options +from java.util import Vector + + +def addTemplate(core): + mobileTemplate = MobileTemplate() + mobileTemplate.setCreatureName('bio_engineered_gurreck') + mobileTemplate.setLevel(90) + mobileTemplate.setDifficulty(Difficulty.NORMAL) + + mobileTemplate.setMinSpawnDistance(5) + mobileTemplate.setMaxSpawnDistance(10) + mobileTemplate.setDeathblow(True) + mobileTemplate.setSocialGroup("gurreck") + mobileTemplate.setAssistRange(12) + mobileTemplate.setStalker(False) + mobileTemplate.setOptionsBitmask(Options.ATTACKABLE) + + templates = Vector() + templates.add('object/mobile/shared_gurreck.iff') + mobileTemplate.setTemplates(templates) + + weaponTemplates = Vector() + weapontemplate = WeaponTemplate('object/weapon/melee/unarmed/shared_unarmed_default.iff', WeaponType.UNARMED, 1.0, 6, 'kinetic') + weaponTemplates.add(weapontemplate) + mobileTemplate.setWeaponTemplateVector(weaponTemplates) + + attacks = Vector() + mobileTemplate.setDefaultAttack('creatureMeleeAttack') + mobileTemplate.setAttacks(attacks) + + core.spawnService.addMobileTemplate('pet_gurreck', mobileTemplate) + return \ No newline at end of file diff --git a/scripts/object/intangible/pet/droideka.py b/scripts/object/intangible/pet/droideka.py index ccad8904..d7c3f4a1 100644 --- a/scripts/object/intangible/pet/droideka.py +++ b/scripts/object/intangible/pet/droideka.py @@ -1,4 +1,5 @@ import sys def setup(core, object): + object.setAttachment('radial_filename', 'datapad/vehicle_pcd') return \ No newline at end of file diff --git a/scripts/object/intangible/pet/gurreck_hue.py b/scripts/object/intangible/pet/gurreck_hue.py index ccad8904..d7c3f4a1 100644 --- a/scripts/object/intangible/pet/gurreck_hue.py +++ b/scripts/object/intangible/pet/gurreck_hue.py @@ -1,4 +1,5 @@ import sys def setup(core, object): + object.setAttachment('radial_filename', 'datapad/vehicle_pcd') return \ No newline at end of file diff --git a/scripts/object/tangible/deed/pet_deed/deed_droideka.py b/scripts/object/tangible/deed/pet_deed/deed_droideka.py index ccad8904..589cccaf 100644 --- a/scripts/object/tangible/deed/pet_deed/deed_droideka.py +++ b/scripts/object/tangible/deed/pet_deed/deed_droideka.py @@ -1,4 +1,11 @@ import sys def setup(core, object): - return \ No newline at end of file + object.setAttachment('radial_filename', 'deeds/petDeed') + return + +def use(core, actor, object): + core.petService.generatePet(actor, object, 'pet_droideka', 'object/intangible/pet/shared_droideka.iff') + + return + \ No newline at end of file diff --git a/scripts/object/tangible/deed/pet_deed/gurreck_deed.py b/scripts/object/tangible/deed/pet_deed/gurreck_deed.py index ccad8904..ccbcbf92 100644 --- a/scripts/object/tangible/deed/pet_deed/gurreck_deed.py +++ b/scripts/object/tangible/deed/pet_deed/gurreck_deed.py @@ -1,4 +1,11 @@ import sys def setup(core, object): - return \ No newline at end of file + object.setAttachment('radial_filename', 'deeds/petDeed') + return + +def use(core, actor, object): + core.petService.generatePet(actor, object, 'pet_gurreck', 'object/intangible/pet/shared_gurreck_hue.iff') + #core.petService.generatePet(actor, object, 'pet_gurreck', 'object/intangible/vehicle/shared_speederbike_swoop_pcd.iff') + return + \ No newline at end of file diff --git a/scripts/radial/datapad/pet_pcd.py b/scripts/radial/datapad/pet_pcd.py new file mode 100644 index 00000000..e38b4a6e --- /dev/null +++ b/scripts/radial/datapad/pet_pcd.py @@ -0,0 +1,23 @@ +from resources.common import RadialOptions +import sys + +def createRadial(core, owner, target, radials): + if core.petService.getMount(target) is None: + radials.add(RadialOptions(0, 61, 1, '@pet/pet_menu:menu_call')) + else: + radials.add(RadialOptions(0, 61, 1, '@pet/pet_menu:menu_store')) + radials.add(RadialOptions(0, 7, 1, '')) + radials.add(RadialOptions(0, 15, 1, '')) + return + +def handleSelection(core, owner, target, option): + if option == 61 and target: + if core.petService.getMount(target) is None: + core.petService.call(owner, target) + else: + core.petService.store(owner, core.petService.getMount(target)) + elif option == 15 and target: + core.petService.destroy(owner, target) + + return + \ No newline at end of file diff --git a/scripts/radial/deeds/petdeed.py b/scripts/radial/deeds/petdeed.py new file mode 100644 index 00000000..ad08a40f --- /dev/null +++ b/scripts/radial/deeds/petdeed.py @@ -0,0 +1,16 @@ +from resources.common import RadialOptions +import sys + +def createRadial(core, owner, target, radials): + radials.add(RadialOptions(0, 7, 1, '')) + radials.add(RadialOptions(0, 15, 1, '')) + radials.add(RadialOptions(0, 61, 3, '@pet/pet_menu:menu_generate')) + return + +def handleSelection(core, owner, target, option): + if option == 61 and target: + core.objectService.useObject(owner, target) + if option == 15 and target: + core.objectService.destroyObject(target) + return + \ No newline at end of file diff --git a/src/main/NGECore.java b/src/main/NGECore.java index af65f16a..145ef6e7 100644 --- a/src/main/NGECore.java +++ b/src/main/NGECore.java @@ -99,6 +99,7 @@ import services.object.ObjectId; import services.object.ObjectService; import services.object.UpdateService; import services.pet.MountService; +import services.pet.PetService; import services.playercities.PlayerCity; import services.playercities.PlayerCityService; import services.resources.HarvesterService; @@ -202,6 +203,7 @@ public class NGECore { public MountService mountService; public PlayerCityService playerCityService; public ReverseEngineeringService reverseEngineeringService; + public PetService petService; // Login Server public NetworkDispatch loginDispatch; @@ -345,6 +347,7 @@ public class NGECore { playerCityService = new PlayerCityService(this); staticService = new StaticService(this); reverseEngineeringService = new ReverseEngineeringService(this); + petService = new PetService(this); if (config.keyExists("JYTHONCONSOLE.PORT")) { int jythonPort = config.getInt("JYTHONCONSOLE.PORT"); @@ -404,6 +407,7 @@ public class NGECore { zoneDispatch.addService(playerCityService); zoneDispatch.addService(staticService); zoneDispatch.addService(reverseEngineeringService); + zoneDispatch.addService(petService); if (optionsConfigLoaded && options.getInt("LOAD.RESOURCE.SYSTEM") == 1) { zoneDispatch.addService(surveyService); diff --git a/src/resources/objects/creature/CreatureObject.java b/src/resources/objects/creature/CreatureObject.java index b178e20a..e65109d7 100644 --- a/src/resources/objects/creature/CreatureObject.java +++ b/src/resources/objects/creature/CreatureObject.java @@ -183,6 +183,8 @@ public class CreatureObject extends TangibleObject implements Serializable { private transient long tefTime = 0; @NotPersistent private transient SWGObject useTarget; + @NotPersistent + private transient CreatureObject calledPet; private byte locomotion = 0; @@ -1868,4 +1870,12 @@ public class CreatureObject extends TangibleObject implements Serializable { { return getCustomName().split(" ")[1]; } + + public CreatureObject getCalledPet() { + return calledPet; + } + + public void setCalledPet(CreatureObject calledPet) { + this.calledPet = calledPet; + } } diff --git a/src/services/DevService.java b/src/services/DevService.java index fcb5c294..4b9bb46e 100644 --- a/src/services/DevService.java +++ b/src/services/DevService.java @@ -92,6 +92,7 @@ public class DevService implements INetworkDispatch { suiOptions.put((long) 120, "House Deeds"); suiOptions.put((long) 125, "Crafting Tools"); suiOptions.put((long) 130, "Vehicle Deeds"); + suiOptions.put((long) 131, "Pet Deeds"); suiOptions.put((long) 121, "Sandbox City"); if(creature.getPlayerObject().getProfession().equals("trader_0a") || creature.getPlayerObject().getProfession().equals("trader_0b") || creature.getPlayerObject().getProfession().equals("trader_0c") || creature.getPlayerObject().getProfession().equals("trader_0d")) suiOptions.put((long) 177, "REing"); @@ -1309,6 +1310,13 @@ public class DevService implements INetworkDispatch { inventory.add(swoopDeed); inventory.add(av21deed); return; + + case 131: + TangibleObject gurreckDeed = (TangibleObject) core.objectService.createObject("object/tangible/deed/pet_deed/shared_gurreck_deed.iff", planet); + inventory.add(gurreckDeed); + TangibleObject droidekaDeed = (TangibleObject) core.objectService.createObject("object/tangible/deed/pet_deed/shared_deed_droideka.iff", planet); + inventory.add(droidekaDeed); + return; case 140: TangibleObject guildRegistry = (TangibleObject) core.objectService.createObject("object/tangible/furniture/technical/shared_guild_registry_initial.iff", planet); diff --git a/src/services/LootService.java b/src/services/LootService.java index 12f43747..ff677f79 100644 --- a/src/services/LootService.java +++ b/src/services/LootService.java @@ -52,6 +52,7 @@ import resources.objects.creature.CreatureObject; import resources.objects.factorycrate.FactoryCrateObject; import resources.objects.group.GroupObject; import resources.objects.intangible.IntangibleObject; +import resources.objects.player.PlayerObject; import resources.objects.tangible.TangibleObject; import resources.objects.weapon.WeaponObject; import services.ai.AIActor; @@ -130,6 +131,9 @@ public class LootService implements INetworkDispatch { if (requester==null || lootedObject==null) return; //QA + if (! requester.isPlayer()) + return; //QA + GroupObject group = null; if(requester.getGroupId()!=0) group = (GroupObject) core.objectService.getObject(requester.getGroupId()); diff --git a/src/services/ai/AIActor.java b/src/services/ai/AIActor.java index b0489bd3..acb8996d 100644 --- a/src/services/ai/AIActor.java +++ b/src/services/ai/AIActor.java @@ -42,7 +42,10 @@ import services.ai.states.AIState; import services.ai.states.AIState.StateResult; import services.ai.states.AttackState; import services.ai.states.DeathState; +import services.ai.states.FollowState; import services.ai.states.IdleState; +import services.ai.states.LoiterState; +import services.ai.states.PatrolState; import services.ai.states.RetreatState; import services.combat.CombatEvents.DamageTaken; import services.spawn.MobileTemplate; @@ -66,7 +69,7 @@ public class AIActor { private boolean isStalking = false; private byte milkState = 0; private boolean hasBeenHarvested = false; - private Vector patrolPoints; + private Vector patrolPoints = new Vector(); private int patrolPointIndex = 0; private String loiterDestType = "LOITER"; private Point3D loiterDestination; @@ -75,13 +78,17 @@ public class AIActor { private float maxLoiterDist; private String waitState = "NO"; private long waitStartTime = 0L; + private AIState intendedPrimaryAIState; + private Point3D lastPositionBeforeStateChange; public AIActor(CreatureObject 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 IdleState(); + setIntendedPrimaryAIState(this.currentState); // to switch back to after aggro regenTask = scheduler.scheduleAtFixedRate(() -> { try { if(creature.getHealth() < creature.getMaxHealth() && !creature.isInCombat() && creature.getPosture() != 13 && creature.getPosture() != 14) @@ -95,14 +102,25 @@ public class AIActor { try { if(creature == null || creature.getObservers().isEmpty() || creature.isInCombat() || isStalking) return; - creature.getObservers().stream().map(Client::getParent).filter(obj -> obj.inRange(creature.getWorldPosition(), 10)).forEach((obj) -> { - if(new Random().nextFloat() <= 0.33 || creature.isInCombat() || isStalking) { + creature.getObservers().stream().map(Client::getParent).filter(obj -> obj.inRange(creature.getWorldPosition(), 15)).forEach((obj) -> { + if(new Random().nextFloat() <= 0.5 || creature.isInCombat() || isStalking) { /*if(mobileTemplate.isStalker()) { setFollowObject((CreatureObject) obj); setCurrentState(new StalkState()); } else */ - addDefender((CreatureObject) obj); - + + 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) { @@ -301,11 +319,23 @@ public class AIActor { case StateResult.DEAD: setCurrentState(new DeathState()); + break; case StateResult.FINISHED: case StateResult.UNFINISHED: return; case StateResult.IDLE: setCurrentState(new IdleState()); + return; + case StateResult.PATROL: + setCurrentState(new PatrolState()); + return; + case StateResult.LOITER: + setCurrentState(new LoiterState()); + return; + case StateResult.FOLLOW: + setCurrentState(new FollowState()); + return; + //case StateResult.NONE: // System.out.println("State action returned error result"); @@ -385,7 +415,7 @@ public class AIActor { public void setPatrolPoints(Vector patrolPoints){ this.patrolPoints = patrolPoints; - setMovementPoints(patrolPoints); + //setMovementPoints(patrolPoints); } public int getPatrolPointIndex() { @@ -451,5 +481,26 @@ public class AIActor { public void setWaitStartTime(long waitStartTime) { this.waitStartTime = waitStartTime; } + + public Vector getPatrolPoints() { + return patrolPoints; + } + + public AIState getIntendedPrimaryAIState() { + return intendedPrimaryAIState; + } + + public void setIntendedPrimaryAIState(AIState intendedPrimaryAIState) { + this.intendedPrimaryAIState = intendedPrimaryAIState; + } + + public Point3D getLastPositionBeforeStateChange() { + return lastPositionBeforeStateChange; + } + + public void setLastPositionBeforeStateChange( + Point3D lastPositionBeforeStateChange) { + this.lastPositionBeforeStateChange = lastPositionBeforeStateChange; + } } diff --git a/src/services/ai/AIService.java b/src/services/ai/AIService.java index cc477b63..b492da20 100644 --- a/src/services/ai/AIService.java +++ b/src/services/ai/AIService.java @@ -33,6 +33,7 @@ import resources.datatables.GcwType; import resources.objects.creature.CreatureObject; import resources.objects.group.GroupObject; import resources.objects.player.PlayerObject; +import services.ai.states.AIState; import services.ai.states.IdleState; import services.ai.states.LoiterState; import services.ai.states.PatrolState; @@ -174,7 +175,9 @@ public class AIService { return; actor.setPatrolPoints(patrolpoints); - actor.setCurrentState(new PatrolState()); + AIState intendedPrimaryAIState = new PatrolState(); + actor.setIntendedPrimaryAIState(intendedPrimaryAIState); + actor.setCurrentState(intendedPrimaryAIState); } public void setPatrol(CreatureObject creature, boolean active){ @@ -182,8 +185,11 @@ public class AIService { if (actor==null) return; - if (active) - actor.setCurrentState(new PatrolState()); + if (active){ + AIState intendedPrimaryAIState = new PatrolState(); + actor.setIntendedPrimaryAIState(intendedPrimaryAIState); + actor.setCurrentState(intendedPrimaryAIState); + } else actor.setCurrentState(new IdleState()); } @@ -198,6 +204,20 @@ public class AIService { actor.setLoiterDestination(currentDestination); actor.setMinLoiterDist(minDist); actor.setMaxLoiterDist(maxDist); - actor.setCurrentState(new LoiterState()); + AIState intendedPrimaryAIState = new LoiterState(); + actor.setIntendedPrimaryAIState(intendedPrimaryAIState); + actor.setCurrentState(intendedPrimaryAIState); } + + public void logAI(String logMsg){ + if (checkDeveloperIdentity()){ + System.err.println("AI-LOG: " + logMsg); + } + } + + public boolean checkDeveloperIdentity(){ + if (System.getProperty("user.name").equals("Charon")) + return true; + return false; + } } diff --git a/src/services/ai/states/AIState.java b/src/services/ai/states/AIState.java index d7354dc7..1b2b84e7 100644 --- a/src/services/ai/states/AIState.java +++ b/src/services/ai/states/AIState.java @@ -21,6 +21,7 @@ ******************************************************************************/ package services.ai.states; +import java.util.NoSuchElementException; import java.util.Vector; import main.NGECore; @@ -50,6 +51,9 @@ public abstract class AIState { public static final byte DEAD = 3; // for deaggro or idling public static final byte IDLE = 4; + public static final byte PATROL = 5; + public static final byte LOITER = 6; + public static final byte FOLLOW = 7; } @@ -64,17 +68,28 @@ public abstract class AIState { boolean finished = false; float dx, dz, newX = 0, newY = 0, newZ = 0; Vector movementPoints = actor.getMovementPoints(); + Vector patrolPoints = actor.getPatrolPoints(); CellObject cell = null; //while(!finished && movementPoints.size() != 0) { - if (movementPoints.size() == 0) { - return false; + if ((!(actor.getCurrentState() instanceof FollowState)) && movementPoints.size() == 0 && patrolPoints.size() == 0) { + return false; } - targetPosition = movementPoints.firstElement(); + if (movementPoints.size() != 0){ + try{ + targetPosition = movementPoints.firstElement(); + } catch (NoSuchElementException e) { + targetPosition = currentPosition; + } + } if (actor.getCurrentState().getClass().equals(PatrolState.class)){ - targetPosition = movementPoints.get(actor.getPatrolPointIndex()); + targetPosition = patrolPoints.get(actor.getPatrolPointIndex()); + } + + if (actor.getCurrentState().getClass().equals(FollowState.class)){ + targetPosition = actor.getFollowObject().getWorldPosition(); } Vector path = core.aiService.findPath(creature.getPlanetId(), currentPosition, targetPosition); @@ -90,6 +105,8 @@ public abstract class AIState { Point3D oldPosition = null; float pathDistance = 0; + + for(int i = 1; i < path.size() && !finished; i++) { Point3D currentPathPosition = path.get(i); @@ -104,7 +121,7 @@ public abstract class AIState { finished = true; - if(currentPosition.getWorldPosition().getDistance(currentPathPosition.getWorldPosition()) <= stopDistance && cell == creature.getContainer()) { + if(movementPoints.size() != 0 && currentPosition.getWorldPosition().getDistance(currentPathPosition.getWorldPosition()) <= stopDistance && cell == creature.getContainer()) { if(i == path.size() - 1) movementPoints.remove(0); finished = false; @@ -273,6 +290,128 @@ public abstract class AIState { return true; } + public boolean findNewFollowPosition(AIActor actor, float speed, float stopDistance, Point3D newPosition) { + + speed *= 0.5; // 2 updates per second + CreatureObject creature = actor.getCreature(); + NGECore core = NGECore.getInstance(); + Point3D currentPosition = creature.getPosition(); + Point3D targetPosition = null; + float maxDistance = stopDistance; + boolean finished = false; + float dx, dz, newX = 0, newY = 0, newZ = 0; + Vector movementPoints = actor.getMovementPoints(); + Vector patrolPoints = actor.getPatrolPoints(); + CellObject cell = null; + //while(!finished && movementPoints.size() != 0) { + + if ((!(actor.getCurrentState() instanceof FollowState)) && movementPoints.size() == 0 && patrolPoints.size() == 0) { + return false; + } + + if (movementPoints.size() != 0) + targetPosition = movementPoints.firstElement(); + + if (actor.getCurrentState().getClass().equals(PatrolState.class)){ + targetPosition = patrolPoints.get(actor.getPatrolPointIndex()); + } + + if (actor.getCurrentState().getClass().equals(FollowState.class)){ + targetPosition = actor.getFollowObject().getWorldPosition(); + } + + Vector path = core.aiService.findPath(creature.getPlanetId(), currentPosition, targetPosition); + + float distanceToTarget = targetPosition.getWorldPosition().getDistance(creature.getWorldPosition()); + + if(distanceToTarget > stopDistance) { + maxDistance = Math.min(speed, distanceToTarget - stopDistance); + } else { + return false; + } + + Point3D oldPosition = null; + float pathDistance = 0; + + + + for(int i = 1; i < path.size() && !finished; i++) { + + Point3D currentPathPosition = path.get(i); + cell = currentPathPosition.getCell(); + + if(oldPosition == null) + oldPosition = path.get(0); + Point3D oldWorldPos = oldPosition.getWorldPosition(); + + pathDistance += oldWorldPos.getDistance(currentPathPosition.getWorldPosition()); + if(pathDistance >= maxDistance || i == path.size() - 1 || currentPathPosition.getCell() != creature.getContainer()) { + + finished = true; + + if(movementPoints.size() != 0 && currentPosition.getWorldPosition().getDistance(currentPathPosition.getWorldPosition()) <= stopDistance && cell == creature.getContainer()) { + if(i == path.size() - 1) + movementPoints.remove(0); + finished = false; + } else { + + if(cell == null) + oldPosition = oldPosition.getWorldPosition(); + else { + if(oldPosition.getCell() == null) + oldPosition = core.simulationService.convertPointToModelSpace(oldPosition, cell.getContainer()); + } + + if(pathDistance > maxDistance) { + + float distance = oldWorldPos.getDistance(currentPathPosition.getWorldPosition()); + float travelDistance = distance - (pathDistance - maxDistance); + // temp fix for melee npcs + travelDistance *= 1.3; + if(travelDistance <= 0) { + newX = currentPathPosition.x; + newZ = currentPathPosition.z; + } else { + + if(distance > 0) { + dx = currentPathPosition.x - oldPosition.x; + dz = currentPathPosition.z - oldPosition.z; + float deltaDist = (float) Math.sqrt((dx * dx) + (dz * dz)); + newX = (float) (oldPosition.x + (speed * (dx / deltaDist))); + newZ = (float) (oldPosition.z + (speed * (dz / deltaDist))); + + } else { + newX = currentPathPosition.x; + newZ = currentPathPosition.z; + } + + } + + if(cell == null) { + float height = core.terrainService.getHeight(creature.getPlanetId(), newX, newZ); + newY = height; + } else { + newY = currentPathPosition.y; + } + } + } + + } else { + newX = currentPathPosition.x; + newZ = currentPathPosition.z; + newY = core.terrainService.getHeight(creature.getPlanetId(), newX, newZ); + } + oldPosition = currentPathPosition; + } + //} + newPosition.x = newX; + newPosition.y = newY; + newPosition.z = newZ; + newPosition.setCell(cell); + + return true; + } + public void doMove(AIActor actor) { NGECore core = NGECore.getInstance(); @@ -320,7 +459,7 @@ public abstract class AIState { } public void doPatrol(AIActor actor) { - + //NGECore.getInstance().aiService.logAI("AI STATE doPatrol"); NGECore core = NGECore.getInstance(); CreatureObject creature = actor.getCreature(); @@ -347,10 +486,10 @@ public abstract class AIState { // Manage Patrol points maxDistance = 1; - Point3D currentDestination = actor.getMovementPoints().get(actor.getPatrolPointIndex()); + Point3D currentDestination = actor.getPatrolPoints().get(actor.getPatrolPointIndex()); //System.out.println("currentPosition.getDistance2D(currentDestination)<1) " + currentPosition.getDistance2D(currentDestination)); if (currentPosition.getDistance2D(currentDestination)<2){ - if (actor.getPatrolPointIndex() 0.0f) { + quaternion.y *= -1; + quaternion.w *= -1; + } + +// if (newPosition.getCell()==null) +// System.out.println("newPosition.getCell() is NULL"); + try{ + core.simulationService.moveObject(creature, newPosition, quaternion, creature.getMovementCounter(), speed, newPosition.getCell()); + } catch (NullPointerException e) { + // Just to identify what exactly is null here +// if (creature==null) +// System.out.print("creature==null" ); +// if (newPosition==null) +// System.out.print("newPosition==null" ); +// if (quaternion==null) +// System.out.print("quaternion==null" ); +// if (newPosition.getCell()==null) +// System.out.print("newPosition.getCell()==null" ); +// +// System.out.print("creature.getMovementCounter() " + creature.getMovementCounter()); } } diff --git a/src/services/ai/states/FollowState.java b/src/services/ai/states/FollowState.java new file mode 100644 index 00000000..01efb83a --- /dev/null +++ b/src/services/ai/states/FollowState.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2013 + * + * 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 . + * + * 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.states; + +import main.NGECore; +import resources.objects.creature.CreatureObject; +import services.ai.AIActor; + +public class FollowState extends AIState { + + @Override + public byte onEnter(AIActor actor) { + // TODO Auto-generated method stub + NGECore.getInstance().aiService.logAI("FOLLOW ENTERED!"); + CreatureObject creature = actor.getCreature(); + if(creature.getPosture() == 14) + return StateResult.DEAD; + actor.scheduleMovement(); + actor.scheduleRecovery(); + return StateResult.UNFINISHED; + } + + @Override + public byte onExit(AIActor actor) { + // TODO Auto-generated method stub + NGECore.getInstance().aiService.logAI("EXITING FOLLOW STATE"); + return 0; + } + + @Override + public byte move(AIActor actor) { + // TODO Auto-generated method stub + //System.out.println("move in patrolState"); + doFollow(actor); +// if (actor.getCreature().getPlanet().getName().equals("dathomir")) +// NGECore.getInstance().aiService.logAI("WalkingSpeed " + actor.getCreature().getWalkSpeed()); + actor.scheduleMovement(); + return StateResult.UNFINISHED; + } + + @Override + public byte recover(AIActor actor) { + // TODO Auto-generated method stub + return 0; + } + +} diff --git a/src/services/ai/states/LoiterState.java b/src/services/ai/states/LoiterState.java index 1358185a..7acf9458 100644 --- a/src/services/ai/states/LoiterState.java +++ b/src/services/ai/states/LoiterState.java @@ -28,7 +28,6 @@ public class LoiterState extends AIState { @Override public byte onEnter(AIActor actor) { - // TODO Auto-generated method stub CreatureObject creature = actor.getCreature(); if(creature.getPosture() == 14) return StateResult.DEAD; @@ -45,7 +44,6 @@ public class LoiterState extends AIState { @Override public byte move(AIActor actor) { - // TODO Auto-generated method stub doLoiter(actor); actor.scheduleMovement(); return StateResult.UNFINISHED; diff --git a/src/services/ai/states/PatrolState.java b/src/services/ai/states/PatrolState.java index 27218b1c..2a571391 100644 --- a/src/services/ai/states/PatrolState.java +++ b/src/services/ai/states/PatrolState.java @@ -21,6 +21,7 @@ ******************************************************************************/ package services.ai.states; +import main.NGECore; import resources.objects.creature.CreatureObject; import services.ai.AIActor; @@ -29,6 +30,7 @@ public class PatrolState extends AIState { @Override public byte onEnter(AIActor actor) { // TODO Auto-generated method stub + NGECore.getInstance().aiService.logAI("PATROL ENTERED!"); CreatureObject creature = actor.getCreature(); if(creature.getPosture() == 14) return StateResult.DEAD; @@ -40,6 +42,8 @@ public class PatrolState extends AIState { @Override public byte onExit(AIActor actor) { // TODO Auto-generated method stub + NGECore.getInstance().aiService.logAI("EXITING PATROL STATE"); + actor.setLastPositionBeforeStateChange(actor.getCreature().getWorldPosition()); return 0; } @@ -48,6 +52,8 @@ public class PatrolState extends AIState { // TODO Auto-generated method stub //System.out.println("move in patrolState"); doPatrol(actor); +// if (actor.getCreature().getPlanet().getName().equals("dathomir")) +// NGECore.getInstance().aiService.logAI("WalkingSpeed " + actor.getCreature().getWalkSpeed()); actor.scheduleMovement(); return StateResult.UNFINISHED; } diff --git a/src/services/ai/states/RetreatState.java b/src/services/ai/states/RetreatState.java index 315f8cb3..00aa451e 100644 --- a/src/services/ai/states/RetreatState.java +++ b/src/services/ai/states/RetreatState.java @@ -21,6 +21,8 @@ ******************************************************************************/ package services.ai.states; +import main.NGECore; +import engine.resources.scene.Point3D; import services.ai.AIActor; public class RetreatState extends AIState { @@ -32,6 +34,7 @@ public class RetreatState extends AIState { actor.removeDefender(defender); } }*/ + NGECore.getInstance().aiService.logAI("RetreatState ENTER "); actor.setFollowObject(null); actor.scheduleMovement(); return StateResult.UNFINISHED; @@ -40,17 +43,38 @@ public class RetreatState extends AIState { @Override public byte onExit(AIActor actor) { // TODO Auto-generated method stub + //actor.setCurrentState(actor.getIntendedPrimaryAIState()); return StateResult.FINISHED; } @Override public byte move(AIActor actor) { - actor.setNextPosition(actor.getSpawnPosition()); + + Point3D retreatPosition = actor.getSpawnPosition(); + if (actor.getIntendedPrimaryAIState() instanceof PatrolState) + retreatPosition = actor.getLastPositionBeforeStateChange(); + + actor.setNextPosition(retreatPosition); + actor.getMovementPoints().clear(); + actor.getMovementPoints().add(retreatPosition); doMove(actor); - if(actor.getCreature().getWorldPosition().getDistance(actor.getSpawnPosition()) > 3) { + + if (actor.getIntendedPrimaryAIState().getClass().equals(FollowState.class)) + return StateResult.FOLLOW; + + if(actor.getCreature().getWorldPosition().getDistance(retreatPosition) > 5) { + //NGECore.getInstance().aiService.logAI("RetreatState ENTER move if case dist " + actor.getCreature().getWorldPosition().getDistance(retreatPosition)); actor.scheduleMovement(); } else { - return StateResult.IDLE; + //NGECore.getInstance().aiService.logAI("RetreatState ENTER move else case"); + if (actor.getIntendedPrimaryAIState().getClass().equals(IdleState.class)) + return StateResult.IDLE; + if (actor.getIntendedPrimaryAIState().getClass().equals(PatrolState.class)) + return StateResult.PATROL; + if (actor.getIntendedPrimaryAIState().getClass().equals(LoiterState.class)) + return StateResult.LOITER; + if (actor.getIntendedPrimaryAIState().getClass().equals(FollowState.class)) + return StateResult.FOLLOW; } return StateResult.UNFINISHED; } diff --git a/src/services/combat/CombatService.java b/src/services/combat/CombatService.java index 2a732b7a..83c6c742 100644 --- a/src/services/combat/CombatService.java +++ b/src/services/combat/CombatService.java @@ -318,6 +318,15 @@ public class CombatService implements INetworkDispatch { core.mountService.damage((CreatureObject) target); } + if (target instanceof CreatureObject){ + CreatureObject targetCreature = (CreatureObject)target; + if (targetCreature.getCalledPet()!=null) { + AIActor petActor = (AIActor) targetCreature.getCalledPet().getAttachment("AI"); + if (petActor!=null) + petActor.addDefender(attacker); + } + } + DamageTaken event = events.new DamageTaken(); event.attacker = attacker; event.damage = damage; diff --git a/src/services/pet/MountService.java b/src/services/pet/MountService.java index e326c362..39e78ecb 100644 --- a/src/services/pet/MountService.java +++ b/src/services/pet/MountService.java @@ -36,6 +36,7 @@ import resources.datatables.State; import resources.objects.building.BuildingObject; import resources.objects.creature.CreatureObject; import resources.objects.player.PlayerObject; +import services.ai.AIActor; import engine.clientdata.ClientFileManager; import engine.clientdata.visitors.DatatableVisitor; import engine.resources.container.Traverser; @@ -184,6 +185,8 @@ public class MountService implements INetworkDispatch { mount.setFaction(actor.getFaction()); mount.setFactionStatus(actor.getFactionStatus()); mount.setOwnerId(actor.getObjectID()); + AIActor aiActor = (AIActor) mount.getAttachment("AI"); + aiActor.setFollowObject(actor); if (pcd.getTemplate().contains("vehicle")) { callVehicle(actor, pcd, player, mount); @@ -786,5 +789,5 @@ public class MountService implements INetworkDispatch { public void shutdown() { } - + } diff --git a/src/services/pet/PetService.java b/src/services/pet/PetService.java new file mode 100644 index 00000000..bf863810 --- /dev/null +++ b/src/services/pet/PetService.java @@ -0,0 +1,794 @@ +/******************************************************************************* + * Copyright (c) 2013 + * + * 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 . + * + * 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.pet; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.atomic.LongAdder; + +import protocol.swg.UpdateContainmentMessage; +import main.NGECore; +import resources.common.OutOfBand; +import resources.datatables.DisplayType; +import resources.datatables.Options; +import resources.datatables.Posture; +import resources.datatables.State; +import resources.objects.building.BuildingObject; +import resources.objects.creature.CreatureObject; +import resources.objects.player.PlayerObject; +import services.ai.AIActor; +import services.ai.states.FollowState; +import engine.clientdata.ClientFileManager; +import engine.clientdata.visitors.DatatableVisitor; +import engine.resources.container.Traverser; +import engine.resources.objects.SWGObject; +import engine.resources.service.INetworkDispatch; +import engine.resources.service.INetworkRemoteEvent; + +public class PetService implements INetworkDispatch { + + private NGECore core; + + public PetService(NGECore core) { + this.core = core; + } + + + + public void generateMount(CreatureObject actor, SWGObject deed, String vehicleTemplate, String pcdTemplate) { + return; // NOT IMPLEMENTED + } + + public void call(CreatureObject actor, CreatureObject pet, SWGObject pcd) { + if (actor == null) { + return; + } + + if (pcd == null) { + System.out.println("(pcd == null)"); + actor.sendSystemMessage(OutOfBand.ProsePackage("@pet/pet_menu:cant_call"), DisplayType.Broadcast); + return; + } + + if (isMounted(actor)) { + actor.sendSystemMessage(OutOfBand.ProsePackage("@pet/pet_menu:cannot_call_another_rideable"), DisplayType.Broadcast); + return; + } + + if (actor.isInCombat()) { + actor.sendSystemMessage(OutOfBand.ProsePackage("@pet/pet_menu:cannot_call_in_combat"), DisplayType.Broadcast); + return; + } + + if (actor.getContainer() != null) { + actor.sendSystemMessage(OutOfBand.ProsePackage("@pet/pet_menu:cannot_call_indoors"), DisplayType.Broadcast); + return; + } + + if (actor.getPosture() == Posture.Dead) { + actor.sendSystemMessage(OutOfBand.ProsePackage("@pet/pet_menu:cannot_call_while_dead"), DisplayType.Broadcast); + return; + } + +// if (pcd.getSlottedObject("inventory") == null) { +// System.out.println("(pcd.getSlottedObject(inventory)"); +// actor.sendSystemMessage(OutOfBand.ProsePackage("@pet/pet_menu:cant_call"), DisplayType.Broadcast); +// return; +// } + + + + PlayerObject player = (PlayerObject) actor.getSlottedObject("ghost"); + + if (player == null) { + return; + } + + //CreatureObject mount = (CreatureObject) core.objectService.getObject((Long) pcd.getAttachment("companionId")); + + if (pet == null) { + System.out.println("mount == null)"); + // Somehow the vehicle object has got lost + actor.sendSystemMessage(OutOfBand.ProsePackage("@pet/pet_menu:cant_call"), DisplayType.Broadcast); + return; + } + + //pet.setAttachment("pcdAppearanceFilename", pcd.getTemplateData().getAttribute("appearanceFilename")); + + pet.setFaction(actor.getFaction()); + pet.setFactionStatus(actor.getFactionStatus()); + pet.setOwnerId(actor.getObjectID()); + AIActor aiActor = (AIActor) pet.getAttachment("AI"); + aiActor.setFollowObject(actor); + aiActor.setCurrentState(new FollowState()); + + if (pcd.getTemplate().contains("vehicle")) { + callVehicle(actor, pcd, player, pet); + } else { + callMount(actor, pcd, player, pet); + } + } + + private void callVehicle(CreatureObject actor, SWGObject pcd, PlayerObject player, CreatureObject mount) { + if ((mount.getLevel() - actor.getLevel()) > 5) { + actor.sendSystemMessage(OutOfBand.ProsePackage("@pet/pet_menu:cant_call_level"), DisplayType.Broadcast); + return; + } + + // FIXME Movement skillmod should always be used instead of CREO4 speed vars directly. Movement skillmod should NEVER be 0 unless rooted. Currently it is, which is wrong. + //if (actor.getSkillModBase("movement") == 0) { + //actor.sendSystemMessage(OutOfBand.ProsePackage("@pet/pet_menu:cant_call_vehicle_rooted"), DisplayType.Broadcast); + //return; + //} + + if (actor.getPlanet().getName().contains("kashyyyk") && !actor.getPlanet().getName().contains("_main")) { + actor.sendSystemMessage(OutOfBand.ProsePackage("@pet/pet_menu:vehicle_restricted_scene"), DisplayType.Broadcast); + //mount_restricted_scene for creature mounts + return; + } + + if (player.isCallingCompanion()) { + actor.sendSystemMessage(OutOfBand.ProsePackage("@pet/pet_menu:cant_call_1sec"), DisplayType.Broadcast); + return; + } + + storeAll(actor); + + player.setCallingCompanion(true); + + if (actor.getTefTime() > 0) { + actor.sendSystemMessage(OutOfBand.ProsePackage("@pet/pet_menu:call_vehicle_delay", actor.getTefTime()), DisplayType.Broadcast); + } + + try { + while (actor.getTefTime() > 0) { + Thread.sleep(3000); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + + SWGObject datapad = actor.getSlottedObject("datapad"); + + if (datapad == null) { + return; + } + + datapad.viewChildren(actor, false, false, new Traverser() { + + public void process(SWGObject pcd) { + if (pcd.getAttachment("companionId") != null && mount.getObjectID() == ((Long) pcd.getAttachment("companionId"))) { + if (pcd.getSlottedObject("inventory") != null) { + LongAdder adder = new LongAdder(); + pcd.getSlottedObject("inventory").viewChildren(actor, false, false, (obj) -> adder.increment()); + + if (adder.intValue() == 1) { + pcd.getSlottedObject("inventory").remove(mount); + mount.setPosition(actor.getPosition().clone()); + mount.setOrientation(actor.getOrientation().clone()); + mount.setPlanet(actor.getPlanet()); + core.simulationService.add(mount, actor.getWorldPosition().x, actor.getWorldPosition().z, false); + } + } + } + } + + }); + + player.setCallingCompanion(false); + } + + private void callMount(CreatureObject actor, SWGObject pcd, PlayerObject player, CreatureObject mount) { + return; // NOT IMPLEMENTED + } + + public void damage(CreatureObject mount) { + // alter components to progressively reduce the painting. + //stfs + //customization_fading_veh + //customization_gone_veh + + if (mount == null) { + return; + } + + CreatureObject owner = (CreatureObject) core.objectService.getObject(mount.getOwnerId()); + + if (!mount.getTemplate().contains("vehicle")) { + dismount(owner, mount); + store(owner, mount); + mount.setConditionDamage(0); + return; + } + + if (mount.getConditionDamage() >= mount.getMaximumCondition()) { + disable(mount); + } + } + + public void disable(CreatureObject mount) { + mount.setOptions(Options.DISABLED, true); + + mount.viewChildren(mount, false, false, new Traverser() { + + public void process(SWGObject object) { + if (object == null || !(object instanceof CreatureObject)) { + return; + } + + CreatureObject rider = (CreatureObject) object; + + dismount(rider, mount); + + rider.sendSystemMessage(OutOfBand.ProsePackage("@pet/pet_menu:veh_disabled"), DisplayType.Broadcast); + } + + }); + } + + public boolean canRepair(CreatureObject actor, CreatureObject mount) { + if (actor == null || mount == null) { + return false; + } + + if (!mount.getOption(Options.MOUNT)) { + return false; + } + + if (!mount.getTemplate().contains("vehicle")) { + return false; + } + + if (mount.getOwnerId() != actor.getObjectID()) { + return false; + } + + if (mount.getOption(Options.DISABLED)) { + return false; + } + + if (mount.getConditionDamage() == 0) { + return false; + } + + List surroundings = core.simulationService.get(mount.getPlanet(), mount.getPosition().x, mount.getPosition().z, 32); + + for (SWGObject object : surroundings) { + if (object instanceof BuildingObject) { + if (object.getTemplate().contains("garage")) { + return true; + } + } + } + + return false; + } + + public void repair(CreatureObject repairer, CreatureObject mount) { + if (repairer == null || mount == null) { + return; + } + + if (!mount.getOption(Options.MOUNT)) { + return; + } + + if (core.objectService.getObject(mount.getOwnerId()) != repairer) { + return; + } + + if (mount.getOption(Options.DISABLED)) { + repairer.sendSystemMessage(OutOfBand.ProsePackage("@pet/pet_menu:cannot_repair_disabled"), DisplayType.Broadcast); + return; + } + + if (mount.getConditionDamage() == 0) { + repairer.sendSystemMessage(OutOfBand.ProsePackage("@pet/pet_menu:undamaged_vehicle"), DisplayType.Broadcast); + return; + } + + // This should produce an SUI window regarding cost and cost might be determined differently. + // But at the moment it's not important. + + if (!canRepair(repairer, mount)) { + repairer.sendSystemMessage(OutOfBand.ProsePackage("@pet/pet_menu:repair_unrecognized_garages"), DisplayType.Broadcast); + return; + } + + int cost = mount.getConditionDamage(); + + if (repairer.getCashCredits() < cost) { + repairer.sendSystemMessage(OutOfBand.ProsePackage("@pet/pet_menu:repair_failed_due_to_funds"), DisplayType.Broadcast); + return; + } + + mount.setConditionDamage(0); + + mount.setOptions(Options.DISABLED, false); + + repairer.sendSystemMessage(OutOfBand.ProsePackage("@pet/pet_menu:repaired_to_max"), DisplayType.Broadcast); + } + + public void mount(CreatureObject rider, CreatureObject mount) { + // Check if mount may be mounted // This is already checked in canMount + + if (rider == null) { + return; + } + + if (mount == null) { + return; + } + + if (!mount.getOption(Options.MOUNT)) { + return; + } + + if (mount.getOption(Options.DISABLED)) { + rider.sendSystemMessage(OutOfBand.ProsePackage("@pet/pet_menu:cant_mount_veh_disabled"), DisplayType.Broadcast); + return; + } + + if (rider.isInStealth()) { + rider.sendSystemMessage(OutOfBand.ProsePackage("@pet/pet_menu:no_mount_stealth"), DisplayType.Broadcast); + return; + } + + if (!canMount(rider, mount)) { + rider.sendSystemMessage(OutOfBand.ProsePackage("@pet/pet_menu:cant_mount"), DisplayType.Broadcast); + return; + } + + mount._add(rider); + mount.notifyObservers(new UpdateContainmentMessage(rider.getObjectID(), mount.getObjectID(), 4), true); + core.simulationService.remove(rider, rider.getWorldPosition().x, rider.getWorldPosition().z, false); + + rider.setState(State.RidingMount, true); + mount.setState(State.MountedCreature, true); + + // For some reason SOE decided the mount would need the following posture to be set for the character to have the driving or riding animation. + mount.setPosture((mount.getTemplate().contains("vehicle")) ? Posture.DrivingVehicle : Posture.RidingCreature); + + if (!mount.getSlotNameForObject(rider).equals("rider1")) { + core.buffService.addBuffToCreature(rider, "vehicle_passenger", mount); + } + } + + public CreatureObject getMount(SWGObject pcd) { + if (pcd == null) { + return null; + } + + if (pcd.getAttachment("companionId") == null) { + return null; + } + + CreatureObject mount = (CreatureObject) core.objectService.getObject((Long) pcd.getAttachment("companionId")); + + if (mount == null) { + return null; + } + + if (mount.getContainer() != null) { + return null; + } + + return mount; + } + + public CreatureObject getCompanion(CreatureObject actor) { + if (actor == null) { + return null; + } + + if (isMounted(actor)) { + return (CreatureObject) actor.getContainer(); + } + + SWGObject datapad = actor.getSlottedObject("datapad"); + + if (datapad == null) { + return null; + } + + final List companions = new ArrayList(); + + datapad.viewChildren(actor, false, false, new Traverser() { + + public void process(SWGObject pcd) { + if (pcd.getAttachment("companionId") != null) { + if (pcd.getSlottedObject("inventory") != null) { + LongAdder adder = new LongAdder(); + pcd.getSlottedObject("inventory").viewChildren(actor, false, false, (obj) -> adder.increment()); + + if (adder.intValue() == 0) { + CreatureObject companion = (CreatureObject) core.objectService.getObject((Long) pcd.getAttachment("companionId")); + companions.add(companion); + } + } + } + } + + }); + + if (companions.size() > 0) { + return companions.get(0); + } else { + return null; + } + } + + public boolean isMounted(CreatureObject rider) { + if (rider.getContainer() == null) { + return false; + } + + if (!(rider.getContainer() instanceof CreatureObject)) { + return false; + } + + CreatureObject mount = (CreatureObject) rider.getContainer(); + + if (!mount.getOption(Options.MOUNT)) { + return false; + } + + return true; + } + + public boolean isMounted(SWGObject actor, CreatureObject mount) { + return actor.getContainer() == mount; + } + + public boolean canMount(CreatureObject rider, CreatureObject mount) { + if (mount == null) { + return false; + } + + if (!mount.getOption(Options.MOUNT)) { + return false; + } + + if (isMounted(rider)) { + return false; + } + + if (rider.getObjectID() == mount.getOwnerId()) { + return true; + } + + // It's a potential passenger + + // See if there's driver first + if (mount.getSlottedObject("rider") == null) { + return false; + } + + // Check if there are any passenger slots left + + LongAdder adder = new LongAdder(); + + try { + mount.viewChildren(mount, false, false, (object) -> adder.increment()); + } catch(Exception ex) { + + } + + int passengers = adder.intValue(); + + try { + DatatableVisitor visitor = ClientFileManager.loadFile("datatables/mount/saddle_appearance_map.iff", DatatableVisitor.class); + + for (int i = 0; i < visitor.getRowCount(); i++) { + if (visitor.getObject(i, 2).equals(mount.getAttachment("pcdAppearanceFilename"))) { // saddle_appearance_filename + if (passengers >= (int) visitor.getObject(i, 1)) { // saddle_capacity + return false; + } + + break; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + + CreatureObject owner = (CreatureObject) NGECore.getInstance().objectService.getObject(mount.getOwnerId()); + + if (owner == null) { + return false; + } + + if (owner.isAttackableBy(rider)) { + return false; + } + + if (rider.getGroupId() == 0 || rider.getGroupId() != owner.getGroupId()) { + return false; + } + + return true; + } + + public void dismount(CreatureObject rider, CreatureObject mount) { + + if (rider == null || mount == null) { + return; + } + + if (!isMounted(rider)) { + return; + } + + if (!mount.getOption(Options.MOUNT)) { + return; + } + + // Dismount all passengers + if (rider.getObjectID() == mount.getOwnerId()) { + + mount.viewChildren(mount, false, false, new Traverser() + { + public void process(SWGObject passenger) + { + if (passenger.getObjectId() != mount.getOwnerId()) dismount((CreatureObject) passenger, mount); + } + + }); + } + + // Remove rider from mount + mount._remove(rider); + + // Set mount states and stuff + if (rider.getObjectID() == mount.getOwnerId()) { + mount.setStateBitmask(mount.getOptionsBitmask() & ~State.MountedCreature); + mount.setState(State.MountedCreature, false); + } + + // Set rider states and stuff + rider.setStateBitmask(rider.getOptionsBitmask() & ~State.RidingMount); + rider.setState(State.RidingMount, false); + rider.setPosture(Posture.Upright); + + // Update observers and quadtree + mount.notifyObservers(new UpdateContainmentMessage(rider.getObjectID(), 0, -1), true); + core.simulationService.teleport(rider, mount.getWorldPosition(), mount.getOrientation(), 0); + core.simulationService.add(rider, mount.getWorldPosition().x, mount.getWorldPosition().z, false); + + core.buffService.clearBuffs(mount); + core.buffService.removeBuffFromCreature(rider, rider.getBuffByName("vehicle_passenger")); + + // Store mount if it's a creature + if (!mount.getTemplate().contains("vehicle") && rider.getObjectID() == mount.getOwnerId()) { + store(rider, mount); + } + } + + /* + * They seem to have been stored in the inventory of the pcd object. + * Judging by the fact pcds have inventories and many other things don't. + */ + public void store(CreatureObject storer, CreatureObject mount) { + if (mount == null) { + System.err.println("MountService:store(): mount is null; this should never be the case."); + return; + } + + if (mount.getContainer() != null) { + return; + } + + if (mount.getOwnerId() == 0) { + return; + } + + CreatureObject owner = (CreatureObject) core.objectService.getObject(mount.getOwnerId()); + + if (owner == null || storer == null || owner != storer) { + return; + } + + PlayerObject player = (PlayerObject) owner.getSlottedObject("ghost"); + + if (player.isCallingCompanion()) { + storer.sendSystemMessage(OutOfBand.ProsePackage("@pet/pet_menu:cant_store_1sec"), DisplayType.Broadcast); + return; + } + + if (isMounted(owner, mount)) { + //storer.sendSystemMessage(OutOfBand.ProsePackage("@pet/pet_menu:must_dismount"), DisplayType.Broadcast); + //return; + dismount(storer, mount); + } + + if (owner.getTefTime() > 0) { + owner.sendSystemMessage(OutOfBand.ProsePackage("@pet/pet_menu:prose_cant_store_yet", owner.getTefTime()), DisplayType.Broadcast); + return; + } + + SWGObject datapad = owner.getSlottedObject("datapad"); + + if (datapad == null) { + return; + } + + datapad.viewChildren(owner, false, false, new Traverser() { + + public void process(SWGObject pcd) { + if (pcd.getAttachment("companionId") != null && mount.getObjectID() == ((Long) pcd.getAttachment("companionId"))) { + if (pcd.getSlottedObject("inventory") != null) { + LongAdder adder = new LongAdder(); + pcd.getSlottedObject("inventory").viewChildren(owner, false, false, (obj) -> adder.increment()); + + if (adder.intValue() == 0) { + core.simulationService.remove(mount, mount.getWorldPosition().x, mount.getWorldPosition().z, true); + pcd.getSlottedObject("inventory").add(mount); + } + } + } + } + + }); + } + + public void storeAll(CreatureObject actor) { + if (actor == null) { + return; + } + + while (getCompanion(actor) != null) { + SWGObject mount = getCompanion(actor); + + SWGObject datapad = actor.getSlottedObject("datapad"); + + if (datapad == null) { + return; + } + + datapad.viewChildren(actor, false, false, new Traverser() { + + public void process(SWGObject pcd) { + if (pcd.getAttachment("companionId") != null && mount.getObjectID() == ((Long) pcd.getAttachment("companionId"))) { + if (pcd.getSlottedObject("inventory") != null) { + LongAdder adder = new LongAdder(); + pcd.getSlottedObject("inventory").viewChildren(actor, false, false, (obj) -> adder.increment()); + + if (adder.intValue() == 0) { + core.simulationService.remove(mount, mount.getWorldPosition().x, mount.getWorldPosition().z, true); + pcd.getSlottedObject("inventory").add(mount); + } + } + } + } + + }); + } + } + + public void destroy(CreatureObject destroyer, SWGObject pcd) { + if (destroyer == null || pcd == null || pcd.getGrandparent() == null) { + return; + } + + if (destroyer.getObjectID() != pcd.getGrandparent().getObjectID()) { + return; + } + + if (pcd.getAttachment("companionId") == null) { + return; + } + + CreatureObject mount = (CreatureObject) core.objectService.getObject((Long) pcd.getAttachment("companionId")); + + dismount(destroyer, mount); + + store(destroyer, mount); + + String type = mount.getTemplate(); + + core.objectService.destroyObject(mount); + core.objectService.destroyObject(pcd); + + if (type.contains("vehicle")) { + destroyer.sendSystemMessage(OutOfBand.ProsePackage("@pet/pet_menu:vehicle_released"), DisplayType.Broadcast); + } else { + destroyer.sendSystemMessage(OutOfBand.ProsePackage("@pet/pet_menu:pet_released"), DisplayType.Broadcast); + } + } + + public void insertOpcodes(Map arg0, Map arg1) { + + } + + public void shutdown() { + + } + + + public void generatePet(CreatureObject actor, SWGObject deed, String petTemplate, String pcdTemplate) { + if (actor == null || petTemplate == null || pcdTemplate == null) { + return; + } + + if (actor.getSlottedObject("ghost") == null) { + actor.sendSystemMessage(OutOfBand.ProsePackage("@pet/pet_menu:failed_to_call_vehicle"), DisplayType.Broadcast); + return; + } + + SWGObject datapad = actor.getSlottedObject("datapad"); + + if (datapad == null) { + return; + } + + // Unsure if these are the right attributes. It doesn't generate the vehicle if the datapad has max # of vehicles. + //if (datapad.getIntAttribute("data_size") >= datapad.getIntAttribute("datapad_slots")) { + //actor.sendSystemMessage(OutOfBand.ProsePackage("@pet/pet_menu:has_max_vehicle"), DisplayType.Broadcast); + //return; + //} + + if (actor.getTefTime() > 0){ + actor.sendSystemMessage(OutOfBand.ProsePackage("@pet/pet_menu:prose_cant_generate_yet", actor.getTefTime()), DisplayType.Broadcast); + return; + } + + SWGObject pcd = core.objectService.createObject(pcdTemplate, actor.getPlanet()); + + if (pcd == null) { + return; + } + +// if (pcd.getSlottedObject("inventory") == null) { +// pcd.add(core.objectService.createObject("object/tangible/inventory/shared_character_inventory.iff", pcd.getPlanet())); +// } + + //CreatureObject vehicle = (CreatureObject) core.objectService.createObject(petTemplate, actor.getPlanet()); + + CreatureObject pet = (CreatureObject) NGECore.getInstance().staticService.spawnObject(petTemplate, actor.getPlanet().getName(), 0L, actor.getWorldPosition().x, actor.getWorldPosition().y, actor.getWorldPosition().z, actor.getOrientation().y, actor.getOrientation().w); + + if (pet == null) { + return; + } + + +// pet.setOptions(Options.MOUNT | Options.ATTACKABLE, true); +// + pet.setOwnerId(actor.getObjectID()); + actor.setCalledPet(pet); +// + pcd.setAttachment("companionId", pet.getObjectID()); +// +// //pcd.getSlottedObject("inventory").add(pet); +// + datapad.add(pcd); +// + if (deed != null) { + core.objectService.destroyObject(deed); + } + + actor.sendSystemMessage(OutOfBand.ProsePackage("@pet/pet_menu:device_added"), DisplayType.Broadcast); + + call(actor, pet, pcd); + } + +}