Add request server to produce item

This commit is contained in:
HungDK
2026-01-15 15:05:22 +07:00
parent 39079f9804
commit dd96ccd0ed
8 changed files with 269 additions and 85 deletions
@@ -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;
@@ -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>()
};
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()
@@ -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
{
@@ -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<int> aRoleIDs)
{
int iNumLimit = 128;
@@ -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);
@@ -9,28 +9,33 @@ using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using CSNetwork;
namespace BrewMonster
{
public class DlgProduce : AUIDialog
{
[Header("Produce Tabs")]
public Transform tabBtnContainer;
public GameObject tabBtnPb;
public string tabButtonTextComponentName = "Text";
[SerializeField] private Transform tabBtnContainer;
[SerializeField] private GameObject tabBtnPb;
[SerializeField] private string tabButtonTextComponentName = "Text";
[Header("Produce Detail")]
public Transform itemContainer;
public GameObject itemPb;
[SerializeField] private Transform itemContainer;
[SerializeField] private GameObject itemPb;
[Header("Material Slots")]
public List<Transform> materialSlots = new List<Transform>();
[SerializeField] private List<Transform> materialSlots = new List<Transform>();
[Header("Result Slot")]
public Transform itemResult;
[SerializeField] private Transform itemResult;
private NPC_MAKE_SERVICE? cachedMakeService = null;
private int currentTabIndex = 0;
private uint selectedRecipeId = 0; // Track the currently selected recipe
[SerializeField] private Button startProduceBtn;
[SerializeField] private Button cancelProduceBtn;
public void OpenProduce(uint npcId)
{
@@ -40,10 +45,27 @@ namespace BrewMonster
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();
@@ -261,6 +283,7 @@ namespace BrewMonster
public void ShowRecipeMaterials(uint recipeId)
{
selectedRecipeId = recipeId; // Track the selected recipe
ClearMaterialSlots();
var edm = ElementDataManProvider.GetElementDataMan();
@@ -350,6 +373,37 @@ namespace BrewMonster
}
}
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);
@@ -358,9 +412,51 @@ namespace BrewMonster
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 OnDestroy()
{
+2 -76
View File
@@ -3959,81 +3959,6 @@ MonoBehaviour:
m_OnClick:
m_PersistentCalls:
m_Calls: []
--- !u!1 &2703059059040193158
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 7991120577813580350}
- component: {fileID: 1861257096977318129}
- component: {fileID: 4248307541097962793}
m_Layer: 0
m_Name: Mask
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &7991120577813580350
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2703059059040193158}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1.6, y: 1.6, z: 1.6}
m_ConstrainProportionsScale: 1
m_Children: []
m_Father: {fileID: 8062547726938481465}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &1861257096977318129
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2703059059040193158}
m_CullTransparentMesh: 1
--- !u!114 &4248307541097962793
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 2703059059040193158}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 0, g: 0, b: 0, a: 0}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 0}
m_Type: 0
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!1 &2805715107032174497
GameObject:
m_ObjectHideFlags: 0
@@ -7242,6 +7167,8 @@ MonoBehaviour:
- {fileID: 7749576438893503211}
- {fileID: 4622013364428910847}
itemResult: {fileID: 8891250597797895463}
startProduceBtn: {fileID: 104721682242719380}
cancelProduceBtn: {fileID: 786694399164181467}
--- !u!1 &5819068069398175026
GameObject:
m_ObjectHideFlags: 0
@@ -8472,7 +8399,6 @@ RectTransform:
m_ConstrainProportionsScale: 1
m_Children:
- {fileID: 8572018612644380183}
- {fileID: 7991120577813580350}
- {fileID: 6993523375080496617}
m_Father: {fileID: 1108604418086848939}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+95 -1
View File
@@ -536,6 +536,9 @@ namespace BrewMonster
case int value when value == EC_MsgDef.MSG_HST_PICKUPITEM:
OnMsgHstPickupItem(Msg);
break;
case int value when value == EC_MsgDef.MSG_HST_PRODUCEITEM:
OnMsgHstProduceItem(Msg);
break;
case int value when value == EC_MsgDef.MSG_HST_SELTARGET:
OnMsgHstSelTarget(Msg); break;
case int value when value == EC_MsgDef.MSG_HST_ATKRESULT: OnMsgHstAttackResult(Msg); break;
@@ -2014,9 +2017,100 @@ namespace BrewMonster
}
break;
case CommandID.PRODUCE_ONCE:
{
// Parse cmd_produce_once struct data
cmd_produce_once produceCmd = GPDataTypeHelper.FromBytes<cmd_produce_once>(data);
int produceItemId = produceCmd.type;
int produceExpireDate = 0;
uint produceAmount = produceCmd.amount;
byte producePack = produceCmd.where;
byte produceSlot = produceCmd.index;
// Create new inventory item data
var produceNewItem = new EC_IvtrItem
{
Package = producePack,
Slot = produceSlot,
m_tid = produceItemId,
m_expire_date = produceExpireDate,
State = 0,
m_iCount = (int)produceAmount,
Crc = 0,
Content = null
};
// TODO: Handle other pickup item commands if necessary
// Add item to inventory
var produce_ivt = GetInventory(producePack);
if (!produce_ivt.MergeItem(produceItemId, produceExpireDate, (int)produceAmount, out var produceLastSlot, out var produceSlotNum) ||
produceLastSlot != produceSlot || produceSlotNum != (int)produceCmd.slot_amount)
{
Debug.LogWarning($"[PRODUCE_ONCE] Failed to merge item {produceItemId} to package {producePack}, slot {produceSlot}");
return;
}
produce_ivt.SetItem(produceSlot, produceNewItem);
Debug.Log($"[PRODUCE_ONCE] Successfully added produced item {produceItemId} to package {producePack}, slot {produceSlot} with count {produceAmount}");
// Trigger UI refresh if an EC_InventoryUI is present in scene
var produce_ui = GameObject.FindFirstObjectByType<EC_InventoryUI>();
if (produce_ui != null)
{
produce_ui.RefreshAll();
}
UpdateEquipSkins();
// Notify DlgProduce about successful produce (NOTIFY_PRODUCE_END_ONE)
var dlgProduce = GameObject.FindFirstObjectByType<DlgProduce>();
if (dlgProduce != null)
{
dlgProduce.OnProduceOnce(produceCmd);
}
}
break;
}
}
public void OnMsgHstProduceItem(in ECMSG Msg)
{
var data = Msg.dwParam1 as byte[];
int cmd = Convert.ToInt32(Msg.dwParam2);
// Get DlgProduce to notify
var dlgProduce = GameObject.FindFirstObjectByType<DlgProduce>();
if (dlgProduce == null)
{
Debug.LogWarning("[OnMsgHstProduceItem] DlgProduce not found");
return;
}
switch (cmd)
{
case CommandID.PRODUCE_START:
{
cmd_produce_start pCmd = GPDataTypeHelper.FromBytes<cmd_produce_start>(data);
Debug.Log($"[PRODUCE_START] type={pCmd.type}, use_time={pCmd.use_time}, count={pCmd.count}");
dlgProduce.OnProduceStart(pCmd);
}
break;
case CommandID.PRODUCE_END:
{
Debug.Log("[PRODUCE_END] Production ended");
dlgProduce.OnProduceEnd();
}
break;
case CommandID.PRODUCE_NULL:
{
cmd_produce_null pCmd = GPDataTypeHelper.FromBytes<cmd_produce_null>(data);
Debug.Log($"[PRODUCE_NULL] type={pCmd.type}");
dlgProduce.OnProduceNull(pCmd);
}
break;
default:
Debug.LogWarning($"[OnMsgHstProduceItem] Unknown command: {cmd}");
break;
}
}