diff --git a/src/protocol/swg/CommPlayerMessage.java b/src/protocol/swg/CommPlayerMessage.java index 5d1274c8..9acde60c 100644 --- a/src/protocol/swg/CommPlayerMessage.java +++ b/src/protocol/swg/CommPlayerMessage.java @@ -25,15 +25,16 @@ import java.nio.ByteOrder; import org.apache.mina.core.buffer.IoBuffer; -import resources.common.Console; +import engine.resources.common.CRC; import resources.common.OutOfBand; -import resources.common.StringUtilities; public class CommPlayerMessage extends SWGMessage { private long objectId; + private String model; private OutOfBand outOfBand; - + private int time; + public CommPlayerMessage(long objectId, OutOfBand outOfBand) { this.objectId = objectId; this.outOfBand = outOfBand; @@ -46,28 +47,35 @@ public class CommPlayerMessage extends SWGMessage { @Override public IoBuffer serialize() { - IoBuffer buffer = IoBuffer.allocate(200).order(ByteOrder.LITTLE_ENDIAN); + IoBuffer buffer = IoBuffer.allocate(50).order(ByteOrder.LITTLE_ENDIAN); + buffer.setAutoExpand(true); + buffer.putShort((short) 2); buffer.putInt(0x594AD258); buffer.put((byte) 0); // aurebesh borders on comm, space version? Can cause crashes buffer.putLong(objectId); - /* Seen numbers: - * 52 (Starting Station Comms) - * 54 (Tansarii Comms) - * 57 (Supply Drops) - * 58 (Faction Covert->Overt) - * 68 (Imperial stop) - */ buffer.put(outOfBand.serialize().array()); // Officer Supply Drop: 0x3E894347 // Rebel Faction Dude: 0x528CB3D7 - buffer.putInt(0x528CB3D7); // model crc, can be anything w/o crashing + if (model == null) + buffer.putInt(0x3E894347); + else + buffer.putInt(CRC.StringtoCRC(model)); buffer.putInt(0); // sound buffer.putShort((short) 0); // unk - buffer.putShort((short) 16576); // comm display time, unsure on how it's calculated (probably milliseconds) + if (time == 0) + buffer.putShort((short) 16576); + else + buffer.putShort((short) time); buffer.flip(); - Console.println("CPM: " + StringUtilities.bytesToHex(buffer.array())); return buffer; } + + public void setModel(String model) { + this.model = model; + } + public void setTime(int time) { + this.time = time; + } } diff --git a/src/resources/common/BountyListItem.java b/src/resources/common/BountyListItem.java index f7662948..10033656 100644 --- a/src/resources/common/BountyListItem.java +++ b/src/resources/common/BountyListItem.java @@ -27,9 +27,9 @@ import java.util.List; import com.sleepycat.persist.model.Entity; import com.sleepycat.persist.model.PrimaryKey; - + @Entity(version=1) -public class BountyListItem implements Serializable { +public class BountyListItem implements Serializable { private static final long serialVersionUID = 1L; @PrimaryKey @@ -40,6 +40,7 @@ public class BountyListItem implements Serializable { private String name; private List assignedHunters; private List bountyPlacers; + private int failedAttempts; public BountyListItem() { } @@ -91,6 +92,10 @@ public class BountyListItem implements Serializable { public void addBounty(int amountToAdd) { this.creditReward += amountToAdd; } + + public void deductBounty(int amountToDeduct) { + this.creditReward = this.creditReward - amountToDeduct; + } public List getAssignedHunters() { return assignedHunters; @@ -115,4 +120,16 @@ public class BountyListItem implements Serializable { public void setBountyPlacers(List bountyPlacers) { this.bountyPlacers = bountyPlacers; } + + public int getFailedAttempts() { + return failedAttempts; + } + + public void setFailedAttempts(int failedAttempts) { + this.failedAttempts = failedAttempts; + } + + public void incrementFailedAttempts() { + this.failedAttempts += 1; + } } \ No newline at end of file diff --git a/src/resources/objectives/BountyMissionObjective.java b/src/resources/objectives/BountyMissionObjective.java index e17de5cd..caa8edbf 100644 --- a/src/resources/objectives/BountyMissionObjective.java +++ b/src/resources/objectives/BountyMissionObjective.java @@ -22,18 +22,35 @@ package resources.objectives; import java.util.Random; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import protocol.swg.CommPlayerMessage; +import engine.resources.container.Traverser; +import engine.resources.objects.SWGObject; import engine.resources.scene.Point3D; import main.NGECore; import resources.common.BountyListItem; +import resources.common.OutOfBand; +import resources.common.ProsePackage; +import resources.datatables.DisplayType; import resources.objects.creature.CreatureObject; import resources.objects.mission.MissionObject; +import resources.objects.waypoint.WaypointObject; +import services.chat.Mail; import services.mission.MissionObjective; public class BountyMissionObjective extends MissionObjective { private Point3D lastKnownLocation; + private String lastKnownPlanet; + private ScheduledFuture locationUpdater; private long markObjId; + + private boolean seekerActive = false; + private String seekerPlanet = ""; + private boolean arakydActive = false; public BountyMissionObjective(MissionObject parent) { super(parent); @@ -46,29 +63,175 @@ public class BountyMissionObjective extends MissionObjective { BountyListItem bountyTarget = core.missionService.getBountyListItem(getMissionObject().getBountyObjId()); + if (bountyTarget == null) { + core.missionService.handleMissionAbort(player, getMissionObject()); + player.sendSystemMessage("@bountyhunter:null_mission", DisplayType.Broadcast); + return; + } + + bountyTarget.addBountyHunter(player.getObjectId()); + setMarkObjId(bountyTarget.getObjectId()); - // TODO: Change this to a comm message. - player.sendSystemMessage("@mission/mission_bounty_informant:target_hard_" + Integer.toString(new Random().nextInt(5)), (byte) 0); + String message = "@mission/mission_bounty_informant:target_hard_" + Integer.toString(new Random().nextInt(4) + 1); + CommPlayerMessage comm = new CommPlayerMessage(player.getObjectId(), new OutOfBand(new ProsePackage(message))); + comm.setTime(5000); + switch (bountyTarget.getFaction()) { + case "neutral": + comm.setModel("object/mobile/shared_dressed_assassin_mission_giver_0" + Integer.toString(new Random().nextInt(2) + 1) + ".iff"); + break; + case "rebel": + comm.setModel("object/mobile/shared_dressed_assassin_mission_giver_imp_hum_m_0" + Integer.toString(new Random().nextInt(2) + 1) + ".iff"); + break; + case "imperial": + comm.setModel("object/mobile/shared_dressed_assassin_mission_giver_reb_0" + Integer.toString(new Random().nextInt(2) + 1) + ".iff"); + break; + } //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()); } @Override public void complete(NGECore core, CreatureObject player) { + BountyListItem bounty = core.missionService.getBountyListItem(markObjId); + + if (bounty == null) + return; + + int reward = getMissionObject().getCreditReward(); + if (bounty.getCreditReward() - reward == 0) + player.addBankCredits(reward); + else + player.addBankCredits(bounty.getCreditReward()); + + notifyBountyPlacers(core, player, bounty); + + clearActiveMissions(core, bounty); + + core.missionService.removeBounty(markObjId); } @Override public void abort(NGECore core, CreatureObject player) { - + BountyListItem bounty = core.missionService.getBountyListItem(markObjId); + + if (bounty == null) + return; + + bounty.removeBountyHunter(player.getObjectId()); } @Override public void update(NGECore core, CreatureObject player) { - + } + + public void notifyBountyPlacers(NGECore core, CreatureObject player, BountyListItem bounty) { + String message = "Dear Sir or Madam,\nThe Galactic Bounty Network wishes to inform you that the bounty that you placed on the head of "+ bounty.getName() + + " has been claimed by the bounty hunter, " + player.getCustomName() + ".\nThank you for your patronage, and have a nice day."; + + bounty.getBountyPlacers().forEach(id -> { + Mail bountyMail = new Mail(); + bountyMail.setMailId(core.chatService.generateMailId()); + bountyMail.setTimeStamp((int) (new java.util.Date().getTime() / 1000)); + bountyMail.setRecieverId(id); + bountyMail.setSubject("Bounty Claim Notification"); + bountyMail.setSenderName("Galactic Bounty Network"); + bountyMail.setMessage(message); + bountyMail.setStatus(Mail.NEW); + + core.chatService.storePersistentMessage(bountyMail); + + SWGObject obj = core.objectService.getObject(id); + if (obj != null) + core.chatService.sendPersistentMessageHeader(obj.getClient(), bountyMail); + }); + } + + public void clearActiveMissions(NGECore core, BountyListItem bounty) { + bounty.getAssignedHunters().forEach(id -> { + CreatureObject hunter = (CreatureObject) core.objectService.getObject(id); + + if (hunter != null) { + hunter.sendSystemMessage(new OutOfBand(new ProsePackage("@bounty_hunter:bounty_failed_hunter", "TT", getMissionObject().getGrandparent().getObjectId())), DisplayType.Broadcast); + hunter.getSlottedObject("datapad").viewChildren(hunter, true, false, new Traverser() { + @Override + public void process(SWGObject item) { + if (item instanceof MissionObject) { + MissionObject mission = (MissionObject) item; + if (mission.getMissionType().equals("bounty")) { + core.missionService.handleMissionAbort(hunter, mission); + } + } + } + }); + } + }); + } + + public void checkBountyActiveStatus(NGECore core) { + BountyListItem bountyTarget = core.missionService.getBountyListItem(getMissionObject().getBountyObjId()); + CreatureObject player = (CreatureObject) getMissionObject().getGrandparent(); + + if (bountyTarget == null || !bountyTarget.getAssignedHunters().contains(player.getObjectId())) { + player.sendSystemMessage("@bounty_hunter:bounty_incomplete", DisplayType.Broadcast); + core.missionService.handleMissionAbort(player, getMissionObject(), true); + } + } + + public void beginSeekerUpdates(NGECore core) { + + if (!seekerActive) { + setSeekerActive(true); + setSeekerPlanet(getMissionObject().getGrandparent().getPlanet().getName()); + + ScheduledFuture updates = 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); + cancelLocationUpdates(); + return; + } else if (target.getPlanet().getName() != getSeekerPlanet()) { + player.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); + cancelLocationUpdates(); + return; + } + missionWp.setPosition(target.getWorldPosition()); + + mission.setAttachedWaypoint(missionWp); + player.sendSystemMessage("@mission/mission_generic:target_location_updated_ground", DisplayType.Broadcast); + } + } + }, 60, 15, TimeUnit.SECONDS); + + setLocationUpdater(updates); + } + } + + public void cancelLocationUpdates() { + getLocationUpdater().cancel(true); + setSeekerActive(false); + setSeekerPlanet(""); + setArakydActive(false); + } + public Point3D getLastKnownLocation() { return lastKnownLocation; } public void setLastKnownLocation(Point3D lastKnownLocation) { this.lastKnownLocation = lastKnownLocation; } @@ -77,4 +240,24 @@ public class BountyMissionObjective extends MissionObjective { public void setMarkObjId(long markObjId) { this.markObjId = markObjId; } + public String getLastKnownPlanet() { return lastKnownPlanet; } + + 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; } + + public boolean isArakydActive() { return arakydActive; } + + public void setArakydActive(boolean araykdActive) { this.arakydActive = araykdActive; } + + public String getSeekerPlanet() { return seekerPlanet; } + + public void setSeekerPlanet(String seekerPlanet) { this.seekerPlanet = seekerPlanet; } + } diff --git a/src/services/PlayerService.java b/src/services/PlayerService.java index b37c49a0..4a50e774 100644 --- a/src/services/PlayerService.java +++ b/src/services/PlayerService.java @@ -1292,7 +1292,7 @@ public class PlayerService implements INetworkDispatch { victim.setCashCredits(0); } else { victim.setBankCredits(victim.getBankCredits() - bounty); } - if (!core.missionService.addToExistingBounty(attacker, victim.getObjectId(), bounty)) + if (!core.missionService.addToExistingBounty(attacker.getObjectId(), victim.getObjectId(), bounty)) core.missionService.createNewBounty(attacker, victim.getObjectId(), bounty); } } diff --git a/src/services/mission/MissionService.java b/src/services/mission/MissionService.java index d5f0e2bf..1d02b267 100644 --- a/src/services/mission/MissionService.java +++ b/src/services/mission/MissionService.java @@ -39,7 +39,6 @@ import org.apache.mina.core.buffer.IoBuffer; import org.apache.mina.core.session.IoSession; import com.sleepycat.je.Transaction; -import com.sleepycat.persist.EntityCursor; import protocol.swg.ObjControllerMessage; import protocol.swg.objectControllerObjects.MissionAbort; @@ -208,9 +207,13 @@ public class MissionService implements INetworkDispatch { } public void handleMissionAbort(CreatureObject creature, MissionObject mission) { + handleMissionAbort(creature, mission, false); + } + + public void handleMissionAbort(CreatureObject creature, MissionObject mission, boolean silent) { MissionObjective objective = mission.getObjective(); - if (objective != null) + if (objective != null && !silent) objective.abort(core, creature); core.objectService.destroyObject(mission.getObjectId()); @@ -220,7 +223,7 @@ public class MissionService implements INetworkDispatch { File missionFolder = new File("./clientdata/string/en/mission"); File[] missionFiles = missionFolder.listFiles(); - int missionStrings = 0; + //int missionStrings = 0; for (int i = 0; i < missionFiles.length; i++) { if (missionFiles[i].getName().contains("_easy") || missionFiles[i].getName().contains("_medium") || missionFiles[i].getName().contains("_hard")) { @@ -242,7 +245,7 @@ public class MissionService implements INetworkDispatch { continue; } } catch (Exception e) { e.printStackTrace(); } - missionStrings++; + //missionStrings++; } } //System.out.println("Loaded " + missionStrings + " mission entry counts."); @@ -282,14 +285,13 @@ public class MissionService implements INetworkDispatch { MissionObject mission = (MissionObject) obj; - if (typeOneCount.get() < 4) { + if (typeOneCount.get() < 5) { if (type == TerminalType.GENERIC) randomDeliveryMission(player, mission); else if (type == TerminalType.BOUNTY) - //randomBountyMission(player, mission); - return; + randomBountyMission(player, mission); else if (type == TerminalType.ARTISAN) return; @@ -300,7 +302,7 @@ public class MissionService implements INetworkDispatch { mission.setRepeatCount(requestCounter); typeOneCount.incrementAndGet(); - } else if (typeTwoCount.get() < 4) { + } else if (typeTwoCount.get() < 5) { if (type == TerminalType.GENERIC) return; @@ -443,7 +445,7 @@ public class MissionService implements INetworkDispatch { } } - if (bountyTarget == null) + if (bountyTarget == null || bountyTarget.getAssignedHunters().contains(player.getObjectId())) return; mission.setMissionType("bounty"); @@ -451,17 +453,20 @@ public class MissionService implements INetworkDispatch { String missionStf = "mission/mission_bounty_jedi"; if (!bountyTarget.getProfession().equals("")) { // TODO: Smuggler mission checks. - if (bountyTarget.getFaction().equals("neutral")) { + if (bountyTarget.getFaction().equals("neutral")) { // There were no neutral bounty missions. Remove this when done testing. mission.setMissionTargetName("@mission/mission_bounty_jedi:neutral_jedi"); mission.setMissionId(3); + mission.setCreator("Corporate Sector Authority"); } else if (bountyTarget.getFaction().equals("rebel")) { - mission.setMissionTargetName("@mission/mission_bounty_jedi:rebel_jedi"); + mission.setMissionTargetName("Rebel Bounty"); mission.setMissionId(2); + mission.setCreator("The Galactic Empire"); } else if (bountyTarget.getFaction().equals("imperial")) { - mission.setMissionTargetName("@mission/mission_bounty_jedi:imperial_jedi"); + mission.setMissionTargetName("Imperial Bounty"); mission.setMissionId(1); + mission.setCreator("The Alliance"); } mission.setMissionTitle(missionStf); mission.setMissionDescription(missionStf); @@ -480,7 +485,7 @@ public class MissionService implements INetworkDispatch { mission.setMissionDescription(missionStf, "s"); } - mission.setMissionLevel(90); + mission.setMissionLevel(new Random().nextInt(100 - 95) + 95); mission.setCreditReward(bountyTarget.getCreditReward()); @@ -537,16 +542,16 @@ public class MissionService implements INetworkDispatch { return bounty; } - public boolean addToExistingBounty(CreatureObject bountyTarget, long placer, int amountToAdd) { + public boolean addToExistingBounty(long bountyTarget, long placer, int amountToAdd) { - BountyListItem bounty = getBountyListItem(bountyTarget.getObjectId()); + BountyListItem bounty = getBountyListItem(bountyTarget); if (bounty == null) return false; bounty.addBounty(amountToAdd); - if (placer != 0) + if (placer != 0 && !bounty.getBountyPlacers().contains(placer)) bounty.getBountyPlacers().add(placer); bountiesODB.put(bounty.getObjectId(), bounty); @@ -555,18 +560,18 @@ public class MissionService implements INetworkDispatch { return true; } - public boolean removeBounty(CreatureObject bountyTarget, boolean listRemove) { + public boolean removeBounty(long bountyTarget, boolean listRemove) { Transaction txn = bountiesODB.getEnvironment().beginTransaction(null, null); if (listRemove) - bountyList.remove(bountiesODB.get(bountyTarget.getObjectId())); + bountyList.remove(bountiesODB.get(bountyTarget)); - bountiesODB.remove(bountyTarget.getObjectId()); + bountiesODB.remove(bountyTarget); txn.commitSync(); return true; } - public boolean removeBounty(CreatureObject bountyTarget) { + public boolean removeBounty(long bountyTarget) { return removeBounty(bountyTarget, true); } @@ -576,7 +581,7 @@ public class MissionService implements INetworkDispatch { while(cursor.hasNext()) { BountyListItem bounty = (BountyListItem) cursor.next(); if (!core.characterService.playerExists(bounty.getObjectId())) { - bounties.add(bounty); + bounties.add(bounty); } } bounties.stream().mapToLong(b -> b.getObjectId()).forEach(bountiesODB::remove); diff --git a/src/services/object/ObjectService.java b/src/services/object/ObjectService.java index 12b3bac4..a08928db 100644 --- a/src/services/object/ObjectService.java +++ b/src/services/object/ObjectService.java @@ -98,6 +98,7 @@ import engine.resources.scene.Quaternion; import engine.resources.service.INetworkDispatch; import engine.resources.service.INetworkRemoteEvent; import main.NGECore; +import resources.objectives.BountyMissionObjective; import resources.objects.Delta; import resources.objects.building.BuildingObject; import resources.objects.cell.CellObject; @@ -900,6 +901,21 @@ public class ObjectService implements INetworkDispatch { if (bounty != null) core.missionService.getBountyList().add(bounty); + if (creature.getSlottedObject("datapad") != null) { + creature.getSlottedObject("datapad").viewChildren(creature, true, false, new Traverser() { + + @Override + public void process(SWGObject obj) { + if (obj instanceof MissionObject) { + MissionObject mission = (MissionObject) obj; + if (mission.getMissionType().equals("bounty")) { + ((BountyMissionObjective) mission.getObjective()).checkBountyActiveStatus(core); + } + } + } + }); + } + core.playerService.postZoneIn(creature); }