diff --git a/src/main/java/emu/grasscutter/game/dungeons/DungeonSystem.java b/src/main/java/emu/grasscutter/game/dungeons/DungeonSystem.java index d3b0dcaf5..c1ae5798e 100644 --- a/src/main/java/emu/grasscutter/game/dungeons/DungeonSystem.java +++ b/src/main/java/emu/grasscutter/game/dungeons/DungeonSystem.java @@ -61,16 +61,27 @@ public class DungeonSystem extends BaseGameSystem { } } - public void getEntryInfo(Player player, int pointId) { - ScenePointEntry entry = GameData.getScenePointEntryById(player.getScene().getId(), pointId); - + /** + * Sends the entry info for the given dungeon point to the player. + * + * @param player The player to send the entry info to. + * @param pointId The dungeon point ID. + */ + public void sendEntryInfoFor(Player player, int pointId) { + var entry = GameData.getScenePointEntryById(player.getScene().getId(), pointId); if (entry == null) { - // Error + // An invalid point ID was sent. player.sendPacket(new PacketDungeonEntryInfoRsp()); return; } - player.sendPacket(new PacketDungeonEntryInfoRsp(player, entry.getPointData())); + // Check if the player has quests with dungeon IDs. + var questDungeons = player.getQuestManager().questsForDungeon(entry); + if (questDungeons.size() > 0) { + player.sendPacket(new PacketDungeonEntryInfoRsp(entry.getPointData(), questDungeons)); + } else { + player.sendPacket(new PacketDungeonEntryInfoRsp(entry.getPointData())); + } } public boolean triggerCondition( diff --git a/src/main/java/emu/grasscutter/game/quest/GameQuest.java b/src/main/java/emu/grasscutter/game/quest/GameQuest.java index 363e618d8..00877bc3c 100644 --- a/src/main/java/emu/grasscutter/game/quest/GameQuest.java +++ b/src/main/java/emu/grasscutter/game/quest/GameQuest.java @@ -21,8 +21,11 @@ import emu.grasscutter.server.packet.send.PacketDelQuestNotify; import emu.grasscutter.server.packet.send.PacketQuestListUpdateNotify; import emu.grasscutter.utils.Utils; import java.util.HashMap; +import java.util.List; import java.util.Map; import javax.script.Bindings; + +import it.unimi.dsi.fastutil.ints.IntIntImmutablePair; import lombok.Getter; import lombok.Setter; import lombok.val; @@ -281,6 +284,26 @@ public class GameQuest { return true; } + /** + * @return A list of dungeon IDs associated with the quest's 'QUEST_CONTENT_ENTER_DUNGEON' triggers. + * The first element of the pair is the dungeon ID. + * The second element of the pair is the dungeon's scene point. + */ + public List getDungeonIds() { + var conditions = this.getQuestData().getFinishCond().stream() + .filter(cond -> cond.getType() == QuestContent.QUEST_CONTENT_ENTER_DUNGEON) + .toList(); + + return conditions.stream() + .map(condition -> { + var params = condition.getParam(); + // The first parameter is the ID of the dungeon. + // The second parameter is the dungeon entry's scene point. + // ex. [1, 1] = dungeon ID 1, scene point 1 or 'KaeyaDungeon'. + return new IntIntImmutablePair(params[0], params[1]); + }).toList(); + } + public void save() { getMainQuest().save(); } diff --git a/src/main/java/emu/grasscutter/game/quest/QuestManager.java b/src/main/java/emu/grasscutter/game/quest/QuestManager.java index a9a4c8b2f..20e186ad2 100644 --- a/src/main/java/emu/grasscutter/game/quest/QuestManager.java +++ b/src/main/java/emu/grasscutter/game/quest/QuestManager.java @@ -3,6 +3,7 @@ package emu.grasscutter.game.quest; import emu.grasscutter.Grasscutter; import emu.grasscutter.data.GameData; import emu.grasscutter.data.binout.MainQuestData; +import emu.grasscutter.data.binout.ScenePointEntry; import emu.grasscutter.data.excels.QuestData; import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.game.player.BasePlayerManager; @@ -13,6 +14,7 @@ import emu.grasscutter.utils.Position; import io.netty.util.concurrent.FastThreadLocalThread; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import it.unimi.dsi.fastutil.ints.IntIntImmutablePair; import lombok.Getter; import lombok.val; @@ -443,4 +445,35 @@ public class QuestManager extends BasePlayerManager { public List getActiveMainQuests() { return getMainQuests().values().stream().filter(p -> !p.isFinished()).toList(); } + + /** + * Fetches dungeon IDs for quests which have a dungeon. + * + * @param point The associated scene point of the dungeon. + * @return A list of dungeon IDs, or an empty list if none are found. + */ + public List questsForDungeon(ScenePointEntry point) { + var pointId = point.getPointData().getId(); + // Get the active quests. + return this.getActiveMainQuests().stream() + // Get the sub-quests of the main quest. + .map(GameMainQuest::getChildQuests) + // Get the values of the sub-quests map. + .map(Map::values) + .map(quests -> quests.stream() + // Get the dungeon IDs of each quest. + .map(GameQuest::getDungeonIds) + .map(ids -> ids.stream() + // Find entry points which match this dungeon. + .filter(id -> id.rightInt() == pointId) + .toList()) + .map(ids -> ids.stream() + // Of the remaining dungeons, find the ID of the quest dungeon. + .map(IntIntImmutablePair::leftInt) + .toList()) + .flatMap(Collection::stream) + .toList()) + .flatMap(Collection::stream) + .toList(); + } } diff --git a/src/main/java/emu/grasscutter/scripts/ScriptLib.java b/src/main/java/emu/grasscutter/scripts/ScriptLib.java index 6eb0845a7..2beedd96b 100644 --- a/src/main/java/emu/grasscutter/scripts/ScriptLib.java +++ b/src/main/java/emu/grasscutter/scripts/ScriptLib.java @@ -105,11 +105,11 @@ public class ScriptLib { configId,gadgetState); GameEntity entity = getSceneScriptManager().getScene().getEntityByConfigId(configId); - if (!(entity instanceof EntityGadget)) { + if (!(entity instanceof EntityGadget gadget)) { return 1; } - ((EntityGadget) entity).updateState(gadgetState); + gadget.updateState(gadgetState); return 0; } @@ -118,10 +118,10 @@ public class ScriptLib { groupId,configId,gadgetState); val entity = getSceneScriptManager().getScene().getEntityByConfigId(configId, groupId); - if(!(entity instanceof EntityGadget)){ + if(!(entity instanceof EntityGadget gadget)){ return -1; } - ((EntityGadget) entity).updateState(gadgetState); + gadget.updateState(gadgetState); return 0; } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerDungeonEntryInfoReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerDungeonEntryInfoReq.java index 052aed0bf..91ddc6a10 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerDungeonEntryInfoReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerDungeonEntryInfoReq.java @@ -1,18 +1,18 @@ -package emu.grasscutter.server.packet.recv; - -import emu.grasscutter.net.packet.Opcodes; -import emu.grasscutter.net.packet.PacketHandler; -import emu.grasscutter.net.packet.PacketOpcodes; -import emu.grasscutter.net.proto.DungeonEntryInfoReqOuterClass.DungeonEntryInfoReq; -import emu.grasscutter.server.game.GameSession; - -@Opcodes(PacketOpcodes.DungeonEntryInfoReq) -public class HandlerDungeonEntryInfoReq extends PacketHandler { - - @Override - public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { - DungeonEntryInfoReq req = DungeonEntryInfoReq.parseFrom(payload); - - session.getServer().getDungeonSystem().getEntryInfo(session.getPlayer(), req.getPointId()); - } -} +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.DungeonEntryInfoReqOuterClass.DungeonEntryInfoReq; +import emu.grasscutter.server.game.GameSession; + +@Opcodes(PacketOpcodes.DungeonEntryInfoReq) +public class HandlerDungeonEntryInfoReq extends PacketHandler { + + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + DungeonEntryInfoReq req = DungeonEntryInfoReq.parseFrom(payload); + + session.getServer().getDungeonSystem().sendEntryInfoFor(session.getPlayer(), req.getPointId()); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonEntryInfoRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonEntryInfoRsp.java index 815b85b56..e5f1a23a6 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonEntryInfoRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketDungeonEntryInfoRsp.java @@ -1,35 +1,63 @@ -package emu.grasscutter.server.packet.send; - -import emu.grasscutter.data.common.PointData; -import emu.grasscutter.game.player.Player; -import emu.grasscutter.net.packet.BasePacket; -import emu.grasscutter.net.packet.PacketOpcodes; -import emu.grasscutter.net.proto.DungeonEntryInfoOuterClass.DungeonEntryInfo; -import emu.grasscutter.net.proto.DungeonEntryInfoRspOuterClass.DungeonEntryInfoRsp; - -public class PacketDungeonEntryInfoRsp extends BasePacket { - - public PacketDungeonEntryInfoRsp(Player player, PointData pointData) { - super(PacketOpcodes.DungeonEntryInfoRsp); - - DungeonEntryInfoRsp.Builder proto = - DungeonEntryInfoRsp.newBuilder().setPointId(pointData.getId()); - - if (pointData.getDungeonIds() != null) { - for (int dungeonId : pointData.getDungeonIds()) { - DungeonEntryInfo info = DungeonEntryInfo.newBuilder().setDungeonId(dungeonId).build(); - proto.addDungeonEntryList(info); - } - } - - this.setData(proto); - } - - public PacketDungeonEntryInfoRsp() { - super(PacketOpcodes.DungeonEntryInfoRsp); - - DungeonEntryInfoRsp proto = DungeonEntryInfoRsp.newBuilder().setRetcode(1).build(); - - this.setData(proto); - } -} +package emu.grasscutter.server.packet.send; + +import emu.grasscutter.data.common.PointData; +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.DungeonEntryInfoOuterClass.DungeonEntryInfo; +import emu.grasscutter.net.proto.DungeonEntryInfoRspOuterClass.DungeonEntryInfoRsp; + +import java.util.Arrays; +import java.util.List; + +public class PacketDungeonEntryInfoRsp extends BasePacket { + + public PacketDungeonEntryInfoRsp(PointData pointData) { + super(PacketOpcodes.DungeonEntryInfoRsp); + + DungeonEntryInfoRsp.Builder proto = + DungeonEntryInfoRsp.newBuilder().setPointId(pointData.getId()); + + if (pointData.getDungeonIds() != null) { + for (int dungeonId : pointData.getDungeonIds()) { + DungeonEntryInfo info = DungeonEntryInfo.newBuilder().setDungeonId(dungeonId).build(); + proto.addDungeonEntryList(info); + } + } + + this.setData(proto); + } + + /** + * Used in conjunction with quest-related dungeons. + * + * @param pointData The data associated with the dungeon. + * @param additional A collection of additional quest-related dungeon IDs. + */ + public PacketDungeonEntryInfoRsp(PointData pointData, List additional) { + super(PacketOpcodes.DungeonEntryInfoRsp); + + var packet = DungeonEntryInfoRsp.newBuilder() + .setPointId(pointData.getId()); + + // Add dungeon IDs from the point data. + if (pointData.getDungeonIds() != null) { + Arrays.stream(pointData.getDungeonIds()) + .forEach(id -> packet.addDungeonEntryList( + DungeonEntryInfo.newBuilder().setDungeonId(id))); + } + + // Add additional dungeon IDs. + additional.forEach(id -> packet.addDungeonEntryList( + DungeonEntryInfo.newBuilder().setDungeonId(id))); + + this.setData(packet); + } + + public PacketDungeonEntryInfoRsp() { + super(PacketOpcodes.DungeonEntryInfoRsp); + + DungeonEntryInfoRsp proto = DungeonEntryInfoRsp.newBuilder().setRetcode(1).build(); + + this.setData(proto); + } +}