From 1190e7b1fe8925b5d3bbf21199604e7edf50d84d Mon Sep 17 00:00:00 2001 From: Waverunner Date: Sun, 24 Aug 2014 15:07:37 -0400 Subject: [PATCH 1/2] Added quest signal scrpts, Added Pall conversation --- scripts/conversation/majolnir.py | 35 +++++++++++++++++ .../generic/static/tatooine/majolnir.py | 33 ++++++++++++++++ scripts/object/mobile/majolnir.py | 1 + .../events/signals/giveCustomizationKit.py | 11 ++++++ .../tatooine/mos_eisley_quest.py | 18 +++++++++ src/main/NGECore.java | 2 + src/services/quest/QuestService.java | 39 +++++++++++++++++++ 7 files changed, 139 insertions(+) create mode 100644 scripts/conversation/majolnir.py create mode 100644 scripts/mobiles/generic/static/tatooine/majolnir.py create mode 100644 scripts/quests/events/signals/giveCustomizationKit.py create mode 100644 scripts/static_spawns/tatooine/mos_eisley_quest.py diff --git a/scripts/conversation/majolnir.py b/scripts/conversation/majolnir.py new file mode 100644 index 00000000..91431d53 --- /dev/null +++ b/scripts/conversation/majolnir.py @@ -0,0 +1,35 @@ +from resources.common import ConversationOption +from resources.common import OutOfBand +from resources.common import ProsePackage +from java.util import Vector +import sys + +def startConversation(core, actor, npc): + convSvc = core.conversationService + player = actor.getPlayerObject() + + print ('conversation') + if player is None: + return + + quest = player.getQuest('find_majolnir') + + if quest is None: + endConversation(core, actor, npc) + return + + if quest.isComplete(): + core.conversationService.sendStopConversation(actor, npc, 'conversation/tatooine_eisley_majolnir', 's_24') + return + + if quest.getActiveTask() == 3: + core.conversationService.sendStopConversation(actor, npc, 'conversation/tatooine_eisley_majolnir', 's_18') + core.questService.handleActivateSignal(actor, npc, quest, 3) + return + + endConversation(core, actor, npc) + return + +def endConversation(core, actor, npc): + core.conversationService.sendStopConversation(actor, npc, 'conversation/tatooine_eisley_majolnir', 's_65') + return \ No newline at end of file diff --git a/scripts/mobiles/generic/static/tatooine/majolnir.py b/scripts/mobiles/generic/static/tatooine/majolnir.py new file mode 100644 index 00000000..0bcf02d5 --- /dev/null +++ b/scripts/mobiles/generic/static/tatooine/majolnir.py @@ -0,0 +1,33 @@ +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('majolnir') + mobileTemplate.setLevel(1) + mobileTemplate.setDifficulty(Difficulty.NORMAL) + mobileTemplate.setSocialGroup("township") + mobileTemplate.setOptionsBitmask(Options.INVULNERABLE | Options.CONVERSABLE) + + templates = Vector() + templates.add('object/mobile/shared_majolnir.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('majolnir', mobileTemplate) + return \ No newline at end of file diff --git a/scripts/object/mobile/majolnir.py b/scripts/object/mobile/majolnir.py index ccad8904..d80c08c5 100644 --- a/scripts/object/mobile/majolnir.py +++ b/scripts/object/mobile/majolnir.py @@ -1,4 +1,5 @@ import sys def setup(core, object): + object.setAttachment('conversationFile', 'majolnir') return \ No newline at end of file diff --git a/scripts/quests/events/signals/giveCustomizationKit.py b/scripts/quests/events/signals/giveCustomizationKit.py new file mode 100644 index 00000000..94611f89 --- /dev/null +++ b/scripts/quests/events/signals/giveCustomizationKit.py @@ -0,0 +1,11 @@ +def setup(core): + return + +def wait(core, actor): + print ('waiting') + return + +def activate(core, actor, quest): + print ('Activate!!') + core.questService.completeActiveTask(actor, quest) + return \ No newline at end of file diff --git a/scripts/static_spawns/tatooine/mos_eisley_quest.py b/scripts/static_spawns/tatooine/mos_eisley_quest.py new file mode 100644 index 00000000..d8ca91cb --- /dev/null +++ b/scripts/static_spawns/tatooine/mos_eisley_quest.py @@ -0,0 +1,18 @@ +import sys +# Project SWG: MosEisley Quest: Static Spawns +# (C)2014 ProjectSWG + +from resources.datatables import Options +from resources.datatables import State + +# This script is intended for any quest conversation mobiles, or single attackable npcs for a quest, within Mos Eisley +def addPlanetSpawns(core, planet): + + + stcSvc = core.staticService + objSvc = core.objectService + + # Pall - Talk to Pall + pall = stcSvc.spawnObject('majolnir', 'tatooine', long(0), float(3526), float(5), float(-4619), float(0), float(0), float(0), float(0), 1) + + return \ No newline at end of file diff --git a/src/main/NGECore.java b/src/main/NGECore.java index 1cc1d7e3..06c23fc5 100644 --- a/src/main/NGECore.java +++ b/src/main/NGECore.java @@ -575,6 +575,8 @@ public class NGECore { playerCityService.loadCityRankCaps(); playerCityService.loadCities(); + questService.loadEvents(); + retroService.run(); browserService = new BrowserService(this); diff --git a/src/services/quest/QuestService.java b/src/services/quest/QuestService.java index 91596ffc..dec1e873 100644 --- a/src/services/quest/QuestService.java +++ b/src/services/quest/QuestService.java @@ -216,6 +216,11 @@ public class QuestService implements INetworkDispatch { quest.setTimer(taskTimer); break; + // quest.task.ground.wait_for_signal + case "wait_for_signal": + core.scriptService.callScript("scripts/quests/events/signals/", task.getSignalName(), "wait", core, quester); + break; + // quest.task.ground.show_message_box case "show_message_box": break; @@ -438,6 +443,26 @@ public class QuestService implements INetworkDispatch { } + public void handleActivateSignal(CreatureObject actor, TangibleObject npc, Quest quest, int taskId) { + QuestTask task = getQuestData(quest.getName()).getTasks().get(taskId); + if (task == null) + return; + + if (core.scriptService.getMethod("scripts/quests/events/signals/", task.getSignalName(), "activate") != null) { + core.scriptService.callScript("scripts/quests/events/signals/", task.getSignalName(), "activate", core, actor, quest); + return; + } + + completeActiveTask(actor, quest); + } + + public void handleAddConversationScript(TangibleObject object, String script) { + if (object == null) + return; + + object.setAttachment("conversationFile", script); + } + public WaypointObject createWaypoint(String name, Point3D location, String planet) { WaypointObject waypoint = (WaypointObject) core.objectService.createObject("object/waypoint/shared_waypoint.iff", core.terrainService.getPlanetByName(planet)); if (waypoint == null) @@ -561,6 +586,7 @@ public class QuestService implements INetworkDispatch { 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("RADIUS", r) != null && visitor.getObjectByColumnNameAndIndex("RADIUS", r) != "") task.setRadius(Float.parseFloat((String) visitor.getObjectByColumnNameAndIndex("RADIUS", r))); + if (visitor.getObjectByColumnNameAndIndex("SIGNAL_NAME", r) != null) task.setSignalName((String) visitor.getObjectByColumnNameAndIndex("SIGNAL_NAME", r)); //if (visitor.getObjectByColumnNameAndIndex("", r)) if (task.getType().equals("quest.task.ground.go_to_location")) { @@ -650,6 +676,19 @@ public class QuestService implements INetworkDispatch { return template.replace(file, "shared_" + file); } + public void loadEvents() { + FileVisitor fv = new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException + { + core.scriptService.callScript("scripts/quests/events/signals/", file.getFileName().toString().replace(".py", ""), "setup", core); + return FileVisitResult.CONTINUE; + } + }; + try { Files.walkFileTree(Paths.get("scripts/quests/events/signals/"), fv); } + catch (IOException e) { e.printStackTrace(); } + } + @Override public void shutdown() { } From 298fb1f59a126feb92f656e46bdb99bec3078b8a Mon Sep 17 00:00:00 2001 From: Waverunner Date: Mon, 25 Aug 2014 17:44:22 -0400 Subject: [PATCH 2/2] Added quest completion window, Changed sui service to not need conversation npcs to need conversation radial attached, Changed goToLocation event to check all of players quests instead of active one --- scripts/commands/completequest.py | 25 +++++++++ .../{ => quests/tatooine}/majolnir.py | 5 +- scripts/object/mobile/majolnir.py | 2 +- scripts/quests/events/go_to_location.py | 4 +- .../events/signals/giveCustomizationKit.py | 2 - .../item_content_veh_custom_kit_02_02.py | 4 ++ src/protocol/swg/ObjControllerMessage.java | 1 + .../ShowQuestCompletionWindow.java | 56 +++++++++++++++++++ src/resources/quest/Quest.java | 5 ++ src/services/ConversationService.java | 1 + src/services/quest/QuestService.java | 23 ++++---- src/services/sui/SUIService.java | 11 +++- 12 files changed, 120 insertions(+), 19 deletions(-) create mode 100644 scripts/commands/completequest.py rename scripts/conversation/{ => quests/tatooine}/majolnir.py (95%) create mode 100644 scripts/quests/rewards/item_content_veh_custom_kit_02_02.py create mode 100644 src/protocol/swg/objectControllerObjects/ShowQuestCompletionWindow.java diff --git a/scripts/commands/completequest.py b/scripts/commands/completequest.py new file mode 100644 index 00000000..1eeea046 --- /dev/null +++ b/scripts/commands/completequest.py @@ -0,0 +1,25 @@ +import sys + +def setup(): + return + +def run(core, actor, target, commandString): + + if actor is None: + return + + player = actor.getPlayerObject() + + if player is None: + return + + quest = player.getQuest(int(commandString)) + + if quest is None: + return + + if (quest.getActiveTask() != (core.questService.getQuestData(quest.getName()).getTasks().size() - 1)): + return + + core.questService.completeQuest(player, quest) + return \ No newline at end of file diff --git a/scripts/conversation/majolnir.py b/scripts/conversation/quests/tatooine/majolnir.py similarity index 95% rename from scripts/conversation/majolnir.py rename to scripts/conversation/quests/tatooine/majolnir.py index 91431d53..469f4efb 100644 --- a/scripts/conversation/majolnir.py +++ b/scripts/conversation/quests/tatooine/majolnir.py @@ -7,8 +7,7 @@ import sys def startConversation(core, actor, npc): convSvc = core.conversationService player = actor.getPlayerObject() - - print ('conversation') + if player is None: return @@ -18,7 +17,7 @@ def startConversation(core, actor, npc): endConversation(core, actor, npc) return - if quest.isComplete(): + if quest.isCompleted(): core.conversationService.sendStopConversation(actor, npc, 'conversation/tatooine_eisley_majolnir', 's_24') return diff --git a/scripts/object/mobile/majolnir.py b/scripts/object/mobile/majolnir.py index d80c08c5..68dc2c93 100644 --- a/scripts/object/mobile/majolnir.py +++ b/scripts/object/mobile/majolnir.py @@ -1,5 +1,5 @@ import sys def setup(core, object): - object.setAttachment('conversationFile', 'majolnir') + object.setAttachment('conversationFile', 'quests/tatooine/majolnir') return \ No newline at end of file diff --git a/scripts/quests/events/go_to_location.py b/scripts/quests/events/go_to_location.py index 396505a0..d5e3b141 100644 --- a/scripts/quests/events/go_to_location.py +++ b/scripts/quests/events/go_to_location.py @@ -7,10 +7,10 @@ def run(core, activator, collidable): player = activator.getPlayerObject() - if player.getActiveQuestName() != collidable.getAttachedQuest(): + if player.getQuest(collidable.getAttachedQuest()) == None: return - quest = player.getQuest(player.getActiveQuest()) + quest = player.getQuest(collidable.getAttachedQuest()) if quest.isCompleted(): return diff --git a/scripts/quests/events/signals/giveCustomizationKit.py b/scripts/quests/events/signals/giveCustomizationKit.py index 94611f89..2c219af1 100644 --- a/scripts/quests/events/signals/giveCustomizationKit.py +++ b/scripts/quests/events/signals/giveCustomizationKit.py @@ -2,10 +2,8 @@ def setup(core): return def wait(core, actor): - print ('waiting') return def activate(core, actor, quest): - print ('Activate!!') core.questService.completeActiveTask(actor, quest) return \ No newline at end of file diff --git a/scripts/quests/rewards/item_content_veh_custom_kit_02_02.py b/scripts/quests/rewards/item_content_veh_custom_kit_02_02.py new file mode 100644 index 00000000..73c2b4d8 --- /dev/null +++ b/scripts/quests/rewards/item_content_veh_custom_kit_02_02.py @@ -0,0 +1,4 @@ +import sys + +def run(): + return "object/tangible/item/shared_vehicle_customization.iff" \ No newline at end of file diff --git a/src/protocol/swg/ObjControllerMessage.java b/src/protocol/swg/ObjControllerMessage.java index d9d66a97..580318e0 100644 --- a/src/protocol/swg/ObjControllerMessage.java +++ b/src/protocol/swg/ObjControllerMessage.java @@ -81,6 +81,7 @@ public class ObjControllerMessage extends SWGMessage { public static final int QUEST_TASK_COUNTER = 0x0441; public static final int QUEST_TASK_TIMER = 0x0444; public static final int FORCE_ACTIVATE_QUEST = 0x04BE; + public static final int SHOW_QUEST_COMPLETION_WINDOW = 0x04B8; public ObjControllerMessage() { diff --git a/src/protocol/swg/objectControllerObjects/ShowQuestCompletionWindow.java b/src/protocol/swg/objectControllerObjects/ShowQuestCompletionWindow.java new file mode 100644 index 00000000..6078f56d --- /dev/null +++ b/src/protocol/swg/objectControllerObjects/ShowQuestCompletionWindow.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * 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 protocol.swg.objectControllerObjects; + +import org.apache.mina.core.buffer.IoBuffer; + +import protocol.swg.ObjControllerMessage; +import engine.resources.objects.Baseline; + +public class ShowQuestCompletionWindow extends ObjControllerObject { + + private long objectId; + private int questCrc; + + public ShowQuestCompletionWindow(long objectId, int questCrc) { + this.objectId = objectId; + this.questCrc = questCrc; + } + + @Override + public void deserialize(IoBuffer data) { + } + + @Override + public IoBuffer serialize() { + IoBuffer buffer = Baseline.createBuffer(20); + + buffer.putInt(ObjControllerMessage.SHOW_QUEST_COMPLETION_WINDOW); + buffer.putLong(objectId); + buffer.putInt(0); + + buffer.putInt(questCrc); + + return buffer.flip(); + } + +} diff --git a/src/resources/quest/Quest.java b/src/resources/quest/Quest.java index 2e346183..73549238 100644 --- a/src/resources/quest/Quest.java +++ b/src/resources/quest/Quest.java @@ -133,6 +133,11 @@ public class Quest extends Delta implements Serializable { //System.out.println("Active step was "+ (activeStep - 1) + " and is now " + activeStep ); } + public void complete() { + completedStepBitmask.set(activeStep); + activeStepBitmask.clear(); + } + public ScheduledFuture getTimer() { return timer; } diff --git a/src/services/ConversationService.java b/src/services/ConversationService.java index 29dd18ec..d3418d7c 100644 --- a/src/services/ConversationService.java +++ b/src/services/ConversationService.java @@ -60,6 +60,7 @@ public class ConversationService implements INetworkDispatch { core.simulationService.faceTarget(npc, player); player.setConversingNpc(npc); + // TODO: Come up with a way for not needing to send the StartConversation packet, some only sent the end conversation packet to show the chat bubble sendStartConversation(player, npc); core.scriptService.callScript("scripts/conversation/", (String) npc.getAttachment("conversationFile"), "startConversation", core, player, npc); } diff --git a/src/services/quest/QuestService.java b/src/services/quest/QuestService.java index dec1e873..094e2b8a 100644 --- a/src/services/quest/QuestService.java +++ b/src/services/quest/QuestService.java @@ -49,11 +49,11 @@ import protocol.swg.objectControllerObjects.ForceActivateQuest; import protocol.swg.objectControllerObjects.QuestTaskCounterMessage; import protocol.swg.objectControllerObjects.QuestTaskTimerMessage; import protocol.swg.objectControllerObjects.ShowLootBox; +import protocol.swg.objectControllerObjects.ShowQuestCompletionWindow; import resources.common.Console; import resources.common.ObjControllerOpcodes; import resources.common.OutOfBand; import resources.common.ProsePackage; -import resources.common.collidables.CollidableCircle; import resources.common.collidables.QuestCollidable; import resources.datatables.DisplayType; import resources.objects.SWGMap; @@ -262,16 +262,19 @@ public class QuestService implements INetworkDispatch { quest.getTimer().cancel(true); if (activeStep + 1 >= qData.getTasks().size()) { - QuestList listItem = questRewardMap.get(quest.getName()); + //QuestList listItem = questRewardMap.get(quest.getName()); - if (listItem.isCompleteWhenTasksComplete()) + quest.setCompleted(true); + + if (!task.isGrantQuestOnCompleteShowSystemMessage()) { completeQuest(player, quest); // Force complete packet sent if the quest isn't auto completed (window shows up, typical for exclusive reward items) - /*else - sendQuestCompleteWindow(quester, quest.getName());*/ + player.getContainer().getClient().getSession().write(player.getBaseline(8).createDelta(7)); + } else + sendQuestCompleteWindow(quester, quest.getCrc()); if (task.getGrantQuestOnComplete() != null && !task.getGrantQuestOnComplete().equals("")) activateQuest(quester, task.getGrantQuestOnComplete()); - + return; } @@ -318,9 +321,8 @@ public class QuestService implements INetworkDispatch { CreatureObject creo = (CreatureObject) player.getContainer(); - if (!quest.isCompleted()) { + if (!quest.isCompleted()) quest.setCompleted(true); - } QuestList info = getQuestList(quest.getName()); @@ -402,8 +404,9 @@ public class QuestService implements INetworkDispatch { } - public void sendQuestCompleteWindow(CreatureObject reciever, String questName) { - + public void sendQuestCompleteWindow(CreatureObject reciever, int questCrc) { + ObjControllerMessage objController = new ObjControllerMessage(0x0B, new ShowQuestCompletionWindow(reciever.getObjectID(), questCrc)); + reciever.getClient().getSession().write(objController.serialize()); } public String getQuestItemRadialName(CreatureObject quester, String template) { diff --git a/src/services/sui/SUIService.java b/src/services/sui/SUIService.java index 0165c5d1..9745276b 100644 --- a/src/services/sui/SUIService.java +++ b/src/services/sui/SUIService.java @@ -150,12 +150,17 @@ public class SUIService implements INetworkDispatch { return; } + if (target.getAttachment("conversationFile") != null) { + core.scriptService.callScript("scripts/radial/", "object/conversation", "createRadial", core, owner, target, request.getRadialOptions()); + sendRadial(owner, target, request.getRadialOptions(), request.getRadialCount()); + return; + } + core.scriptService.callScript("scripts/radial/", getRadialFilename(target), "createRadial", core, owner, target, request.getRadialOptions()); if(getRadialFilename(target).equals("default")) return; sendRadial(owner, target, request.getRadialOptions(), request.getRadialCount()); - } }); @@ -191,6 +196,10 @@ public class SUIService implements INetworkDispatch { return; } + if (target.getAttachment("conversationFile") != null) { + core.scriptService.callScript("scripts/radial/", "object/conversation", "handleSelection", core, owner, target, objMenuSelect.getSelection()); + return; + } core.scriptService.callScript("scripts/radial/", getRadialFilename(target), "handleSelection", core, owner, target, objMenuSelect.getSelection()); }