diff --git a/Assets/PerfectWorld/Resources/UI/DialogScriptTableObject.asset b/Assets/PerfectWorld/Resources/UI/DialogScriptTableObject.asset index 415919ac8c..81e92ecbf1 100644 --- a/Assets/PerfectWorld/Resources/UI/DialogScriptTableObject.asset +++ b/Assets/PerfectWorld/Resources/UI/DialogScriptTableObject.asset @@ -27,3 +27,5 @@ MonoBehaviour: prefab: {fileID: 977375840943150650, guid: 51bad2e6d1ec69a4683135ce85288faa, type: 3} - id: Win_Hpmpxp prefab: {fileID: 6032603119232429246, guid: 8350aa55906d08448bb47e10a473ca61, type: 3} + - id: Win_Produce + prefab: {fileID: 5750242998044155948, guid: ecf0d8daf08db6f4a8d94a4bb07847ec, type: 3} diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommand.cs b/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommand.cs index e544775d8d..a295956f24 100644 --- a/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommand.cs +++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommand.cs @@ -1447,6 +1447,13 @@ namespace CSNetwork.S2CCommand public int idTask; } + public struct NPCSevMakeItemCONTENT + { + public int idSkill; + public int idItem; + public uint dwCount; + } + public struct cmd_sevnpc_serve2 { public int service_type; diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommandFactory.cs b/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommandFactory.cs index 7c2b26dbbf..0efa3a9bdd 100644 --- a/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommandFactory.cs +++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/C2SCommand/C2SCommandFactory.cs @@ -686,6 +686,25 @@ namespace CSNetwork.C2SCommand return octets; } + + public static Octets CreateNPCSevMakeItemCmd(int idSkill, int idItem, uint dwCount) + { + var cmd = new cmd_sevnpc_serve + { + service_type = NPC_service_type.GP_NPCSEV_MAKEITEM, + len = (uint)Marshal.SizeOf() + }; + + NPCSevMakeItemCONTENT content = new NPCSevMakeItemCONTENT() + { + idSkill = idSkill, + idItem = idItem, + dwCount = dwCount + }; + + return SerializeCommand(CommandID.SEVNPC_SERVE, cmd, content); + } + public static Octets CreateEmoteActionCmd(int wPose) { cmd_emote_action pCmd = new cmd_emote_action() diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/GPDataType.cs b/Assets/PerfectWorld/Scripts/Network/CSNetwork/GPDataType.cs index e9cc4bac9d..3059e57888 100644 --- a/Assets/PerfectWorld/Scripts/Network/CSNetwork/GPDataType.cs +++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/GPDataType.cs @@ -1369,6 +1369,30 @@ namespace CSNetwork.GPDataType public byte byPackage; public byte bySlot; } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct cmd_produce_start + { + public ushort use_time; + public ushort count; + public int type; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct cmd_produce_once + { + public int type; + public uint amount; + public uint slot_amount; + public byte where; // Which package: 0 standard, 2 trash, 1 equip + public byte index; // Which slot in that package + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct cmd_produce_null + { + public int type; + } [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct info_matter { diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs b/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs index 3f0e0d4fbb..c053fc48a5 100644 --- a/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs +++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs @@ -512,7 +512,7 @@ namespace CSNetwork break; case CommandID.PICKUP_ITEM: case CommandID.HOST_OBTAIN_ITEM: - // case CommandID.PRODUCE_ONCE: + case CommandID.PRODUCE_ONCE: case CommandID.TASK_DELIVER_ITEM: EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_PICKUPITEM, (int)MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader); @@ -807,6 +807,13 @@ namespace CSNetwork case CommandID.FLYSWORD_TIME: EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_FLYSWORDTIME, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader); break; + case CommandID.PRODUCE_START: + case CommandID.PRODUCE_END: + case CommandID.PRODUCE_NULL: + // Post MSG_HST_PRODUCEITEM message with command ID as parameter (matches C++ behavior) + EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_PRODUCEITEM, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader); + break; + } } @@ -1254,6 +1261,12 @@ namespace CSNetwork gamedatasend.Data = C2SCommandFactory.CreateNPCSevWaypointCmd(NPC_service_type.GP_NPCSEV_WAYPOINT, 0); SendProtocol(gamedatasend); } + public void c2s_SendCmdNPCSevMakeItem(int idSkill, int idItem, uint dwCount) + { + gamedatasend gamedatasend = new gamedatasend(); + gamedatasend.Data = C2SCommandFactory.CreateNPCSevMakeItemCmd(idSkill, idItem, dwCount); + SendProtocol(gamedatasend); + } public void GetRoleBaseInfo(int iNumRole, List aRoleIDs) { int iNumLimit = 128; diff --git a/Assets/PerfectWorld/Scripts/Network/UnityGameSession.cs b/Assets/PerfectWorld/Scripts/Network/UnityGameSession.cs index 7bfdb79240..169a54a821 100644 --- a/Assets/PerfectWorld/Scripts/Network/UnityGameSession.cs +++ b/Assets/PerfectWorld/Scripts/Network/UnityGameSession.cs @@ -292,6 +292,11 @@ namespace BrewMonster.Network Instance._gameSession.c2s_SendCmdNPCSevWaypoint(); } + public static void c2s_CmdNPCSevMakeItem(int idSkill, int idItem, uint dwCount) + { + Instance._gameSession.c2s_SendCmdNPCSevMakeItem(idSkill, idItem, dwCount); + } + public void GetFactionInfo(int iNumFaction, int[] aFactinoIDs) { m_stubbornFactionInfoSender.Add(iNumFaction, aFactinoIDs); diff --git a/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgNPC.cs b/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgNPC.cs index db0b980284..0012c24bae 100644 --- a/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgNPC.cs +++ b/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgNPC.cs @@ -763,27 +763,245 @@ namespace BrewMonster.UI else if (DataType == DATA_TYPE.DT_NPC_MAKE_SERVICE) { NPC_MAKE_SERVICE pService = (NPC_MAKE_SERVICE)pData; - m_pLst_Main.AddString(strText + Encoding.Unicode.GetString(MemoryMarshal.AsBytes(pService.name))); + string serviceName = Encoding.Unicode.GetString(MemoryMarshal.AsBytes(pService.name)); + m_pLst_Main.AddString(strText + serviceName); + + // Log NPC_MAKE_SERVICE data + BMLogger.Log($"NPC_MAKE_SERVICE detected - ServiceID: {a_uiService[i]}, MakeServiceID: {pService.id}, Name: {serviceName}, MakeSkillID: {pService.id_make_skill}, ProduceType: {pService.produce_type}"); + + // Log pages data + if (pService.pages != null) + { + for (int pageIdx = 0; pageIdx < pService.pages.Length; pageIdx++) + { + var page = pService.pages[pageIdx]; + string pageTitle = Encoding.Unicode.GetString(MemoryMarshal.AsBytes(page.page_title)); + // Trim null characters and whitespace from page title + pageTitle = pageTitle?.TrimEnd('\0', ' ', '\t', '\r', '\n') ?? ""; + + // Collect all non-zero goods IDs with their names + // Note: id_goods contains RECIPE IDs, not item IDs + List goodsInfo = new List(); + if (page.id_goods != null) + { + for (int goodsIdx = 0; goodsIdx < page.id_goods.Length; goodsIdx++) + { + uint recipeId = page.id_goods[goodsIdx]; + if (recipeId != 0) + { + // Get recipe data, output item, and materials + string outputItemInfo = ""; + List materialInfo = new List(); + try + { + var edm = ElementDataManProvider.GetElementDataMan(); + if (edm == null) + { + // ElementDataMan is null, skip + } + else + { + // Try recipe space first + DATA_TYPE dt = DATA_TYPE.DT_INVALID; + object recipeData = edm.get_data_ptr(recipeId, ID_SPACE.ID_SPACE_RECIPE, ref dt); + + // Check if we got recipe data - sometimes dt is DT_INVALID but data is still RECIPE_ESSENCE + RECIPE_ESSENCE? recipe = null; + if (recipeData != null && recipeData is RECIPE_ESSENCE) + { + recipe = (RECIPE_ESSENCE)recipeData; + } + else if (recipeData != null && dt == DATA_TYPE.DT_RECIPE_ESSENCE) + { + recipe = (RECIPE_ESSENCE)recipeData; + } + + if (recipe.HasValue) + { + RECIPE_ESSENCE recipeValue = recipe.Value; + + // Get output item from first target (main output) + if (recipeValue.targets != null && recipeValue.targets.Length > 0) + { + if (recipeValue.targets[0].id_to_make != 0) + { + uint outputItemId = recipeValue.targets[0].id_to_make; + try + { + EC_IvtrItem pItem = EC_IvtrItem.CreateItem((int)outputItemId, 0, 1); + if (pItem != null) + { + string outputName = pItem.GetName(); + outputItemInfo = $"{outputItemId} ({outputName})"; + } + else + { + outputItemInfo = $"{outputItemId} (unknown)"; + } + } + catch (Exception ex2) + { + outputItemInfo = $"{outputItemId} (error: {ex2.Message})"; + } + } + else + { + BMLogger.LogWarning($" Recipe {recipeId}: First target id_to_make is 0"); + } + } + else + { + BMLogger.LogWarning($" Recipe {recipeId}: targets is null or empty"); + } + + // Get all materials + if (recipeValue.materials != null) + { + for (int matIdx = 0; matIdx < recipeValue.materials.Length; matIdx++) + { + var material = recipeValue.materials[matIdx]; + if (material.id != 0 && material.num > 0) + { + string materialName = ""; + try + { + EC_IvtrItem matItem = EC_IvtrItem.CreateItem((int)material.id, 0, 1); + if (matItem != null) + { + materialName = matItem.GetName(); + } + } + catch (Exception ex2) + { + materialName = $"error: {ex2.Message}"; + } + + string matEntry = !string.IsNullOrEmpty(materialName) + ? $"{material.id} ({materialName}) x{material.num}" + : $"{material.id} (unknown) x{material.num}"; + materialInfo.Add(matEntry); + } + } + } + else + { + BMLogger.LogWarning($" Recipe {recipeId}: materials is null"); + } + } + } + } + catch (Exception ex) + { + BMLogger.LogWarning($" Failed to get data for recipe ID {recipeId}: {ex.Message}\n{ex.StackTrace}"); + } + + // Format: "RecipeID -> Output: ID (Name) | Materials: ID (Name) xCount, ..." + string goodsEntry = $"Recipe {recipeId}"; + if (!string.IsNullOrEmpty(outputItemInfo)) + { + goodsEntry += $" -> Output: {outputItemInfo}"; + } + else + { + goodsEntry += " -> Output: (none)"; + } + if (materialInfo.Count > 0) + { + goodsEntry += $" | Materials: {string.Join(", ", materialInfo)}"; + } + else + { + goodsEntry += " | Materials: (none)"; + } + goodsInfo.Add(goodsEntry); + } + } + } + + // Log page if it has a title or has goods + if (!string.IsNullOrEmpty(pageTitle) || goodsInfo.Count > 0) + { + string goodsList = goodsInfo.Count > 0 ? string.Join(", ", goodsInfo) : ""; + BMLogger.Log($" Page[{pageIdx}]: Title=\"{pageTitle}\", Goods=[{goodsList}], GoodsCount={goodsInfo.Count}"); + } + } + } } else if (DataType == DATA_TYPE.DT_NPC_DECOMPOSE_SERVICE) { - //NPC_DECOMPOSE_SERVICE pService = (NPC_DECOMPOSE_SERVICE)pData; - //CECHostPlayer pHost = GetHostPlayer(); - //CECSkill pSkill; - //int j = 0; - //for (j = 0; j < pHost.GetPassiveSkillNum(); j++) - //{ - // pSkill = pHost.GetPassiveSkillByIndex(j); - // if ((pSkill.GetType() == CECSkill::TYPE_LIVE || - // pSkill.GetType() == CECSkill::TYPE_PRODUCE) && - // (int)pService.id_decompose_skill == pSkill.GetSkillID()) - // { - // m_pLst_Main.AddString(strText + pService.name); - // break; - // } - //} - //if (j == pHost.GetPassiveSkillNum()) - // continue; + NPC_DECOMPOSE_SERVICE pService = (NPC_DECOMPOSE_SERVICE)pData; + string serviceName = Encoding.Unicode.GetString(MemoryMarshal.AsBytes(pService.name)); + + // Log NPC_DECOMPOSE_SERVICE data + BMLogger.Log($"NPC_DECOMPOSE_SERVICE detected - ServiceID: {a_uiService[i]}, DecomposeServiceID: {pService.id}, Name: {serviceName}, DecomposeSkillID: {pService.id_decompose_skill}"); + + CECHostPlayer hostPlayer = GetHostPlayer(); + bool hasRequiredSkill = false; + + // TODO: Implement proper skill check when GetPassiveSkillNum() and GetPassiveSkillByIndex() are available + // For now, we'll use reflection to access private GetPassiveSkillByID method or implement a workaround + // The C++ code checks if player has the required decompose skill (TYPE_LIVE or TYPE_PRODUCE) + try + { + // Try using reflection to access private GetPassiveSkillByID method + var method = typeof(CECHostPlayer).GetMethod("GetPassiveSkillByID", + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + + if (method != null) + { + var pSkill = method.Invoke(hostPlayer, new object[] { (int)pService.id_decompose_skill, false }) as CECSkill; + if (pSkill != null) + { + int skillType = pSkill.GetType(); + int skillID = pSkill.GetSkillID(); + + BMLogger.Log($" Found skill by ID (via reflection): ID={skillID}, Type={skillType}"); + + // Check if this is the required decompose skill with correct type + if ((skillType == (int)CECSkill.SkillType.TYPE_LIVE || + skillType == (int)CECSkill.SkillType.TYPE_PRODUCE) && + (int)pService.id_decompose_skill == skillID) + { + hasRequiredSkill = true; + BMLogger.Log($" Skill check PASSED - Player has required decompose skill (ID: {pService.id_decompose_skill}, Type: {skillType})"); + m_pLst_Main.AddString(strText + serviceName); + m_pLst_Main.SetItemData(m_pLst_Main.GetCount() - 1, a_uiService[i]); + m_pLst_Main.SetItemDataPtr(m_pLst_Main.GetCount() - 1, pData); + } + else + { + BMLogger.Log($" Skill found but type mismatch - Expected TYPE_LIVE or TYPE_PRODUCE, got {skillType}"); + } + } + else + { + BMLogger.Log($" Skill check FAILED - Player does not have required decompose skill (ID: {pService.id_decompose_skill})"); + } + } + else + { + BMLogger.LogWarning($" GetPassiveSkillByID method not found via reflection - skill check cannot be performed"); + // For now, show the service anyway for logging/debugging purposes + // TODO: Remove this when proper skill check is implemented + hasRequiredSkill = true; + m_pLst_Main.AddString(strText + serviceName); + m_pLst_Main.SetItemData(m_pLst_Main.GetCount() - 1, a_uiService[i]); + m_pLst_Main.SetItemDataPtr(m_pLst_Main.GetCount() - 1, pData); + } + } + catch (Exception ex) + { + BMLogger.LogError($"Error checking decompose skill: {ex.Message}"); + BMLogger.LogError($"Stack trace: {ex.StackTrace}"); + // For debugging: show service anyway + hasRequiredSkill = true; + m_pLst_Main.AddString(strText + serviceName); + m_pLst_Main.SetItemData(m_pLst_Main.GetCount() - 1, a_uiService[i]); + m_pLst_Main.SetItemDataPtr(m_pLst_Main.GetCount() - 1, pData); + } + + if (!hasRequiredSkill) + continue; } else if (DataType == DATA_TYPE.DT_NPC_IDENTIFY_SERVICE) { @@ -3024,11 +3242,22 @@ namespace BrewMonster.UI else if (DataType == DATA_TYPE.DT_NPC_MAKE_SERVICE) { NPC_MAKE_SERVICE pService = (NPC_MAKE_SERVICE)pData; + string serviceName = Encoding.Unicode.GetString(MemoryMarshal.AsBytes(pService.name)); + + // Log NPC_MAKE_SERVICE data when selected + BMLogger.Log($"SelectListItem - NPC_MAKE_SERVICE selected - ServiceID: {iService}, MakeServiceID: {pService.id}, Name: {serviceName}, MakeSkillID: {pService.id_make_skill}, ProduceType: {pService.produce_type}"); + idFunction = (int)SERVICE_TYPE.NPC_MAKE; } else if (DataType == DATA_TYPE.DT_NPC_DECOMPOSE_SERVICE) { NPC_DECOMPOSE_SERVICE pService = (NPC_DECOMPOSE_SERVICE)pData; + string serviceName = Encoding.Unicode.GetString(MemoryMarshal.AsBytes(pService.name)); + + // Log NPC_DECOMPOSE_SERVICE data when selected + BMLogger.Log($"SelectListItem - NPC_DECOMPOSE_SERVICE selected - ServiceID: {iService}, DecomposeServiceID: {pService.id}, Name: {serviceName}, DecomposeSkillID: {pService.id_decompose_skill}"); + BMLogger.Log($" Note: This decompose service is being treated as idFunction={SERVICE_TYPE.NPC_DECOMPOSE}"); + idFunction = (int)SERVICE_TYPE.NPC_DECOMPOSE; } else if (DataType == DATA_TYPE.DT_NPC_IDENTIFY_SERVICE) @@ -3215,38 +3444,106 @@ namespace BrewMonster.UI } else if (idFunction == (int)SERVICE_TYPE.NPC_MAKE) { - // C++ - //NPC_MAKE_SERVICE pMake = (NPC_MAKE_SERVICE)pData; - //if (pMake.produce_type == 2) - // pShow1 = m_pAUIManager.GetDialog("Win_Produce1"); - //else - // pShow1 = m_pAUIManager.GetDialog("Win_Produce"); - //GetGameUIMan().m_pDlgProduce = (CDlgProduce*)pShow1; - //GetHostPlayer().PrepareNPCService(iService); - //pShow1.SetDataPtr(pMake, "ptr_NPC_MAKE_SERVICE"); - //if (pMake.produce_type == 1 || - // pMake.produce_type == 3 || - // pMake.produce_type == 4 || - // pMake.produce_type == 5) - // GetGameUIMan().m_pDlgProduce.ClearMaterial(); - //pShow2 = m_pAUIManager.GetDialog("Win_Inventory"); - //GetGameUIMan().m_pDlgProduce.UpdateProduce(1, 0); + NPC_MAKE_SERVICE pMake = (NPC_MAKE_SERVICE)pData; - // NPC_MAKE_SERVICE pMake = (NPC_MAKE_SERVICE)pData; - // if (pMake.produce_type == 2) - // pShow1 = m_pAUIManager.GetDialog("Win_Produce1"); - // else - // pShow1 = m_pAUIManager.GetDialog("Win_Produce"); - // GetGameUIMan().m_pDlgProduce = (CDlgProduce*)pShow1; - // GetHostPlayer().PrepareNPCService(iService); - // pShow1.SetDataPtr(pMake, "ptr_NPC_MAKE_SERVICE"); - // if (pMake.produce_type == 1 || - // pMake.produce_type == 3 || - // pMake.produce_type == 4 || - // pMake.produce_type == 5) - // GetGameUIMan().m_pDlgProduce.ClearMaterial(); - // pShow2 = m_pAUIManager.GetDialog("Win_Inventory"); - // GetGameUIMan().m_pDlgProduce.UpdateProduce(1, 0); + BMLogger.Log($"PopupCorrespondingServiceDialog - NPC_MAKE: produce_type={pMake.produce_type}, MakeSkillID={pMake.id_make_skill}"); + + // Dialog loading commented out - Win_Produce dialog not yet implemented + // Determine which dialog to use based on produce_type + //string dialogName = (pMake.produce_type == 2) ? "Win_Produce1" : "Win_Produce"; + //pShow1 = m_pAUIManager.GetDialog(dialogName); + + //if (pShow1 == null) + //{ + // BMLogger.LogError($"NPC_MAKE: Dialog '{dialogName}' not found! Service may not work correctly."); + // // Try alternative dialog name or create placeholder + // // For now, just log the error and continue + //} + //else + //{ + // // Get or set DlgProduce reference (if it exists) + // // GetGameUIMan().m_pDlgProduce = (CDlgProduce*)pShow1; + // + // // Prepare NPC service + // try + // { + // GetHostPlayer().PrepareNPCService(iService); + // } + // catch (Exception ex) + // { + // BMLogger.LogError($"NPC_MAKE: Error calling PrepareNPCService: {ex.Message}"); + // } + // + // // Set data pointer + // try + // { + // pShow1.SetDataPtr(pMake, "ptr_NPC_MAKE_SERVICE"); + // } + // catch (Exception ex) + // { + // BMLogger.LogError($"NPC_MAKE: Error setting data pointer: {ex.Message}"); + // } + // + // // Clear material for certain produce types + // if (pMake.produce_type == 1 || + // pMake.produce_type == 3 || + // pMake.produce_type == 4 || + // pMake.produce_type == 5) + // { + // try + // { + // // GetGameUIMan().m_pDlgProduce.ClearMaterial(); + // // Note: ClearMaterial() will be called when DlgProduce is implemented + // BMLogger.Log($"NPC_MAKE: Should clear material for produce_type={pMake.produce_type}"); + // } + // catch (Exception ex) + // { + // BMLogger.LogError($"NPC_MAKE: Error clearing material: {ex.Message}"); + // } + // } + // + // // Get inventory dialog + // pShow2 = m_pAUIManager.GetDialog("Win_Inventory"); + // + // // Update produce dialog + // try + // { + // // GetGameUIMan().m_pDlgProduce.UpdateProduce(1, 0); + // // Note: UpdateProduce() will be called when DlgProduce is implemented + // BMLogger.Log($"NPC_MAKE: Should call UpdateProduce(1, 0)"); + // } + // catch (Exception ex) + // { + // BMLogger.LogError($"NPC_MAKE: Error updating produce: {ex.Message}"); + // } + //} + + uint npcID = pCurNPCEssence.HasValue ? pCurNPCEssence.Value.id : 0; + + AUIDialog dlg = m_pAUIManager.GetDialog("Win_Produce"); + DlgProduce dlgProduce = dlg as DlgProduce; + if (dlgProduce == null) + { + CECGameUIMan gameUIMan = GetGameUIMan(); + DialogScriptTableObject dialogResource = gameUIMan.GetDialogResource(); + Canvas canvas = gameUIMan.GetCanvas(); + + if(dlgProduce != null && canvas != null) + { + GameObject ob = dialogResource.GetPrefabDialog("Win_Produce"); + if (ob != null) + { + dlgProduce = GameObject.Instantiate(ob, canvas.transform).GetComponent(); + } + } + } + + if(dlgProduce != null) + { + dlgProduce.Show(true); + dlgProduce.OpenProduce(npcID); + } + } else if (idFunction == (int)SERVICE_TYPE.NPC_DECOMPOSE) { @@ -3534,10 +3831,10 @@ namespace BrewMonster.UI else { EC_IvtrItem pItem = EC_IvtrItem.CreateItem((int)pService.storage_open_item, 0, 1); - string szMsg; // cannot open storage task m_pLst_Main.ResetContent(); - //m_pTxt_Content.SetText(bTaskNPC ? pCurNPCEssence.Value.hello_msg : szMsg.Format(GetStringFromTable(984), pItem.GetName())); + //string szMsg = string.Format(GetStringFromTable(984), pItem.GetName()); + //m_pTxt_Content.SetText(bTaskNPC ? Encoding.Unicode.GetString(MemoryMarshal.AsBytes(pCurNPCEssence.Value.hello_msg)) : szMsg); //SetData(NPC_DIALOG.NPC_DIALOG_TASK_LIST, ""); } } diff --git a/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgProduce.cs b/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgProduce.cs new file mode 100644 index 0000000000..844bc44f19 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/UI/Dialogs/DlgProduce.cs @@ -0,0 +1,609 @@ +using BrewMonster.Network; +using BrewMonster.Scripts.Managers; +using BrewMonster.Scripts.Task; +using BrewMonster.UI; +using ModelRenderer.Scripts.Common; +using PerfectWorld.Scripts.Managers; +using PerfectWorld.Scripts.Task; +using System; +using System.Collections.Generic; +using UnityEngine; +using UnityEngine.UI; +using CSNetwork; +using System.Text; +using TMPro; + +namespace BrewMonster +{ + public class DlgProduce : AUIDialog + { + [Header("Produce Tabs")] + [SerializeField] private Transform tabBtnContainer; + [SerializeField] private GameObject tabBtnPb; + [SerializeField] private string tabButtonTextComponentName = "Text"; + + [Header("Produce Detail")] + [SerializeField] private Transform itemContainer; + [SerializeField] private GameObject itemPb; + + [Header("Material Slots")] + [SerializeField] private List materialSlots = new List(); + + [Header("Result Slot")] + [SerializeField] private Transform itemResult; + + [Header("Item Info Panel")] + public Transform itemInfoRoot; + public TMPro.TextMeshProUGUI infoNameText; + public TMPro.TextMeshProUGUI infoDescText; + public TMPro.TextMeshProUGUI infoExtraText; + + private NPC_MAKE_SERVICE? cachedMakeService = null; + private int currentTabIndex = 0; + private uint selectedRecipeId = 0; // Track the currently selected recipe + + static readonly Color COLOR_NOT_ENOUGH = new Color32(145, 145, 145, 255); + static readonly Color COLOR_ENOUGH = Color.white; + + [SerializeField] private Button startProduceBtn; + [SerializeField] private Button cancelProduceBtn; + + public void OpenProduce(uint npcId) + { + if (!LoadMakeService(npcId)) + { + Debug.LogError("[DlgProduce] LoadMakeService failed"); + return; + } + + selectedRecipeId = 0; // Reset selected recipe + SetupButtonHandlers(); + CreateTabs(); + OnTabSelected(0); + } + + void SetupButtonHandlers() + { + if (startProduceBtn != null) + { + startProduceBtn.onClick.RemoveAllListeners(); + startProduceBtn.onClick.AddListener(OnStartProduceClicked); + } + + if (cancelProduceBtn != null) + { + cancelProduceBtn.onClick.RemoveAllListeners(); + cancelProduceBtn.onClick.AddListener(OnCancelProduceClicked); + } + } + + bool LoadMakeService(uint npcId) + { + var edm = ElementDataManProvider.GetElementDataMan(); + if (edm == null) + return false; + + DATA_TYPE dt = DATA_TYPE.DT_INVALID; + object npcData = edm.get_data_ptr(npcId, ID_SPACE.ID_SPACE_ESSENCE, ref dt); + + if (dt != DATA_TYPE.DT_NPC_ESSENCE || npcData == null) + return false; + + NPC_ESSENCE npc = (NPC_ESSENCE)npcData; + if (npc.id_make_service == 0) + return false; + + DATA_TYPE serviceDt = DATA_TYPE.DT_INVALID; + object serviceData = edm.get_data_ptr( + npc.id_make_service, + ID_SPACE.ID_SPACE_ESSENCE, + ref serviceDt + ); + + if (serviceDt != DATA_TYPE.DT_NPC_MAKE_SERVICE || serviceData == null) + return false; + + cachedMakeService = (NPC_MAKE_SERVICE)serviceData; + return true; + } + + void CreateTabs() + { + ClearContainer(tabBtnContainer); + + if (!cachedMakeService.HasValue || cachedMakeService.Value.pages == null) + return; + + var makeService = cachedMakeService.Value; + + for (int pageIndex = 0; pageIndex < makeService.pages.Length; pageIndex++) + { + var page = makeService.pages[pageIndex]; + bool hasRecipes = false; + if (page.id_goods != null) + { + foreach (uint recipeId in page.id_goods) + { + if (recipeId != 0) + { + hasRecipes = true; + break; + } + } + } + + if (!hasRecipes) + continue; + + GameObject tabObj = Instantiate(tabBtnPb, tabBtnContainer); + tabObj.name = $"ProduceTab_Page_{pageIndex}"; + tabObj.SetActive(true); + + string pageTitle = ByteToStringUtils.UshortArrayToUnicodeString(page.page_title); + if (string.IsNullOrWhiteSpace(pageTitle)) + pageTitle = $"Page {pageIndex + 1}"; + + TMPro.TextMeshProUGUI tabTMP = null; + TextMeshProUGUI tabText = null; + + Transform textTf = tabObj.transform.Find(tabButtonTextComponentName); + if (textTf != null) + { + tabTMP = textTf.GetComponent(); + if (tabTMP == null) + { + tabText = textTf.GetComponent(); + } + } + + if (tabTMP == null && tabText == null) + { + tabTMP = tabObj.GetComponentInChildren(); + if(tabTMP == null) + { + tabText = tabObj.GetComponentInChildren(); + } + } + + if(tabTMP != null) + tabTMP.text = pageTitle; + else if(tabText != null) + tabText.text = pageTitle; + + Button btn = tabObj.GetComponent