diff --git a/scripts/conversation/missions/bounty_droid_probot.py b/scripts/conversation/missions/bounty_droid_probot.py new file mode 100644 index 00000000..ea327232 --- /dev/null +++ b/scripts/conversation/missions/bounty_droid_probot.py @@ -0,0 +1,70 @@ +from resources.common import ConversationOption +from resources.common import OutOfBand +from resources.common import ProsePackage +from java.util import Vector +import time +import sys + +def startConversation(core, actor, npc): + convSvc = core.conversationService + + probotRequester = core.objectService.getObject(long(npc.getAttachment('probotRequester'))) + + if probotRequester is None: + core.objectService.destroyObject(npc) + return + + if probotRequester.getObjectId() != actor.getObjectId(): + prose = ProsePackage('conversation/bounty_probot', 's_6') + outOfBand = OutOfBand() + outOfBand.addProsePackage(prose) + convSvc.sendConversationMessage(actor, npc, outOfBand) + return + + mission = core.objectService.getObject(long(npc.getAttachment('attachedMission'))) + + if mission is None: + prose = ProsePackage('conversation/bounty_probot', 's_4') + outOfBand = OutOfBand() + outOfBand.addProsePackage(prose) + convSvc.sendConversationMessage(actor, npc, prose) + return + + objective = mission.getObjective() + + if objective is None: + prose = ProsePackage('conversation/bounty_probot', 's_4') + outOfBand = OutOfBand() + outOfBand.addProsePackage(prose) + convSvc.sendConversationMessage(actor, npc, prose) + return + + prose = ProsePackage('conversation/bounty_probot', 's_5') + outOfBand = OutOfBand() + outOfBand.addProsePackage(prose) + convSvc.sendConversationMessage(actor, npc, outOfBand) + + options = Vector() + optionProse = ProsePackage('conversation/bounty_probot', 's_8') + optionOOB = OutOfBand() + optionOOB.addProsePackage(optionProse) + options.add(ConversationOption(optionOOB, 0)) + convSvc.sendConversationOptions(actor, npc, options, handleBioTransmit) + + return + +def handleBioTransmit(core, actor, npc, selection): + core.conversationService.handleEndConversation(actor, npc) + actor.sendSystemMessage('@mission/mission_generic:probe_droid_takeoff', 0) + + npc.setPosture(8) + + time.sleep(7) + objective = core.objectService.getObject(long(npc.getAttachment('attachedMission'))).getObjective() + objective.beginArakydUpdate(actor) + core.objectService.destroyObject(npc) + return + +def endConversation(core, actor, npc): + core.conversationService.sendStopConversation(actor, npc, '', '') + return diff --git a/scripts/object/creature/npc/droid/imperial_probot_bounty.py b/scripts/object/creature/npc/droid/imperial_probot_bounty.py index ccad8904..de71643c 100644 --- a/scripts/object/creature/npc/droid/imperial_probot_bounty.py +++ b/scripts/object/creature/npc/droid/imperial_probot_bounty.py @@ -1,4 +1,8 @@ +from resources.datatables import Options import sys def setup(core, object): + object.setAttachment('radial_filename', 'object/conversation'); + object.setAttachment('conversationFile','missions/bounty_droid_probot') + object.setOptionsBitmask(Options.CONVERSABLE | Options.INVULNERABLE) return \ No newline at end of file diff --git a/scripts/object/tangible/mission/mission_bounty_droid_probot.py b/scripts/object/tangible/mission/mission_bounty_droid_probot.py index ccad8904..4b30916a 100644 --- a/scripts/object/tangible/mission/mission_bounty_droid_probot.py +++ b/scripts/object/tangible/mission/mission_bounty_droid_probot.py @@ -1,4 +1,5 @@ import sys def setup(core, object): + object.setAttachment('radial_filename', 'npc/bounty_probot') return \ No newline at end of file diff --git a/scripts/object/tangible/mission/mission_bounty_droid_seeker.py b/scripts/object/tangible/mission/mission_bounty_droid_seeker.py index ccad8904..516734c3 100644 --- a/scripts/object/tangible/mission/mission_bounty_droid_seeker.py +++ b/scripts/object/tangible/mission/mission_bounty_droid_seeker.py @@ -1,4 +1,5 @@ import sys def setup(core, object): + object.setAttachment('radial_filename', 'npc/bounty_seeker') return \ No newline at end of file diff --git a/scripts/radial/npc/bounty_probot.py b/scripts/radial/npc/bounty_probot.py new file mode 100644 index 00000000..d7329b30 --- /dev/null +++ b/scripts/radial/npc/bounty_probot.py @@ -0,0 +1,30 @@ +from resources.common import RadialOptions +import sys + +def createRadial(core, owner, target, radials): + radials.add(RadialOptions(0, 138, 1, '@mission/mission_generic:probe_droid_summon')) + return + +def handleSelection(core, owner, target, option): + + if option == 138 and target: + id = owner.getAttachment('bountyMissionId') + if id is None or id == 0: + owner.sendSystemMessage('@mission/mission_generic:bounty_no_mission', 0) + return + + mission = core.objectService.getObject(owner.getAttachment('bountyMissionId')) + if mission is None: + owner.sendSystemMessage('@mission/mission_generic:bounty_no_mission', 0) + return + + if mission.getObjective().isArakydActive(): + owner.sendSystemMessage('@mission/mission_generic:probe_droid_too_many', 0) + return + + if not core.terrainService.canBuildAtPosition(owner, owner.getPosition().x, owner.getPosition().z): + owner.sendSystemMessage('@mission/mission_generic:probe_droid_bad_location', 0) + return + + mission.getObjective().handleProbeDroidSummon(core, owner) + return \ No newline at end of file diff --git a/scripts/radial/npc/bounty_seeker.py b/scripts/radial/npc/bounty_seeker.py new file mode 100644 index 00000000..6c2352f6 --- /dev/null +++ b/scripts/radial/npc/bounty_seeker.py @@ -0,0 +1,41 @@ +from resources.common import RadialOptions +from resources.common import SpawnPoint +import time +import sys + +def createRadial(core, owner, target, radials): + radials.clear() + radials.add(RadialOptions(0, 138, 0, 'Track Target')) + radials.add(RadialOptions(1, 136, 0, '@mission/mission_generic:probe_droid_track_target')) # Track Bounty Target + radials.add(RadialOptions(1, 137, 0, '@mission/mission_generic:probe_droid_find_target')) # Identify Bounty Target + return + +def handleSelection(core, owner, target, option): + + if option == 136 and target or option == 138 and target: + id = owner.getAttachment('bountyMissionId') + if id is None or id == 0: + owner.sendSystemMessage('@mission/mission_generic:bounty_no_mission', 0) + return + + mission = core.objectService.getObject(owner.getAttachment('bountyMissionId')) + if mission is None: + owner.sendSystemMessage('@mission/mission_generic:bounty_no_mission', 0) + return + + if mission.getObjective().isSeekerActive(): + owner.sendSystemMessage('@mission/mission_generic:bounty_already_tracking', 0) + return + pos = SpawnPoint.getRandomPosition(owner.getPosition(), 1, 3, owner.getPlanetId()) + ori = owner.getOrientation() + seeker = core.staticService.spawnObject('object/creature/npc/droid/crafted/shared_probe_droid_advanced.iff', owner.getPlanet().getName(), 0, pos.x, pos.y, pos.z, ori.y, ori.w) + time.sleep(3) + owner.sendSystemMessage('@mission/mission_generic:seeker_droid_standby', 0) + seeker.setPosture(8) + time.sleep(7.5) + mission.getObjective().beginSeekerUpdates(core, owner) + return + + if option == 137 and target: + return + return \ No newline at end of file diff --git a/src/resources/objectives/BountyMissionObjective.java b/src/resources/objectives/BountyMissionObjective.java index caa8edbf..2e5619c0 100644 --- a/src/resources/objectives/BountyMissionObjective.java +++ b/src/resources/objectives/BountyMissionObjective.java @@ -25,15 +25,20 @@ import java.util.Random; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import protocol.swg.CommPlayerMessage; +import protocol.swg.PlayClientEffectLocMessage; +import engine.resources.common.CRC; import engine.resources.container.Traverser; import engine.resources.objects.SWGObject; import engine.resources.scene.Point3D; +import engine.resources.scene.Quaternion; import main.NGECore; import resources.common.BountyListItem; import resources.common.OutOfBand; import resources.common.ProsePackage; +import resources.common.SpawnPoint; import resources.datatables.DisplayType; import resources.objects.creature.CreatureObject; import resources.objects.mission.MissionObject; @@ -43,15 +48,27 @@ import services.mission.MissionObjective; public class BountyMissionObjective extends MissionObjective { + private static final long serialVersionUID = 1L; + private Point3D lastKnownLocation; private String lastKnownPlanet; - private ScheduledFuture locationUpdater; + private ScheduledFuture seekerUpdates; + private ScheduledFuture probeSummonTask; private long markObjId; private boolean seekerActive = false; private String seekerPlanet = ""; private boolean arakydActive = false; - + + private Point3D arakydLandingLocation; + + ////// Droids in the world - Set Posture to 8 to launch them. ////// + // Seeker Droids: object/creature/npc/droid/crafted/shared_probe_droid_advanced.iff + // Arakyd Droids: object/creature/npc/droid/shared_imperial_probot_bounty.iff + + ///// Inventory form ///// + // Seeker Droids: object/tangible/mission/shared_mission_bounty_droid_seeker.iff + // Arakyd Droids: object/tangible/mission/shared_mission_bounty_droid_probot.iff public BountyMissionObjective(MissionObject parent) { super(parent); } @@ -90,6 +107,9 @@ public class BountyMissionObjective extends MissionObjective { } //Could also be: object/mobile/shared_bounty_guild_bounty_check.iff || object/mobile/shared_bounty_check_fugitive_x.iff player.getClient().getSession().write(comm.serialize()); + + player.setAttachment("bountyMissionId", getMissionObject().getObjectId()); + } @Override @@ -111,16 +131,20 @@ public class BountyMissionObjective extends MissionObjective { clearActiveMissions(core, bounty); core.missionService.removeBounty(markObjId); + player.setAttachment("bountyMissionId", 0); } @Override public void abort(NGECore core, CreatureObject player) { + cancelLocationUpdates(); + BountyListItem bounty = core.missionService.getBountyListItem(markObjId); if (bounty == null) return; bounty.removeBountyHunter(player.getObjectId()); + player.setAttachment("bountyMissionId", 0); } @Override @@ -182,51 +206,147 @@ public class BountyMissionObjective extends MissionObjective { } } - public void beginSeekerUpdates(NGECore core) { + public void handleProbeDroidSummon(NGECore core, final CreatureObject actor) { + + actor.sendSystemMessage("@mission/mission_generic:probe_droid_launch_prep", DisplayType.Broadcast); + AtomicInteger countDown = new AtomicInteger(); + + ScheduledFuture summon = Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() { + + @Override + public void run() { + switch(countDown.incrementAndGet()) { + case 15: + actor.sendSystemMessage("@mission/mission_generic:probe_droid_arrival_5", DisplayType.Broadcast); + break; + case 16: + actor.sendSystemMessage("@mission/mission_generic:probe_droid_arrival_4", DisplayType.Broadcast); + break; + case 17: + actor.sendSystemMessage("@mission/mission_generic:probe_droid_arrival_3", DisplayType.Broadcast); + break; + case 18: + actor.sendSystemMessage("@mission/mission_generic:probe_droid_arrival_2", DisplayType.Broadcast); + break; + case 19: + actor.sendSystemMessage("@mission/mission_generic:probe_droid_arrival_1", DisplayType.Broadcast); + arakydLandingLocation = SpawnPoint.getRandomPosition(actor.getPosition(), 50, 120, actor.getPlanetId()); + PlayClientEffectLocMessage effectMsg = new PlayClientEffectLocMessage("clienteffect/probot_delivery.cef", actor.getPlanet().getName(), arakydLandingLocation); + actor.getClient().getSession().write(effectMsg.serialize()); + break; + case 20: + break; + case 23: + Quaternion ori = actor.getOrientation(); + + CreatureObject probe = (CreatureObject) NGECore.getInstance().staticService.spawnObject("object/creature/npc/droid/shared_imperial_probot_bounty.iff", + actor.getPlanet().getName(), 0, arakydLandingLocation.x, arakydLandingLocation.y, arakydLandingLocation.z, ori.y, ori.w); + + // TODO: Move probe to the player. + + probe.setAttachment("probotRequester", actor.getObjectID()); + probe.setAttachment("attachedMission", getMissionObject().getObjectID()); + actor.sendSystemMessage("@mission/mission_generic:probe_droid_arrival", DisplayType.Broadcast); + probeSummonTask.cancel(false); + break; + } + } + }, 1, 1, TimeUnit.SECONDS); + + probeSummonTask = summon; + + } + + public void beginSeekerUpdates(NGECore core, CreatureObject actor) { if (!seekerActive) { setSeekerActive(true); - setSeekerPlanet(getMissionObject().getGrandparent().getPlanet().getName()); + setSeekerPlanet(actor.getPlanet().getName()); - ScheduledFuture updates = Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() { + AtomicInteger updates = new AtomicInteger(); + ScheduledFuture updater = Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() { @Override public void run() { - + MissionObject mission = getMissionObject(); SWGObject target = core.objectService.getObject(markObjId); - CreatureObject player = (CreatureObject) mission.getGrandparent(); - + if (target == null) { - player.sendSystemMessage("@mission/mission_generic:player_target_inactive", DisplayType.Broadcast); + actor.sendSystemMessage("@mission/mission_generic:player_target_inactive", DisplayType.Broadcast); cancelLocationUpdates(); return; } else if (target.getPlanet().getName() != getSeekerPlanet()) { - player.sendSystemMessage("@mission/mission_generic:target_not_on_planet", DisplayType.Broadcast); + actor.sendSystemMessage("@mission/mission_generic:target_not_on_planet", DisplayType.Broadcast); cancelLocationUpdates(); return; } else { - WaypointObject missionWp = mission.getAttachedWaypoint(); - - if (missionWp == null) { - player.sendSystemMessage("@mission/mission_generic:target_not_found_1", DisplayType.Broadcast); + + if (updates.get() >= 5) { + actor.sendSystemMessage("@mission/mission_generic:target_track_lost", DisplayType.Broadcast); cancelLocationUpdates(); return; } - missionWp.setPosition(target.getWorldPosition()); + + WaypointObject missionWp = mission.getAttachedWaypoint(); - mission.setAttachedWaypoint(missionWp); - player.sendSystemMessage("@mission/mission_generic:target_location_updated_ground", DisplayType.Broadcast); + if (missionWp == null) { + WaypointObject waypoint = (WaypointObject) NGECore.getInstance().objectService.createObject("object/waypoint/shared_waypoint.iff", target.getPlanet(), + target.getWorldPosition().x, target.getWorldPosition().z, target.getWorldPosition().y); + waypoint.setActive(true); + waypoint.setColor(WaypointObject.ORANGE); + waypoint.setName(getMissionObject().getMissionTargetName()); + waypoint.setPlanetCRC(CRC.StringtoCRC(target.getPlanet().getName())); + waypoint.setStringAttribute("", ""); + getMissionObject().setAttachedWaypoint(waypoint); + actor.sendSystemMessage("@mission/mission_generic:target_location_updated_ground", DisplayType.Broadcast); + } else { + missionWp.setPosition(target.getWorldPosition()); + mission.setAttachedWaypoint(missionWp); + actor.sendSystemMessage("@mission/mission_generic:target_location_updated_ground", DisplayType.Broadcast); + } + updates.getAndIncrement(); } } }, 60, 15, TimeUnit.SECONDS); - setLocationUpdater(updates); + seekerUpdates = updater; } } + public void beginArakydUpdate(final CreatureObject actor) { + arakydActive = true; + Executors.newScheduledThreadPool(1).schedule(() -> { + + int number = new Random().nextInt(100); + + // Effectively a 16% chance of failing. The failure type is calculated by subtracting 10 from the number received. + if (number <= 16 && number != 0) { actor.sendSystemMessage("@mission/mission_generic:target_not_found_" + String.valueOf(10 - number), (byte) 0); return; } + + SWGObject target = NGECore.getInstance().objectService.getObject(markObjId); + if (target == null) { actor.sendSystemMessage("@mission/mission_generic:player_target_inactive", DisplayType.Broadcast); } + + WaypointObject waypoint = (WaypointObject) NGECore.getInstance().objectService.createObject("object/waypoint/shared_waypoint.iff", target.getPlanet(), + target.getWorldPosition().x, target.getWorldPosition().z, target.getWorldPosition().y); + waypoint.setActive(true); + waypoint.setColor(WaypointObject.ORANGE); + waypoint.setName(getMissionObject().getMissionTargetName()); + waypoint.setPlanetCRC(CRC.StringtoCRC(target.getPlanet().getName())); + waypoint.setStringAttribute("", ""); + getMissionObject().setAttachedWaypoint(waypoint); + + actor.sendSystemMessage("@mission/mission_generic:target_location_updated_ground", DisplayType.Broadcast); + actor.sendSystemMessage("@mission/mission_generic:target_located_" + target.getPlanet().getName(), DisplayType.Broadcast); + + }, 180, TimeUnit.SECONDS); + } + public void cancelLocationUpdates() { - getLocationUpdater().cancel(true); + if (seekerUpdates != null) + seekerUpdates.cancel(true); + if (probeSummonTask != null) + probeSummonTask.cancel(true); + setSeekerActive(false); setSeekerPlanet(""); setArakydActive(false); @@ -244,10 +364,6 @@ public class BountyMissionObjective extends MissionObjective { public void setLastKnownPlanet(String lastKnownPlanet) { this.lastKnownPlanet = lastKnownPlanet; } - public ScheduledFuture getLocationUpdater() { return locationUpdater; } - - public void setLocationUpdater(ScheduledFuture locationUpdater) { this.locationUpdater = locationUpdater; } - public boolean isSeekerActive() { return seekerActive; } public void setSeekerActive(boolean seekerActive) { this.seekerActive = seekerActive; } diff --git a/src/resources/objectives/DeliveryMissionObjective.java b/src/resources/objectives/DeliveryMissionObjective.java index 6a116a8e..7d5f029b 100644 --- a/src/resources/objectives/DeliveryMissionObjective.java +++ b/src/resources/objectives/DeliveryMissionObjective.java @@ -33,6 +33,8 @@ import services.mission.MissionObjective; public class DeliveryMissionObjective extends MissionObjective { + private static final long serialVersionUID = 1L; + private TangibleObject deliveryObject; private CreatureObject missionGiver; private CreatureObject dropOffNpc; diff --git a/src/resources/objectives/DestroyMissionObjective.java b/src/resources/objectives/DestroyMissionObjective.java index 7437d46f..442e4384 100644 --- a/src/resources/objectives/DestroyMissionObjective.java +++ b/src/resources/objectives/DestroyMissionObjective.java @@ -28,6 +28,8 @@ import services.mission.MissionObjective; public class DestroyMissionObjective extends MissionObjective { + private static final long serialVersionUID = 1L; + public DestroyMissionObjective(MissionObject parent) { super(parent); } diff --git a/src/services/DevService.java b/src/services/DevService.java index 270587d7..b226a6d4 100644 --- a/src/services/DevService.java +++ b/src/services/DevService.java @@ -92,6 +92,7 @@ public class DevService implements INetworkDispatch { if(creature.getClient().isGM()) suiOptions.put((long) 125, "Crafting Tools"); if(creature.getClient().isGM()) suiOptions.put((long) 130, "Vehicle Deeds"); if(creature.getClient().isGM()) suiOptions.put((long) 121, "Sandbox City"); + if(creature.getPlayerObject().getProfession().equals("bounty_hunter_1a")) suiOptions.put((long) 122, "Tracking Droids"); break; case 3: // [Items] Weapons @@ -1179,8 +1180,19 @@ public class DevService implements INetworkDispatch { return; case 121: - NGECore.getInstance().playerCityService.buildSandboxTestCity(player); - + core.playerCityService.buildSandboxTestCity(player); + return; + + case 122: + TangibleObject arakydDroids = (TangibleObject) core.objectService.createObject("object/tangible/mission/shared_mission_bounty_droid_probot.iff", planet); + //arakydDroids.setStackable(true); + //arakydDroids.setStackCount(10); + inventory.add(arakydDroids); + + TangibleObject seekerDroids = (TangibleObject) core.objectService.createObject("object/tangible/mission/shared_mission_bounty_droid_seeker.iff", planet); + inventory.add(seekerDroids); + return; + case 125: TangibleObject genericCraftingTool = (TangibleObject) core.objectService.createObject("object/tangible/crafting/station/shared_generic_tool.iff", planet); genericCraftingTool.setCustomName("Generic Crafting Tool"); diff --git a/src/services/mission/MissionService.java b/src/services/mission/MissionService.java index c2fc0371..bf1f2ea4 100644 --- a/src/services/mission/MissionService.java +++ b/src/services/mission/MissionService.java @@ -153,11 +153,8 @@ public class MissionService implements INetworkDispatch { if (terminalType == TerminalType.GENERIC) { handleMissionListRequest(core.objectService.getObject(request.getObjectId()), request.getTickCount(), TerminalType.GENERIC); } else if (terminalType == TerminalType.BOUNTY) { - if (!object.hasSkill("class_bountyhunter_phase1_novice")) { - object.sendSystemMessage("@mission/mission_generic:not_bounty_hunter_terminal", (byte) 0); - } /*else { - handleMissionListRequest(core.objectService.getObject(request.getObjectId()), request.getTickCount(), TerminalType.BOUNTY); - }*/ + if (!object.hasSkill("class_bountyhunter_phase1_novice")) { object.sendSystemMessage("@mission/mission_generic:not_bounty_hunter_terminal", (byte) 0); } + else { handleMissionListRequest(core.objectService.getObject(request.getObjectId()), request.getTickCount(), TerminalType.BOUNTY); } } else if (terminalType == TerminalType.ENTERTAINER) { } else if (terminalType == TerminalType.ARTISAN) { diff --git a/src/services/object/ObjectService.java b/src/services/object/ObjectService.java index a08928db..215bb04f 100644 --- a/src/services/object/ObjectService.java +++ b/src/services/object/ObjectService.java @@ -897,9 +897,8 @@ public class ObjectService implements INetworkDispatch { if(!core.getConfig().getString("MOTD").equals("")) creature.sendSystemMessage(core.getConfig().getString("MOTD"), (byte) 2); - BountyListItem bounty = (BountyListItem) core.getBountiesODB().get(creature.getObjectId()); - if (bounty != null) - core.missionService.getBountyList().add(bounty); + if (core.getBountiesODB().contains(creature.getObjectId())) + core.missionService.getBountyList().add((BountyListItem) core.getBountiesODB().get(creature.getObjectId())); if (creature.getSlottedObject("datapad") != null) { creature.getSlottedObject("datapad").viewChildren(creature, true, false, new Traverser() {