Add ext description text

This commit is contained in:
HungDK
2025-12-08 16:15:01 +07:00
parent 892d0d3aef
commit 6392baf369
7 changed files with 722 additions and 67 deletions
@@ -53,6 +53,12 @@ namespace BrewMonster.Scripts.Managers
private static bool s_hasPendingCash;
private static int s_pendingCashAmount;
// Flags to prevent log spam for extended description warnings
// 防止扩展描述警告日志刷屏的标志
private static bool m_HasLoggedExtDescNull = false;
private static bool m_HasLoggedExtDescNotInit = false;
private static bool m_HasLoggedExtDescError = false;
private InventoryModel model;
private InventoryView view;
@@ -828,16 +834,81 @@ namespace BrewMonster.Scripts.Managers
{
// Fallback to legacy string-table description if centralised pipeline returns empty
string itemDescription = GetItemDescription(item.m_tid);
string itemExtendedDesc = GetItemExtendedDescription(item.m_tid);
descriptionText?.Set(itemDescription ?? "No description available");
extendedDescText?.Set(itemExtendedDesc ?? "");
}
// Get extended description exactly like C++ code: g_pGame->GetItemExtDesc(m_tid)
// C++ code doesn't check IsInitialized() - it just calls GetWideString() directly
// 完全按照C++代码获取扩展描述:g_pGame->GetItemExtDesc(m_tid)
// C++代码不检查IsInitialized() - 它直接调用GetWideString()
string szExtDesc = null;
try
{
var itemExtDescTab = EC_Game.GetItemExtDesc();
if (itemExtDescTab != null)
{
// First try to get mapped message ID (like TryGetItemExtDesc does)
// 首先尝试获取映射的消息ID(如TryGetItemExtDesc所做)
if (EC_Game.TryGetItemMsg(item.m_tid, out int messageId, out int displayMode))
{
szExtDesc = itemExtDescTab.GetWideString(messageId);
}
// Fallback: direct lookup using tid (exactly like C++: m_ItemExtDesc.GetWideString(tid))
// 回退:直接使用tid查找(完全像C++m_ItemExtDesc.GetWideString(tid)
if (string.IsNullOrEmpty(szExtDesc))
{
szExtDesc = itemExtDescTab.GetWideString(item.m_tid);
}
}
}
catch (System.Exception ex)
{
// Only log once to avoid spam
// 仅记录一次以避免垃圾日志
if (!m_HasLoggedExtDescError)
{
Debug.LogWarning($"[InventoryUI] Error getting extended description: {ex.Message}");
m_HasLoggedExtDescError = true;
}
}
// Display extended description if found (exactly like C++ checks: if (!szExtDesc || !szExtDesc[0]))
// 如果找到扩展描述则显示(完全像C++检查:if (!szExtDesc || !szExtDesc[0])
string displayText = !string.IsNullOrEmpty(szExtDesc)
? szExtDesc.Replace("\\r", "\n")
: "";
// Debug logging to diagnose issues
// 调试日志以诊断问题
if (string.IsNullOrEmpty(displayText))
{
Debug.Log($"[InventoryUI] Extended description is empty for tid={item.m_tid}. szExtDesc was null/empty.");
}
else
{
Debug.Log($"[InventoryUI] Found extended description for tid={item.m_tid}, length={displayText.Length}");
}
// Setup equip and drop buttons
SetupEquipButton(package, item);
SetupDropButton(package, item);
// Show panel first
// 先显示面板
ShowDetailPanel(true);
// Set text directly - if this causes rebuild issues, we'll use coroutine
// 直接设置文本 - 如果这导致重建问题,我们将使用协程
if (extendedDescText != null)
{
extendedDescText.Set(displayText);
Debug.Log($"[InventoryUI] Set extended description text, extendedDescText is {(extendedDescText == null ? "null" : "not null")}");
}
else
{
Debug.LogWarning("[InventoryUI] extendedDescText is null! Check Inspector assignment.");
}
}
private void SetupEquipButton(byte package, EC_IvtrItem item)
@@ -1250,14 +1250,180 @@ namespace BrewMonster.Scripts.Managers
m_strDesc += "\n";
}
// Add extend description to description string / 添加扩展描述到描述字符串
protected void AddExtDescText()
{
// Extend description from item_ext_desc.txt via EC_Game
string ext = TryGetItemExtDesc();
if (!string.IsNullOrEmpty(ext))
// Get extended description from item_ext_desc.txt using tid / 使用tid从item_ext_desc.txt获取扩展描述
string szExtDesc = TryGetItemExtDesc();
// Note: Original C++ had early return commented out / 注意:原始C++代码的早期返回被注释掉了
// if (!szExtDesc || !szExtDesc[0])
// return;
m_strDesc += "\\r";
bool bAddLine = true;
// Add special properties description / 添加特殊属性描述
var pDescTab = EC_Game.GetItemDesc();
// Note: ITEMDESC_COL2_BRIGHTBLUE constant - adjust based on actual string table / 注意:ITEMDESC_COL2_BRIGHTBLUE常量 - 根据实际字符串表调整
int green = 1000; // ITEMDESC_COL2_BRIGHTBLUE placeholder - adjust this value
if (m_iCID != (int)InventoryClassId.ICID_GOBLIN) // goblin does not need to display these special properties / 地精不需要显示这些特殊属性
{
AddDescText(0, true, ext);
// Exact C++ logic: (PROC_NO_USER_TRASH) || (!PROC_BINDING && (PROC_DROPWHENDIE || ...))
// 精确的C++逻辑:(PROC_NO_USER_TRASH) || (!PROC_BINDING && (PROC_DROPWHENDIE || ...))
if ((m_iProcType & (int)ProcType.PROC_NO_USER_TRASH) != 0
|| (!((m_iProcType & (int)ProcType.PROC_BINDING) != 0)
&& ((m_iProcType & (int)ProcType.PROC_DROPWHENDIE) != 0
|| (m_iProcType & (int)ProcType.PROC_DROPPABLE) != 0
|| (m_iProcType & (int)ProcType.PROC_SELLABLE) != 0
|| (m_iProcType & (int)ProcType.PROC_TRADEABLE) != 0
|| (m_iProcType & (int)ProcType.PROC_DISAPEAR) != 0
|| (m_iProcType & (int)ProcType.PROC_USE) != 0
|| (m_iProcType & (int)ProcType.PROC_DEADDROP) != 0
|| (m_iProcType & (int)ProcType.PROC_OFFLINE) != 0
|| (m_iProcType & (int)ProcType.PROC_UNREPAIRABLE) != 0)))
{
bAddLine = false;
if (pDescTab != null && pDescTab.IsInitialized())
{
string szCol = pDescTab.GetWideString(green);
if (!string.IsNullOrEmpty(szCol))
{
m_strDesc += szCol;
}
}
// Note: These message IDs are placeholders - adjust based on actual string table / 注意:这些消息ID是占位符 - 根据实际字符串表调整
if ((m_iProcType & (int)ProcType.PROC_DROPWHENDIE) != 0)
{
m_strDesc += "\\r";
if (pDescTab != null && pDescTab.IsInitialized())
{
// ITEMDESC_DEAD_PROTECT placeholder - adjust this value
string desc = pDescTab.GetWideString(2000); // Placeholder ID
if (!string.IsNullOrEmpty(desc))
m_strDesc += desc;
}
}
if ((m_iProcType & (int)ProcType.PROC_DROPPABLE) != 0)
{
m_strDesc += "\\r";
if (pDescTab != null && pDescTab.IsInitialized())
{
// ITEMDESC_NO_DROP placeholder - adjust this value
string desc = pDescTab.GetWideString(2001); // Placeholder ID
if (!string.IsNullOrEmpty(desc))
m_strDesc += desc;
}
}
if ((m_iProcType & (int)ProcType.PROC_SELLABLE) != 0)
{
m_strDesc += "\\r";
if (pDescTab != null && pDescTab.IsInitialized())
{
// ITEMDESC_NO_TRADE placeholder - adjust this value
string desc = pDescTab.GetWideString(2002); // Placeholder ID
if (!string.IsNullOrEmpty(desc))
m_strDesc += desc;
}
}
if ((m_iProcType & (int)ProcType.PROC_TRADEABLE) != 0)
{
m_strDesc += "\\r";
if (pDescTab != null && pDescTab.IsInitialized())
{
// ITEMDESC_NO_PLAYER_TRADE placeholder - adjust this value
string desc = pDescTab.GetWideString(2003); // Placeholder ID
if (!string.IsNullOrEmpty(desc))
m_strDesc += desc;
}
}
if ((m_iProcType & (int)ProcType.PROC_DISAPEAR) != 0)
{
m_strDesc += "\\r";
if (pDescTab != null && pDescTab.IsInitialized())
{
// ITEMDESC_LEAVE_SCENE_DISAPEAR placeholder - adjust this value
string desc = pDescTab.GetWideString(2004); // Placeholder ID
if (!string.IsNullOrEmpty(desc))
m_strDesc += desc;
}
}
if ((m_iProcType & (int)ProcType.PROC_USE) != 0)
{
m_strDesc += "\\r";
if (pDescTab != null && pDescTab.IsInitialized())
{
// ITEMDESC_USE_AFTER_PICK_UP placeholder - adjust this value
string desc = pDescTab.GetWideString(2005); // Placeholder ID
if (!string.IsNullOrEmpty(desc))
m_strDesc += desc;
}
}
if ((m_iProcType & (int)ProcType.PROC_DEADDROP) != 0)
{
m_strDesc += "\\r";
if (pDescTab != null && pDescTab.IsInitialized())
{
// ITEMDESC_DROP_WHEN_DEAD placeholder - adjust this value
string desc = pDescTab.GetWideString(2006); // Placeholder ID
if (!string.IsNullOrEmpty(desc))
m_strDesc += desc;
}
}
if ((m_iProcType & (int)ProcType.PROC_OFFLINE) != 0)
{
m_strDesc += "\\r";
if (pDescTab != null && pDescTab.IsInitialized())
{
// ITEMDESC_DROP_WHEN_OFFLINE placeholder - adjust this value
string desc = pDescTab.GetWideString(2007); // Placeholder ID
if (!string.IsNullOrEmpty(desc))
m_strDesc += desc;
}
}
if ((m_iProcType & (int)ProcType.PROC_UNREPAIRABLE) != 0)
{
m_strDesc += "\\r";
if (pDescTab != null && pDescTab.IsInitialized())
{
// ITEMDESC_UNREPAIRABLE placeholder - adjust this value
string desc = pDescTab.GetWideString(2008); // Placeholder ID
if (!string.IsNullOrEmpty(desc))
m_strDesc += desc;
}
}
if ((m_iProcType & (int)ProcType.PROC_NO_USER_TRASH) != 0)
{
m_strDesc += "\\r";
if (pDescTab != null && pDescTab.IsInitialized())
{
// ITEMDESC_NO_USER_TRASH placeholder - adjust this value
string desc = pDescTab.GetWideString(2009); // Placeholder ID
if (!string.IsNullOrEmpty(desc))
m_strDesc += desc;
}
}
}
else
{
TrimLastReturn();
}
}
else
{
TrimLastReturn();
}
if (string.IsNullOrEmpty(szExtDesc))
return;
// Extend description is below special properties / 扩展描述在特殊属性下方
if (bAddLine)
m_strDesc += "\\r\\r";
else
m_strDesc += "\\r";
m_strDesc += szExtDesc;
}
protected void AddExpireTimeDesc()
@@ -1401,6 +1567,182 @@ namespace BrewMonster.Scripts.Managers
return string.Empty;
}
/// <summary>
/// Get extended description text for UI display.
/// This method mirrors AddExtDescText() logic but returns a string instead of modifying m_strDesc.
/// 此方法镜像AddExtDescText()逻辑,但返回字符串而不是修改m_strDesc
/// </summary>
public string GetExtendedDescText()
{
string result = string.Empty;
// Get extended description from item_ext_desc.txt using tid / 使用tid从item_ext_desc.txt获取扩展描述
string szExtDesc = TryGetItemExtDesc();
// Note: Original C++ had early return commented out / 注意:原始C++代码的早期返回被注释掉了
// if (!szExtDesc || !szExtDesc[0])
// return;
result += "\\r";
bool bAddLine = true;
// Add special properties description / 添加特殊属性描述
var pDescTab = EC_Game.GetItemDesc();
// Note: ITEMDESC_COL2_BRIGHTBLUE constant - adjust based on actual string table / 注意:ITEMDESC_COL2_BRIGHTBLUE常量 - 根据实际字符串表调整
int green = 1000; // ITEMDESC_COL2_BRIGHTBLUE placeholder - adjust this value
if (m_iCID != (int)InventoryClassId.ICID_GOBLIN) // goblin does not need to display these special properties / 地精不需要显示这些特殊属性
{
// Exact C++ logic: (PROC_NO_USER_TRASH) || (!PROC_BINDING && (PROC_DROPWHENDIE || ...))
// 精确的C++逻辑:(PROC_NO_USER_TRASH) || (!PROC_BINDING && (PROC_DROPWHENDIE || ...))
if ((m_iProcType & (int)ProcType.PROC_NO_USER_TRASH) != 0
|| (!((m_iProcType & (int)ProcType.PROC_BINDING) != 0)
&& ((m_iProcType & (int)ProcType.PROC_DROPWHENDIE) != 0
|| (m_iProcType & (int)ProcType.PROC_DROPPABLE) != 0
|| (m_iProcType & (int)ProcType.PROC_SELLABLE) != 0
|| (m_iProcType & (int)ProcType.PROC_TRADEABLE) != 0
|| (m_iProcType & (int)ProcType.PROC_DISAPEAR) != 0
|| (m_iProcType & (int)ProcType.PROC_USE) != 0
|| (m_iProcType & (int)ProcType.PROC_DEADDROP) != 0
|| (m_iProcType & (int)ProcType.PROC_OFFLINE) != 0
|| (m_iProcType & (int)ProcType.PROC_UNREPAIRABLE) != 0)))
{
bAddLine = false;
if (pDescTab != null && pDescTab.IsInitialized())
{
string szCol = pDescTab.GetWideString(green);
if (!string.IsNullOrEmpty(szCol))
{
result += szCol;
}
}
// Note: These message IDs are placeholders - adjust based on actual string table / 注意:这些消息ID是占位符 - 根据实际字符串表调整
if ((m_iProcType & (int)ProcType.PROC_DROPWHENDIE) != 0)
{
result += "\\r";
if (pDescTab != null && pDescTab.IsInitialized())
{
// ITEMDESC_DEAD_PROTECT placeholder - adjust this value
string desc = pDescTab.GetWideString(2000); // Placeholder ID
if (!string.IsNullOrEmpty(desc))
result += desc;
}
}
if ((m_iProcType & (int)ProcType.PROC_DROPPABLE) != 0)
{
result += "\\r";
if (pDescTab != null && pDescTab.IsInitialized())
{
// ITEMDESC_NO_DROP placeholder - adjust this value
string desc = pDescTab.GetWideString(2001); // Placeholder ID
if (!string.IsNullOrEmpty(desc))
result += desc;
}
}
if ((m_iProcType & (int)ProcType.PROC_SELLABLE) != 0)
{
result += "\\r";
if (pDescTab != null && pDescTab.IsInitialized())
{
// ITEMDESC_NO_TRADE placeholder - adjust this value
string desc = pDescTab.GetWideString(2002); // Placeholder ID
if (!string.IsNullOrEmpty(desc))
result += desc;
}
}
if ((m_iProcType & (int)ProcType.PROC_TRADEABLE) != 0)
{
result += "\\r";
if (pDescTab != null && pDescTab.IsInitialized())
{
// ITEMDESC_NO_PLAYER_TRADE placeholder - adjust this value
string desc = pDescTab.GetWideString(2003); // Placeholder ID
if (!string.IsNullOrEmpty(desc))
result += desc;
}
}
if ((m_iProcType & (int)ProcType.PROC_DISAPEAR) != 0)
{
result += "\\r";
if (pDescTab != null && pDescTab.IsInitialized())
{
// ITEMDESC_LEAVE_SCENE_DISAPEAR placeholder - adjust this value
string desc = pDescTab.GetWideString(2004); // Placeholder ID
if (!string.IsNullOrEmpty(desc))
result += desc;
}
}
if ((m_iProcType & (int)ProcType.PROC_USE) != 0)
{
result += "\\r";
if (pDescTab != null && pDescTab.IsInitialized())
{
// ITEMDESC_USE_AFTER_PICK_UP placeholder - adjust this value
string desc = pDescTab.GetWideString(2005); // Placeholder ID
if (!string.IsNullOrEmpty(desc))
result += desc;
}
}
if ((m_iProcType & (int)ProcType.PROC_DEADDROP) != 0)
{
result += "\\r";
if (pDescTab != null && pDescTab.IsInitialized())
{
// ITEMDESC_DROP_WHEN_DEAD placeholder - adjust this value
string desc = pDescTab.GetWideString(2006); // Placeholder ID
if (!string.IsNullOrEmpty(desc))
result += desc;
}
}
if ((m_iProcType & (int)ProcType.PROC_OFFLINE) != 0)
{
result += "\\r";
if (pDescTab != null && pDescTab.IsInitialized())
{
// ITEMDESC_DROP_WHEN_OFFLINE placeholder - adjust this value
string desc = pDescTab.GetWideString(2007); // Placeholder ID
if (!string.IsNullOrEmpty(desc))
result += desc;
}
}
if ((m_iProcType & (int)ProcType.PROC_UNREPAIRABLE) != 0)
{
result += "\\r";
if (pDescTab != null && pDescTab.IsInitialized())
{
// ITEMDESC_UNREPAIRABLE placeholder - adjust this value
string desc = pDescTab.GetWideString(2008); // Placeholder ID
if (!string.IsNullOrEmpty(desc))
result += desc;
}
}
if ((m_iProcType & (int)ProcType.PROC_NO_USER_TRASH) != 0)
{
result += "\\r";
if (pDescTab != null && pDescTab.IsInitialized())
{
// ITEMDESC_NO_USER_TRASH placeholder - adjust this value
string desc = pDescTab.GetWideString(2009); // Placeholder ID
if (!string.IsNullOrEmpty(desc))
result += desc;
}
}
}
}
if (string.IsNullOrEmpty(szExtDesc))
return result;
// Extend description is below special properties / 扩展描述在特殊属性下方
if (bAddLine)
result += "\\r\\r";
else
result += "\\r";
result += szExtDesc;
return result;
}
#endregion
}
@@ -6,6 +6,7 @@ using UnityEngine;
using UnityEngine.UI;
using TMPro;
using PerfectWorld.Scripts.Shop;
using PerfectWorld.Scripts.Managers;
using BrewMonster.Scripts.Managers;
using BrewMonster.Network;
using CSNetwork.C2SCommand;
@@ -13,10 +14,9 @@ using CSNetwork.C2SCommand;
public class NPCShopDetailPanel : MonoBehaviour
{
[Header("UI Components")]
public TextMeshProUGUI itemNameText;
public TextMeshProUGUI itemDescriptionText;
public TextOutlet itemDescriptionText;
public Image itemIconImage;
public TextMeshProUGUI itemPriceText;
//public TextMeshProUGUI itemPriceText;
[Header("Action Buttons")]
public Button closeButton;
@@ -55,15 +55,11 @@ public class NPCShopDetailPanel : MonoBehaviour
return;
}
// Set item name
if (itemNameText != null)
itemNameText.text = currentItem.name;
// Set item description
if (itemDescriptionText != null)
{
string description = GetItemDescription((int)currentItem.id);
itemDescriptionText.text = description;
itemDescriptionText.Set(description);
}
// Set price (use first available price)
@@ -80,8 +76,8 @@ public class NPCShopDetailPanel : MonoBehaviour
}
}
if (itemPriceText != null)
itemPriceText.text = price > 0 ? $"Price: {price}" : "Price: N/A";
// if (itemPriceText != null)
// itemPriceText.text = price > 0 ? $"Price: {price}" : "Price: N/A";
// Load icon
if (itemIconImage != null)
@@ -114,7 +110,7 @@ public class NPCShopDetailPanel : MonoBehaviour
}
}
string GetItemDescription(int itemId)
string GetItemDescription(int itemId)
{
// First check if description is already in the item data
if (!string.IsNullOrEmpty(currentItem.desc))
@@ -122,27 +118,48 @@ public class NPCShopDetailPanel : MonoBehaviour
return currentItem.desc;
}
// Otherwise, build description using the same central pipeline as inventory:
try
// Try to use EC_IvtrEquip description for equipment items (like EC_InventoryUI does)
try
{
// Create EC_IvtrEquip for equipment items - this will work for weapons and armor
EC_IvtrEquip equipment = new EC_IvtrEquip(itemId, 0);
// For NPC shop items, we typically have static data only
// Try to get description using GetNormalDesc() which works with base stats
string equipDesc = equipment.GetNormalDesc();
if (!string.IsNullOrEmpty(equipDesc))
{
EC_IvtrItem item = EC_IvtrItem.CreateItem(itemId, 0, 1);
if (item != null)
// Replace C++ style "\r" line separators with real newlines for TMP
return equipDesc.Replace("\\r", "\n");
}
}
catch (System.Exception ex)
{
// If EC_IvtrEquip fails, fall through to EC_IvtrItem method
Debug.LogWarning($"[NPCShopDetailPanel] Failed to get equipment description for item {itemId}: {ex.Message}");
}
// Fallback: build description using EC_IvtrItem (for non-equipment items)
try
{
EC_IvtrItem item = EC_IvtrItem.CreateItem(itemId, 0, 1);
if (item != null)
{
// For NPC shop, we typically have static data only, so load from local DB
item.GetDetailDataFromLocal();
string description = item.GetDesc();
if (!string.IsNullOrEmpty(description))
{
// For NPC shop, we typically have static data only, so load from local DB
item.GetDetailDataFromLocal();
string description = item.GetDesc();
if (!string.IsNullOrEmpty(description))
{
return description.Replace("\\r", "\n");
}
return description.Replace("\\r", "\n");
}
}
catch (System.Exception ex)
{
Debug.LogWarning($"[NPCShopDetailPanel] Failed to get description for item {itemId}: {ex.Message}");
}
}
catch (System.Exception ex)
{
Debug.LogWarning($"[NPCShopDetailPanel] Failed to get description for item {itemId}: {ex.Message}");
}
return "No description available.";
return "No description available.";
}
void OnCloseClicked()
@@ -197,5 +214,25 @@ public class NPCShopDetailPanel : MonoBehaviour
if (buyButton != null)
buyButton.onClick.RemoveListener(OnBuyButtonClicked);
}
// === Text Outlet Helper ===
[System.Serializable]
public class TextOutlet
{
[SerializeField] public Text legacy;
[SerializeField] public TMPro.TextMeshProUGUI tmp;
public void Set(string value)
{
if (legacy != null)
{
legacy.text = EC_Utility.FormatForLegacyText(value ?? string.Empty);
}
if (tmp != null)
{
tmp.text = EC_Utility.FormatForTextMeshPro(value ?? string.Empty);
}
}
}
}
+3 -1
View File
@@ -896,7 +896,9 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
itemNameText: {fileID: 2676848775259769377}
itemDescriptionText: {fileID: 1509498505529622136}
itemDescriptionText:
legacy: {fileID: 0}
tmp: {fileID: 1509498505529622136}
itemIconImage: {fileID: 5729739685989188916}
itemPriceText: {fileID: 3533247489785110134}
closeButton: {fileID: 0}
+157 -5
View File
@@ -1899,7 +1899,7 @@ RectTransform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 21, y: -22.97}
m_AnchoredPosition: {x: 21, y: -0}
m_SizeDelta: {x: 472.5032, y: 0}
m_Pivot: {x: 0, y: 0.5}
--- !u!222 &2043307214318211948
@@ -1930,7 +1930,7 @@ MonoBehaviour:
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_text: aaa
m_text:
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 369c2e14814cc9a4b8e3ad4e37769134, type: 2}
m_sharedMaterial: {fileID: 9092487103257209053, guid: 369c2e14814cc9a4b8e3ad4e37769134, type: 2}
@@ -4442,6 +4442,7 @@ RectTransform:
m_Children:
- {fileID: 9106031791145292554}
- {fileID: 1001152567372181051}
- {fileID: 1333165094145940333}
m_Father: {fileID: 5834405183358786743}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
@@ -7163,6 +7164,157 @@ MonoBehaviour:
m_hasFontAssetChanged: 0
m_baseMaterial: {fileID: 0}
m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
--- !u!1 &5371522206622176611
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1333165094145940333}
- component: {fileID: 7370426642012978972}
- component: {fileID: 37862130938576806}
- component: {fileID: 9183113038478938762}
m_Layer: 5
m_Name: Text (TMP) (2)
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &1333165094145940333
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5371522206622176611}
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: 7205431771786927886}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 21, y: -57.98}
m_SizeDelta: {x: 465.7476, y: 0}
m_Pivot: {x: 0, y: 0.5}
--- !u!222 &7370426642012978972
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5371522206622176611}
m_CullTransparentMesh: 1
--- !u!114 &37862130938576806
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5371522206622176611}
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: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_text:
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 369c2e14814cc9a4b8e3ad4e37769134, type: 2}
m_sharedMaterial: {fileID: 9092487103257209053, guid: 369c2e14814cc9a4b8e3ad4e37769134, 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: 1
m_VerticalAlignment: 512
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!114 &9183113038478938762
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5371522206622176611}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 3245ec927659c4140ac4f8d17403cc18, type: 3}
m_Name:
m_EditorClassIdentifier:
m_HorizontalFit: 0
m_VerticalFit: 2
--- !u!1 &5460797107099608431
GameObject:
m_ObjectHideFlags: 0
@@ -8046,13 +8198,13 @@ MonoBehaviour:
hideDetailOnStart: 1
nameText:
legacy: {fileID: 0}
tmp: {fileID: 7082730707602873357}
tmp: {fileID: 0}
descriptionText:
legacy: {fileID: 0}
tmp: {fileID: 6020258894941961325}
extendedDescText:
legacy: {fileID: 0}
tmp: {fileID: 0}
tmp: {fileID: 37862130938576806}
equipButton: {fileID: 472698755110594484}
dropButton: {fileID: 540159372834342487}
autoRefresh: 1
@@ -11506,7 +11658,7 @@ RectTransform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 21, y: -68.909996}
m_AnchoredPosition: {x: 21, y: -28.989998}
m_SizeDelta: {x: 465.7476, y: 0}
m_Pivot: {x: 0, y: 0.5}
--- !u!222 &5347950336050242333
+9 -9
View File
@@ -1943,9 +1943,9 @@ RectTransform:
- {fileID: 4504331075840543341}
m_Father: {fileID: 1361524257611413148}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 108.9562, y: -31.05}
m_SizeDelta: {x: 179.9124, y: 68.0217}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &8804506040386004496
@@ -5768,9 +5768,9 @@ RectTransform:
- {fileID: 2027606699309904338}
m_Father: {fileID: 1361524257611413148}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 468.781, y: -31.05}
m_SizeDelta: {x: 179.9124, y: 68.0217}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &6741821173640675138
@@ -18122,9 +18122,9 @@ RectTransform:
- {fileID: 911293677621153352}
m_Father: {fileID: 1361524257611413148}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 288.8686, y: -31.05}
m_SizeDelta: {x: 179.9124, y: 68.0217}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &5623009994815814977
+70 -19
View File
@@ -54,35 +54,74 @@ namespace BrewMonster
protected bool LoadANSIStrings(string resourceName)
{
TextAsset textAsset = Resources.Load<TextAsset>(resourceName);
if (textAsset == null)
try
{
Debug.LogError($"[CECStringTab] Resource not found: {resourceName}");
// If a real file path is provided (e.g. StreamingAssets), read directly from disk.
// 如果提供的是实际文件路径(例如 StreamingAssets),则直接从磁盘读取。
if (File.Exists(resourceName))
{
// ANSI tables are in CP936 in original PW; keep using CP936 decoder.
// 原版完美世界的ANSI表是CP936编码,这里保持一致。
byte[] bytes = File.ReadAllBytes(resourceName);
string content = ByteToStringUtils.ByteArrayToCP936String(bytes);
using var srFile = new StringReader(content);
return ParseIntoDict(srFile, isWide: false);
}
// Fallback to Resources (old behaviour).
// 回退到 Resources 加载(旧行为)。
TextAsset textAsset = Resources.Load<TextAsset>(resourceName);
if (textAsset == null)
{
Debug.LogError($"[CECStringTab] Resource not found: {resourceName}");
return false;
}
string resContent = ByteToStringUtils.ByteArrayToCP936String(textAsset.bytes);
using var srRes = new StringReader(resContent);
return ParseIntoDict(srRes, isWide: false);
}
catch (Exception e)
{
Debug.LogError($"[CECStringTab] LoadANSIStrings failed for '{resourceName}': {e}");
return false;
}
// Giải mã bytes -> string (ANSI: dùng Encoding.Default)
string content = ByteToStringUtils.ByteArrayToCP936String(textAsset.bytes);
using var sr = new StringReader(content);
return ParseIntoDict(sr, isWide: false);
}
protected bool LoadWideStrings(string resourceName)
{
TextAsset textAsset = Resources.Load<TextAsset>(resourceName);
if (textAsset == null)
try
{
Debug.LogError($"[CECStringTab] Resource not found: {resourceName}");
// Support absolute / relative filesystem paths (e.g. StreamingAssets/configs/*.txt)
// 支持文件系统路径(例如 StreamingAssets/configs/*.txt
if (File.Exists(resourceName))
{
// String tables we ship in StreamingAssets are saved as UTF-8.
// 我们放在 StreamingAssets 里的字符串表保存为 UTF-8。
string content = File.ReadAllText(resourceName, Encoding.UTF8);
using var srFile = new StringReader(content);
return ParseIntoDict(srFile, isWide: true);
}
// Fallback to Resources-based loading (old behaviour)
// 回退到基于 Resources 的加载(旧行为)
TextAsset textAsset = Resources.Load<TextAsset>(resourceName);
if (textAsset == null)
{
Debug.LogError($"[CECStringTab] Resource not found: {resourceName}");
return false;
}
// Unity TextAsset.text is already UTF-8 decoded.
string resContent = textAsset.text;
using var srRes = new StringReader(resContent);
return ParseIntoDict(srRes, isWide: true);
}
catch (Exception e)
{
Debug.LogError($"[CECStringTab] LoadWideStrings failed for '{resourceName}': {e}");
return false;
}
// Unity TextAsset mặc định đã decode text UTF8 -> textAsset.text
// nhưng để chắc chắn BOM/Unicode thì đọc từ bytes
string content;
content = textAsset.text;
using var sr = new StringReader(content);
return ParseIntoDict(sr, isWide: true);
}
private static Encoding DetectEncoding(byte[] bom)
@@ -183,6 +222,18 @@ namespace BrewMonster
private void PutString(int id, string value, bool isWide)
{
if (string.IsNullOrEmpty(value))
return;
// Many PW string tables wrap the payload in double quotes, e.g.:
// 12345 "^ffcb4aSome text\rMore text"
// Strip a single leading/trailing quote pair to avoid showing raw quotes in UI.
// 许多字符串表会用双引号包裹内容,这里去掉首尾各一个引号以避免在UI中显示多余的引号。
if (value.Length >= 2 && value[0] == '"' && value[value.Length - 1] == '"')
{
value = value.Substring(1, value.Length - 2);
}
if (isWide) m_WStrTab[id] = value;
else m_AStrTab[id] = value;
}