diff --git a/src/main/java/emu/grasscutter/data/custom/QuestConfigData.java b/src/main/java/emu/grasscutter/data/custom/QuestConfigData.java index 4ba0ce47c..3ede024f2 100644 --- a/src/main/java/emu/grasscutter/data/custom/QuestConfigData.java +++ b/src/main/java/emu/grasscutter/data/custom/QuestConfigData.java @@ -1,7 +1,7 @@ package emu.grasscutter.data.custom; import emu.grasscutter.game.quest.enums.LogicType; -import emu.grasscutter.game.quest.enums.QuestCondType; +import emu.grasscutter.game.quest.enums.QuestTriggerType; import emu.grasscutter.game.quest.enums.QuestType; public class QuestConfigData { @@ -90,10 +90,10 @@ public class QuestConfigData { } public class QuestCondition { - private QuestCondType type; + private QuestTriggerType type; private int[] param; - public QuestCondType getType() { + public QuestTriggerType getType() { return type; } diff --git a/src/main/java/emu/grasscutter/game/quest/GameQuest.java b/src/main/java/emu/grasscutter/game/quest/GameQuest.java index 53599830c..d3a240a07 100644 --- a/src/main/java/emu/grasscutter/game/quest/GameQuest.java +++ b/src/main/java/emu/grasscutter/game/quest/GameQuest.java @@ -3,10 +3,13 @@ package emu.grasscutter.game.quest; import dev.morphia.annotations.Entity; import dev.morphia.annotations.Transient; import emu.grasscutter.data.custom.QuestConfig; +import emu.grasscutter.data.custom.QuestConfigData.QuestCondition; import emu.grasscutter.data.custom.QuestConfigData.SubQuestConfigData; import emu.grasscutter.game.player.Player; +import emu.grasscutter.game.quest.enums.LogicType; import emu.grasscutter.game.quest.enums.QuestState; import emu.grasscutter.net.proto.QuestOuterClass.Quest; +import emu.grasscutter.server.packet.send.PacketQuestListUpdateNotify; import emu.grasscutter.server.packet.send.PacketQuestProgressUpdateNotify; import emu.grasscutter.utils.Utils; @@ -137,19 +140,34 @@ public class GameQuest { } this.getOwner().getSession().send(new PacketQuestProgressUpdateNotify(this)); - - // Finish main quest if all child quests are done - this.tryFinishMainQuest(); + this.getOwner().getSession().send(new PacketQuestListUpdateNotify(this)); this.save(); + + this.tryAcceptQuestLine(); } - public boolean tryFinishMainQuest() { + public boolean tryAcceptQuestLine() { try { - SubQuestConfigData subQuestData = getConfig().getMainQuest().getSubQuests()[getConfig().getMainQuest().getSubQuests().length - 1]; - - if (subQuestData.getSubId() == this.getQuestId()) { - getMainQuest().finish(); - return true; + for (SubQuestConfigData questData : getConfig().getMainQuest().getSubQuests()) { + GameQuest quest = getMainQuest().getChildQuestById(questData.getSubId()); + + if (quest == null) { + int[] accept = new int[questData.getAcceptCond().length]; + + // TODO + for (int i = 0; i < questData.getAcceptCond().length; i++) { + QuestCondition condition = questData.getAcceptCond()[i]; + boolean result = getOwner().getServer().getQuestHandler().triggerCondition(this, condition); + + accept[i] = result ? 1 : 0; + } + + boolean shouldAccept = LogicType.calculate(questData.getAcceptCondComb(), accept); + + if (shouldAccept) { + this.getOwner().getQuestManager().addQuest(questData.getSubId()); + } + } } } catch (Exception e) { diff --git a/src/main/java/emu/grasscutter/game/quest/QuestManager.java b/src/main/java/emu/grasscutter/game/quest/QuestManager.java index e1c26704c..76c098b07 100644 --- a/src/main/java/emu/grasscutter/game/quest/QuestManager.java +++ b/src/main/java/emu/grasscutter/game/quest/QuestManager.java @@ -1,16 +1,25 @@ package emu.grasscutter.game.quest; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.function.Consumer; import java.util.function.Function; import emu.grasscutter.data.GameData; import emu.grasscutter.data.custom.QuestConfig; +import emu.grasscutter.data.custom.QuestConfigData.QuestCondition; +import emu.grasscutter.data.custom.QuestConfigData.SubQuestConfigData; import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.game.player.Player; +import emu.grasscutter.game.quest.enums.QuestTriggerType; +import emu.grasscutter.game.quest.enums.LogicType; import emu.grasscutter.game.quest.enums.QuestState; import emu.grasscutter.server.packet.send.PacketFinishedParentQuestUpdateNotify; import emu.grasscutter.server.packet.send.PacketQuestListUpdateNotify; +import emu.grasscutter.server.packet.send.PacketQuestProgressUpdateNotify; import emu.grasscutter.server.packet.send.PacketServerCondMeetQuestListUpdateNotify; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; @@ -59,6 +68,17 @@ public class QuestManager { } } + // TODO + public void forEachActiveQuest(Consumer callback) { + for (GameMainQuest mainQuest : getQuests().values()) { + for (GameQuest quest : mainQuest.getChildQuests().values()) { + if (quest.getState() != QuestState.QUEST_STATE_FINISHED) { + callback.accept(quest); + } + } + } + } + public GameMainQuest addMainQuest(QuestConfig questConfig) { GameMainQuest mainQuest = new GameMainQuest(getPlayer(), questConfig.getMainQuest().getId()); getQuests().put(mainQuest.getParentQuestId(), mainQuest); @@ -101,6 +121,50 @@ public class QuestManager { return quest; } + + public void triggerEvent(QuestTriggerType condType, int... params) { + Set changedQuests = new HashSet<>(); + + this.forEachActiveQuest(quest -> { + SubQuestConfigData data = quest.getConfig().getSubQuest(); + + for (int i = 0; i < data.getFinishCond().length; i++) { + if (quest.getFinishProgressList()[i] == 1) { + continue; + } + + QuestCondition condition = data.getFinishCond()[i]; + + if (condition.getType() != condType) { + continue; + } + + boolean result = getPlayer().getServer().getQuestHandler().triggerContent(quest, condition, params); + + if (result) { + quest.getFinishProgressList()[i] = 1; + + changedQuests.add(quest); + } + } + }); + + for (GameQuest quest : changedQuests) { + LogicType logicType = quest.getConfig().getSubQuest().getFailCondComb(); + int[] progress = quest.getFinishProgressList(); + + // Handle logical comb + boolean finish = LogicType.calculate(logicType, progress); + + // Finish + if (finish) { + quest.finish(); + } else { + getPlayer().sendPacket(new PacketQuestProgressUpdateNotify(quest)); + quest.save(); + } + } + } public void loadFromDatabase() { List quests = DatabaseHelper.getAllQuests(getPlayer()); diff --git a/src/main/java/emu/grasscutter/game/quest/QuestValue.java b/src/main/java/emu/grasscutter/game/quest/QuestValue.java new file mode 100644 index 000000000..3042ad5de --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/QuestValue.java @@ -0,0 +1,11 @@ +package emu.grasscutter.game.quest; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import emu.grasscutter.game.quest.enums.QuestTriggerType; + +@Retention(RetentionPolicy.RUNTIME) +public @interface QuestValue { + QuestTriggerType value(); +} diff --git a/src/main/java/emu/grasscutter/game/quest/ServerQuestHandler.java b/src/main/java/emu/grasscutter/game/quest/ServerQuestHandler.java new file mode 100644 index 000000000..1c269de90 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/ServerQuestHandler.java @@ -0,0 +1,91 @@ +package emu.grasscutter.game.quest; + +import java.util.Set; + +import org.reflections.Reflections; + +import emu.grasscutter.data.custom.QuestConfigData.QuestCondition; +import emu.grasscutter.game.quest.enums.QuestTriggerType; +import emu.grasscutter.game.quest.handlers.QuestBaseHandler; +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.server.game.GameServer; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; + +@SuppressWarnings("unchecked") +public class ServerQuestHandler { + private final Int2ObjectMap condHandlers; + private final Int2ObjectMap contHandlers; + private final Int2ObjectMap execHandlers; + + public ServerQuestHandler() { + this.condHandlers = new Int2ObjectOpenHashMap<>(); + this.contHandlers = new Int2ObjectOpenHashMap<>(); + this.execHandlers = new Int2ObjectOpenHashMap<>(); + + this.registerHandlers(); + } + + public void registerHandlers() { + this.registerHandlers(this.condHandlers, "emu.grasscutter.game.quest.conditions"); + this.registerHandlers(this.contHandlers, "emu.grasscutter.game.quest.content"); + this.registerHandlers(this.execHandlers, "emu.grasscutter.game.quest.exec"); + } + + public void registerHandlers(Int2ObjectMap map, String packageName) { + Reflections reflections = new Reflections(packageName); + Set handlerClasses = reflections.getSubTypesOf(QuestBaseHandler.class); + + for (Object obj : handlerClasses) { + this.registerPacketHandler(map, (Class) obj); + } + } + + public void registerPacketHandler(Int2ObjectMap map, Class handlerClass) { + try { + QuestValue opcode = handlerClass.getAnnotation(QuestValue.class); + + if (opcode == null || opcode.value().getValue() <= 0) { + return; + } + + QuestBaseHandler packetHandler = (QuestBaseHandler) handlerClass.newInstance(); + + map.put(opcode.value().getValue(), packetHandler); + } catch (Exception e) { + e.printStackTrace(); + } + } + + // TODO make cleaner + + public boolean triggerCondition(GameQuest quest, QuestCondition condition, int... params) { + QuestBaseHandler handler = condHandlers.get(condition.getType().getValue()); + + if (handler == null || quest.getConfig() == null) { + return false; + } + + return handler.execute(quest, condition, params); + } + + public boolean triggerContent(GameQuest quest, QuestCondition condition, int... params) { + QuestBaseHandler handler = contHandlers.get(condition.getType().getValue()); + + if (handler == null || quest.getConfig() == null) { + return false; + } + + return handler.execute(quest, condition, params); + } + + public boolean triggerExec(GameQuest quest, QuestCondition condition, int... params) { + QuestBaseHandler handler = execHandlers.get(condition.getType().getValue()); + + if (handler == null || quest.getConfig() == null) { + return false; + } + + return handler.execute(quest, condition, params); + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/conditions/BaseCondition.java b/src/main/java/emu/grasscutter/game/quest/conditions/BaseCondition.java new file mode 100644 index 000000000..903773f0e --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/conditions/BaseCondition.java @@ -0,0 +1,18 @@ +package emu.grasscutter.game.quest.conditions; + +import emu.grasscutter.data.custom.QuestConfigData.QuestCondition; +import emu.grasscutter.game.quest.QuestValue; +import emu.grasscutter.game.quest.GameQuest; +import emu.grasscutter.game.quest.enums.QuestTriggerType; +import emu.grasscutter.game.quest.handlers.QuestBaseHandler; + +@QuestValue(QuestTriggerType.QUEST_CONTENT_NONE) +public class BaseCondition extends QuestBaseHandler { + + @Override + public boolean execute(GameQuest quest, QuestCondition condition, int... params) { + // TODO Auto-generated method stub + return false; + } + +} diff --git a/src/main/java/emu/grasscutter/game/quest/conditions/ConditionPlayerLevelEqualGreater.java b/src/main/java/emu/grasscutter/game/quest/conditions/ConditionPlayerLevelEqualGreater.java new file mode 100644 index 000000000..f5df2b13c --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/conditions/ConditionPlayerLevelEqualGreater.java @@ -0,0 +1,17 @@ +package emu.grasscutter.game.quest.conditions; + +import emu.grasscutter.data.custom.QuestConfigData.QuestCondition; +import emu.grasscutter.game.quest.QuestValue; +import emu.grasscutter.game.quest.GameQuest; +import emu.grasscutter.game.quest.enums.QuestTriggerType; +import emu.grasscutter.game.quest.handlers.QuestBaseHandler; + +@QuestValue(QuestTriggerType.QUEST_COND_PLAYER_LEVEL_EQUAL_GREATER) +public class ConditionPlayerLevelEqualGreater extends QuestBaseHandler { + + @Override + public boolean execute(GameQuest quest, QuestCondition condition, int... params) { + return quest.getOwner().getLevel() >= condition.getParam()[0]; + } + +} diff --git a/src/main/java/emu/grasscutter/game/quest/conditions/ConditionStateEqual.java b/src/main/java/emu/grasscutter/game/quest/conditions/ConditionStateEqual.java new file mode 100644 index 000000000..71b44c967 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/conditions/ConditionStateEqual.java @@ -0,0 +1,23 @@ +package emu.grasscutter.game.quest.conditions; + +import emu.grasscutter.data.custom.QuestConfigData.QuestCondition; +import emu.grasscutter.game.quest.QuestValue; +import emu.grasscutter.game.quest.GameQuest; +import emu.grasscutter.game.quest.enums.QuestTriggerType; +import emu.grasscutter.game.quest.handlers.QuestBaseHandler; + +@QuestValue(QuestTriggerType.QUEST_COND_STATE_EQUAL) +public class ConditionStateEqual extends QuestBaseHandler { + + @Override + public boolean execute(GameQuest quest, QuestCondition condition, int... params) { + GameQuest checkQuest = quest.getOwner().getQuestManager().getQuestById(condition.getParam()[0]); + + if (checkQuest != null) { + return checkQuest.getState().getValue() == condition.getParam()[1]; + } + + return false; + } + +} diff --git a/src/main/java/emu/grasscutter/game/quest/content/BaseContent.java b/src/main/java/emu/grasscutter/game/quest/content/BaseContent.java new file mode 100644 index 000000000..820d6f133 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/content/BaseContent.java @@ -0,0 +1,18 @@ +package emu.grasscutter.game.quest.content; + +import emu.grasscutter.data.custom.QuestConfigData.QuestCondition; +import emu.grasscutter.game.quest.QuestValue; +import emu.grasscutter.game.quest.GameQuest; +import emu.grasscutter.game.quest.enums.QuestTriggerType; +import emu.grasscutter.game.quest.handlers.QuestBaseHandler; + +@QuestValue(QuestTriggerType.QUEST_CONTENT_NONE) +public class BaseContent extends QuestBaseHandler { + + @Override + public boolean execute(GameQuest quest, QuestCondition condition, int... params) { + // TODO Auto-generated method stub + return false; + } + +} diff --git a/src/main/java/emu/grasscutter/game/quest/content/ContentCompleteTalk.java b/src/main/java/emu/grasscutter/game/quest/content/ContentCompleteTalk.java new file mode 100644 index 000000000..aad196306 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/content/ContentCompleteTalk.java @@ -0,0 +1,17 @@ +package emu.grasscutter.game.quest.content; + +import emu.grasscutter.data.custom.QuestConfigData.QuestCondition; +import emu.grasscutter.game.quest.QuestValue; +import emu.grasscutter.game.quest.GameQuest; +import emu.grasscutter.game.quest.enums.QuestTriggerType; +import emu.grasscutter.game.quest.handlers.QuestBaseHandler; + +@QuestValue(QuestTriggerType.QUEST_CONTENT_COMPLETE_TALK) +public class ContentCompleteTalk extends QuestBaseHandler { + + @Override + public boolean execute(GameQuest quest, QuestCondition condition, int... params) { + return condition.getParam()[0] == params[0]; + } + +} diff --git a/src/main/java/emu/grasscutter/game/quest/enums/LogicType.java b/src/main/java/emu/grasscutter/game/quest/enums/LogicType.java index 608ec9c28..12677ee0d 100644 --- a/src/main/java/emu/grasscutter/game/quest/enums/LogicType.java +++ b/src/main/java/emu/grasscutter/game/quest/enums/LogicType.java @@ -1,5 +1,7 @@ package emu.grasscutter.game.quest.enums; +import java.util.Arrays; + public enum LogicType { LOGIC_NONE (0), LOGIC_AND (1), @@ -20,4 +22,22 @@ public enum LogicType { public int getValue() { return value; } + + public static boolean calculate(LogicType logicType, int[] progress) { + if (logicType == null) { + return progress[0] == 1; + } + + switch (logicType) { + case LOGIC_AND -> { + return Arrays.stream(progress).allMatch(i -> i == 1); + } + case LOGIC_OR -> { + return Arrays.stream(progress).anyMatch(i -> i == 1); + } + default -> { + return Arrays.stream(progress).anyMatch(i -> i == 1); + } + } + } } diff --git a/src/main/java/emu/grasscutter/game/quest/enums/QuestCondType.java b/src/main/java/emu/grasscutter/game/quest/enums/QuestCondType.java deleted file mode 100644 index 42db14f2d..000000000 --- a/src/main/java/emu/grasscutter/game/quest/enums/QuestCondType.java +++ /dev/null @@ -1,92 +0,0 @@ -package emu.grasscutter.game.quest.enums; - -public enum QuestCondType { - QUEST_COND_NONE (0), - QUEST_COND_STATE_EQUAL (1), - QUEST_COND_STATE_NOT_EQUAL (2), - QUEST_COND_PACK_HAVE_ITEM (3), - QUEST_COND_AVATAR_ELEMENT_EQUAL (4), - QUEST_COND_AVATAR_ELEMENT_NOT_EQUAL (5), - QUEST_COND_AVATAR_CAN_CHANGE_ELEMENT (6), - QUEST_COND_CITY_LEVEL_EQUAL_GREATER (7), - QUEST_COND_ITEM_NUM_LESS_THAN (8), - QUEST_COND_DAILY_TASK_START (9), - QUEST_COND_OPEN_STATE_EQUAL (10), - QUEST_COND_DAILY_TASK_OPEN (11), - QUEST_COND_DAILY_TASK_REWARD_CAN_GET (12), - QUEST_COND_DAILY_TASK_REWARD_RECEIVED (13), - QUEST_COND_PLAYER_LEVEL_REWARD_CAN_GET (14), - QUEST_COND_EXPLORATION_REWARD_CAN_GET (15), - QUEST_COND_IS_WORLD_OWNER (16), - QUEST_COND_PLAYER_LEVEL_EQUAL_GREATER (17), - QUEST_COND_SCENE_AREA_UNLOCKED (18), - QUEST_COND_ITEM_GIVING_ACTIVED (19), - QUEST_COND_ITEM_GIVING_FINISHED (20), - QUEST_COND_IS_DAYTIME (21), - QUEST_COND_CURRENT_AVATAR (22), - QUEST_COND_CURRENT_AREA (23), - QUEST_COND_QUEST_VAR_EQUAL (24), - QUEST_COND_QUEST_VAR_GREATER (25), - QUEST_COND_QUEST_VAR_LESS (26), - QUEST_COND_FORGE_HAVE_FINISH (27), - QUEST_COND_DAILY_TASK_IN_PROGRESS (28), - QUEST_COND_DAILY_TASK_FINISHED (29), - QUEST_COND_ACTIVITY_COND (30), - QUEST_COND_ACTIVITY_OPEN (31), - QUEST_COND_DAILY_TASK_VAR_GT (32), - QUEST_COND_DAILY_TASK_VAR_EQ (33), - QUEST_COND_DAILY_TASK_VAR_LT (34), - QUEST_COND_BARGAIN_ITEM_GT (35), - QUEST_COND_BARGAIN_ITEM_EQ (36), - QUEST_COND_BARGAIN_ITEM_LT (37), - QUEST_COND_COMPLETE_TALK (38), - QUEST_COND_NOT_HAVE_BLOSSOM_TALK (39), - QUEST_COND_IS_CUR_BLOSSOM_TALK (40), - QUEST_COND_QUEST_NOT_RECEIVE (41), - QUEST_COND_QUEST_SERVER_COND_VALID (42), - QUEST_COND_ACTIVITY_CLIENT_COND (43), - QUEST_COND_QUEST_GLOBAL_VAR_EQUAL (44), - QUEST_COND_QUEST_GLOBAL_VAR_GREATER (45), - QUEST_COND_QUEST_GLOBAL_VAR_LESS (46), - QUEST_COND_PERSONAL_LINE_UNLOCK (47), - QUEST_COND_CITY_REPUTATION_REQUEST (48), - QUEST_COND_MAIN_COOP_START (49), - QUEST_COND_MAIN_COOP_ENTER_SAVE_POINT (50), - QUEST_COND_CITY_REPUTATION_LEVEL (51), - QUEST_COND_CITY_REPUTATION_UNLOCK (52), - QUEST_COND_LUA_NOTIFY (53), - QUEST_COND_CUR_CLIMATE (54), - QUEST_COND_ACTIVITY_END (55), - QUEST_COND_COOP_POINT_RUNNING (56), - QUEST_COND_GADGET_TALK_STATE_EQUAL (57), - QUEST_COND_AVATAR_FETTER_GT (58), - QUEST_COND_AVATAR_FETTER_EQ (59), - QUEST_COND_AVATAR_FETTER_LT (60), - QUEST_COND_NEW_HOMEWORLD_MOUDLE_UNLOCK (61), - QUEST_COND_NEW_HOMEWORLD_LEVEL_REWARD (62), - QUEST_COND_NEW_HOMEWORLD_MAKE_FINISH (63), - QUEST_COND_HOMEWORLD_NPC_EVENT (64), - QUEST_COND_TIME_VAR_GT_EQ (65), - QUEST_COND_TIME_VAR_PASS_DAY (66), - QUEST_COND_HOMEWORLD_NPC_NEW_TALK (67), - QUEST_COND_PLAYER_CHOOSE_MALE (68), - QUEST_COND_HISTORY_GOT_ANY_ITEM (69), - QUEST_COND_LEARNED_RECIPE (70), - QUEST_COND_LUNARITE_REGION_UNLOCKED (71), - QUEST_COND_LUNARITE_HAS_REGION_HINT_COUNT (72), - QUEST_COND_LUNARITE_COLLECT_FINISH (73), - QUEST_COND_LUNARITE_MARK_ALL_FINISH (74), - QUEST_COND_NEW_HOMEWORLD_SHOP_ITEM (75), - QUEST_COND_SCENE_POINT_UNLOCK (76), - QUEST_COND_SCENE_LEVEL_TAG_EQ (77); - - private final int value; - - QuestCondType(int id) { - this.value = id; - } - - public int getValue() { - return value; - } -} diff --git a/src/main/java/emu/grasscutter/game/quest/enums/QuestExecType.java b/src/main/java/emu/grasscutter/game/quest/enums/QuestExecType.java deleted file mode 100644 index 4f3c2557c..000000000 --- a/src/main/java/emu/grasscutter/game/quest/enums/QuestExecType.java +++ /dev/null @@ -1,82 +0,0 @@ -package emu.grasscutter.game.quest.enums; - -public enum QuestExecType { - QUEST_EXEC_NONE (0), - QUEST_EXEC_DEL_PACK_ITEM (1), - QUEST_EXEC_UNLOCK_POINT (2), - QUEST_EXEC_UNLOCK_AREA (3), - QUEST_EXEC_UNLOCK_FORCE (4), - QUEST_EXEC_LOCK_FORCE (5), - QUEST_EXEC_CHANGE_AVATAR_ELEMET (6), - QUEST_EXEC_REFRESH_GROUP_MONSTER (7), - QUEST_EXEC_SET_IS_FLYABLE (8), - QUEST_EXEC_SET_IS_WEATHER_LOCKED (9), - QUEST_EXEC_SET_IS_GAME_TIME_LOCKED (10), - QUEST_EXEC_SET_IS_TRANSFERABLE (11), - QUEST_EXEC_GRANT_TRIAL_AVATAR (12), - QUEST_EXEC_OPEN_BORED (13), - QUEST_EXEC_ROLLBACK_QUEST (14), - QUEST_EXEC_NOTIFY_GROUP_LUA (15), - QUEST_EXEC_SET_OPEN_STATE (16), - QUEST_EXEC_LOCK_POINT (17), - QUEST_EXEC_DEL_PACK_ITEM_BATCH (18), - QUEST_EXEC_REFRESH_GROUP_SUITE (19), - QUEST_EXEC_REMOVE_TRIAL_AVATAR (20), - QUEST_EXEC_SET_GAME_TIME (21), - QUEST_EXEC_SET_WEATHER_GADGET (22), - QUEST_EXEC_ADD_QUEST_PROGRESS (23), - QUEST_EXEC_NOTIFY_DAILY_TASK (24), - QUEST_EXEC_CREATE_PATTERN_GROUP (25), - QUEST_EXEC_REMOVE_PATTERN_GROUP (26), - QUEST_EXEC_REFRESH_GROUP_SUITE_RANDOM (27), - QUEST_EXEC_ACTIVE_ITEM_GIVING (28), - QUEST_EXEC_DEL_ALL_SPECIFIC_PACK_ITEM (29), - QUEST_EXEC_ROLLBACK_PARENT_QUEST (30), - QUEST_EXEC_LOCK_AVATAR_TEAM (31), - QUEST_EXEC_UNLOCK_AVATAR_TEAM (32), - QUEST_EXEC_UPDATE_PARENT_QUEST_REWARD_INDEX (33), - QUEST_EXEC_SET_DAILY_TASK_VAR (34), - QUEST_EXEC_INC_DAILY_TASK_VAR (35), - QUEST_EXEC_DEC_DAILY_TASK_VAR (36), - QUEST_EXEC_ACTIVE_ACTIVITY_COND_STATE (37), - QUEST_EXEC_INACTIVE_ACTIVITY_COND_STATE (38), - QUEST_EXEC_ADD_CUR_AVATAR_ENERGY (39), - QUEST_EXEC_START_BARGAIN (41), - QUEST_EXEC_STOP_BARGAIN (42), - QUEST_EXEC_SET_QUEST_GLOBAL_VAR (43), - QUEST_EXEC_INC_QUEST_GLOBAL_VAR (44), - QUEST_EXEC_DEC_QUEST_GLOBAL_VAR (45), - QUEST_EXEC_REGISTER_DYNAMIC_GROUP (46), - QUEST_EXEC_UNREGISTER_DYNAMIC_GROUP (47), - QUEST_EXEC_SET_QUEST_VAR (48), - QUEST_EXEC_INC_QUEST_VAR (49), - QUEST_EXEC_DEC_QUEST_VAR (50), - QUEST_EXEC_RANDOM_QUEST_VAR (51), - QUEST_EXEC_ACTIVATE_SCANNING_PIC (52), - QUEST_EXEC_RELOAD_SCENE_TAG (53), - QUEST_EXEC_REGISTER_DYNAMIC_GROUP_ONLY (54), - QUEST_EXEC_CHANGE_SKILL_DEPOT (55), - QUEST_EXEC_ADD_SCENE_TAG (56), - QUEST_EXEC_DEL_SCENE_TAG (57), - QUEST_EXEC_INIT_TIME_VAR (58), - QUEST_EXEC_CLEAR_TIME_VAR (59), - QUEST_EXEC_MODIFY_CLIMATE_AREA (60), - QUEST_EXEC_GRANT_TRIAL_AVATAR_AND_LOCK_TEAM (61), - QUEST_EXEC_CHANGE_MAP_AREA_STATE (62), - QUEST_EXEC_DEACTIVE_ITEM_GIVING (63), - QUEST_EXEC_CHANGE_SCENE_LEVEL_TAG (64), - QUEST_EXEC_UNLOCK_PLAYER_WORLD_SCENE (65), - QUEST_EXEC_LOCK_PLAYER_WORLD_SCENE (66), - QUEST_EXEC_FAIL_MAINCOOP (67), - QUEST_EXEC_MODIFY_WEATHER_AREA (68); - - private final int value; - - QuestExecType(int id) { - this.value = id; - } - - public int getValue() { - return value; - } -} diff --git a/src/main/java/emu/grasscutter/game/quest/enums/QuestTriggerType.java b/src/main/java/emu/grasscutter/game/quest/enums/QuestTriggerType.java new file mode 100644 index 000000000..cf8dabdba --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/enums/QuestTriggerType.java @@ -0,0 +1,235 @@ +package emu.grasscutter.game.quest.enums; + +public enum QuestTriggerType { + QUEST_COND_NONE (0), + QUEST_COND_STATE_EQUAL (1), + QUEST_COND_STATE_NOT_EQUAL (2), + QUEST_COND_PACK_HAVE_ITEM (3), + QUEST_COND_AVATAR_ELEMENT_EQUAL (4), + QUEST_COND_AVATAR_ELEMENT_NOT_EQUAL (5), + QUEST_COND_AVATAR_CAN_CHANGE_ELEMENT (6), + QUEST_COND_CITY_LEVEL_EQUAL_GREATER (7), + QUEST_COND_ITEM_NUM_LESS_THAN (8), + QUEST_COND_DAILY_TASK_START (9), + QUEST_COND_OPEN_STATE_EQUAL (10), + QUEST_COND_DAILY_TASK_OPEN (11), + QUEST_COND_DAILY_TASK_REWARD_CAN_GET (12), + QUEST_COND_DAILY_TASK_REWARD_RECEIVED (13), + QUEST_COND_PLAYER_LEVEL_REWARD_CAN_GET (14), + QUEST_COND_EXPLORATION_REWARD_CAN_GET (15), + QUEST_COND_IS_WORLD_OWNER (16), + QUEST_COND_PLAYER_LEVEL_EQUAL_GREATER (17), + QUEST_COND_SCENE_AREA_UNLOCKED (18), + QUEST_COND_ITEM_GIVING_ACTIVED (19), + QUEST_COND_ITEM_GIVING_FINISHED (20), + QUEST_COND_IS_DAYTIME (21), + QUEST_COND_CURRENT_AVATAR (22), + QUEST_COND_CURRENT_AREA (23), + QUEST_COND_QUEST_VAR_EQUAL (24), + QUEST_COND_QUEST_VAR_GREATER (25), + QUEST_COND_QUEST_VAR_LESS (26), + QUEST_COND_FORGE_HAVE_FINISH (27), + QUEST_COND_DAILY_TASK_IN_PROGRESS (28), + QUEST_COND_DAILY_TASK_FINISHED (29), + QUEST_COND_ACTIVITY_COND (30), + QUEST_COND_ACTIVITY_OPEN (31), + QUEST_COND_DAILY_TASK_VAR_GT (32), + QUEST_COND_DAILY_TASK_VAR_EQ (33), + QUEST_COND_DAILY_TASK_VAR_LT (34), + QUEST_COND_BARGAIN_ITEM_GT (35), + QUEST_COND_BARGAIN_ITEM_EQ (36), + QUEST_COND_BARGAIN_ITEM_LT (37), + QUEST_COND_COMPLETE_TALK (38), + QUEST_COND_NOT_HAVE_BLOSSOM_TALK (39), + QUEST_COND_IS_CUR_BLOSSOM_TALK (40), + QUEST_COND_QUEST_NOT_RECEIVE (41), + QUEST_COND_QUEST_SERVER_COND_VALID (42), + QUEST_COND_ACTIVITY_CLIENT_COND (43), + QUEST_COND_QUEST_GLOBAL_VAR_EQUAL (44), + QUEST_COND_QUEST_GLOBAL_VAR_GREATER (45), + QUEST_COND_QUEST_GLOBAL_VAR_LESS (46), + QUEST_COND_PERSONAL_LINE_UNLOCK (47), + QUEST_COND_CITY_REPUTATION_REQUEST (48), + QUEST_COND_MAIN_COOP_START (49), + QUEST_COND_MAIN_COOP_ENTER_SAVE_POINT (50), + QUEST_COND_CITY_REPUTATION_LEVEL (51), + QUEST_COND_CITY_REPUTATION_UNLOCK (52), + QUEST_COND_LUA_NOTIFY (53), + QUEST_COND_CUR_CLIMATE (54), + QUEST_COND_ACTIVITY_END (55), + QUEST_COND_COOP_POINT_RUNNING (56), + QUEST_COND_GADGET_TALK_STATE_EQUAL (57), + QUEST_COND_AVATAR_FETTER_GT (58), + QUEST_COND_AVATAR_FETTER_EQ (59), + QUEST_COND_AVATAR_FETTER_LT (60), + QUEST_COND_NEW_HOMEWORLD_MOUDLE_UNLOCK (61), + QUEST_COND_NEW_HOMEWORLD_LEVEL_REWARD (62), + QUEST_COND_NEW_HOMEWORLD_MAKE_FINISH (63), + QUEST_COND_HOMEWORLD_NPC_EVENT (64), + QUEST_COND_TIME_VAR_GT_EQ (65), + QUEST_COND_TIME_VAR_PASS_DAY (66), + QUEST_COND_HOMEWORLD_NPC_NEW_TALK (67), + QUEST_COND_PLAYER_CHOOSE_MALE (68), + QUEST_COND_HISTORY_GOT_ANY_ITEM (69), + QUEST_COND_LEARNED_RECIPE (70), + QUEST_COND_LUNARITE_REGION_UNLOCKED (71), + QUEST_COND_LUNARITE_HAS_REGION_HINT_COUNT (72), + QUEST_COND_LUNARITE_COLLECT_FINISH (73), + QUEST_COND_LUNARITE_MARK_ALL_FINISH (74), + QUEST_COND_NEW_HOMEWORLD_SHOP_ITEM (75), + QUEST_COND_SCENE_POINT_UNLOCK (76), + QUEST_COND_SCENE_LEVEL_TAG_EQ (77), + + QUEST_CONTENT_NONE (0), + QUEST_CONTENT_KILL_MONSTER (1), + QUEST_CONTENT_COMPLETE_TALK (2), + QUEST_CONTENT_MONSTER_DIE (3), + QUEST_CONTENT_FINISH_PLOT (4), + QUEST_CONTENT_OBTAIN_ITEM (5), + QUEST_CONTENT_TRIGGER_FIRE (6), + QUEST_CONTENT_CLEAR_GROUP_MONSTER (7), + QUEST_CONTENT_NOT_FINISH_PLOT (8), + QUEST_CONTENT_ENTER_DUNGEON (9), + QUEST_CONTENT_ENTER_MY_WORLD (10), + QUEST_CONTENT_FINISH_DUNGEON (11), + QUEST_CONTENT_DESTROY_GADGET (12), + QUEST_CONTENT_OBTAIN_MATERIAL_WITH_SUBTYPE (13), + QUEST_CONTENT_NICK_NAME (14), + QUEST_CONTENT_WORKTOP_SELECT (15), + QUEST_CONTENT_SEAL_BATTLE_RESULT (16), + QUEST_CONTENT_ENTER_ROOM (17), + QUEST_CONTENT_GAME_TIME_TICK (18), + QUEST_CONTENT_FAIL_DUNGEON (19), + QUEST_CONTENT_LUA_NOTIFY (20), + QUEST_CONTENT_TEAM_DEAD (21), + QUEST_CONTENT_COMPLETE_ANY_TALK (22), + QUEST_CONTENT_UNLOCK_TRANS_POINT (23), + QUEST_CONTENT_ADD_QUEST_PROGRESS (24), + QUEST_CONTENT_INTERACT_GADGET (25), + QUEST_CONTENT_DAILY_TASK_COMP_FINISH (26), + QUEST_CONTENT_FINISH_ITEM_GIVING (27), + QUEST_CONTENT_SKILL (107), + QUEST_CONTENT_CITY_LEVEL_UP (109), + QUEST_CONTENT_PATTERN_GROUP_CLEAR_MONSTER (110), + QUEST_CONTENT_ITEM_LESS_THAN (111), + QUEST_CONTENT_PLAYER_LEVEL_UP (112), + QUEST_CONTENT_DUNGEON_OPEN_STATUE (113), + QUEST_CONTENT_UNLOCK_AREA (114), + QUEST_CONTENT_OPEN_CHEST_WITH_GADGET_ID (115), + QUEST_CONTENT_UNLOCK_TRANS_POINT_WITH_TYPE (116), + QUEST_CONTENT_FINISH_DAILY_DUNGEON (117), + QUEST_CONTENT_FINISH_WEEKLY_DUNGEON (118), + QUEST_CONTENT_QUEST_VAR_EQUAL (119), + QUEST_CONTENT_QUEST_VAR_GREATER (120), + QUEST_CONTENT_QUEST_VAR_LESS (121), + QUEST_CONTENT_OBTAIN_VARIOUS_ITEM (122), + QUEST_CONTENT_FINISH_TOWER_LEVEL (123), + QUEST_CONTENT_BARGAIN_SUCC (124), + QUEST_CONTENT_BARGAIN_FAIL (125), + QUEST_CONTENT_ITEM_LESS_THAN_BARGAIN (126), + QUEST_CONTENT_ACTIVITY_TRIGGER_FAILED (127), + QUEST_CONTENT_MAIN_COOP_ENTER_SAVE_POINT (128), + QUEST_CONTENT_ANY_MANUAL_TRANSPORT (129), + QUEST_CONTENT_USE_ITEM (130), + QUEST_CONTENT_MAIN_COOP_ENTER_ANY_SAVE_POINT (131), + QUEST_CONTENT_ENTER_MY_HOME_WORLD (132), + QUEST_CONTENT_ENTER_MY_WORLD_SCENE (133), + QUEST_CONTENT_TIME_VAR_GT_EQ (134), + QUEST_CONTENT_TIME_VAR_PASS_DAY (135), + QUEST_CONTENT_QUEST_STATE_EQUAL (136), + QUEST_CONTENT_QUEST_STATE_NOT_EQUAL (137), + QUEST_CONTENT_UNLOCKED_RECIPE (138), + QUEST_CONTENT_NOT_UNLOCKED_RECIPE (139), + QUEST_CONTENT_FISHING_SUCC (140), + QUEST_CONTENT_ENTER_ROGUE_DUNGEON (141), + QUEST_CONTENT_USE_WIDGET (142), + QUEST_CONTENT_CAPTURE_SUCC (143), + QUEST_CONTENT_CAPTURE_USE_CAPTURETAG_LIST (144), + QUEST_CONTENT_CAPTURE_USE_MATERIAL_LIST (145), + QUEST_CONTENT_ENTER_VEHICLE (147), + QUEST_CONTENT_SCENE_LEVEL_TAG_EQ (148), + QUEST_CONTENT_LEAVE_SCENE (149), + QUEST_CONTENT_LEAVE_SCENE_RANGE (150), + QUEST_CONTENT_IRODORI_FINISH_FLOWER_COMBINATION (151), + QUEST_CONTENT_IRODORI_POETRY_REACH_MIN_PROGRESS (152), + QUEST_CONTENT_IRODORI_POETRY_FINISH_FILL_POETRY (153), + + QUEST_EXEC_NONE (0), + QUEST_EXEC_DEL_PACK_ITEM (1), + QUEST_EXEC_UNLOCK_POINT (2), + QUEST_EXEC_UNLOCK_AREA (3), + QUEST_EXEC_UNLOCK_FORCE (4), + QUEST_EXEC_LOCK_FORCE (5), + QUEST_EXEC_CHANGE_AVATAR_ELEMET (6), + QUEST_EXEC_REFRESH_GROUP_MONSTER (7), + QUEST_EXEC_SET_IS_FLYABLE (8), + QUEST_EXEC_SET_IS_WEATHER_LOCKED (9), + QUEST_EXEC_SET_IS_GAME_TIME_LOCKED (10), + QUEST_EXEC_SET_IS_TRANSFERABLE (11), + QUEST_EXEC_GRANT_TRIAL_AVATAR (12), + QUEST_EXEC_OPEN_BORED (13), + QUEST_EXEC_ROLLBACK_QUEST (14), + QUEST_EXEC_NOTIFY_GROUP_LUA (15), + QUEST_EXEC_SET_OPEN_STATE (16), + QUEST_EXEC_LOCK_POINT (17), + QUEST_EXEC_DEL_PACK_ITEM_BATCH (18), + QUEST_EXEC_REFRESH_GROUP_SUITE (19), + QUEST_EXEC_REMOVE_TRIAL_AVATAR (20), + QUEST_EXEC_SET_GAME_TIME (21), + QUEST_EXEC_SET_WEATHER_GADGET (22), + QUEST_EXEC_ADD_QUEST_PROGRESS (23), + QUEST_EXEC_NOTIFY_DAILY_TASK (24), + QUEST_EXEC_CREATE_PATTERN_GROUP (25), + QUEST_EXEC_REMOVE_PATTERN_GROUP (26), + QUEST_EXEC_REFRESH_GROUP_SUITE_RANDOM (27), + QUEST_EXEC_ACTIVE_ITEM_GIVING (28), + QUEST_EXEC_DEL_ALL_SPECIFIC_PACK_ITEM (29), + QUEST_EXEC_ROLLBACK_PARENT_QUEST (30), + QUEST_EXEC_LOCK_AVATAR_TEAM (31), + QUEST_EXEC_UNLOCK_AVATAR_TEAM (32), + QUEST_EXEC_UPDATE_PARENT_QUEST_REWARD_INDEX (33), + QUEST_EXEC_SET_DAILY_TASK_VAR (34), + QUEST_EXEC_INC_DAILY_TASK_VAR (35), + QUEST_EXEC_DEC_DAILY_TASK_VAR (36), + QUEST_EXEC_ACTIVE_ACTIVITY_COND_STATE (37), + QUEST_EXEC_INACTIVE_ACTIVITY_COND_STATE (38), + QUEST_EXEC_ADD_CUR_AVATAR_ENERGY (39), + QUEST_EXEC_START_BARGAIN (41), + QUEST_EXEC_STOP_BARGAIN (42), + QUEST_EXEC_SET_QUEST_GLOBAL_VAR (43), + QUEST_EXEC_INC_QUEST_GLOBAL_VAR (44), + QUEST_EXEC_DEC_QUEST_GLOBAL_VAR (45), + QUEST_EXEC_REGISTER_DYNAMIC_GROUP (46), + QUEST_EXEC_UNREGISTER_DYNAMIC_GROUP (47), + QUEST_EXEC_SET_QUEST_VAR (48), + QUEST_EXEC_INC_QUEST_VAR (49), + QUEST_EXEC_DEC_QUEST_VAR (50), + QUEST_EXEC_RANDOM_QUEST_VAR (51), + QUEST_EXEC_ACTIVATE_SCANNING_PIC (52), + QUEST_EXEC_RELOAD_SCENE_TAG (53), + QUEST_EXEC_REGISTER_DYNAMIC_GROUP_ONLY (54), + QUEST_EXEC_CHANGE_SKILL_DEPOT (55), + QUEST_EXEC_ADD_SCENE_TAG (56), + QUEST_EXEC_DEL_SCENE_TAG (57), + QUEST_EXEC_INIT_TIME_VAR (58), + QUEST_EXEC_CLEAR_TIME_VAR (59), + QUEST_EXEC_MODIFY_CLIMATE_AREA (60), + QUEST_EXEC_GRANT_TRIAL_AVATAR_AND_LOCK_TEAM (61), + QUEST_EXEC_CHANGE_MAP_AREA_STATE (62), + QUEST_EXEC_DEACTIVE_ITEM_GIVING (63), + QUEST_EXEC_CHANGE_SCENE_LEVEL_TAG (64), + QUEST_EXEC_UNLOCK_PLAYER_WORLD_SCENE (65), + QUEST_EXEC_LOCK_PLAYER_WORLD_SCENE (66), + QUEST_EXEC_FAIL_MAINCOOP (67), + QUEST_EXEC_MODIFY_WEATHER_AREA (68); + + private final int value; + + QuestTriggerType(int id) { + this.value = id; + } + + public int getValue() { + return value; + } +} diff --git a/src/main/java/emu/grasscutter/game/quest/handlers/QuestBaseHandler.java b/src/main/java/emu/grasscutter/game/quest/handlers/QuestBaseHandler.java new file mode 100644 index 000000000..68bf88361 --- /dev/null +++ b/src/main/java/emu/grasscutter/game/quest/handlers/QuestBaseHandler.java @@ -0,0 +1,10 @@ +package emu.grasscutter.game.quest.handlers; + +import emu.grasscutter.data.custom.QuestConfigData.QuestCondition; +import emu.grasscutter.game.quest.GameQuest; + +public abstract class QuestBaseHandler { + + public abstract boolean execute(GameQuest quest, QuestCondition condition, int... params); + +} diff --git a/src/main/java/emu/grasscutter/server/game/GameServer.java b/src/main/java/emu/grasscutter/server/game/GameServer.java index 71e1cf856..f44aa1bc4 100644 --- a/src/main/java/emu/grasscutter/server/game/GameServer.java +++ b/src/main/java/emu/grasscutter/server/game/GameServer.java @@ -14,6 +14,8 @@ import emu.grasscutter.game.managers.ChatManager; import emu.grasscutter.game.managers.InventoryManager; import emu.grasscutter.game.managers.MultiplayerManager; import emu.grasscutter.game.player.Player; +import emu.grasscutter.game.quest.ServerQuestHandler; +import emu.grasscutter.game.quest.handlers.QuestBaseHandler; import emu.grasscutter.game.shop.ShopManager; import emu.grasscutter.game.tower.TowerScheduleManager; import emu.grasscutter.game.world.World; @@ -37,7 +39,8 @@ import static emu.grasscutter.Configuration.*; public final class GameServer extends KcpServer { private final InetSocketAddress address; private final GameServerPacketHandler packetHandler; - + private final ServerQuestHandler questHandler; + private final Map players; private final Set worlds; @@ -68,6 +71,7 @@ public final class GameServer extends KcpServer { this.setServerInitializer(new GameServerInitializer(this)); this.address = address; this.packetHandler = new GameServerPacketHandler(PacketHandler.class); + this.questHandler = new ServerQuestHandler(); this.players = new ConcurrentHashMap<>(); this.worlds = Collections.synchronizedSet(new HashSet<>()); @@ -91,6 +95,10 @@ public final class GameServer extends KcpServer { return packetHandler; } + public ServerQuestHandler getQuestHandler() { + return questHandler; + } + public Map getPlayers() { return players; } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerNpcTalkReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerNpcTalkReq.java index 309d7e2e2..515552289 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerNpcTalkReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerNpcTalkReq.java @@ -1,6 +1,7 @@ package emu.grasscutter.server.packet.recv; import emu.grasscutter.game.inventory.GameItem; +import emu.grasscutter.game.quest.enums.QuestTriggerType; import emu.grasscutter.net.packet.Opcodes; import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.proto.NpcTalkReqOuterClass.NpcTalkReq; @@ -14,6 +15,8 @@ public class HandlerNpcTalkReq extends PacketHandler { @Override public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { NpcTalkReq req = NpcTalkReq.parseFrom(payload); + + session.getPlayer().getQuestManager().triggerEvent(QuestTriggerType.QUEST_CONTENT_COMPLETE_TALK, req.getTalkId()); session.send(new PacketNpcTalkRsp(req.getNpcEntityId(), req.getTalkId(), req.getEntityId())); }