update skill for chat

This commit is contained in:
CuongNV
2026-04-01 16:47:02 +07:00
parent 9e81951f12
commit a82337f617
2 changed files with 102 additions and 7 deletions
@@ -0,0 +1,26 @@
---
name: C# Chat System Formatting
description: Hướng dẫn viết code C# cho hệ thống chat dùng AUIDialog.FormatPrintf và pGameUI.GetStringFromTable.
---
# C# Chat System Formatting
Skill này cung cấp hướng dẫn khi viết code C# cho hệ thống chat (ví dụ như khi chuyển đổi code từ C++ sang C# trong Unity).
## Quy tắc lấy chuỗi và format
Khi cần lấy một chuỗi từ bảng ngôn ngữ (Table) và format chuỗi đó với các tham số (thay thế cho %s, %d,...), hãy sử dụng kết hợp `pGameUI.GetStringFromTable``AUIDialog.FormatPrintf`.
Trong phiên bản C++ gốc (ví dụ trong `EC_GameUIMan.cpp`), việc lấy chuỗi thường dùng `GetStringFromTable` và format bằng `swprintf` hoặc các hàm tương tự. Ở phiên bản C#, chúng ta thực hiện như sau:
```csharp
// Ví dụ Table 9343: chuỗi dạng "Thời gian đăng nhập trước: %s"
// AUIDialog.FormatPrintf sẽ thực hiện kết hợp giữa pGameUI.GetStringFromTable(9343) và chuỗi timeStr
string textTime = AUIDialog.FormatPrintf(pGameUI.GetStringFromTable(9343), timeStr);
```
## Các bước thực hiện
1. Đảm bảo đã lấy được referent tới `pGameUI`, ví dụ qua `CECUIManager.Instance?.GetInGameUIMan()`.
2. Lấy chuỗi format gốc từ bảng ngôn ngữ: `pGameUI.GetStringFromTable(ID)`.
3. Dùng `AUIDialog.FormatPrintf` để thay thế các placeholder (như `%s`, `%d`) trong chuỗi lấy được bằng các giá trị thực tế.
4. Gửi chuỗi đã được format đến hệ thống chat, ví dụ qua `EventBus.Publish(new GameSession.ChatMessageEvent {...})`.
@@ -66,16 +66,23 @@ ACString str;
str.Format(GetStringFromTable(11326), needMoney, needSp);
```
**C# (string)**:
**C#**:
**Method A: Using `AUIDialog.FormatPrintf` (Recommended for Legacy Strings)**
If the string from the table uses C++ format specifiers (`%d`, `%s`, `%f`), `AUIDialog.FormatPrintf` handles them natively without requiring string conversion.
```csharp
string confirmMessage = AUIDialog.FormatPrintf(GetStringFromTable(11326), needMoney, needSp);
```
**Method B: Using `string.Format` (Standard C#)**
If the string table has been updated to use C# format specifiers (`{0}`, `{1}`), you can use the built-in `string.Format`.
```csharp
string confirmMessage = string.Format(GetStringFromTable(11326), needMoney, needSp);
```
**Notes**:
- C++'s `ACString::Format()` is a member function
- C#'s `string.Format()` is a static function
- Both use the same placeholder syntax: `%d` (C++) or `{0}`, `{1}` (C#)
- If your string table uses C++ format specifiers, you may need to convert them:
- C++'s `ACString::Format()` is a member function.
- C#'s `string.Format()` is a static function but only understands `{0}`, `{1}` syntax.
- If your string table still uses C++ format specifiers (`%d`, `%s`, `%f`), **you should use `AUIDialog.FormatPrintf`**. Otherwise, you would need to manually convert the placeholders in the string table:
- `%d``{0}`, `{1}` for integers
- `%s``{0}`, `{1}` for strings
- `%f``{0}`, `{1}` for floats
@@ -346,11 +353,66 @@ if (spOK) {
}
```
### Example 4: Clickable Text Links (TextMeshPro)
**C++ (Rich Text and Actions)**:
In C++, player names or interactive elements (like items) in chat are often wrapped with custom delimiters like `&PlayerName&` to be parsed later for coloring and clicking. The click actions are handled by complex UI dialogue components.
**C# (Unity TextMeshPro `<link>` tag)**:
In Unity's TextMeshPro, we can use the standard `<link>` tag to define an interactive region of text. By parsing the legacy `&PlayerName&` format using Regex, we can inject a `<link>` tag that Unity's event system can interact with.
1. **Format the string with Regex replacing delimiters (e.g., in `EC_GameUIMan.cs`)**:
```csharp
if (parsedMsg.Contains("&"))
{
// Convert &PlayerName& into <color=#HexColor><u><link="PlayerName">PlayerName</link></u></color>
parsedMsg = System.Text.RegularExpressions.Regex.Replace(
parsedMsg,
@"&([^&]+)&",
$"<color=#{colorHex}><u><link=\"$1\">$1</link></u></color>"
);
}
```
2. **Handle the Click Event via Unity EventSystems (e.g., in `ChatMessageView.cs`)**:
The UI View script attached to the TextMeshPro text must implement `IPointerClickHandler` to intercept pointer clicks on the `<link>` markup.
```csharp
using UnityEngine.EventSystems;
using TMPro;
public class ChatMessageView : MonoBehaviour, IPointerClickHandler
{
public EC_UIUtility.TextOutlet messageText;
public void OnPointerClick(PointerEventData eventData)
{
if (messageText == null || messageText.tmp == null) return;
// Check if the pointer click intersects with any <link> tag boundary
int linkIndex = TMP_TextUtilities.FindIntersectingLink(messageText.tmp, eventData.position, eventData.pressEventCamera);
if (linkIndex != -1)
{
// Retrieve the link ID (which we dynamically set to the player's name)
TMP_LinkInfo linkInfo = messageText.tmp.textInfo.linkInfo[linkIndex];
string linkId = linkInfo.GetLinkID();
if (!string.IsNullOrEmpty(linkId))
{
// Trigger logic, e.g., Whisper the player!
EventBus.Publish(new WhisperPlayerEvent(linkId));
}
}
}
}
```
**Note for specific PW Dialogues**: Legacy classes (such as `DlgNameLink.cs`) may implement a customized command pattern (e.g., `LinkCommand`, `MoveToLinkCommand` alongside `StyledTaskTraceText`) to process complex hyperlink commands recursively. However, for completely rewritten or standalone UI systems like standard Chat or logging panels, utilizing TextMeshPro's native `TMP_TextUtilities.FindIntersectingLink` combined with `IPointerClickHandler` is significantly faster, lightweight, and standard for Unity development.
---
## Common Pitfalls
### ❌ Wrong: Using C++ format specifiers in C#
### ❌ Wrong: Using C++ format specifiers with `string.Format`
```csharp
// This won't work if the string uses C++ format specifiers
@@ -358,8 +420,15 @@ string template = "Cost: %d gold"; // C++ style
string message = string.Format(template, 1000); // Error!
```
### ✅ Correct: Convert to C# format
### ✅ Correct: Use `AUIDialog.FormatPrintf` OR convert to C# format
**Option 1: Use AUIDialog.FormatPrintf for C++ styles:**
```csharp
string template = "Cost: %d gold"; // C++ style
string message = AUIDialog.FormatPrintf(template, 1000); // "Cost: 1000 gold"
```
**Option 2: Convert to C# format strings:**
```csharp
string template = "Cost: {0} gold"; // C# style
string message = string.Format(template, 1000); // "Cost: 1000 gold"