diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/AUICommon.cs b/Assets/PerfectWorld/Scripts/Network/CSNetwork/AUICommon.cs
index 49e29e4a5f..2cc27dfac0 100644
--- a/Assets/PerfectWorld/Scripts/Network/CSNetwork/AUICommon.cs
+++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/AUICommon.cs
@@ -321,52 +321,6 @@ namespace CSNetwork
return sb.ToString();
}
- ///
- /// Chuyển đổi định dạng printf (C-style: %s, %d) sang string.Format (C#-style: {0}, {1})
- ///
- 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();
- }
-
///
/// [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.
diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs b/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs
index 934c224f0b..8b4ba6c6a3 100644
--- a/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs
+++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs
@@ -1962,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);
@@ -2057,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);
@@ -2208,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
@@ -2231,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,
@@ -2275,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 {
diff --git a/Assets/PerfectWorld/Scripts/Players/EC_ElsePlayer.cs b/Assets/PerfectWorld/Scripts/Players/EC_ElsePlayer.cs
index 8b7cbe0ea4..6b697df049 100644
--- a/Assets/PerfectWorld/Scripts/Players/EC_ElsePlayer.cs
+++ b/Assets/PerfectWorld/Scripts/Players/EC_ElsePlayer.cs
@@ -19,6 +19,9 @@ using PerfectWorld.Scripts.Managers;
using UnityEngine;
using BrewMonster.Scripts.Managers;
using System.Collections.Generic;
+using static BrewMonster.CECHostPlayer;
+using TMPro;
+using BrewMonster.PerfectWorld.Scripts.UI;
namespace BrewMonster
{
@@ -995,6 +998,7 @@ namespace BrewMonster
m_bBaseInfoReady = true;
SetPlayerName(szName ?? "");
EC_Game.GetGameRun().AddPlayerName(m_PlayerInfo.cid, szName, true);
+ GetComponentInChildren().RefreshName();
}
// Level up
public void LevelUp()
diff --git a/Assets/PerfectWorld/Scripts/Players/PlayerModelPreview.cs b/Assets/PerfectWorld/Scripts/Players/PlayerModelPreview.cs
index 7724b5e1d1..d6a9a17567 100644
--- a/Assets/PerfectWorld/Scripts/Players/PlayerModelPreview.cs
+++ b/Assets/PerfectWorld/Scripts/Players/PlayerModelPreview.cs
@@ -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 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();
+ 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();
+ var skinnedMeshRenderereFromDataList = armorObject.GetComponentsInChildren();
+ 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(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();
+ }
+ return skeletonBuilder;
+ }
}
}
diff --git a/Assets/Scripts/CECHostPlayer.cs b/Assets/Scripts/CECHostPlayer.cs
index 9d94d64bc5..bd12ed5487 100644
--- a/Assets/Scripts/CECHostPlayer.cs
+++ b/Assets/Scripts/CECHostPlayer.cs
@@ -649,7 +649,7 @@ namespace BrewMonster
cmd_pickup_money pCmd = GPDataTypeHelper.FromBytes(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);
}
diff --git a/Assets/Scripts/ChatInputHandler.cs b/Assets/Scripts/ChatInputHandler.cs
index 09226d7ebf..b696e24255 100644
--- a/Assets/Scripts/ChatInputHandler.cs
+++ b/Assets/Scripts/ChatInputHandler.cs
@@ -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
{
diff --git a/Assets/Scripts/EC_GameRun.cs b/Assets/Scripts/EC_GameRun.cs
index 0932be7aba..71b267d14b 100644
--- a/Assets/Scripts/EC_GameRun.cs
+++ b/Assets/Scripts/EC_GameRun.cs
@@ -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