610 lines
20 KiB
C#
610 lines
20 KiB
C#
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<Transform> materialSlots = new List<Transform>();
|
|
|
|
[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<TMPro.TextMeshProUGUI>();
|
|
if (tabTMP == null)
|
|
{
|
|
tabText = textTf.GetComponent<TextMeshProUGUI>();
|
|
}
|
|
}
|
|
|
|
if (tabTMP == null && tabText == null)
|
|
{
|
|
tabTMP = tabObj.GetComponentInChildren<TMPro.TextMeshProUGUI>();
|
|
if(tabTMP == null)
|
|
{
|
|
tabText = tabObj.GetComponentInChildren<TextMeshProUGUI>();
|
|
}
|
|
}
|
|
|
|
if(tabTMP != null)
|
|
tabTMP.text = pageTitle;
|
|
else if(tabText != null)
|
|
tabText.text = pageTitle;
|
|
|
|
Button btn = tabObj.GetComponent<Button>();
|
|
if(btn == null)
|
|
btn = tabObj.GetComponentInChildren<Button>();
|
|
|
|
if(btn != null)
|
|
{
|
|
int capturedIndex = pageIndex;
|
|
btn.onClick.AddListener(() => OnTabSelected(capturedIndex));
|
|
}
|
|
}
|
|
}
|
|
|
|
void OnTabSelected(int index)
|
|
{
|
|
currentTabIndex = index;
|
|
RefreshSubItemList();
|
|
}
|
|
|
|
void RefreshSubItemList()
|
|
{
|
|
ClearContainer(itemContainer);
|
|
|
|
if (!cachedMakeService.HasValue)
|
|
return;
|
|
|
|
var service = cachedMakeService.Value;
|
|
if (service.pages == null || currentTabIndex >= service.pages.Length)
|
|
return;
|
|
|
|
var page = service.pages[currentTabIndex];
|
|
if (page.id_goods == null)
|
|
return;
|
|
|
|
foreach (uint recipeId in page.id_goods)
|
|
{
|
|
if (recipeId == 0)
|
|
continue;
|
|
|
|
CreateSubItem(recipeId);
|
|
}
|
|
}
|
|
|
|
void CreateSubItem(uint recipeId)
|
|
{
|
|
if (itemPb == null || itemContainer == null)
|
|
return;
|
|
|
|
GameObject item = Instantiate(itemPb, itemContainer);
|
|
item.name = $"Recipe_{recipeId}";
|
|
item.SetActive(true);
|
|
Debug.Log("[DlgProduce] Creating produce item for recipe ID: " + item.name);
|
|
|
|
ProduceItemPanel panel = item.GetComponent<ProduceItemPanel>();
|
|
if(panel != null)
|
|
{
|
|
panel.Setup(recipeId,this);
|
|
}
|
|
else
|
|
{
|
|
Debug.LogWarning("[DlgProduce] ProduceItemPanel component not found on item prefab");
|
|
}
|
|
|
|
var trigger = item.AddComponent<UnityEngine.EventSystems.EventTrigger>();
|
|
|
|
var entryEnter = new UnityEngine.EventSystems.EventTrigger.Entry
|
|
{
|
|
eventID = UnityEngine.EventSystems.EventTriggerType.PointerEnter
|
|
};
|
|
entryEnter.callback.AddListener((_) =>
|
|
{
|
|
ShowItemInfoByRecipe(recipeId);
|
|
});
|
|
trigger.triggers.Add(entryEnter);
|
|
|
|
var entryExit = new UnityEngine.EventSystems.EventTrigger.Entry
|
|
{
|
|
eventID = UnityEngine.EventSystems.EventTriggerType.PointerExit
|
|
};
|
|
entryExit.callback.AddListener((_) =>
|
|
{
|
|
HideItemInfo();
|
|
});
|
|
trigger.triggers.Add(entryExit);
|
|
|
|
}
|
|
|
|
void ClearContainer(Transform container)
|
|
{
|
|
if (container == null)
|
|
return;
|
|
|
|
for (int i = container.childCount - 1; i >= 0; i--)
|
|
{
|
|
Destroy(container.GetChild(i).gameObject);
|
|
}
|
|
}
|
|
|
|
void ClearMaterialSlots()
|
|
{
|
|
foreach (var slot in materialSlots)
|
|
{
|
|
if (slot == null) continue;
|
|
|
|
slot.gameObject.SetActive(false);
|
|
|
|
Transform iconTf = slot.Find("item");
|
|
if (iconTf != null)
|
|
{
|
|
Image img = iconTf.GetComponent<Image>();
|
|
if (img != null)
|
|
img.sprite = null;
|
|
}
|
|
|
|
Transform qtyTf = slot.Find("text_quantity");
|
|
if (qtyTf != null)
|
|
{
|
|
TextMeshProUGUI txt = qtyTf.GetComponent<TextMeshProUGUI>();
|
|
if (txt != null)
|
|
txt.text = "";
|
|
}
|
|
}
|
|
|
|
if (itemResult != null)
|
|
{
|
|
itemResult.gameObject.SetActive(false);
|
|
|
|
Transform iconTf = itemResult.Find("item");
|
|
if (iconTf != null)
|
|
{
|
|
Image img = iconTf.GetComponent<Image>();
|
|
if (img != null)
|
|
img.sprite = null;
|
|
}
|
|
|
|
Transform qtyTf = itemResult.Find("text_quantity");
|
|
if (qtyTf != null)
|
|
{
|
|
TextMeshProUGUI txt = qtyTf.GetComponent<TextMeshProUGUI>();
|
|
if (txt != null)
|
|
txt.text = "";
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
public void ShowRecipeMaterials(uint recipeId)
|
|
{
|
|
selectedRecipeId = recipeId; // Track the selected recipe
|
|
ClearMaterialSlots();
|
|
|
|
var edm = ElementDataManProvider.GetElementDataMan();
|
|
if (edm == null)
|
|
return;
|
|
|
|
DATA_TYPE dt = DATA_TYPE.DT_INVALID;
|
|
object data = edm.get_data_ptr(recipeId, ID_SPACE.ID_SPACE_RECIPE, ref dt);
|
|
|
|
if (data is not RECIPE_ESSENCE recipe)
|
|
return;
|
|
|
|
if (itemResult != null &&
|
|
recipe.targets != null &&
|
|
recipe.targets.Length > 0 &&
|
|
recipe.targets[0].id_to_make != 0)
|
|
{
|
|
uint outputItemId = recipe.targets[0].id_to_make;
|
|
|
|
itemResult.gameObject.SetActive(true);
|
|
|
|
Transform iconTf = itemResult.Find("item");
|
|
if (iconTf != null)
|
|
{
|
|
Image img = iconTf.GetComponent<Image>();
|
|
if (img != null)
|
|
{
|
|
Sprite sp = EC_IvtrItemUtils.Instance.ResolveItemIconSprite((int)outputItemId);
|
|
if (sp != null)
|
|
{
|
|
img.sprite = sp;
|
|
img.enabled = true;
|
|
img.preserveAspect = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
Transform qtyTf = itemResult.Find("text_quantity");
|
|
if (qtyTf != null)
|
|
{
|
|
TextMeshProUGUI txt = qtyTf.GetComponent<TextMeshProUGUI>();
|
|
if (txt != null)
|
|
txt.text = "1";
|
|
}
|
|
}
|
|
|
|
if (recipe.materials == null)
|
|
return;
|
|
|
|
bool allEnough = true;
|
|
int slotIndex = 0;
|
|
foreach (var mat in recipe.materials)
|
|
{
|
|
if (slotIndex >= materialSlots.Count)
|
|
break;
|
|
|
|
if (mat.id == 0 || mat.num <= 0)
|
|
continue;
|
|
|
|
int owned = GetInventoryItemCount(mat.id);
|
|
bool enough = owned >= mat.num;
|
|
|
|
if (!enough)
|
|
allEnough = false;
|
|
|
|
Transform slot = materialSlots[slotIndex];
|
|
slot.gameObject.SetActive(true);
|
|
|
|
Transform iconTf = slot.Find("item");
|
|
if (iconTf != null)
|
|
{
|
|
Image img = iconTf.GetComponent<Image>();
|
|
if (img != null)
|
|
{
|
|
Sprite sp = EC_IvtrItemUtils.Instance.ResolveItemIconSprite((int)mat.id);
|
|
img.sprite = sp;
|
|
img.enabled = true;
|
|
img.preserveAspect = true;
|
|
img.color = enough ? COLOR_ENOUGH : COLOR_NOT_ENOUGH;
|
|
}
|
|
}
|
|
|
|
Transform qtyTf = slot.Find("text_quantity");
|
|
if (qtyTf != null)
|
|
{
|
|
var tmp = qtyTf.GetComponent<TMPro.TextMeshProUGUI>();
|
|
if (tmp != null)
|
|
tmp.text = $"{mat.num}";
|
|
else
|
|
{
|
|
TextMeshProUGUI txt = qtyTf.GetComponent<TextMeshProUGUI>();
|
|
if (txt != null)
|
|
txt.text = $"{mat.num}";
|
|
}
|
|
}
|
|
|
|
slotIndex++;
|
|
}
|
|
|
|
if (itemResult != null)
|
|
{
|
|
Transform iconTf = itemResult.Find("item");
|
|
if (iconTf != null)
|
|
{
|
|
Image img = iconTf.GetComponent<Image>();
|
|
if (img != null)
|
|
{
|
|
img.color = allEnough ? COLOR_ENOUGH : COLOR_NOT_ENOUGH;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(startProduceBtn != null)
|
|
{
|
|
startProduceBtn.interactable = allEnough;
|
|
}
|
|
}
|
|
|
|
void OnStartProduceClicked()
|
|
{
|
|
if (selectedRecipeId == 0)
|
|
{
|
|
Debug.LogWarning("[DlgProduce] No recipe selected");
|
|
return;
|
|
}
|
|
|
|
if (!cachedMakeService.HasValue)
|
|
{
|
|
Debug.LogError("[DlgProduce] No make service cached");
|
|
return;
|
|
}
|
|
|
|
// Get skill ID from the service (not from recipe)
|
|
// The second parameter is the recipe ID (not the item ID)
|
|
int idSkill = (int)cachedMakeService.Value.id_make_skill;
|
|
int idRecipe = (int)selectedRecipeId;
|
|
uint dwCount = 1; // Default count is 1 (matching C++ code for Win_Produce1)
|
|
|
|
// Send the command to the server
|
|
// Parameters: idSkill (from service), idRecipe (recipe ID), dwCount
|
|
UnityGameSession.c2s_CmdNPCSevMakeItem(idSkill, idRecipe, dwCount);
|
|
Debug.Log($"[DlgProduce] Sent make item command: skill={idSkill}, recipe={idRecipe}, count={dwCount}");
|
|
}
|
|
|
|
void OnCancelProduceClicked()
|
|
{
|
|
CloseProduce();
|
|
}
|
|
|
|
public void CloseProduce()
|
|
{
|
|
gameObject.SetActive(false);
|
|
ClearContainer(tabBtnContainer);
|
|
ClearContainer(itemContainer);
|
|
ClearMaterialSlots();
|
|
cachedMakeService = null;
|
|
currentTabIndex = 0;
|
|
selectedRecipeId = 0;
|
|
Debug.Log("[DlgProduce] Produce dialog closed");
|
|
}
|
|
|
|
// Called when production starts (NOTIFY_PRODUCE_START)
|
|
public void OnProduceStart(CSNetwork.GPDataType.cmd_produce_start cmd)
|
|
{
|
|
Debug.Log($"[DlgProduce] OnProduceStart: type={cmd.type}, use_time={cmd.use_time}, count={cmd.count}");
|
|
// TODO: Update progress bar, disable start button, etc.
|
|
// This would typically start a progress bar showing production time
|
|
if (startProduceBtn != null)
|
|
{
|
|
startProduceBtn.interactable = false;
|
|
}
|
|
}
|
|
|
|
// Called when one item is produced (NOTIFY_PRODUCE_END_ONE)
|
|
public void OnProduceOnce(CSNetwork.GPDataType.cmd_produce_once cmd)
|
|
{
|
|
Debug.Log($"[DlgProduce] OnProduceOnce: type={cmd.type}, amount={cmd.amount}, where={cmd.where}, index={cmd.index}");
|
|
// TODO: Update UI counters, progress, skill ability, etc.
|
|
// This would typically update the remaining count and progress
|
|
}
|
|
|
|
// Called when production ends (NOTIFY_PRODUCE_END)
|
|
public void OnProduceEnd()
|
|
{
|
|
Debug.Log("[DlgProduce] OnProduceEnd: Production completed");
|
|
// TODO: Re-enable start button, reset progress, etc.
|
|
if (startProduceBtn != null)
|
|
{
|
|
startProduceBtn.interactable = true;
|
|
}
|
|
}
|
|
|
|
// Called when production fails (NOTIFY_PRODUCE_NULL)
|
|
public void OnProduceNull(CSNetwork.GPDataType.cmd_produce_null cmd)
|
|
{
|
|
Debug.Log($"[DlgProduce] OnProduceNull: type={cmd.type} - Production failed");
|
|
// TODO: Show error message, re-enable start button, etc.
|
|
if (startProduceBtn != null)
|
|
{
|
|
startProduceBtn.interactable = true;
|
|
}
|
|
}
|
|
|
|
public void ShowItemInfoByRecipe(uint recipeId)
|
|
{
|
|
if (itemInfoRoot == null)
|
|
return;
|
|
|
|
var edm = ElementDataManProvider.GetElementDataMan();
|
|
if (edm == null)
|
|
return;
|
|
|
|
DATA_TYPE dt = DATA_TYPE.DT_INVALID;
|
|
object data = edm.get_data_ptr(recipeId, ID_SPACE.ID_SPACE_RECIPE, ref dt);
|
|
|
|
if (data is not RECIPE_ESSENCE recipe ||
|
|
recipe.targets == null ||
|
|
recipe.targets.Length == 0)
|
|
return;
|
|
|
|
uint itemId = recipe.targets[0].id_to_make;
|
|
if (itemId == 0)
|
|
return;
|
|
|
|
EC_IvtrItem item = null;
|
|
try
|
|
{
|
|
item = EC_IvtrItem.CreateItem((int)itemId, 0, 1);
|
|
item?.GetDetailDataFromLocal();
|
|
}
|
|
catch { }
|
|
|
|
if (item == null)
|
|
return;
|
|
|
|
itemInfoRoot.gameObject.SetActive(true);
|
|
|
|
if (infoNameText != null)
|
|
infoNameText.text = EC_Utility.ProcessColorCodes(new StringBuilder (item.GetName()));
|
|
|
|
if (infoDescText != null)
|
|
infoDescText.text = EC_Utility.ProcessColorCodes(new StringBuilder (item.GetDesc()?.Replace("\\r", "\n") ?? ""));
|
|
|
|
if (infoExtraText != null)
|
|
{
|
|
infoExtraText.text = $"Item ID: {itemId}";
|
|
}
|
|
}
|
|
|
|
public void HideItemInfo()
|
|
{
|
|
if (itemInfoRoot != null)
|
|
itemInfoRoot.gameObject.SetActive(false);
|
|
}
|
|
|
|
// Helper to get total count of an item in player's inventory
|
|
int GetInventoryItemCount(uint itemId)
|
|
{
|
|
var host = CECGameRun.Instance?.GetHostPlayer();
|
|
if (host == null)
|
|
return 0;
|
|
|
|
var inv = host.GetInventory(0); // 0 = inventory bag
|
|
if (inv == null)
|
|
return 0;
|
|
|
|
int total = 0;
|
|
int size = inv.GetSize();
|
|
|
|
for (int i = 0; i < size; i++)
|
|
{
|
|
var item = inv.GetItem(i, false);
|
|
if (item != null && item.m_tid == itemId)
|
|
{
|
|
total += item.m_iCount;
|
|
}
|
|
}
|
|
|
|
return total;
|
|
}
|
|
|
|
|
|
public void OnDestroy()
|
|
{
|
|
ClearContainer(tabBtnContainer);
|
|
ClearContainer(itemContainer);
|
|
}
|
|
}
|
|
}
|