From d95a30a1a5d56b838b352c2a5b49a0fcf29d585b Mon Sep 17 00:00:00 2001
From: GanyusLeftHorn <1244229+GanyusLeftHorn@users.noreply.github.com>
Date: Sat, 2 Jul 2022 01:11:58 -0700
Subject: [PATCH] Cooking and unlocking recipies.

---
 .../game/managers/CookingManager.java         | 87 ++++++++++++++++++-
 .../game/managers/InventoryManager.java       |  5 ++
 .../packet/recv/HandlerPlayerCookArgsReq.java | 16 ++++
 .../packet/recv/HandlerPlayerCookReq.java     | 16 ++++
 .../send/PacketCookRecipeDataNotify.java      | 28 ++++++
 .../packet/send/PacketPlayerCookArgsRsp.java  | 17 ++++
 .../packet/send/PacketPlayerCookRsp.java      | 42 +++++++++
 7 files changed, 210 insertions(+), 1 deletion(-)
 create mode 100644 src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerCookArgsReq.java
 create mode 100644 src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerCookReq.java
 create mode 100644 src/main/java/emu/grasscutter/server/packet/send/PacketCookRecipeDataNotify.java
 create mode 100644 src/main/java/emu/grasscutter/server/packet/send/PacketPlayerCookArgsRsp.java
 create mode 100644 src/main/java/emu/grasscutter/server/packet/send/PacketPlayerCookRsp.java

diff --git a/src/main/java/emu/grasscutter/game/managers/CookingManager.java b/src/main/java/emu/grasscutter/game/managers/CookingManager.java
index ad5b92b2..bcc31bb3 100644
--- a/src/main/java/emu/grasscutter/game/managers/CookingManager.java
+++ b/src/main/java/emu/grasscutter/game/managers/CookingManager.java
@@ -7,11 +7,23 @@ import java.util.List;
 import java.util.Set;
 
 import emu.grasscutter.data.GameData;
+import emu.grasscutter.data.common.ItemParamData;
+import emu.grasscutter.data.excels.ItemData;
+import emu.grasscutter.game.inventory.GameItem;
 import emu.grasscutter.game.player.Player;
+import emu.grasscutter.game.props.ActionReason;
 import emu.grasscutter.net.proto.CookRecipeDataOuterClass;
+import emu.grasscutter.net.proto.PlayerCookArgsReqOuterClass.PlayerCookArgsReq;
+import emu.grasscutter.net.proto.PlayerCookReqOuterClass.PlayerCookReq;
+import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
 import emu.grasscutter.server.packet.send.PacketCookDataNotify;
+import emu.grasscutter.server.packet.send.PacketCookRecipeDataNotify;
+import emu.grasscutter.server.packet.send.PacketPlayerCookArgsRsp;
+import emu.grasscutter.server.packet.send.PacketPlayerCookRsp;
 
 public class CookingManager {
+    private static final int MANUAL_PERFECT_COOK_QUALITY = 3;
+
     private static Set<Integer> defaultUnlockedRecipies;
     private final Player player;
 
@@ -30,10 +42,83 @@ public class CookingManager {
         }
     }
 
+    /********************
+     * Unlocking for recipies.
+     ********************/
+    public synchronized boolean unlockRecipe(GameItem recipeItem) {
+		// Make sure this is actually a cooking recipe.
+		if (!recipeItem.getItemData().getItemUse().get(0).getUseOp().equals("ITEM_USE_UNLOCK_COOK_RECIPE")) {
+			return false;
+		}
+
+		// Determine the recipe we should unlock.
+		int recipeId = Integer.parseInt(recipeItem.getItemData().getItemUse().get(0).getUseParam().get(0));
+
+		// Remove the item from the player's inventory.
+		// We need to do this here, before sending CookRecipeDataNotify, or the the UI won't correctly update.
+		player.getInventory().removeItem(recipeItem, 1);
+
+		// Tell the client that this blueprint is now unlocked and add the unlocked item to the player.
+		this.player.getUnlockedRecipies().put(recipeId, 0);
+		this.player.sendPacket(new PacketCookRecipeDataNotify(recipeId));
+
+		return true;
+	}
+
     /********************
      * Perform cooking.
      ********************/
