diff --git a/src/main/java/emu/grasscutter/data/GameData.java b/src/main/java/emu/grasscutter/data/GameData.java index ffbda065a..cfb9859c0 100644 --- a/src/main/java/emu/grasscutter/data/GameData.java +++ b/src/main/java/emu/grasscutter/data/GameData.java @@ -93,6 +93,7 @@ public class GameData { private static final Int2ObjectMap homeWorldLevelDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap furnitureMakeConfigDataMap = new Int2ObjectOpenHashMap<>(); private static final Int2ObjectMap investigationMonsterDataMap = new Int2ObjectOpenHashMap<>(); + private static final Int2ObjectMap cityDataMap = new Int2ObjectOpenHashMap<>(); // Cache private static Map> fetters = new HashMap<>(); @@ -411,4 +412,8 @@ public class GameData { public static Int2ObjectMap getInvestigationMonsterDataMap() { return investigationMonsterDataMap; } + public static Int2ObjectMap getCityDataMap() { + return cityDataMap; + } + } diff --git a/src/main/java/emu/grasscutter/data/excels/CityData.java b/src/main/java/emu/grasscutter/data/excels/CityData.java new file mode 100644 index 000000000..8746da373 --- /dev/null +++ b/src/main/java/emu/grasscutter/data/excels/CityData.java @@ -0,0 +1,30 @@ +package emu.grasscutter.data.excels; + +import emu.grasscutter.data.GameResource; +import emu.grasscutter.data.ResourceType; +import lombok.AccessLevel; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.FieldDefaults; + +import java.util.List; + +@ResourceType(name = "CityConfigData.json", loadPriority = ResourceType.LoadPriority.HIGH) +@Getter +@Setter +@FieldDefaults(level = AccessLevel.PRIVATE) +public class CityData extends GameResource { + int cityId; + int sceneId; + List areaIdVec; + + @Override + public int getId() { + return this.cityId; + } + + @Override + public void onLoad() { + super.onLoad(); + } +} diff --git a/src/main/java/emu/grasscutter/data/excels/InvestigationMonsterData.java b/src/main/java/emu/grasscutter/data/excels/InvestigationMonsterData.java index 7567659a6..9fbf3d6f2 100644 --- a/src/main/java/emu/grasscutter/data/excels/InvestigationMonsterData.java +++ b/src/main/java/emu/grasscutter/data/excels/InvestigationMonsterData.java @@ -1,22 +1,28 @@ package emu.grasscutter.data.excels; +import emu.grasscutter.data.GameData; import emu.grasscutter.data.GameResource; import emu.grasscutter.data.ResourceType; +import lombok.AccessLevel; import lombok.Data; +import lombok.Getter; +import lombok.experimental.FieldDefaults; import java.util.List; -@ResourceType(name = "InvestigationMonsterConfigData.json") -@Data +@ResourceType(name = "InvestigationMonsterConfigData.json", loadPriority = ResourceType.LoadPriority.LOW) +@Getter +@FieldDefaults(level = AccessLevel.PRIVATE) public class InvestigationMonsterData extends GameResource { - private int id; - private int cityId; - private List monsterIdList; - private List groupIdList; - private int rewardPreviewId; - private String mapMarkCreateType; - private String monsterCategory; + int id; + int cityId; + List monsterIdList; + List groupIdList; + int rewardPreviewId; + String mapMarkCreateType; + String monsterCategory; + CityData cityData; @Override public int getId() { return this.id; @@ -24,6 +30,6 @@ public class InvestigationMonsterData extends GameResource { @Override public void onLoad() { - super.onLoad(); + this.cityData = GameData.getCityDataMap().get(cityId); } } diff --git a/src/main/java/emu/grasscutter/game/world/WorldDataManager.java b/src/main/java/emu/grasscutter/game/world/WorldDataManager.java index 11e01f0ac..dcffe5417 100644 --- a/src/main/java/emu/grasscutter/game/world/WorldDataManager.java +++ b/src/main/java/emu/grasscutter/game/world/WorldDataManager.java @@ -4,10 +4,16 @@ import com.google.gson.reflect.TypeToken; import emu.grasscutter.Grasscutter; import emu.grasscutter.data.DataLoader; import emu.grasscutter.data.GameData; +import emu.grasscutter.data.excels.InvestigationMonsterData; import emu.grasscutter.data.excels.RewardPreviewData; +import emu.grasscutter.data.excels.WorldLevelData; import emu.grasscutter.game.entity.gadget.chest.BossChestInteractHandler; import emu.grasscutter.game.entity.gadget.chest.ChestInteractHandler; import emu.grasscutter.game.entity.gadget.chest.NormalChestInteractHandler; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.net.proto.InvestigationMonsterOuterClass; +import emu.grasscutter.scripts.data.SceneGroup; +import emu.grasscutter.scripts.data.SceneMonster; import emu.grasscutter.server.game.GameServer; import java.io.InputStream; @@ -15,14 +21,19 @@ import java.io.InputStreamReader; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; public class WorldDataManager { private final GameServer gameServer; private final Map chestInteractHandlerMap; // chestType-Handler + private final Map sceneInvestigationGroupMap; // public WorldDataManager(GameServer gameServer){ this.gameServer = gameServer; this.chestInteractHandlerMap = new HashMap<>(); + this.sceneInvestigationGroupMap = new ConcurrentHashMap<>(); + loadChestConfig(); } @@ -59,4 +70,80 @@ public class WorldDataManager { } return GameData.getRewardPreviewDataMap().get(investigationMonsterData.get().getRewardPreviewId()); } + + private SceneGroup getInvestigationGroup(int sceneId, int groupId){ + var key = sceneId + "_" + groupId; + if(!sceneInvestigationGroupMap.containsKey(key)){ + var group = SceneGroup.of(groupId).load(sceneId); + sceneInvestigationGroupMap.putIfAbsent(key, group); + return group; + } + return sceneInvestigationGroupMap.get(key); + } + + public int getMonsterLevel(SceneMonster monster, World world){ + // Calculate level + int level = monster.level; + WorldLevelData worldLevelData = GameData.getWorldLevelDataMap().get(world.getWorldLevel()); + + if (worldLevelData != null) { + level = worldLevelData.getMonsterLevel(); + } + return level; + } + private InvestigationMonsterOuterClass.InvestigationMonster getInvestigationMonster(Player player, InvestigationMonsterData imd) { + var groupId = imd.getGroupIdList().get(0); + var monsterId = imd.getMonsterIdList().get(0); + var sceneId = imd.getCityData().getSceneId(); + var group = getInvestigationGroup(sceneId, groupId); + + if(group == null || group.monsters == null){ + return null; + } + + var monster = group.monsters.values().stream() + .filter(x -> x.monster_id == monsterId) + .findFirst(); + if(monster.isEmpty()){ + return null; + } + + var builder = InvestigationMonsterOuterClass.InvestigationMonster.newBuilder(); + + builder.setId(imd.getId()) + .setCityId(imd.getCityId()) + .setSceneId(imd.getCityData().getSceneId()) + .setGroupId(groupId) + .setMonsterId(monsterId) + .setLevel(getMonsterLevel(monster.get(), player.getWorld())) + .setIsAlive(true) + .setNextRefreshTime(Integer.MAX_VALUE) + .setRefreshInterval(Integer.MAX_VALUE) + .setPos(monster.get().pos.toProto()); + + if("Boss".equals(imd.getMonsterCategory())){ + var bossChest = group.searchBossChestInGroup(); + if(bossChest.isPresent()){ + builder.setResin(bossChest.get().resin); + builder.setBossChestNum(bossChest.get().take_num); + } + } + return builder.build(); + } + + public List getInvestigationMonstersByCityId(Player player, int cityId){ + var cityData = GameData.getCityDataMap().get(cityId); + if(cityData == null){ + Grasscutter.getLogger().warn("City not exist {}", cityId); + return List.of(); + } + + return GameData.getInvestigationMonsterDataMap().values() + .parallelStream() + .filter(imd -> imd.getCityId() == cityId) + .map(imd -> this.getInvestigationMonster(player, imd)) + .filter(Objects::nonNull) + .toList(); + } + } diff --git a/src/main/java/emu/grasscutter/scripts/data/SceneGroup.java b/src/main/java/emu/grasscutter/scripts/data/SceneGroup.java index 0be30342d..e670ddcb5 100644 --- a/src/main/java/emu/grasscutter/scripts/data/SceneGroup.java +++ b/src/main/java/emu/grasscutter/scripts/data/SceneGroup.java @@ -16,6 +16,7 @@ import org.luaj.vm2.LuaValue; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.stream.Collectors; import static emu.grasscutter.Configuration.SCRIPT; @@ -165,4 +166,12 @@ public class SceneGroup { Grasscutter.getLogger().info("group {} in scene {} is loaded successfully.", id, sceneId); return this; } + + public Optional searchBossChestInGroup() { + return gadgets.values().stream() + .filter(g -> g.boss_chest != null && g.boss_chest.monster_config_id > 0) + .map(g -> g.boss_chest) + .findFirst(); + } + } diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetInvestigationMonsterReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetInvestigationMonsterReq.java index e3689a094..f326731c8 100644 --- a/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetInvestigationMonsterReq.java +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerGetInvestigationMonsterReq.java @@ -14,7 +14,11 @@ public class HandlerGetInvestigationMonsterReq extends PacketHandler { public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { var req = GetInvestigationMonsterReqOuterClass.GetInvestigationMonsterReq.parseFrom(payload); - session.send(new PacketGetInvestigationMonsterRsp(req.getCityIdListList())); + session.send(new PacketGetInvestigationMonsterRsp( + session.getPlayer(), + session.getServer().getWorldDataManager(), + req.getCityIdListList())); + } } diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketGetInvestigationMonsterRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketGetInvestigationMonsterRsp.java index d6e921711..51e88a71e 100644 --- a/src/main/java/emu/grasscutter/server/packet/send/PacketGetInvestigationMonsterRsp.java +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketGetInvestigationMonsterRsp.java @@ -1,18 +1,23 @@ package emu.grasscutter.server.packet.send; +import emu.grasscutter.game.player.Player; +import emu.grasscutter.game.world.WorldDataManager; import emu.grasscutter.net.packet.BasePacket; import emu.grasscutter.net.packet.PacketOpcodes; import emu.grasscutter.net.proto.GetActivityInfoRspOuterClass; +import emu.grasscutter.net.proto.GetInvestigationMonsterRspOuterClass; import java.util.List; public class PacketGetInvestigationMonsterRsp extends BasePacket { - public PacketGetInvestigationMonsterRsp(List cityIdListList) { + public PacketGetInvestigationMonsterRsp(Player player, WorldDataManager worldDataManager, List cityIdListList) { + super(PacketOpcodes.GetInvestigationMonsterRsp); - var resp = GetActivityInfoRspOuterClass.GetActivityInfoRsp.newBuilder(); + var resp = GetInvestigationMonsterRspOuterClass.GetInvestigationMonsterRsp.newBuilder(); + cityIdListList.forEach(id -> resp.addAllMonsterList(worldDataManager.getInvestigationMonstersByCityId(player, id))); this.setData(resp.build());