diff --git a/src/main/java/emu/grasscutter/game/combine/CombineManger.java b/src/main/java/emu/grasscutter/game/combine/CombineManger.java index c1636da0d4a..dbe179c430d 100644 --- a/src/main/java/emu/grasscutter/game/combine/CombineManger.java +++ b/src/main/java/emu/grasscutter/game/combine/CombineManger.java @@ -1,23 +1,37 @@ package emu.grasscutter.game.combine; +import emu.grasscutter.Grasscutter; +import emu.grasscutter.data.DataLoader; import emu.grasscutter.data.GameData; import emu.grasscutter.data.common.ItemParamData; import emu.grasscutter.data.excels.CombineData; import emu.grasscutter.game.inventory.GameItem; +import emu.grasscutter.game.inventory.Inventory; import emu.grasscutter.game.inventory.ItemType; import emu.grasscutter.game.player.Player; import emu.grasscutter.game.props.ActionReason; import emu.grasscutter.net.proto.RetcodeOuterClass; +import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode; import emu.grasscutter.server.game.GameServer; import emu.grasscutter.server.packet.send.PacketCombineFormulaDataNotify; import emu.grasscutter.server.packet.send.PacketCombineRsp; +import emu.grasscutter.server.packet.send.PacketReliquaryDecomposeRsp; +import emu.grasscutter.utils.Utils; import it.unimi.dsi.fastutil.Pair; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import java.io.InputStreamReader; +import java.io.Reader; import java.util.ArrayList; +import java.util.Collection; import java.util.List; +import com.google.gson.reflect.TypeToken; + public class CombineManger { private final GameServer gameServer; + private final static Int2ObjectMap> reliquaryDecomposeData = new Int2ObjectOpenHashMap<>(); public GameServer getGameServer() { return gameServer; @@ -27,6 +41,22 @@ public CombineManger(GameServer gameServer) { this.gameServer = gameServer; } + public static void initialize() { + // Read the data we need for strongbox. + try (Reader fileReader = new InputStreamReader(DataLoader.load("ReliquaryDecompose.json"))) { + List decomposeEntries = Grasscutter.getGsonFactory().fromJson(fileReader, TypeToken.getParameterized(Collection.class, ReliquaryDecomposeEntry.class).getType()); + + for (ReliquaryDecomposeEntry entry : decomposeEntries) { + reliquaryDecomposeData.put(entry.getConfigId(), entry.getItems()); + } + + Grasscutter.getLogger().debug("Loaded {} reliquary decompose entries.", reliquaryDecomposeData.size()); + } + catch (Exception ex) { + Grasscutter.getLogger().error("Unable to load reliquary decompose data.", ex); + } + } + public boolean unlockCombineDiagram(Player player, GameItem diagramItem) { // Make sure this is actually a diagram. if (!diagramItem.getItemData().getItemUse().get(0).getUseOp().equals("ITEM_USE_UNLOCK_COMBINE")) { @@ -87,4 +117,44 @@ public CombineResult combineItem(Player player, int cid, int count){ return result; } + public synchronized void decomposeReliquaries(Player player, int configId, int count, List input) { + // Check if the configId is legal. + List possibleDrops = reliquaryDecomposeData.get(configId); + if (possibleDrops == null) { + player.sendPacket(new PacketReliquaryDecomposeRsp(Retcode.RET_RELIQUARY_DECOMPOSE_PARAM_ERROR)); + return; + } + + // Check if the number of input items matches the output count. + if (input.size() != count * 3) { + player.sendPacket(new PacketReliquaryDecomposeRsp(Retcode.RET_RELIQUARY_DECOMPOSE_PARAM_ERROR)); + return; + } + + // Check if all the input reliquaries actually are in the player's inventory. + for (long guid : input) { + if (player.getInventory().getItemByGuid(guid) == null) { + player.sendPacket(new PacketReliquaryDecomposeRsp(Retcode.RET_RELIQUARY_DECOMPOSE_PARAM_ERROR)); + return; + } + } + + // Delete the input reliquaries. + for (long guid : input) { + player.getInventory().removeItem(guid); + } + + // Generate outoput reliquaries. + List resultItems = new ArrayList<>(); + for (int i = 0; i < count; i++) { + int itemId = Utils.drawRandomListElement(possibleDrops); + GameItem newReliquary = new GameItem(itemId, 1); + + player.getInventory().addItem(newReliquary); + resultItems.add(newReliquary.getGuid()); + } + + // Send packet. + player.sendPacket(new PacketReliquaryDecomposeRsp(resultItems)); + } } diff --git a/src/main/java/emu/grasscutter/game/combine/ReliquaryDecomposeEntry.java b/src/main/java/emu/grasscutter/game/combine/ReliquaryDecomposeEntry.java new file mode 100644 index 00000000000..30aa1145e4e --- /dev/null +++ b/src/main/java/emu/grasscutter/game/combine/ReliquaryDecomposeEntry.java @@ -0,0 +1,22 @@ +package emu.grasscutter.game.combine; + +import java.util.List; + +public class ReliquaryDecomposeEntry { + private int configId; + private List items; + + public int getConfigId() { + return configId; + } + public void setConfigId(int configId) { + this.configId = configId; + } + + public List getItems() { + return items; + } + public void setItems(List items) { + this.items = items; + } +} diff --git a/src/main/java/emu/grasscutter/server/game/GameServer.java b/src/main/java/emu/grasscutter/server/game/GameServer.java index 5b5325cbfa4..7db3ae0da51 100644 --- a/src/main/java/emu/grasscutter/server/game/GameServer.java +++ b/src/main/java/emu/grasscutter/server/game/GameServer.java @@ -92,6 +92,7 @@ public GameServer(InetSocketAddress address) { EnergyManager.initialize(); StaminaManager.initialize(); CookingManager.initialize(); + CombineManger.initialize(); this.address = address; this.packetHandler = new GameServerPacketHandler(PacketHandler.class); diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerReliquaryDecomposeReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerReliquaryDecomposeReq.java new file mode 100644 index 00000000000..d348335964a --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerReliquaryDecomposeReq.java @@ -0,0 +1,16 @@ +package emu.grasscutter.server.packet.recv; + +import emu.grasscutter.net.packet.Opcodes; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.ReliquaryDecomposeReqOuterClass.ReliquaryDecomposeReq; +import emu.grasscutter.net.packet.PacketHandler; +import emu.grasscutter.server.game.GameSession; + +@Opcodes(PacketOpcodes.ReliquaryDecomposeReq) +public class HandlerReliquaryDecomposeReq extends PacketHandler { + @Override + public void handle(GameSession session, byte[] header, byte[] payload) throws Exception { + ReliquaryDecomposeReq req = ReliquaryDecomposeReq.parseFrom(payload); + session.getServer().getCombineManger().decomposeReliquaries(session.getPlayer(), req.getConfigId(), req.getTargetCount(), req.getGuidListList()); + } +} diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketReliquaryDecomposeRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketReliquaryDecomposeRsp.java new file mode 100644 index 00000000000..f114dc4f71b --- /dev/null +++ b/src/main/java/emu/grasscutter/server/packet/send/PacketReliquaryDecomposeRsp.java @@ -0,0 +1,30 @@ +package emu.grasscutter.server.packet.send; + +import java.util.List; + +import emu.grasscutter.net.packet.BasePacket; +import emu.grasscutter.net.packet.PacketOpcodes; +import emu.grasscutter.net.proto.ReliquaryDecomposeRspOuterClass.ReliquaryDecomposeRsp; +import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode; + +public class PacketReliquaryDecomposeRsp extends BasePacket { + public PacketReliquaryDecomposeRsp(Retcode retcode) { + super(PacketOpcodes.ReliquaryDecomposeRsp); + + ReliquaryDecomposeRsp proto = ReliquaryDecomposeRsp.newBuilder() + .setRetcode(retcode.getNumber()) + .build(); + + this.setData(proto); + } + + public PacketReliquaryDecomposeRsp(List output) { + super(PacketOpcodes.ReliquaryDecomposeRsp); + + ReliquaryDecomposeRsp proto = ReliquaryDecomposeRsp.newBuilder() + .addAllGuidList(output) + .build(); + + this.setData(proto); + } +} diff --git a/src/main/java/emu/grasscutter/utils/Utils.java b/src/main/java/emu/grasscutter/utils/Utils.java index fc687399434..1c86b3d1260 100644 --- a/src/main/java/emu/grasscutter/utils/Utils.java +++ b/src/main/java/emu/grasscutter/utils/Utils.java @@ -399,4 +399,13 @@ public static T drawRandomListElement(List list, List probabilit // Should never happen. return list.get(0); } + + /*** + * Draws a random element from the given list, following a uniform probability distribution. + * @param list The list from which to draw the element. + * @return A randomly drawn element from the given list. + */ + public static T drawRandomListElement(List list) { + return drawRandomListElement(list, null); + } } diff --git a/src/main/resources/defaults/data/ReliquaryDecompose.json b/src/main/resources/defaults/data/ReliquaryDecompose.json new file mode 100644 index 00000000000..dd16216101c --- /dev/null +++ b/src/main/resources/defaults/data/ReliquaryDecompose.json @@ -0,0 +1,22 @@ +[ + { + "configId": 1, + "comment": "Gladiator", + "items": [75513, 75514, 75523, 75524, 75533, 75534, 75543, 75544, 75553, 75554] + }, + { + "configId": 2, + "comment": "Wanderer's Troupe", + "items": [77513, 77514, 77523, 77524, 77533, 77534, 77543, 77544, 77553, 77554] + }, + { + "configId": 3, + "comment": "Bloodstained", + "items": [82513, 82514, 82523, 82524, 82533, 82534, 82543, 82544, 82553, 82554] + }, + { + "configId": 4, + "comment": "Noblesse", + "items": [81513, 81514, 81523, 81524, 81533, 81534, 81543, 81544, 81553, 81554] + } +] \ No newline at end of file