Merge branch 'develop' into feature/update-ui
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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<UIPlayer>().RefreshName();
|
||||
}
|
||||
// Level up
|
||||
public void LevelUp()
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user