diff --git a/scripts/quests/events/find_majolnir.py b/scripts/quests/events/find_majolnir.py new file mode 100644 index 00000000..50909c6b --- /dev/null +++ b/scripts/quests/events/find_majolnir.py @@ -0,0 +1,22 @@ +def setup(core): + core.questService.addCollisionEvent('find_majolnir', 'task2', 3526, 5, -4619, 10, 'tatooine') + return + +def task2(core, activator, collidable): + print ('called.') + if activator.getPlayerObject() is None: + return + + player = activator.getPlayerObject() + + if player.getQuest('find_majolnir') is None: + print ('not in quest journal') + return + + quest = player.getQuest('find_majolnir') + + if quest.isCompleted() or quest.getActiveStep() != 2: + return + + core.questService.completeActiveTask(activator, quest) + return \ No newline at end of file diff --git a/scripts/quest_rewards/item_deed_landspeeder_x38_02_01.py b/scripts/quests/rewards/item_deed_landspeeder_x38_02_01.py similarity index 97% rename from scripts/quest_rewards/item_deed_landspeeder_x38_02_01.py rename to scripts/quests/rewards/item_deed_landspeeder_x38_02_01.py index 67c43fef..38f51817 100644 --- a/scripts/quest_rewards/item_deed_landspeeder_x38_02_01.py +++ b/scripts/quests/rewards/item_deed_landspeeder_x38_02_01.py @@ -1,4 +1,4 @@ -import sys - -def run(): +import sys + +def run(): return "object/tangible/deed/vehicle_deed/shared_landspeeder_xp38_deed.iff" \ No newline at end of file diff --git a/src/main/NGECore.java b/src/main/NGECore.java index cd3208c1..06c23fc5 100644 --- a/src/main/NGECore.java +++ b/src/main/NGECore.java @@ -571,8 +571,12 @@ public class NGECore { staticService.spawnStatics(); equipmentService.loadBonusSets(); + playerCityService.loadCityRankCaps(); playerCityService.loadCities(); + + questService.loadEvents(); + retroService.run(); browserService = new BrowserService(this); diff --git a/src/resources/objects/player/PlayerObject.java b/src/resources/objects/player/PlayerObject.java index f8d49268..98bb8b9c 100644 --- a/src/resources/objects/player/PlayerObject.java +++ b/src/resources/objects/player/PlayerObject.java @@ -47,6 +47,7 @@ import resources.objects.waypoint.WaypointObject; import resources.quest.Quest; import services.quest.QuestItem; import engine.clients.Client; +import engine.resources.common.CRC; import engine.resources.common.StringUtilities; import engine.resources.objects.Baseline; import engine.resources.scene.Planet; @@ -609,6 +610,10 @@ public class PlayerObject extends IntangibleObject implements Serializable { getBaseline(8).set("activeQuest", questCRC); } + public Quest getQuest(String questName) { + return getQuestJournal().get(CRC.StringtoCRC("quest/" + questName)); + } + public String getProfessionWheelPosition() { return (String) getBaseline(8).get("professionWheelPosition"); } diff --git a/src/resources/quest/Quest.java b/src/resources/quest/Quest.java index 0e49750b..58efc4c9 100644 --- a/src/resources/quest/Quest.java +++ b/src/resources/quest/Quest.java @@ -23,10 +23,12 @@ package resources.quest; import java.io.Serializable; import java.util.BitSet; +import java.util.concurrent.ScheduledFuture; import org.apache.mina.core.buffer.IoBuffer; import engine.resources.common.CRC; +import engine.resources.common.StringUtilities; import engine.resources.objects.Delta; public class Quest extends Delta implements Serializable { @@ -35,6 +37,7 @@ public class Quest extends Delta implements Serializable { private long ownerId; private int activeStep; // Quest starts at step 0 + private BitSet activeStepBitmask = new BitSet(16); private BitSet completedStepBitmask = new BitSet(16); @@ -42,6 +45,7 @@ public class Quest extends Delta implements Serializable { private boolean recievedAward = false; private String name; + private ScheduledFuture timer; private long waypointId; public Quest() {} @@ -120,12 +124,23 @@ public class Quest extends Delta implements Serializable { } public void incrementQuestStep() { - activeStepBitmask.set(activeStep, false); completedStepBitmask.set(activeStep); - this.activeStep++; - activeStepBitmask.set(activeStep); + activeStepBitmask.set(activeStep, false); + + activeStep++; + activeStepBitmask.set(activeStep, true); + + //System.out.println("Active step was "+ (activeStep - 1) + " and is now " + activeStep ); } + public ScheduledFuture getTimer() { + return timer; + } + + public void setTimer(ScheduledFuture timer) { + this.timer = timer; + } + public byte[] getBytes() { byte[] activeStepBytes = activeStepBitmask.toByteArray(); byte[] completedStepBytes = completedStepBitmask.toByteArray(); @@ -175,6 +190,7 @@ public class Quest extends Delta implements Serializable { buffer.flip(); + StringUtilities.printBytes(buffer.array()); return buffer.array(); } diff --git a/src/services/quest/QuestService.java b/src/services/quest/QuestService.java index 8c7be458..0be11907 100644 --- a/src/services/quest/QuestService.java +++ b/src/services/quest/QuestService.java @@ -22,9 +22,22 @@ package services.quest; import java.io.File; +import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.FileVisitor; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.Map; +import java.util.Random; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import org.apache.mina.core.buffer.IoBuffer; import org.apache.mina.core.session.IoSession; @@ -36,9 +49,11 @@ import protocol.swg.objectControllerObjects.ForceActivateQuest; import protocol.swg.objectControllerObjects.QuestTaskCounterMessage; import protocol.swg.objectControllerObjects.QuestTaskTimerMessage; import protocol.swg.objectControllerObjects.ShowLootBox; +import resources.common.Console; import resources.common.ObjControllerOpcodes; import resources.common.OutOfBand; import resources.common.ProsePackage; +import resources.common.collidables.CollidableCircle; import resources.datatables.DisplayType; import resources.objects.SWGMap; import resources.objects.creature.CreatureObject; @@ -60,7 +75,7 @@ public class QuestService implements INetworkDispatch { private NGECore core; private Map questMap = new ConcurrentHashMap(); private Map questRewardMap = new ConcurrentHashMap(); - + public QuestService(NGECore core) { this.core = core; } @@ -77,9 +92,6 @@ public class QuestService implements INetworkDispatch { }); } - @Override - public void shutdown() { } - public boolean doesPlayerHaveQuest(CreatureObject creo, String questName) { PlayerObject ghost = creo.getPlayerObject(); @@ -144,9 +156,6 @@ public class QuestService implements INetworkDispatch { if (player == null) return; - //if (quest.getWaypointId() != 0) - //core.objectService.destroyObject(quest.getWaypointId()); - int activeStep = quest.getActiveStep(); QuestTask task = qData.getTasks().get(activeStep); @@ -156,6 +165,16 @@ public class QuestService implements INetworkDispatch { switch (type) { + // quest.task.ground.go_to_location + case "go_to_location": + Point3D location = new Point3D(task.getLocationX(), task.getLocationY(), task.getLocationZ()); + WaypointObject wpGoTo = createWaypoint(task.getWaypointName(), location, task.getPlanet()); + player.getWaypoints().put(wpGoTo.getObjectID(), wpGoTo); + + quest.setWaypointId(wpGoTo.getObjectID()); + + break; + // quest.task.ground.comm_player case "comm_player": @@ -172,18 +191,31 @@ public class QuestService implements INetworkDispatch { ObjControllerMessage itemCount = new ObjControllerMessage(11, new QuestTaskCounterMessage(quester.getObjectID(), quest.getCrcName(), "@quest/groundquests:retrieve_item_counter")); quester.getClient().getSession().write(itemCount.serialize()); - WaypointObject waypoint = createWaypoint(task.getWaypointName(), new Point3D(task.getLocationX(), task.getLocationY(), task.getLocationZ()), task.getPlanet()); - player.getWaypoints().put(waypoint.getObjectID(), waypoint); + WaypointObject wpRetrieve = createWaypoint(task.getWaypointName(), new Point3D(task.getLocationX(), task.getLocationY(), task.getLocationZ()), task.getPlanet()); + player.getWaypoints().put(wpRetrieve.getObjectID(), wpRetrieve); - quest.setWaypointId(waypoint.getObjectID()); + quest.setWaypointId(wpRetrieve.getObjectID()); break; + // quest.task.ground.timer case "timer": - ObjControllerMessage timer = new ObjControllerMessage(11, new QuestTaskTimerMessage(quester.getObjectID(), quest.getCrcName(), "@quest/groundquests:timer_timertext", 20)); - quester.getClient().getSession().write(timer.serialize()); - System.out.println("Sent timer."); + AtomicInteger time = new AtomicInteger(new Random(task.getMaxTime()).nextInt((task.getMaxTime() - task.getMinTime()) + task.getMinTime())); + + ScheduledFuture taskTimer = Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> { + Console.println("Timer is: " + time.get()); + if (time.get() == 0) { + //quest.incrementQuestStep(); + completeActiveTask((CreatureObject) core.objectService.getObject(quester.getObjectID()), quest.getName()); + } else { + ObjControllerMessage timer = new ObjControllerMessage(11, new QuestTaskTimerMessage(quester.getObjectID(), quest.getCrcName(), "@quest/groundquests:timer_timertext", time.getAndDecrement())); + quester.getClient().getSession().write(timer.serialize()); + } + }, 1, 1, TimeUnit.SECONDS); + + quest.setTimer(taskTimer); break; + // quest.task.ground.show_message_box case "show_message_box": break; @@ -192,6 +224,36 @@ public class QuestService implements INetworkDispatch { break; } + + if (!task.isVisible()) { + completeActiveTask(quester, quest); + return; + } + + } + + public void completeActiveTask(CreatureObject quester, String questName) { + if (quester.getPlayerObject().getQuestJournal().containsKey(CRC.StringtoCRC("quest/" + questName)) && !quester.getPlayerObject().getQuestJournal().get(CRC.StringtoCRC("quest/" + questName)).isCompleted()) { + completeActiveTask(quester, quester.getPlayerObject().getQuestJournal().get(CRC.StringtoCRC("quest/" + questName))); + } + } + + public void completeActiveTask(CreatureObject quester, Quest quest) { + PlayerObject player = quester.getPlayerObject(); + if (player == null) + return; + + QuestData qData = getQuestData(quest.getName()); + + int activeStep = quest.getActiveStep(); + + QuestTask task = qData.getTasks().get(activeStep); + + //if (quest.getWaypointId() != 0) + //core.objectService.destroyObject(quest.getWaypointId()); + + if (quest.getTimer() != null) + quest.getTimer().cancel(true); if (activeStep + 1 >= qData.getTasks().size()) { QuestList listItem = questRewardMap.get(quest.getName()); @@ -209,14 +271,12 @@ public class QuestService implements INetworkDispatch { if (task.getGrantQuestOnComplete() != null && !task.getGrantQuestOnComplete().equals("")) activateQuest(quester, task.getGrantQuestOnComplete()); - - quest.incrementQuestStep(); // After handling the quest type, we can now increment the step - if (!task.isVisible()) { - player.getContainer().getClient().getSession().write(player.getBaseline(8).createDelta(7)); - activateNextTask(quester, quest); - return; - } + quest.incrementQuestStep(); + + player.getContainer().getClient().getSession().write(player.getBaseline(8).createDelta(7)); + + activateNextTask(quester, quest); } @@ -275,7 +335,7 @@ public class QuestService implements INetworkDispatch { // Give quest items if (info.getRewardLootName() != null && !info.getRewardLootName().equals("")) { - SWGObject rewardLoot = core.objectService.createObject(core.scriptService.getMethod("scripts/quest_rewards/", info.getRewardLootName(), "run").__call__().asString(), creo.getPlanet()); + SWGObject rewardLoot = core.objectService.createObject(core.scriptService.getMethod("scripts/quests/rewards/", info.getRewardLootName(), "run").__call__().asString(), creo.getPlanet()); inventory.add(rewardLoot); recievedItems.add(rewardLoot.getObjectID()); } @@ -371,7 +431,7 @@ public class QuestService implements INetworkDispatch { return; QuestItem item = player.getQuestRetrieveItemTemplates().get(target.getTemplate()); - activateNextTask(quester, item.getQuestName()); + completeActiveTask(quester, item.getQuestName()); player.getQuestRetrieveItemTemplates().remove(target.getTemplate(), item); @@ -416,6 +476,12 @@ public class QuestService implements INetworkDispatch { } } + public void addCollisionEvent(String questName, String method, float x, float y, float z, float radius, String planet) { + CollidableCircle collision = new CollidableCircle(new Point3D(x,y,z), radius, core.terrainService.getPlanetByName(planet)); + collision.setCallback(core.scriptService.getMethod("scripts/quests/events/", questName, method)); + core.simulationService.addCollidable(collision, x, z); + } + public boolean adminActivateQuest(CreatureObject creo, String questName) { PlayerObject ghost = creo.getPlayerObject(); @@ -497,6 +563,8 @@ public class QuestService implements INetworkDispatch { if (visitor.getObjectByColumnNameAndIndex("ITEM_NAME", r) != null) task.setItemName((String) visitor.getObjectByColumnNameAndIndex("ITEM_NAME", r)); if (visitor.getObjectByColumnNameAndIndex("DROP_PERCENT", r) != null) task.setDropPercent((int) visitor.getObjectByColumnNameAndIndex("DROP_PERCENT", r)); if (visitor.getObjectByColumnNameAndIndex("RETRIEVE_MENU_TEXT", r) != null) task.setRetrieveMenuText((String) visitor.getObjectByColumnNameAndIndex("RETRIEVE_MENU_TEXT", r)); + if (visitor.getObjectByColumnNameAndIndex("MIN_TIME", r) != null) task.setMinTime((int) visitor.getObjectByColumnNameAndIndex("MIN_TIME", r)); + if (visitor.getObjectByColumnNameAndIndex("MAX_TIME", r) != null) task.setMaxTime((int) visitor.getObjectByColumnNameAndIndex("MAX_TIME", r)); //if (visitor.getObjectByColumnNameAndIndex("", r)) data.getTasks().add(task); @@ -544,6 +612,19 @@ public class QuestService implements INetworkDispatch { return qList; } + public void loadEvents() { + FileVisitor fv = new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException + { + core.scriptService.callScript("scripts/quests/events/", file.getFileName().toString().replace(".py", ""), "setup", core); + return FileVisitResult.CONTINUE; + } + }; + try { Files.walkFileTree(Paths.get("scripts/quests/events/"), fv); } + catch (IOException e) { e.printStackTrace(); } + } + // useful for custom quest scripts, and for the some of the datatables that are all screwed up /*private QuestData parseXmlQuestData(Quest quest) { try { @@ -579,4 +660,8 @@ public class QuestService implements INetworkDispatch { return template.replace(file, "shared_" + file); } + + @Override + public void shutdown() { } + } diff --git a/src/services/quest/QuestTask.java b/src/services/quest/QuestTask.java index 1934259c..3e53aefe 100644 --- a/src/services/quest/QuestTask.java +++ b/src/services/quest/QuestTask.java @@ -65,6 +65,8 @@ public class QuestTask { private int timeToComplete; private int numRequired; private int dropPercent; + private int minTime; + private int maxTime; private long waitMarkerBuilding; @@ -74,6 +76,7 @@ public class QuestTask { private float waitMarkerX; private float waitMarkerY; private float waitMarkerZ; + private float radius; public QuestTask() {} @@ -451,4 +454,28 @@ public class QuestTask { public void setRetrieveMenuText(String retrieveMenuText) { this.retrieveMenuText = retrieveMenuText; } + + public int getMinTime() { + return minTime; + } + + public void setMinTime(int minTime) { + this.minTime = minTime; + } + + public int getMaxTime() { + return maxTime; + } + + public void setMaxTime(int maxTime) { + this.maxTime = maxTime; + } + + public float getRadius() { + return radius; + } + + public void setRadius(float radius) { + this.radius = radius; + } }