-    
+    public void handlePlayerCookReq(PlayerCookReq req) {
+        // Get info from the request.
+        int recipeId = req.getRecipeId();
+        int quality = req.getQteQuality();
+        int count = req.getCookCount();
+
+        // Get recipe data.
+        var recipeData = GameData.getCookRecipeDataMap().get(recipeId);
+        if (recipeData == null) {
+            this.player.sendPacket(new PacketPlayerCookRsp(Retcode.RET_FAIL));
+            return;
+        }
+
+        // Get proficiency for player.
+        int proficiency = this.player.getUnlockedRecipies().getOrDefault(recipeId, 0);
+
+        // Try consuming materials.
+		boolean success = player.getInventory().payItems(recipeData.getInputVec().toArray(new ItemParamData[0]), count, ActionReason.Cook);
+		if (!success) {
+			this.player.sendPacket(new PacketPlayerCookRsp(Retcode.RET_FAIL)); //ToDo: Probably the wrong return code.
+		}
+
+        // Obtain results.
+        int qualityIndex = 
+            quality == 0 
+            ? 2 
+            : quality - 1;
+
+        ItemParamData resultParam = recipeData.getQualityOutputVec().get(qualityIndex);
+        ItemData resultItemData = GameData.getItemDataMap().get(resultParam.getItemId());
+
+		GameItem cookResult = new GameItem(resultItemData, resultParam.getCount() * count);
+		this.player.getInventory().addItem(cookResult);
+
+        // Increase player proficiency, if this was a manual perfect cook.
+        if (quality == MANUAL_PERFECT_COOK_QUALITY) {
+            proficiency = Math.min(proficiency + 1, recipeData.getMaxProficiency());
+            this.player.getUnlockedRecipies().put(recipeId, proficiency);
+        }
+
+        // Send response.
+        this.player.sendPacket(new PacketPlayerCookRsp(cookResult, quality, count, recipeId, proficiency));
+    }
+
+
+    /********************
+     * Cooking arguments.
+     ********************/
+    public void handleCookArgsReq(PlayerCookArgsReq req) {
+        this.player.sendPacket(new PacketPlayerCookArgsRsp());
+    }
 
      /********************
      * Notify unlocked recipies.
diff --git a/src/main/java/emu/grasscutter/game/managers/InventoryManager.java b/src/main/java/emu/grasscutter/game/managers/InventoryManager.java
index 47fc2d8c..01224ea3 100644
--- a/src/main/java/emu/grasscutter/game/managers/InventoryManager.java
+++ b/src/main/java/emu/grasscutter/game/managers/InventoryManager.java
@@ -852,6 +852,11 @@ public class InventoryManager {
 					// Unlock.
 					useSuccess = player.getServer().getCombineManger().unlockCombineDiagram(player, useItem);
 				}
+				// Handle cooking recipies.
+				if (useItem.getItemData().getItemUse().get(0).getUseOp().equals("ITEM_USE_UNLOCK_COOK_RECIPE")) {
+					// Unlock.
+					useSuccess = player.getCookingManager().unlockRecipe(useItem);
+				}
 				break;
 			case MATERIAL_FURNITURE_FORMULA:
 			case MATERIAL_FURNITURE_SUITE_FORMULA:
diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerCookArgsReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerCookArgsReq.java
new file mode 100644
index 00000000..c55dca3b
--- /dev/null
+++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerCookArgsReq.java
@@ -0,0 +1,16 @@
+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.PlayerCookArgsReqOuterClass;
+import emu.grasscutter.server.game.GameSession;
+
+@Opcodes(PacketOpcodes.PlayerCookArgsReq)
+public class HandlerPlayerCookArgsReq extends PacketHandler {
+    @Override
+    public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
+        PlayerCookArgsReqOuterClass.PlayerCookArgsReq req = PlayerCookArgsReqOuterClass.PlayerCookArgsReq.parseFrom(payload);
+        session.getPlayer().getCookingManager().handleCookArgsReq(req);
+    }
+}
diff --git a/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerCookReq.java b/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerCookReq.java
new file mode 100644
index 00000000..46208350
--- /dev/null
+++ b/src/main/java/emu/grasscutter/server/packet/recv/HandlerPlayerCookReq.java
@@ -0,0 +1,16 @@
+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.PlayerCookReqOuterClass;
+import emu.grasscutter.server.game.GameSession;
+
+@Opcodes(PacketOpcodes.PlayerCookReq)
+public class HandlerPlayerCookReq extends PacketHandler {
+    @Override
+    public void handle(GameSession session, byte[] header, byte[] payload) throws Exception {
+        PlayerCookReqOuterClass.PlayerCookReq req = PlayerCookReqOuterClass.PlayerCookReq.parseFrom(payload);
+        session.getPlayer().getCookingManager().handlePlayerCookReq(req);
+    }
+}
diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketCookRecipeDataNotify.java b/src/main/java/emu/grasscutter/server/packet/send/PacketCookRecipeDataNotify.java
new file mode 100644
index 00000000..00762d92
--- /dev/null
+++ b/src/main/java/emu/grasscutter/server/packet/send/PacketCookRecipeDataNotify.java
@@ -0,0 +1,28 @@
+package emu.grasscutter.server.packet.send;
+
+import emu.grasscutter.net.packet.BasePacket;
+import emu.grasscutter.net.packet.PacketOpcodes;
+import emu.grasscutter.net.proto.CookRecipeDataNotifyOuterClass.CookRecipeDataNotify;
+import emu.grasscutter.net.proto.CookRecipeDataOuterClass.CookRecipeData;
+
+public class PacketCookRecipeDataNotify extends BasePacket {
+    public PacketCookRecipeDataNotify(CookRecipeData recipe) {
+        super(PacketOpcodes.CookRecipeDataNotify);
+
+        CookRecipeDataNotify proto = CookRecipeDataNotify.newBuilder()
+                .setRecipeData(recipe)
+                .build();
+        
+        this.setData(proto);
+    }
+
+    public PacketCookRecipeDataNotify(int recipeId) {
+        super(PacketOpcodes.CookRecipeDataNotify);
+
+        CookRecipeDataNotify proto = CookRecipeDataNotify.newBuilder()
+                .setRecipeData(CookRecipeData.newBuilder().setRecipeId(recipeId))
+                .build();
+        
+        this.setData(proto);
+    }
+}
diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerCookArgsRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerCookArgsRsp.java
new file mode 100644
index 00000000..bfdbe334
--- /dev/null
+++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerCookArgsRsp.java
@@ -0,0 +1,17 @@
+package emu.grasscutter.server.packet.send;
+
+import emu.grasscutter.net.packet.BasePacket;
+import emu.grasscutter.net.packet.PacketOpcodes;
+import emu.grasscutter.net.proto.PlayerCookArgsRspOuterClass.PlayerCookArgsRsp;
+
+public class PacketPlayerCookArgsRsp extends BasePacket {
+    
+    public PacketPlayerCookArgsRsp() {
+        super(PacketOpcodes.PlayerCookArgsRsp);
+
+        PlayerCookArgsRsp proto = PlayerCookArgsRsp.newBuilder()
+            .build();
+
+        this.setData(proto);
+    }
+}
diff --git a/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerCookRsp.java b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerCookRsp.java
new file mode 100644
index 00000000..517ba36e
--- /dev/null
+++ b/src/main/java/emu/grasscutter/server/packet/send/PacketPlayerCookRsp.java
@@ -0,0 +1,42 @@
+package emu.grasscutter.server.packet.send;
+
+import emu.grasscutter.game.inventory.GameItem;
+import emu.grasscutter.net.packet.BasePacket;
+import emu.grasscutter.net.packet.PacketOpcodes;
+import emu.grasscutter.net.proto.CookRecipeDataOuterClass.CookRecipeData;
+import emu.grasscutter.net.proto.ItemParamOuterClass.ItemParam;
+import emu.grasscutter.net.proto.PlayerCookRspOuterClass.PlayerCookRsp;
+import emu.grasscutter.net.proto.RetcodeOuterClass.Retcode;
+
+public class PacketPlayerCookRsp extends BasePacket {
+    public PacketPlayerCookRsp(Retcode retcode) {
+        super(PacketOpcodes.PlayerCookRsp);
+
+        PlayerCookRsp proto = PlayerCookRsp.newBuilder()
+            .setRetcode(retcode.getNumber())
+            .build();
+
+        this.setData(proto);
+    }
+
+    public PacketPlayerCookRsp(GameItem output, int quality, int count, int recipeId, int proficiency) {
+        super(PacketOpcodes.PlayerCookRsp);
+
+        PlayerCookRsp proto = PlayerCookRsp.newBuilder()
+            .setRecipeData(
+                CookRecipeData.newBuilder()
+                    .setRecipeId(recipeId)
+                    .setProficiency(proficiency)    
+            )
+            .setQteQuality(quality)
+            .addItemList(
+                ItemParam.newBuilder()
+                    .setItemId(output.getItemId())
+                    .setCount(output.getCount())
+            )
+            .setCookCount(count)
+            .build();
+
+        this.setData(proto);
+    }
+}
-- 
GitLab