Merge remote-tracking branch 'origin/develop' into feature/HP_jump

This commit is contained in:
Tungdv
2026-04-06 18:40:17 +07:00
24 changed files with 3541 additions and 195 deletions
@@ -38,7 +38,7 @@ RectTransform:
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 161.25, y: 0}
m_SizeDelta: {x: 322.5, y: 0}
m_SizeDelta: {x: 322.5, y: 300}
m_Pivot: {x: 0.5, y: 1}
--- !u!222 &9154858122360570458
CanvasRenderer:
@@ -372,6 +372,8 @@ MonoBehaviour:
m_pTxt_Desc: {fileID: 2531022629832384091}
m_pBtnFinishTaskByContribution: {fileID: 0}
m_pBtnContributionTaskHelp: {fileID: 0}
m_pToggleReceived: {fileID: 3754705535557026321}
m_pToggleNotReceived: {fileID: 6274674518893574845}
--- !u!1 &2291589776560736050
GameObject:
m_ObjectHideFlags: 0
@@ -449,7 +451,7 @@ MonoBehaviour:
m_PressedTrigger: Pressed
m_SelectedTrigger: Selected
m_DisabledTrigger: Disabled
m_Interactable: 0
m_Interactable: 1
m_TargetGraphic: {fileID: 8307833885626771741}
toggleTransition: 1
graphic: {fileID: 1446671815119800730}
+14 -11
View File
@@ -2741,19 +2741,28 @@ MonoBehaviour:
m_ItemText: {fileID: 2918232694486320309}
m_ItemImage: {fileID: 0}
m_Value: 0
m_MultiSelect: 1
m_MultiSelect: 0
m_Options:
m_Options:
- m_Text: All
m_Image: {fileID: 0}
m_Color: {r: 0, g: 0, b: 0, a: 0}
- m_Text: KK
m_Image: {fileID: 0}
m_Color: {r: 0, g: 0, b: 0, a: 0}
- m_Text: PS
m_Image: {fileID: 0}
m_Color: {r: 0, g: 0, b: 0, a: 0}
- m_Text: Th.T
- m_Text: VS
m_Image: {fileID: 0}
m_Color: {r: 0, g: 0, b: 0, a: 0}
- m_Text: TT
- m_Text: TiT
m_Image: {fileID: 0}
m_Color: {r: 0, g: 0, b: 0, a: 0}
- m_Text: ThT
m_Image: {fileID: 0}
m_Color: {r: 0, g: 0, b: 0, a: 0}
- m_Text: TK
m_Image: {fileID: 0}
m_Color: {r: 0, g: 0, b: 0, a: 0}
- m_Text: VM
@@ -2762,19 +2771,13 @@ MonoBehaviour:
- m_Text: VL
m_Image: {fileID: 0}
m_Color: {r: 0, g: 0, b: 0, a: 0}
- m_Text: TK
m_Image: {fileID: 0}
m_Color: {r: 0, g: 0, b: 0, a: 0}
- m_Text: VS
m_Image: {fileID: 0}
m_Color: {r: 0, g: 0, b: 0, a: 0}
- m_Text: KL
m_Image: {fileID: 0}
m_Color: {r: 0, g: 0, b: 0, a: 0}
- m_Text: ML
m_Image: {fileID: 0}
m_Color: {r: 0, g: 0, b: 0, a: 0}
- m_Text: DA
- m_Text: Da
m_Image: {fileID: 0}
m_Color: {r: 0, g: 0, b: 0, a: 0}
- m_Text: NT
@@ -8555,7 +8558,7 @@ MonoBehaviour:
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_text: Nothing
m_text: All
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 369c2e14814cc9a4b8e3ad4e37769134, type: 2}
m_sharedMaterial: {fileID: 9092487103257209053, guid: 369c2e14814cc9a4b8e3ad4e37769134, type: 2}
@@ -39,6 +39,19 @@ namespace BrewMonster.Scripts.Managers
[SerializeField] private Button equipButton;
[SerializeField] private Button dropButton;
[Header("Stack Split UI (assign in Inspector)")]
[SerializeField] private GameObject splitPanelRoot;
[SerializeField] private TMPro.TMP_InputField splitAmountText;
[SerializeField] private Button splitConfirmButton;
[SerializeField] private Button splitCloseButton;
[SerializeField] private Button splitOpenButton;
[SerializeField] private Button splitIncreaseButton;
[SerializeField] private Button splitDecreaseButton;
[SerializeField] private Button splitMaxButton;
private int _splitAmount = 1;
private int _splitMaxAmount = 1;
[Header("Inventory Settings")]
[SerializeField] private bool autoRefresh = true;
[SerializeField] private float refreshInterval = 1.0f;
@@ -112,6 +125,7 @@ namespace BrewMonster.Scripts.Managers
model = new InventoryModel();
view = new InventoryView();
WireBagTabButtons();
WireSplitUI();
//if (currentDragImage == null)
//{
@@ -135,6 +149,7 @@ namespace BrewMonster.Scripts.Managers
{
ShowDetailPanel(false);
}
ShowSplitPanel(false);
// Apply any pending currency values captured before the UI became active
ApplyPendingCurrency();
}
@@ -145,9 +160,125 @@ namespace BrewMonster.Scripts.Managers
ApplyPendingCurrency();
UpdateCharacterInfo();
ShowDetailPanel(false);
ShowSplitPanel(false);
RefreshAll();
}
private void WireSplitUI()
{
if (splitOpenButton != null)
{
splitOpenButton.onClick.RemoveAllListeners();
splitOpenButton.onClick.AddListener(OpenSplitPanelForSelection);
}
if (splitConfirmButton != null)
{
splitConfirmButton.onClick.RemoveAllListeners();
splitConfirmButton.onClick.AddListener(ConfirmSplit);
}
if (splitCloseButton != null)
{
splitCloseButton.onClick.RemoveAllListeners();
splitCloseButton.onClick.AddListener(() => ShowSplitPanel(false));
}
if (splitIncreaseButton != null)
{
splitIncreaseButton.onClick.RemoveAllListeners();
splitIncreaseButton.onClick.AddListener(() => SetSplitAmount(_splitAmount + 1));
}
if (splitDecreaseButton != null)
{
splitDecreaseButton.onClick.RemoveAllListeners();
splitDecreaseButton.onClick.AddListener(() => SetSplitAmount(_splitAmount - 1));
}
if (splitMaxButton != null)
{
splitMaxButton.onClick.RemoveAllListeners();
splitMaxButton.onClick.AddListener(() => SetSplitAmount(_splitMaxAmount));
}
if (splitAmountText != null)
{
splitAmountText.onValueChanged.RemoveAllListeners();
splitAmountText.onValueChanged.AddListener(OnSplitAmountInputChanged);
}
}
private void ShowSplitPanel(bool show)
{
if (splitPanelRoot != null)
splitPanelRoot.SetActive(show);
}
/// <summary>
/// Call this from your UI (or bind it to a button) to open the split panel for current selection.
/// </summary>
public void OpenSplitPanelForSelection()
{
if (currentSelectedItem == null || currentSelectedPackage != PKG_INVENTORY)
{
Debug.LogWarning("[InventoryUI] OpenSplitPanelForSelection: select an inventory stack first");
return;
}
int total = currentSelectedItem.m_iCount;
if (total <= 1)
{
Debug.LogWarning("[InventoryUI] OpenSplitPanelForSelection: item count <= 1");
return;
}
_splitMaxAmount = Math.Max(1, total - 1);
_splitAmount = Mathf.Clamp(_splitAmount, 1, _splitMaxAmount);
UpdateSplitAmountUI();
ShowSplitPanel(true);
}
private void SetSplitAmount(int amount)
{
_splitAmount = Mathf.Clamp(amount, 1, Math.Max(1, _splitMaxAmount));
UpdateSplitAmountUI();
}
private void UpdateSplitAmountUI()
{
if (splitAmountText != null)
splitAmountText.SetTextWithoutNotify(_splitAmount.ToString());
if (splitIncreaseButton != null)
splitIncreaseButton.interactable = _splitAmount < _splitMaxAmount;
if (splitDecreaseButton != null)
splitDecreaseButton.interactable = _splitAmount > 1;
if (splitMaxButton != null)
splitMaxButton.interactable = _splitAmount < _splitMaxAmount;
}
private void OnSplitAmountInputChanged(string raw)
{
if (!int.TryParse(raw, out int v))
return;
v = Mathf.Clamp(v, 1, Math.Max(1, _splitMaxAmount));
if (v == _splitAmount)
return;
_splitAmount = v;
UpdateSplitAmountUI();
}
private void ConfirmSplit()
{
int amount = _splitAmount;
if (amount <= 0) return;
if (SeparateSelectedStack(amount))
{
ShowSplitPanel(false);
}
}
private void WireBagTabButtons()
{
if (tabItemButton != null)
@@ -596,6 +727,69 @@ namespace BrewMonster.Scripts.Managers
RefreshAll();
}
/// <summary>
/// Separate part of the currently selected stack into a new empty slot (C2S MOVE_IVTR_ITEM with partial count).
/// Your UI can call this directly once you've picked the split amount.
/// </summary>
public bool SeparateSelectedStack(int splitAmount, int preferredEmptySlot = -1)
{
if (currentSelectedItem == null)
{
Debug.LogWarning("[InventoryUI] SeparateSelectedStack: no item selected");
return false;
}
// This client-side move command is for the main inventory pack (IVTRTYPE_PACK) like the original client.
// Task pack / other packs may need different commands depending on server implementation.
if (currentSelectedPackage != PKG_INVENTORY)
{
Debug.LogWarning($"[InventoryUI] SeparateSelectedStack: unsupported package {currentSelectedPackage} (only PKG_INVENTORY supported)");
return false;
}
int total = currentSelectedItem.m_iCount;
if (total <= 1)
{
Debug.LogWarning("[InventoryUI] SeparateSelectedStack: item count <= 1");
return false;
}
if (splitAmount <= 0 || splitAmount >= total)
{
Debug.LogWarning($"[InventoryUI] SeparateSelectedStack: invalid splitAmount={splitAmount}, total={total}");
return false;
}
int emptySlot = preferredEmptySlot >= 0 ? preferredEmptySlot : FindEmptySlotInPackage(PKG_INVENTORY);
if (emptySlot < 0)
{
Debug.LogWarning("[InventoryUI] SeparateSelectedStack: no empty slot available");
return false;
}
// Send MOVE_IVTR_ITEM(src, dest, count). When dest is empty and count < stack, server will split.
UnityGameSession.RequestMoveIvtrItem((byte)currentSelectedSlot, (byte)emptySlot, (uint)splitAmount);
// UI will update when S2C item operation arrives; this is a best-effort immediate refresh for responsiveness.
RefreshAll();
return true;
}
private int FindEmptySlotInPackage(byte package)
{
var host = CECGameRun.Instance?.GetHostPlayer();
var inv = host?.GetInventory(package);
if (inv == null) return -1;
int size = inv.GetSize();
for (int i = 0; i < size; i++)
{
if (inv.GetItem(i, false) == null)
return i;
}
return -1;
}
private int FindEmptyInventorySlot()
{
@@ -1128,18 +1322,18 @@ namespace BrewMonster.Scripts.Managers
//if item is @EC_IvtrEquip and is not equipped, show equip button
if(item is EC_IvtrEquip)
{
tmpText.text = "Equip";
tmpText.text = "Trang bị";
equipButton.gameObject.SetActive(true);
}
else
{
tmpText.text = "Use";
tmpText.text = "Sử dụng";
equipButton.gameObject.SetActive(true);
}
}
else if (package == PKG_EQUIPMENT)
{
tmpText.text = "UnEquip";
tmpText.text = "Tháo";
equipButton.gameObject.SetActive(true);
}
else
@@ -1154,18 +1348,18 @@ namespace BrewMonster.Scripts.Managers
{
if(item is EC_IvtrEquip)
{
buttonText.text = "Equip";
buttonText.text = "Trang bị";
equipButton.gameObject.SetActive(true);
}
else
{
buttonText.text = "Use";
buttonText.text = "Sử dụng";
equipButton.gameObject.SetActive(true);
}
}
else if (package == PKG_EQUIPMENT)
{
buttonText.text = "UnEquip";
buttonText.text = "Tháo";
equipButton.gameObject.SetActive(true);
}
else
@@ -1192,7 +1386,7 @@ namespace BrewMonster.Scripts.Managers
{
if (package == PKG_INVENTORY || package == PKG_EQUIPMENT)
{
tmpText.text = "Drop";
tmpText.text = "Vứt";
dropButton.gameObject.SetActive(true);
}
else
@@ -1205,7 +1399,7 @@ namespace BrewMonster.Scripts.Managers
{
if (package == PKG_INVENTORY || package == PKG_EQUIPMENT)
{
buttonText.text = "Drop";
buttonText.text = "Vứt";
dropButton.gameObject.SetActive(true);
}
else
@@ -321,52 +321,6 @@ namespace CSNetwork
return sb.ToString();
}
/// <summary>
/// Chuyển đổi định dạng printf (C-style: %s, %d) sang string.Format (C#-style: {0}, {1})
/// </summary>
public static string ConvertPrintfToCSharpFormat(string format)
{
if (string.IsNullOrEmpty(format)) return "";
StringBuilder sb = new StringBuilder();
int argIndex = 0;
for (int i = 0; i < format.Length; i++)
{
if (format[i] == '%' && i + 1 < format.Length)
{
char next = format[i + 1];
if (next == '%') // Trường hợp %% -> %
{
sb.Append('%');
i++;
}
else
{
sb.Append('{').Append(argIndex++).Append('}');
i++;
// Nhảy qua các ký tự định dạng (ví dụ: %02d, %ls, %f)
while (i < format.Length && (char.IsDigit(format[i]) || format[i] == '.' || format[i] == 'l' || format[i] == 'u' || format[i] == 'd' || format[i] == 's' || format[i] == 'f' || format[i] == 'x'))
{
// Nếu gặp ký tự kết thúc định dạng (s, d, f, ...) thì dừng lại sau ký tự đó
char c = format[i];
if (c == 's' || c == 'd' || c == 'f' || c == 'u' || c == 'x' || c == 'g')
{
// i++; // Đã ở đúng vị trí để vòng lặp cha thực hiện i++ tiếp theo
break;
}
i++;
}
}
}
else
{
sb.Append(format[i]);
}
}
return sb.ToString();
}
/// <summary>
/// [Port] CECGameUIMan::FilterInvalidTags (EC_GameUIMan.cpp:6424)
/// Lọc bỏ các tag đặc biệt không hợp lệ trong nội dung chat nhận từ server.
@@ -418,6 +418,27 @@ namespace CSNetwork.C2SCommand
return SerializeCommand(CommandID.DROP_EQUIP_ITEM, cmd);
}
public static Octets CreateExchangeIvtrItem(byte index1, byte index2)
{
var cmd = new CMD_ExchangeInventoryItem
{
index1 = index1,
index2 = index2
};
return SerializeCommand(CommandID.EXG_IVTR_ITEM, cmd);
}
public static Octets CreateMoveIvtrItem(byte src, byte dest, uint count)
{
var cmd = new CMD_MoveInventoryItem
{
src = src,
dest = dest,
count = count
};
return SerializeCommand(CommandID.MOVE_IVTR_ITEM, cmd);
}
public static Octets CreatePickupItem(int idItem, int tid)
{
var cmd = new CMD_Pickup
@@ -440,6 +440,20 @@ namespace CSNetwork
SendProtocol(gamedatasendRequest);
}
public void RequestExchangeIvtrItem(byte index1, byte index2)
{
gamedatasend gamedatasendRequest = new gamedatasend();
gamedatasendRequest.Data = C2SCommandFactory.CreateExchangeIvtrItem(index1, index2);
SendProtocol(gamedatasendRequest);
}
public void RequestMoveIvtrItem(byte src, byte dest, uint count)
{
gamedatasend gamedatasendRequest = new gamedatasend();
gamedatasendRequest.Data = C2SCommandFactory.CreateMoveIvtrItem(src, dest, count);
SendProtocol(gamedatasendRequest);
}
public void RequestPickupItem(int idItem, int tid)
{
gamedatasend gamedatasendRequest = new gamedatasend();
@@ -1948,7 +1962,8 @@ namespace CSNetwork
string szName = pHost.GetName();
char[] szText = new char[80];
AUICommon.AUI_ConvertChatString(ref szName, ref szText, false);
string fmt = AUICommon.ConvertPrintfToCSharpFormat(pStrTab.GetWideString((int)FixedMsg.FIXMSG_CHAT));
Debug.Log($"[Cuong] {szText}");
string fmt = AUIDialog.FormatPrintf(pStrTab.GetWideString((int)FixedMsg.FIXMSG_CHAT));
string str;
try {
str = string.Format(fmt, szName, szMsg);
@@ -2043,8 +2058,8 @@ namespace CSNetwork
string strMsg = Encoding.Unicode.GetString(p.Msg.ToArray());
string strSrcName = Encoding.Unicode.GetString(p.Name.ToArray());
string fmt = AUICommon.ConvertPrintfToCSharpFormat(pStrTab.GetWideString((int)FixedMsg.FIXMSG_CHAT));
Debug.Log($"[Cuong] {strMsg}");
string fmt = AUIDialog.FormatPrintf(pStrTab.GetWideString((int)FixedMsg.FIXMSG_CHAT));
string formatted;
try {
formatted = string.Format(fmt, strSrcName, strMsg);
@@ -2194,13 +2209,14 @@ namespace CSNetwork
{
char[] szText = new char[80];
AUICommon.AUI_ConvertChatString(ref szName,ref szText, false);
string fmt = AUICommon.ConvertPrintfToCSharpFormat(pStrTab.GetWideString((int)FixedMsg.FIXMSG_CHAT));
Debug.Log($"[Cuong] {szText}");
string fmt = AUIDialog.FormatPrintf(pStrTab.GetWideString((int)FixedMsg.FIXMSG_CHAT), szText);
string str = string.Format(
fmt,
szName,
szMsg
);
Debug.Log($"[Cuong] {str} {fmt}");
// [Port] Gọi AddChatMessage để hiển thị lên UI Chat Box.
// AddChatMessage bên trong đã tự publish ChatMessageEvent (cho ChatPanelUI)
// VÀ EventChatMessageOnTopPlayer (cho Head Bubble) nếu channel thuộc nhóm
@@ -2217,8 +2233,7 @@ namespace CSNetwork
if (pNPC != null)
{
string str;
string template = AUICommon.ConvertPrintfToCSharpFormat(pStrTab.GetWideString((int)FixedMsg.FIXMSG_CHAT2));
string template = AUIDialog.FormatPrintf(pStrTab.GetWideString((int)FixedMsg.FIXMSG_CHAT2));
string message = string.Format(
template,
@@ -2261,7 +2276,7 @@ namespace CSNetwork
if (p.Channel == 0 /* CHANNEL_NORMAL */ || p.Channel == 1 /* CHANNEL_NORMALRE */)
{
// Format: "[Name] whispers to [You]: [Message]"
string fmt = AUICommon.ConvertPrintfToCSharpFormat(pStrTab.GetWideString((int)FixedMsg.FIXMSG_PRIVATECHAT1));
string fmt = AUIDialog.FormatPrintf(pStrTab.GetWideString((int)FixedMsg.FIXMSG_PRIVATECHAT1));
BMLogger.Log($"[Cuong] OnPrtcPrivateChat {fmt}");
string formatted;
try {
@@ -443,6 +443,16 @@ namespace BrewMonster.Network
{
Instance._gameSession.RequestDropIvtrItem(index, amount);
}
public static void RequestExchangeIvtrItem(byte index1, byte index2)
{
Instance._gameSession.RequestExchangeIvtrItem(index1, index2);
}
public static void RequestMoveIvtrItem(byte src, byte dest, uint count)
{
Instance._gameSession.RequestMoveIvtrItem(src, dest, count);
}
public static void LoadConfigData()
{
Instance._gameSession.LoadConfigData();
@@ -1,6 +1,7 @@
using System.Collections.Generic;
using BrewMonster.Scripts.Managers;
using CSNetwork.Protocols.RPCData;
using Cysharp.Threading.Tasks;
using UnityEngine;
namespace BrewMonster.Scripts
@@ -42,7 +43,10 @@ namespace BrewMonster.Scripts
for (int i = 0; i < roleInfos.Count; i++)
{
RoleInfo role = roleInfos[i];
GameObject model = await NPCManager.Instance.GetModelPlayer(role.occupation, role.gender);
GameObject model = await LoadPlayerModel(role);
if (model == null)
continue;
if (version != _loadVersion)
{
@@ -104,5 +108,202 @@ namespace BrewMonster.Scripts
playerModels.Clear();
playerModelIds.Clear();
}
private async UniTask<GameObject> LoadPlayerModel(RoleInfo role)
{
var elemendataman = BrewMonster.ElementDataManProvider.GetElementDataMan();
GameObject prefab = await NPCManager.Instance.GetModelPlayer(role.occupation, role.gender);
if (prefab == null)
{
return null;
}
GameObject model = Instantiate(prefab);
var playerDefaultEquipments = model.GetComponentInChildren<PlayerDefaultEquipments>();
if (playerDefaultEquipments == null)
{
return null;
}
DATA_TYPE DataType = default;
bool useDefaultUpper = true;
bool useDefaultLower = true;
bool useDefaultWrist = true;
bool useDefaultFoot = true;
GRoleInventory equipment;
for(int i = 0; i < role.equipment.Count; i++)
{
equipment = role.equipment[i];
var equipData = elemendataman.get_data_ptr((uint)equipment.id, ID_SPACE.ID_SPACE_ESSENCE, ref DataType);
switch (DataType)
{
case DATA_TYPE.DT_WEAPON_ESSENCE:
var weaponData = (WEAPON_ESSENCE)equipData;
string fileModelRight = AFile.NormalizePath(weaponData.FileModelRight, true).ToLower();
string fileModelLeft = AFile.NormalizePath(weaponData.FileModelLeft, true).ToLower();
GameObject weaponPrefab = null;
if (!string.IsNullOrEmpty(fileModelRight))
{
weaponPrefab = await AddressableManager.Instance.LoadPrefabAsync(fileModelRight);
var weaponObject = Instantiate(weaponPrefab);
if (weaponObject != null)
{
weaponObject.transform.SetParent(FindChildObjectRecursive(model.transform, CECPlayer._hh_right_hand_weapon).transform);
weaponObject.transform.localPosition = weaponPrefab.transform.localPosition;
weaponObject.transform.localRotation = weaponPrefab.transform.localRotation;
weaponObject.transform.localScale = Vector3.one;
weaponObject.SetActive(true);
}
}
if (!string.IsNullOrEmpty(fileModelLeft))
{
weaponPrefab = await AddressableManager.Instance.LoadPrefabAsync(fileModelLeft);
var weaponObject = Instantiate(weaponPrefab);
if (weaponObject != null)
{
weaponObject.transform.SetParent(FindChildObjectRecursive(model.transform, CECPlayer._hh_left_hand_weapon).transform);
weaponObject.transform.localPosition = weaponPrefab.transform.localPosition;
weaponObject.transform.localRotation = weaponPrefab.transform.localRotation;
weaponObject.transform.localScale = Vector3.one;
weaponObject.SetActive(true);
}
}
break;
case DATA_TYPE.DT_ARMOR_ESSENCE:
var pArmor = (ARMOR_ESSENCE)equipData;
var nLocation = pArmor.equip_location;
// BMLogger.Log($"ShowEquipments():: Armor Essence: {pArmor.RealName}");
var armorSkinPath = CECPlayer._GenEquipmentSkinPath(role.occupation, role.gender, pArmor.RealName);
if (!armorSkinPath.EndsWith(".ecm"))
{
armorSkinPath += ".ecm";
}
var armorPrefab = await AddressableManager.Instance.LoadPrefabAsync(armorSkinPath);
if (armorPrefab != null)
{
var armorObject = Instantiate(armorPrefab);
armorObject.transform.SetParent(GetSkeletonBuilder(model)?.transform);
armorObject.transform.localPosition = Vector3.zero;
armorObject.transform.localRotation = Quaternion.identity;
armorObject.transform.localScale = Vector3.one;
var skinnedMeshRenderer = armorObject.GetComponent<SkinnedMeshRenderer>();
var skinnedMeshRenderereFromDataList = armorObject.GetComponentsInChildren<SkinnedMeshRenderFromData>();
foreach (var skinnedMeshRenderereFromData in skinnedMeshRenderereFromDataList)
{
if (skinnedMeshRenderereFromData != null)
{
skinnedMeshRenderereFromData._skinnedMeshRenderer.bones = GetSkeletonBuilder(model).GetBones(skinnedMeshRenderereFromData.BoneNames);
skinnedMeshRenderereFromData._skinnedMeshRenderer.rootBone = skinnedMeshRenderereFromData._skinnedMeshRenderer.bones[^1];
}
}
// disable/enable the default equipment
switch (nLocation)
{
case (uint)CECPlayer.SkinIndex.SKIN_UPPER_BODY_INDEX:
useDefaultUpper = false;
break;
case (uint)CECPlayer.SkinIndex.SKIN_LOWER_INDEX:
useDefaultLower = false;
break;
case (uint)CECPlayer.SkinIndex.SKIN_WRIST_INDEX:
useDefaultWrist = false;
break;
case (uint)CECPlayer.SkinIndex.SKIN_FOOT_INDEX:
useDefaultFoot = false;
break;
}
}
break;
//TODO: Handle Wings later
// case DATA_TYPE.DT_WINGMANWING_ESSENCE:
// m_wingType = enumWingType.WINGTYPE_WING;
// //ChangeWing(pResult, static_cast<const WINGMANWING_ESSENCE*>(pEquip)->file_model);
// var pWingData = (WINGMANWING_ESSENCE)equipData;
// //string path1 = "models/players/通用装备/翅膀/天鹅之翼/天鹅之翼_Test.ecm";
// //var pWingPrefab = await AddressableManager.Instance.LoadPrefabAsync(path1);
// var pWingPrefab = await AddressableManager.Instance.LoadPrefabAsync(pWingData.FileModel.ToLower().Replace('\\', '/'));
// //Transform parentWing = FindChildRecursive(_pPlayerModel.transform, "HH_chibang");
// if(pWingPrefab != null)
// {
// var pflySwordObject = Instantiate(pWingPrefab).transform;
// pflySwordObject.parent = m_pPlayerCECModel.m_skeletonBuilder.GetHook("HH_chibang").transform;
// pflySwordObject.localPosition = Vector3.zero;
// pflySwordObject.localRotation = Quaternion.identity;
// pflySwordObject.localScale = Vector3.one;
// m_Wing = pflySwordObject.transform;
// m_Wing.gameObject.SetActive(false);
// }
// BMLogger.Log($"ShowEquipments():: Wingman Wing Essence: {pWingData.id} {pWingData.Name} -- {pWingData.FileModel}");
// break;
default:
break;
switch (equipment.pos)
{
case InventoryConst.EQUIPIVTR_BODY:
useDefaultUpper = false;
break;
case InventoryConst.EQUIPIVTR_LEG:
useDefaultLower = false;
break;
case InventoryConst.EQUIPIVTR_WRIST:
useDefaultWrist = false;
break;
case InventoryConst.EQUIPIVTR_FOOT:
useDefaultFoot = false;
break;
}
}
}
playerDefaultEquipments.DefaultUpper.SetActive(useDefaultUpper);
playerDefaultEquipments.DefaultLower.SetActive(useDefaultLower);
playerDefaultEquipments.DefaultWirst.SetActive(useDefaultWrist);
playerDefaultEquipments.DefaultFoot.SetActive(useDefaultFoot);
return model;
}
private GameObject FindChildObjectRecursive(Transform parent, string name)
{
foreach (Transform child in parent)
{
if (child.name == name)
{
return child.gameObject;
}
var childObject = FindChildObjectRecursive(child, name);
if (childObject != null)
{
return childObject;
}
}
return null;
}
private SkeletonBuilder GetSkeletonBuilder(GameObject characterModel)
{
if (characterModel == null)
{
return null;
}
SkeletonBuilder skeletonBuilder = null;
if (skeletonBuilder == null)
{
skeletonBuilder = characterModel.GetComponentInChildren<SkeletonBuilder>();
}
return skeletonBuilder;
}
}
}
@@ -587,6 +587,14 @@ namespace BrewMonster.Scripts.Task
pTempl.ClearValidCount();
// OnServerNotify method signature may need adjustment for C# (ref/out parameters)
pTempl.OnServerNotify(pTask, pEntry, pNotify, sz, pBuf);
// TASK_SVR_NOTIFY_COMPLETE (reason 2): re-expand trace for this quest line (subtask chain advance).
// TASK_SVR_NOTIFY_COMPLETEreason=2):子任务推进后重新挂接追踪链。
if (pNotify.reason == TaskTemplConstants.TASK_SVR_NOTIFY_COMPLETE)
{
var gameUi = EC_Game.GetGameRun()?.GetUIManager()?.GetInGameUIMan() as CECGameUIMan;
gameUi?.RetraceTaskAfterServerNotifyComplete(pNotify.task);
}
}
// Helper method to get task template manager
+163 -41
View File
@@ -122,6 +122,7 @@ namespace BrewMonster.Scripts.Task.UI
// private:
private static List<int> m_vecTasksUnFinish = new List<int>();
private static List<int> m_vecTasksCanFinish = new List<int>();
private static List<int> m_vecTasksCanGet = new List<int>();
// [中文] 目标坐标集合
// [English] Target coordinates collection
@@ -378,6 +379,7 @@ namespace BrewMonster.Scripts.Task.UI
m_pBtn_SearchQuest.interactable = true;
m_pBtn_HaveQuest.interactable = false;
UpdateTask();
RefreshVecTasksCanGet();
}
public void OnCommand_showtrace(string szCommand,bool state) {
m_bShowTrace = state;
@@ -401,6 +403,7 @@ namespace BrewMonster.Scripts.Task.UI
vec_task.Remove((int)idTask);
}
}
Debug.Log($"[DlgTask] TraceTask: idTask={idTask}, task name={pTask.GetTaskTemplMan().GetTaskTemplByID((uint)idTask).GetName()}");
//get the position of idTask in pLst
int position = pTask.GetFirstSubTaskPosition(idTask);
while(position != -1)
@@ -410,6 +413,60 @@ namespace BrewMonster.Scripts.Task.UI
}
ShowTraceDialog();
}
/// <summary>
/// Add task to trace lists if traceable (no toggle). Used when seeding trace after TASK_DATA init; mirrors TraceTask minus remove/toggle.
/// 若可追踪则加入追踪列表(无切换)。在 TASK_DATA 初始化后填充追踪时用;逻辑对齐 TraceTask 但不移除/切换。
/// </summary>
private void AddTaskToTraceListsNonToggle(uint idTask)
{
if (IsPQTaskOrSubTask((int)idTask))
return;
CECTaskInterface pTask = GetHostPlayer()?.GetTaskInterface();
if (pTask == null)
return;
bool bFinishedTask = pTask.CanFinishTask(idTask);
List<int> vec_task = bFinishedTask ? m_vecTasksCanFinish : m_vecTasksUnFinish;
if (IsTaskTraceable(idTask))
{
if (!vec_task.Contains((int)idTask))
vec_task.Add((int)idTask);
}
int position = pTask.GetFirstSubTaskPosition(idTask);
while (position != -1)
{
int idSub = pTask.GetNextSub(ref position);
AddTaskToTraceListsNonToggle((uint)idSub);
}
}
/// <summary>
/// After server task pack and templates are loaded (CECTaskInterface.Init finished), apply trace like USER_LAYOUT from server:
/// SyncTrace with a full dwTraceMask for the first 32 top-level slots, then add any further top-level tasks (mask is only 32 bits).
/// 服务端任务包与模板加载完成后调用:用满掩码对前 32 个顶层槽位 SyncTrace,再为更多顶层任务补充追踪(掩码仅 32 位)。
/// </summary>
public void SyncTraceAfterTaskDataInit(CECTaskInterface pTask)
{
if (pTask == null || GetHostPlayer()?.GetTaskInterface() != pTask)
return;
var ul = new USER_LAYOUT { bTraceAll = true };
int n = (int)pTask.GetTaskCount();
if (n > 0)
{
int bits = Mathf.Min(n, 32);
ul.dwTraceMask = bits >= 32 ? uint.MaxValue : ((1u << bits) - 1u);
}
SyncTrace(ul, true);
for (int i = 32; i < n; i++)
AddTaskToTraceListsNonToggle(pTask.GetTaskId((uint)i));
RefreshVecTasksCanGet(pTask);
RefreshTaskTrace(ul.bTraceAll);
}
public void ShowTraceDialog()
{
Debug.Log($"[DlgTask] ShowTraceDialog: m_bShowTrace={m_bShowTrace}");
@@ -422,6 +479,28 @@ namespace BrewMonster.Scripts.Task.UI
EventBus.Publish(new UIEvent(UIEventType.HideTrace));
}
}
/// <summary>
/// TASK_SVR_NOTIFY_COMPLETE (reason 2): re-expand the quest trace from the top task (add-only, same subchain walk as TraceTask).
/// Using TraceTask() here would toggle entries off/on incorrectly; this re-seeds the active subtask chain after server advance.
/// 服务端 reason=2 任务推进/完成后,从顶层任务重新挂接追踪子链(与 TraceTask 同 walk,但只添加不切换)。
/// </summary>
public void RetraceAfterTaskServerNotifyComplete(uint notifyTaskId, bool bShowTrace)
{
var pIface = GetHostPlayer()?.GetTaskInterface();
if (pIface == null)
return;
ATaskTemplMan pMan = EC_Game.GetTaskTemplateMan();
ATaskTempl t = pMan?.GetTaskTemplByID(notifyTaskId);
if (t == null)
return;
uint rootId = t.GetTopTask().GetID();
if (rootId == 0)
return;
if (pIface.HasTask(rootId))
AddTaskToTraceListsNonToggle(rootId);
RefreshTaskTrace(bShowTrace);
}
public void OnCommand_focus(string szCommand="") {
var pTree = m_pTv_Quest;
var pItem = pTree?.GetSelectedItem();
@@ -506,6 +585,7 @@ namespace BrewMonster.Scripts.Task.UI
int taskIdInt = (int)topTaskId;
m_vecTasksCanFinish.Remove(taskIdInt);
m_vecTasksUnFinish.Remove(taskIdInt);
RefreshVecTasksCanGet();
// Send notification to server to abandon the currently selected task
TaskClient._notify_svr(pTask, (byte)ClientNotificationConstants.TASK_CLT_NOTIFY_CHECK_GIVEUP, (ushort)topTaskId);
@@ -788,48 +868,62 @@ namespace BrewMonster.Scripts.Task.UI
{
CECHostPlayer host = GetHostPlayer();
CECTaskInterface pTask = host.GetTaskInterface();
if( m_vecTasksCanFinish.Count > 0 )
{
for(int i = 0; i < m_vecTasksCanFinish.Count; i++ )
{
int idTask = m_vecTasksCanFinish[i];
bool bCanFinish = pTask.CanFinishTask((uint)idTask);
bool bHasTask = pTask.HasTask((uint)idTask);
if(!bHasTask || !bCanFinish)
{
// 任务未完成,状态变化要删除
m_vecTasksCanFinish.RemoveAt(i);
i--;
if (bHasTask && !bCanFinish) {
if (!m_vecTasksUnFinish.Contains(idTask))
m_vecTasksUnFinish.Add(idTask);
}
}
}
}
if( m_vecTasksUnFinish.Count > 0 )
{
for(int i = 0; i < m_vecTasksUnFinish.Count; i++ )
{
int idTask = m_vecTasksUnFinish[i];
bool bCanFinish = pTask.CanFinishTask((uint)idTask);
bool bHasTask = pTask.HasTask((uint)idTask);
if(!bHasTask || bCanFinish)
{
// ûˣ״̬仯ҪƳ
m_vecTasksUnFinish.RemoveAt(i);
i--;
if (bHasTask && bCanFinish) {
if (!m_vecTasksCanFinish.Contains(idTask))
m_vecTasksCanFinish.Add(idTask);
}
}
}
}
ShowType2 showType2 = pDlgTaskTrace.GetShowType2();
List<int> tasks = new List<int>();
tasks.Capacity = m_vecTasksCanFinish.Count + m_vecTasksUnFinish.Count;
tasks.AddRange(m_vecTasksCanFinish);
tasks.AddRange(m_vecTasksUnFinish);
if(showType2 == ShowType2.ST_RECIEVED)
{
if( m_vecTasksCanFinish.Count > 0 )
{
for(int i = 0; i < m_vecTasksCanFinish.Count; i++ )
{
int idTask = m_vecTasksCanFinish[i];
bool bCanFinish = pTask.CanFinishTask((uint)idTask);
bool bHasTask = pTask.HasTask((uint)idTask);
if(!bHasTask || !bCanFinish)
{
// 任务未完成,状态变化要删除
m_vecTasksCanFinish.RemoveAt(i);
i--;
if (bHasTask && !bCanFinish) {
if (!m_vecTasksUnFinish.Contains(idTask))
m_vecTasksUnFinish.Add(idTask);
}
}
}
}
if( m_vecTasksUnFinish.Count > 0 )
{
for(int i = 0; i < m_vecTasksUnFinish.Count; i++ )
{
int idTask = m_vecTasksUnFinish[i];
bool bCanFinish = pTask.CanFinishTask((uint)idTask);
bool bHasTask = pTask.HasTask((uint)idTask);
if(!bHasTask || bCanFinish)
{
// ûˣ״̬仯ҪƳ
m_vecTasksUnFinish.RemoveAt(i);
i--;
if (bHasTask && bCanFinish) {
if (!m_vecTasksCanFinish.Contains(idTask))
m_vecTasksCanFinish.Add(idTask);
}
}
}
}
tasks.Capacity = m_vecTasksCanFinish.Count + m_vecTasksUnFinish.Count;
tasks.AddRange(m_vecTasksCanFinish);
tasks.AddRange(m_vecTasksUnFinish);
}
else if(showType2 == ShowType2.ST_NOT_RECIEVED)
{
// Re-run GetAvailableTasks every trace refresh; m_vecTasksCanGet was only filled on quest UI events and stayed stale.
// 每次追踪刷新重新计算可接任务;原先仅在任务界面事件里更新列表,游戏中会一直停留在开局数据。
RefreshVecTasksCanGet(pTask);
tasks.Capacity = m_vecTasksCanGet.Count;
tasks.AddRange(m_vecTasksCanGet);
}
List<ATaskTempl> titlle_list = new List<ATaskTempl>();
List<int> titletask_list = new List<int>();
//Dictionary<int, int> title_map = new Dictionary<int, int>();
@@ -1129,6 +1223,11 @@ namespace BrewMonster.Scripts.Task.UI
// TaskTemplLst ttl;
List<ATaskTempl> ttl = new List<ATaskTempl>(); // TaskTemplLst -> List<ATaskTempl>
pMan.GetAvailableTasks(pTask, ttl);
// Mirror available-to-accept IDs for trace / other systems (same set as tree nodes).
// 与可接任务树节点一致,填充可接任务 ID 列表供追踪等系统使用。
m_vecTasksCanGet.Clear();
for (int j = 0; j < ttl.Count; j++)
m_vecTasksCanGet.Add((int)ttl[j].GetID());
if( ttl.Count <= 0 ) return true;
for(int i = 0; i < ttl.Count; i++ )
@@ -1198,6 +1297,11 @@ namespace BrewMonster.Scripts.Task.UI
}
}
}
// Search view already rebuilt m_vecTasksCanGet inside SearchForTask(-1).
// 搜索页在 SearchForTask 内已更新 m_vecTasksCanGet。
if (m_iType == 0)
RefreshVecTasksCanGet();
return result;
}
@@ -1227,6 +1331,7 @@ namespace BrewMonster.Scripts.Task.UI
// store m_bShowTrace flag instead of bTraceAll flag
m_bShowTrace = pul.bTraceAll;
RefreshVecTasksCanGet();
}
else
{
@@ -1303,6 +1408,23 @@ namespace BrewMonster.Scripts.Task.UI
#region PRIVATE METHODS
/// <summary>
/// Rebuild m_vecTasksCanGet from ATaskTemplMan.GetAvailableTasks (same source as Search quest tree).
/// 用 GetAvailableTasks 重建可接任务 ID 列表,与可接任务搜索树数据源一致。
/// </summary>
private void RefreshVecTasksCanGet(CECTaskInterface pTaskIfKnown = null)
{
m_vecTasksCanGet.Clear();
CECTaskInterface pTask = pTaskIfKnown ?? GetHostPlayer()?.GetTaskInterface();
if (pTask == null) return;
ATaskTemplMan pMan = EC_Game.GetTaskTemplateMan();
if (pMan == null) return;
List<ATaskTempl> ttl = new List<ATaskTempl>();
pMan.GetAvailableTasks(pTask, ttl);
for (int i = 0; i < ttl.Count; i++)
m_vecTasksCanGet.Add((int)ttl[i].GetID());
}
private bool OnInitDialog()
{
// m_pTxt_QuestNO = (PAUILABEL)GetDlgItem("Txt_QuestNO");
@@ -20,6 +20,13 @@ namespace BrewMonster.Scripts.Task.UI
ST_TITLE, // ʾѽӳƺ
ST_CONTRIBUTION, // ʾѵĹ׶
}
//New show type in Unity version
public enum ShowType2
{
ST_NONE,
ST_RECIEVED,
ST_NOT_RECIEVED,
}
public class DlgTaskTrace : DlgNameLink
{
static string COLOR_YELLOW = "<color=#ffcb4a>";
@@ -34,8 +41,11 @@ namespace BrewMonster.Scripts.Task.UI
// [SerializeField] protected Button m_pBtnChat;
[SerializeField] protected Button m_pBtnFinishTaskByContribution;
[SerializeField] protected Button m_pBtnContributionTaskHelp;
[SerializeField] protected Toggle m_pToggleReceived;
[SerializeField] protected Toggle m_pToggleNotReceived;
protected int m_nLastTracedTasks; // ʾݶӦ׷ijЩбл
protected ShowType m_nLastShowType; // ʾݶӦб
protected ShowType2 m_nLastShowType2;
protected int m_nTraceWorldID; // ʾϢ׷ϢӦĵͼID
protected int m_iContributionCurrentPage;
protected int m_iContributionTotalPage;
@@ -69,13 +79,29 @@ namespace BrewMonster.Scripts.Task.UI
}
public override void Show(bool value)
{
// RefreshTaskTrace calls Show(true) every tick while visible; do not reset filter or re-wire toggles then.
// RefreshTaskTrace 每帧对已显示窗口调用 Show(true);勿重置“已接/可接”筛选,也勿重复注册 Toggle。
bool wasActive = gameObject.activeSelf;
gameObject.SetActive(value);
m_bShow = value;
if (value && !wasActive)
m_nLastShowType2 = ShowType2.ST_RECIEVED;
// Remove-before-Add in OnShowDialogue keeps this safe when RefreshTaskTrace calls Show(true) every frame.
OnShowDialogue();
}
public override void OnShowDialogue()
{
base.OnShowDialogue();
if (m_pToggleReceived != null && m_pToggleNotReceived != null)
{
m_pToggleReceived.onValueChanged.RemoveListener(OnToggleReceivedValueChanged);
m_pToggleNotReceived.onValueChanged.RemoveListener(OnToggleNotReceivedValueChanged);
if (m_bShow)
{
m_pToggleReceived.onValueChanged.AddListener(OnToggleReceivedValueChanged);
m_pToggleNotReceived.onValueChanged.AddListener(OnToggleNotReceivedValueChanged);
}
}
//m_pTxt_Desc = dynamic_cast<PAUITEXTAREA>(GetDlgItem("Txt_Link_Trace"));
//if(m_pTxt_Desc) m_pTxt_Desc->SetForceRenderScroll(false);
//m_pChk_Collapse = dynamic_cast<PAUICHECKBOX>(GetDlgItem("Chk_Collapse"));
@@ -96,6 +122,20 @@ namespace BrewMonster.Scripts.Task.UI
// );
// ׷ٽʾĬб
}
public void OnToggleReceivedValueChanged(bool value)
{
if(value)
{
m_nLastShowType2 = ShowType2.ST_RECIEVED;
}
}
public void OnToggleNotReceivedValueChanged(bool value)
{
if(value)
{
m_nLastShowType2 = ShowType2.ST_NOT_RECIEVED;
}
}
public void OnShowDialog()
{
GameObject pObjRadio = transform.Find("Rdo_Quest3").gameObject;
@@ -316,6 +356,10 @@ namespace BrewMonster.Scripts.Task.UI
{
return m_nLastShowType;
}
public ShowType2 GetShowType2()
{
return m_nLastShowType2;
}
void PrepareRebuildTaskTrace(){
m_Buffer = new StringRef();
m_Buffer.Value = "";
@@ -541,6 +585,10 @@ namespace BrewMonster.Scripts.Task.UI
var pTempl = pTaskMan.GetTaskTemplByID((uint)id);
var pEntry = pActiveLst.GetEntry((uint)id);
if (pTempl != null) {
// 任务名与描述都为空时不显示(数据缺失或损坏) / Hide when both name and description are empty (missing or corrupt data)
if (string.IsNullOrWhiteSpace(pTempl.GetName()) && string.IsNullOrWhiteSpace(pTempl.GetDescription()))
return;
int contribution = 0;
// ׶ȵֻڶʾֵ
if (hasContributionAward) {
@@ -635,17 +683,15 @@ namespace BrewMonster.Scripts.Task.UI
if (withName)
{
AppendText(strIndentIn);
string strNum = index.ToString();
strNum = string.Format("{0}", index);
string strName = pTemp.GetName();
DeleteColorStr(strName);
strName = strNum + " " + strName;
strName = " " + strName;
TaskNameHoverCommand cmd = new TaskNameHoverCommand(m_Buffer, strName, idTask, tsi.m_ulErrCode != 0, bCanContributionFinish);
BindLinkCommand(m_pTxt_Desc.tmp, strName, cmd);
if (contribution != 0) {
int colorIndex = contribution > 0 ? 11288 : 11291;
strNum = string.Format(" {0} {1}", GetStringFromTable(colorIndex), contribution);
AppendText(strNum);
strName = string.Format(" {0} {1}", GetStringFromTable(colorIndex), contribution);
AppendText(strName);
}
AppendText("\n");
}
@@ -58,7 +58,7 @@ namespace BrewMonster.UI
#endif
}
private void Start()
private new void Start()
{
BindButtonListeners();
}
@@ -109,10 +109,22 @@ namespace BrewMonster.UI
private void BindProfDropdown()
{
if (m_pDropdown_Prof != null) m_pDropdown_Prof.interactable = false;
if (m_pDropdown_Prof == null) return;
m_pDropdown_Prof.onValueChanged.RemoveListener(OnProfessionDropdownChanged);
m_pDropdown_Prof.onValueChanged.AddListener(OnProfessionDropdownChanged);
}
private void UnbindProfDropdown() { }
private void UnbindProfDropdown()
{
if (m_pDropdown_Prof == null) return;
m_pDropdown_Prof.onValueChanged.RemoveListener(OnProfessionDropdownChanged);
}
private void OnProfessionDropdownChanged(int _)
{
UpdateProfDropdownCaption();
UpdateTeam(true);
}
private void CloseProfFilterPanel() { }
@@ -150,14 +162,34 @@ namespace BrewMonster.UI
private void EnsureProfDropdownHasCaption()
{
if (m_pDropdown_Prof == null) return;
if (m_pDropdown_Prof.options.Count == 0)
m_pDropdown_Prof.AddOptions(new List<string> { "Profession" });
if (m_pDropdown_Prof.options.Count != 0) return;
var opts = new List<string>();
opts.Add("All");
var gameRun = EC_Game.GetGameRun();
for (int i = 0; i < (int)Profession.NUM_PROFESSION; i++)
{
string name = gameRun?.GetProfName(i);
opts.Add(string.IsNullOrEmpty(name) ? $"Prof {i}" : name);
}
m_pDropdown_Prof.AddOptions(opts);
}
/// <summary>Selected profession filter: null = show all.</summary>
private HashSet<int> GetSelectedProfessionSet()
{
return null;
if (m_pDropdown_Prof == null) return null;
// Dropdown mapping:
// 0 = All (no filter)
// 1..NUM_PROFESSION = specific profession (value-1)
int v = m_pDropdown_Prof.value;
if (v <= 0) return null;
int prof = v - 1;
if (prof < 0 || prof >= (int)Profession.NUM_PROFESSION) return null;
return new HashSet<int> { prof };
}
/// <summary>Refresh teammate list, optional nearby list, and button states.</summary>
@@ -193,6 +193,18 @@ namespace BrewMonster.UI
return m_pDlgTask.UpdateQuestView();
}
}
/// <summary>
/// Call after CECTaskInterface.Init completes (TASK_DATA). Restores/applies quest trace via DlgTask.SyncTrace and refreshes minimion.
/// 在 CECTaskInterface.InitTASK_DATA)完成后调用,通过 DlgTask.SyncTrace 应用任务追踪并刷新小窗。
/// </summary>
public void OnHostTaskDataInitialized(CECTaskInterface pTask)
{
if (m_pDlgTask == null || pTask == null)
return;
m_pDlgTask.SyncTraceAfterTaskDataInit(pTask);
}
public DialogScriptTableObject GetDialogResource()
{
return m_dialogResouce;
@@ -406,6 +418,18 @@ namespace BrewMonster.UI
}
}
/// <summary>
/// TASK_SVR_NOTIFY_COMPLETE (reason 2): refresh quest trace after server advances subtasks.
/// 服务端任务完成/推进通知后刷新任务追踪。
/// </summary>
public void RetraceTaskAfterServerNotifyComplete(ushort taskId)
{
if (m_pDlgTask == null)
return;
bool showTrace = IsDialogShow("Win_QuestMinion");
m_pDlgTask.RetraceAfterTaskServerNotifyComplete((uint)taskId, showTrace);
}
public void ShowErrorMsg(string pszMsg, string pszName)
{
@@ -29,6 +29,7 @@ public class NPCShopUIManager : AUIDialog
[Header("Texts")]
public TextMeshProUGUI itemDetailNameText;
public TextMeshProUGUI itemMoneyText;
public TextOutlet itemDescriptionText;
public TextMeshProUGUI itemsBuyAmountText;
public TextMeshProUGUI itemsBuyTotalMoneyText;
@@ -391,47 +392,45 @@ public class NPCShopUIManager : AUIDialog
// Initialize buy array with at least one option
shopItem.buy = new GShopBuyOption[4]; // GShopItem supports up to 4 buy options
// Get item name and price based on type
string itemName = "Unknown";
int shopPrice = 0;
switch (itemDataType)
// Resolve display name/price in a type-agnostic way.
// Some element types come back as DATA_TYPE values we don't special-case here (arrows, flyswords, fashion, etc).
string itemName = EC_IvtrItemUtils.Instance.ResolveItemName(unchecked((int)good.id));
if (string.IsNullOrWhiteSpace(itemName))
itemName = $"Item_{good.id}";
static int ExtractInt(object data, params string[] fieldOrPropNames)
{
case DATA_TYPE.DT_WEAPON_ESSENCE:
var weaponEssence = (WEAPON_ESSENCE)itemData;
itemName = weaponEssence.Name;
shopPrice = weaponEssence.shop_price;
break;
case DATA_TYPE.DT_ARMOR_ESSENCE:
var armorEssence = (ARMOR_ESSENCE)itemData;
itemName = armorEssence.Name;
shopPrice = armorEssence.shop_price;
break;
case DATA_TYPE.DT_MEDICINE_ESSENCE:
var medicineEssence = (MEDICINE_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(medicineEssence.name);
shopPrice = medicineEssence.shop_price;
break;
case DATA_TYPE.DT_DECORATION_ESSENCE:
var decorationEssence = (DECORATION_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(decorationEssence.name);
shopPrice = decorationEssence.shop_price;
break;
case DATA_TYPE.DT_STONE_ESSENCE:
var stoneEssence = (STONE_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(stoneEssence.name);
shopPrice = stoneEssence.shop_price;
break;
case DATA_TYPE.DT_MATERIAL_ESSENCE:
var materialEssence = (MATERIAL_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(materialEssence.name);
shopPrice = materialEssence.shop_price;
break;
default:
itemName = $"Item_{good.id}";
break;
if (data == null || fieldOrPropNames == null || fieldOrPropNames.Length == 0)
return 0;
var t = data.GetType();
for (int i = 0; i < fieldOrPropNames.Length; i++)
{
string name = fieldOrPropNames[i];
if (string.IsNullOrEmpty(name))
continue;
var field = t.GetField(name, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.IgnoreCase);
if (field != null && field.FieldType == typeof(int))
{
try { return (int)field.GetValue(data); } catch { }
}
var prop = t.GetProperty(name, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.IgnoreCase);
if (prop != null && prop.PropertyType == typeof(int) && prop.CanRead)
{
try { return (int)prop.GetValue(data); } catch { }
}
}
return 0;
}
// Prefer shop_price; fall back to base price if that's all we have.
int shopPrice = ExtractInt(itemData, "shop_price");
if (shopPrice <= 0)
shopPrice = ExtractInt(itemData, "price");
shopItem.name = itemName;
// Set price from contribution cost or shop price
@@ -582,6 +581,15 @@ public class NPCShopUIManager : AUIDialog
itemDetailNameText.text = currentItem.name;
}
if (itemMoneyText != null)
{
uint money = GetCurrentUnitPrice();
if(money > 0)
itemMoneyText.text = $"Giá {money.ToString()} Ngân lượng";
else
itemMoneyText.text = BuyUiEmptyValue;
}
buyCount = BuyCountMin;
UpdateBuyPriceTexts();
@@ -1145,7 +1153,9 @@ public class NPCShopUIManager : AUIDialog
}
if (tmp != null)
{
tmp.text = EC_Utility.FormatForTextMeshPro(value ?? string.Empty);
string formatTextDeail = EC_Utility.FormatForTextMeshPro(value ?? string.Empty);
formatTextDeail.Replace("<color=#FFFFFF>", "<color=#FFD05D>");
tmp.text = formatTextDeail;
}
}
}
+9 -6
View File
@@ -1416,14 +1416,14 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 0, g: 0, b: 0, a: 0.39215687}
m_Color: {r: 0, g: 0, b: 0, a: 1}
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: 10907, guid: 0000000000000000f000000000000000, type: 0}
m_Sprite: {fileID: 21300000, guid: ef3296c82b3934a24b288ac6309eb7a7, type: 3}
m_Type: 1
m_PreserveAspect: 0
m_FillCenter: 1
@@ -2337,6 +2337,8 @@ MonoBehaviour:
- {fileID: 9087947546155311648}
equipmentPackButtons: []
fashionPackButtons: []
tabItemButton: {fileID: 0}
tabTaskButton: {fileID: 0}
detailPanelRoot: {fileID: 1203170638860719746}
detailPanelOffset: {x: 20, y: 0}
hideDetailOnStart: 1
@@ -7119,14 +7121,14 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 0, g: 0, b: 0, a: 0.39215687}
m_Color: {r: 0, g: 0, b: 0, a: 1}
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: 10907, guid: 0000000000000000f000000000000000, type: 0}
m_Sprite: {fileID: 21300000, guid: ef3296c82b3934a24b288ac6309eb7a7, type: 3}
m_Type: 1
m_PreserveAspect: 0
m_FillCenter: 1
@@ -11287,14 +11289,14 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 0, g: 0, b: 0, a: 0.39215687}
m_Color: {r: 0, g: 0, b: 0, a: 1}
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: 10907, guid: 0000000000000000f000000000000000, type: 0}
m_Sprite: {fileID: 21300000, guid: ef3296c82b3934a24b288ac6309eb7a7, type: 3}
m_Type: 1
m_PreserveAspect: 0
m_FillCenter: 1
@@ -13106,6 +13108,7 @@ MonoBehaviour:
contentMidSell: {fileID: 5285943178504563476}
item_info: {fileID: 3637242207861637912}
itemDetailNameText: {fileID: 2529529646566217934}
itemMoneyText: {fileID: 2517942723267868233}
itemDescriptionText:
legacy: {fileID: 0}
tmp: {fileID: 5329995747664012504}
+262 -1
View File
@@ -12438,6 +12438,7 @@ RectTransform:
- {fileID: 4137397199301223842}
- {fileID: 6801399374756499883}
- {fileID: 7205431771786927886}
- {fileID: 2409990490665478014}
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0.5, y: 0.5}
@@ -12572,6 +12573,14 @@ MonoBehaviour:
tmp: {fileID: 6020258894941961325}
equipButton: {fileID: 472698755110594484}
dropButton: {fileID: 540159372834342487}
splitPanelRoot: {fileID: 3772330938303001319}
splitAmountText: {fileID: 3418403519615399396}
splitConfirmButton: {fileID: 7942200720793983179}
splitCloseButton: {fileID: 7769815641486000385}
splitOpenButton: {fileID: 8357517184490870543}
splitIncreaseButton: {fileID: 8779682634462917281}
splitDecreaseButton: {fileID: 4996709440620641399}
splitMaxButton: {fileID: 1013848439096781118}
autoRefresh: 1
refreshInterval: 1
showEquipmentDetails: 1
@@ -19862,6 +19871,187 @@ MonoBehaviour:
m_hasFontAssetChanged: 0
m_baseMaterial: {fileID: 0}
m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
--- !u!1001 &8161685697759622574
PrefabInstance:
m_ObjectHideFlags: 0
serializedVersion: 2
m_Modification:
serializedVersion: 3
m_TransformParent: {fileID: 5834405183358786743}
m_Modifications:
- target: {fileID: 4980456610223425353, guid: 51e96aded6737254d81e1407089a11a7, type: 3}
propertyPath: m_Name
value: TachItem
objectReference: {fileID: 0}
- target: {fileID: 4980456610223425353, guid: 51e96aded6737254d81e1407089a11a7, type: 3}
propertyPath: m_IsActive
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5779842851872854736, guid: 51e96aded6737254d81e1407089a11a7, type: 3}
propertyPath: m_Pivot.x
value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 5779842851872854736, guid: 51e96aded6737254d81e1407089a11a7, type: 3}
propertyPath: m_Pivot.y
value: 0.5
objectReference: {fileID: 0}
- target: {fileID: 5779842851872854736, guid: 51e96aded6737254d81e1407089a11a7, type: 3}
propertyPath: m_AnchorMax.x
value: 1
objectReference: {fileID: 0}
- target: {fileID: 5779842851872854736, guid: 51e96aded6737254d81e1407089a11a7, type: 3}
propertyPath: m_AnchorMax.y
value: 1
objectReference: {fileID: 0}
- target: {fileID: 5779842851872854736, guid: 51e96aded6737254d81e1407089a11a7, type: 3}
propertyPath: m_AnchorMin.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5779842851872854736, guid: 51e96aded6737254d81e1407089a11a7, type: 3}
propertyPath: m_AnchorMin.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5779842851872854736, guid: 51e96aded6737254d81e1407089a11a7, type: 3}
propertyPath: m_SizeDelta.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5779842851872854736, guid: 51e96aded6737254d81e1407089a11a7, type: 3}
propertyPath: m_SizeDelta.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5779842851872854736, guid: 51e96aded6737254d81e1407089a11a7, type: 3}
propertyPath: m_LocalPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5779842851872854736, guid: 51e96aded6737254d81e1407089a11a7, type: 3}
propertyPath: m_LocalPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5779842851872854736, guid: 51e96aded6737254d81e1407089a11a7, type: 3}
propertyPath: m_LocalPosition.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5779842851872854736, guid: 51e96aded6737254d81e1407089a11a7, type: 3}
propertyPath: m_LocalRotation.w
value: 1
objectReference: {fileID: 0}
- target: {fileID: 5779842851872854736, guid: 51e96aded6737254d81e1407089a11a7, type: 3}
propertyPath: m_LocalRotation.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5779842851872854736, guid: 51e96aded6737254d81e1407089a11a7, type: 3}
propertyPath: m_LocalRotation.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5779842851872854736, guid: 51e96aded6737254d81e1407089a11a7, type: 3}
propertyPath: m_LocalRotation.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5779842851872854736, guid: 51e96aded6737254d81e1407089a11a7, type: 3}
propertyPath: m_AnchoredPosition.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5779842851872854736, guid: 51e96aded6737254d81e1407089a11a7, type: 3}
propertyPath: m_AnchoredPosition.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5779842851872854736, guid: 51e96aded6737254d81e1407089a11a7, type: 3}
propertyPath: m_LocalEulerAnglesHint.x
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5779842851872854736, guid: 51e96aded6737254d81e1407089a11a7, type: 3}
propertyPath: m_LocalEulerAnglesHint.y
value: 0
objectReference: {fileID: 0}
- target: {fileID: 5779842851872854736, guid: 51e96aded6737254d81e1407089a11a7, type: 3}
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 7763429162104525692, guid: 51e96aded6737254d81e1407089a11a7, type: 3}
propertyPath: m_text
value: "\u200B"
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects: []
m_AddedComponents: []
m_SourcePrefab: {fileID: 100100000, guid: 51e96aded6737254d81e1407089a11a7, type: 3}
--- !u!114 &1013848439096781118 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 9175459919676808336, guid: 51e96aded6737254d81e1407089a11a7, type: 3}
m_PrefabInstance: {fileID: 8161685697759622574}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!224 &2409990490665478014 stripped
RectTransform:
m_CorrespondingSourceObject: {fileID: 5779842851872854736, guid: 51e96aded6737254d81e1407089a11a7, type: 3}
m_PrefabInstance: {fileID: 8161685697759622574}
m_PrefabAsset: {fileID: 0}
--- !u!114 &3418403519615399396 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 6788255073064960074, guid: 51e96aded6737254d81e1407089a11a7, type: 3}
m_PrefabInstance: {fileID: 8161685697759622574}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 2da0c512f12947e489f739169773d7ca, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!1 &3772330938303001319 stripped
GameObject:
m_CorrespondingSourceObject: {fileID: 4980456610223425353, guid: 51e96aded6737254d81e1407089a11a7, type: 3}
m_PrefabInstance: {fileID: 8161685697759622574}
m_PrefabAsset: {fileID: 0}
--- !u!114 &4996709440620641399 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 3752555281223092697, guid: 51e96aded6737254d81e1407089a11a7, type: 3}
m_PrefabInstance: {fileID: 8161685697759622574}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!114 &7769815641486000385 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 1916228653064467631, guid: 51e96aded6737254d81e1407089a11a7, type: 3}
m_PrefabInstance: {fileID: 8161685697759622574}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!114 &7942200720793983179 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 2268825337156595045, guid: 51e96aded6737254d81e1407089a11a7, type: 3}
m_PrefabInstance: {fileID: 8161685697759622574}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!114 &8779682634462917281 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 617999410739726095, guid: 51e96aded6737254d81e1407089a11a7, type: 3}
m_PrefabInstance: {fileID: 8161685697759622574}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3}
m_Name:
m_EditorClassIdentifier:
--- !u!1001 &8542071282636773511
PrefabInstance:
m_ObjectHideFlags: 0
@@ -19870,6 +20060,22 @@ PrefabInstance:
serializedVersion: 3
m_TransformParent: {fileID: 5834405183358786743}
m_Modifications:
- target: {fileID: 636299721907915661, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchorMax.y
value: 1
objectReference: {fileID: 0}
- target: {fileID: 636299721907915661, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchorMin.y
value: 1
objectReference: {fileID: 0}
- target: {fileID: 636299721907915661, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchoredPosition.x
value: 20
objectReference: {fileID: 0}
- target: {fileID: 636299721907915661, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchoredPosition.y
value: -0
objectReference: {fileID: 0}
- target: {fileID: 1546246053547542409, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_Pivot.x
value: 0.5
@@ -19900,7 +20106,7 @@ PrefabInstance:
objectReference: {fileID: 0}
- target: {fileID: 1546246053547542409, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_SizeDelta.y
value: 0
value: 948.02
objectReference: {fileID: 0}
- target: {fileID: 1546246053547542409, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_LocalPosition.x
@@ -19950,10 +20156,54 @@ PrefabInstance:
propertyPath: m_LocalEulerAnglesHint.z
value: 0
objectReference: {fileID: 0}
- target: {fileID: 4552886554498063383, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_text
value: "T\xE1ch"
objectReference: {fileID: 0}
- target: {fileID: 6830833846243993097, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_Name
value: item_info
objectReference: {fileID: 0}
- target: {fileID: 6830833846243993097, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_IsActive
value: 0
objectReference: {fileID: 0}
- target: {fileID: 7209086543831860202, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchorMax.y
value: 1
objectReference: {fileID: 0}
- target: {fileID: 7209086543831860202, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchorMin.y
value: 1
objectReference: {fileID: 0}
- target: {fileID: 7209086543831860202, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchoredPosition.x
value: 20
objectReference: {fileID: 0}
- target: {fileID: 7209086543831860202, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchoredPosition.y
value: -928.02
objectReference: {fileID: 0}
- target: {fileID: 8894405194986632892, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchorMax.y
value: 1
objectReference: {fileID: 0}
- target: {fileID: 8894405194986632892, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchorMin.y
value: 1
objectReference: {fileID: 0}
- target: {fileID: 8894405194986632892, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_SizeDelta.y
value: 928.02
objectReference: {fileID: 0}
- target: {fileID: 8894405194986632892, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchoredPosition.x
value: 20
objectReference: {fileID: 0}
- target: {fileID: 8894405194986632892, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
propertyPath: m_AnchoredPosition.y
value: -464.01
objectReference: {fileID: 0}
m_RemovedComponents: []
m_RemovedGameObjects: []
m_AddedGameObjects: []
@@ -20008,3 +20258,14 @@ RectTransform:
m_CorrespondingSourceObject: {fileID: 1546246053547542409, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
m_PrefabInstance: {fileID: 8542071282636773511}
m_PrefabAsset: {fileID: 0}
--- !u!114 &8357517184490870543 stripped
MonoBehaviour:
m_CorrespondingSourceObject: {fileID: 391906676374785928, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
m_PrefabInstance: {fileID: 8542071282636773511}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3}
m_Name:
m_EditorClassIdentifier:
File diff suppressed because it is too large Load Diff
+7
View File
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 51e96aded6737254d81e1407089a11a7
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+260 -2
View File
@@ -37,7 +37,7 @@ RectTransform:
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 20, y: -464.01}
m_SizeDelta: {x: 400, y: 928.02}
m_SizeDelta: {x: 400, y: 0}
m_Pivot: {x: 0, y: 0.5}
--- !u!222 &4376431126769957786
CanvasRenderer:
@@ -584,6 +584,263 @@ MonoBehaviour:
m_hasFontAssetChanged: 0
m_baseMaterial: {fileID: 0}
m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
--- !u!1 &5176462084121257164
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 6900679650323527362}
- component: {fileID: 6199238498013377944}
- component: {fileID: 6207433149234425199}
- component: {fileID: 391906676374785928}
m_Layer: 5
m_Name: Button (2)
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &6900679650323527362
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5176462084121257164}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children:
- {fileID: 902438409941943524}
m_Father: {fileID: 636299721907915661}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 547.334, y: -173}
m_SizeDelta: {x: 171, y: 64}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &6199238498013377944
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5176462084121257164}
m_CullTransparentMesh: 1
--- !u!114 &6207433149234425199
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5176462084121257164}
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: 1, g: 1, b: 1, a: 1}
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: 21300000, guid: 8f24853d9cfea43389e8fb3101ffaae1, type: 3}
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!114 &391906676374785928
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5176462084121257164}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 4e29b1a8efbd4b44bb3f3716e73f07ff, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Navigation:
m_Mode: 3
m_WrapAround: 0
m_SelectOnUp: {fileID: 0}
m_SelectOnDown: {fileID: 0}
m_SelectOnLeft: {fileID: 0}
m_SelectOnRight: {fileID: 0}
m_Transition: 1
m_Colors:
m_NormalColor: {r: 1, g: 1, b: 1, a: 1}
m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608}
m_ColorMultiplier: 1
m_FadeDuration: 0.1
m_SpriteState:
m_HighlightedSprite: {fileID: 0}
m_PressedSprite: {fileID: 0}
m_SelectedSprite: {fileID: 0}
m_DisabledSprite: {fileID: 0}
m_AnimationTriggers:
m_NormalTrigger: Normal
m_HighlightedTrigger: Highlighted
m_PressedTrigger: Pressed
m_SelectedTrigger: Selected
m_DisabledTrigger: Disabled
m_Interactable: 1
m_TargetGraphic: {fileID: 6207433149234425199}
m_OnClick:
m_PersistentCalls:
m_Calls: []
--- !u!1 &5326319689215368846
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 902438409941943524}
- component: {fileID: 5055926984262207787}
- component: {fileID: 4552886554498063383}
m_Layer: 5
m_Name: Text (TMP)
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &902438409941943524
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5326319689215368846}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 6900679650323527362}
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: 2.5}
m_SizeDelta: {x: -16, y: -15}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &5055926984262207787
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5326319689215368846}
m_CullTransparentMesh: 1
--- !u!114 &4552886554498063383
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5326319689215368846}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 0
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_text: Button
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 369c2e14814cc9a4b8e3ad4e37769134, type: 2}
m_sharedMaterial: {fileID: 2100000, guid: 31b77628c21b17e45a6577a3d3d5aef0, type: 2}
m_fontSharedMaterials: []
m_fontMaterial: {fileID: 0}
m_fontMaterials: []
m_fontColor32:
serializedVersion: 2
rgba: 4294967295
m_fontColor: {r: 1, g: 1, b: 1, a: 1}
m_enableVertexGradient: 0
m_colorMode: 3
m_fontColorGradient:
topLeft: {r: 1, g: 1, b: 1, a: 1}
topRight: {r: 1, g: 1, b: 1, a: 1}
bottomLeft: {r: 1, g: 1, b: 1, a: 1}
bottomRight: {r: 1, g: 1, b: 1, a: 1}
m_fontColorGradientPreset: {fileID: 0}
m_spriteAsset: {fileID: 0}
m_tintAllSprites: 0
m_StyleSheet: {fileID: 0}
m_TextStyleHashCode: -1183493901
m_overrideHtmlColors: 0
m_faceColor:
serializedVersion: 2
rgba: 4294967295
m_fontSize: 36
m_fontSizeBase: 36
m_fontWeight: 400
m_enableAutoSizing: 0
m_fontSizeMin: 18
m_fontSizeMax: 72
m_fontStyle: 0
m_HorizontalAlignment: 2
m_VerticalAlignment: 8192
m_textAlignment: 65535
m_characterSpacing: 0
m_wordSpacing: 0
m_lineSpacing: 0
m_lineSpacingMax: 0
m_paragraphSpacing: 0
m_charWidthMaxAdj: 0
m_TextWrappingMode: 1
m_wordWrappingRatios: 0.4
m_overflowMode: 0
m_linkedTextComponent: {fileID: 0}
parentLinkedComponent: {fileID: 0}
m_enableKerning: 0
m_ActiveFontFeatures: 6e72656b
m_enableExtraPadding: 0
checkPaddingRequired: 0
m_isRichText: 1
m_EmojiFallbackSupport: 1
m_parseCtrlCharacters: 1
m_isOrthographic: 1
m_isCullingEnabled: 0
m_horizontalMapping: 0
m_verticalMapping: 0
m_uvLineOffset: 0
m_geometrySortingOrder: 0
m_IsTextObjectScaleStatic: 0
m_VertexBufferAutoSizeReduction: 0
m_useMaxVisibleDescender: 1
m_pageToDisplay: 1
m_margin: {x: 0, y: 0, z: 0, w: 0}
m_isUsingLegacyAnimationComponent: 0
m_isVolumetricText: 0
m_hasFontAssetChanged: 0
m_baseMaterial: {fileID: 0}
m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
--- !u!1 &5721094068644211543
GameObject:
m_ObjectHideFlags: 0
@@ -725,7 +982,7 @@ GameObject:
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 0
m_IsActive: 1
--- !u!224 &1546246053547542409
RectTransform:
m_ObjectHideFlags: 0
@@ -871,6 +1128,7 @@ RectTransform:
m_Children:
- {fileID: 1900527214026617767}
- {fileID: 4639188770757162324}
- {fileID: 6900679650323527362}
m_Father: {fileID: 1546246053547542409}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
+75
View File
@@ -369,6 +369,81 @@ namespace BrewMonster
int cmd = Convert.ToInt32(Msg.dwParam2);
switch (cmd)
{
case CommandID.EXG_IVTR_ITEM:
{
// S2C::cmd_exg_ivtr_item { byte index1; byte index2; }
if (data == null || data.Length < 2)
{
Debug.LogWarning("[Inventory] EXG_IVTR_ITEM: Invalid data length");
break;
}
byte index1 = data[0];
byte index2 = data[1];
if (m_pPack != null)
{
m_pPack.ExchangeItem(index1, index2);
var pItem1 = m_pPack.GetItem(index1, false);
if (pItem1 != null)
{
pItem1.Package = InventoryConst.IVTRTYPE_PACK;
pItem1.Slot = index1;
}
var pItem2 = m_pPack.GetItem(index2, false);
if (pItem2 != null)
{
pItem2.Package = InventoryConst.IVTRTYPE_PACK;
pItem2.Slot = index2;
}
}
var ui = GameObject.FindFirstObjectByType<EC_InventoryUI>();
ui?.RefreshAll();
break;
}
case CommandID.MOVE_IVTR_ITEM:
{
// S2C::cmd_move_ivtr_item { byte src; byte dest; uint count; }
if (data == null || data.Length < 6)
{
Debug.LogWarning("[Inventory] MOVE_IVTR_ITEM: Invalid data length");
break;
}
byte src = data[0];
byte dest = data[1];
uint count = BitConverter.ToUInt32(data, 2);
if (m_pPack != null)
{
// Client-side mirror of server operation.
// When dest is empty and count < stack, this becomes "separate/split stack".
m_pPack.MoveItem(src, dest, (int)count);
var pItemSrc = m_pPack.GetItem(src, false);
if (pItemSrc != null)
{
pItemSrc.Package = InventoryConst.IVTRTYPE_PACK;
pItemSrc.Slot = src;
}
var pItemDest = m_pPack.GetItem(dest, false);
if (pItemDest != null)
{
pItemDest.Package = InventoryConst.IVTRTYPE_PACK;
pItemDest.Slot = dest;
}
}
var ui = GameObject.FindFirstObjectByType<EC_InventoryUI>();
ui?.RefreshAll();
break;
}
case CommandID.PLAYER_DROP_ITEM:
{
// Parse the drop item data from the server response
+5
View File
@@ -107,6 +107,11 @@ namespace BrewMonster
// GET_ALL_DATA end flag tasks were here in C++ (LoadConfigData), omitted in C#
UnityGameSession.LoadConfigData();
// Quest trace lists + minimion: apply SyncTrace once active tasks and templates are ready.
// 任务追踪:在任务数据与模板就绪后调用 SyncTrace 同步追踪列表与小窗。
var pGameUI = EC_Game.GetGameRun()?.GetUIManager()?.GetInGameUIMan();
pGameUI?.OnHostTaskDataInitialized(m_pTaskInterface);
// if (UpdateEquipSkills()) UpdateEquipSkillCoolDown(); // methods not ported yet
}
else if (header == CommandID.TASK_VAR_DATA)
+1 -1
View File
@@ -649,7 +649,7 @@ namespace BrewMonster
cmd_pickup_money pCmd = GPDataTypeHelper.FromBytes<cmd_pickup_money>(data);
AddMoneyAmount(pCmd.amount);
CECGameRun pGameRun = EC_Game.GetGameRun();
pGameRun.AddFixedMessage((int)FixedMsg.FIXMSG_PICKUPMONEY);
pGameRun.AddFixedMessage((int)FixedMsg.FIXMSG_PICKUPMONEY, pCmd.amount);
BubbleText((int)BubbleTextType.BUBBLE_MONEY, (uint)pCmd.amount);
}
+1 -1
View File
@@ -457,7 +457,7 @@ namespace BrewMonster.Scripts.ChatUI
// Tránh truyền "&target&" vì kết quả sẽ là "&&target&&" → regex chỉ match "target"
// nhưng để lại & dư ở ngoài → UI hiển thị "&target&" thay vì link sạch.
CECStringTab pStrTab = EC_Game.GetFixedMsgs();
string fmt = AUICommon.ConvertPrintfToCSharpFormat(pStrTab.GetWideString((int)FixedMsg.FIXMSG_PRIVATECHAT2));
string fmt = AUIDialog.FormatPrintf(pStrTab.GetWideString((int)FixedMsg.FIXMSG_PRIVATECHAT2));
string localMsg;
try
{
+8 -16
View File
@@ -773,23 +773,19 @@ public partial class CECGameRun : ITickable
return;
}
// Format the message with provided arguments
// fixed_msg.txt uses printf-style (%d, %s); not C# {0} placeholders
string szFormattedMsg;
try
{
if (args != null && args.Length > 0)
{
szFormattedMsg = string.Format(szFixMsg, args);
}
szFormattedMsg = AUIDialog.FormatPrintf(szFixMsg, args);
else
{
szFormattedMsg = szFixMsg;
}
}
catch (System.FormatException ex)
catch (Exception ex)
{
Debug.LogError($"[AddFixedMessage] Format error for message {iMsg}: {ex.Message}");
szFormattedMsg = szFixMsg; // Use unformatted message as fallback
szFormattedMsg = szFixMsg;
}
// Try to add to in-game UI chat
@@ -828,23 +824,19 @@ public partial class CECGameRun : ITickable
return;
}
// Format the message with provided arguments
// fixed_msg.txt uses printf-style (%d, %s); not C# {0} placeholders
string szFormattedMsg;
try
{
if (args != null && args.Length > 0)
{
szFormattedMsg = string.Format(szFixMsg, args);
}
szFormattedMsg = AUIDialog.FormatPrintf(szFixMsg, args);
else
{
szFormattedMsg = szFixMsg;
}
}
catch (System.FormatException ex)
catch (Exception ex)
{
Debug.LogError($"[AddFixedChannelMsg] Format error for message {iMsg}: {ex.Message}");
szFormattedMsg = szFixMsg; // Use unformatted message as fallback
szFormattedMsg = szFixMsg;
}
// Try to add to in-game UI chat with specific channel