Merge branch 'develop' into feature/elseplayer
# Conflicts: # Assets/PerfectWorld/Scripts/Network/CSNetwork/GPDataType.cs # Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs # Assets/PerfectWorld/Scripts/UI/GamePlay/EC_GameUIMan.cs
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
# EditorConfig for Unity Project
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.cs]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
end_of_line = crlf
|
||||
|
||||
# Unity Editor Only Usage Analyzer
|
||||
# ERROR (màu đỏ) khi sử dụng code trong #if UNITY_EDITOR từ code không có directive
|
||||
dotnet_diagnostic.UNITY_EDITOR_ONLY_USAGE.severity = error
|
||||
|
||||
# Các rule khác cho Unity
|
||||
dotnet_analyzer_diagnostic.category-Unity.severity = warning
|
||||
|
||||
[*.{asmdef,asmref}]
|
||||
indent_size = 2
|
||||
|
||||
[*.{json,md}]
|
||||
indent_size = 2
|
||||
+1
-1
@@ -102,4 +102,4 @@ InitTestScene*.unity*
|
||||
.idea
|
||||
|
||||
# AI Context
|
||||
claude.md
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d6389bfe41ba69e42991434d87c1319f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,157 @@
|
||||
#if UNITY_EDITOR
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
/// <summary>
|
||||
/// Tự động thêm UnityEditorOnlyAnalyzer vào các .csproj files khi Unity generate chúng
|
||||
/// Analyzer phải ở project root, KHÔNG trong Assets/ để tránh Unity load như runtime assembly
|
||||
/// </summary>
|
||||
public class AddAnalyzerPostprocessor : AssetPostprocessor
|
||||
{
|
||||
private static string GetAnalyzerPath()
|
||||
{
|
||||
string projectRoot = Path.GetDirectoryName(Application.dataPath);
|
||||
string analyzerPath = Path.Combine(projectRoot, "UnityEditorOnlyAnalyzer/bin/Release/netstandard2.0/UnityEditorOnlyAnalyzer.dll");
|
||||
return Path.GetFullPath(analyzerPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unity gọi method này cho từng .csproj file được generate
|
||||
/// Return content đã được modify - Unity sẽ ghi content này vào file
|
||||
/// </summary>
|
||||
public static string OnGeneratedCSProject(string path, string content)
|
||||
{
|
||||
string fileName = Path.GetFileName(path);
|
||||
|
||||
// Chỉ thêm vào các runtime assemblies, không thêm vào Editor assemblies
|
||||
if (fileName != "Assembly-CSharp.csproj" && fileName != "Assembly-CSharp-firstpass.csproj")
|
||||
{
|
||||
return content;
|
||||
}
|
||||
|
||||
// Kiểm tra xem đã có analyzer chưa
|
||||
if (content.Contains("UnityEditorOnlyAnalyzer.dll"))
|
||||
{
|
||||
return content;
|
||||
}
|
||||
|
||||
string analyzerPath = GetAnalyzerPath();
|
||||
|
||||
// Kiểm tra analyzer có tồn tại không
|
||||
if (!File.Exists(analyzerPath))
|
||||
{
|
||||
Debug.LogWarning($"[UnityEditorOnlyAnalyzer] Analyzer not found at {analyzerPath}. " +
|
||||
"Please build the analyzer first: cd UnityEditorOnlyAnalyzer && dotnet build -c Release");
|
||||
return content;
|
||||
}
|
||||
|
||||
// Bug fix 1: Tìm "</Project>" không có dấu cách ở đầu
|
||||
int lastProjectTag = content.LastIndexOf("</Project>");
|
||||
if (lastProjectTag < 0)
|
||||
{
|
||||
Debug.LogWarning($"[UnityEditorOnlyAnalyzer] Could not find </Project> tag in {fileName}");
|
||||
return content;
|
||||
}
|
||||
|
||||
// Bug fix 2: Không double-escape backslash trong XML - XML tự động escape
|
||||
// Chỉ cần đảm bảo path có backslash đúng format Windows
|
||||
string analyzerInclude = $@" <ItemGroup>
|
||||
<Analyzer Include=""{analyzerPath}"" />
|
||||
</ItemGroup>
|
||||
</Project>";
|
||||
|
||||
string modifiedContent = content.Substring(0, lastProjectTag) + analyzerInclude;
|
||||
Debug.Log($"[UnityEditorOnlyAnalyzer] ✅ Added analyzer to {fileName}");
|
||||
|
||||
return modifiedContent;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Public method để menu item có thể gọi thủ công
|
||||
/// </summary>
|
||||
public static void AddAnalyzerToProjects()
|
||||
{
|
||||
// Trigger Unity regenerate .csproj files để OnGeneratedCSProject được gọi
|
||||
UnityEditorInternal.InternalEditorUtility.OpenFileAtLineExternal("", 0);
|
||||
AssetDatabase.Refresh();
|
||||
|
||||
// Cũng gọi fallback method để đảm bảo
|
||||
OnGeneratedCSProjectFiles();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Backup method: Unity gọi method này sau khi generate tất cả .csproj files
|
||||
/// Chỉ dùng nếu OnGeneratedCSProject không hoạt động
|
||||
/// </summary>
|
||||
private static void OnGeneratedCSProjectFiles()
|
||||
{
|
||||
// Fallback: patch files sau khi đã được generate
|
||||
string projectRoot = Path.GetDirectoryName(Application.dataPath);
|
||||
string analyzerPath = GetAnalyzerPath();
|
||||
|
||||
if (!File.Exists(analyzerPath))
|
||||
{
|
||||
Debug.LogWarning($"[UnityEditorOnlyAnalyzer] Analyzer not found at {analyzerPath}. " +
|
||||
"Please build the analyzer first: cd UnityEditorOnlyAnalyzer && dotnet build -c Release");
|
||||
return;
|
||||
}
|
||||
|
||||
string[] csprojFiles = Directory.GetFiles(projectRoot, "*.csproj", SearchOption.TopDirectoryOnly);
|
||||
bool anyModified = false;
|
||||
|
||||
foreach (string csprojFile in csprojFiles)
|
||||
{
|
||||
string fileName = Path.GetFileName(csprojFile);
|
||||
if (fileName == "Assembly-CSharp.csproj" || fileName == "Assembly-CSharp-firstpass.csproj")
|
||||
{
|
||||
string content = File.ReadAllText(csprojFile);
|
||||
|
||||
if (!content.Contains("UnityEditorOnlyAnalyzer.dll"))
|
||||
{
|
||||
// Bug fix: Tìm "</Project>" không có dấu cách
|
||||
int lastProjectTag = content.LastIndexOf("</Project>");
|
||||
if (lastProjectTag >= 0)
|
||||
{
|
||||
string analyzerInclude = $@" <ItemGroup>
|
||||
<Analyzer Include=""{analyzerPath}"" />
|
||||
</ItemGroup>
|
||||
</Project>";
|
||||
|
||||
content = content.Substring(0, lastProjectTag) + analyzerInclude;
|
||||
File.WriteAllText(csprojFile, content);
|
||||
anyModified = true;
|
||||
Debug.Log($"[UnityEditorOnlyAnalyzer] ✅ Added analyzer to {fileName} (fallback method)");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (anyModified)
|
||||
{
|
||||
Debug.Log("[UnityEditorOnlyAnalyzer] ✅ Analyzer added to .csproj files! Please reload your IDE.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Menu item để trigger thủ công việc thêm analyzer
|
||||
/// </summary>
|
||||
public class UnityEditorOnlyAnalyzerMenu
|
||||
{
|
||||
[MenuItem("Tools/Unity Editor Only Analyzer/Add Analyzer to Projects")]
|
||||
public static void AddAnalyzerManually()
|
||||
{
|
||||
AddAnalyzerPostprocessor.AddAnalyzerToProjects();
|
||||
}
|
||||
|
||||
[MenuItem("Tools/Unity Editor Only Analyzer/Regenerate Project Files")]
|
||||
public static void RegenerateProjectFiles()
|
||||
{
|
||||
// Trigger Unity regenerate .csproj files
|
||||
UnityEditorInternal.InternalEditorUtility.OpenFileAtLineExternal("", 0);
|
||||
AssetDatabase.Refresh();
|
||||
Debug.Log("[UnityEditorOnlyAnalyzer] Project files regeneration triggered. Check Console for analyzer messages.");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f4597c784f3bab545bcae652cd8541d9
|
||||
@@ -50,7 +50,7 @@ ParticleSystem:
|
||||
ringBufferMode: 0
|
||||
ringBufferLoopRange: {x: 0, y: 1}
|
||||
emitterVelocityMode: 1
|
||||
looping: 1
|
||||
looping: 0
|
||||
prewarm: 0
|
||||
playOnAwake: 1
|
||||
useUnscaledTime: 0
|
||||
@@ -4883,7 +4883,7 @@ ParticleSystem:
|
||||
ringBufferMode: 0
|
||||
ringBufferLoopRange: {x: 0, y: 1}
|
||||
emitterVelocityMode: 1
|
||||
looping: 1
|
||||
looping: 0
|
||||
prewarm: 0
|
||||
playOnAwake: 1
|
||||
useUnscaledTime: 0
|
||||
@@ -9774,7 +9774,7 @@ ParticleSystem:
|
||||
ringBufferMode: 0
|
||||
ringBufferLoopRange: {x: 0, y: 1}
|
||||
emitterVelocityMode: 1
|
||||
looping: 1
|
||||
looping: 0
|
||||
prewarm: 0
|
||||
playOnAwake: 1
|
||||
useUnscaledTime: 0
|
||||
|
||||
@@ -26,13 +26,13 @@ Transform:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7683706845393114874}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalRotation: {x: 0, y: 0.7071068, z: 0, w: 0.7071068}
|
||||
m_LocalPosition: {x: 0.365, y: -0.002, z: -0.002}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 90, z: 0}
|
||||
--- !u!33 &2133889840078759797
|
||||
MeshFilter:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
@@ -26,13 +26,13 @@ Transform:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1062321236472470772}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalRotation: {x: 0, y: 0.7071068, z: 0, w: 0.7071068}
|
||||
m_LocalPosition: {x: 0.365, y: -0.002, z: -0.002}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 90, z: 0}
|
||||
--- !u!33 &3655013707296471465
|
||||
MeshFilter:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
@@ -26,13 +26,13 @@ Transform:
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3330708356810309571}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalRotation: {x: 0, y: 0.7071068, z: 0, w: 0.7071068}
|
||||
m_LocalPosition: {x: 0.388, y: -0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 90, z: 0}
|
||||
--- !u!33 &666975227472843166
|
||||
MeshFilter:
|
||||
m_ObjectHideFlags: 0
|
||||
|
||||
@@ -620,7 +620,7 @@ GameObject:
|
||||
- component: {fileID: 8250962023850685786}
|
||||
- component: {fileID: 7766051278568089760}
|
||||
m_Layer: 5
|
||||
m_Name: ButtonOk
|
||||
m_Name: ButtonYes
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
@@ -927,7 +927,7 @@ MonoBehaviour:
|
||||
imageProgress: {fileID: 0}
|
||||
titleText: {fileID: 5031655611580643013}
|
||||
messageText: {fileID: 7448521238108099750}
|
||||
okButton: {fileID: 7766051278568089760}
|
||||
_yesButton: {fileID: 7766051278568089760}
|
||||
_noButton: {fileID: 7010901635634620631}
|
||||
_closeButton: {fileID: 482550456836939169}
|
||||
--- !u!1 &5664175764923475105
|
||||
|
||||
@@ -509,7 +509,7 @@ MonoBehaviour:
|
||||
m_PressedTrigger: Pressed
|
||||
m_SelectedTrigger: Selected
|
||||
m_DisabledTrigger: Disabled
|
||||
m_Interactable: 0
|
||||
m_Interactable: 1
|
||||
m_TargetGraphic: {fileID: 3492245093881047436}
|
||||
m_OnClick:
|
||||
m_PersistentCalls:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:0dd087950038db412bdd07208e3e3dc407a2da4ebecc8fbd49ad2246197aecdb
|
||||
size 314447
|
||||
oid sha256:47ff1377fe87865c1bdada70b8f6fee638a20879f70b84b40d62fb978aee203e
|
||||
size 325051
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:8c97935b0995b6688065daf1645d0f26e8b964dd5bf92b6cd66c37c5f97b5586
|
||||
size 104077
|
||||
oid sha256:4b854fd255462f21d3b44d27526138345930a673f5c74cd2c1ca261ebd834048
|
||||
size 106212
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2f24d310ca7b52342ab8380890ad8c0b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,239 @@
|
||||
using System.Collections.Generic;
|
||||
using BrewMonster.Network;
|
||||
using BrewMonster.Scripts.Managers;
|
||||
using BrewMonster.UI;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BrewMonster.Scripts.Chat
|
||||
{
|
||||
public class CECPateText
|
||||
{
|
||||
protected List<string> m_aTextStrs;
|
||||
//protected List<> m_aEmotions;
|
||||
//protected List<ITEM, ITEM> m_aItems;
|
||||
//protected EditBoxItemSet m_ItemSet;
|
||||
|
||||
// Text item type
|
||||
public enum ETextType
|
||||
{
|
||||
TYPE_TEXT = 0,
|
||||
TYPE_EMOTION,
|
||||
TYPE_BOOTHNAME,
|
||||
}
|
||||
|
||||
// Text item
|
||||
public struct ITEM
|
||||
{
|
||||
public int iType; // Text type
|
||||
public int iIndex; // Index of item
|
||||
public int iExtX; // Extent
|
||||
public int iExtY;
|
||||
|
||||
public int iLine;
|
||||
|
||||
//A3DCOLOR clItem;
|
||||
public Color clItem;
|
||||
};
|
||||
|
||||
public int SetText(string szText,
|
||||
bool bIncEmotion,
|
||||
out string pstrTextConverted,
|
||||
bool bEllipsis = true,
|
||||
EC_IvtrItem pIvtrItem = null)
|
||||
{
|
||||
// Clear old content
|
||||
Clear();
|
||||
|
||||
pstrTextConverted = null;
|
||||
|
||||
if (string.IsNullOrEmpty(szText))
|
||||
return 0;
|
||||
|
||||
CECGameUIMan pGameUI =
|
||||
EC_Game.GetGameRun().GetUIManager().GetInGameUIMan();
|
||||
|
||||
/*string str = pGameUI.AUI_FilterEditboxItem(
|
||||
szText,
|
||||
CECGameUIMan.AUI_EditboxItemMaskFilter(1 << (int)enumEICoord)
|
||||
);
|
||||
|
||||
string strName;
|
||||
A3DCOLOR clrName;
|
||||
|
||||
pGameUI.TransformNameColor(pIvtrItem, out strName, out clrName);
|
||||
|
||||
str = UnmarshalEditBoxText(str, m_ItemsSet, 0, strName, clrName);
|
||||
|
||||
szText = str;
|
||||
pstrTextConverted = str;
|
||||
|
||||
int iAddedChar = 0;
|
||||
|
||||
if (!bIncEmotion)
|
||||
{
|
||||
int iLen = szText.Length;
|
||||
|
||||
if (iLen > m_iMaxLineLen)
|
||||
{
|
||||
string sub = szText.Substring(0, m_iMaxLineLen);
|
||||
|
||||
if (bEllipsis)
|
||||
sub += "...";
|
||||
|
||||
CreateTextItem(sub, -1, 0);
|
||||
|
||||
iAddedChar = m_iMaxLineLen;
|
||||
}
|
||||
else
|
||||
{
|
||||
CreateTextItem(szText, -1, 0);
|
||||
iAddedChar = iLen;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int i = 0;
|
||||
int iStart = 0;
|
||||
int iEnd = 0;
|
||||
int iLenCnt = 0;
|
||||
|
||||
bool bTooLong = false;
|
||||
int iLine = 0;
|
||||
|
||||
while (i < szText.Length)
|
||||
{
|
||||
char ch = szText[i];
|
||||
|
||||
if (IsEditboxItemCode(ch))
|
||||
{
|
||||
if (iEnd > iStart)
|
||||
CreateTextItem(szText.Substring(iStart, iEnd - iStart), -1, iLine);
|
||||
|
||||
EditBoxItemBase pItem = m_ItemsSet.GetItemByChar(ch);
|
||||
|
||||
if (pItem != null)
|
||||
{
|
||||
if (pItem.GetType() == enumEIEmotion)
|
||||
{
|
||||
int nSet = 0;
|
||||
int nIndex = 0;
|
||||
|
||||
UnmarshalEmotionInfo(pItem.GetInfo(), out nSet, out nIndex);
|
||||
|
||||
CreateEmotionItem(nSet, nIndex, iLine);
|
||||
|
||||
iLenCnt += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
string szName = pItem.GetName();
|
||||
|
||||
CreateTextItem(szName, -1, iLine, pItem.GetColor());
|
||||
|
||||
iLenCnt += szName.Length;
|
||||
}
|
||||
}
|
||||
|
||||
i++;
|
||||
iStart = i;
|
||||
iEnd = i;
|
||||
|
||||
goto CheckLength;
|
||||
}
|
||||
|
||||
iEnd++;
|
||||
i++;
|
||||
iLenCnt++;
|
||||
|
||||
CheckLength:
|
||||
|
||||
if (iLenCnt > m_iMaxLineLen)
|
||||
{
|
||||
if (iLine + 1 >= m_iMaxLines)
|
||||
{
|
||||
bTooLong = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (iEnd > iStart)
|
||||
CreateTextItem(szText.Substring(iStart, iEnd - iStart), -1, iLine);
|
||||
|
||||
iStart = i;
|
||||
iLine++;
|
||||
iLenCnt = 0;
|
||||
}
|
||||
}
|
||||
|
||||
iAddedChar = i;
|
||||
|
||||
if (iEnd > iStart)
|
||||
{
|
||||
if (bTooLong)
|
||||
{
|
||||
string strEnd = szText.Substring(iStart, iEnd - iStart);
|
||||
|
||||
if (bEllipsis)
|
||||
strEnd += "...";
|
||||
|
||||
CreateTextItem(strEnd, -1, iLine);
|
||||
}
|
||||
else
|
||||
{
|
||||
CreateTextItem(szText.Substring(iStart, iEnd - iStart), -1, iLine);
|
||||
}
|
||||
}
|
||||
else if (bTooLong)
|
||||
{
|
||||
if (bEllipsis)
|
||||
CreateTextItem("...", -1, iLine);
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate extent
|
||||
|
||||
m_iExtX = 0;
|
||||
m_iExtY = 0;
|
||||
|
||||
int iLineExtX = 0;
|
||||
int iLastLine = 0;
|
||||
|
||||
for (int i = 0; i < m_aItems.GetSize(); i++)
|
||||
{
|
||||
ITEM item = m_aItems[i];
|
||||
|
||||
if (item.iLine != iLastLine)
|
||||
{
|
||||
iLastLine = item.iLine;
|
||||
|
||||
if (m_iExtX < iLineExtX)
|
||||
m_iExtX = iLineExtX;
|
||||
|
||||
iLineExtX = item.iExtX;
|
||||
}
|
||||
else
|
||||
{
|
||||
iLineExtX += item.iExtX;
|
||||
}
|
||||
|
||||
if (m_iExtY < item.iExtY)
|
||||
m_iExtY = item.iExtY;
|
||||
}
|
||||
|
||||
m_iLines = iLastLine + 1;
|
||||
|
||||
if (m_iExtX < iLineExtX)
|
||||
m_iExtX = iLineExtX;*/
|
||||
//return iAddedChar;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
m_aTextStrs.Clear();
|
||||
/*m_aEmotions.Clear();
|
||||
m_aItems.Clear();
|
||||
m_ItemsSet.Clear();*/
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f6eb4b59e25704044a88b0055abb0e99
|
||||
@@ -0,0 +1,26 @@
|
||||
using BrewMonster.Scripts.Managers;
|
||||
using CSNetwork;
|
||||
|
||||
namespace BrewMonster.Scripts.Chat
|
||||
{
|
||||
public abstract partial class CECPlayer : CECObject
|
||||
{
|
||||
private CECPateText m_pPateLastWords1;
|
||||
private CECPateText m_pPateLastWords2;
|
||||
private CECCounter m_strLastSayCnt;
|
||||
|
||||
// Set last said words
|
||||
public void SetLastSaidWords(string szWords, int nEmotionSet, EC_IvtrItem pItem)
|
||||
{
|
||||
if (m_pPateLastWords1 == null || m_pPateLastWords2 == null)
|
||||
return;
|
||||
|
||||
string str = AUICommon.FilterEmotionSet(szWords, nEmotionSet);
|
||||
szWords = str;
|
||||
|
||||
m_pPateLastWords1.SetText(szWords, true , out var newStr,true, pItem);
|
||||
m_pPateLastWords2.Clear();
|
||||
m_strLastSayCnt.Reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2c109cbbaf8d4ce19a93990a5f5883f6
|
||||
timeCreated: 1772699598
|
||||
@@ -0,0 +1,254 @@
|
||||
using System;
|
||||
using System.Buffers.Binary;
|
||||
using System.Runtime.InteropServices;
|
||||
using BrewMonster.Network;
|
||||
using BrewMonster.Scripts.Managers;
|
||||
using CSNetwork;
|
||||
using CSNetwork.GPDataType;
|
||||
|
||||
namespace BrewMonster.Scripts.Chat
|
||||
{
|
||||
struct chat_policy_parameter
|
||||
{
|
||||
short cmd_id;
|
||||
|
||||
int parameter_mask;
|
||||
//if(parameter_mask & CHAT_PARAMETER_ROLEID) 1 int insert here
|
||||
//if(parameter_mask & CHAT_PARAMETER_LOCALVAL0) 1 int insert here
|
||||
//if(parameter_m ask & CHAT_PARAMETER_LOCALVAL1) 1 int insert here
|
||||
//if(parameter_mask & CHAT_PARAMETER_LOCALVAL2) 1 int insert here
|
||||
};
|
||||
|
||||
public struct chat_item_base
|
||||
{
|
||||
public short cmd_id;
|
||||
}
|
||||
|
||||
public struct chat_equip_item
|
||||
{
|
||||
public short cmd_id;
|
||||
public char where;
|
||||
public short index;
|
||||
}
|
||||
|
||||
struct chat_generalcard_collection
|
||||
{
|
||||
public short cmd_id;
|
||||
public int card_id;
|
||||
};
|
||||
|
||||
public static class CHAT_S2C
|
||||
{
|
||||
public struct chat_equip_item
|
||||
{
|
||||
public short cmd_id;
|
||||
public int type;
|
||||
public int expire_date;
|
||||
public int proc_type;
|
||||
public ushort content_length;
|
||||
public byte[] content;
|
||||
|
||||
public int LenghtHeader()
|
||||
{
|
||||
return Marshal.SizeOf<short>() + (Marshal.SizeOf<int>() * 3) + Marshal.SizeOf<ushort>();
|
||||
}
|
||||
}
|
||||
|
||||
public enum EChatS2CCommand : short
|
||||
{
|
||||
CHAT_EQUIP_ITEM,
|
||||
CHAT_GENERALCARD_COLLECTION,
|
||||
CHAT_POLICYCHAT_PARAMETER,
|
||||
}
|
||||
|
||||
public static EC_IvtrItem CreateChatItem(Octets data)
|
||||
{
|
||||
EC_IvtrItem pIvtrItem = null;
|
||||
if (data.Size > 0)
|
||||
{
|
||||
chat_item_base pInfo = GPDataTypeHelper.FromBytes<chat_item_base>(data.ByteArray);
|
||||
if (pInfo.cmd_id == (short)EChatS2CCommand.CHAT_EQUIP_ITEM)
|
||||
{
|
||||
chat_equip_item pItemInfo = default;
|
||||
var sz = pItemInfo.LenghtHeader();
|
||||
|
||||
if (data.Size >= sz && sz + pItemInfo.content_length == data.Size)
|
||||
{
|
||||
if (pItemInfo.cmd_id == (short)EChatS2CCommand.CHAT_EQUIP_ITEM)
|
||||
{
|
||||
pIvtrItem = EC_IvtrItem.CreateItem(pItemInfo.type, pItemInfo.expire_date, 1);
|
||||
if (pIvtrItem != null)
|
||||
{
|
||||
pIvtrItem.SetProcType(pItemInfo.proc_type);
|
||||
pIvtrItem.SetItemInfo(pItemInfo.content, pItemInfo.content_length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (pInfo.cmd_id == (short)EChatS2CCommand.CHAT_GENERALCARD_COLLECTION)
|
||||
{
|
||||
chat_generalcard_collection pItemInfo =
|
||||
GPDataTypeHelper.FromBytes<chat_generalcard_collection>(data.ByteArray);
|
||||
if (data.Size > Marshal.SizeOf<chat_generalcard_collection>())
|
||||
{
|
||||
if (pItemInfo.cmd_id == (short)EChatS2CCommand.CHAT_GENERALCARD_COLLECTION)
|
||||
{
|
||||
pIvtrItem = EC_IvtrItem.CreateItem(pItemInfo.card_id, 0, 1);
|
||||
if (pIvtrItem != null)
|
||||
{
|
||||
pIvtrItem.GetDetailDataFromLocal();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pIvtrItem;
|
||||
}
|
||||
|
||||
[System.Flags]
|
||||
public enum ChatParameterMask
|
||||
{
|
||||
CHAT_PARAMETER_ROLEID = 0x00000001,
|
||||
CHAT_PARAMETER_LOCALVAL0 = 0x00000002,
|
||||
CHAT_PARAMETER_LOCALVAL1 = 0x00000004,
|
||||
CHAT_PARAMETER_LOCALVAL2 = 0x00000008
|
||||
}
|
||||
|
||||
public class PolicyChatParameter
|
||||
{
|
||||
public int role_id = -1;
|
||||
public string name = string.Empty;
|
||||
public ChatParameterMask parameter_mask = 0;
|
||||
|
||||
private int localval_0 = -1;
|
||||
private int localval_1 = -1;
|
||||
private int localval_2 = -1;
|
||||
|
||||
EC_Game g_pGame;
|
||||
|
||||
public bool HasRoleID()
|
||||
{
|
||||
return (parameter_mask & ChatParameterMask.CHAT_PARAMETER_ROLEID) != 0;
|
||||
}
|
||||
|
||||
public bool HasLocalValue(int valueNum)
|
||||
{
|
||||
switch (valueNum)
|
||||
{
|
||||
case 0:
|
||||
return (parameter_mask & ChatParameterMask.CHAT_PARAMETER_LOCALVAL0) != 0;
|
||||
case 1:
|
||||
return (parameter_mask & ChatParameterMask.CHAT_PARAMETER_LOCALVAL1) != 0;
|
||||
case 2:
|
||||
return (parameter_mask & ChatParameterMask.CHAT_PARAMETER_LOCALVAL2) != 0;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void GetNameFromServer()
|
||||
{
|
||||
if (role_id != -1 && HasRoleID())
|
||||
{
|
||||
int[] arr = new int[1] { role_id };
|
||||
UnityGameSession.Instance.GameSession.CmdCache.SendGetPlayerBriefInfo(1, arr, 1);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetLocalValue(int localValue, int id)
|
||||
{
|
||||
switch (id)
|
||||
{
|
||||
case 0:
|
||||
localval_0 = localValue;
|
||||
parameter_mask |= ChatParameterMask.CHAT_PARAMETER_LOCALVAL0;
|
||||
break;
|
||||
case 1:
|
||||
localval_1 = localValue;
|
||||
parameter_mask |= ChatParameterMask.CHAT_PARAMETER_LOCALVAL1;
|
||||
break;
|
||||
case 2:
|
||||
localval_2 = localValue;
|
||||
parameter_mask |= ChatParameterMask.CHAT_PARAMETER_LOCALVAL2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetLocalValue(int localVariableID, out int localVariable)
|
||||
{
|
||||
localVariable = -1;
|
||||
|
||||
if (!HasLocalValue(localVariableID))
|
||||
return false;
|
||||
|
||||
switch (localVariableID)
|
||||
{
|
||||
case 0: localVariable = localval_0; break;
|
||||
case 1: localVariable = localval_1; break;
|
||||
case 2: localVariable = localval_2; break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsNameReady()
|
||||
{
|
||||
return HasRoleID() ? !string.IsNullOrEmpty(name) : true;
|
||||
}
|
||||
}
|
||||
|
||||
public static PolicyChatParameter CreatPolicyChatParameter(Octets data)
|
||||
{
|
||||
PolicyChatParameter result = null;
|
||||
if (data.Size >= Marshal.SizeOf(typeof(chat_item_base)))
|
||||
{
|
||||
chat_item_base pInfo = GPDataTypeHelper.FromBytes<chat_item_base>(data.ByteArray);
|
||||
if (pInfo.cmd_id == (short)EChatS2CCommand.CHAT_POLICYCHAT_PARAMETER)
|
||||
{
|
||||
if (data.Size >= Marshal.SizeOf(typeof(chat_policy_parameter)))
|
||||
{
|
||||
Byte pData = GPDataTypeHelper.FromBytes<Byte>(data.ByteArray,
|
||||
Marshal.SizeOf(typeof(chat_policy_parameter)));
|
||||
result = new PolicyChatParameter();
|
||||
// Todo: Check logic
|
||||
result.parameter_mask =
|
||||
GPDataTypeHelper.FromBytes<ChatParameterMask>(data.ByteArray, sizeof(short));
|
||||
/*int paraMask = result.parameter_mask;
|
||||
if (paraMask & ChatParameterMask.CHAT_PARAMETER_ROLEID){
|
||||
if (pData + sizeof(int) > data.end()){
|
||||
return null;
|
||||
}
|
||||
result->SetRoleId(*(int*)pData);
|
||||
pData += sizeof(int);
|
||||
}
|
||||
if (paraMask & ChatParameterMask.CHAT_PARAMETER_LOCALVAL0){
|
||||
if (pData + sizeof(int) > data.end()){
|
||||
return null;
|
||||
}
|
||||
result->SetLocaValue(*(int*)pData, 0);
|
||||
pData += sizeof(int);
|
||||
}
|
||||
if (paraMask & ChatParameterMask.CHAT_PARAMETER_LOCALVAL1){
|
||||
if (pData + sizeof(int) > data.end()){
|
||||
return null;
|
||||
}
|
||||
result.SetLocaValue((int)pData, 1);
|
||||
pData += sizeof(int);
|
||||
}
|
||||
if (paraMask & ChatParameterMask.CHAT_PARAMETER_LOCALVAL2){
|
||||
if (pData + sizeof(int) > data.end()){
|
||||
return null;
|
||||
}
|
||||
// Check logic
|
||||
result.SetLocalValue((int)pData, 2);
|
||||
pData += sizeof(int);
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f7564fa8d29f58c40a1aa0425fc0c046
|
||||
@@ -0,0 +1,243 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using BrewMonster.Network;
|
||||
using BrewMonster.Scripts.UI;
|
||||
using CSNetwork.GPDataType;
|
||||
using CSNetwork.Protocols;
|
||||
|
||||
namespace BrewMonster.Scripts.Chat
|
||||
{
|
||||
public static class Chat_GameSession
|
||||
{
|
||||
private static List<Protocol> m_aPendingProtocols = new();
|
||||
private static List<int> m_aPendingPlayers = new();
|
||||
|
||||
public static void AddElemForPendingProtocols(Protocol p)
|
||||
{
|
||||
m_aPendingProtocols.Add(p);
|
||||
}
|
||||
|
||||
// Add one player's id to a buffer in order to get his name later
|
||||
public static void AddChatPlayerID(int id)
|
||||
{
|
||||
if (EC_Game.GetGameRun().GetPlayerName(id, false) != null)
|
||||
{
|
||||
m_aPendingPlayers.Add(id);
|
||||
}
|
||||
}
|
||||
|
||||
public static bool ShouldBlockByLevel(chatmessage p)
|
||||
{
|
||||
int levelBlock = EC_Game.GetConfigs().GetBlackListSettings().levelBlock;
|
||||
if (p.Srclevel > 0 && p.Srclevel < levelBlock)
|
||||
{
|
||||
if ((ChatChannel)p.Channel is ChatChannel.GP_CHAT_LOCAL
|
||||
or ChatChannel.GP_CHAT_WHISPER
|
||||
or ChatChannel.GP_CHAT_TRADE)
|
||||
{
|
||||
if (!EC_Game.GetGameRun().GetHostPlayer().IsOmitBlocking(p.Srcroleid))
|
||||
{
|
||||
// should be filted by level
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool PolicyResolver(Protocol pProtocol, chatmessage p, ref string strTemp)
|
||||
{
|
||||
if (IsPolicyChat(p))
|
||||
{
|
||||
// Todo: check logic
|
||||
CHAT_S2C.PolicyChatParameter pPolicyChatPara = CHAT_S2C.CreatPolicyChatParameter(p.Data);
|
||||
if (pPolicyChatPara != null && p.Data.Size > 0)
|
||||
{
|
||||
strTemp = ("???");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pPolicyChatPara != null && !pPolicyChatPara.IsNameReady())
|
||||
{
|
||||
pPolicyChatPara.GetNameFromServer();
|
||||
m_aPendingProtocols.Add(pProtocol);
|
||||
return false;
|
||||
}
|
||||
|
||||
strTemp = CECUIHelper.PolicySpecialCharReplace(strTemp, pPolicyChatPara);
|
||||
if (CanFormatCoordText(p))
|
||||
{
|
||||
//strTemp = CECUIHelper.FormatCoordText(strTemp);
|
||||
}
|
||||
}
|
||||
//int strLen = strTemp.GetLength();
|
||||
//wcsncpy(szMsg, strTemp, strLen);
|
||||
//szMsg[strLen] = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
//AUI_ConvertChatString(strTemp, szMsg, false);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*private void AUI_ConvertChatString(string pszChat, string pszConv, bool bName)
|
||||
{
|
||||
int i, nLen = 0;
|
||||
if (pszChat != null || pszConv != null)
|
||||
return;
|
||||
|
||||
pszConv[0] = 0;
|
||||
for( i = 0; i < (int)a_strlen(pszChat); i++ )
|
||||
{
|
||||
if( pszChat[i] == '^' )
|
||||
{
|
||||
pszConv[nLen] = '^';
|
||||
pszConv[nLen + 1] = '^';
|
||||
nLen += 2;
|
||||
}
|
||||
else if( pszChat[i] == '&' )
|
||||
{
|
||||
pszConv[nLen] = '^';
|
||||
pszConv[nLen + 1] = '&';
|
||||
nLen += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
pszConv[nLen] = pszChat[i];
|
||||
nLen++;
|
||||
}
|
||||
}
|
||||
pszConv[nLen] = 0;
|
||||
}*/
|
||||
|
||||
private static bool IsPolicyChat(chatmessage p)
|
||||
{
|
||||
bool bOK = false;
|
||||
switch (p.Channel)
|
||||
{
|
||||
case (byte)ChatChannel.GP_CHAT_LOCAL: // ÃæÏò¸Ã NPC ¿É¼ûÓòÖÐËùÓÐÍæ¼Ò
|
||||
if (p.Srcroleid == 0 || // £¨²ßÂÔº°»°Öбê¼Ç $A £©
|
||||
ISNPCID(p.Srcroleid))
|
||||
{
|
||||
// £¨²ßÂÔº°»°ÖÐÎÞ±ê¼Ç £©
|
||||
bOK = true;
|
||||
}
|
||||
|
||||
break;
|
||||
case (byte)ChatChannel.GP_CHAT_BATTLE: // ÃæÏò³ÇÕ½¸±±¾Ö¸¶¨ÕóÓª»òÈ«²¿Íæ¼Òº°»°£¨²ßÂÔº°»°Öбê¼Ç $F¡¢$T £©
|
||||
if (p.Srcroleid == 0)
|
||||
{
|
||||
// Ϊ·ÀÒÔºóÓб仯£¬Ôö¼Ó´ËÅжÏ
|
||||
bOK = true;
|
||||
}
|
||||
|
||||
break;
|
||||
case (byte)ChatChannel.GP_CHAT_BROADCAST: // ÃæÏòÈ«ÌåÔÚÏßÍæ¼Ò£¨²ßÂÔº°»°Öбê¼Ç $S £©
|
||||
if (p.Srcroleid == 0)
|
||||
{
|
||||
bOK = true;
|
||||
}
|
||||
|
||||
break;
|
||||
case (byte)ChatChannel.GP_CHAT_INSTANCE
|
||||
: // ÃæÏò¸±±¾ÖÐÍæ¼Ò£¨²ßÂÔº°»°Öбê¼Ç $I £©£¨ $X ±ê¼Çʱ p->srcroleid == 1£¬´Ëʱֻ³öÏÖÔÚÆÁÄ»ÖÐÑ룩
|
||||
if (p.Srcroleid == 0 || p.Srcroleid == 1)
|
||||
{
|
||||
bOK = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return bOK;
|
||||
}
|
||||
|
||||
static bool ISNPCID(int id)
|
||||
{
|
||||
uint uid = (uint)id;
|
||||
return (uid & 0x80000000) != 0 &&
|
||||
(uid & 0x40000000) == 0;
|
||||
}
|
||||
|
||||
static bool CanFormatCoordText(chatmessage p)
|
||||
{
|
||||
bool bOK = false;
|
||||
switch (p.Channel)
|
||||
{
|
||||
case (byte)ChatChannel.GP_CHAT_LOCAL: // ÃæÏò¸Ã NPC ¿É¼ûÓòÖÐËùÓÐÍæ¼Ò
|
||||
if (p.Srcroleid == 0 || // £¨²ßÂÔº°»°Öбê¼Ç $A £©
|
||||
ISNPCID(p.Srcroleid))
|
||||
{
|
||||
// £¨²ßÂÔº°»°ÖÐÎÞ±ê¼Ç £©
|
||||
bOK = true;
|
||||
}
|
||||
|
||||
break;
|
||||
case (byte)ChatChannel.GP_CHAT_BATTLE: // ÃæÏò³ÇÕ½¸±±¾Ö¸¶¨ÕóÓª»òÈ«²¿Íæ¼Òº°»°£¨²ßÂÔº°»°Öбê¼Ç $F¡¢$T £©
|
||||
if (p.Srcroleid == 0)
|
||||
{
|
||||
// Ϊ·ÀÒÔºóÓб仯£¬Ôö¼Ó´ËÅжÏ
|
||||
bOK = true;
|
||||
}
|
||||
|
||||
break;
|
||||
case (byte)ChatChannel.GP_CHAT_BROADCAST: // ÃæÏòÈ«ÌåÔÚÏßÍæ¼Ò£¨²ßÂÔº°»°Öбê¼Ç $S £©
|
||||
if (p.Srcroleid == 0)
|
||||
{
|
||||
bOK = true;
|
||||
}
|
||||
|
||||
break;
|
||||
case (byte)ChatChannel.GP_CHAT_INSTANCE: //p->srcroleid == 1 ʱ²»½øÐÐ×ø±êµÄÌæ»»
|
||||
if (p.Srcroleid == 0)
|
||||
{
|
||||
bOK = true;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return bOK;
|
||||
}
|
||||
|
||||
public class AUICTranslate
|
||||
{
|
||||
protected string m_AString = string.Empty;
|
||||
protected string m_AWString = string.Empty;
|
||||
|
||||
public AUICTranslate()
|
||||
{
|
||||
}
|
||||
|
||||
public string Translate(string str)
|
||||
{
|
||||
// In original C++ this likely performs UI charset translation.
|
||||
// Here we simply return the same string or store it.
|
||||
m_AString = str;
|
||||
return m_AString;
|
||||
}
|
||||
|
||||
public string Translate(ReadOnlySpan<char> str)
|
||||
{
|
||||
m_AWString = new string(str);
|
||||
return m_AWString;
|
||||
}
|
||||
|
||||
public string ReverseTranslate(string str)
|
||||
{
|
||||
// Reverse translation placeholder
|
||||
m_AString = str;
|
||||
return m_AString;
|
||||
}
|
||||
|
||||
public string ReverseTranslate(ReadOnlySpan<char> str)
|
||||
{
|
||||
m_AWString = new string(str);
|
||||
return m_AWString;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7c774ca3c7d345e4ea0fbb7397bf1d88
|
||||
@@ -0,0 +1,17 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace BrewMonster.Scripts.Chat
|
||||
{
|
||||
public static class Chat_Helper
|
||||
{
|
||||
public static Color32 ToColor32(uint c)
|
||||
{
|
||||
byte a = (byte)((c >> 24) & 0xFF);
|
||||
byte r = (byte)((c >> 16) & 0xFF);
|
||||
byte g = (byte)((c >> 8) & 0xFF);
|
||||
byte b = (byte)(c & 0xFF);
|
||||
|
||||
return new Color32(r, g, b, a);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5ef4426a7625eb74aa802808e2223421
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3d2c00e6fd97fce4c8061a0c54327a5f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,15 @@
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BrewMonster.Scripts.ChatUI
|
||||
{
|
||||
public class ChatMessageView : MonoBehaviour
|
||||
{
|
||||
public TextMeshProUGUI messageText;
|
||||
|
||||
public void Bind(string message)
|
||||
{
|
||||
messageText.text = message;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 179d32c667fc2f641bdcb7afb18046b9
|
||||
@@ -0,0 +1,177 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using CSNetwork;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Pool;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace BrewMonster.Scripts.ChatUI
|
||||
{
|
||||
public class ChatPanelUI : MonoBehaviour
|
||||
{
|
||||
[Header("UI")] public ScrollRect scrollRect;
|
||||
public GameObject chatPanelUIGO;
|
||||
public RectTransform content;
|
||||
public ChatMessageView messagePrefab;
|
||||
//public GameObject newMessageIndicator;
|
||||
|
||||
[Header("Config")] public int maxVisibleMessages = 30;
|
||||
public int maxStoredMessages = 2000;
|
||||
|
||||
private List<string> _messages = new();
|
||||
private List<ChatMessageView> _visibleViews = new();
|
||||
|
||||
private ObjectPool<ChatMessageView> _pool;
|
||||
|
||||
private bool _userAtBottom = true;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
EventBus.Subscribe<GameSession.ChatMessageEvent>(OnChatMessageReceived);
|
||||
_pool = new ObjectPool<ChatMessageView>(
|
||||
CreateItem,
|
||||
OnGetItem,
|
||||
OnReleaseItem,
|
||||
OnDestroyItem,
|
||||
false,
|
||||
10,
|
||||
100
|
||||
);
|
||||
|
||||
scrollRect.onValueChanged.AddListener(OnScrollChanged);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
EventBus.Unsubscribe<GameSession.ChatMessageEvent>(OnChatMessageReceived);
|
||||
}
|
||||
|
||||
private void OnChatMessageReceived(GameSession.ChatMessageEvent x)
|
||||
{
|
||||
ChatThreadDispatcher.Instance.Post(() =>
|
||||
{
|
||||
AddMessage(x.context);
|
||||
});
|
||||
}
|
||||
|
||||
ChatMessageView CreateItem()
|
||||
{
|
||||
var item = Instantiate(messagePrefab);
|
||||
item.transform.SetParent(content, false);
|
||||
return item;
|
||||
}
|
||||
|
||||
void OnGetItem(ChatMessageView item)
|
||||
{
|
||||
item.gameObject.SetActive(true);
|
||||
}
|
||||
|
||||
void OnReleaseItem(ChatMessageView item)
|
||||
{
|
||||
item.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
void OnDestroyItem(ChatMessageView item)
|
||||
{
|
||||
Destroy(item.gameObject);
|
||||
}
|
||||
|
||||
void OnScrollChanged(Vector2 pos)
|
||||
{
|
||||
_userAtBottom = scrollRect.verticalNormalizedPosition <= 0.001f;
|
||||
|
||||
if (_userAtBottom)
|
||||
{
|
||||
//newMessageIndicator.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
bool IsAtBottom()
|
||||
{
|
||||
return scrollRect.verticalNormalizedPosition <= 0.001f;
|
||||
}
|
||||
|
||||
public void AddMessage(string msg)
|
||||
{
|
||||
_messages.Add(msg);
|
||||
|
||||
if (_messages.Count > maxStoredMessages)
|
||||
_messages.RemoveAt(0);
|
||||
|
||||
if (!chatPanelUIGO.activeSelf)
|
||||
return;
|
||||
|
||||
AddMessageView(msg);
|
||||
|
||||
if (_userAtBottom)
|
||||
ScrollToBottom();
|
||||
}
|
||||
|
||||
void AddMessageView(string msg)
|
||||
{
|
||||
var view = _pool.Get();
|
||||
view.transform.SetParent(content, false);
|
||||
view.transform.SetAsLastSibling();
|
||||
view.Bind(msg);
|
||||
|
||||
_visibleViews.Add(view);
|
||||
|
||||
if (_visibleViews.Count > maxVisibleMessages)
|
||||
{
|
||||
var old = _visibleViews[0];
|
||||
_visibleViews.RemoveAt(0);
|
||||
_pool.Release(old);
|
||||
}
|
||||
|
||||
Canvas.ForceUpdateCanvases();
|
||||
}
|
||||
|
||||
void RefreshVisible()
|
||||
{
|
||||
foreach (var view in _visibleViews)
|
||||
_pool.Release(view);
|
||||
|
||||
_visibleViews.Clear();
|
||||
|
||||
int start = Mathf.Max(0, _messages.Count - maxVisibleMessages);
|
||||
|
||||
for (int i = start; i < _messages.Count; i++)
|
||||
{
|
||||
var view = _pool.Get();
|
||||
view.transform.SetParent(content, false);
|
||||
view.transform.SetAsLastSibling();
|
||||
view.Bind(_messages[i]);
|
||||
|
||||
_visibleViews.Add(view);
|
||||
}
|
||||
|
||||
Canvas.ForceUpdateCanvases();
|
||||
ScrollToBottom();
|
||||
}
|
||||
|
||||
public void ScrollToBottom()
|
||||
{
|
||||
Canvas.ForceUpdateCanvases();
|
||||
scrollRect.verticalNormalizedPosition = 0f;
|
||||
//newMessageIndicator.SetActive(false);
|
||||
}
|
||||
|
||||
public void ClearChat()
|
||||
{
|
||||
foreach (var view in _visibleViews)
|
||||
_pool.Release(view);
|
||||
|
||||
_visibleViews.Clear();
|
||||
_messages.Clear();
|
||||
}
|
||||
|
||||
public void OnHandlerChatButton()
|
||||
{
|
||||
bool open = !chatPanelUIGO.activeSelf;
|
||||
chatPanelUIGO.SetActive(open);
|
||||
|
||||
if (open)
|
||||
RefreshVisible();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5790dcd71bafcec4697f10b5366bec2c
|
||||
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using UnityEngine;
|
||||
|
||||
namespace BrewMonster.Scripts.ChatUI
|
||||
{
|
||||
/// <summary>
|
||||
/// Responsible for switching chat messages from background threads to Unity main thread.
|
||||
/// Only handles chat related actions.
|
||||
/// </summary>
|
||||
public class ChatThreadDispatcher : MonoSingleton<ChatThreadDispatcher>
|
||||
{
|
||||
private static readonly ConcurrentQueue<Action> _queue = new ConcurrentQueue<Action>();
|
||||
|
||||
protected override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
DontDestroyOnLoad(gameObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called from ANY thread (network thread safe)
|
||||
/// </summary>
|
||||
public void Post(Action action)
|
||||
{
|
||||
if (action == null)
|
||||
return;
|
||||
|
||||
_queue.Enqueue(action);
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
while (_queue.TryDequeue(out var action))
|
||||
{
|
||||
try
|
||||
{
|
||||
action?.Invoke();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"ChatThreadDispatcher error: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 00392315a7ee47e7a9527eda504fb312
|
||||
timeCreated: 1773310087
|
||||
@@ -270,7 +270,7 @@ namespace BrewMonster.Common
|
||||
{
|
||||
m_UseItemCmdList.Clear();
|
||||
|
||||
// 重置 C2S 命令计时器
|
||||
// ���� C2S �����ʱ��
|
||||
m_CounterMap[(int)CommandID.USE_ITEM].Reset(true);
|
||||
|
||||
m_EnterSanctuaryList.Clear();
|
||||
@@ -280,7 +280,7 @@ namespace BrewMonster.Common
|
||||
m_PresentInfoList.Clear();
|
||||
m_CounterMap[(int)CommandID.PLAYER_GIVE_PRESENT].Reset(true);
|
||||
|
||||
// 重置协议计时器
|
||||
// ����Э���ʱ��
|
||||
m_GetPlayerBriefInfoList.Clear();
|
||||
m_CounterMap2[(int)ProtocolType.PROTOCOL_GETPLAYERBRIEFINFO].Reset(true);
|
||||
|
||||
@@ -377,13 +377,13 @@ namespace BrewMonster.Common
|
||||
getplayerbriefinfo p = m_GetPlayerBriefInfoList[0];
|
||||
if (p.Playerlist.Count != 0)
|
||||
{
|
||||
// 获取第一个玩家id并向服务器发送协议
|
||||
// ��ȡ��һ�����id�������������Э��
|
||||
getplayerbriefinfo temp = p;
|
||||
temp.Playerlist.Clear();
|
||||
temp.Playerlist.Add(p.Playerlist[0]);
|
||||
UnityGameSession.Instance.GameSession.SendProtocol(temp);
|
||||
|
||||
// 从列表中清除
|
||||
// ��������
|
||||
p.Playerlist.Remove(p.Playerlist[0]);
|
||||
}
|
||||
|
||||
@@ -670,12 +670,12 @@ namespace BrewMonster.Common
|
||||
}
|
||||
|
||||
// Send protocols ...
|
||||
void SendGetPlayerBriefInfo(int iNumPlayer, int[] aIDs, int iReason)
|
||||
public void SendGetPlayerBriefInfo(int iNumPlayer, int[] aIDs, int iReason)
|
||||
{
|
||||
if (iNumPlayer == 0 || aIDs == null || aIDs.Length == 0)
|
||||
return;
|
||||
|
||||
// 1.合并添加到列表
|
||||
// 1.�ϲ���ӵ��б�
|
||||
getplayerbriefinfo p = new getplayerbriefinfo();
|
||||
p.Roleid = EC_Game.GetGameRun().GetHostPlayer().GetCharacterID();
|
||||
p.Reason = (byte)iReason;
|
||||
@@ -687,7 +687,7 @@ namespace BrewMonster.Common
|
||||
if (p.Playerlist.Count > 0)
|
||||
m_GetPlayerBriefInfoList.Add(p);
|
||||
|
||||
// 2.检查并发送
|
||||
// 2.��鲢����
|
||||
SendCachedGetPlayerBriefInfo();
|
||||
}
|
||||
|
||||
|
||||
@@ -124,3 +124,5 @@ namespace BrewMonster.Scripts
|
||||
}
|
||||
}
|
||||
}
|
||||
/// d 2000 la kn
|
||||
/// d 1988 + so tien
|
||||
|
||||
@@ -4,6 +4,11 @@ using UnityEngine;
|
||||
public static class GameConstants
|
||||
{
|
||||
public static int NUM_MAGICCLASS = 5;
|
||||
public static int ARMOR_RUIN_SPEED = -25;
|
||||
public static int WEAPON_RUIN_SPEED = -2;
|
||||
public static float PLAYER_PRICE_SCALE = 1.0f;
|
||||
public static int ENDURANCE_SCALE = 100;
|
||||
|
||||
}
|
||||
|
||||
public struct ROLEBASICPROP
|
||||
|
||||
@@ -2,10 +2,6 @@ namespace BrewMonster.Scripts
|
||||
{
|
||||
public class InventoryConst
|
||||
{
|
||||
// Equipment endurance scale
|
||||
public const int ENDURANCE_SCALE = 100;
|
||||
// NUM_MAGICCLASS
|
||||
public const int NUM_MAGICCLASS = 5;
|
||||
// Index of item in equipment inventory
|
||||
public const int EQUIPIVTR_WEAPON = 0;
|
||||
public const int EQUIPIVTR_HEAD = 1;
|
||||
|
||||
@@ -316,8 +316,8 @@ namespace BrewMonster.Network
|
||||
}
|
||||
public static void update_require_data(ref prerequisition require)
|
||||
{
|
||||
require.durability *= BrewMonster.Scripts.InventoryConst.ENDURANCE_SCALE;
|
||||
require.max_durability *= BrewMonster.Scripts.InventoryConst.ENDURANCE_SCALE;
|
||||
require.durability *= GameConstants.ENDURANCE_SCALE;
|
||||
require.max_durability *= GameConstants.ENDURANCE_SCALE;
|
||||
}
|
||||
public static void set_to_classid(DATA_TYPE type, byte[] data, int major_type)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@@ -146,6 +146,7 @@ namespace BrewMonster
|
||||
pEvent.SetDelay(dwDelayTime);
|
||||
pEvent.SetReverse(bReverse);
|
||||
pEvent.SetParam(param);
|
||||
BMLogger.LogError("[HoangDev] bTraceTarget=" + bTraceTarget);
|
||||
pEvent.SetTraceTarget(bTraceTarget);
|
||||
pEvent.SetModifier(dwModifier);
|
||||
pEvent.SetIsCluster(bCluster);
|
||||
@@ -503,4 +504,4 @@ namespace BrewMonster
|
||||
public bool bGfxDisableCamShake;
|
||||
public bool bHostECMCreatedByGfx;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,7 +271,6 @@ namespace BrewMonster
|
||||
if (m_enumState == GfxSkillEventState.enumHit && m_bTraceTarget)
|
||||
{
|
||||
UpdateHitGfxTransform();
|
||||
|
||||
// Check if hit GFX has been destroyed (3 second lifetime) or target is destroyed
|
||||
// 检查命中特效是否已被销毁(3秒生命周期)或目标是否已销毁
|
||||
if (m_hitGfxInstance == null || (!m_bTargetExist && m_nTargetID != 0))
|
||||
@@ -296,7 +295,26 @@ namespace BrewMonster
|
||||
protected override void HitTarget(Vector3 vTarget)
|
||||
{
|
||||
base.HitTarget(vTarget);
|
||||
DestroyFlyGfx();
|
||||
|
||||
// Only destroy fly GFX if NOT tracing target
|
||||
// If tracing target, fly GFX will be cleaned up when buff expires
|
||||
// 只有在不跟踪目标时才销毁飞行特效
|
||||
// 如果跟踪目标,飞行特效将在buff过期时清理
|
||||
if (!m_bTraceTarget)
|
||||
{
|
||||
DestroyFlyGfx();
|
||||
}
|
||||
else
|
||||
{
|
||||
// If fly GFX exists and m_bTraceTarget is true, add to tracking list
|
||||
// 如果飞行特效存在且m_bTraceTarget为true,添加到跟踪列表
|
||||
if (m_flyGfxInstance != null)
|
||||
{
|
||||
SkillGfxMan.InstanceSub?.AddTraceTargetGfx(m_flyGfxInstance, 0); // Skill ID not available, use 0
|
||||
BMLogger.Log($"[TRACE_TARGET_GFX] HitTarget: Added fly GFX to trace target list (m_bTraceTarget=true)");
|
||||
}
|
||||
}
|
||||
|
||||
SpawnHitGfx(vTarget);
|
||||
|
||||
// TODO Phase 2: Special hit effects (rune, critical, nullity)
|
||||
@@ -333,6 +351,13 @@ namespace BrewMonster
|
||||
|
||||
m_flyGfxInstance = GameObject.Instantiate(prefab, pos, prefab.transform.rotation);
|
||||
|
||||
// If m_bTraceTarget is true, add to tracking list when spawned
|
||||
// 如果m_bTraceTarget为true,在生成时添加到跟踪列表
|
||||
if (m_bTraceTarget)
|
||||
{
|
||||
SkillGfxMan.InstanceSub?.AddTraceTargetGfx(m_flyGfxInstance, 0); // Skill ID not available, use 0
|
||||
BMLogger.Log($"[TRACE_TARGET_GFX] SpawnFlyGfx: Added fly GFX to trace target list (m_bTraceTarget=true)");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -401,7 +426,8 @@ namespace BrewMonster
|
||||
// 这与C++逻辑匹配:当投射物击中地面(无目标)时使用m_szHitGrndGfx
|
||||
bool bTargetExists = m_bTargetExist && m_nTargetID != 0;
|
||||
GameObject prefab = bTargetExists ? m_pComposer.GetHitGFX() : m_pComposer.GetHitGrdGFX();
|
||||
BMLogger.LogError("HitGfx : " + m_pComposer.hitGfxName);
|
||||
//BMLogger.LogError("bTargetExists : " + bTargetExists);
|
||||
//BMLogger.LogError("HitGfx : " + m_pComposer.hitGfxName);
|
||||
|
||||
if (prefab == null)
|
||||
{
|
||||
@@ -429,9 +455,21 @@ namespace BrewMonster
|
||||
|
||||
m_hitGfxInstance = GameObject.Instantiate(prefab, vTarget, prefab.transform.rotation);
|
||||
|
||||
// Destroy hit GFX after 3 seconds (unless m_bTraceTarget is true, then it follows target until destroyed)
|
||||
// 3秒后销毁命中特效(除非m_bTraceTarget为true,否则它会跟随目标直到被销毁)
|
||||
//GameObject.Destroy(m_hitGfxInstance, 3.0f);
|
||||
// If m_bTraceTarget is true, add to tracking list (don't auto-destroy)
|
||||
// 如果m_bTraceTarget为true,添加到跟踪列表(不自动销毁)
|
||||
if (m_bTraceTarget)
|
||||
{
|
||||
SkillGfxMan.InstanceSub?.AddTraceTargetGfx(m_hitGfxInstance, 0); // Skill ID not available, use 0
|
||||
//BMLogger.Log($"[TRACE_TARGET_GFX] SpawnHitGfx: Added hit GFX to trace target list (m_bTraceTarget=true)");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Destroy hit GFX after 5 seconds (unless m_bTraceTarget is true, then it follows target until destroyed)
|
||||
// 5秒后销毁命中特效(除非m_bTraceTarget为true,否则它会跟随目标直到被销毁)
|
||||
//HIT_GFX_MAX_TIMESPAN 5000
|
||||
//BMLogger.Log($"[TRACE_TARGET_GFX] SpawnHitGfx: GameObject.Destroy(m_hitGfxInstance, 5.0f);");
|
||||
GameObject.Destroy(m_hitGfxInstance, 5.0f);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -440,10 +478,28 @@ namespace BrewMonster
|
||||
/// </summary>
|
||||
public new void Resume()
|
||||
{
|
||||
DestroyFlyGfx();
|
||||
// Hit GFX is auto-destroyed by Unity's Destroy timer, don't null it here
|
||||
// 命中特效由Unity的Destroy计时器自动销毁,不在此处置null
|
||||
m_hitGfxInstance = null;
|
||||
// Don't destroy GFX if it's in trace target list
|
||||
// It will be cleaned up when buff expires
|
||||
// 如果GFX在跟踪目标列表中,不要销毁它
|
||||
// 它将在buff过期时清理
|
||||
if (m_flyGfxInstance != null)
|
||||
{
|
||||
if (SkillGfxMan.InstanceSub != null && !SkillGfxMan.InstanceSub.IsTraceTargetGfx(m_flyGfxInstance))
|
||||
{
|
||||
DestroyFlyGfx();
|
||||
}
|
||||
}
|
||||
|
||||
if (m_hitGfxInstance != null)
|
||||
{
|
||||
if (SkillGfxMan.InstanceSub != null && !SkillGfxMan.InstanceSub.IsTraceTargetGfx(m_hitGfxInstance))
|
||||
{
|
||||
// Hit GFX is auto-destroyed by Unity's Destroy timer, don't null it here
|
||||
// 命中特效由Unity的Destroy计时器自动销毁,不在此处置null
|
||||
m_hitGfxInstance = null;
|
||||
}
|
||||
}
|
||||
|
||||
base.Resume();
|
||||
}
|
||||
|
||||
@@ -878,6 +934,13 @@ namespace BrewMonster
|
||||
protected EC_ManPlayer m_pPlayerMan;
|
||||
protected CECNPCMan m_pNPCMan;
|
||||
|
||||
// Track GFX instances that have m_bTraceTarget = true
|
||||
// These are typically buff-related trail effects that persist until buff expires
|
||||
// 跟踪具有m_bTraceTarget = true的GFX实例
|
||||
// 这些通常是持续到buff结束的buff相关轨迹效果
|
||||
private List<GameObject> m_TraceTargetGfxList = new List<GameObject>();
|
||||
private Dictionary<GameObject, int> m_TraceTargetGfxSkillMap = new Dictionary<GameObject, int>();
|
||||
|
||||
public SkillGfxMan(CECGameRun pGameRun)
|
||||
{
|
||||
m_EventLst = new LinkedList<CECSkillGfxEvent>();
|
||||
@@ -971,6 +1034,110 @@ namespace BrewMonster
|
||||
{
|
||||
m_FreeLst[i].Clear();
|
||||
}
|
||||
|
||||
// Clean up trace target GFX
|
||||
// 清理跟踪目标GFX
|
||||
RemoveAllTraceTargetGfx();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add a GFX instance to trace target tracking list
|
||||
/// 将GFX实例添加到跟踪目标跟踪列表
|
||||
/// </summary>
|
||||
public void AddTraceTargetGfx(GameObject gfxInstance, int skillId)
|
||||
{
|
||||
if (gfxInstance == null) return;
|
||||
|
||||
if (!m_TraceTargetGfxList.Contains(gfxInstance))
|
||||
{
|
||||
m_TraceTargetGfxList.Add(gfxInstance);
|
||||
m_TraceTargetGfxSkillMap[gfxInstance] = skillId;
|
||||
|
||||
BMLogger.Log($"[TRACE_TARGET_GFX] Added GFX for skill {skillId}, total tracked: {m_TraceTargetGfxList.Count}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove all trace target GFX (called when buff states update)
|
||||
/// 移除所有跟踪目标GFX(在buff状态更新时调用)
|
||||
/// </summary>
|
||||
public void RemoveAllTraceTargetGfx()
|
||||
{
|
||||
BMLogger.Log($"[TRACE_TARGET_GFX] Removing {m_TraceTargetGfxList.Count} trace target GFX");
|
||||
|
||||
foreach (GameObject gfx in m_TraceTargetGfxList)
|
||||
{
|
||||
if (gfx != null)
|
||||
{
|
||||
GameObject.Destroy(gfx);
|
||||
}
|
||||
}
|
||||
|
||||
m_TraceTargetGfxList.Clear();
|
||||
m_TraceTargetGfxSkillMap.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove trace target GFX for specific skill
|
||||
/// 移除特定技能的跟踪目标GFX
|
||||
/// </summary>
|
||||
public void RemoveTraceTargetGfxForSkill(int skillId)
|
||||
{
|
||||
List<GameObject> toRemove = new List<GameObject>();
|
||||
|
||||
foreach (var kvp in m_TraceTargetGfxSkillMap)
|
||||
{
|
||||
if (kvp.Value == skillId)
|
||||
{
|
||||
toRemove.Add(kvp.Key);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (GameObject gfx in toRemove)
|
||||
{
|
||||
if (gfx != null)
|
||||
{
|
||||
GameObject.Destroy(gfx);
|
||||
}
|
||||
m_TraceTargetGfxList.Remove(gfx);
|
||||
m_TraceTargetGfxSkillMap.Remove(gfx);
|
||||
}
|
||||
|
||||
if (toRemove.Count > 0)
|
||||
{
|
||||
BMLogger.Log($"[TRACE_TARGET_GFX] Removed {toRemove.Count} GFX for skill {skillId}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check if a GFX instance is tracked as trace target GFX
|
||||
/// 检查GFX实例是否被跟踪为跟踪目标GFX
|
||||
/// </summary>
|
||||
public bool IsTraceTargetGfx(GameObject gfx)
|
||||
{
|
||||
return m_TraceTargetGfxList.Contains(gfx);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clean up null references (GFX destroyed elsewhere)
|
||||
/// 清理空引用(在其他地方销毁的GFX)
|
||||
/// </summary>
|
||||
public void CleanupTraceTargetGfx()
|
||||
{
|
||||
m_TraceTargetGfxList.RemoveAll(gfx => gfx == null);
|
||||
|
||||
List<GameObject> nullKeys = new List<GameObject>();
|
||||
foreach (var kvp in m_TraceTargetGfxSkillMap)
|
||||
{
|
||||
if (kvp.Key == null)
|
||||
{
|
||||
nullKeys.Add(kvp.Key);
|
||||
}
|
||||
}
|
||||
foreach (var key in nullKeys)
|
||||
{
|
||||
m_TraceTargetGfxSkillMap.Remove(key);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -12,13 +12,17 @@ namespace BrewMonster
|
||||
public int m_nLev;
|
||||
public int m_nMemNum;
|
||||
|
||||
public int GetLevel() { return m_nLev; }
|
||||
public int GetLevel()
|
||||
{
|
||||
return m_nLev;
|
||||
}
|
||||
}
|
||||
|
||||
public class CECFactionMan
|
||||
{
|
||||
Dictionary<uint, Faction_Info> m_FactionMap;
|
||||
public List<int> m_alliance = new List<int>();
|
||||
|
||||
public Faction_Info GetFaction(uint uId, bool bRequestFromServer)
|
||||
{
|
||||
if (!m_FactionMap.TryGetValue(uId, out var it))
|
||||
@@ -30,6 +34,7 @@ namespace BrewMonster
|
||||
|
||||
return it;
|
||||
}
|
||||
|
||||
public bool IsFactionAlliance(int fid)
|
||||
{
|
||||
if (fid == 0)
|
||||
@@ -43,5 +48,35 @@ namespace BrewMonster
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private Dictionary<int, Faction_Mem_Info> m_MemMap;
|
||||
|
||||
public Faction_Mem_Info GetMember(int roleId)
|
||||
{
|
||||
if (m_MemMap.TryGetValue(roleId, out var member))
|
||||
return member;
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public class Faction_Mem_Info
|
||||
{
|
||||
public int RoleId { get; set; }
|
||||
public int FRoleId { get; set; }
|
||||
public int Level { get; set; }
|
||||
public int Profession { get; set; }
|
||||
public byte OnlineStatus { get; set; }
|
||||
public byte Gender { get; set; }
|
||||
public int LoginTime { get; set; }
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string NickName { get; set; } = string.Empty;
|
||||
public int Contrib { get; set; }
|
||||
public bool DelayExpel { get; set; }
|
||||
public uint ExpelEndTime { get; set; }
|
||||
public int Reputation { get; set; }
|
||||
public int ReincarnationTimes { get; set; }
|
||||
|
||||
public bool IsOnline => OnlineStatus != 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,251 @@
|
||||
using System.Collections.Generic;
|
||||
using BrewMonster.Network;
|
||||
|
||||
namespace BrewMonster
|
||||
{
|
||||
public class CECFriendMan
|
||||
{
|
||||
// =========================
|
||||
// Nested Types
|
||||
// =========================
|
||||
|
||||
public struct Friend
|
||||
{
|
||||
public int Id;
|
||||
public int Profession;
|
||||
public int GroupId;
|
||||
public byte Status;
|
||||
public int Level;
|
||||
public int AreaId;
|
||||
public string Name;
|
||||
|
||||
public bool IsGameOnline() => IsGameOnline(Status);
|
||||
public bool IsGTOnline() => IsGTOnline(Status);
|
||||
|
||||
public static bool IsGameOnline(byte s)
|
||||
{
|
||||
// TODO: implement đúng theo logic C++
|
||||
return (s & 0x01) != 0;
|
||||
}
|
||||
|
||||
public static bool IsGTOnline(byte s)
|
||||
{
|
||||
// TODO: implement đúng theo logic C++
|
||||
return (s & 0x02) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
public class FRIEND_EX
|
||||
{
|
||||
public bool NewFriendPlaceHolder;
|
||||
public int Uid;
|
||||
public int Rid;
|
||||
public int Level;
|
||||
public long LastLoginTime;
|
||||
public long UpdateTime;
|
||||
public int ReincarnationCount;
|
||||
public string Remarks = string.Empty;
|
||||
}
|
||||
|
||||
public class SEND_INFO
|
||||
{
|
||||
public int Rid;
|
||||
public long SendMailTime;
|
||||
}
|
||||
|
||||
public class GROUP
|
||||
{
|
||||
public string Name = string.Empty;
|
||||
public int GroupId;
|
||||
public uint Color; // nếu dùng Unity thì có thể đổi sang Color
|
||||
|
||||
public List<Friend> Friends = new();
|
||||
}
|
||||
|
||||
public class MESSAGE
|
||||
{
|
||||
public string SenderName = string.Empty;
|
||||
public int SenderId;
|
||||
public string MessageText = string.Empty;
|
||||
public byte Flag;
|
||||
}
|
||||
|
||||
// =========================
|
||||
// Fields
|
||||
// =========================
|
||||
|
||||
private List<GROUP> m_Groups = new();
|
||||
private Dictionary<int, Friend> m_FriendTable = new();
|
||||
private List<MESSAGE> m_OfflineMsgs = new();
|
||||
private List<FRIEND_EX> m_FriendEx = new();
|
||||
private List<SEND_INFO> m_SendInfo = new();
|
||||
|
||||
// =========================
|
||||
// Operations
|
||||
// =========================
|
||||
|
||||
public bool CheckInit() => m_Groups.Count > 0;
|
||||
|
||||
public Friend AddFriend(int id, int profession, int groupId, byte status, string name)
|
||||
{
|
||||
var friend = new Friend
|
||||
{
|
||||
Id = id,
|
||||
Profession = profession,
|
||||
GroupId = groupId,
|
||||
Status = status,
|
||||
Name = name
|
||||
};
|
||||
|
||||
m_FriendTable[id] = friend;
|
||||
|
||||
var group = GetGroupByID(groupId);
|
||||
group?.Friends.Add(friend);
|
||||
if (friend.IsGameOnline())
|
||||
{
|
||||
// Todo: Check again
|
||||
EC_Game.GetGameRun().AddPlayerName(id, name);
|
||||
}
|
||||
|
||||
return friend;
|
||||
}
|
||||
|
||||
public void RemoveFriend(int idFriend)
|
||||
{
|
||||
if (!m_FriendTable.TryGetValue(idFriend, out var friend))
|
||||
return;
|
||||
|
||||
m_FriendTable.Remove(idFriend);
|
||||
|
||||
var group = GetGroupByID(friend.GroupId);
|
||||
group?.Friends.Remove(friend);
|
||||
}
|
||||
|
||||
public void RemoveAllFriends()
|
||||
{
|
||||
m_FriendTable.Clear();
|
||||
foreach (var group in m_Groups)
|
||||
group.Friends.Clear();
|
||||
}
|
||||
|
||||
public void ChangeFriendStatus(int idFriend, byte status)
|
||||
{
|
||||
if (m_FriendTable.TryGetValue(idFriend, out var friend))
|
||||
friend.Status = status;
|
||||
}
|
||||
|
||||
public void ChangeFriendGroup(int idFriend, int newGroupId)
|
||||
{
|
||||
if (!m_FriendTable.TryGetValue(idFriend, out var friend))
|
||||
return;
|
||||
|
||||
var oldGroup = GetGroupByID(friend.GroupId);
|
||||
oldGroup?.Friends.Remove(friend);
|
||||
|
||||
friend.GroupId = newGroupId;
|
||||
|
||||
var newGroup = GetGroupByID(newGroupId);
|
||||
newGroup?.Friends.Add(friend);
|
||||
}
|
||||
|
||||
public Friend? GetFriendByID(int idFriend)
|
||||
{
|
||||
m_FriendTable.TryGetValue(idFriend, out var friend);
|
||||
return friend;
|
||||
}
|
||||
|
||||
public Friend? GetFriendByName(string name)
|
||||
{
|
||||
foreach (var friend in m_FriendTable.Values)
|
||||
{
|
||||
if (friend.Name == name)
|
||||
return friend;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void SetFriendLevel(int idFriend, int level)
|
||||
{
|
||||
if (m_FriendTable.TryGetValue(idFriend, out var friend))
|
||||
friend.Level = level;
|
||||
}
|
||||
|
||||
public void SetFriendArea(int idFriend, int areaId)
|
||||
{
|
||||
if (m_FriendTable.TryGetValue(idFriend, out var friend))
|
||||
friend.AreaId = areaId;
|
||||
}
|
||||
|
||||
// =========================
|
||||
// Group Operations
|
||||
// =========================
|
||||
|
||||
public bool AddGroup(int idGroup, string name)
|
||||
{
|
||||
if (GetGroupByID(idGroup) != null)
|
||||
return false;
|
||||
|
||||
m_Groups.Add(new GROUP
|
||||
{
|
||||
GroupId = idGroup,
|
||||
Name = name
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void RemoveGroup(int idGroup)
|
||||
{
|
||||
m_Groups.RemoveAll(g => g.GroupId == idGroup);
|
||||
}
|
||||
|
||||
public void ChangeGroupName(int idGroup, string name)
|
||||
{
|
||||
var group = GetGroupByID(idGroup);
|
||||
if (group != null)
|
||||
group.Name = name;
|
||||
}
|
||||
|
||||
public void SetGroupColor(int idGroup, uint color)
|
||||
{
|
||||
var group = GetGroupByID(idGroup);
|
||||
if (group != null)
|
||||
group.Color = color;
|
||||
}
|
||||
|
||||
public int GetGroupNum() => m_Groups.Count;
|
||||
|
||||
public GROUP? GetGroupByIndex(int index)
|
||||
{
|
||||
if (index < 0 || index >= m_Groups.Count)
|
||||
return null;
|
||||
|
||||
return m_Groups[index];
|
||||
}
|
||||
|
||||
public GROUP? GetGroupByID(int id)
|
||||
{
|
||||
return m_Groups.Find(g => g.GroupId == id);
|
||||
}
|
||||
|
||||
// =========================
|
||||
// Offline Messages
|
||||
// =========================
|
||||
|
||||
public int GetOfflineMsgNum() => m_OfflineMsgs.Count;
|
||||
|
||||
public MESSAGE? GetOfflineMsg(int index)
|
||||
{
|
||||
if (index < 0 || index >= m_OfflineMsgs.Count)
|
||||
return null;
|
||||
|
||||
return m_OfflineMsgs[index];
|
||||
}
|
||||
|
||||
public void RemoveAllOfflineMsgs()
|
||||
{
|
||||
m_OfflineMsgs.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b5aa739db9534099a9eead426912429c
|
||||
timeCreated: 1772089238
|
||||
@@ -1349,11 +1349,12 @@ namespace BrewMonster.Scripts
|
||||
string message = $"Cannot find path to target position.\nPlease move manually to target location.\n\nMap Coordinates: ({mapX}, {mapZ}, ↑{mapY})";
|
||||
string messageCN = $"无法找到到目标位置的路径。\n请手动移动到目标位置。\n\n地图坐标: ({mapX}, {mapZ}, ↑{mapY})";
|
||||
|
||||
CECUIManager.Instance.ShowMessageBox(
|
||||
"Path Not Found", // 路径未找到
|
||||
message, // English message with map coordinates
|
||||
BrewMonster.MessageBoxType.YesButton
|
||||
);
|
||||
// CECUIManager.Instance.ShowMessageBox(
|
||||
// "Path Not Found", // 路径未找到
|
||||
// message, // English message with map coordinates
|
||||
// BrewMonster.MessageBoxType.YesButton
|
||||
// );
|
||||
CECUIManager.Instance.ShowMessageBoxYes("Path Not Found", message, null, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -11,7 +11,8 @@ namespace BrewMonster.Scripts.Managers
|
||||
public class EC_Inventory
|
||||
{
|
||||
// ===== Instance-based inventory (per-pack) =====
|
||||
|
||||
public static int IVTRTYPE_CLIENT_GENERALCARD_PACK = 1024; // �ͻ��˱��ذ����� ���ڿ���ͼ��Ҫ����ѻ�ÿ���ͨ�����췢�͡�Ϊ��ͳһ�������촰�ڵ���Ʒ�����ӱ��ذ�����
|
||||
|
||||
// Item array: index is slot, null means empty.
|
||||
private EC_IvtrItem[] m_aItems = Array.Empty<EC_IvtrItem>();
|
||||
|
||||
@@ -27,12 +28,6 @@ namespace BrewMonster.Scripts.Managers
|
||||
IVTRTYPE_GENERALCARD_BOX = 7; // ���ư���
|
||||
};
|
||||
|
||||
// ע�� IVTRTYPE_CLIENT_GENERALCARD_PACK ö��ֵ���ܺ������ Inventory type ֵ�ظ�����
|
||||
public static class IVTRTYPE_PACK_CLIENT_GENERALCAR
|
||||
{
|
||||
public const int IVTRTYPE_CLIENT_GENERALCARD_PACK = 1024; // �ͻ��˱��ذ����� ���ڿ���ͼ��Ҫ����ѻ�ÿ���ͨ�����췢�͡�Ϊ��ͳһ�������촰�ڵ���Ʒ�����ӱ��ذ�����
|
||||
};
|
||||
|
||||
public EC_Inventory()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -201,8 +201,8 @@ namespace PerfectWorld.Scripts.Managers
|
||||
StrengthReq = m_pDBEssence.require_strength;
|
||||
AgilityReq = m_pDBEssence.require_agility;
|
||||
ReputationReq = m_pDBEssence.require_reputation;
|
||||
CurEndurance = m_pDBEssence.durability_min * ENDURANCE_SCALE;
|
||||
MaxEndurance = m_pDBEssence.durability_min * ENDURANCE_SCALE;
|
||||
CurEndurance = m_pDBEssence.durability_min * GameConstants.ENDURANCE_SCALE;
|
||||
MaxEndurance = m_pDBEssence.durability_min * GameConstants.ENDURANCE_SCALE;
|
||||
}
|
||||
// Get item icon file name
|
||||
public override string GetIconFile()
|
||||
|
||||
@@ -207,8 +207,8 @@ namespace PerfectWorld.Scripts.Managers
|
||||
StrengthReq = m_pDBEssence.require_strength;
|
||||
AgilityReq = m_pDBEssence.require_agility;
|
||||
ReputationReq = m_pDBEssence.require_reputation;
|
||||
CurEndurance = m_pDBEssence.durability_min * ENDURANCE_SCALE;
|
||||
MaxEndurance = m_pDBEssence.durability_min * ENDURANCE_SCALE;
|
||||
CurEndurance = m_pDBEssence.durability_min * GameConstants.ENDURANCE_SCALE;
|
||||
MaxEndurance = m_pDBEssence.durability_min * GameConstants.ENDURANCE_SCALE;
|
||||
}
|
||||
// Get item icon file name
|
||||
public override string GetIconFile()
|
||||
|
||||
@@ -174,8 +174,8 @@ namespace PerfectWorld.Scripts.Managers
|
||||
StrengthReq = 0;
|
||||
AgilityReq = 0;
|
||||
ReputationReq = m_pDBEssence.require_reputation;
|
||||
CurEndurance = m_pDBEssence.durability_min * ENDURANCE_SCALE;
|
||||
MaxEndurance = m_pDBEssence.durability_min * ENDURANCE_SCALE;
|
||||
CurEndurance = m_pDBEssence.durability_min * GameConstants.ENDURANCE_SCALE;
|
||||
MaxEndurance = m_pDBEssence.durability_min * GameConstants.ENDURANCE_SCALE;
|
||||
}
|
||||
public override string GetIconFile()
|
||||
{
|
||||
|
||||
@@ -14,7 +14,6 @@ using System.Text.RegularExpressions;
|
||||
using System.Reflection;
|
||||
using BrewMonster.Scripts.Managers;
|
||||
using BrewMonster.Scripts;
|
||||
|
||||
namespace PerfectWorld.Scripts.Managers
|
||||
{
|
||||
/// <summary>
|
||||
@@ -161,9 +160,6 @@ namespace PerfectWorld.Scripts.Managers
|
||||
// Scale Types
|
||||
public const int SCALE_SELL = 1;
|
||||
|
||||
// Endurance Scale
|
||||
public const int ENDURANCE_SCALE = 100;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Fields
|
||||
@@ -925,7 +921,7 @@ namespace PerfectWorld.Scripts.Managers
|
||||
/// </summary>
|
||||
public static int VisualizeEndurance(int v)
|
||||
{
|
||||
return (v + ENDURANCE_SCALE - 1) / ENDURANCE_SCALE;
|
||||
return (v + GameConstants.ENDURANCE_SCALE - 1) / GameConstants.ENDURANCE_SCALE;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -115,8 +115,8 @@ namespace BrewMonster.Scripts.Managers
|
||||
magic_damage = dr.ReadInt();
|
||||
defense = dr.ReadInt();
|
||||
armor = dr.ReadInt();
|
||||
resistance = new int[InventoryConst.NUM_MAGICCLASS];
|
||||
for (int i = 0; i < InventoryConst.NUM_MAGICCLASS; i++)
|
||||
resistance = new int[GameConstants.NUM_MAGICCLASS];
|
||||
for (int i = 0; i < GameConstants.NUM_MAGICCLASS; i++)
|
||||
{
|
||||
resistance[i] = dr.ReadInt();
|
||||
}
|
||||
@@ -132,13 +132,13 @@ namespace BrewMonster.Scripts.Managers
|
||||
public int[] resistance;
|
||||
public IVTR_ESSENCE_ARMOR(byte[] data)
|
||||
{
|
||||
resistance = new int[InventoryConst.NUM_MAGICCLASS];
|
||||
resistance = new int[GameConstants.NUM_MAGICCLASS];
|
||||
CECDataReader dr = new(data, data.Length);
|
||||
defense = dr.ReadInt();
|
||||
armor = dr.ReadInt();
|
||||
mp_enhance = dr.ReadInt();
|
||||
hp_enhance = dr.ReadInt();
|
||||
for (int i = 0; i < InventoryConst.NUM_MAGICCLASS; i++)
|
||||
for (int i = 0; i < GameConstants.NUM_MAGICCLASS; i++)
|
||||
{
|
||||
resistance[i] = dr.ReadInt();
|
||||
}
|
||||
|
||||
@@ -69,6 +69,7 @@ namespace PerfectWorld.Scripts.Managers
|
||||
case EC_MsgDef.MSG_PM_PLAYERFLY:
|
||||
case EC_MsgDef.MSG_PM_PLAYERMOUNT:
|
||||
case EC_MsgDef.MSG_PM_PLAYERCHGSHAPE:
|
||||
case EC_MsgDef.MSG_PM_PLAYERSKILLRESULT:
|
||||
TransmitMessage(Msg);
|
||||
break;
|
||||
case EC_MsgDef.MSG_PM_PLAYERDIED:
|
||||
@@ -141,14 +142,22 @@ namespace PerfectWorld.Scripts.Managers
|
||||
|
||||
if (host != null && cid == host.GetCharacterID())
|
||||
{
|
||||
host.ProcessMessage(Msg);
|
||||
// Call OnMsgPlayerExtState directly instead of ProcessMessage
|
||||
// ProcessMessage doesn't handle MSG_PM_PLAYEREXTSTATE
|
||||
// 直接调用OnMsgPlayerExtState而不是ProcessMessage
|
||||
// ProcessMessage不处理MSG_PM_PLAYEREXTSTATE
|
||||
host.OnMsgPlayerExtState(Msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
EC_ElsePlayer elsePlayer = SeekOutElsePlayer(cid);
|
||||
if (elsePlayer != null)
|
||||
{
|
||||
elsePlayer.ProcessMessage(Msg);
|
||||
// Call OnMsgPlayerExtState directly instead of ProcessMessage
|
||||
// ProcessMessage doesn't handle MSG_PM_PLAYEREXTSTATE
|
||||
// 直接调用OnMsgPlayerExtState而不是ProcessMessage
|
||||
// ProcessMessage不处理MSG_PM_PLAYEREXTSTATE
|
||||
elsePlayer.OnMsgPlayerExtState(Msg);
|
||||
}
|
||||
|
||||
if (Convert.ToInt32(Msg.dwParam2) == CommandID.ICON_STATE_NOTIFY && host != null && host.GetTeam() != null)
|
||||
@@ -735,6 +744,9 @@ namespace PerfectWorld.Scripts.Managers
|
||||
case EC_MsgDef.MSG_PM_PLAYERCHGSHAPE:
|
||||
cid = (GPDataTypeHelper.FromBytes<cmd_player_chgshape>((byte[])Msg.dwParam1)).idPlayer;
|
||||
break;
|
||||
case EC_MsgDef.MSG_PM_PLAYERSKILLRESULT:
|
||||
cid = GPDataTypeHelper.FromBytes<cmd_object_skill_attack_result>((byte[])Msg.dwParam1).attacker_id;
|
||||
break;
|
||||
default:
|
||||
System.Diagnostics.Debug.Assert(false, "Unknown message");
|
||||
return false;
|
||||
@@ -917,4 +929,4 @@ namespace PerfectWorld.Scripts.Managers
|
||||
public CECModel pFlyNviagteModel;
|
||||
//CECPlayer::EquipsLoadResult EquipResult;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,9 +13,8 @@ using UnityEngine;
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public class CECObject : MonoBehaviour
|
||||
public partial class CECObject : MonoBehaviour
|
||||
{
|
||||
|
||||
protected static int ALPHA_HASH = Shader.PropertyToID("_Alpha");
|
||||
protected Quaternion targetRotation;
|
||||
protected Quaternion startRotation; // Store starting rotation for Slerp
|
||||
|
||||
@@ -231,8 +231,7 @@ namespace BrewMonster.Scripts
|
||||
{
|
||||
// Constants
|
||||
public const int EC_MAXNOPKLEVEL = 0; // The maximum no PK level
|
||||
public const float EC_TABSEL_DIST = 60.0f; // Distance of TAB selection
|
||||
public const int NUM_MAGICCLASS = 5;
|
||||
public const float EC_TABSEL_DIST = 60.0f;
|
||||
public const int NUM_ESBYTE = (int)(ExtendState.NUM_EXTSTATE + 31) >> 5;
|
||||
|
||||
// Helper methods for bit manipulation (equivalent to the C++ inline functions)
|
||||
|
||||
@@ -36,6 +36,11 @@ namespace BrewMonster
|
||||
[SerializeField] internal INFO m_PlayerInfo;
|
||||
public CECModel m_pPlayerCECModel;
|
||||
protected GameObject m_pPlayerModel => m_pPlayerCECModel.m_pPlayerModel;
|
||||
public bool IsPlayerModelReady
|
||||
{
|
||||
get;
|
||||
protected set;
|
||||
}
|
||||
protected float rotationSpeed = 5;
|
||||
internal int m_iMoveMode; // Player's move mode
|
||||
[SerializeField] internal int m_idSelTarget;
|
||||
@@ -331,6 +336,9 @@ namespace BrewMonster
|
||||
m_pModels[(int)PLAYERMODEL_TYPE.PLAYERMODEL_MAJOR] = m_pPlayerModel;
|
||||
m_iShape = 0;
|
||||
|
||||
// Set the player model ready flag
|
||||
IsPlayerModelReady = true;
|
||||
|
||||
// Update visual components after model is set (with delay to ensure NamedAnimancerComponent is available)
|
||||
// 设置模型后更新视觉组件(延迟以确保NamedAnimancerComponent可用)
|
||||
StartCoroutine(UpdateVisualComponentsDelayed());
|
||||
@@ -741,6 +749,15 @@ namespace BrewMonster
|
||||
public virtual void SetNewExtendStates(int unknown, uint[] states, int count)
|
||||
{
|
||||
// TODO: Implement appearance or physics change
|
||||
|
||||
// Remove all trace target GFX when buff states change
|
||||
// This ensures buff-related trail effects are cleaned up when buffs expire
|
||||
// 当buff状态改变时移除所有跟踪目标GFX
|
||||
// 这确保buff相关的轨迹效果在buff过期时被清理
|
||||
if (SkillGfxMan.InstanceSub != null)
|
||||
{
|
||||
SkillGfxMan.InstanceSub.RemoveAllTraceTargetGfx();
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void SetUpPlayer()
|
||||
@@ -2202,7 +2219,45 @@ namespace BrewMonster
|
||||
}
|
||||
}
|
||||
}
|
||||
protected void OnMsgEnchantResult(ECMSG msg)
|
||||
{
|
||||
// 从消息中获取cmd_enchant_result结构 // Get cmd_enchant_result structure from message
|
||||
cmd_enchant_result pCmd = GPDataTypeHelper.FromBytes<cmd_enchant_result>((byte[])msg.dwParam1);
|
||||
|
||||
// 初始化掩码为全1 // Initialize mask to all 1s
|
||||
uint mask = 0xFFFFFFFF;
|
||||
|
||||
// 如果目标不是主机玩家且当前不是主机玩家,则过滤会引起气泡文本的修饰符
|
||||
// We should filter out these things that will cause bubble texts
|
||||
CECHostPlayer pHost = EC_ManMessageMono.Instance.GetECManPlayer.GetHostPlayer();
|
||||
if (pCmd.target != pHost.GetCharacterID() && !IsHostPlayer())
|
||||
{
|
||||
mask &= (uint)(MOD.MOD_PHYSIC_ATTACK_RUNE | MOD.MOD_MAGIC_ATTACK_RUNE |
|
||||
MOD.MOD_CRITICAL_STRIKE | MOD.MOD_ENCHANT_FAILED);
|
||||
}
|
||||
|
||||
// 获取修饰符 // Get modifier
|
||||
uint dwModifier = (uint)pCmd.attack_flag;
|
||||
|
||||
// 获取技能类型 // Get skill type
|
||||
int nDamage = -2; // 默认为-2,不会引起受伤动作 // Default to -2, will not cause wounded action
|
||||
|
||||
if (ElementSkill.GetType((uint)pCmd.skill) == (byte)skill_type.TYPE_ATTACK)
|
||||
{
|
||||
// 只有攻击技能会引起受伤动作,伤害值为-1 // Only attack skill will cause wounded action, when damage is -1
|
||||
nDamage = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 其他技能不会引起受伤动作,伤害值设为-2 // Other skills will not cause wounded action, so we set damage to -2
|
||||
nDamage = -2;
|
||||
}
|
||||
|
||||
// 播放攻击效果 // Play attack effect
|
||||
int attackTime = 0;
|
||||
PlayAttackEffect(pCmd.target, pCmd.skill, pCmd.level, nDamage,
|
||||
dwModifier & mask, 0, ref attackTime, pCmd.section);
|
||||
}
|
||||
// 判断是否在飞行 / Check if flying
|
||||
public bool IsFlying()
|
||||
{
|
||||
|
||||
@@ -6,6 +6,7 @@ using CSNetwork.GPDataType;
|
||||
using ModelRenderer.Scripts.Common;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using BrewMonster.Scripts.Chat;
|
||||
using UnityEngine;
|
||||
|
||||
public class CECNPC : CECObject
|
||||
@@ -469,7 +470,7 @@ public class CECNPC : CECObject
|
||||
{
|
||||
return m_pNPCModelPolicy.PlayAttackAction(nAttackSpeed, attackevent);
|
||||
}
|
||||
void NPCTurnFaceTo(int idTarget, float dwTime = 0.3f)
|
||||
public void NPCTurnFaceTo(int idTarget, float dwTime = 0.3f)
|
||||
{
|
||||
if (IsDirFixed())
|
||||
{
|
||||
@@ -1602,6 +1603,34 @@ public class CECNPC : CECObject
|
||||
|
||||
// Get NPC name color
|
||||
public virtual uint GetNameColor() { return 0xffffff00; }
|
||||
|
||||
CECPateText m_pPateLastWords1;
|
||||
CECPateText m_pPateLastWords2;
|
||||
public void SetLastSaidWords(string words, int timeShow = -1)
|
||||
{
|
||||
if (m_pPateLastWords1 == null)
|
||||
{
|
||||
m_pPateLastWords1 = new CECPateText();
|
||||
}
|
||||
|
||||
if (m_pPateLastWords2 == null)
|
||||
{
|
||||
m_pPateLastWords2 = new CECPateText();
|
||||
}
|
||||
|
||||
if (words == null)
|
||||
return;
|
||||
|
||||
int len1 = m_pPateLastWords1.SetText(words, true, out string strWords, false, null);
|
||||
|
||||
/*
|
||||
if (len1 < strWords.Length)
|
||||
m_pPateLastWords2.SetText(strWords.Substring(len1), true, true);
|
||||
else
|
||||
m_pPateLastWords2.Clear();
|
||||
|
||||
m_iLastSayCnt = timeShow > 0 ? timeShow : 20000;*/
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get NPC's CECModel instance
|
||||
|
||||
@@ -1,12 +1,34 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using CSNetwork;
|
||||
|
||||
namespace CSNetwork
|
||||
{
|
||||
public static class AUICommon
|
||||
{
|
||||
public static void AUI_ConvertChatString(string pszChat, char[] pszConv, bool bName)
|
||||
public const char AUICOMMON_ITEM_CODE_START = (char)0xE000;
|
||||
public const char AUICOMMON_ITEM_CODE_END = (char)0xE3FF;
|
||||
public const int AUIMANAGER_MAX_EMOTIONGROUPS = 32;
|
||||
|
||||
public const int MAX_EDITBOX_ITEM_NUM =
|
||||
AUICOMMON_ITEM_CODE_END - AUICOMMON_ITEM_CODE_START + 1;
|
||||
|
||||
public const int MAXNUM_CUSTOM_ITEM = 255;
|
||||
|
||||
public enum EditboxItemType
|
||||
{
|
||||
enumEIEmotion = 0,
|
||||
enumEIIvtrlItem,
|
||||
enumEICoord,
|
||||
enumEIImage,
|
||||
enumEIScriptItem,
|
||||
enumEICustom,
|
||||
enumEINum = enumEICustom + MAXNUM_CUSTOM_ITEM
|
||||
}
|
||||
|
||||
public static void AUI_ConvertChatString(ref string pszChat, ref char[] pszConv, bool bName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(pszChat) || pszConv == null)
|
||||
return;
|
||||
@@ -49,5 +71,782 @@ namespace CSNetwork
|
||||
pszConv[nLen] = '\0';
|
||||
}
|
||||
|
||||
public static string FilterEmotionSet(string szText, int nEmotionSet)
|
||||
{
|
||||
EditBoxItemsSet itemsSet = new EditBoxItemsSet();
|
||||
|
||||
string strOrgText = UnmarshalEditBoxText(szText, itemsSet);
|
||||
|
||||
int nCount = itemsSet.GetItemCount();
|
||||
|
||||
if (nCount == 0)
|
||||
return szText;
|
||||
|
||||
var it = itemsSet.GetItemIterator();
|
||||
|
||||
for (int i = 0; i < nCount; i++)
|
||||
{
|
||||
EditBoxItemBase pItem = it.Current.Value;
|
||||
|
||||
if (pItem.GetType() == EditboxItemType.enumEIEmotion)
|
||||
{
|
||||
int nSet = 0;
|
||||
int nIndex = 0;
|
||||
|
||||
UnmarshalEmotionInfo(pItem.GetInfo(), ref nSet, ref nIndex);
|
||||
|
||||
pItem.SetInfo(MarshalEmotionInfo(nEmotionSet, nIndex));
|
||||
}
|
||||
|
||||
it.MoveNext();
|
||||
}
|
||||
|
||||
return MarshalEditBoxText(strOrgText, itemsSet);
|
||||
}
|
||||
|
||||
public static string MarshalEmotionInfo(int nEmotionSet, int nIndex)
|
||||
{
|
||||
return nEmotionSet.ToString() + ":" + nIndex.ToString();
|
||||
}
|
||||
|
||||
public static void UnmarshalEmotionInfo(string szText, ref int nEmotionSet, ref int nIndex)
|
||||
{
|
||||
if (string.IsNullOrEmpty(szText))
|
||||
return;
|
||||
|
||||
var parts = szText.Split(':');
|
||||
|
||||
if (parts.Length >= 2)
|
||||
{
|
||||
int.TryParse(parts[0], out nEmotionSet);
|
||||
int.TryParse(parts[1], out nIndex);
|
||||
}
|
||||
|
||||
// a_ClampFloor(nEmotionSet, 0);
|
||||
if (nEmotionSet < 0)
|
||||
nEmotionSet = 0;
|
||||
|
||||
// a_ClampFloor(nIndex, 0);
|
||||
if (nIndex < 0)
|
||||
nIndex = 0;
|
||||
|
||||
if (nEmotionSet >= AUIMANAGER_MAX_EMOTIONGROUPS)
|
||||
nEmotionSet = 0;
|
||||
}
|
||||
|
||||
public static string MarshalEditBoxText(string text, EditBoxItemsSet itemsSet)
|
||||
{
|
||||
var sb = new StringBuilder(text.Length * 2);
|
||||
|
||||
int start = 0;
|
||||
|
||||
for (int i = 0; i < text.Length; i++)
|
||||
{
|
||||
char ch = text[i];
|
||||
|
||||
if (IsEditboxItemCode(ch))
|
||||
{
|
||||
sb.Append(text, start, i - start + 1);
|
||||
|
||||
var item = itemsSet.GetItemByChar(ch);
|
||||
if (item != null)
|
||||
sb.Append(item.Serialize());
|
||||
|
||||
start = i + 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (start < text.Length)
|
||||
sb.Append(text, start, text.Length - start);
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static bool IsEditboxItemCode(char ch)
|
||||
{
|
||||
return ch >= AUICOMMON_ITEM_CODE_START && ch <= AUICOMMON_ITEM_CODE_END;
|
||||
}
|
||||
|
||||
public static string UnmarshalEditBoxText(string szText, EditBoxItemsSet itemsSet)
|
||||
{
|
||||
return UnmarshalEditBoxText(
|
||||
szText,
|
||||
itemsSet,
|
||||
0,
|
||||
null,
|
||||
0,
|
||||
0,
|
||||
null,
|
||||
0,
|
||||
false,
|
||||
false,
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
public static string UnmarshalEditBoxText(
|
||||
string? sztext,
|
||||
EditBoxItemsSet itemsSet,
|
||||
int msgIndex,
|
||||
string ivtrItem,
|
||||
uint clIvtrItem,
|
||||
int itemMask,
|
||||
EditboxScriptItem[]? scriptItems,
|
||||
int scriptItemCount,
|
||||
bool underLine,
|
||||
bool sameColor,
|
||||
uint clUnderLine)
|
||||
{
|
||||
if (sztext == null)
|
||||
return "";
|
||||
|
||||
var scriptInfo = new AUI_UNMARSH_SCRIPTITEM_INFO
|
||||
{
|
||||
ScriptItems = scriptItems,
|
||||
ScriptItemCount = scriptItemCount
|
||||
};
|
||||
|
||||
var underlineInfo = new AUI_UNMARSH_UNDERLINE_INFO
|
||||
{
|
||||
UnderLine = underLine,
|
||||
SameColor = sameColor,
|
||||
UnderLineColor = clUnderLine
|
||||
};
|
||||
|
||||
return UnmarshalEditBoxTextEx(
|
||||
sztext,
|
||||
itemsSet,
|
||||
msgIndex,
|
||||
ivtrItem,
|
||||
clIvtrItem,
|
||||
itemMask,
|
||||
scriptInfo,
|
||||
underlineInfo
|
||||
);
|
||||
}
|
||||
|
||||
public static string UnmarshalEditBoxTextEx(
|
||||
string text,
|
||||
EditBoxItemsSet itemsSet,
|
||||
int msgIndex,
|
||||
string ivtrItem,
|
||||
uint clIvtrItem,
|
||||
int itemMask,
|
||||
AUI_UNMARSH_SCRIPTITEM_INFO? scriptInfo,
|
||||
AUI_UNMARSH_UNDERLINE_INFO? underlineInfo)
|
||||
{
|
||||
if (text == null)
|
||||
return "";
|
||||
|
||||
int start = 0;
|
||||
int i = 0;
|
||||
int curScriptIndex = 0;
|
||||
|
||||
var sb = new StringBuilder();
|
||||
|
||||
while (i < text.Length)
|
||||
{
|
||||
char ch = text[i];
|
||||
|
||||
if (IsEditboxItemCode(ch))
|
||||
{
|
||||
if (i > start)
|
||||
sb.Append(text, start, i - start);
|
||||
|
||||
i++;
|
||||
|
||||
EditBoxItemBase? item = EditBoxItemBase.Unserialize(text, ref i);
|
||||
start = i;
|
||||
|
||||
if (item != null)
|
||||
{
|
||||
if ((itemMask & (1 << (int)item.GetType())) != 0)
|
||||
{
|
||||
char newChar = itemsSet.AppendItem(item);
|
||||
|
||||
if (newChar != '\0')
|
||||
{
|
||||
sb.Append(newChar);
|
||||
|
||||
item.SetMsgIndex(msgIndex);
|
||||
|
||||
if (underlineInfo != null)
|
||||
{
|
||||
item.SetUnderLine(
|
||||
underlineInfo.UnderLine,
|
||||
underlineInfo.SameColor,
|
||||
underlineInfo.UnderLineColor);
|
||||
}
|
||||
|
||||
switch (item.GetType())
|
||||
{
|
||||
case EditboxItemType.enumEIIvtrlItem:
|
||||
|
||||
item.SetName(ivtrItem);
|
||||
item.SetColor(clIvtrItem);
|
||||
break;
|
||||
|
||||
case EditboxItemType.enumEIScriptItem:
|
||||
|
||||
if (scriptInfo != null &&
|
||||
curScriptIndex < scriptInfo.ScriptItemCount)
|
||||
{
|
||||
var sItem = scriptInfo.ScriptItems![curScriptIndex];
|
||||
|
||||
item.SetName(sItem.Name);
|
||||
item.SetColor(sItem.Color);
|
||||
|
||||
var data = sItem.Name;
|
||||
if (data != null)
|
||||
item.SetExtraData(sItem.Data, sItem.GetDataSize());
|
||||
|
||||
curScriptIndex++;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
if (i > start)
|
||||
sb.Append(text, start, i - start);
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class AUI_UNMARSH_SCRIPTITEM_INFO
|
||||
{
|
||||
public EditboxScriptItem[]? ScriptItems;
|
||||
public int ScriptItemCount;
|
||||
}
|
||||
|
||||
public class AUI_UNMARSH_UNDERLINE_INFO
|
||||
{
|
||||
public bool UnderLine;
|
||||
public bool SameColor;
|
||||
public uint UnderLineColor;
|
||||
}
|
||||
|
||||
public class EditBoxItemsSet
|
||||
{
|
||||
const char AUICOMMON_ITEM_CODE_START = '\u0001';
|
||||
const char AUICOMMON_ITEM_CODE_END = '\u0010';
|
||||
protected Dictionary<char, EditBoxItemBase> m_Items = new();
|
||||
protected int[] m_ItemsCount = new int[(int)AUICommon.EditboxItemType.enumEINum];
|
||||
protected char m_cNextItemChar;
|
||||
|
||||
public EditBoxItemsSet()
|
||||
{
|
||||
Array.Clear(m_ItemsCount, 0, m_ItemsCount.Length);
|
||||
m_cNextItemChar = AUICommon.AUICOMMON_ITEM_CODE_START;
|
||||
}
|
||||
|
||||
public EditBoxItemsSet(EditBoxItemsSet itemsset)
|
||||
{
|
||||
this.Assign(itemsset);
|
||||
}
|
||||
|
||||
public void Assign(EditBoxItemsSet src)
|
||||
{
|
||||
m_Items.Clear();
|
||||
|
||||
foreach (var kv in src.m_Items)
|
||||
{
|
||||
m_Items[kv.Key] = new EditBoxItemBase(kv.Value);
|
||||
}
|
||||
|
||||
Array.Copy(src.m_ItemsCount, m_ItemsCount, m_ItemsCount.Length);
|
||||
m_cNextItemChar = src.m_cNextItemChar;
|
||||
}
|
||||
|
||||
public void Release()
|
||||
{
|
||||
m_Items.Clear();
|
||||
|
||||
Array.Clear(m_ItemsCount, 0, m_ItemsCount.Length);
|
||||
|
||||
m_cNextItemChar = AUICommon.AUICOMMON_ITEM_CODE_START;
|
||||
}
|
||||
|
||||
public int GetItemCount()
|
||||
{
|
||||
return m_Items.Count;
|
||||
}
|
||||
|
||||
public Dictionary<char, EditBoxItemBase>.Enumerator GetItemIterator()
|
||||
{
|
||||
return m_Items.GetEnumerator();
|
||||
}
|
||||
|
||||
public EditBoxItemBase? GetItemByChar(char ch)
|
||||
{
|
||||
if (!IsEditboxItemCode(ch))
|
||||
throw new Exception("Invalid editbox item char");
|
||||
|
||||
if (m_Items.TryGetValue(ch, out var item))
|
||||
return item;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool IsEditboxItemCode(char ch)
|
||||
{
|
||||
return ch >= AUICOMMON_ITEM_CODE_START && ch <= AUICOMMON_ITEM_CODE_END;
|
||||
}
|
||||
|
||||
public int GetItemCountByType(AUICommon.EditboxItemType type)
|
||||
{
|
||||
return m_ItemsCount[(int)type];
|
||||
}
|
||||
|
||||
public void DelItemByChar(char ch)
|
||||
{
|
||||
if (m_Items.TryGetValue(ch, out var item))
|
||||
{
|
||||
m_ItemsCount[(int)item.GetType()]--;
|
||||
m_Items.Remove(ch);
|
||||
}
|
||||
}
|
||||
|
||||
public char AppendItem(EditBoxItemBase pItem)
|
||||
{
|
||||
if (m_Items.Count >= AUICommon.MAX_EDITBOX_ITEM_NUM)
|
||||
return '\0';
|
||||
|
||||
char cur = m_cNextItemChar;
|
||||
|
||||
do
|
||||
{
|
||||
if (m_Items.ContainsKey(m_cNextItemChar))
|
||||
{
|
||||
m_cNextItemChar = EditboxGetNextChar(m_cNextItemChar);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_Items[m_cNextItemChar] = pItem;
|
||||
m_ItemsCount[(int)pItem.GetType()]++;
|
||||
|
||||
char ret = m_cNextItemChar;
|
||||
m_cNextItemChar = EditboxGetNextChar(m_cNextItemChar);
|
||||
|
||||
return ret;
|
||||
}
|
||||
} while (m_cNextItemChar != cur);
|
||||
|
||||
return '\0';
|
||||
}
|
||||
|
||||
public char EditboxGetNextChar(char cur)
|
||||
{
|
||||
if (cur >= AUICOMMON_ITEM_CODE_END)
|
||||
return AUICOMMON_ITEM_CODE_START;
|
||||
else
|
||||
return (char)(cur + 1);
|
||||
}
|
||||
|
||||
public char AppendItem(AUICommon.EditboxItemType type, uint cl, string szName, string szInfo)
|
||||
{
|
||||
// Implement theo logic C++ gốc
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public int GetTotalExtraDataSize()
|
||||
{
|
||||
int sz = 0;
|
||||
|
||||
foreach (var item in m_Items.Values)
|
||||
{
|
||||
sz += item.GetExtraDataSize();
|
||||
}
|
||||
|
||||
return sz;
|
||||
}
|
||||
}
|
||||
|
||||
public class EditBoxItemBase
|
||||
{
|
||||
protected AUICommon.EditboxItemType m_type;
|
||||
protected uint m_dwColor;
|
||||
protected string m_strName = "";
|
||||
protected string m_strInfo = "";
|
||||
|
||||
protected int m_nMsgIndex;
|
||||
protected int m_nImageIndex;
|
||||
protected int m_nImageFrame;
|
||||
protected float m_fImageScale;
|
||||
|
||||
protected byte[]? m_pExtraData;
|
||||
protected int m_uExtraDataSize;
|
||||
|
||||
protected bool m_bUnderLine;
|
||||
protected bool m_bSameColor;
|
||||
protected uint m_dwUnderLineColor;
|
||||
|
||||
protected static EditBoxItemBase?[] m_mapCustomType = new EditBoxItemBase[AUICommon.MAXNUM_CUSTOM_ITEM];
|
||||
|
||||
public EditBoxItemBase(AUICommon.EditboxItemType type)
|
||||
{
|
||||
m_type = type;
|
||||
m_dwColor = 0xffffffff;
|
||||
|
||||
m_nMsgIndex = 0;
|
||||
m_nImageIndex = 0;
|
||||
m_nImageFrame = 0;
|
||||
m_fImageScale = 1.0f;
|
||||
|
||||
m_bUnderLine = false;
|
||||
m_bSameColor = true;
|
||||
m_dwUnderLineColor = 0;
|
||||
|
||||
RegisterCustomType(type);
|
||||
}
|
||||
|
||||
public EditBoxItemBase(EditBoxItemBase src)
|
||||
{
|
||||
Assign(src);
|
||||
}
|
||||
|
||||
public bool UnserializeContent(string text, ref int index)
|
||||
{
|
||||
int szNext;
|
||||
int szEnd = text.IndexOf("<", index);
|
||||
|
||||
if (szEnd == -1)
|
||||
return false;
|
||||
|
||||
if (m_type == AUICommon.EditboxItemType.enumEICoord)
|
||||
{
|
||||
szNext = szEnd + 1;
|
||||
|
||||
if (!TryParseInt(text, szNext, out int color))
|
||||
return false;
|
||||
|
||||
SetColor((uint)color);
|
||||
|
||||
szEnd = text.IndexOf("><", szNext);
|
||||
if (szEnd == -1) return false;
|
||||
|
||||
szNext = szEnd + 2;
|
||||
|
||||
if (!TryParseInt(text, szNext, out int underline))
|
||||
return false;
|
||||
|
||||
m_bUnderLine = underline != 0;
|
||||
|
||||
szEnd = text.IndexOf("><", szNext);
|
||||
if (szEnd == -1) return false;
|
||||
|
||||
szNext = szEnd + 2;
|
||||
|
||||
if (!TryParseInt(text, szNext, out int underlineColor))
|
||||
return false;
|
||||
|
||||
m_dwUnderLineColor = (uint)underlineColor;
|
||||
|
||||
szEnd = text.IndexOf("><", szNext);
|
||||
if (szEnd == -1) return false;
|
||||
|
||||
m_bSameColor = (m_dwUnderLineColor == m_dwColor);
|
||||
|
||||
szNext = szEnd + 2;
|
||||
|
||||
szEnd = text.IndexOf("><", szNext);
|
||||
if (szEnd == -1) return false;
|
||||
|
||||
SetName(text.Substring(szNext, szEnd - szNext));
|
||||
|
||||
szEnd += 1;
|
||||
}
|
||||
else if (m_type == AUICommon.EditboxItemType.enumEIImage)
|
||||
{
|
||||
szNext = szEnd + 1;
|
||||
|
||||
if (!TryParseUInt(text, szNext, out uint color))
|
||||
return false;
|
||||
|
||||
SetColor(color);
|
||||
|
||||
szEnd = text.IndexOf("><", szNext);
|
||||
if (szEnd == -1) return false;
|
||||
|
||||
szNext = szEnd + 2;
|
||||
|
||||
if (!TryParseInt(text, szNext, out int imageIndex))
|
||||
return false;
|
||||
|
||||
SetImageIndex(imageIndex);
|
||||
|
||||
szEnd = text.IndexOf("><", szNext);
|
||||
if (szEnd == -1) return false;
|
||||
|
||||
szNext = szEnd + 2;
|
||||
|
||||
if (!TryParseInt(text, szNext, out int frame))
|
||||
return false;
|
||||
|
||||
SetImageFrame(frame);
|
||||
|
||||
szEnd = text.IndexOf("><", szNext);
|
||||
if (szEnd == -1) return false;
|
||||
|
||||
szNext = szEnd + 2;
|
||||
|
||||
if (!float.TryParse(text.Substring(szNext), out float f))
|
||||
return false;
|
||||
|
||||
SetImageScale(f);
|
||||
|
||||
szEnd = text.IndexOf("><", szNext);
|
||||
if (szEnd == -1) return false;
|
||||
|
||||
szEnd += 1;
|
||||
}
|
||||
else if (m_type == AUICommon.EditboxItemType.enumEIEmotion)
|
||||
{
|
||||
SetName("W");
|
||||
}
|
||||
|
||||
szNext = szEnd + 1;
|
||||
|
||||
szEnd = text.IndexOf('>', szNext);
|
||||
|
||||
if (szEnd == -1)
|
||||
return false;
|
||||
|
||||
SetInfo(text.Substring(szNext, szEnd - szNext));
|
||||
|
||||
index = szEnd + 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool TryParseInt(string text, int start, out int value)
|
||||
{
|
||||
int end = start;
|
||||
|
||||
while (end < text.Length && char.IsDigit(text[end]))
|
||||
end++;
|
||||
|
||||
return int.TryParse(text.Substring(start, end - start), out value);
|
||||
}
|
||||
|
||||
private bool TryParseUInt(string text, int start, out uint value)
|
||||
{
|
||||
int end = start;
|
||||
|
||||
while (end < text.Length && char.IsDigit(text[end]))
|
||||
end++;
|
||||
|
||||
return uint.TryParse(text.Substring(start, end - start), out value);
|
||||
}
|
||||
|
||||
protected void RegisterCustomType(AUICommon.EditboxItemType type)
|
||||
{
|
||||
if (type >= AUICommon.EditboxItemType.enumEICustom &&
|
||||
type < AUICommon.EditboxItemType.enumEINum)
|
||||
{
|
||||
int index = (int)type - (int)AUICommon.EditboxItemType.enumEICustom;
|
||||
|
||||
if (m_mapCustomType[index] == null)
|
||||
m_mapCustomType[index] = this;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual EditBoxItemBase Create()
|
||||
{
|
||||
return new EditBoxItemBase(m_type);
|
||||
}
|
||||
|
||||
protected static EditBoxItemBase? GetCustomItemFromType(AUICommon.EditboxItemType type)
|
||||
{
|
||||
if (type >= AUICommon.EditboxItemType.enumEICustom &&
|
||||
type < AUICommon.EditboxItemType.enumEINum)
|
||||
{
|
||||
return m_mapCustomType[(int)type - (int)AUICommon.EditboxItemType.enumEICustom];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public uint GetColor() => m_dwColor;
|
||||
public AUICommon.EditboxItemType GetType() => m_type;
|
||||
public string GetName() => m_strName;
|
||||
public string GetInfo() => m_strInfo;
|
||||
public int GetMsgIndex() => m_nMsgIndex;
|
||||
public int GetImageIndex() => m_nImageIndex;
|
||||
public int GetImageFrame() => m_nImageFrame;
|
||||
public float GetImageScale() => m_fImageScale;
|
||||
public bool GetUnderLine() => m_bUnderLine;
|
||||
public bool GetSameColor() => m_bSameColor;
|
||||
public uint GetUnderLineColor() => m_dwUnderLineColor;
|
||||
|
||||
public void SetColor(uint cl) => m_dwColor = cl;
|
||||
public void SetName(string name) => m_strName = name;
|
||||
public void SetInfo(string info) => m_strInfo = info;
|
||||
public void SetMsgIndex(int n) => m_nMsgIndex = n;
|
||||
public void SetImageIndex(int n) => m_nImageIndex = n;
|
||||
public void SetImageFrame(int n) => m_nImageFrame = n;
|
||||
public void SetImageScale(float f) => m_fImageScale = f;
|
||||
|
||||
public byte[]? GetExtraData() => m_pExtraData;
|
||||
public int GetExtraDataSize() => m_uExtraDataSize;
|
||||
|
||||
public void SetUnderLine(bool underline, bool sameColor = true, uint underlineColor = 0)
|
||||
{
|
||||
m_bUnderLine = underline;
|
||||
m_bSameColor = sameColor;
|
||||
m_dwUnderLineColor = underlineColor;
|
||||
}
|
||||
|
||||
public void SetExtraData(byte[] data, int size)
|
||||
{
|
||||
m_pExtraData = new byte[size];
|
||||
Array.Copy(data, 0, m_pExtraData, 0, size);
|
||||
m_uExtraDataSize = size;
|
||||
}
|
||||
|
||||
public virtual string Serialize()
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
public static EditBoxItemBase Unserialize(string text, ref int index)
|
||||
{
|
||||
int start = text.IndexOf('<', index);
|
||||
if (start == -1)
|
||||
return null;
|
||||
|
||||
start++;
|
||||
|
||||
int endType = text.IndexOf('>', start);
|
||||
if (endType == -1)
|
||||
return null;
|
||||
|
||||
string typeStr = text.Substring(start, endType - start);
|
||||
|
||||
if (!int.TryParse(typeStr, out int type))
|
||||
return null;
|
||||
|
||||
if (type < 0 || type >= (int)AUICommon.EditboxItemType.enumEINum)
|
||||
return null;
|
||||
|
||||
index = endType + 1;
|
||||
|
||||
EditBoxItemBase pItem = EditBoxItemBase.GetCustomItemFromType((AUICommon.EditboxItemType)type);
|
||||
|
||||
EditBoxItemBase pItemNew;
|
||||
|
||||
if (pItem == null)
|
||||
{
|
||||
pItemNew = new EditBoxItemBase((AUICommon.EditboxItemType)type);
|
||||
}
|
||||
else
|
||||
{
|
||||
pItemNew = pItem.Create();
|
||||
}
|
||||
|
||||
if (!pItemNew.UnserializeContent(text, ref index))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return pItemNew;
|
||||
}
|
||||
|
||||
public void Assign(EditBoxItemBase src)
|
||||
{
|
||||
m_type = src.m_type;
|
||||
m_dwColor = src.m_dwColor;
|
||||
m_strName = src.m_strName;
|
||||
m_strInfo = src.m_strInfo;
|
||||
|
||||
m_nMsgIndex = src.m_nMsgIndex;
|
||||
m_nImageIndex = src.m_nImageIndex;
|
||||
m_nImageFrame = src.m_nImageFrame;
|
||||
m_fImageScale = src.m_fImageScale;
|
||||
|
||||
if (src.m_pExtraData != null)
|
||||
{
|
||||
SetExtraData(src.m_pExtraData, src.m_uExtraDataSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pExtraData = null;
|
||||
m_uExtraDataSize = 0;
|
||||
}
|
||||
|
||||
RegisterCustomType(m_type);
|
||||
}
|
||||
}
|
||||
|
||||
public class EditboxScriptItem
|
||||
{
|
||||
public string Name { get; set; } = "";
|
||||
public uint Color { get; set; }
|
||||
public byte[]? Data { get; private set; }
|
||||
|
||||
public void SetData(byte[] data)
|
||||
{
|
||||
Data = data.ToArray(); // deep copy
|
||||
}
|
||||
|
||||
public int GetDataSize()
|
||||
{
|
||||
return Data?.Length ?? 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -381,7 +381,7 @@ namespace CSNetwork.C2SCommand
|
||||
public static Octets CreateGetOtherEquipCmd(int iNumID, int[] aIDs)
|
||||
{
|
||||
var cmd = new CMD_GetOtherEquip { size = (ushort)iNumID, idList = aIDs };
|
||||
return SerializeCommand(CommandID.GET_OTHER_EQUIP, cmd);
|
||||
return SerializeCommand(CommandID.GET_OTHER_EQUIP, cmd, false);
|
||||
}
|
||||
|
||||
/// <summary>Create C2S GET_OTHER_EQUIP_DETAIL command (view other player profile/equip). Sends cmd + roleId only (4-byte body).</summary>
|
||||
|
||||
@@ -1539,6 +1539,18 @@ namespace CSNetwork.GPDataType
|
||||
public byte section;
|
||||
};
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct cmd_host_skill_attacked
|
||||
{
|
||||
public int idAttacker;
|
||||
public int idSkill;
|
||||
public int iDamage;
|
||||
public byte cEquipment; // The equipment which is mangled, ��λ������ι����Dz���Ӧ�ñ��ɫ
|
||||
|
||||
public int attack_flag; //��Ǹù����Ƿ��й����Ż����ͷ����Ż������ػ�����
|
||||
public byte speed; //�����ٶ� speed * 50 ms
|
||||
public byte section; // skill section
|
||||
};
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
public struct cmd_matter_info_list
|
||||
{
|
||||
public ushort count;
|
||||
@@ -1800,6 +1812,46 @@ namespace CSNetwork.GPDataType
|
||||
Marshal.FreeHGlobal(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert data become byte array (Demo)
|
||||
/// </summary>
|
||||
/// <param name="dataTypeLenght">Marshal.SizeOf<Data Type>()</param>
|
||||
/// <param name="totalSize">Max size data</param>
|
||||
/// <param name="lenghtHeaderIgnore">Removes the number of leading bytes</param>
|
||||
/// <param name="lenghtLastIgnore">Remove the last byte count</param>
|
||||
/// <returns></returns>
|
||||
public static byte[] ContentBytes(int dataTypeLenght, int totalSize, int lenghtHeaderIgnore, int lenghtLastIgnore = 0)
|
||||
{
|
||||
int dataLength = totalSize - (lenghtHeaderIgnore + lenghtLastIgnore);
|
||||
if(dataLength <= 0) throw new ArgumentException("Buffer không đủ dữ liệu");
|
||||
byte[] dataContent = new byte[dataLength];
|
||||
int offset = dataTypeLenght;
|
||||
|
||||
for (int i = 0; i < dataLength; i++)
|
||||
{
|
||||
dataContent[i] = FromBytes<byte>(dataContent, offset);
|
||||
offset += dataTypeLenght;
|
||||
}
|
||||
return dataContent;
|
||||
}
|
||||
|
||||
public static byte[] ContentBytes<T>(int totalSize, int lenghtHeaderIgnore, int lenghtLastIgnore = 0)
|
||||
{
|
||||
int dataLength = totalSize - (lenghtHeaderIgnore + lenghtLastIgnore);
|
||||
if(dataLength <= 0) throw new ArgumentException("Buffer không đủ dữ liệu");
|
||||
byte[] dataContent = new byte[dataLength];
|
||||
int dataTypeSize = Marshal.SizeOf<T>();
|
||||
int offset = dataTypeSize;
|
||||
|
||||
for (int i = 0; i < dataLength; i++)
|
||||
{
|
||||
dataContent[i] = FromBytes<byte>(dataContent, offset);
|
||||
offset += dataTypeSize;
|
||||
}
|
||||
return dataContent;
|
||||
}
|
||||
|
||||
/// <summary>Parse variable-length cmd_team_member_data from buffer. Returns (header, data[]) and bytes consumed.</summary>
|
||||
public static (cmd_team_member_data_header header, cmd_team_member_data_MEMBER[] data) ParseTeamMemberData(byte[] data, int startIndex = 0)
|
||||
{
|
||||
|
||||
@@ -14,9 +14,15 @@ using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using BrewMonster.PerfectWorld.Scripts.UI;
|
||||
using BrewMonster.Scripts;
|
||||
using BrewMonster.Scripts.Chat;
|
||||
using BrewMonster.Scripts.Managers;
|
||||
using BrewMonster.Scripts.UI;
|
||||
using BrewMonster.Scripts;
|
||||
using BrewMonster.Scripts.Managers;
|
||||
using UnityEngine;
|
||||
using CECPlayer = BrewMonster.CECPlayer;
|
||||
using CommandID = CSNetwork.GPDataType.CommandID;
|
||||
|
||||
namespace CSNetwork
|
||||
@@ -51,6 +57,15 @@ namespace CSNetwork
|
||||
/// <summary>Raised when server sends PROTOCOL_PLAYERLOGOUT(69).</summary>
|
||||
public event Action<playerlogout> PlayerLogoutReceived;
|
||||
|
||||
/// <summary>
|
||||
/// Raised when server sends RPC_ADDFRIENDRQST(204) — another player wants to add you as friend.
|
||||
/// Args: xid (RPC transaction id), srcroleid, askerName.
|
||||
/// </summary>
|
||||
public event Action<uint, int, string> FriendRequestReceived;
|
||||
|
||||
/// <summary>Raised when server sends PROTOCOL_ADDFRIEND_RE(203). Args: retcode (0=success), message.</summary>
|
||||
public event Action<byte, string> AddFriendResultReceived;
|
||||
|
||||
/// <summary>Raised when the underlying network disconnects.</summary>
|
||||
public event Action Disconnected;
|
||||
|
||||
@@ -253,7 +268,7 @@ namespace CSNetwork
|
||||
|
||||
Debug.Log($"[GameSession] Creating role - UserID: {_currentUserId}, Localsid: {_localsid}, Profession: {roleInfo.occupation}, Gender: {roleInfo.gender}");
|
||||
Debug.Log($"[GameSession] RoleInfo details - Name size: {roleInfo.name?.Size ?? 0}, Equipment count: {roleInfo.equipment?.Count ?? 0}, Custom data size: {roleInfo.custom_data?.Size ?? 0}, Race: {roleInfo.race}");
|
||||
|
||||
|
||||
// Log first few bytes of custom_data for debugging
|
||||
if (roleInfo.custom_data != null && roleInfo.custom_data.Size > 0)
|
||||
{
|
||||
@@ -262,7 +277,7 @@ namespace CSNetwork
|
||||
string hexPreview = BitConverter.ToString(customDataPreview).Replace("-", " ");
|
||||
Debug.Log($"[GameSession] Custom_data preview (first 16 bytes): {hexPreview}");
|
||||
}
|
||||
|
||||
|
||||
Debug.Log($"[GameSession] Sending createrole protocol (Type: {createRoleProtocol.Type})");
|
||||
SendProtocol(createRoleProtocol);
|
||||
}
|
||||
@@ -325,7 +340,7 @@ namespace CSNetwork
|
||||
// Initialize custom data exactly as C++ does: memset to 0, then set specific values
|
||||
// This matches C++ LoadDefaultCustomizeData behavior
|
||||
roleInfo.custom_data = CreateDefaultCustomizeData(profession, gender);
|
||||
|
||||
|
||||
// Initialize other empty custom data fields
|
||||
roleInfo.custom_status = new Octets();
|
||||
roleInfo.charactermode = new Octets();
|
||||
@@ -364,31 +379,31 @@ namespace CSNetwork
|
||||
const int CUSTOMIZE_DATA_SIZE = 176; // Match server expectation (176 bytes from response)
|
||||
const uint CUSTOMIZE_DATA_VERSION = 0x10007001; // CUSTOMIZE_DATA_VERSION from C++
|
||||
byte[] customDataBytes = new byte[CUSTOMIZE_DATA_SIZE];
|
||||
|
||||
|
||||
// Step 1: memset to 0 (already done by new byte[])
|
||||
|
||||
|
||||
// Step 2: Set dwVersion at offset 0-3 (little-endian)
|
||||
byte[] versionBytes = BitConverter.GetBytes(CUSTOMIZE_DATA_VERSION);
|
||||
Array.Copy(versionBytes, 0, customDataBytes, 0, 4);
|
||||
|
||||
|
||||
// Step 3: FACE_CUSTOMIZEDATA at offset 4-87 is already zero (84 bytes)
|
||||
// (In C++ this would be loaded from INI, but for now we use zeros)
|
||||
|
||||
|
||||
// Step 4: bodyID at offset 88-89 is already zero (2 bytes)
|
||||
// Note: There might be 2 bytes padding here if struct is 4-byte aligned
|
||||
|
||||
// Step 5: Set colorBody to 0xffffffff
|
||||
|
||||
// Step 5: Set colorBody to 0xffffffff
|
||||
// Try offset 90 first (no padding), if that doesn't work try 92 (with padding)
|
||||
byte[] colorBodyBytes = BitConverter.GetBytes(0xffffffffu);
|
||||
Array.Copy(colorBodyBytes, 0, customDataBytes, 90, 4); // Try 90 first
|
||||
|
||||
|
||||
// Step 6: Set all 6 scale fields to 128
|
||||
// Try offset 94 first (no padding), if that doesn't work try 96 (with padding)
|
||||
for (int i = 94; i < 100; i++)
|
||||
{
|
||||
customDataBytes[i] = 128;
|
||||
}
|
||||
|
||||
|
||||
return new Octets(customDataBytes);
|
||||
}
|
||||
|
||||
@@ -531,7 +546,6 @@ namespace CSNetwork
|
||||
private void OnProtocolReceived(Protocol protocol)
|
||||
{
|
||||
_logger.Log(LogType.Debug, $"Received protocol: {protocol.GetType().Name} (Type: {protocol.Type})");
|
||||
BMLogger.Log($"Received protocol: {protocol.GetType().Name} (Type: {protocol.Type})");
|
||||
if (protocol is null)
|
||||
return;
|
||||
|
||||
@@ -569,6 +583,9 @@ namespace CSNetwork
|
||||
case ProtocolType.PROTOCOL_CHATMESSAGE:
|
||||
OnPrtcChatMessage(protocol, false);
|
||||
break;
|
||||
case ProtocolType.PROTOCOL_WORLDCHAT:
|
||||
OnPrtcWorldChat(protocol, false);
|
||||
break;
|
||||
case ProtocolType.PROTOCOL_PLAYERBASEINFO_RE:
|
||||
OnPrtcPlayerBaseInfoRe(protocol);
|
||||
break;
|
||||
@@ -587,6 +604,14 @@ namespace CSNetwork
|
||||
}
|
||||
break;
|
||||
|
||||
case ProtocolType.RPC_ADDFRIENDRQST:
|
||||
OnAddFriendRqst((addfriendrqst)protocol);
|
||||
break;
|
||||
|
||||
case ProtocolType.PROTOCOL_ADDFRIEND_RE:
|
||||
OnAddFriendRe((addfriend_re)protocol);
|
||||
break;
|
||||
|
||||
default:
|
||||
_logger.Log(LogType.Warning, $"Received unhandled protocol type: {protocol.GetPType()}");
|
||||
break;
|
||||
@@ -601,6 +626,49 @@ namespace CSNetwork
|
||||
// PostToUnityContext(() => PlayerLogoutReceived?.Invoke(protocol));
|
||||
}
|
||||
|
||||
private void OnAddFriendRqst(addfriendrqst p)
|
||||
{
|
||||
string askerName = "";
|
||||
if (p.Srcname != null && p.Srcname.Size > 0)
|
||||
askerName = System.Text.Encoding.Unicode.GetString(p.Srcname.ToArray(), 0, p.Srcname.Size);
|
||||
// Forward xid + srcroleid + name to Unity layer so it can answer via Friend_AddResponse(xid, agree)
|
||||
PostToUnityContext(() => FriendRequestReceived?.Invoke(p.Xid, p.Srcroleid, askerName));
|
||||
}
|
||||
|
||||
private void OnAddFriendRe(addfriend_re p)
|
||||
{
|
||||
string friendName = "";
|
||||
if (p.info?.name != null && p.info.name.Size > 0)
|
||||
friendName = System.Text.Encoding.Unicode.GetString(p.info.name.ToArray(), 0, p.info.name.Size);
|
||||
string msg = p.retcode == 0
|
||||
? $"Add friend success: {friendName}"
|
||||
: $"Add friend failed: {GetAddFriendRetcodeMessage(p.retcode)}";
|
||||
if (p.retcode == 0)
|
||||
{
|
||||
Friend_GetList();
|
||||
}
|
||||
|
||||
PostToUnityContext(() => AddFriendResultReceived?.Invoke(p.retcode, msg));
|
||||
}
|
||||
|
||||
/// <summary>FS_ERR server retcodes for addfriend_re.</summary>
|
||||
private static string GetAddFriendRetcodeMessage(byte retcode)
|
||||
{
|
||||
switch (retcode)
|
||||
{
|
||||
case 0: return "Success";
|
||||
case 1: return "Player is offline";
|
||||
case 2: return "Request was refused";
|
||||
case 3: return "Timeout";
|
||||
case 4: return "No remaining space";
|
||||
case 5: return "Not found";
|
||||
case 6: return "Invalid parameter";
|
||||
case 7: return "Duplicate entry";
|
||||
case 8: return "Friend list has not been retrieved yet";
|
||||
default: return $"retcode={retcode}";
|
||||
}
|
||||
}
|
||||
|
||||
// void CECGameSession::OnPrtcPlayerLogout(GNET::Protocol* pProtocol)
|
||||
// {
|
||||
// using namespace GNET;
|
||||
@@ -989,7 +1057,7 @@ namespace CSNetwork
|
||||
}
|
||||
else if (pCmd.iMessage == 133 || pCmd.iMessage == 134)
|
||||
{
|
||||
// deal failed
|
||||
// deal failed
|
||||
//pGameRun.PostMessage(MSG_HST_BUY_SELL_FAIL, MAN_PLAYER, 0, (DWORD)pDataBuf, pCmdHeader.cmd);
|
||||
}
|
||||
else if (pCmd.iMessage == 158)
|
||||
@@ -1115,6 +1183,9 @@ namespace CSNetwork
|
||||
case CommandID.HOST_SKILL_ATTACK_RESULT:
|
||||
EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_SKILLRESULT, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
|
||||
break;
|
||||
case CommandID.HOST_SKILL_ATTACKED:
|
||||
EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_SKILLATTACKED, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
|
||||
break;
|
||||
case CommandID.CHANGE_FACE_START:
|
||||
case CommandID.CHANGE_FACE_END:
|
||||
EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_CHANGEFACE, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader, dwDataSize);
|
||||
@@ -1300,6 +1371,16 @@ namespace CSNetwork
|
||||
case CommandID.LEAVE_SANCTUARY:
|
||||
EC_ManMessage.PostMessage(EC_MsgDef.MSG_HST_SANCTUARY, MANAGER_INDEX.MAN_PLAYER, 0, pDataBuf, pCmdHeader);
|
||||
break;
|
||||
case CommandID.OBJECT_SKILL_ATTACK_RESULT:
|
||||
{
|
||||
cmd_object_skill_attack_result pCmd = GPDataTypeHelper.FromBytes <cmd_object_skill_attack_result>((byte[])pDataBuf);
|
||||
if (ISPLAYERID(pCmd.attacker_id))
|
||||
EC_ManMessage.PostMessage(EC_MsgDef.MSG_PM_PLAYERSKILLRESULT, MANAGER_INDEX.MAN_PLAYER, -1,pDataBuf, pCmdHeader);
|
||||
else if (ISNPCID(pCmd.attacker_id))
|
||||
EC_ManMessage.PostMessage(EC_MsgDef.MSG_NM_NPCSKILLRESULT, MANAGER_INDEX.MAN_NPC, 0, pDataBuf, pCmdHeader);
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
#if UNITY_EDITOR
|
||||
if (isDebug)
|
||||
@@ -1326,7 +1407,7 @@ namespace CSNetwork
|
||||
private void HandleCreateRoleResponse(createrole_re protocol)
|
||||
{
|
||||
Debug.Log($"[GameSession] HandleCreateRoleResponse - result: {protocol.result}, roleid: {protocol.roleid}");
|
||||
|
||||
|
||||
if (protocol.result != (int)ErrCode.ERR_SUCCESS)
|
||||
{
|
||||
string errorMsg = $"Create role failed with result code: {protocol.result} (ERR_SUCCESS = {(int)ErrCode.ERR_SUCCESS})";
|
||||
@@ -1347,7 +1428,7 @@ namespace CSNetwork
|
||||
private void HandleErrorInfo(errorinfo protocol)
|
||||
{
|
||||
Debug.LogError($"[GameSession] Server error - Errcode: {protocol.Errcode}");
|
||||
|
||||
|
||||
// If we're waiting for create role response and get an error, fail the callback
|
||||
if (_createRoleCallback != null)
|
||||
{
|
||||
@@ -1639,8 +1720,8 @@ namespace CSNetwork
|
||||
g.Data = C2SCommandFactory.CreatePlayerLogoutCmd(outType);
|
||||
SendProtocol(g, complete);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void c2s_SendCmdStopMove(in Vector3 vDest, float fSpeed, int iMoveMode,
|
||||
byte byDir, ushort wStamp, int iTime)
|
||||
{
|
||||
@@ -1657,17 +1738,94 @@ namespace CSNetwork
|
||||
gamedatasend.Data = C2SCommandFactory.CreateNakeCmd(C2SCommand.CommandID.ENTER_PK_PROTECTED);
|
||||
SendProtocol(gamedatasend);
|
||||
}
|
||||
|
||||
public const int SUPER_FAR_CRY_EMOTION_SET = 6;
|
||||
/// <summary>
|
||||
/// Convert string become byte (Encoding.Unicode trong .NET = UTF-16 LE)
|
||||
/// </summary>
|
||||
/// <param name="cChannel"> Data type: enum, Chanel chat </param>
|
||||
/// <param name="szMsg"> Text send to server </param>
|
||||
/// <param name="iPack"></param>
|
||||
/// <param name="iSlot"></param>
|
||||
public void SendChatData(byte cChannel, in string szMsg, int iPack, int iSlot)
|
||||
{
|
||||
publicchat publicChat = new publicchat();
|
||||
publicChat.Channel = cChannel;
|
||||
publicChat.Roleid = m_iCharID;
|
||||
if (string.IsNullOrEmpty(szMsg))
|
||||
return;
|
||||
|
||||
publicchat p = new publicchat();
|
||||
p.Channel = cChannel;
|
||||
p.Roleid = m_iCharID;
|
||||
if (iPack == EC_Inventory.IVTRTYPE_CLIENT_GENERALCARD_PACK)
|
||||
{
|
||||
CECHostPlayer pHost = EC_Game.GetGameRun().GetHostPlayer();
|
||||
EC_Inventory clientPack =
|
||||
pHost != null ? pHost.GetPack(EC_Inventory.IVTRTYPE_CLIENT_GENERALCARD_PACK) : null;
|
||||
|
||||
if (pHost != null && clientPack != null)
|
||||
{
|
||||
EC_IvtrItem pCard = clientPack.GetItem(iSlot);
|
||||
|
||||
if (pCard != null)
|
||||
{
|
||||
short cmd = (short)CHAT_S2C.EChatS2CCommand.CHAT_GENERALCARD_COLLECTION;
|
||||
int cardId = pCard.GetTemplateID();
|
||||
|
||||
byte[] bytes = new byte[6];
|
||||
|
||||
BitConverter.GetBytes(cmd).CopyTo(bytes, 0);
|
||||
BitConverter.GetBytes(cardId).CopyTo(bytes, 2);
|
||||
p.Data.Replace(bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (iPack >= 0 && iSlot >= 0)
|
||||
{
|
||||
byte[] bytes = new byte[5];
|
||||
|
||||
short cmd = (short)CHAT_S2C.EChatS2CCommand.CHAT_EQUIP_ITEM;
|
||||
short index = (short)iSlot;
|
||||
byte where = (byte)iPack;
|
||||
|
||||
BitConverter.GetBytes(cmd).CopyTo(bytes, 0);
|
||||
bytes[2] = where;
|
||||
BitConverter.GetBytes(index).CopyTo(bytes, 3);
|
||||
p.Data.Replace(bytes);
|
||||
}
|
||||
|
||||
byte[] unicodeBytes = Encoding.Unicode.GetBytes(szMsg);
|
||||
publicChat.Msg.Replace(unicodeBytes);
|
||||
_logger.Log(LogType.Warning, $"HoangDev : publicChat {publicChat}");
|
||||
SendProtocol(publicChat);
|
||||
p.Msg.Replace(unicodeBytes);
|
||||
SendProtocol(p);
|
||||
|
||||
if (cChannel is (byte)ChatChannel.GP_CHAT_LOCAL
|
||||
or (byte)ChatChannel.GP_CHAT_FARCRY
|
||||
or (byte)ChatChannel.GP_CHAT_SUPERFARCRY
|
||||
or (byte)ChatChannel.GP_CHAT_BATTLE
|
||||
or (byte)ChatChannel.GP_CHAT_COUNTRY) {
|
||||
CECHostPlayer pHost = EC_Game.GetGameRun().GetHostPlayer();
|
||||
//int nEmotionSet = pHost.GetCurEmotionSet();
|
||||
int nEmotionSet = 0;
|
||||
string strMsg = szMsg;
|
||||
if (cChannel == (byte)ChatChannel.GP_CHAT_SUPERFARCRY)
|
||||
{
|
||||
if (strMsg.Length > 8)
|
||||
{
|
||||
strMsg = strMsg.Substring(0, strMsg.Length - 8);
|
||||
}
|
||||
nEmotionSet = SUPER_FAR_CRY_EMOTION_SET;
|
||||
}
|
||||
|
||||
EC_IvtrItem pItem = null;
|
||||
if (iPack >= 0 && iSlot >= 0) {
|
||||
EC_Inventory pPack = pHost.GetPack(iPack);
|
||||
if (pPack != null) {
|
||||
pItem = pPack.GetItem(iSlot);
|
||||
}
|
||||
}
|
||||
EventBus.PublishChannel(pHost.GetCharacterID(), new EventChatMessageOnTopPlayer(pHost.GetCharacterID(), szMsg));
|
||||
EventBus.Publish(new ChatMessageEvent(szMsg, p.Channel));
|
||||
}
|
||||
}
|
||||
|
||||
public void LoadConfigData()
|
||||
{
|
||||
getuiconfig p = new getuiconfig();
|
||||
@@ -1682,7 +1840,7 @@ namespace CSNetwork
|
||||
public void SaveConfigData(byte[] pBuf, int len)
|
||||
{
|
||||
BMLogger.Log($"[MH] Session.SaveConfigData | len={len}");
|
||||
|
||||
|
||||
if (pBuf == null || len <= 0) return;
|
||||
var p = new setuiconfig();
|
||||
p.Roleid = m_iCharID;
|
||||
@@ -1690,7 +1848,7 @@ namespace CSNetwork
|
||||
byte[] slice = new byte[len];
|
||||
Buffer.BlockCopy(pBuf, 0, slice, 0, len);
|
||||
p.Ui_config = new Octets(slice);
|
||||
|
||||
|
||||
// return;
|
||||
SendProtocol(p);
|
||||
}
|
||||
@@ -1700,23 +1858,226 @@ namespace CSNetwork
|
||||
m_iCharID = iCharID;
|
||||
}
|
||||
|
||||
private void OnPrtcChatMessage(Protocol pProtocol, bool bCalledagain)
|
||||
private bool OnPrtcWorldChat(Protocol pProtocol, bool bCalledagain)
|
||||
{
|
||||
worldchat p = (worldchat)pProtocol;
|
||||
/*if (p.Channel == (byte)ChatChannel.GP_CHAT_FARCRY && p.Roleid == 24)
|
||||
{
|
||||
OnTaskChatMessage(p.Msg.RawBuffer, p.Msg.Size);
|
||||
return true;
|
||||
}*/
|
||||
|
||||
// TODO: Porting logic OnPrtcWorldChat hoàn chỉnh từ C++ if needed
|
||||
Debug.Log("[Cuong] OnPrtcWorldChat");
|
||||
return true;
|
||||
}
|
||||
|
||||
/*private void OnTaskChatMessage(byte[] pBuf, int sz)
|
||||
{
|
||||
... logic ẩn ...
|
||||
}*/
|
||||
|
||||
private bool OnBattleChatMessage(chatmessage p, List<int> pPendingFactions)
|
||||
{
|
||||
// Tương đương OnBattleChatMessage trong C++
|
||||
// Hiện tại đơn giản hóa việc hiển thị, thực tế cần parse nội dung Battle cụ thể
|
||||
Debug.Log($"[Battle Chat] RoleID: {p.Srcroleid}");
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool OnFortressChatMessage(chatmessage p, List<int> pPendingFactions)
|
||||
{
|
||||
// Tương đương OnFortressChatMessage trong C++
|
||||
Debug.Log($"[Fortress Chat] RoleID: {p.Srcroleid}");
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool OnKingChatMessage(chatmessage p, bool bGetPlayerName = true)
|
||||
{
|
||||
// Tương đương OnKingChatMessage trong C++
|
||||
Debug.Log($"[King Chat] RoleID: {p.Srcroleid}");
|
||||
return true;
|
||||
}
|
||||
|
||||
private void OnFactionPVPChatMessage(chatmessage p)
|
||||
{
|
||||
// Tương đương OnFactionPVPChatMessage trong C++
|
||||
Debug.Log($"[FactionPVP Chat] RoleID: {p.Srcroleid}");
|
||||
}
|
||||
|
||||
private bool OnPrtcChatMessage(Protocol pProtocol, bool bCalledagain)
|
||||
{
|
||||
CECGameUIMan pGameUI = EC_Game.GetGameRun().GetUIManager().GetInGameUIMan();
|
||||
|
||||
chatmessage p = (chatmessage)pProtocol;
|
||||
//var channel = (ChatChannel)p.Channel;
|
||||
Debug.Log("[Cuong] reciver chat channel: " + p.Channel);
|
||||
|
||||
string strTemp = System.Text.Encoding.Unicode.GetString(p.Msg.ToArray(), 0, p.Msg.Length);
|
||||
if (Chat_GameSession.ShouldBlockByLevel(p))
|
||||
{
|
||||
Debug.Log("[Cuong] 1");
|
||||
return true;
|
||||
}
|
||||
|
||||
_logger.Log(LogType.Warning, $"HoangDev : OnPrtcChatMessage :{strTemp}");
|
||||
EventBus.Publish(new ChatMessageEvent(strTemp));
|
||||
EC_IvtrItem pItem = CHAT_S2C.CreateChatItem(p.Data);
|
||||
|
||||
string szMsg = null;
|
||||
string strTemp = Encoding.Unicode.GetString(p.Msg.ToArray(), 0, p.Msg.Length);
|
||||
string strMsgOrigion = strTemp;
|
||||
// Todo: Show Text on Ui Game
|
||||
//strTemp = pGameUI.FilterInvalidTags(strTemp, pItem==NULL);
|
||||
/*if (!Chat_GameSession.PolicyResolver(pProtocol, p, ref strTemp))
|
||||
{
|
||||
Debug.Log("[Cuong] 2");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Chat_GameSession.HanldeGPChatSystem(p, bCalledagain))
|
||||
{
|
||||
Debug.Log("[Cuong] 3");
|
||||
}*/
|
||||
|
||||
if (p.Channel is (byte)ChatChannel.GP_CHAT_BROADCAST
|
||||
or (byte)ChatChannel.GP_CHAT_SYSTEM || p.Srcroleid == 0)
|
||||
{
|
||||
if (p.Channel == (byte)ChatChannel.GP_CHAT_SYSTEM && p.Srcroleid > 0)
|
||||
{
|
||||
switch (p.Srcroleid)
|
||||
{
|
||||
case 1: case 2: case 3: case 4: case 6: case 7: // Battle Message
|
||||
if (!bCalledagain)
|
||||
{
|
||||
List<int> pending = new List<int>();
|
||||
if (!OnBattleChatMessage(p, pending))
|
||||
{
|
||||
// Handle pending faction info
|
||||
return false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 18: case 19: case 20: case 21: case 22: // Auction Message
|
||||
// pGameUI.AddSysAuctionMessage(...)
|
||||
Debug.Log("[Auction] " + strTemp);
|
||||
EventBus.Publish(new ChatMessageEvent(strTemp, p.Channel));
|
||||
break;
|
||||
case 24: // Task Message
|
||||
// OnTaskChatMessage(p.Data.RawBuffer, p.Data.Size);
|
||||
break;
|
||||
case 29: case 30: case 31: case 32: case 33: case 34:
|
||||
case 35: case 36: case 37: case 38: case 39: case 40:
|
||||
case 41: case 42: case 43: case 45: // Fortress Message
|
||||
OnFortressChatMessage(p, null);
|
||||
break;
|
||||
case 46: case 47: case 48: case 49: // Country Battle
|
||||
// OnCountryChatMessage(p);
|
||||
break;
|
||||
case 50: case 51: case 52: case 53: case 54:
|
||||
case 55: case 56: case 57: case 58: case 59: // King Chat
|
||||
OnKingChatMessage(p);
|
||||
break;
|
||||
case 60: case 61: case 62: case 63: case 64: // Faction PVP
|
||||
OnFactionPVPChatMessage(p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("[Cuong] 5");
|
||||
EventBus.Publish(new ChatMessageEvent(strTemp, p.Channel));
|
||||
}
|
||||
}else if (p.Channel == (byte)ChatChannel.GP_CHAT_INSTANCE && p.Srcroleid == 1)
|
||||
{
|
||||
Debug.Log("[Cuong] 6");
|
||||
// Chat_GameSession.AUICTranslate trans;
|
||||
// EC_Game.GetGameRun().GetUIManager().GetInGameUIMan().AddHeartBeatHint(trans.Translate(szMsg ));
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("[Cuong] Other");
|
||||
CECStringTab pStrTab = EC_Game.GetFixedMsgs();
|
||||
|
||||
if (ISPLAYERID(p.Srcroleid))
|
||||
{
|
||||
string szName = EC_Game.GetGameRun().GetPlayerName(p.Srcroleid, false);
|
||||
if (string.IsNullOrEmpty(strTemp))
|
||||
{
|
||||
Debug.Log("[Cuong] Other 0");
|
||||
if (!bCalledagain)
|
||||
{
|
||||
Chat_GameSession.AddElemForPendingProtocols(pProtocol);
|
||||
Chat_GameSession.AddChatPlayerID(p.Srcroleid);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("[Cuong] Other 1");
|
||||
char[] szText = new char[80];
|
||||
AUICommon.AUI_ConvertChatString(ref szName,ref szText, false);
|
||||
|
||||
string fmt = AUICommon.ConvertPrintfToCSharpFormat(pStrTab.GetWideString((int)FixedMsg.FIXMSG_CHAT));
|
||||
string str = string.Format(
|
||||
fmt,
|
||||
szName,
|
||||
strTemp
|
||||
);
|
||||
// Convert to EventBus
|
||||
/*EC_Game.GetGameRun().AddChatMessage(strTemp, p.Channel, p.Srcroleid,
|
||||
null, 0, p.Emotion, null, strMsgOrigion);*/
|
||||
EventBus.Publish(new ChatMessageEvent(str, p.Channel));
|
||||
// Set player's last said words
|
||||
CECPlayer pPlayer = EC_Game.GetGameRun().GetWorld().GetPlayerMan().GetPlayer(p.Srcroleid);
|
||||
if (pPlayer != null)
|
||||
{
|
||||
EventBus.PublishChannel(p.Srcroleid, new EventChatMessageOnTopPlayer(p.Srcroleid, strTemp));
|
||||
//pPlayer.SetLastSaidWords(strTemp, p.Emotion);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(ISNPCID(p.Srcroleid))
|
||||
{
|
||||
Debug.Log("[Cuong] ISNPCID " + strTemp);
|
||||
CECNPC pNPC = EC_Game.GetGameRun().GetWorld().GetNPCMan().GetNPC(p.Srcroleid);
|
||||
|
||||
if (pNPC != null)
|
||||
{
|
||||
string str;
|
||||
string template = AUICommon.ConvertPrintfToCSharpFormat(pStrTab.GetWideString((int)FixedMsg.FIXMSG_CHAT2));
|
||||
|
||||
string message = string.Format(
|
||||
template,
|
||||
pNPC.GetName(),
|
||||
strTemp
|
||||
);
|
||||
|
||||
EC_Game.GetGameRun().AddChatMessage(
|
||||
message,
|
||||
p.Channel,
|
||||
p.Srcroleid,
|
||||
null,
|
||||
0,
|
||||
p.Emotion
|
||||
);
|
||||
EventBus.Publish(new ChatMessageEvent(message));
|
||||
|
||||
CECUIHelper.RemoveNameFlagFromNPCChat(strTemp, out szMsg);
|
||||
|
||||
pNPC.SetLastSaidWords(szMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public struct ChatMessageEvent
|
||||
{
|
||||
public string context;
|
||||
public byte channel;
|
||||
|
||||
public ChatMessageEvent(string context)
|
||||
public ChatMessageEvent(string context, byte channel = 0)
|
||||
{
|
||||
this.context = context;
|
||||
this.channel = channel;
|
||||
}
|
||||
}
|
||||
public void OnPrtcGetConfigRe(Protocol pProtocol)
|
||||
@@ -1733,7 +2094,7 @@ namespace CSNetwork
|
||||
EC_Game.GetConfigs().ApplyUserSetting();
|
||||
}
|
||||
|
||||
// Now, Get config data request is sent after all host initial data ready.
|
||||
// Now, Get config data request is sent after all host initial data ready.
|
||||
// so when we receive this reply, we can do some last work before game
|
||||
// really starts. Maybe it's not the best place to do these work, but
|
||||
// now we do it here.
|
||||
@@ -1744,7 +2105,7 @@ namespace CSNetwork
|
||||
pGameUI.EnableUI(true);
|
||||
|
||||
// Get referral name for adding friend or other display
|
||||
//TODO: a Hung lam phan select role info di
|
||||
//TODO: a Hung lam phan select role info di
|
||||
/* RoleInfo info = EC_Game.GetGameRun().GetSelectedRoleInfo();
|
||||
if (info.referrer_role > 0)
|
||||
GetPlayerBriefInfo(1, info.referrer_role, 2);*/
|
||||
@@ -1765,7 +2126,7 @@ namespace CSNetwork
|
||||
CECMCDownload::GetInstance().SendGetDownloadOK();*/
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void OnPrtcSetConfigRe(Protocol pProtocol)
|
||||
{
|
||||
SetUIConfig_Re p = (SetUIConfig_Re)pProtocol;
|
||||
@@ -1775,12 +2136,12 @@ namespace CSNetwork
|
||||
if (CECGameRun.Instance != null)
|
||||
{
|
||||
TestLogoutLogic.Instance.WasClientSendLogoutMessage = true;
|
||||
|
||||
|
||||
CECGameRun.Instance.GetPendingLogOut().TriggerAll();
|
||||
CECGameRun.Instance.GetPendingLogOut().Clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void OnPrtcPlayerBaseInfoRe(Protocol pProtocol)
|
||||
{
|
||||
playerbaseinfo_re p = (playerbaseinfo_re)pProtocol;
|
||||
@@ -1898,7 +2259,7 @@ namespace CSNetwork
|
||||
BYTE* pBuf = (BYTE*)a_malloctemp(iSize);
|
||||
if (!pBuf)
|
||||
return;
|
||||
|
||||
|
||||
((cmd_header*)pBuf)->cmd = C2S::GET_OTHER_EQUIP;
|
||||
|
||||
cmd_get_other_equip* pCmd = (cmd_get_other_equip*)(pBuf + sizeof (cmd_header));
|
||||
@@ -1915,7 +2276,7 @@ namespace CSNetwork
|
||||
var idlist = new int[iNumSend];
|
||||
for (int i=0; i < iNumSend; i++)
|
||||
idlist[i] = aIDs[iCount+i];
|
||||
|
||||
|
||||
gamedatasend gamedatasend = new gamedatasend();
|
||||
gamedatasend.Data = C2SCommandFactory.CreateGetOtherEquipCmd(iNumID, idlist);
|
||||
SendProtocol(gamedatasend);
|
||||
@@ -2031,7 +2392,7 @@ namespace CSNetwork
|
||||
gamedatasend.Data = C2SCommandFactory.CreateNakeCmd(C2SCommand.CommandID.STAND_UP);
|
||||
SendProtocol(gamedatasend);
|
||||
}
|
||||
|
||||
|
||||
public void c2s_SendCmdAutoTeamSetGoal(int type, int goal_id, int op)
|
||||
{
|
||||
gamedatasend gamedatasend = new gamedatasend();
|
||||
@@ -2074,6 +2435,43 @@ namespace CSNetwork
|
||||
SendProtocol(g);
|
||||
}
|
||||
|
||||
/// <summary>Send PROTOCOL_ADDFRIEND(202). Port of CECGameSession::friend_Add(idPlayer, szName).</summary>
|
||||
public void Friend_Add(int idPlayer, string name)
|
||||
{
|
||||
var p = new addfriend();
|
||||
p.Srcroleid = m_iCharID;
|
||||
p.Dstroleid = idPlayer;
|
||||
if (!string.IsNullOrEmpty(name))
|
||||
p.Dstname = new Octets(System.Text.Encoding.Unicode.GetBytes(name));
|
||||
else
|
||||
p.Dstname = new Octets();
|
||||
p.Srclsid = (int)_localsid;
|
||||
SendProtocol(p);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Send RPC_ADDFRIENDRQST(204) response. Port of CECGameSession::friend_AddResponse(dwHandle, bAgree).
|
||||
/// Answer to received friend request: xid from request, agree = true (0) or false (69).
|
||||
/// Matches GNET Rpc XID: low bit = is_request; response must clear that bit.
|
||||
/// </summary>
|
||||
public void Friend_AddResponse(uint xid, bool agree)
|
||||
{
|
||||
const byte ERR_TRADE_AGREE = 0;
|
||||
const byte ERR_TRADE_REFUSE = 69;
|
||||
uint responseXid = xid & 0x7FFFFFFFu; // clear request bit, keep count
|
||||
var p = new addfriendrqstres(responseXid, agree ? ERR_TRADE_AGREE : ERR_TRADE_REFUSE);
|
||||
SendProtocol(p);
|
||||
}
|
||||
|
||||
/// <summary>Send PROTOCOL_GETFRIENDS(206). Port of CECGameSession::friend_GetList().</summary>
|
||||
public void Friend_GetList()
|
||||
{
|
||||
var p = new getfriends();
|
||||
p.Roleid = m_iCharID;
|
||||
p.Localsid = (int)_localsid;
|
||||
SendProtocol(p);
|
||||
}
|
||||
|
||||
public void c2s_SendCmdTeamKickMember(int idMember)
|
||||
{
|
||||
var g = new gamedatasend();
|
||||
@@ -2082,7 +2480,7 @@ namespace CSNetwork
|
||||
}
|
||||
|
||||
public void c2s_SendCmdTeamLeaveParty()
|
||||
{
|
||||
{
|
||||
var g = new gamedatasend();
|
||||
g.Data = C2SCommandFactory.CreateTeamLeavePartyCommand();
|
||||
SendProtocol(g);
|
||||
@@ -2119,9 +2517,9 @@ namespace CSNetwork
|
||||
|
||||
public void c2s_CmdGoto(float x, float y, float z)
|
||||
{
|
||||
c2s_SendCmdGoto(x, y, z);
|
||||
c2s_SendCmdGoto(x, y, z);
|
||||
}
|
||||
|
||||
|
||||
// Send C2S::GOTO command data
|
||||
void c2s_SendCmdGoto(float x, float y, float z)
|
||||
{
|
||||
@@ -2248,7 +2646,7 @@ namespace CSNetwork
|
||||
gamedatasend.Data = C2SCommandFactory.CreateNPCSevRestorePetCmd(iPetIdx);
|
||||
SendProtocol(gamedatasend);
|
||||
}
|
||||
|
||||
|
||||
// Cross-server get in (C++: c2s_CmdNPCSevCrossServerGetIn) — TODO: implement C2S packet when needed
|
||||
public void c2s_CmdNPCSevCrossServerGetIn()
|
||||
{
|
||||
@@ -2260,6 +2658,6 @@ namespace CSNetwork
|
||||
{
|
||||
// TODO: C2SCommandFactory.CreateNPCSevCrossServerGetOutCmd() and SendProtocol
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,6 +109,23 @@ namespace CSNetwork
|
||||
_position += 4;
|
||||
}
|
||||
|
||||
/// <summary>Write int32 in little-endian (for GNET protocol compatibility with C++ client/server).</summary>
|
||||
public void WriteInt32LE(int value)
|
||||
{
|
||||
var bytes = BitConverter.GetBytes(value);
|
||||
_octets.Insert(_position, bytes);
|
||||
_position += 4;
|
||||
}
|
||||
|
||||
/// <summary>Read int32 in little-endian (for GNET protocol compatibility with C++ client/server).</summary>
|
||||
public int ReadInt32LE()
|
||||
{
|
||||
if (_position + 4 > _octets.Length)
|
||||
throw new IndexOutOfRangeException("Attempt to read beyond the end of the stream.");
|
||||
var bytes = ReadBytes(4);
|
||||
return BitConverter.ToInt32(bytes, 0);
|
||||
}
|
||||
|
||||
public void Write(float value)
|
||||
{
|
||||
var bytes = BitConverter.GetBytes(value);
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
using CSNetwork.Protocols.RPCData;
|
||||
|
||||
namespace CSNetwork.Protocols
|
||||
{
|
||||
/// <summary>PROTOCOL_ADDFRIEND_RE(203). Port of GNET::AddFriend_Re (inl/addfriend_re).</summary>
|
||||
public class addfriend_re : Protocol
|
||||
{
|
||||
public byte retcode { get; set; }
|
||||
public GFriendInfo info { get; set; }
|
||||
public uint srclsid { get; set; }
|
||||
|
||||
public addfriend_re() : base(ProtocolType.PROTOCOL_ADDFRIEND_RE)
|
||||
{
|
||||
info = new GFriendInfo();
|
||||
}
|
||||
|
||||
public override Protocol Clone() => new addfriend_re
|
||||
{
|
||||
retcode = retcode,
|
||||
info = info != null ? new GFriendInfo { rid = info.rid, cls = info.cls, gid = info.gid, name = new Octets(info.name?.ToArray() ?? System.Array.Empty<byte>()) } : new GFriendInfo(),
|
||||
srclsid = srclsid
|
||||
};
|
||||
|
||||
public override void Marshal(OctetsStream os)
|
||||
{
|
||||
os.Write(retcode);
|
||||
if (info != null) info.Marshal(os); else new GFriendInfo().Marshal(os);
|
||||
os.Write(srclsid);
|
||||
}
|
||||
|
||||
public override void Unmarshal(OctetsStream os)
|
||||
{
|
||||
retcode = os.ReadByte();
|
||||
info = new GFriendInfo();
|
||||
info.Unmarshal(os);
|
||||
srclsid = os.ReadUInt32();
|
||||
}
|
||||
|
||||
public override int PriorPolicy() => 1;
|
||||
|
||||
public override bool SizePolicy(int size) => size <= 128;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 158e35cf46fa51a4cba4fa7485fdb534
|
||||
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
|
||||
namespace CSNetwork.Protocols
|
||||
{
|
||||
/// <summary>RPC_ADDFRIENDRQST(204). Port of GNET AddFriendRqst (argument + xid). Server notifies target of friend request. Body: xid (4) + AddFriendRqstArg.</summary>
|
||||
public class addfriendrqst : Protocol
|
||||
{
|
||||
/// <summary>RPC transaction id; must be sent back in addfriendrqstres so server can match the response.</summary>
|
||||
public uint Xid { get; set; }
|
||||
public int Srcroleid { get; set; }
|
||||
public Octets Srcname { get; set; }
|
||||
public uint Dstlsid { get; set; }
|
||||
|
||||
public addfriendrqst() : base(ProtocolType.RPC_ADDFRIENDRQST)
|
||||
{
|
||||
Srcname = new Octets();
|
||||
}
|
||||
|
||||
public override Protocol Clone() => new addfriendrqst
|
||||
{
|
||||
Xid = Xid,
|
||||
Srcroleid = Srcroleid,
|
||||
Srcname = new Octets(Srcname.ToArray()),
|
||||
Dstlsid = Dstlsid
|
||||
};
|
||||
|
||||
public override void Marshal(OctetsStream os)
|
||||
{
|
||||
os.Write(Xid);
|
||||
os.Write(Srcroleid);
|
||||
os.Write(Srcname);
|
||||
os.Write(Dstlsid);
|
||||
}
|
||||
|
||||
public override void Unmarshal(OctetsStream os)
|
||||
{
|
||||
Xid = os.ReadUInt32();
|
||||
Srcroleid = os.ReadInt32();
|
||||
Srcname = os.ReadOctets();
|
||||
Dstlsid = os.ReadUInt32();
|
||||
}
|
||||
|
||||
public override int PriorPolicy() => 1;
|
||||
|
||||
public override bool SizePolicy(int size) => size <= 256;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bf82fc1afd675674588f017b6f9e0621
|
||||
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
|
||||
namespace CSNetwork.Protocols
|
||||
{
|
||||
/// <summary>
|
||||
/// RPC_ADDFRIENDRQST(204) response. Port of GNET Rpc marshal (xid + AddFriendRqstRes).
|
||||
/// Client sends this to answer a friend request: retcode 0 = agree, 69 = refuse (ERR_TRADE_AGREE / ERR_TRADE_REFUSE).
|
||||
/// </summary>
|
||||
public class addfriendrqstres : Protocol
|
||||
{
|
||||
/// <summary>RPC transaction id from the received addfriendrqst; server uses it to match the response.</summary>
|
||||
public uint Xid { get; set; }
|
||||
/// <summary>0 = ERR_TRADE_AGREE (accept), 69 = ERR_TRADE_REFUSE (decline).</summary>
|
||||
public byte retcode { get; set; }
|
||||
|
||||
public addfriendrqstres() : base(ProtocolType.RPC_ADDFRIENDRQST)
|
||||
{
|
||||
}
|
||||
|
||||
public addfriendrqstres(uint xid, byte retcode) : base(ProtocolType.RPC_ADDFRIENDRQST)
|
||||
{
|
||||
Xid = xid;
|
||||
this.retcode = retcode;
|
||||
}
|
||||
|
||||
public override Protocol Clone() => new addfriendrqstres(Xid, retcode);
|
||||
|
||||
public override void Marshal(OctetsStream os)
|
||||
{
|
||||
os.Write(Xid);
|
||||
os.Write(retcode);
|
||||
}
|
||||
|
||||
public override void Unmarshal(OctetsStream os)
|
||||
{
|
||||
Xid = os.ReadUInt32();
|
||||
retcode = os.ReadByte();
|
||||
}
|
||||
|
||||
public override int PriorPolicy() => 1;
|
||||
|
||||
public override bool SizePolicy(int size) => size <= 64;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 80bb3b37b64736040a16d67848d9f953
|
||||
@@ -1,11 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using CSNetwork.Protocols.RPCData;
|
||||
|
||||
namespace CSNetwork.Protocols
|
||||
{
|
||||
public class friendextlist : Protocol
|
||||
{
|
||||
public int Roleid { get; set; }
|
||||
public List<GFriendExtInfo> ExtraInfo { get; set; } = new List<GFriendExtInfo>();
|
||||
public List<GSendAUMailRecord> SendInfo { get; set; } = new List<GSendAUMailRecord>();
|
||||
public int Localsid { get; set; }
|
||||
|
||||
public friendextlist() : base(ProtocolType.PROTOCOL_FRIENDEXTLIST)
|
||||
@@ -16,18 +19,26 @@ namespace CSNetwork.Protocols
|
||||
public override Protocol Clone() => new friendextlist
|
||||
{
|
||||
Roleid = Roleid,
|
||||
ExtraInfo = new List<GFriendExtInfo>(ExtraInfo ?? new List<GFriendExtInfo>()),
|
||||
SendInfo = new List<GSendAUMailRecord>(SendInfo ?? new List<GSendAUMailRecord>()),
|
||||
Localsid = Localsid
|
||||
};
|
||||
|
||||
public override void Marshal(OctetsStream os)
|
||||
{
|
||||
os.Write(Roleid);
|
||||
os.WriteList(ExtraInfo ?? new List<GFriendExtInfo>());
|
||||
os.WriteList(SendInfo ?? new List<GSendAUMailRecord>());
|
||||
os.Write(Localsid);
|
||||
}
|
||||
|
||||
public override void Unmarshal(OctetsStream os)
|
||||
{
|
||||
Roleid = os.ReadInt32();
|
||||
ExtraInfo = new List<GFriendExtInfo>();
|
||||
os.ReadList(ExtraInfo);
|
||||
SendInfo = new List<GSendAUMailRecord>();
|
||||
os.ReadList(SendInfo);
|
||||
Localsid = os.ReadInt32();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using CSNetwork.Protocols.RPCData;
|
||||
|
||||
namespace CSNetwork.Protocols
|
||||
{
|
||||
/// <summary>PROTOCOL_GETFRIENDS_RE(207). Server response to getfriends(206).</summary>
|
||||
public class getfriends_re : Protocol
|
||||
{
|
||||
public int Roleid { get; set; }
|
||||
public List<GGroupInfo> Groups { get; set; } = new List<GGroupInfo>();
|
||||
public List<GFriendInfo> Friends { get; set; } = new List<GFriendInfo>();
|
||||
public List<byte> Status { get; set; } = new List<byte>();
|
||||
public int Localsid { get; set; }
|
||||
|
||||
public getfriends_re() : base(ProtocolType.PROTOCOL_GETFRIENDS_RE)
|
||||
{
|
||||
}
|
||||
|
||||
public override Protocol Clone() => new getfriends_re
|
||||
{
|
||||
Roleid = Roleid,
|
||||
Groups = new List<GGroupInfo>(Groups ?? new List<GGroupInfo>()),
|
||||
Friends = new List<GFriendInfo>(Friends ?? new List<GFriendInfo>()),
|
||||
Status = new List<byte>(Status ?? new List<byte>()),
|
||||
Localsid = Localsid
|
||||
};
|
||||
|
||||
public override void Marshal(OctetsStream os)
|
||||
{
|
||||
os.Write(Roleid);
|
||||
os.WriteList(Groups ?? new List<GGroupInfo>());
|
||||
os.WriteList(Friends ?? new List<GFriendInfo>());
|
||||
os.WriteCompactUInt((uint)(Status?.Count ?? 0));
|
||||
if (Status != null)
|
||||
{
|
||||
for (int i = 0; i < Status.Count; i++)
|
||||
os.Write(Status[i]);
|
||||
}
|
||||
os.Write(Localsid);
|
||||
}
|
||||
|
||||
public override void Unmarshal(OctetsStream os)
|
||||
{
|
||||
Roleid = os.ReadInt32();
|
||||
Groups = new List<GGroupInfo>();
|
||||
os.ReadList(Groups);
|
||||
Friends = new List<GFriendInfo>();
|
||||
os.ReadList(Friends);
|
||||
uint sc = os.ReadCompactUInt();
|
||||
Status = new List<byte>((int)sc);
|
||||
for (int i = 0; i < sc; i++)
|
||||
Status.Add(os.ReadByte());
|
||||
Localsid = os.ReadInt32();
|
||||
}
|
||||
|
||||
public override int PriorPolicy() => 1;
|
||||
|
||||
public override bool SizePolicy(int size) => size <= 8192;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bd80f916689adb4488d055af7d1f0465
|
||||
@@ -0,0 +1,51 @@
|
||||
namespace CSNetwork.Protocols.RPCData
|
||||
{
|
||||
/// <summary>Port of GNET::GFriendExtInfo (rpcdata/gfriendextinfo).</summary>
|
||||
public class GFriendExtInfo : IMarshallable
|
||||
{
|
||||
public int uid;
|
||||
public int rid;
|
||||
public int level;
|
||||
public int last_logintime;
|
||||
public int update_time;
|
||||
public byte reincarnation_times;
|
||||
public Octets remarks;
|
||||
public short reserved1;
|
||||
public int reserved2;
|
||||
public int reserved3;
|
||||
|
||||
public GFriendExtInfo()
|
||||
{
|
||||
remarks = new Octets();
|
||||
}
|
||||
|
||||
public void Marshal(OctetsStream os)
|
||||
{
|
||||
os.Write(uid);
|
||||
os.Write(rid);
|
||||
os.Write(level);
|
||||
os.Write(last_logintime);
|
||||
os.Write(update_time);
|
||||
os.Write(reincarnation_times);
|
||||
os.Write(remarks ?? new Octets());
|
||||
os.Write(reserved1);
|
||||
os.Write(reserved2);
|
||||
os.Write(reserved3);
|
||||
}
|
||||
|
||||
public void Unmarshal(OctetsStream os)
|
||||
{
|
||||
uid = os.ReadInt32();
|
||||
rid = os.ReadInt32();
|
||||
level = os.ReadInt32();
|
||||
last_logintime = os.ReadInt32();
|
||||
update_time = os.ReadInt32();
|
||||
reincarnation_times = os.ReadByte();
|
||||
remarks = os.ReadOctets();
|
||||
reserved1 = os.ReadInt16();
|
||||
reserved2 = os.ReadInt32();
|
||||
reserved3 = os.ReadInt32();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cbd227cebee40b0438f44b5a52f47459
|
||||
@@ -0,0 +1,32 @@
|
||||
namespace CSNetwork.Protocols.RPCData
|
||||
{
|
||||
/// <summary>Port of GNET::GFriendInfo (rpcdata/gfriendinfo).</summary>
|
||||
public class GFriendInfo : IMarshallable
|
||||
{
|
||||
public int rid;
|
||||
public byte cls;
|
||||
public byte gid;
|
||||
public Octets name;
|
||||
|
||||
public GFriendInfo()
|
||||
{
|
||||
name = new Octets();
|
||||
}
|
||||
|
||||
public void Marshal(OctetsStream os)
|
||||
{
|
||||
os.Write(rid);
|
||||
os.Write(cls);
|
||||
os.Write(gid);
|
||||
os.Write(name ?? new Octets());
|
||||
}
|
||||
|
||||
public void Unmarshal(OctetsStream os)
|
||||
{
|
||||
rid = os.ReadInt32();
|
||||
cls = os.ReadByte();
|
||||
gid = os.ReadByte();
|
||||
name = os.ReadOctets();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a0bf31ab9d8853a479215aa7e1fa5e94
|
||||
@@ -0,0 +1,27 @@
|
||||
namespace CSNetwork.Protocols.RPCData
|
||||
{
|
||||
/// <summary>Port of GNET::GGroupInfo (rpcdata/ggroupinfo).</summary>
|
||||
public class GGroupInfo : IMarshallable
|
||||
{
|
||||
public byte gid;
|
||||
public Octets name;
|
||||
|
||||
public GGroupInfo()
|
||||
{
|
||||
name = new Octets();
|
||||
}
|
||||
|
||||
public void Marshal(OctetsStream os)
|
||||
{
|
||||
os.Write(gid);
|
||||
os.Write(name ?? new Octets());
|
||||
}
|
||||
|
||||
public void Unmarshal(OctetsStream os)
|
||||
{
|
||||
gid = os.ReadByte();
|
||||
name = os.ReadOctets();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 14af8f8307d31b94585bf4526fcdc1b1
|
||||
@@ -0,0 +1,22 @@
|
||||
namespace CSNetwork.Protocols.RPCData
|
||||
{
|
||||
/// <summary>Port of GNET::GSendAUMailRecord (rpcdata/gsendaumailrecord).</summary>
|
||||
public class GSendAUMailRecord : IMarshallable
|
||||
{
|
||||
public int rid;
|
||||
public int sendmail_time;
|
||||
|
||||
public void Marshal(OctetsStream os)
|
||||
{
|
||||
os.Write(rid);
|
||||
os.Write(sendmail_time);
|
||||
}
|
||||
|
||||
public void Unmarshal(OctetsStream os)
|
||||
{
|
||||
rid = os.ReadInt32();
|
||||
sendmail_time = os.ReadInt32();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c573cfeb052d9f840891d71dfbaecd6a
|
||||
@@ -163,6 +163,9 @@ namespace BrewMonster.Network
|
||||
{
|
||||
BaseSecurity.Initizalize();
|
||||
ProtocolFactory.RegisterAllProtocols();
|
||||
// Type 204 is used for both addfriendrqst (server→client request) and addfriendrqstres (client→server response).
|
||||
// Client only receives addfriendrqst; ensure decode uses it so we don't get InvalidCastException.
|
||||
Protocol.Register<addfriendrqst>((uint)ProtocolType.RPC_ADDFRIENDRQST);
|
||||
_gameSession = new GameSession();
|
||||
#if UNITY_EDITOR
|
||||
var path = Application.dataPath.Substring(0, Application.dataPath.LastIndexOf("Assets"));
|
||||
@@ -173,7 +176,9 @@ namespace BrewMonster.Network
|
||||
|
||||
// Subscribe to unexpected disconnects
|
||||
_gameSession.Disconnected += OnUnexpectedDisconnect;
|
||||
|
||||
_gameSession.FriendRequestReceived += OnFriendRequestReceived;
|
||||
_gameSession.AddFriendResultReceived += OnAddFriendResultReceived;
|
||||
|
||||
_isInitialized = true;
|
||||
|
||||
DontDestroyOnLoad(gameObject);
|
||||
@@ -184,6 +189,8 @@ namespace BrewMonster.Network
|
||||
// Tell LoginScene what to show next.
|
||||
LogoutFlowState.NextLoginEntry = entryTarget;
|
||||
_gameSession.Disconnected -= OnUnexpectedDisconnect;
|
||||
_gameSession.FriendRequestReceived -= OnFriendRequestReceived;
|
||||
_gameSession.AddFriendResultReceived -= OnAddFriendResultReceived;
|
||||
EC_ManMessageMono.Instance.CECNPCMan.Release();
|
||||
|
||||
if (clearSavedCreds)
|
||||
@@ -632,6 +639,20 @@ namespace BrewMonster.Network
|
||||
{
|
||||
Instance._gameSession.c2s_SendCmdDuelReply(accept, idInviter);
|
||||
}
|
||||
/// <summary>Send PROTOCOL_ADDFRIEND(202). Port of CECGameSession::friend_Add.</summary>
|
||||
public static void Friend_Add(int idPlayer, string name)
|
||||
{
|
||||
Instance._gameSession.Friend_Add(idPlayer, name ?? "");
|
||||
}
|
||||
/// <summary>Send PROTOCOL_GETFRIENDS(206). Port of CECGameSession::friend_GetList().</summary>
|
||||
public static void Friend_GetList()
|
||||
{
|
||||
Instance._gameSession.Friend_GetList();
|
||||
}
|
||||
public static void Friend_AddResponse(uint xid, bool agree)
|
||||
{
|
||||
Instance._gameSession.Friend_AddResponse(xid, agree);
|
||||
}
|
||||
public static void c2s_CmdTeamKickMember(int idMember)
|
||||
{
|
||||
Instance._gameSession.c2s_SendCmdTeamKickMember(idMember);
|
||||
@@ -712,6 +733,26 @@ namespace BrewMonster.Network
|
||||
/// <summary>
|
||||
/// Handles unexpected server disconnections. Shows a message box and returns to login.
|
||||
/// </summary>
|
||||
private void OnFriendRequestReceived(uint xid, int srcroleid, string askerName)
|
||||
{
|
||||
string name = string.IsNullOrEmpty(askerName) ? ("Player " + srcroleid) : askerName;
|
||||
CECUIManager.Instance?.ShowMessageBoxYesAndNo(
|
||||
title: "Friend Request",
|
||||
message: $"{name} wants to add you as a friend.",
|
||||
dlg: null,
|
||||
onClickedYes: () => Friend_AddResponse(xid, agree: true),
|
||||
onClickedNo: () => Friend_AddResponse(xid, agree: false));
|
||||
}
|
||||
|
||||
private void OnAddFriendResultReceived(byte retcode, string message)
|
||||
{
|
||||
CECUIManager.Instance?.ShowMessageBoxYes(
|
||||
title: retcode == 0 ? "Friend added" : "Add friend failed",
|
||||
message: message,
|
||||
dlg: null,
|
||||
null);
|
||||
}
|
||||
|
||||
private void OnUnexpectedDisconnect()
|
||||
{
|
||||
// If this was an intentional disconnect (logout), skip UI
|
||||
@@ -722,16 +763,13 @@ namespace BrewMonster.Network
|
||||
}
|
||||
|
||||
// Show disconnect message box
|
||||
CECUIManager.Instance?.ShowMessageBox(
|
||||
title: "Disconnected",
|
||||
message: "Connection to the server has been lost.",
|
||||
messageBoxType: MessageBoxType.YesButton,
|
||||
onClickedYes: () =>
|
||||
CECUIManager.Instance?.ShowMessageBoxYes("Disconnected", "Connection to the server has been lost.", null,
|
||||
() =>
|
||||
{
|
||||
// Return to login screen
|
||||
LogoutAccount();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public static void c2s_CmdGoto(float x, float y, float z)
|
||||
|
||||
@@ -2,6 +2,7 @@ using BrewMonster;
|
||||
using BrewMonster.Managers;
|
||||
using BrewMonster.Network;
|
||||
using BrewMonster.Scripts;
|
||||
using BrewMonster.Scripts.Skills;
|
||||
using CSNetwork;
|
||||
using CSNetwork.GPDataType;
|
||||
using CSNetwork.Protocols;
|
||||
@@ -452,13 +453,51 @@ namespace BrewMonster
|
||||
case EC_MsgDef.MSG_PM_PLAYERBASEINFO: OnMsgPlayerBaseInfo(Msg); break;
|
||||
case EC_MsgDef.MSG_PM_PLAYEREQUIPDATA: OnMsgPlayerEquipData(Msg); break;
|
||||
case EC_MsgDef.MSG_PM_PLAYERATKRESULT: OnMsgPlayerAtkResult(Msg); break;
|
||||
case EC_MsgDef.MSG_PM_CASTSKILL: OnMsgPlayerCastSkill(Msg); break;
|
||||
case EC_MsgDef.MSG_PM_ENCHANTRESULT: OnMsgEnchantResult(Msg); break;
|
||||
case EC_MsgDef.MSG_PM_PLAYERDOEMOTE: OnMsgPlayerDoEmote(Msg); break;
|
||||
case EC_MsgDef.MSG_PM_PLAYERSKILLRESULT: OnMsgPlayerSkillResult(Msg); break;
|
||||
case EC_MsgDef.MSG_PM_PLAYERGATHER: OnMsgPlayerGather(Msg); break;
|
||||
case EC_MsgDef.MSG_PM_PLAYERMOUNT: OnMsgPlayerMount(Msg); break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles skill attack result messages for else players.
|
||||
///
|
||||
/// This method is called when the server sends MSG_PM_PLAYERSKILLRESULT (OBJECT_SKILL_ATTACK_RESULT).
|
||||
/// Unlike OnMsgPlayerAtkResult which handles melee attacks, this handles skill attacks specifically.
|
||||
///
|
||||
/// Flow:
|
||||
/// 1. Parse cmd_object_skill_attack_result (includes skill_id directly)
|
||||
/// 2. Face target
|
||||
/// 3. Call PlayAttackEffect with skill_id from command (triggers GFX system)
|
||||
/// 4. Enter fight state if skill is attack or curse type
|
||||
///
|
||||
/// C++ equivalent: CECElsePlayer::OnMsgPlayerSkillResult (EC_ElsePlayer.cpp:1787)
|
||||
/// </summary>
|
||||
private void OnMsgPlayerSkillResult(ECMSG Msg)
|
||||
{
|
||||
cmd_object_skill_attack_result pCmd = GPDataTypeHelper.FromBytes<cmd_object_skill_attack_result>((byte[])Msg.dwParam1);
|
||||
|
||||
// Face to target
|
||||
TurnFaceTo(pCmd.target_id);
|
||||
|
||||
// Call PlayAttackEffect with skill_id directly from command (like C++ does)
|
||||
// Unlike OnMsgPlayerAtkResult, we get skill_id from the command structure, not from m_pCurSkill
|
||||
int attackTime = int.MinValue;
|
||||
PlayAttackEffect(pCmd.target_id, pCmd.skill_id, 0, -1,
|
||||
(uint)pCmd.attack_flag, pCmd.speed * 50, ref attackTime, pCmd.section);
|
||||
|
||||
// Check skill type and enter fight state if needed (matching C++ logic)
|
||||
byte skillType = ElementSkill.GetType((uint)pCmd.skill_id);
|
||||
if (skillType == (byte)skill_type.TYPE_ATTACK || skillType == (byte)skill_type.TYPE_CURSE)
|
||||
{
|
||||
EnterFightState();
|
||||
}
|
||||
}
|
||||
|
||||
public void HandleRevive(short sReviveType, A3DVECTOR3 pos)
|
||||
{
|
||||
SetServerPos(pos);
|
||||
@@ -494,7 +533,7 @@ namespace BrewMonster
|
||||
}
|
||||
}
|
||||
|
||||
public void OnMsgPlayerEquipData(ECMSG Msg)
|
||||
public async void OnMsgPlayerEquipData(ECMSG Msg)
|
||||
{
|
||||
// using namespace S2C;
|
||||
|
||||
@@ -528,12 +567,32 @@ namespace BrewMonster
|
||||
}
|
||||
|
||||
// // Change equipment
|
||||
ChangeEquipments(bReset, crc, iAddMask, iDelMask, aAdded);
|
||||
// ChangeEquipments(bReset, crc, iAddMask, iDelMask, aAdded);
|
||||
await QueueChangeEquipments(bReset, crc, iAddMask, iDelMask, aAdded);
|
||||
}
|
||||
|
||||
private async UniTask QueueChangeEquipments(bool bReset, int crc, long iAddMask, long iDelMask, int[] aAddedEquip)
|
||||
{
|
||||
while (!IsPlayerModelReady)
|
||||
{
|
||||
await UniTask.DelayFrame(1);
|
||||
}
|
||||
ChangeEquipments(bReset, crc, iAddMask, iDelMask, aAddedEquip);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handles attack result messages for else players (both melee and skill attacks).
|
||||
///
|
||||
/// For skill attacks:
|
||||
/// - Uses m_pCurSkill (set in OnMsgPlayerCastSkill) to get skill ID
|
||||
/// - Calls PlayAttackEffect with skill ID, which triggers GFX system
|
||||
/// - GFX system (A3DSkillGfxComposerMan) spawns effects at hook positions
|
||||
///
|
||||
/// For melee attacks:
|
||||
/// - idSkill is 0, triggers normal melee attack work
|
||||
/// </summary>
|
||||
void OnMsgPlayerAtkResult(ECMSG Msg)
|
||||
{
|
||||
|
||||
cmd_object_atk_result pCmd = GPDataTypeHelper.FromBytes<cmd_object_atk_result>((byte[])Msg.dwParam1);
|
||||
//ASSERT(pCmd && pCmd.attacker_id == m_PlayerInfo.cid);
|
||||
|
||||
@@ -542,16 +601,142 @@ namespace BrewMonster
|
||||
|
||||
// TO DO: fix later
|
||||
int attackTime = int.MinValue;
|
||||
PlayAttackEffect(pCmd.target_id, 0, 0, -1, (uint)pCmd.attack_flag, pCmd.speed* 50, ref attackTime);
|
||||
PlayAttackEffect(pCmd.target_id, 0, 0, -1, (uint)pCmd.attack_flag, pCmd.speed * 50, ref attackTime);
|
||||
BMLogger.Log($"[ELSEPLAYER_SKILL_FLOW] PlayAttackEffect: Complete, attackTime={attackTime}");
|
||||
|
||||
if (!m_pEPWorkMan.FindWork(CECEPWorkMan.Work_type.WT_NORMAL, CECEPWork.EP_work_ID.WORK_HACKOBJECT)){
|
||||
m_pEPWorkMan.StartNormalWork(new CECEPWorkMelee(m_pEPWorkMan, pCmd.target_id));
|
||||
}
|
||||
if (!m_pEPWorkMan.FindWork(CECEPWorkMan.Work_type.WT_NORMAL, CECEPWork.EP_work_ID.WORK_HACKOBJECT))
|
||||
{
|
||||
m_pEPWorkMan.StartNormalWork(new CECEPWorkMelee(m_pEPWorkMan, pCmd.target_id));
|
||||
}
|
||||
|
||||
// Enter fight state
|
||||
EnterFightState();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Handles skill casting messages from server for else players.
|
||||
///
|
||||
/// Flow:
|
||||
/// 1. Server sends OBJECT_CAST_SKILL -> This handler plays cast animation
|
||||
/// 2. Server sends SKILL_PERFORM -> Skill execution begins (for durative skills)
|
||||
/// 3. Server sends attack result -> OnMsgPlayerAtkResult triggers PlayAttackEffect
|
||||
/// 4. PlayAttackEffect -> CECAttacksMan.AddSkillAttack -> GFX system spawns effects
|
||||
/// 5. Server sends SKILL_INTERRUPTED -> Clears casting state (if interrupted)
|
||||
///
|
||||
/// Note: Else players don't maintain skill lists, so we create temporary CECSkill objects
|
||||
/// for tracking purposes only. The actual skill data comes from ElementSkill static methods.
|
||||
/// </summary>
|
||||
private void OnMsgPlayerCastSkill(ECMSG Msg)
|
||||
{
|
||||
int commandID = Convert.ToInt32(Msg.dwParam2);
|
||||
|
||||
switch (commandID)
|
||||
{
|
||||
case CommandID.OBJECT_CAST_SKILL:
|
||||
{
|
||||
cmd_object_cast_skill pCmd =
|
||||
GPDataTypeHelper.FromBytes<cmd_object_cast_skill>((byte[])Msg.dwParam1);
|
||||
|
||||
// Get skill object (else players don't have skill lists, so we create a temporary skill reference)
|
||||
// For else players, we mainly need the skill ID for animation purposes
|
||||
int skillID = pCmd.skill;
|
||||
|
||||
// Store current skill target
|
||||
m_idCurSkillTarget = pCmd.target;
|
||||
|
||||
// Face the target
|
||||
TurnFaceTo(pCmd.target);
|
||||
|
||||
// Play skill cast animation
|
||||
PlaySkillCastAction(skillID);
|
||||
|
||||
// Create a temporary skill object for tracking (if needed)
|
||||
// Note: Else players don't maintain skill lists like host player does
|
||||
// We create a minimal skill object just for the current cast
|
||||
if (m_pCurSkill == null || m_pCurSkill.GetSkillID() != skillID)
|
||||
{
|
||||
// Create a temporary skill object with level 1 (we don't know the actual level)
|
||||
m_pCurSkill = new CECSkill(skillID, 1);
|
||||
}
|
||||
|
||||
// Enter fight state
|
||||
EnterFightState();
|
||||
|
||||
break;
|
||||
}
|
||||
case CommandID.OBJECT_CAST_INSTANT_SKILL:
|
||||
{
|
||||
cmd_object_cast_instant_skill pCmd =
|
||||
GPDataTypeHelper.FromBytes<cmd_object_cast_instant_skill>((byte[])Msg.dwParam1);
|
||||
|
||||
int skillID = pCmd.skill;
|
||||
|
||||
m_idCurSkillTarget = pCmd.target;
|
||||
|
||||
TurnFaceTo(pCmd.target);
|
||||
PlaySkillCastAction(skillID);
|
||||
|
||||
if (m_pCurSkill == null || m_pCurSkill.GetSkillID() != skillID)
|
||||
{
|
||||
m_pCurSkill = new CECSkill(skillID, 1);
|
||||
}
|
||||
|
||||
EnterFightState();
|
||||
break;
|
||||
}
|
||||
case CommandID.OBJECT_CAST_POS_SKILL:
|
||||
{
|
||||
cmd_object_cast_pos_skill pCmd =
|
||||
GPDataTypeHelper.FromBytes<cmd_object_cast_pos_skill>((byte[])Msg.dwParam1);
|
||||
|
||||
int skillID = pCmd.skill;
|
||||
|
||||
// For position-based skills, target is the position, not an object
|
||||
// We still play the cast animation
|
||||
PlaySkillCastAction(skillID);
|
||||
|
||||
if (m_pCurSkill == null || m_pCurSkill.GetSkillID() != skillID)
|
||||
{
|
||||
m_pCurSkill = new CECSkill(skillID, 1);
|
||||
}
|
||||
|
||||
EnterFightState();
|
||||
break;
|
||||
}
|
||||
case CommandID.SKILL_PERFORM:
|
||||
{
|
||||
// Skill perform - the skill has finished casting and is being executed
|
||||
// For else players, we keep m_pCurSkill until attack result is received
|
||||
// This allows PlayAttackEffect to use the skill information
|
||||
// Durative skills (channeling) will continue until interrupted
|
||||
if (m_pCurSkill != null && m_pCurSkill.IsDurative())
|
||||
{
|
||||
// For durative skills, we keep the skill active
|
||||
// It will be cleared when SKILL_INTERRUPTED is received
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CommandID.SKILL_INTERRUPTED:
|
||||
{
|
||||
// Skill was interrupted, clear current skill
|
||||
cmd_skill_interrupted pCmd =
|
||||
GPDataTypeHelper.FromBytes<cmd_skill_interrupted>((byte[])Msg.dwParam1);
|
||||
|
||||
if (m_pCurSkill != null)
|
||||
{
|
||||
StopSkillCastAction();
|
||||
m_pCurSkill = null;
|
||||
}
|
||||
m_idCurSkillTarget = 0;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async void LoadAppearGfx()
|
||||
{
|
||||
if (!m_pAppearGFX && m_iAppearFlag == (int)PlayerAppearFlag.APPEAR_ENTERWORLD)
|
||||
@@ -809,6 +994,7 @@ namespace BrewMonster
|
||||
m_iGender = iGender;
|
||||
m_bBaseInfoReady = true;
|
||||
SetPlayerName(szName ?? "");
|
||||
EC_Game.GetGameRun().AddPlayerName(m_PlayerInfo.cid, szName, true);
|
||||
}
|
||||
// Level up
|
||||
public void LevelUp()
|
||||
|
||||
@@ -120,7 +120,6 @@ namespace BrewMonster
|
||||
m_AttFlyMode = (GfxAttackMode)0;
|
||||
m_AttHitMode = (GfxAttackMode)0;
|
||||
m_dwFlyTime = 200;
|
||||
m_bTraceTarget = true;
|
||||
m_FlyClusterCount = 1;
|
||||
m_FlyClusterInterval = 0;
|
||||
m_HitClusterCount = 1;
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
using System;
|
||||
using System;
|
||||
using BrewMonster.UI;
|
||||
using PerfectWorld.Scripts.Common;
|
||||
using UnityEngine;
|
||||
using TMPro;
|
||||
using UnityEngine.Serialization;
|
||||
using UnityEngine.UI;
|
||||
using static CECUIManager;
|
||||
|
||||
@@ -27,16 +28,19 @@ namespace BrewMonster
|
||||
{
|
||||
[SerializeField] private TMP_Text titleText;
|
||||
[SerializeField] private TMP_Text messageText;
|
||||
[SerializeField] private Button okButton;
|
||||
[SerializeField] private Button _yesButton;
|
||||
[SerializeField] private Button _noButton;
|
||||
[SerializeField] private Button _closeButton;
|
||||
|
||||
private Action _onClickedYesBtn;
|
||||
private Action _onClickedNoBtn;
|
||||
|
||||
private MessageBoxData _messageData;
|
||||
|
||||
public override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
okButton.onClick.AddListener(OnOkClicked);
|
||||
_yesButton.onClick.AddListener(OnYesClicked);
|
||||
_noButton.onClick.AddListener(OnNoClicked);
|
||||
_closeButton.onClick.AddListener(OnCloseClicked);
|
||||
|
||||
@@ -44,59 +48,135 @@ namespace BrewMonster
|
||||
|
||||
public override void OnDisable()
|
||||
{
|
||||
okButton.onClick.RemoveListener(OnOkClicked);
|
||||
_yesButton.onClick.RemoveListener(OnYesClicked);
|
||||
_noButton.onClick.RemoveListener(OnNoClicked);
|
||||
_closeButton.onClick.RemoveListener(OnCloseClicked);
|
||||
}
|
||||
|
||||
#region Button Events
|
||||
private void OnOkClicked()
|
||||
private void OnYesClicked()
|
||||
{
|
||||
EventBus.Publish(new MessageBoxEvent(1,_messageData.Dlg));
|
||||
_messageData.OnClickedYes?.Invoke();
|
||||
_onClickedYesBtn?.Invoke();
|
||||
Show(false);
|
||||
}
|
||||
private void OnNoClicked()
|
||||
{
|
||||
EventBus.Publish(new MessageBoxEvent(0,_messageData.Dlg));
|
||||
_messageData.OnClickedNo?.Invoke();
|
||||
_onClickedNoBtn?.Invoke();
|
||||
Show(false);
|
||||
}
|
||||
|
||||
private void OnCloseClicked()
|
||||
{
|
||||
// treat the close button as OK button
|
||||
OnOkClicked();
|
||||
OnYesClicked();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public void ShowMessageBox(MessageBoxData messageBoxData)
|
||||
// public void ShowMessageBox(MessageBoxData messageBoxData)
|
||||
// {
|
||||
// _messageData = messageBoxData;
|
||||
// messageBoxData.Message = EC_TextFormatter.FormatForTextMeshPro(messageBoxData.Message);
|
||||
// SetName(string.IsNullOrEmpty(messageBoxData.Title) ? "" : messageBoxData.Title);
|
||||
// messageText.text = string.IsNullOrEmpty(messageBoxData.Message) ? "" : messageBoxData.Message;
|
||||
//
|
||||
// _yesButton.gameObject.SetActive(false);
|
||||
// _noButton.gameObject.SetActive(false);
|
||||
// switch (_messageData.MessageBoxType)
|
||||
// {
|
||||
// case MessageBoxType.YesButton:
|
||||
// _yesButton.gameObject.SetActive(true);
|
||||
// break;
|
||||
// case MessageBoxType.NoButton:
|
||||
// _noButton.gameObject.SetActive(true);
|
||||
// break;
|
||||
// case MessageBoxType.BothYesNoButton:
|
||||
// _yesButton.gameObject.SetActive(true);
|
||||
// _noButton.gameObject.SetActive(true);
|
||||
// break;
|
||||
// }
|
||||
// Show(true);
|
||||
// }
|
||||
|
||||
/// <summary>
|
||||
/// message with title and message only
|
||||
/// </summary>
|
||||
/// <param name="title"></param>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="dlg"></param>
|
||||
public void ShowMessageBoxGeneral(string title, string message, AUIDialog dlg)
|
||||
{
|
||||
_messageData = messageBoxData;
|
||||
// messageBoxData.Message = messageBoxData.Message?
|
||||
// .Replace("\r\n", "\n")
|
||||
// .Replace("\r", "\n");
|
||||
messageBoxData.Message = EC_TextFormatter.FormatForTextMeshPro(messageBoxData.Message);
|
||||
SetName(string.IsNullOrEmpty(messageBoxData.Title) ? "" : messageBoxData.Title);
|
||||
messageText.text = string.IsNullOrEmpty(messageBoxData.Message) ? "" : messageBoxData.Message;
|
||||
|
||||
okButton.gameObject.SetActive(false);
|
||||
_onClickedYesBtn = null;
|
||||
_onClickedYesBtn = null;
|
||||
string formattedMessage = EC_TextFormatter.FormatForTextMeshPro(message);
|
||||
SetName(string.IsNullOrEmpty(title) ? "" : title);
|
||||
messageText.text = string.IsNullOrEmpty(formattedMessage) ? "" : formattedMessage;
|
||||
_noButton.gameObject.SetActive(false);
|
||||
switch (_messageData.MessageBoxType)
|
||||
{
|
||||
case MessageBoxType.YesButton:
|
||||
okButton.gameObject.SetActive(true);
|
||||
break;
|
||||
case MessageBoxType.NoButton:
|
||||
_noButton.gameObject.SetActive(true);
|
||||
break;
|
||||
case MessageBoxType.BothYesNoButton:
|
||||
okButton.gameObject.SetActive(true);
|
||||
_noButton.gameObject.SetActive(true);
|
||||
break;
|
||||
}
|
||||
_yesButton.gameObject.SetActive(true);
|
||||
Show(true);
|
||||
transform.SetAsLastSibling();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// message with yes button only
|
||||
/// </summary>
|
||||
/// <param name="title"></param>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="dlg"></param>
|
||||
/// <param name="onClickedYes"></param>
|
||||
public void ShowMessageBoxYes(string title, string message, AUIDialog dlg, Action onClickedYes)
|
||||
{
|
||||
_onClickedYesBtn = onClickedYes;
|
||||
string formattedMessage = EC_TextFormatter.FormatForTextMeshPro(message);
|
||||
SetName(string.IsNullOrEmpty(title) ? "" : title);
|
||||
messageText.text = string.IsNullOrEmpty(formattedMessage) ? "" : formattedMessage;
|
||||
_noButton.gameObject.SetActive(false);
|
||||
_yesButton.gameObject.SetActive(true);
|
||||
Show(true);
|
||||
transform.SetAsLastSibling();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// message with no button only
|
||||
/// </summary>
|
||||
/// <param name="title"></param>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="dlg"></param>
|
||||
/// <param name="onClickedNo"></param>
|
||||
public void ShowMessageBoxNo(string title, string message, AUIDialog dlg, Action onClickedNo)
|
||||
{
|
||||
_onClickedNoBtn = onClickedNo;
|
||||
string formattedMessage = EC_TextFormatter.FormatForTextMeshPro(message);
|
||||
SetName(string.IsNullOrEmpty(title) ? "" : title);
|
||||
messageText.text = string.IsNullOrEmpty(formattedMessage) ? "" : formattedMessage;
|
||||
_yesButton.gameObject.SetActive(false);
|
||||
_noButton.gameObject.SetActive(true);
|
||||
Show(true);
|
||||
transform.SetAsLastSibling();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// message with yes and no button
|
||||
/// </summary>
|
||||
/// <param name="title"></param>
|
||||
/// <param name="message"></param>
|
||||
/// <param name="dlg"></param>
|
||||
/// <param name="onClickedYes"></param>
|
||||
/// <param name="onClickedNo"></param>
|
||||
public void ShowMessageBoxYesAndNo(string title, string message, AUIDialog dlg, Action onClickedYes, Action onClickedNo)
|
||||
{
|
||||
_onClickedYesBtn = onClickedYes;
|
||||
_onClickedNoBtn = onClickedNo;
|
||||
string formattedMessage = EC_TextFormatter.FormatForTextMeshPro(message);
|
||||
SetName(string.IsNullOrEmpty(title) ? "" : title);
|
||||
messageText.text = string.IsNullOrEmpty(formattedMessage) ? "" : formattedMessage;
|
||||
_yesButton.gameObject.SetActive(true);
|
||||
_noButton.gameObject.SetActive(true);
|
||||
Show(true);
|
||||
transform.SetAsLastSibling();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using BrewMonster.Network;
|
||||
using BrewMonster.Network;
|
||||
using BrewMonster.Scripts.Managers;
|
||||
using BrewMonster.UI;
|
||||
using PerfectWorld.Scripts.Managers;
|
||||
@@ -37,12 +37,13 @@ namespace BrewMonster
|
||||
[Header("Buttons and Money")]
|
||||
[SerializeField]
|
||||
private TextMeshProUGUI m_TxtMoney;
|
||||
[SerializeField] private Button m_useItem;
|
||||
[SerializeField] private Button m_BtnMergeOrReset;
|
||||
[SerializeField] private Button m_BtnCancel;
|
||||
[SerializeField] private Button m_BtnClose;
|
||||
|
||||
[SerializeField] private Sprite khung_item;
|
||||
[SerializeField] private Transform itemInventoryRoot;
|
||||
[SerializeField] private GameObject itemInventoryRoot;
|
||||
|
||||
private EC_IvtrItem m_SelectedEquip;
|
||||
private EC_IvtrItem m_SelectedMaterial;
|
||||
@@ -58,31 +59,14 @@ namespace BrewMonster
|
||||
public override void Awake()
|
||||
{
|
||||
base.Awake();
|
||||
RegisterDrop(m_SlotFirstParent, OnDropEquip);
|
||||
RegisterClick(m_SlotFirstParent, OnClickEquipSlot);
|
||||
|
||||
if (m_Mode == InstallMode.Enchase && m_SlotSecondParent != null)
|
||||
{
|
||||
RegisterDrop(m_SlotSecondParent, OnDropMaterial);
|
||||
RegisterClick(m_SlotSecondParent, OnClickMaterialSlot);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
#if UNITY_EDITOR || UNITY_STANDALONE
|
||||
if (Input.GetMouseButtonDown(0))
|
||||
{
|
||||
CheckHidePanel(Input.mousePosition);
|
||||
}
|
||||
#else
|
||||
if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Began)
|
||||
{
|
||||
CheckHidePanel(Input.GetTouch(0).position);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public override void OnEnable()
|
||||
{
|
||||
base.OnEnable();
|
||||
@@ -91,6 +75,7 @@ namespace BrewMonster
|
||||
m_BtnMergeOrReset.onClick.AddListener(OnClickedMergeOrReset);
|
||||
m_BtnCancel.onClick.AddListener(OnCommandCancel);
|
||||
m_BtnClose.onClick.AddListener(OnCommandCancel);
|
||||
m_useItem.onClick.AddListener(OnUseItemClicked);
|
||||
m_install_price = -1;
|
||||
if (m_SlotSecondParent != null)
|
||||
m_SlotSecondParent.gameObject.SetActive(m_Mode == InstallMode.Enchase);
|
||||
@@ -104,6 +89,7 @@ namespace BrewMonster
|
||||
m_BtnMergeOrReset.onClick.RemoveListener(OnClickedMergeOrReset);
|
||||
m_BtnCancel.onClick.RemoveListener(OnCommandCancel);
|
||||
m_BtnClose.onClick.RemoveListener(OnCommandCancel);
|
||||
m_useItem.onClick.RemoveListener(OnUseItemClicked);
|
||||
}
|
||||
|
||||
private void RestoreInventoryColors()
|
||||
@@ -147,23 +133,6 @@ namespace BrewMonster
|
||||
return list[slot];
|
||||
}
|
||||
|
||||
private void RegisterDrop(Transform target, Action<PointerEventData> callback)
|
||||
{
|
||||
var trigger = target.GetComponent<EventTrigger>();
|
||||
if (trigger == null)
|
||||
trigger = target.gameObject.AddComponent<EventTrigger>();
|
||||
|
||||
trigger.triggers.Clear();
|
||||
|
||||
var entry = new EventTrigger.Entry
|
||||
{
|
||||
eventID = EventTriggerType.Drop
|
||||
};
|
||||
entry.callback.AddListener((data) => { callback((PointerEventData)data); });
|
||||
|
||||
trigger.triggers.Add(entry);
|
||||
}
|
||||
|
||||
private void RegisterClick(Transform target, Action<PointerEventData> callback)
|
||||
{
|
||||
if (target == null) return;
|
||||
@@ -199,117 +168,6 @@ namespace BrewMonster
|
||||
}
|
||||
}
|
||||
|
||||
private EC_IvtrItem GetItemFromDrag(PointerEventData eventData)
|
||||
{
|
||||
if (eventData.pointerDrag == null)
|
||||
return null;
|
||||
|
||||
var btn = eventData.pointerDrag.GetComponent<Button>();
|
||||
if (btn == null)
|
||||
return null;
|
||||
|
||||
// Slot index
|
||||
int slotIndex = btn.transform.GetSiblingIndex();
|
||||
|
||||
// Inventory package = 0
|
||||
var host = CECGameRun.Instance?.GetHostPlayer();
|
||||
if (host == null)
|
||||
return null;
|
||||
|
||||
var inv = host.GetInventory(0);
|
||||
if (inv == null)
|
||||
return null;
|
||||
|
||||
return inv.GetItem(slotIndex, false);
|
||||
}
|
||||
|
||||
private void OnDropEquip(PointerEventData eventData)
|
||||
{
|
||||
if (eventData.pointerDrag == null)
|
||||
return;
|
||||
|
||||
var btn = eventData.pointerDrag.GetComponent<Button>();
|
||||
if (btn == null)
|
||||
return;
|
||||
|
||||
int slotIndex = btn.transform.GetSiblingIndex();
|
||||
|
||||
var item = GetItemFromDrag(eventData);
|
||||
if (item == null)
|
||||
return;
|
||||
|
||||
if(!item.IsEquipment())
|
||||
return;
|
||||
|
||||
EC_IvtrItem detailedItem = EC_IvtrItem.CreateItem(item.m_tid, item.m_expire_date, item.m_iCount);
|
||||
if (item.Content != null && item.Content.Length > 0)
|
||||
detailedItem.SetItemInfo(item.Content, item.Content.Length);
|
||||
else
|
||||
detailedItem.GetDetailDataFromLocal();
|
||||
|
||||
if (m_FirstInvSlot >= 0)
|
||||
{
|
||||
var previosBtn = FindInventoryButtonBySlot(m_FirstInvSlot);
|
||||
SetInventorySlotGray(previosBtn, false);
|
||||
}
|
||||
|
||||
m_SelectedEquip?.Freeze(false);
|
||||
m_SelectedEquip = detailedItem;
|
||||
m_FirstInvSlot = slotIndex;
|
||||
|
||||
m_TxtFirstName.text = detailedItem.GetName();
|
||||
SetSlotIcon(m_SlotFirstParent, detailedItem);
|
||||
|
||||
SetInventorySlotGray(btn, true);
|
||||
|
||||
detailedItem.Freeze(true);
|
||||
|
||||
UpdateResourceInfo();
|
||||
}
|
||||
|
||||
private void OnDropMaterial(PointerEventData eventData)
|
||||
{
|
||||
if (eventData.pointerDrag == null)
|
||||
return;
|
||||
|
||||
var btn = eventData.pointerDrag.GetComponent<Button>();
|
||||
if (btn == null)
|
||||
return;
|
||||
|
||||
int slotIndex = btn.transform.GetSiblingIndex();
|
||||
|
||||
var item = GetItemFromDrag(eventData);
|
||||
if (item == null)
|
||||
return;
|
||||
|
||||
if (item.GetClassID() != (int)EC_IvtrEquip.EQUIP_CLASS_ID.ICID_STONE)
|
||||
return;
|
||||
|
||||
EC_IvtrItem detailedItem = EC_IvtrItem.CreateItem(item.m_tid, item.m_expire_date, item.m_iCount);
|
||||
if (item.Content != null && item.Content.Length > 0)
|
||||
detailedItem.SetItemInfo(item.Content, item.Content.Length);
|
||||
else
|
||||
detailedItem.GetDetailDataFromLocal();
|
||||
|
||||
if (m_SecondInvSlot >= 0)
|
||||
{
|
||||
var previosBtn = FindInventoryButtonBySlot(m_SecondInvSlot);
|
||||
SetInventorySlotGray(previosBtn, false);
|
||||
}
|
||||
|
||||
m_SelectedMaterial?.Freeze(false);
|
||||
m_SelectedMaterial = detailedItem;
|
||||
m_SelectedMaterial?.Freeze(true);
|
||||
m_SecondInvSlot = slotIndex;
|
||||
|
||||
m_TxtSecondName.text = detailedItem.GetName();
|
||||
SetSlotIcon(m_SlotSecondParent, detailedItem);
|
||||
|
||||
SetInventorySlotGray(btn, true);
|
||||
|
||||
UpdateResourceInfo();
|
||||
}
|
||||
|
||||
private void CalculateUninstallPrice(EC_IvtrItem equipment)
|
||||
{
|
||||
if (equipment == null || !equipment.IsEquipment())
|
||||
@@ -431,11 +289,7 @@ namespace BrewMonster
|
||||
if (nMoney > pHost.GetMoneyAmount())
|
||||
{
|
||||
message = GetGameUIMan().GetStringFromTable(226);
|
||||
CECUIManager.Instance.ShowMessageBox(new MessageBoxData()
|
||||
{
|
||||
Message = message,
|
||||
Dlg = this
|
||||
});
|
||||
CECUIManager.Instance.ShowMessageBoxGeneral("", message, this);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -443,11 +297,7 @@ namespace BrewMonster
|
||||
if (!pIvtrA.IsEquipment())
|
||||
{
|
||||
message = GetGameUIMan().GetStringFromTable(223);
|
||||
CECUIManager.Instance.ShowMessageBox(new MessageBoxData()
|
||||
{
|
||||
Message = message,
|
||||
Dlg = this
|
||||
});
|
||||
CECUIManager.Instance.ShowMessageBoxGeneral("", message, this);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -457,11 +307,7 @@ namespace BrewMonster
|
||||
if (pEquipA.GetEmptyHoleNum() <= 0)
|
||||
{
|
||||
message = GetGameUIMan().GetStringFromTable(224);
|
||||
CECUIManager.Instance.ShowMessageBox(new MessageBoxData()
|
||||
{
|
||||
Message = message,
|
||||
Dlg = this
|
||||
});
|
||||
CECUIManager.Instance.ShowMessageBoxGeneral("", message, this);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -469,11 +315,7 @@ namespace BrewMonster
|
||||
if (pIvtrB == null || !pIvtrB.IsEmbeddable())
|
||||
{
|
||||
message = GetGameUIMan().GetStringFromTable(225);
|
||||
CECUIManager.Instance.ShowMessageBox(new MessageBoxData()
|
||||
{
|
||||
Message = message,
|
||||
Dlg = this
|
||||
});
|
||||
CECUIManager.Instance.ShowMessageBoxGeneral("", message, this);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -498,11 +340,7 @@ namespace BrewMonster
|
||||
if (nStoneLevel > nEquipLevel)
|
||||
{
|
||||
message = GetGameUIMan().GetStringFromTable(300);
|
||||
CECUIManager.Instance.ShowMessageBox(new MessageBoxData()
|
||||
{
|
||||
Message = message,
|
||||
Dlg = this
|
||||
});
|
||||
CECUIManager.Instance.ShowMessageBoxGeneral("", message, this);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -515,46 +353,28 @@ namespace BrewMonster
|
||||
pHost.GetPack(InventoryConst.IVTRTYPE_PACK).UnfreezeAllItems();
|
||||
|
||||
message = GetGameUIMan().GetStringFromTable(228);
|
||||
CECUIManager.Instance.ShowMessageBox(new MessageBoxData()
|
||||
{
|
||||
Message = message,
|
||||
Dlg = this
|
||||
});
|
||||
CECUIManager.Instance.ShowMessageBoxGeneral("", message, this);
|
||||
}
|
||||
else if (pHost != null && m_Mode == InstallMode.Disenchase)
|
||||
{
|
||||
if (pEquipA.GetEmptyHoleNum() == pEquipA.GetHoleNum())
|
||||
{
|
||||
message = GetGameUIMan().GetStringFromTable(227);
|
||||
CECUIManager.Instance.ShowMessageBox(new MessageBoxData()
|
||||
{
|
||||
Message = message,
|
||||
Dlg = this
|
||||
});
|
||||
CECUIManager.Instance.ShowMessageBoxGeneral("", message, this);
|
||||
return;
|
||||
}
|
||||
|
||||
message = GetGameUIMan().GetStringFromTable(229);
|
||||
var x = new MessageBoxData()
|
||||
CECUIManager.Instance.ShowMessageBoxYesAndNo("", message, this,
|
||||
() =>
|
||||
{
|
||||
Message = message,
|
||||
Dlg = this,
|
||||
MessageBoxType = MessageBoxType.BothYesNoButton,
|
||||
OnClickedYes = () =>
|
||||
{
|
||||
UnityGameSession.c2s_CmdNPCSevClearEmbeddedChip((ushort)m_FirstInvSlot, pIvtrA.GetTemplateID());
|
||||
ClearEquiment();
|
||||
pHost.GetPack(InventoryConst.IVTRTYPE_PACK).UnfreezeAllItems();
|
||||
UnityGameSession.c2s_CmdNPCSevClearEmbeddedChip((ushort)m_FirstInvSlot, pIvtrA.GetTemplateID());
|
||||
ClearEquiment();
|
||||
pHost.GetPack(InventoryConst.IVTRTYPE_PACK).UnfreezeAllItems();
|
||||
|
||||
string successMessage = GetGameUIMan().GetStringFromTable(230);
|
||||
CECUIManager.Instance.ShowMessageBox(new MessageBoxData()
|
||||
{
|
||||
Message = successMessage,
|
||||
Dlg = this
|
||||
});
|
||||
}
|
||||
};
|
||||
CECUIManager.Instance.ShowMessageBox(x);
|
||||
string successMessage = GetGameUIMan().GetStringFromTable(230);
|
||||
CECUIManager.Instance.ShowMessageBoxGeneral("", message, this);
|
||||
}, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -592,16 +412,127 @@ namespace BrewMonster
|
||||
CloseDialogue();
|
||||
}
|
||||
|
||||
|
||||
private void CheckHidePanel(Vector2 screenPos)
|
||||
private void OnUseItemClicked()
|
||||
{
|
||||
if (!RectTransformUtility.RectangleContainsScreenPoint(
|
||||
itemInventoryRoot as RectTransform, screenPos,
|
||||
Camera.main))
|
||||
itemInventoryRoot.SetActive(false);
|
||||
if (!TryGetSelectedInventoryItem(out var selectedItem, out var selectedSlot))
|
||||
return;
|
||||
|
||||
if (m_Mode == InstallMode.Disenchase)
|
||||
{
|
||||
if (itemInventoryRoot != null)
|
||||
itemInventoryRoot.gameObject.SetActive(false);
|
||||
if (!selectedItem.IsEquipment())
|
||||
{
|
||||
var message = GetGameUIMan().GetStringFromTable(223);
|
||||
CECUIManager.Instance.ShowMessageBoxGeneral("", message, this);
|
||||
return;
|
||||
}
|
||||
AssignEquipItem(selectedItem, selectedSlot);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_SelectedEquip == null)
|
||||
{
|
||||
if (!selectedItem.IsEquipment())
|
||||
{
|
||||
var msg = GetGameUIMan().GetStringFromTable(223);
|
||||
CECUIManager.Instance.ShowMessageBoxGeneral("", msg, this);
|
||||
return;
|
||||
}
|
||||
AssignEquipItem(selectedItem, selectedSlot);
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedItem.IsEquipment())
|
||||
{
|
||||
AssignEquipItem(selectedItem, selectedSlot);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!selectedItem.IsEmbeddable() ||
|
||||
selectedItem.GetClassID() != (int)EC_IvtrEquip.EQUIP_CLASS_ID.ICID_STONE)
|
||||
{
|
||||
var msg = GetGameUIMan().GetStringFromTable(225);
|
||||
CECUIManager.Instance.ShowMessageBoxGeneral("", msg, this);
|
||||
return;
|
||||
}
|
||||
AssginMaterialItem(selectedItem, selectedSlot);
|
||||
}
|
||||
|
||||
private bool TryGetSelectedInventoryItem(out EC_IvtrItem item, out int slot)
|
||||
{
|
||||
item = null;
|
||||
slot = -1;
|
||||
|
||||
var inventoryUI = FindFirstObjectByType<EC_InventoryUI>();
|
||||
if(inventoryUI == null)
|
||||
return false;
|
||||
|
||||
var type = typeof(EC_InventoryUI);
|
||||
var packageField = type.GetField("currentSelectedPackage", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||
var slotField = type.GetField("currentSelectedSlot", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||
var itemField = type.GetField("currentSelectedItem", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||
|
||||
if (packageField == null ||slotField == null || itemField == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var selectedPackage = (byte)packageField.GetValue(inventoryUI);
|
||||
if(selectedPackage != 0)
|
||||
return false;
|
||||
|
||||
slot = (int)slotField.GetValue(inventoryUI);
|
||||
item = itemField.GetValue(inventoryUI) as EC_IvtrItem;
|
||||
|
||||
return item != null && slot >= 0;
|
||||
}
|
||||
|
||||
private void AssignEquipItem(EC_IvtrItem item, int slot)
|
||||
{
|
||||
if(m_FirstInvSlot >= 0 && m_FirstInvSlot != slot)
|
||||
{
|
||||
ReturnItemToInventory(m_FirstInvSlot);
|
||||
}
|
||||
|
||||
m_SelectedEquip?.Freeze(false);
|
||||
m_SelectedEquip = item;
|
||||
m_SelectedEquip.Freeze(true);
|
||||
m_FirstInvSlot = slot;
|
||||
|
||||
m_TxtFirstName.text = EC_IvtrItemUtils.Instance.ResolveItemName(item.m_tid);
|
||||
SetSlotIcon(m_SlotFirstParent, item);
|
||||
SetInventorySlotGray(FindInventoryButtonBySlot(slot), true);
|
||||
|
||||
if(m_Mode == InstallMode.Disenchase)
|
||||
{
|
||||
CalculateUninstallPrice(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
UpdateResourceInfo();
|
||||
}
|
||||
}
|
||||
|
||||
private void AssginMaterialItem(EC_IvtrItem item, int slot)
|
||||
{
|
||||
if(m_SecondInvSlot >= 0 && m_SecondInvSlot != slot)
|
||||
{
|
||||
ReturnItemToInventory(m_SecondInvSlot);
|
||||
}
|
||||
m_SelectedMaterial?.Freeze(false);
|
||||
m_SelectedMaterial = item;
|
||||
m_SelectedMaterial.Freeze(true);
|
||||
m_SecondInvSlot = slot;
|
||||
m_TxtSecondName.text = EC_IvtrItemUtils.Instance.ResolveItemName(item.m_tid);
|
||||
SetSlotIcon(m_SlotSecondParent, item);
|
||||
SetInventorySlotGray(FindInventoryButtonBySlot(slot), true);
|
||||
|
||||
if (item is EC_IvtrStone stone)
|
||||
{
|
||||
var essence = stone.GetDBEssence();
|
||||
m_install_price = essence.install_price;
|
||||
}
|
||||
UpdateResourceInfo();
|
||||
}
|
||||
|
||||
private void UpdateResourceInfo()
|
||||
@@ -655,4 +586,4 @@ namespace BrewMonster
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ namespace BrewMonster.UI
|
||||
CDLGNPC_CARDRESPAWN = 0xFFFFF49, // ¿¨ÅÆ×ªÉú
|
||||
CDLGNPC_QUERYCHARIOTAMOUNT = 0xFFFFF50, // Õ½³µÊýÁ¿
|
||||
CDLGNPC_FLYSWORDIMPROVE = 0xFFFFF51, // ·É½£Ç¿»¯
|
||||
CDLGNPC_OPEN_FACTION_PVP = 0xFFFFF52, // ¿ªÆô°ïÅÉÂÓ¶á
|
||||
CDLGNPC_OPEN_FACTION_PVP = 0xFFFFF52, // ¿ªÆô°ïÅÉÂÓ¶á
|
||||
CDLGNPC_FACTION_RENAME = 0xFFFFF53,
|
||||
CDLGNPC_GOLD_SHOP = 0xFFFFF54,
|
||||
CDLGNPC_PLAYER_CHANGE_GENDER = 0xFFFFF55; // ÐÞ¸ÄÐÔ±ð
|
||||
@@ -765,10 +765,10 @@ namespace BrewMonster.UI
|
||||
NPC_MAKE_SERVICE pService = (NPC_MAKE_SERVICE)pData;
|
||||
string serviceName = Encoding.Unicode.GetString(MemoryMarshal.AsBytes<ushort>(pService.name));
|
||||
m_pLst_Main.AddString(strText + serviceName);
|
||||
|
||||
|
||||
// Log NPC_MAKE_SERVICE data
|
||||
BMLogger.Log($"NPC_MAKE_SERVICE detected - ServiceID: {a_uiService[i]}, MakeServiceID: {pService.id}, Name: {serviceName}, MakeSkillID: {pService.id_make_skill}, ProduceType: {pService.produce_type}");
|
||||
|
||||
|
||||
// Log pages data
|
||||
if (pService.pages != null)
|
||||
{
|
||||
@@ -778,7 +778,7 @@ namespace BrewMonster.UI
|
||||
string pageTitle = Encoding.Unicode.GetString(MemoryMarshal.AsBytes<ushort>(page.page_title));
|
||||
// Trim null characters and whitespace from page title
|
||||
pageTitle = pageTitle?.TrimEnd('\0', ' ', '\t', '\r', '\n') ?? "";
|
||||
|
||||
|
||||
// Collect all non-zero goods IDs with their names
|
||||
// Note: id_goods contains RECIPE IDs, not item IDs
|
||||
List<string> goodsInfo = new List<string>();
|
||||
@@ -804,7 +804,7 @@ namespace BrewMonster.UI
|
||||
// Try recipe space first
|
||||
DATA_TYPE dt = DATA_TYPE.DT_INVALID;
|
||||
object recipeData = edm.get_data_ptr(recipeId, ID_SPACE.ID_SPACE_RECIPE, ref dt);
|
||||
|
||||
|
||||
// Check if we got recipe data - sometimes dt is DT_INVALID but data is still RECIPE_ESSENCE
|
||||
RECIPE_ESSENCE? recipe = null;
|
||||
if (recipeData != null && recipeData is RECIPE_ESSENCE)
|
||||
@@ -815,11 +815,11 @@ namespace BrewMonster.UI
|
||||
{
|
||||
recipe = (RECIPE_ESSENCE)recipeData;
|
||||
}
|
||||
|
||||
|
||||
if (recipe.HasValue)
|
||||
{
|
||||
RECIPE_ESSENCE recipeValue = recipe.Value;
|
||||
|
||||
|
||||
// Get output item from first target (main output)
|
||||
if (recipeValue.targets != null && recipeValue.targets.Length > 0)
|
||||
{
|
||||
@@ -853,7 +853,7 @@ namespace BrewMonster.UI
|
||||
{
|
||||
BMLogger.LogWarning($" Recipe {recipeId}: targets is null or empty");
|
||||
}
|
||||
|
||||
|
||||
// Get all materials
|
||||
if (recipeValue.materials != null)
|
||||
{
|
||||
@@ -875,7 +875,7 @@ namespace BrewMonster.UI
|
||||
{
|
||||
materialName = $"error: {ex2.Message}";
|
||||
}
|
||||
|
||||
|
||||
string matEntry = !string.IsNullOrEmpty(materialName)
|
||||
? $"{material.id} ({materialName}) x{material.num}"
|
||||
: $"{material.id} (unknown) x{material.num}";
|
||||
@@ -894,7 +894,7 @@ namespace BrewMonster.UI
|
||||
{
|
||||
BMLogger.LogWarning($" Failed to get data for recipe ID {recipeId}: {ex.Message}\n{ex.StackTrace}");
|
||||
}
|
||||
|
||||
|
||||
// Format: "RecipeID -> Output: ID (Name) | Materials: ID (Name) xCount, ..."
|
||||
string goodsEntry = $"Recipe {recipeId}";
|
||||
if (!string.IsNullOrEmpty(outputItemInfo))
|
||||
@@ -917,7 +917,7 @@ namespace BrewMonster.UI
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Log page if it has a title or has goods
|
||||
if (!string.IsNullOrEmpty(pageTitle) || goodsInfo.Count > 0)
|
||||
{
|
||||
@@ -931,22 +931,22 @@ namespace BrewMonster.UI
|
||||
{
|
||||
NPC_DECOMPOSE_SERVICE pService = (NPC_DECOMPOSE_SERVICE)pData;
|
||||
string serviceName = Encoding.Unicode.GetString(MemoryMarshal.AsBytes<ushort>(pService.name));
|
||||
|
||||
|
||||
// Log NPC_DECOMPOSE_SERVICE data
|
||||
BMLogger.Log($"NPC_DECOMPOSE_SERVICE detected - ServiceID: {a_uiService[i]}, DecomposeServiceID: {pService.id}, Name: {serviceName}, DecomposeSkillID: {pService.id_decompose_skill}");
|
||||
|
||||
|
||||
CECHostPlayer hostPlayer = GetHostPlayer();
|
||||
bool hasRequiredSkill = false;
|
||||
|
||||
|
||||
// TODO: Implement proper skill check when GetPassiveSkillNum() and GetPassiveSkillByIndex() are available
|
||||
// For now, we'll use reflection to access private GetPassiveSkillByID method or implement a workaround
|
||||
// The C++ code checks if player has the required decompose skill (TYPE_LIVE or TYPE_PRODUCE)
|
||||
try
|
||||
{
|
||||
// Try using reflection to access private GetPassiveSkillByID method
|
||||
var method = typeof(CECHostPlayer).GetMethod("GetPassiveSkillByID",
|
||||
var method = typeof(CECHostPlayer).GetMethod("GetPassiveSkillByID",
|
||||
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||
|
||||
|
||||
if (method != null)
|
||||
{
|
||||
var pSkill = method.Invoke(hostPlayer, new object[] { (int)pService.id_decompose_skill, false }) as CECSkill;
|
||||
@@ -954,9 +954,9 @@ namespace BrewMonster.UI
|
||||
{
|
||||
int skillType = pSkill.GetType();
|
||||
int skillID = pSkill.GetSkillID();
|
||||
|
||||
|
||||
BMLogger.Log($" Found skill by ID (via reflection): ID={skillID}, Type={skillType}");
|
||||
|
||||
|
||||
// Check if this is the required decompose skill with correct type
|
||||
if ((skillType == (int)CECSkill.SkillType.TYPE_LIVE ||
|
||||
skillType == (int)CECSkill.SkillType.TYPE_PRODUCE) &&
|
||||
@@ -999,7 +999,7 @@ namespace BrewMonster.UI
|
||||
m_pLst_Main.SetItemData(m_pLst_Main.GetCount() - 1, a_uiService[i]);
|
||||
m_pLst_Main.SetItemDataPtr(m_pLst_Main.GetCount() - 1, pData);
|
||||
}
|
||||
|
||||
|
||||
if (!hasRequiredSkill)
|
||||
continue;
|
||||
}
|
||||
@@ -1134,7 +1134,7 @@ namespace BrewMonster.UI
|
||||
// m_pLst_Main.AddString(strText + GetStringFromTable(699));
|
||||
// m_pLst_Main.SetItemData(m_pLst_Main.GetCount() - 1, CDLGNPC_BATTLECHALLENGE);
|
||||
// }
|
||||
// // if( gtime.tm_wday == 5 && gtime.tm_hour >= 18 ||
|
||||
// // if( gtime.tm_wday == 5 && gtime.tm_hour >= 18 ||
|
||||
// // gtime.tm_wday == 6 ||
|
||||
// // gtime.tm_wday == 0 )
|
||||
// if (GetHostPlayer().GetFRoleID() != GNETRoles._R_UNMEMBER)
|
||||
@@ -1647,7 +1647,7 @@ namespace BrewMonster.UI
|
||||
SetDataPtr(pTalk, "ptr_talk_proc");
|
||||
if (!IsShow()) Show(true);
|
||||
}
|
||||
|
||||
|
||||
// bool c(int idFunction, int iService, object pData)
|
||||
// {
|
||||
// AUIDialog pShow1 = null, pShow2 = null;
|
||||
@@ -1687,7 +1687,7 @@ namespace BrewMonster.UI
|
||||
// {
|
||||
// shopManager.OpenNPCShop(npcID);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// else if (idFunction == (int)SERVICE_TYPE.NPC_INSTALL)
|
||||
// {
|
||||
@@ -1994,7 +1994,7 @@ namespace BrewMonster.UI
|
||||
//
|
||||
// return true;
|
||||
// }
|
||||
|
||||
|
||||
public void OnCommand_back(string szCommand)
|
||||
{
|
||||
NPC_ESSENCE? pCurNPCEssence = GetGameUIMan().m_pCurNPCEssence;
|
||||
@@ -2096,7 +2096,7 @@ namespace BrewMonster.UI
|
||||
if (id != pTalk.windows[i].id) continue;
|
||||
|
||||
// TO DO: fix later
|
||||
// TO DO: show popup with content is talk_text and 1 btn OK
|
||||
// TO DO: show popup with content is talk_text and 1 btn OK
|
||||
//GetGameUIMan().MessageBox("", pTask.FormatTaskTalk(pTalk.windows[i].talk_text),
|
||||
// MB_OK, A3DCOLORRGBA(255, 255, 255, 160));
|
||||
GetGameUIMan().EndNPCService();
|
||||
@@ -3236,21 +3236,21 @@ namespace BrewMonster.UI
|
||||
{
|
||||
NPC_MAKE_SERVICE pService = (NPC_MAKE_SERVICE)pData;
|
||||
string serviceName = Encoding.Unicode.GetString(MemoryMarshal.AsBytes<ushort>(pService.name));
|
||||
|
||||
|
||||
// Log NPC_MAKE_SERVICE data when selected
|
||||
BMLogger.Log($"SelectListItem - NPC_MAKE_SERVICE selected - ServiceID: {iService}, MakeServiceID: {pService.id}, Name: {serviceName}, MakeSkillID: {pService.id_make_skill}, ProduceType: {pService.produce_type}");
|
||||
|
||||
|
||||
idFunction = (int)SERVICE_TYPE.NPC_MAKE;
|
||||
}
|
||||
else if (DataType == DATA_TYPE.DT_NPC_DECOMPOSE_SERVICE)
|
||||
{
|
||||
NPC_DECOMPOSE_SERVICE pService = (NPC_DECOMPOSE_SERVICE)pData;
|
||||
string serviceName = Encoding.Unicode.GetString(MemoryMarshal.AsBytes<ushort>(pService.name));
|
||||
|
||||
|
||||
// Log NPC_DECOMPOSE_SERVICE data when selected
|
||||
BMLogger.Log($"SelectListItem - NPC_DECOMPOSE_SERVICE selected - ServiceID: {iService}, DecomposeServiceID: {pService.id}, Name: {serviceName}, DecomposeSkillID: {pService.id_decompose_skill}");
|
||||
BMLogger.Log($" Note: This decompose service is being treated as idFunction={SERVICE_TYPE.NPC_DECOMPOSE}");
|
||||
|
||||
|
||||
idFunction = (int)SERVICE_TYPE.NPC_DECOMPOSE;
|
||||
}
|
||||
else if (DataType == DATA_TYPE.DT_NPC_IDENTIFY_SERVICE)
|
||||
@@ -3342,7 +3342,7 @@ namespace BrewMonster.UI
|
||||
CECTaskInterface pTask;
|
||||
ad.m_ulCandItems = 0;
|
||||
opt.param = 0;
|
||||
|
||||
|
||||
// Check if the integer matches any enum value
|
||||
if (Enum.IsDefined(typeof(SERVICE_TYPE), idFunction))
|
||||
{
|
||||
@@ -3391,7 +3391,7 @@ namespace BrewMonster.UI
|
||||
if (pCurNPCEssence.HasValue)
|
||||
{
|
||||
uint npcID = pCurNPCEssence.Value.id;
|
||||
|
||||
|
||||
// var dlgInstall =GetGameUIMan().GetDialog("Win_Enchase");
|
||||
// dlgInstall.Show(true);
|
||||
CECUIManager.Instance.ShowUI("Win_Enchase");
|
||||
@@ -3446,14 +3446,14 @@ namespace BrewMonster.UI
|
||||
else if (idFunction == (int)SERVICE_TYPE.NPC_MAKE)
|
||||
{
|
||||
NPC_MAKE_SERVICE pMake = (NPC_MAKE_SERVICE)pData;
|
||||
|
||||
|
||||
BMLogger.Log($"PopupCorrespondingServiceDialog - NPC_MAKE: produce_type={pMake.produce_type}, MakeSkillID={pMake.id_make_skill}");
|
||||
|
||||
|
||||
// Dialog loading commented out - Win_Produce dialog not yet implemented
|
||||
// Determine which dialog to use based on produce_type
|
||||
//string dialogName = (pMake.produce_type == 2) ? "Win_Produce1" : "Win_Produce";
|
||||
//pShow1 = m_pAUIManager.GetDialog(dialogName);
|
||||
|
||||
|
||||
//if (pShow1 == null)
|
||||
//{
|
||||
// BMLogger.LogError($"NPC_MAKE: Dialog '{dialogName}' not found! Service may not work correctly.");
|
||||
@@ -3464,7 +3464,7 @@ namespace BrewMonster.UI
|
||||
//{
|
||||
// // Get or set DlgProduce reference (if it exists)
|
||||
// // GetGameUIMan().m_pDlgProduce = (CDlgProduce*)pShow1;
|
||||
//
|
||||
//
|
||||
// // Prepare NPC service
|
||||
// try
|
||||
// {
|
||||
@@ -3474,7 +3474,7 @@ namespace BrewMonster.UI
|
||||
// {
|
||||
// BMLogger.LogError($"NPC_MAKE: Error calling PrepareNPCService: {ex.Message}");
|
||||
// }
|
||||
//
|
||||
//
|
||||
// // Set data pointer
|
||||
// try
|
||||
// {
|
||||
@@ -3484,7 +3484,7 @@ namespace BrewMonster.UI
|
||||
// {
|
||||
// BMLogger.LogError($"NPC_MAKE: Error setting data pointer: {ex.Message}");
|
||||
// }
|
||||
//
|
||||
//
|
||||
// // Clear material for certain produce types
|
||||
// if (pMake.produce_type == 1 ||
|
||||
// pMake.produce_type == 3 ||
|
||||
@@ -3502,10 +3502,10 @@ namespace BrewMonster.UI
|
||||
// BMLogger.LogError($"NPC_MAKE: Error clearing material: {ex.Message}");
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// // Get inventory dialog
|
||||
// pShow2 = m_pAUIManager.GetDialog("Win_Inventory");
|
||||
//
|
||||
//
|
||||
// // Update produce dialog
|
||||
// try
|
||||
// {
|
||||
@@ -3683,7 +3683,7 @@ namespace BrewMonster.UI
|
||||
pTask = GetHostPlayer().GetTaskInterface();
|
||||
|
||||
pTask.GetAwardCandidates(opt.param, ref ad);
|
||||
|
||||
|
||||
if (ad.m_ulCandItems > 1)
|
||||
{
|
||||
dialogue1 = "Win_Award";
|
||||
@@ -3827,17 +3827,10 @@ namespace BrewMonster.UI
|
||||
var dlg2 = CECUIManager.Instance.ShowUI(dialogue2);
|
||||
}
|
||||
}
|
||||
CloseDialogue();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void CloseDialogue()
|
||||
{
|
||||
base.CloseDialogue();
|
||||
GetGameUIMan().EndNPCService();
|
||||
}
|
||||
|
||||
public void OnCommand_CANCEL(string szCommand)
|
||||
{
|
||||
int idCurFinishTask = GetGameUIMan().m_idCurFinishTask;
|
||||
@@ -3847,6 +3840,7 @@ namespace BrewMonster.UI
|
||||
GetGameUIMan().m_idCurFinishTask = -1;
|
||||
}
|
||||
CloseDialogue();
|
||||
GetGameUIMan().EndNPCService();
|
||||
}
|
||||
|
||||
public override void Awake()
|
||||
|
||||
@@ -78,8 +78,8 @@ namespace BrewMonster.UI
|
||||
|
||||
void OnAddFriend(int characterId)
|
||||
{
|
||||
Debug.Log("OnAddFriend: " + characterId);
|
||||
// TODO: c2s add friend when available
|
||||
string name = EC_ManMessageMono.Instance?.GetECManPlayer?.GetElsePlayer(characterId)?.GetName() ?? "";
|
||||
UnityGameSession.Friend_Add(characterId, name);
|
||||
}
|
||||
|
||||
void OnDuel(int characterId)
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using BrewMonster.Network;
|
||||
using BrewMonster.Managers;
|
||||
using BrewMonster.Scripts.Task;
|
||||
using BrewMonster.Scripts;
|
||||
using BrewMonster.Scripts.Chat;
|
||||
using CSNetwork.GPDataType;
|
||||
using CSNetwork;
|
||||
|
||||
namespace BrewMonster.Scripts.UI
|
||||
{
|
||||
public class CECUIHelper
|
||||
{
|
||||
public static string DlgTaskName = "Win_Quest";
|
||||
|
||||
|
||||
public static A3DVECTOR3 GetTaskObjectCoordinates(int id, ref bool in_table)
|
||||
{
|
||||
in_table = false;
|
||||
@@ -24,7 +27,7 @@ namespace BrewMonster.Scripts.UI
|
||||
GPDataTypeHelper.ISNPCID(id) ||
|
||||
GPDataTypeHelper.ISMATTERID(id) ||
|
||||
(id > 100000000); // player ids are typically huge; template ids are usually small
|
||||
|
||||
|
||||
A3DVECTOR3 ret = new A3DVECTOR3(0);
|
||||
var world = EC_Game.GetGameRun()?.GetWorld();
|
||||
if (world != null && isLikelyRuntimeObjectId)
|
||||
@@ -34,17 +37,20 @@ namespace BrewMonster.Scripts.UI
|
||||
{
|
||||
var objPos = obj.transform.position;
|
||||
in_table = true;
|
||||
BMLogger.Log($"[CECUIHelper] GetTaskObjectCoordinates isLikelyRuntimeObjectId: objPos={objPos.x},{objPos.y},{objPos.z}, inTable={in_table}");
|
||||
BMLogger.Log(
|
||||
$"[CECUIHelper] GetTaskObjectCoordinates isLikelyRuntimeObjectId: objPos={objPos.x},{objPos.y},{objPos.z}, inTable={in_table}");
|
||||
ret.Set(objPos.x, objPos.y, objPos.z);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
List<OBJECT_COORD> TargetTemp = new List<OBJECT_COORD>();
|
||||
ret = EC_Game.GetGameRun()?.GetHostPlayer().GetObjectCoordinates(id, out TargetTemp, ref in_table) ?? new A3DVECTOR3(0);
|
||||
ret = EC_Game.GetGameRun()?.GetHostPlayer().GetObjectCoordinates(id, out TargetTemp, ref in_table) ??
|
||||
new A3DVECTOR3(0);
|
||||
|
||||
// 1) Try live NPC/Monster in scene by template id (best match to "click name -> go to that entity")
|
||||
// 1) 先尝试在场景中按模板ID查找活体NPC/怪物(最符合“点名字就去找它”)
|
||||
|
||||
|
||||
//This only work in Major map not A61. Skip for now.
|
||||
// if(!in_table)
|
||||
// {
|
||||
@@ -65,14 +71,16 @@ namespace BrewMonster.Scripts.UI
|
||||
// {
|
||||
// BMLogger.Log($"[CECUIHelper] GetTaskObjectCoordinates in_table: ret={ret.x},{ret.y},{ret.z}, inTable={in_table}");
|
||||
// }
|
||||
if(ret.x != 0 && ret.y != 0 && ret.z != 0)
|
||||
if (ret.x != 0 && ret.y != 0 && ret.z != 0)
|
||||
{
|
||||
BMLogger.Log($"[CECUIHelper] GetHostPlayer GetObjectCoordinates True ret={ret.x},{ret.y},{ret.z}, inTable={in_table}");
|
||||
BMLogger.Log(
|
||||
$"[CECUIHelper] GetHostPlayer GetObjectCoordinates True ret={ret.x},{ret.y},{ret.z}, inTable={in_table}");
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
BMLogger.Log($"[CECUIHelper] GetHostPlayer TryGetFirstObjectCoord False ret={ret.x},{ret.y},{ret.z}, inTable={in_table}");
|
||||
BMLogger.Log(
|
||||
$"[CECUIHelper] GetHostPlayer TryGetFirstObjectCoord False ret={ret.x},{ret.y},{ret.z}, inTable={in_table}");
|
||||
}
|
||||
|
||||
// Fallback to task_npc table (C++: ATaskTemplMan::GetTaskNPCInfo)
|
||||
@@ -83,10 +91,12 @@ namespace BrewMonster.Scripts.UI
|
||||
// NOTE: Keep original PW coordinate mapping: ret.Set(x, z, y)
|
||||
// 注意:保持原版坐标映射:ret.Set(x, z, y)
|
||||
in_table = true;
|
||||
BMLogger.Log($"[CECUIHelper] GetTaskObjectCoordinates TryGetTaskNPCInfo: info.x={info.x}, info.z={info.z}, info.y={info.y}, inTable={in_table}");
|
||||
BMLogger.Log(
|
||||
$"[CECUIHelper] GetTaskObjectCoordinates TryGetTaskNPCInfo: info.x={info.x}, info.z={info.z}, info.y={info.y}, inTable={in_table}");
|
||||
return new A3DVECTOR3(info.x, info.z, info.y);
|
||||
}
|
||||
else{
|
||||
else
|
||||
{
|
||||
BMLogger.Log($"[CECUIHelper] GetTaskObjectCoordinates TryGetTaskNPCInfo: not found for id={id}");
|
||||
}
|
||||
|
||||
@@ -95,14 +105,17 @@ namespace BrewMonster.Scripts.UI
|
||||
if (BrewMonster.Network.EC_Game.TryGetFirstObjectCoord(id.ToString(), out var coordPos, out var mapName))
|
||||
{
|
||||
in_table = true;
|
||||
BMLogger.Log($"[CECUIHelper] GetTaskObjectCoordinates TryGetFirstObjectCoord: coordPos.x={coordPos.x}, coordPos.y={coordPos.y}, coordPos.z={coordPos.z}, inTable={in_table}");
|
||||
BMLogger.Log(
|
||||
$"[CECUIHelper] GetTaskObjectCoordinates TryGetFirstObjectCoord: coordPos.x={coordPos.x}, coordPos.y={coordPos.y}, coordPos.z={coordPos.z}, inTable={in_table}");
|
||||
return new A3DVECTOR3(coordPos.x, coordPos.y, coordPos.z);
|
||||
}
|
||||
|
||||
UnityEngine.Debug.LogWarning($"[CECUIHelper] GetTaskObjectCoordinates: Not found for id={id} (isLikelyRuntimeObjectId={isLikelyRuntimeObjectId}).");
|
||||
BMLogger.Log($"[CECUIHelper] GetTaskObjectCoordinates default return ret={ret.x},{ret.y},{ret.z}, inTable={in_table}");
|
||||
UnityEngine.Debug.LogWarning(
|
||||
$"[CECUIHelper] GetTaskObjectCoordinates: Not found for id={id} (isLikelyRuntimeObjectId={isLikelyRuntimeObjectId}).");
|
||||
BMLogger.Log(
|
||||
$"[CECUIHelper] GetTaskObjectCoordinates default return ret={ret.x},{ret.y},{ret.z}, inTable={in_table}");
|
||||
return ret;
|
||||
|
||||
|
||||
// TODO: Implement this method properly
|
||||
// A3DVECTOR3 ret(0.f);
|
||||
// in_table = false;
|
||||
@@ -119,7 +132,7 @@ namespace BrewMonster.Scripts.UI
|
||||
// }
|
||||
// return ret;
|
||||
}
|
||||
|
||||
|
||||
// Follow coord like C++ CECUIHelper::FollowCoord(enumEICoord, taskId)
|
||||
// 像C++的CECUIHelper::FollowCoord(enumEICoord, taskId)一样跟随坐标
|
||||
public static bool FollowCoord(int id, int taskId)
|
||||
@@ -163,9 +176,12 @@ namespace BrewMonster.Scripts.UI
|
||||
|
||||
if (shouldForceNavigate)
|
||||
{
|
||||
UnityEngine.Debug.Log($"[CECUIHelper] FollowCoord: taskId={taskId} => force navigate (bezier) instead of normal auto-move");
|
||||
hostPlayer.OnNaviageEvent(taskId, (int)BrewMonster.Scripts.CECNavigateCtrl.NavigateEvent.EM_PREPARE);
|
||||
hostPlayer.OnNaviageEvent(taskId, (int)BrewMonster.Scripts.CECNavigateCtrl.NavigateEvent.EM_BEGIN);
|
||||
UnityEngine.Debug.Log(
|
||||
$"[CECUIHelper] FollowCoord: taskId={taskId} => force navigate (bezier) instead of normal auto-move");
|
||||
hostPlayer.OnNaviageEvent(taskId,
|
||||
(int)BrewMonster.Scripts.CECNavigateCtrl.NavigateEvent.EM_PREPARE);
|
||||
hostPlayer.OnNaviageEvent(taskId,
|
||||
(int)BrewMonster.Scripts.CECNavigateCtrl.NavigateEvent.EM_BEGIN);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -189,7 +205,8 @@ namespace BrewMonster.Scripts.UI
|
||||
if (templ != null)
|
||||
{
|
||||
// Helper local function: pick first region center if in current world
|
||||
bool TryUseRegion(uint worldId, uint cnt, BrewMonster.Scripts.Task.Task_Region[] regions, string tag, out A3DVECTOR3 pos)
|
||||
bool TryUseRegion(uint worldId, uint cnt, BrewMonster.Scripts.Task.Task_Region[] regions,
|
||||
string tag, out A3DVECTOR3 pos)
|
||||
{
|
||||
pos = new A3DVECTOR3(0);
|
||||
if (cnt == 0 || regions == null || regions.Length == 0) return false;
|
||||
@@ -205,21 +222,27 @@ namespace BrewMonster.Scripts.UI
|
||||
|
||||
// 1) Deliver zone (often where quest giver is)
|
||||
if (templ.m_FixedData.m_bDelvInZone &&
|
||||
TryUseRegion(templ.m_FixedData.m_ulDelvWorld, templ.m_FixedData.m_ulDelvRegionCnt, templ.m_FixedData.m_pDelvRegion, "DelvInZone", out vPos))
|
||||
TryUseRegion(templ.m_FixedData.m_ulDelvWorld, templ.m_FixedData.m_ulDelvRegionCnt,
|
||||
templ.m_FixedData.m_pDelvRegion, "DelvInZone", out vPos))
|
||||
{
|
||||
inTable = true;
|
||||
}
|
||||
// 2) Reach-site regions (for reach-site tasks / also can be used as guidance)
|
||||
else if (TryUseRegion(templ.m_FixedData.m_ulReachSiteId, templ.m_FixedData.m_ulReachSiteCnt, templ.m_FixedData.m_pReachSite, "ReachSite", out vPos))
|
||||
else if (TryUseRegion(templ.m_FixedData.m_ulReachSiteId, templ.m_FixedData.m_ulReachSiteCnt,
|
||||
templ.m_FixedData.m_pReachSite, "ReachSite", out vPos))
|
||||
{
|
||||
inTable = true;
|
||||
}
|
||||
// 3) Enter region / leave region zones (some tasks use these to define where objectives happen)
|
||||
else if (TryUseRegion(templ.m_FixedData.m_ulEnterRegionWorld, templ.m_FixedData.m_ulEnterRegionCnt, templ.m_FixedData.m_pEnterRegion, "EnterRegion", out vPos))
|
||||
else if (TryUseRegion(templ.m_FixedData.m_ulEnterRegionWorld,
|
||||
templ.m_FixedData.m_ulEnterRegionCnt, templ.m_FixedData.m_pEnterRegion,
|
||||
"EnterRegion", out vPos))
|
||||
{
|
||||
inTable = true;
|
||||
}
|
||||
else if (TryUseRegion(templ.m_FixedData.m_ulLeaveRegionWorld, templ.m_FixedData.m_ulLeaveRegionCnt, templ.m_FixedData.m_pLeaveRegion, "LeaveRegion", out vPos))
|
||||
else if (TryUseRegion(templ.m_FixedData.m_ulLeaveRegionWorld,
|
||||
templ.m_FixedData.m_ulLeaveRegionCnt, templ.m_FixedData.m_pLeaveRegion,
|
||||
"LeaveRegion", out vPos))
|
||||
{
|
||||
inTable = true;
|
||||
}
|
||||
@@ -228,7 +251,8 @@ namespace BrewMonster.Scripts.UI
|
||||
|
||||
if (!inTable)
|
||||
{
|
||||
UnityEngine.Debug.LogWarning($"[CECUIHelper] FollowCoord: No coordinates for id={id}, taskId={taskId} (will not move)");
|
||||
UnityEngine.Debug.LogWarning(
|
||||
$"[CECUIHelper] FollowCoord: No coordinates for id={id}, taskId={taskId} (will not move)");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -266,6 +290,7 @@ namespace BrewMonster.Scripts.UI
|
||||
{
|
||||
work.SetTaskNPCInfo(id, taskId);
|
||||
}
|
||||
|
||||
wm.StartWork_p2(work);
|
||||
|
||||
return true;
|
||||
@@ -279,7 +304,8 @@ namespace BrewMonster.Scripts.UI
|
||||
// 验证输入
|
||||
if (m_TargetCoord == null || m_TargetCoord.Count == 0)
|
||||
{
|
||||
UnityEngine.Debug.LogWarning($"[CECUIHelper] FollowCoord: m_TargetCoord is null or empty, traceName={m_strTraceName} (will not move)");
|
||||
UnityEngine.Debug.LogWarning(
|
||||
$"[CECUIHelper] FollowCoord: m_TargetCoord is null or empty, traceName={m_strTraceName} (will not move)");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -292,7 +318,8 @@ namespace BrewMonster.Scripts.UI
|
||||
// 如果可用,记录地图信息(用于调试)
|
||||
if (!string.IsNullOrEmpty(targetCoord.strMap))
|
||||
{
|
||||
UnityEngine.Debug.Log($"[CECUIHelper] FollowCoord: Target map='{targetCoord.strMap}', traceName={m_strTraceName}");
|
||||
UnityEngine.Debug.Log(
|
||||
$"[CECUIHelper] FollowCoord: Target map='{targetCoord.strMap}', traceName={m_strTraceName}");
|
||||
}
|
||||
|
||||
// Start auto-move work to destination (this is what actually moves the player in this project)
|
||||
@@ -300,7 +327,8 @@ namespace BrewMonster.Scripts.UI
|
||||
CECHostPlayer host = EC_Game.GetGameRun()?.GetHostPlayer();
|
||||
if (host == null)
|
||||
{
|
||||
UnityEngine.Debug.LogError($"[CECUIHelper] FollowCoord: Host player is null, traceName={m_strTraceName}");
|
||||
UnityEngine.Debug.LogError(
|
||||
$"[CECUIHelper] FollowCoord: Host player is null, traceName={m_strTraceName}");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -314,7 +342,8 @@ namespace BrewMonster.Scripts.UI
|
||||
CECHPWorkMove work = wm.CreateWork(CECHPWork.Host_work_ID.WORK_MOVETOPOS) as CECHPWorkMove;
|
||||
if (work == null)
|
||||
{
|
||||
UnityEngine.Debug.LogError($"[CECUIHelper] FollowCoord: Failed to create WORK_MOVETOPOS, traceName={m_strTraceName}");
|
||||
UnityEngine.Debug.LogError(
|
||||
$"[CECUIHelper] FollowCoord: Failed to create WORK_MOVETOPOS, traceName={m_strTraceName}");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -329,13 +358,15 @@ namespace BrewMonster.Scripts.UI
|
||||
|
||||
wm.StartWork_p2(work);
|
||||
|
||||
UnityEngine.Debug.Log($"[CECUIHelper] FollowCoord: Started auto-move to ({vPos.x},{vPos.y},{vPos.z}) map={targetCoord.strMap}, traceName={m_strTraceName}, coordCount={m_TargetCoord.Count}");
|
||||
UnityEngine.Debug.Log(
|
||||
$"[CECUIHelper] FollowCoord: Started auto-move to ({vPos.x},{vPos.y},{vPos.z}) map={targetCoord.strMap}, traceName={m_strTraceName}, coordCount={m_TargetCoord.Count}");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public static void AutoMoveStartComplex(A3DVECTOR3 dst, int targetId = 0, int taskId = 0)
|
||||
{
|
||||
UnityEngine.Debug.Log($"[CECUIHelper] AutoMoveStartComplex: dst={dst}, targetId={targetId}, taskId={taskId}");
|
||||
UnityEngine.Debug.Log(
|
||||
$"[CECUIHelper] AutoMoveStartComplex: dst={dst}, targetId={targetId}, taskId={taskId}");
|
||||
// TODO: Implement this method properly
|
||||
// if( CECAutoPolicy.Instance.IsAutoPolicyEnabled() )
|
||||
// return;
|
||||
@@ -348,5 +379,82 @@ namespace BrewMonster.Scripts.UI
|
||||
msg.dwParam4 = new MsgDataAutoMove(0, targetId, taskId);
|
||||
EC_ManMessage.PostMessage(0, 0, 0, msg);
|
||||
}
|
||||
|
||||
public static string PolicySpecialCharReplace(
|
||||
string szText,
|
||||
CHAT_S2C.PolicyChatParameter pPolicyChatPara)
|
||||
{
|
||||
if (string.IsNullOrEmpty(szText))
|
||||
return szText;
|
||||
|
||||
string result = szText;
|
||||
|
||||
//result = ReplaceNameInPolicyChat(result, pPolicyChatPara);
|
||||
|
||||
string subString;
|
||||
string key;
|
||||
string variable;
|
||||
|
||||
while (FindSpecialCharInPolicyChat(result, out subString, out key, out variable))
|
||||
{
|
||||
/*result = ReplaceSpecialCharInPolicyChat(
|
||||
result,
|
||||
subString,
|
||||
key,
|
||||
variable,
|
||||
pPolicyChatPara);*/
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static bool FindSpecialCharInPolicyChat(
|
||||
string srcPolicyChat,
|
||||
out string subString,
|
||||
out string keyInSubString,
|
||||
out string variableInSubString)
|
||||
{
|
||||
subString = null;
|
||||
keyInSubString = null;
|
||||
variableInSubString = null;
|
||||
|
||||
if (string.IsNullOrEmpty(srcPolicyChat))
|
||||
return false;
|
||||
|
||||
int posStart = srcPolicyChat.IndexOf("{");
|
||||
if (posStart == -1)
|
||||
return false;
|
||||
|
||||
int posEnd = srcPolicyChat.IndexOf("}", posStart);
|
||||
if (posEnd == -1)
|
||||
return false;
|
||||
|
||||
subString = srcPolicyChat.Substring(posStart, posEnd - posStart + 1);
|
||||
|
||||
// remove { }
|
||||
string inner = subString.Substring(1, subString.Length - 2);
|
||||
|
||||
var parts = inner.Split(':');
|
||||
|
||||
if (parts.Length == 2)
|
||||
{
|
||||
keyInSubString = parts[0];
|
||||
variableInSubString = parts[1];
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void RemoveNameFlagFromNPCChat(string chat, out string conv)
|
||||
{
|
||||
if (string.IsNullOrEmpty(chat))
|
||||
{
|
||||
conv = string.Empty;
|
||||
return;
|
||||
}
|
||||
|
||||
conv = chat.Replace("&", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -391,9 +391,16 @@ namespace BrewMonster.UI
|
||||
|
||||
public void ShowErrorMsg(string pszMsg, string pszName)
|
||||
{
|
||||
CECUIManager.Instance.ShowMessageBox(pszName, pszMsg, MessageBoxType.YesButton);
|
||||
CECUIManager.Instance.ShowMessageBoxYes(pszName, pszMsg, null,null);
|
||||
}
|
||||
|
||||
/*CDlgPopMsg m_pDlgPopMsg;
|
||||
void AddHeartBeatHint(string pszMsg)
|
||||
{
|
||||
m_pDlgPopMsg->Add(pszMsg);
|
||||
}*/
|
||||
}
|
||||
|
||||
public enum EC_GAMEUI_ICONS : byte
|
||||
{
|
||||
ICONS_ACTION = 0,
|
||||
|
||||
@@ -55,11 +55,7 @@ namespace BrewMonster
|
||||
player.GetCurSkill() != null ||
|
||||
player.IsFighting())
|
||||
{
|
||||
uiManager.ShowMessageBox(new MessageBoxData()
|
||||
{
|
||||
Title = "MessageBox",
|
||||
Message = gameUIMan.GetStringFromTable(11327)
|
||||
});
|
||||
uiManager.ShowMessageBoxGeneral("MessageBox", gameUIMan.GetStringFromTable(11327), this);
|
||||
}
|
||||
|
||||
int nCondition = CECHostSkillModel.Instance.CheckLearnCondition(m_skillID);
|
||||
@@ -102,12 +98,14 @@ namespace BrewMonster
|
||||
int needSp = CECHostSkillModel.Instance.GetSkillSp(m_skillID, m_curLevel + 1);
|
||||
|
||||
string str = GPDataTypeHelper.ReplacePercentD(GetStringFromTable(11326), needMoney, needSp);
|
||||
var messagebox = uiManager.ShowMessageBox(new MessageBoxData()
|
||||
{
|
||||
Title = "Game_LearnSkill",
|
||||
Message = str,
|
||||
OnClickedYes = () => UnityGameSession.c2s_SendCmdNPCSevLearnSkill(m_skillID)
|
||||
});
|
||||
// var messagebox = uiManager.ShowMessageBox(new MessageBoxData()
|
||||
// {
|
||||
// Title = "Game_LearnSkill",
|
||||
// Message = str,
|
||||
// OnClickedYes = () => UnityGameSession.c2s_SendCmdNPCSevLearnSkill(m_skillID)
|
||||
// });
|
||||
var messagebox = uiManager.ShowMessageBoxYes("Game_LearnSkill", str, this,
|
||||
() => UnityGameSession.c2s_SendCmdNPCSevLearnSkill(m_skillID));
|
||||
messagebox.SetData((uint)m_skillID);
|
||||
//GetGameUIMan()->MessageBox("Game_LearnSkill", str, //GetGameUIMan()->GetStringFromTable(231),
|
||||
// MB_OKCANCEL, A3DCOLORRGBA(255, 255, 255, 160), &pMsgBox);
|
||||
|
||||
@@ -40,12 +40,8 @@ namespace BrewMonster.UI
|
||||
|
||||
void OnCommandRepick()
|
||||
{
|
||||
CECUIManager.Instance.ShowMessageBox(
|
||||
title: "Thoát",
|
||||
message: CECUIManager.Instance.GetInGameUIMan().GetStringFromTable(202),
|
||||
messageBoxType: MessageBoxType.BothYesNoButton,
|
||||
onClickedYes: OnClickYes
|
||||
);
|
||||
CECUIManager.Instance.ShowMessageBoxYes("Thoát",
|
||||
CECUIManager.Instance.GetInGameUIMan().GetStringFromTable(202), null, OnClickYes);
|
||||
}
|
||||
|
||||
void OnClickYes()
|
||||
|
||||
@@ -14,7 +14,7 @@ using UnityEngine.UI;
|
||||
namespace BrewMonster.UI
|
||||
{
|
||||
/// <summary>
|
||||
/// Login Flow:
|
||||
/// Login Flow:
|
||||
/// 1. Enter username and password
|
||||
/// 2. Click login button
|
||||
/// 3. Login success, get the list of characters
|
||||
@@ -92,13 +92,14 @@ namespace BrewMonster.UI
|
||||
#if UNITY_EDITOR
|
||||
if (Input.GetKeyUp(KeyCode.LeftAlt))
|
||||
{
|
||||
_usernameInputField.text = "test004";
|
||||
_usernameInputField.text = "test016";
|
||||
_passwordInputField.text = "123456";
|
||||
OnLoginButtonClicked();
|
||||
}
|
||||
|
||||
if (Input.GetKeyUp(KeyCode.Tab))
|
||||
{
|
||||
_usernameInputField.text = "test002";
|
||||
_usernameInputField.text = "test017";
|
||||
_passwordInputField.text = "123456";
|
||||
OnLoginButtonClicked();
|
||||
}
|
||||
@@ -135,7 +136,7 @@ namespace BrewMonster.UI
|
||||
BMLogger.LogError("[LoginScreenUI] Username/password empty.");
|
||||
await BeginGameLoginAsync(_usernameInputField.text, _passwordInputField.text);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private async Task BeginGameLoginAsync(string username, string password)
|
||||
@@ -179,7 +180,7 @@ namespace BrewMonster.UI
|
||||
// If we're returning to select role, skip straight to select role without showing login UI again, since we never fully left the game session.
|
||||
OnLoginComplete(true);
|
||||
return;
|
||||
|
||||
|
||||
// Auto-login to reach Select Role like the original client, without showing Tech3C auth UI again.
|
||||
if (!string.IsNullOrEmpty(_usernameInputField.text) && !string.IsNullOrEmpty(_passwordInputField.text))
|
||||
{
|
||||
@@ -192,7 +193,7 @@ namespace BrewMonster.UI
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Callback when the login is complete.
|
||||
/// Callback when the login is complete.
|
||||
/// Then get the list of characters
|
||||
/// </summary>
|
||||
private void OnLoginComplete(bool result)
|
||||
@@ -214,7 +215,7 @@ namespace BrewMonster.UI
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Callback when the list of characters is retrieved.
|
||||
/// Callback when the list of characters is retrieved.
|
||||
/// Then move to the select character screen
|
||||
/// </summary>
|
||||
private void OnGetRoleListComplete(List<RoleInfo> roleInfos)
|
||||
@@ -344,7 +345,7 @@ namespace BrewMonster.UI
|
||||
UnityGameSession.EnterWorldAsync(roleInfo, OnEnterWorldComplete);
|
||||
#else
|
||||
string nameScene = UnityGameSession.Instance.GetWorldInstanceName();
|
||||
UnityGameSession.Instance.LoadScene(nameScene, LoadSceneMode.Single,
|
||||
UnityGameSession.Instance.LoadScene(nameScene, LoadSceneMode.Single,
|
||||
(progress) =>
|
||||
{
|
||||
LoadingSceneController.Instance.SetProgress(progress);
|
||||
@@ -356,7 +357,7 @@ namespace BrewMonster.UI
|
||||
isDoneWorldRender = true;
|
||||
actLoadChar?.Invoke();
|
||||
UnityGameSession.EnterWorldAsync(roleInfo, OnEnterWorldComplete);
|
||||
|
||||
|
||||
});
|
||||
#endif
|
||||
}, null);
|
||||
@@ -369,7 +370,9 @@ namespace BrewMonster.UI
|
||||
UnityGameSession.RequestAllInventoriesAsync(() => { /*BMLogger.Log("Sent Inventory Detail Requests (all packs)");*/ }, 0, 1, 2);
|
||||
await Task.Delay(1000);
|
||||
UnityGameSession.RequestCheckSecurityPassWd("");
|
||||
// C++ friend_GetList(); required before Add Friend
|
||||
await Task.Delay(1000);
|
||||
UnityGameSession.Friend_GetList();
|
||||
}
|
||||
|
||||
//private void OnInventoryReceived(List<InventoryItem> inventoryData)
|
||||
@@ -433,4 +436,4 @@ namespace BrewMonster.UI
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using BrewMonster.Scripts.ChatUI;
|
||||
using CSNetwork.GPDataType;
|
||||
using Cysharp.Threading.Tasks;
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
@@ -12,9 +15,14 @@ namespace BrewMonster.PerfectWorld.Scripts.UI
|
||||
public Image healthImage;
|
||||
[SerializeField] private TextMeshProUGUI nameText;
|
||||
[SerializeField] private Transform canvasRoot;
|
||||
[SerializeField] private TextMeshProUGUI chatText;
|
||||
|
||||
[Header("References")]
|
||||
[SerializeField] private CECPlayer hostplayer;
|
||||
|
||||
[SerializeField] private float chatDisplayDuration = 5f;
|
||||
|
||||
private CancellationTokenSource _chatCts;
|
||||
|
||||
private const string DefaultLayerName = "Default";
|
||||
private bool _isVisible;
|
||||
@@ -34,18 +42,22 @@ namespace BrewMonster.PerfectWorld.Scripts.UI
|
||||
return;
|
||||
}
|
||||
|
||||
SetVisible(false);
|
||||
SetVisible(true);
|
||||
|
||||
RefreshName();
|
||||
EventBus.SubscribeChannel<EventChatMessageOnTopPlayer>(hostplayer.GetCharacterID(), SetChatMessage);
|
||||
EventBus.SubscribeChannel<cmd_self_info_00>(hostplayer.m_PlayerInfo.cid, UpdateHostPlayerInfoUI);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
_chatCts?.Cancel();
|
||||
_chatCts?.Dispose();
|
||||
if (hostplayer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
EventBus.UnsubscribeChannel<EventChatMessageOnTopPlayer>(hostplayer.GetCharacterID(), SetChatMessage);
|
||||
EventBus.UnsubscribeChannel<cmd_self_info_00>(hostplayer.m_PlayerInfo.cid, UpdateHostPlayerInfoUI);
|
||||
}
|
||||
|
||||
@@ -121,5 +133,63 @@ namespace BrewMonster.PerfectWorld.Scripts.UI
|
||||
for (int i = 0; i < t.childCount; i++)
|
||||
SetLayerRecursively(t.GetChild(i).gameObject, layer);
|
||||
}
|
||||
|
||||
private void SetChatMessage(EventChatMessageOnTopPlayer cxt)
|
||||
{
|
||||
if (chatText == null)
|
||||
{
|
||||
BMLogger.LogError("Don't have chatText TMProUI");
|
||||
return;
|
||||
}
|
||||
ChatThreadDispatcher.Instance.Post(() =>{
|
||||
SetLastSaidWords(cxt.context);
|
||||
});
|
||||
}
|
||||
|
||||
public void SetLastSaidWords(string message, float duration = -1f)
|
||||
{
|
||||
if (chatText == null || string.IsNullOrEmpty(message))
|
||||
return;
|
||||
|
||||
chatText.text = message;
|
||||
chatText.gameObject.SetActive(true);
|
||||
|
||||
// cancel timer cũ
|
||||
_chatCts?.Cancel();
|
||||
_chatCts = new CancellationTokenSource();
|
||||
|
||||
float time = duration > 0 ? duration : chatDisplayDuration;
|
||||
|
||||
HideChatAsync(time, _chatCts.Token).Forget();
|
||||
}
|
||||
|
||||
private async UniTaskVoid HideChatAsync(float time, CancellationToken token)
|
||||
{
|
||||
try
|
||||
{
|
||||
await UniTask.Delay(
|
||||
TimeSpan.FromSeconds(time),
|
||||
cancellationToken: token
|
||||
);
|
||||
|
||||
if (chatText != null)
|
||||
chatText.gameObject.SetActive(false);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// chat mới đến → timer cũ bị cancel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct EventChatMessageOnTopPlayer
|
||||
{
|
||||
public int roleId;
|
||||
public string context;
|
||||
public EventChatMessageOnTopPlayer(int roleId, string context)
|
||||
{
|
||||
this.roleId = roleId;
|
||||
this.context = context;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 45d0006e3eb113541bab860d9be89d94
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
@@ -0,0 +1,117 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b2f1cd3ee517556479672deef3004b2d
|
||||
TextureImporter:
|
||||
internalIDToNameTable: []
|
||||
externalObjects: {}
|
||||
serializedVersion: 13
|
||||
mipmaps:
|
||||
mipMapMode: 0
|
||||
enableMipMap: 0
|
||||
sRGBTexture: 1
|
||||
linearTexture: 0
|
||||
fadeOut: 0
|
||||
borderMipMap: 0
|
||||
mipMapsPreserveCoverage: 0
|
||||
alphaTestReferenceValue: 0.5
|
||||
mipMapFadeDistanceStart: 1
|
||||
mipMapFadeDistanceEnd: 3
|
||||
bumpmap:
|
||||
convertToNormalMap: 0
|
||||
externalNormalMap: 0
|
||||
heightScale: 0.25
|
||||
normalMapFilter: 0
|
||||
flipGreenChannel: 0
|
||||
isReadable: 0
|
||||
streamingMipmaps: 0
|
||||
streamingMipmapsPriority: 0
|
||||
vTOnly: 0
|
||||
ignoreMipmapLimit: 0
|
||||
grayScaleToAlpha: 0
|
||||
generateCubemap: 6
|
||||
cubemapConvolution: 0
|
||||
seamlessCubemap: 0
|
||||
textureFormat: 1
|
||||
maxTextureSize: 2048
|
||||
textureSettings:
|
||||
serializedVersion: 2
|
||||
filterMode: 1
|
||||
aniso: 1
|
||||
mipBias: 0
|
||||
wrapU: 1
|
||||
wrapV: 1
|
||||
wrapW: 0
|
||||
nPOTScale: 0
|
||||
lightmap: 0
|
||||
compressionQuality: 50
|
||||
spriteMode: 1
|
||||
spriteExtrude: 1
|
||||
spriteMeshType: 1
|
||||
alignment: 0
|
||||
spritePivot: {x: 0.5, y: 0.5}
|
||||
spritePixelsToUnits: 100
|
||||
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
|
||||
spriteGenerateFallbackPhysicsShape: 1
|
||||
alphaUsage: 1
|
||||
alphaIsTransparency: 1
|
||||
spriteTessellationDetail: -1
|
||||
textureType: 8
|
||||
textureShape: 1
|
||||
singleChannelComponent: 0
|
||||
flipbookRows: 1
|
||||
flipbookColumns: 1
|
||||
maxTextureSizeSet: 0
|
||||
compressionQualitySet: 0
|
||||
textureFormatSet: 0
|
||||
ignorePngGamma: 0
|
||||
applyGammaDecoding: 0
|
||||
swizzle: 50462976
|
||||
cookieLightType: 0
|
||||
platformSettings:
|
||||
- serializedVersion: 4
|
||||
buildTarget: DefaultTexturePlatform
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
- serializedVersion: 4
|
||||
buildTarget: Standalone
|
||||
maxTextureSize: 2048
|
||||
resizeAlgorithm: 0
|
||||
textureFormat: -1
|
||||
textureCompression: 1
|
||||
compressionQuality: 50
|
||||
crunchedCompression: 0
|
||||
allowsAlphaSplitting: 0
|
||||
overridden: 0
|
||||
ignorePlatformSupport: 0
|
||||
androidETC2FallbackOverride: 0
|
||||
forceMaximumCompressionQuality_BC6H_BC7: 0
|
||||
spriteSheet:
|
||||
serializedVersion: 2
|
||||
sprites: []
|
||||
outline: []
|
||||
customData:
|
||||
physicsShape: []
|
||||
bones: []
|
||||
spriteID: 5e97eb03825dee720800000000000000
|
||||
internalID: 0
|
||||
vertices: []
|
||||
indices:
|
||||
edges: []
|
||||
weights: []
|
||||
secondaryTextures: []
|
||||
spriteCustomMetadata:
|
||||
entries: []
|
||||
nameFileIdTable: {}
|
||||
mipmapLimitGroupName:
|
||||
pSDRemoveMatte: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 38ebca484ba26f2408bed39f0b6b33dd
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,46 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &1735791580658873054
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1508221814777778566}
|
||||
- component: {fileID: 5062616109990871891}
|
||||
m_Layer: 0
|
||||
m_Name: preFfab_ChatThreadDispatcher
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &1508221814777778566
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1735791580658873054}
|
||||
serializedVersion: 2
|
||||
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: 0}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!114 &5062616109990871891
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1735791580658873054}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 00392315a7ee47e7a9527eda504fb312, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 820475c3b63a5de4b9b311fe9e0de9b5
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9dee979d4a90a8640b3dd731816b2656
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,228 @@
|
||||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!1 &6240941777052618231
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 5390685607869309037}
|
||||
- component: {fileID: 7228077960814023056}
|
||||
- component: {fileID: 5305392080666511277}
|
||||
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 &5390685607869309037
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6240941777052618231}
|
||||
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: 1869019404724936087}
|
||||
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: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &7228077960814023056
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6240941777052618231}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &5305392080666511277
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6240941777052618231}
|
||||
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: New Text
|
||||
m_isRightToLeft: 0
|
||||
m_fontAsset: {fileID: 11400000, guid: 8f586378b4e144a9851e7b34d9b748ee, type: 2}
|
||||
m_sharedMaterial: {fileID: 2180264, guid: 8f586378b4e144a9851e7b34d9b748ee, 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: 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 &6627717456258223658
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1869019404724936087}
|
||||
- component: {fileID: 616079771158270572}
|
||||
- component: {fileID: 3486315639058223012}
|
||||
- component: {fileID: 1976417251556044024}
|
||||
m_Layer: 5
|
||||
m_Name: prefab_TextContents
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!224 &1869019404724936087
|
||||
RectTransform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6627717456258223658}
|
||||
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: 5390685607869309037}
|
||||
m_Father: {fileID: 0}
|
||||
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_SizeDelta: {x: 800, y: 50}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!222 &616079771158270572
|
||||
CanvasRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6627717456258223658}
|
||||
m_CullTransparentMesh: 1
|
||||
--- !u!114 &3486315639058223012
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6627717456258223658}
|
||||
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: 0, g: 0, b: 0, a: 0}
|
||||
m_RaycastTarget: 0
|
||||
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
|
||||
m_Maskable: 0
|
||||
m_OnCullStateChanged:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
m_Sprite: {fileID: 0}
|
||||
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 &1976417251556044024
|
||||
MonoBehaviour:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 6627717456258223658}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: 179d32c667fc2f641bdcb7afb18046b9, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
messageText: {fileID: 5305392080666511277}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dcc75569599675f46a99bc66a87efc9a
|
||||
PrefabImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -10122,11 +10122,12 @@ MonoBehaviour:
|
||||
m_SlotSecondParent: {fileID: 7151360590639773519}
|
||||
m_TxtSecondName: {fileID: 4492084240745408081}
|
||||
m_TxtMoney: {fileID: 6140428454487430115}
|
||||
m_useItem: {fileID: 29649554038592406}
|
||||
m_BtnMergeOrReset: {fileID: 8208092408021918524}
|
||||
m_BtnCancel: {fileID: 4503836757578509720}
|
||||
m_BtnClose: {fileID: 5942200196902544367}
|
||||
khung_item: {fileID: 21300000, guid: a5366f3bce011c046902e39b6bd3a077, type: 3}
|
||||
itemInventoryRoot: {fileID: 6829484673054423729}
|
||||
itemInventoryRoot: {fileID: 3361511320564075180}
|
||||
--- !u!1 &5641506892578507279
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
@@ -11541,12 +11542,12 @@ MonoBehaviour:
|
||||
- {fileID: 682304874874096685}
|
||||
equipmentPackButtons: []
|
||||
fashionPackButtons: []
|
||||
detailPanelRoot: {fileID: 4012993487235845803}
|
||||
detailPanelRoot: {fileID: 895914416731758390}
|
||||
detailPanelOffset: {x: 20, y: 0}
|
||||
hideDetailOnStart: 1
|
||||
descriptionText:
|
||||
legacy: {fileID: 0}
|
||||
tmp: {fileID: 7977462308482374098}
|
||||
tmp: {fileID: 6154813818007210063}
|
||||
equipButton: {fileID: 0}
|
||||
dropButton: {fileID: 0}
|
||||
autoRefresh: 1
|
||||
@@ -15326,7 +15327,7 @@ RectTransform:
|
||||
- {fileID: 368043242273701515}
|
||||
- {fileID: 9177880079034759179}
|
||||
- {fileID: 7246818111234384827}
|
||||
- {fileID: 6829484673054423729}
|
||||
- {fileID: 7284815503472110380}
|
||||
m_Father: {fileID: 2126663214709926210}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
m_AnchorMin: {x: 0.5, y: 0.5}
|
||||
@@ -15494,7 +15495,7 @@ MonoBehaviour:
|
||||
m_OnClick:
|
||||
m_PersistentCalls:
|
||||
m_Calls: []
|
||||
--- !u!1001 &5454539435686182200
|
||||
--- !u!1001 &8101275948331805861
|
||||
PrefabInstance:
|
||||
m_ObjectHideFlags: 0
|
||||
serializedVersion: 2
|
||||
@@ -15502,6 +15503,42 @@ PrefabInstance:
|
||||
serializedVersion: 3
|
||||
m_TransformParent: {fileID: 7802877985602690998}
|
||||
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: 777847736648841921, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
|
||||
propertyPath: m_text
|
||||
value: "D\xF9ng"
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 777847736648841921, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
|
||||
propertyPath: m_fontSize
|
||||
value: 48.2
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 777847736648841921, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
|
||||
propertyPath: m_fontSizeBase
|
||||
value: 48.2
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 777847736648841921, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
|
||||
propertyPath: m_enableAutoSizing
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 777847736648841921, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
|
||||
propertyPath: m_VerticalAlignment
|
||||
value: 256
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1546246053547542409, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
|
||||
propertyPath: m_Pivot.x
|
||||
value: 0.5
|
||||
@@ -15532,7 +15569,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
|
||||
@@ -15582,37 +15619,103 @@ PrefabInstance:
|
||||
propertyPath: m_LocalEulerAnglesHint.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1900527214026617767, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
|
||||
propertyPath: m_SizeDelta.x
|
||||
value: 200
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1900527214026617767, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
|
||||
propertyPath: m_SizeDelta.y
|
||||
value: 67.9
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1900527214026617767, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
|
||||
propertyPath: m_AnchoredPosition.x
|
||||
value: 532
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 1900527214026617767, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
|
||||
propertyPath: m_AnchoredPosition.y
|
||||
value: -37
|
||||
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:
|
||||
- {fileID: 5721094068644211543, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
|
||||
- {fileID: 2412057975732520665, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
|
||||
m_AddedGameObjects: []
|
||||
m_AddedComponents: []
|
||||
m_SourcePrefab: {fileID: 100100000, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
|
||||
--- !u!114 &4012993487235845803 stripped
|
||||
--- !u!114 &29649554038592406 stripped
|
||||
MonoBehaviour:
|
||||
m_CorrespondingSourceObject: {fileID: 8936108025019184019, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
|
||||
m_PrefabInstance: {fileID: 5454539435686182200}
|
||||
m_CorrespondingSourceObject: {fileID: 8071811253980610355, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
|
||||
m_PrefabInstance: {fileID: 8101275948331805861}
|
||||
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 &895914416731758390 stripped
|
||||
MonoBehaviour:
|
||||
m_CorrespondingSourceObject: {fileID: 8936108025019184019, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
|
||||
m_PrefabInstance: {fileID: 8101275948331805861}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3361511320564075180}
|
||||
m_Enabled: 1
|
||||
m_EditorHideFlags: 0
|
||||
m_Script: {fileID: 11500000, guid: fc26b8fa93aea49b4abb8fe5455e51fe, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
--- !u!224 &6829484673054423729 stripped
|
||||
RectTransform:
|
||||
m_CorrespondingSourceObject: {fileID: 1546246053547542409, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
|
||||
m_PrefabInstance: {fileID: 5454539435686182200}
|
||||
--- !u!1 &3361511320564075180 stripped
|
||||
GameObject:
|
||||
m_CorrespondingSourceObject: {fileID: 6830833846243993097, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
|
||||
m_PrefabInstance: {fileID: 8101275948331805861}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
--- !u!114 &7977462308482374098 stripped
|
||||
--- !u!114 &6154813818007210063 stripped
|
||||
MonoBehaviour:
|
||||
m_CorrespondingSourceObject: {fileID: 2668322321768899818, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
|
||||
m_PrefabInstance: {fileID: 5454539435686182200}
|
||||
m_PrefabInstance: {fileID: 8101275948331805861}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 0}
|
||||
m_Enabled: 1
|
||||
@@ -15620,3 +15723,8 @@ MonoBehaviour:
|
||||
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
|
||||
m_Name:
|
||||
m_EditorClassIdentifier:
|
||||
--- !u!224 &7284815503472110380 stripped
|
||||
RectTransform:
|
||||
m_CorrespondingSourceObject: {fileID: 1546246053547542409, guid: c56ed80641ff74ce49f91401e3eb8367, type: 3}
|
||||
m_PrefabInstance: {fileID: 8101275948331805861}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user