From 100e734a0f5bc7438a3a3c04a05bbe28df5c5a1d Mon Sep 17 00:00:00 2001 From: CuongNV <> Date: Wed, 22 Apr 2026 16:32:58 +0700 Subject: [PATCH 01/17] Test preview chat for mobile --- .../Scripts/Chat/UI/ChatSystemlUI.cs | 19 +- .../Scripts/Chat/UI/MiniChatUI.cs | 2 +- .../ChatSystem/prefab_ChatSystemUI.prefab | 220 ++++++++++++++++++ Assets/Scripts/ChatInputHandler.cs | 89 +++++++ 4 files changed, 326 insertions(+), 4 deletions(-) diff --git a/Assets/PerfectWorld/Scripts/Chat/UI/ChatSystemlUI.cs b/Assets/PerfectWorld/Scripts/Chat/UI/ChatSystemlUI.cs index 17d416447b..fdb12bd4b2 100644 --- a/Assets/PerfectWorld/Scripts/Chat/UI/ChatSystemlUI.cs +++ b/Assets/PerfectWorld/Scripts/Chat/UI/ChatSystemlUI.cs @@ -156,7 +156,7 @@ namespace BrewMonster.Scripts.ChatUI void OnOpenChatPanelRequested(OpenChatPanelRequestedEvent _) { if (this == null) return; - OpenChatPanel(); + OpenChatPanel(_.focusInputOnOpen); } private bool ShouldShowMessage(ChatMessageData data) @@ -347,13 +347,18 @@ namespace BrewMonster.Scripts.ChatUI } public void OpenChatPanel() + { + OpenChatPanel(true); + } + + public void OpenChatPanel(bool focusInputOnOpen) { if (chatPanelUIGO == null) return; SetChatPanelAndBgVisible(true); RefreshVisible(); _chatInput ??= GetComponent(); - if (_chatInput != null && _chatInput.inputField != null) + if (focusInputOnOpen && _chatInput != null && _chatInput.inputField != null) _chatInput.inputField.ActivateInputField(); } @@ -392,5 +397,13 @@ namespace BrewMonster.Scripts.ChatUI /// /// Mini chat (hoặc HUD) publish để mở panel chat đầy đủ; subscribe. /// - public struct OpenChatPanelRequestedEvent { } + public struct OpenChatPanelRequestedEvent + { + public bool focusInputOnOpen; + + public OpenChatPanelRequestedEvent(bool focusInputOnOpen) + { + this.focusInputOnOpen = focusInputOnOpen; + } + } } diff --git a/Assets/PerfectWorld/Scripts/Chat/UI/MiniChatUI.cs b/Assets/PerfectWorld/Scripts/Chat/UI/MiniChatUI.cs index 101a4f021e..132a66326a 100644 --- a/Assets/PerfectWorld/Scripts/Chat/UI/MiniChatUI.cs +++ b/Assets/PerfectWorld/Scripts/Chat/UI/MiniChatUI.cs @@ -84,7 +84,7 @@ namespace BrewMonster.Scripts.ChatUI if (!string.IsNullOrEmpty(chatDialogName) && CECUIManager.Instance != null) CECUIManager.Instance.ShowUI(chatDialogName); - EventBus.Publish(new OpenChatPanelRequestedEvent()); + EventBus.Publish(new OpenChatPanelRequestedEvent(false)); } void OnChannelFilterChanged(ChatChannelFilterChangedEvent e) diff --git a/Assets/Prefabs/ChatSystem/prefab_ChatSystemUI.prefab b/Assets/Prefabs/ChatSystem/prefab_ChatSystemUI.prefab index f1481018a6..baca285293 100644 --- a/Assets/Prefabs/ChatSystem/prefab_ChatSystemUI.prefab +++ b/Assets/Prefabs/ChatSystem/prefab_ChatSystemUI.prefab @@ -187,6 +187,7 @@ RectTransform: m_Children: - {fileID: 2739455247741079987} - {fileID: 1473246120053017968} + - {fileID: 3282421669272709967} m_Father: {fileID: 0} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 0} @@ -234,6 +235,13 @@ MonoBehaviour: m_EditorClassIdentifier: inputField: {fileID: 3037472824809581303} chatSystem: {fileID: 11400000, guid: 43f54723aa074c74e83e5be28975bee5, type: 2} + inputBarRoot: {fileID: 6545370471477453367} + keyboardBottomPadding: 0 + followMobileKeyboard: 1 + typingPreviewRoot: {fileID: 4018402239503345071} + typingPreviewText: {fileID: 5673195965320428126} + typingPreviewRect: {fileID: 3282421669272709967} + previewVerticalOffset: 8 _spriteMap: {fileID: 11400000, guid: f634ecf63ca3d004f82af9b17c966fc9, type: 2} channelButtons: - channel: 0 @@ -1110,6 +1118,142 @@ MonoBehaviour: m_OnValueChanged: m_PersistentCalls: m_Calls: [] +--- !u!1 &2898638287858915607 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2130739312226787242} + - component: {fileID: 9133278188770223934} + - component: {fileID: 5673195965320428126} + 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 &2130739312226787242 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2898638287858915607} + 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: 3282421669272709967} + 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 &9133278188770223934 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2898638287858915607} + m_CullTransparentMesh: 1 +--- !u!114 &5673195965320428126 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2898638287858915607} + 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: 369c2e14814cc9a4b8e3ad4e37769134, type: 2} + m_sharedMaterial: {fileID: 9092487103257209053, guid: 369c2e14814cc9a4b8e3ad4e37769134, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4294967295 + m_fontColor: {r: 0, g: 0, b: 0, 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: 256 + 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 &3095457626805744279 GameObject: m_ObjectHideFlags: 0 @@ -1869,6 +2013,82 @@ MonoBehaviour: m_OnClick: m_PersistentCalls: m_Calls: [] +--- !u!1 &4018402239503345071 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3282421669272709967} + - component: {fileID: 1622721548801034628} + - component: {fileID: 7848833373218203551} + m_Layer: 5 + m_Name: Typing Preview + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!224 &3282421669272709967 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4018402239503345071} + 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: 2130739312226787242} + m_Father: {fileID: 806170753671297629} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0, y: 0.5} + m_AnchorMax: {x: 1, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 0, y: 100} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &1622721548801034628 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4018402239503345071} + m_CullTransparentMesh: 1 +--- !u!114 &7848833373218203551 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 4018402239503345071} + 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: 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_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!1 &4042949282556349356 GameObject: m_ObjectHideFlags: 0 diff --git a/Assets/Scripts/ChatInputHandler.cs b/Assets/Scripts/ChatInputHandler.cs index 88371501f1..7ed8df6ca6 100644 --- a/Assets/Scripts/ChatInputHandler.cs +++ b/Assets/Scripts/ChatInputHandler.cs @@ -31,6 +31,24 @@ namespace BrewMonster.Scripts.ChatUI public TMP_InputField inputField; public ChatSystemSO chatSystem; + [Header("Mobile Keyboard Follow")] + [Tooltip("Rect chứa input/chat composer cần neo lên trên keyboard.")] + [SerializeField] private RectTransform inputBarRoot; + [Tooltip("Padding đáy thêm cho vùng safe-area/gesture.")] + [SerializeField] private float keyboardBottomPadding = 0f; + [Tooltip("Bật để input bar bám theo keyboard khi mobile keyboard mở.")] + [SerializeField] private bool followMobileKeyboard = true; + + [Header("Typing Preview")] + [Tooltip("Root của box xem trước nội dung đang gõ (nằm trên input bar).")] + [SerializeField] private GameObject typingPreviewRoot; + [Tooltip("Text hiển thị nội dung đang gõ realtime.")] + [SerializeField] private TMP_Text typingPreviewText; + [Tooltip("Rect của preview box để neo theo input bar.")] + [SerializeField] private RectTransform typingPreviewRect; + [Tooltip("Khoảng cách dọc cộng thêm cho preview box.")] + [SerializeField] private float previewVerticalOffset = 8f; + [Header("Emoji")] [Tooltip("SO ánh xạ emotion → TMP sprite tag. Gán EmotionLibrarySpriteMap đã build từ Emotion Atlas Converter.")] [SerializeField] EmotionLibrarySpriteMap _spriteMap; @@ -110,6 +128,9 @@ namespace BrewMonster.Scripts.ChatUI private float m_dwTickFarCry = 0; private float m_dwTickFarCry2 = 0; + private Vector2 _inputBarBaseAnchoredPos; + private Vector2 _typingPreviewBaseAnchoredPos; + private bool _cachedAnchors; private void Awake() { @@ -172,6 +193,13 @@ namespace BrewMonster.Scripts.ChatUI } UpdateChatDropdownInteractable(); + CacheInitialAnchors(); + UpdateTypingPreviewFromInput(); + } + + private void Update() + { + UpdateKeyboardAnchoredLayout(); } // ===================================================== @@ -812,6 +840,8 @@ namespace BrewMonster.Scripts.ChatUI { inputField.text = ""; } + + UpdateTypingPreviewFromInput(); } private int GetEmotionSetForCurrentChannel() @@ -877,6 +907,7 @@ namespace BrewMonster.Scripts.ChatUI if (_syncingWireToDisplay) { _lastInputFieldText = newText; + UpdateTypingPreviewFromInput(); return; } @@ -884,6 +915,7 @@ namespace BrewMonster.Scripts.ChatUI { _lastInputFieldText = newText; SyncChatWireBodyFromInput(); + UpdateTypingPreviewFromInput(); return; } @@ -905,11 +937,13 @@ namespace BrewMonster.Scripts.ChatUI inputField.ForceLabelUpdate(); _lastInputFieldText = fixedText; SyncChatWireBodyFromInput(); + UpdateTypingPreviewFromInput(); return; } _lastInputFieldText = newText; SyncChatWireBodyFromInput(); + UpdateTypingPreviewFromInput(); } /// @@ -981,6 +1015,61 @@ namespace BrewMonster.Scripts.ChatUI inputField.ActivateInputField(); } + void CacheInitialAnchors() + { + if (_cachedAnchors) return; + if (inputBarRoot != null) + _inputBarBaseAnchoredPos = inputBarRoot.anchoredPosition; + if (typingPreviewRect != null) + _typingPreviewBaseAnchoredPos = typingPreviewRect.anchoredPosition; + _cachedAnchors = true; + } + + void UpdateKeyboardAnchoredLayout() + { + if (!followMobileKeyboard) + return; + + CacheInitialAnchors(); + float keyboardHeight = GetVisibleKeyboardHeight(); + float yOffset = keyboardHeight + keyboardBottomPadding; + + if (inputBarRoot != null) + { + var inputPos = _inputBarBaseAnchoredPos; + inputPos.y += yOffset; + inputBarRoot.anchoredPosition = inputPos; + } + + if (typingPreviewRect != null) + { + var previewPos = _typingPreviewBaseAnchoredPos; + previewPos.y += yOffset + previewVerticalOffset; + typingPreviewRect.anchoredPosition = previewPos; + } + } + + float GetVisibleKeyboardHeight() + { + if (!TouchScreenKeyboard.visible) + return 0f; + + Rect area = TouchScreenKeyboard.area; + return area.height > 0f ? area.height : 0f; + } + + void UpdateTypingPreviewFromInput() + { + if (typingPreviewRoot == null || typingPreviewText == null || inputField == null) + return; + + string body = ExtractMessageBodyFromVisual(inputField.text ?? "")?.Trim() ?? ""; + bool shouldShow = inputField.isFocused && !string.IsNullOrEmpty(body); + typingPreviewRoot.SetActive(shouldShow); + if (shouldShow) + typingPreviewText.text = body; + } + /// /// C++: GetHostPlayer()->GetPack()->GetItemTotalNum(id) — đếm túi chính. /// From 1c83f867e5ce97fd1fa825cc3d5118c0db413489 Mon Sep 17 00:00:00 2001 From: CuongNV <> Date: Wed, 22 Apr 2026 17:12:39 +0700 Subject: [PATCH 02/17] update prefab --- Assets/Prefabs/ChatSystem/prefab_ChatSystemUI.prefab | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/Prefabs/ChatSystem/prefab_ChatSystemUI.prefab b/Assets/Prefabs/ChatSystem/prefab_ChatSystemUI.prefab index baca285293..dbdc989313 100644 --- a/Assets/Prefabs/ChatSystem/prefab_ChatSystemUI.prefab +++ b/Assets/Prefabs/ChatSystem/prefab_ChatSystemUI.prefab @@ -235,7 +235,7 @@ MonoBehaviour: m_EditorClassIdentifier: inputField: {fileID: 3037472824809581303} chatSystem: {fileID: 11400000, guid: 43f54723aa074c74e83e5be28975bee5, type: 2} - inputBarRoot: {fileID: 6545370471477453367} + inputBarRoot: {fileID: 3282421669272709967} keyboardBottomPadding: 0 followMobileKeyboard: 1 typingPreviewRoot: {fileID: 4018402239503345071} From eadc742c8a447ae46c3b026d890d1b436da7f48b Mon Sep 17 00:00:00 2001 From: Tran Hai Nam Date: Fri, 24 Apr 2026 15:04:23 +0700 Subject: [PATCH 03/17] add sfx for normal attack --- Assets/PerfectWorld/Scripts/Move/CECPlayer.cs | 68 +++++++++++++++++-- Assets/PerfectWorld/Scripts/NPC/CECModel.cs | 16 +++-- 2 files changed, 71 insertions(+), 13 deletions(-) diff --git a/Assets/PerfectWorld/Scripts/Move/CECPlayer.cs b/Assets/PerfectWorld/Scripts/Move/CECPlayer.cs index 8184cf7522..70bc99ecc1 100644 --- a/Assets/PerfectWorld/Scripts/Move/CECPlayer.cs +++ b/Assets/PerfectWorld/Scripts/Move/CECPlayer.cs @@ -271,6 +271,52 @@ namespace BrewMonster "胧族变身月仙男", "胧族变身月仙女", }; + public Dictionary> m_aWeaponSFX = new Dictionary>() + { + {0, new List{"item/weaponattack/1hshorta", "item/weaponattack/1hshortb", "item/weaponattack/1hshortc"}}, + {1, new List{"item/weaponattack/1hshorta", "item/weaponattack/1hshortb", "item/weaponattack/1hshortc"}}, + {2, new List{"item/weaponattack/2hlonga", "item/weaponattack/2hlongb", "item/weaponattack/2hlongc", "item/weaponattack/2hlongd"}}, + {3, new List{"item/weaponattack/1hshorta", "item/weaponattack/1hshortb", "item/weaponattack/1hshortc"}}, + {4, new List{"item/weaponattack/2hlonga", "item/weaponattack/2hlongb", "item/weaponattack/2hlongc", "item/weaponattack/2hlongd"}}, + {6, new List{"item/weaponattack/bow", "item/weaponattack/bowb", "item/weaponattack/drawbow"}}, + {7, new List{"item/weaponattack/bow", "item/weaponattack/bowb", "item/weaponattack/drawbow"}}, + {8, new List{"item/weaponattack/fista", "item/weaponattack/fistb", "item/weaponattack/fistc", "item/weaponattack/fistd"}}, + {9, new List{"item/weaponattack/bow", "item/weaponattack/bowb", "item/weaponattack/drawbow"}}, + {10, new List{"item/weaponattack/fista", "item/weaponattack/fistb", "item/weaponattack/fistc", "item/weaponattack/fistd"}}, + // {0, new List{"item/weaponattack/1hshorta", "item/weaponattack/1hshortb", "item/weaponattack/1hshortc"}}, + // {1, new List{"item/weaponattack/2hheavya", "item/weaponattack/2hheavyb", "item/weaponattack/2hheavyc", "item/weaponattack/2hheavyd"}}, + // {2, new List{"item/weaponattack/2hlonga", "item/weaponattack/2hlongb", "item/weaponattack/2hlongc", "item/weaponattack/2hlongd"}}, + // {3, new List{"item/weaponattack/bow", "item/weaponattack/bowb", "item/weaponattack/drawbow"}}, + // {4, new List{"item/weaponattack/fista", "item/weaponattack/fistb", "item/weaponattack/fistc", "item/weaponattack/fistd"}}, + // {5, new List{"item/weaponattack/magic", "item/weaponattack/magicb"}}, + }; + public Dictionary> m_aWeaponHitSFX = new Dictionary>() + { + {0, new List{"item/weaponattack/hitsword", "item/weaponattack/hitswordbig"}}, + {1, new List{"item/weaponattack/hitsword", "item/weaponattack/hitswordbig"}}, + {2, new List{"item/weaponattack/hitmace", "item/weaponattack/hitmacebig"}}, + {3, new List{"item/weaponattack/hithammer", "item/weaponattack/hithammerbig"}}, + {4, new List{"item/weaponattack/hitaxe", "item/weaponattack/hitaxebig"}}, + {6, new List{"item/weaponattack/hitthrow"}}, + {7, new List{"item/weaponattack/hitthrow"}}, + {8, new List{"item/weaponattack/hithand"}}, + {9, new List{"item/weaponattack/hitthrow"}}, + {10, new List{"item/weaponattack/hithand"}}, + // {0, new List{"item/weaponattack/hitaxe", "item/weaponattack/hitaxebig"}}, + // {1, new List{"item/weaponattack/hithammer", "item/weaponattack/hithammerbig"}}, + // {2, new List{"item/weaponattack/hitblade", "item/weaponattack/hitbladebig"}}, + // {3, new List{"item/weaponattack/hitdagger"}}, + // {4, new List{"item/weaponattack/hitfist"}}, + // {5, new List{"item/weaponattack/hithand"}}, + // {6, new List{"item/weaponattack/hitstaff"}}, + // {7, new List{"item/weaponattack/hitmace", "item/weaponattack/hitmacebig"}}, + // {8, new List{"item/weaponattack/hitoriginal"}}, + // {9, new List{"item/weaponattack/hitsword", "item/weaponattack/hitswordbig"}}, + // {10, new List{"item/weaponattack/hittiger"}}, + // {11, new List{"item/weaponattack/hitwhip"}}, + // {12, new List{"item/weaponattack/hitthrow"}}, + // {13, new List{"item/weaponattack/hitbow", "item/weaponattack/hitbowbig"}}, + }; public static class Effect_type { public const int EFF_FACEPILL = 1; @@ -1631,11 +1677,13 @@ namespace BrewMonster int nRand = UnityEngine.Random.Range(0, 4); string szAct = string.Empty; - string szShapeName = string.Empty; - GetShapeName(ref szShapeName); int weapon_type = GetShowingWeaponType(); + Debug.Log("PlayAttackAction: weapon_type=" + weapon_type); int nTime1 = 0, nTime2 = 0; int iAction = (int)PLAYER_ACTION_TYPE.ACT_ATTACK_1 + nRand; + + string soundPath = m_aWeaponSFX[weapon_type][nRand%m_aWeaponSFX[weapon_type].Count]; + string hitSoundPath = m_aWeaponHitSFX[weapon_type][nRand%m_aWeaponHitSFX[weapon_type].Count]; bool bHideFX = false;//!CECOptimize::Instance().GetGFX().CanShowAttack(GetCharacterID(), GetClassID()); PLAYER_ACTION action = m_PlayerActions[iAction]; @@ -1654,12 +1702,19 @@ namespace BrewMonster // “起�? 动作(挥起) szAct = EC_Utility.BuildActionName(action, weapon_type, "起"); int iTransTime = 200; - //EventBus.PublishChannel(m_PlayerInfo.cid, new PlayActionEvent(szShapeName, szAct, iTransTime, true)); m_pActionController.PlayNonSkillActionWithName(iAction, szAct, true, iTransTime, bHideFX, attackEvent,COMACT_FLAG_MODE_ONCE_MULTIIGNOREGFX); + //swing sfx + //workaround for sound effect delay, it need to trigger via weapon combine action + SFXManager.Instance.PlaySkillSfxAtPointAsync(soundPath, Vector3.zero,iTransTime/1000f).Forget(); + szAct = EC_Utility.BuildActionName(action, weapon_type, "落"); - queueActionEvent.SetData(szShapeName, szAct, SetApplyDamage, true, attackEvent, iTransTime,false); - //EventBus.PublishChannelClass(m_PlayerInfo.cid, queueActionEvent); m_pActionController.QueueNonSkillActionWithName(iAction, szAct, 0, false, bHideFX); + + //hit sfx + //workaround for sound effect delay, it need to trigger via weapon combine action + //.1f is a magic number to make sure the sound effect is triggered after the action is finished + SFXManager.Instance.PlaySkillSfxAtPointAsync(hitSoundPath, Vector3.zero,iTransTime/1000f+.1f).Forget(); + //PlayNonSkillActionWithName(iAction, szAct, true, 200, true, ref pActFlag, COMACT_FLAG_MODE_ONCE_MULTIIGNOREGFX);gagága /* if (pRightHandWeapon != null && IsUsingMagicWeapon()) @@ -1705,7 +1760,6 @@ namespace BrewMonster // nTime1 = m_pPlayerModel.GetComActTimeSpanByName(szAct); szAct = EC_Utility.BuildActionName(action, weapon_type, "落", szActionMiddleName); - queueActionEvent.SetData(szShapeName, szAct, SetApplyDamage, false, attackEvent, 0, false); //EventBus.PublishChannelClass(m_PlayerInfo.cid, queueActionEvent); m_pActionController.QueueNonSkillActionWithName(iAction, szAct, 0, false, false, true, false); @@ -3911,7 +3965,7 @@ namespace BrewMonster public CECAttackEvent AttackEvent; public bool IsHitAnim; public bool IsForceStopPrevious; - public bool IsLoop; + public bool IsLoop; public QueueActionEvent(string animationName, Action setFlag, bool isHitAnim, CECAttackEvent attackEvent, int iTransTime, bool isForceStopPrevious = false, bool isLoop = false) { diff --git a/Assets/PerfectWorld/Scripts/NPC/CECModel.cs b/Assets/PerfectWorld/Scripts/NPC/CECModel.cs index 450200a90c..93f07f27de 100644 --- a/Assets/PerfectWorld/Scripts/NPC/CECModel.cs +++ b/Assets/PerfectWorld/Scripts/NPC/CECModel.cs @@ -681,16 +681,20 @@ public class CECModel { foreach(var eventInfo in eventInfoList) { - //0 is sound event - if (eventInfo is FX_BASE_INFO sfx) + if(eventInfo.m_nType == 0) { - if(sfx.m_strFilePaths != null && sfx.m_strFilePaths.Count > 0) + //0 is sound event + if (eventInfo is FX_BASE_INFO sfx) { - string soundpath = AFile.NormalizePath(sfx.m_strFilePaths[0]); - soundpath = soundpath.ToLower(); - SFXManager.Instance.PlaySkillSfxAtPointAsync(soundpath, Vector3.zero).Forget(); + if(sfx.m_strFilePaths != null && sfx.m_strFilePaths.Count > 0) + { + string soundpath = AFile.NormalizePath(sfx.m_strFilePaths[0]); + soundpath = soundpath.ToLower(); + SFXManager.Instance.PlaySkillSfxAtPointAsync(soundpath, Vector3.zero).Forget(); + } } } + } } return true; From 43d2f7291aef24691691f6c9b5d4d6255c36b14a Mon Sep 17 00:00:00 2001 From: Tran Hai Nam Date: Fri, 24 Apr 2026 15:24:15 +0700 Subject: [PATCH 04/17] fix some sfx wrong path --- Assets/PerfectWorld/Scripts/NPC/CECModel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/PerfectWorld/Scripts/NPC/CECModel.cs b/Assets/PerfectWorld/Scripts/NPC/CECModel.cs index 93f07f27de..fe1d405ddd 100644 --- a/Assets/PerfectWorld/Scripts/NPC/CECModel.cs +++ b/Assets/PerfectWorld/Scripts/NPC/CECModel.cs @@ -688,7 +688,7 @@ public class CECModel { if(sfx.m_strFilePaths != null && sfx.m_strFilePaths.Count > 0) { - string soundpath = AFile.NormalizePath(sfx.m_strFilePaths[0]); + string soundpath = AFile.NormalizePath(sfx.m_strFilePaths[0],true); soundpath = soundpath.ToLower(); SFXManager.Instance.PlaySkillSfxAtPointAsync(soundpath, Vector3.zero).Forget(); } From b40c9e246f6bca26c195ba6b174229c4508f057c Mon Sep 17 00:00:00 2001 From: CuongNV <> Date: Fri, 24 Apr 2026 15:25:09 +0700 Subject: [PATCH 05/17] add chat typing preview --- .../ChatSystem/prefab_ChatSystemUI.prefab | 9 ++- Assets/Scripts/ChatInputHandler.cs | 77 ++++--------------- Assets/Scripts/TypingPreviewController.cs | 59 ++++++++++++++ .../Scripts/TypingPreviewController.cs.meta | 2 + 4 files changed, 81 insertions(+), 66 deletions(-) create mode 100644 Assets/Scripts/TypingPreviewController.cs create mode 100644 Assets/Scripts/TypingPreviewController.cs.meta diff --git a/Assets/Prefabs/ChatSystem/prefab_ChatSystemUI.prefab b/Assets/Prefabs/ChatSystem/prefab_ChatSystemUI.prefab index dbdc989313..7c6f597b49 100644 --- a/Assets/Prefabs/ChatSystem/prefab_ChatSystemUI.prefab +++ b/Assets/Prefabs/ChatSystem/prefab_ChatSystemUI.prefab @@ -238,10 +238,11 @@ MonoBehaviour: inputBarRoot: {fileID: 3282421669272709967} keyboardBottomPadding: 0 followMobileKeyboard: 1 - typingPreviewRoot: {fileID: 4018402239503345071} - typingPreviewText: {fileID: 5673195965320428126} - typingPreviewRect: {fileID: 3282421669272709967} - previewVerticalOffset: 8 + typingPreview: + typingPreviewRoot: {fileID: 4018402239503345071} + typingPreviewText: {fileID: 5673195965320428126} + typingPreviewRect: {fileID: 3282421669272709967} + previewVerticalOffset: 8 _spriteMap: {fileID: 11400000, guid: f634ecf63ca3d004f82af9b17c966fc9, type: 2} channelButtons: - channel: 0 diff --git a/Assets/Scripts/ChatInputHandler.cs b/Assets/Scripts/ChatInputHandler.cs index 7ed8df6ca6..01480922db 100644 --- a/Assets/Scripts/ChatInputHandler.cs +++ b/Assets/Scripts/ChatInputHandler.cs @@ -3,14 +3,11 @@ using UnityEngine; using TMPro; using CSNetwork.GPDataType; using System.Collections.Generic; -using BrewMonster; using BrewMonster.Network; using BrewMonster.Scripts.Chat; using BrewMonster.Scripts.Chat.EmotionData; -using BrewMonster.Scripts.Managers; using BrewMonster.UI; using CSNetwork; -using PerfectWorld.Scripts.Managers; namespace BrewMonster.Scripts.ChatUI { @@ -31,23 +28,8 @@ namespace BrewMonster.Scripts.ChatUI public TMP_InputField inputField; public ChatSystemSO chatSystem; - [Header("Mobile Keyboard Follow")] - [Tooltip("Rect chứa input/chat composer cần neo lên trên keyboard.")] - [SerializeField] private RectTransform inputBarRoot; - [Tooltip("Padding đáy thêm cho vùng safe-area/gesture.")] - [SerializeField] private float keyboardBottomPadding = 0f; - [Tooltip("Bật để input bar bám theo keyboard khi mobile keyboard mở.")] - [SerializeField] private bool followMobileKeyboard = true; - [Header("Typing Preview")] - [Tooltip("Root của box xem trước nội dung đang gõ (nằm trên input bar).")] - [SerializeField] private GameObject typingPreviewRoot; - [Tooltip("Text hiển thị nội dung đang gõ realtime.")] - [SerializeField] private TMP_Text typingPreviewText; - [Tooltip("Rect của preview box để neo theo input bar.")] - [SerializeField] private RectTransform typingPreviewRect; - [Tooltip("Khoảng cách dọc cộng thêm cho preview box.")] - [SerializeField] private float previewVerticalOffset = 8f; + [SerializeField] private TypingPreviewController typingPreview = new(); [Header("Emoji")] [Tooltip("SO ánh xạ emotion → TMP sprite tag. Gán EmotionLibrarySpriteMap đã build từ Emotion Atlas Converter.")] @@ -129,7 +111,6 @@ namespace BrewMonster.Scripts.ChatUI private float m_dwTickFarCry = 0; private float m_dwTickFarCry2 = 0; private Vector2 _inputBarBaseAnchoredPos; - private Vector2 _typingPreviewBaseAnchoredPos; private bool _cachedAnchors; private void Awake() @@ -193,13 +174,14 @@ namespace BrewMonster.Scripts.ChatUI } UpdateChatDropdownInteractable(); - CacheInitialAnchors(); UpdateTypingPreviewFromInput(); } private void Update() { - UpdateKeyboardAnchoredLayout(); + // Keyboard open/close does not always trigger input value changed, + // so refresh preview gate every frame. + UpdateTypingPreviewFromInput(); } // ===================================================== @@ -1015,40 +997,6 @@ namespace BrewMonster.Scripts.ChatUI inputField.ActivateInputField(); } - void CacheInitialAnchors() - { - if (_cachedAnchors) return; - if (inputBarRoot != null) - _inputBarBaseAnchoredPos = inputBarRoot.anchoredPosition; - if (typingPreviewRect != null) - _typingPreviewBaseAnchoredPos = typingPreviewRect.anchoredPosition; - _cachedAnchors = true; - } - - void UpdateKeyboardAnchoredLayout() - { - if (!followMobileKeyboard) - return; - - CacheInitialAnchors(); - float keyboardHeight = GetVisibleKeyboardHeight(); - float yOffset = keyboardHeight + keyboardBottomPadding; - - if (inputBarRoot != null) - { - var inputPos = _inputBarBaseAnchoredPos; - inputPos.y += yOffset; - inputBarRoot.anchoredPosition = inputPos; - } - - if (typingPreviewRect != null) - { - var previewPos = _typingPreviewBaseAnchoredPos; - previewPos.y += yOffset + previewVerticalOffset; - typingPreviewRect.anchoredPosition = previewPos; - } - } - float GetVisibleKeyboardHeight() { if (!TouchScreenKeyboard.visible) @@ -1060,14 +1008,19 @@ namespace BrewMonster.Scripts.ChatUI void UpdateTypingPreviewFromInput() { - if (typingPreviewRoot == null || typingPreviewText == null || inputField == null) + if (inputField == null) return; - string body = ExtractMessageBodyFromVisual(inputField.text ?? "")?.Trim() ?? ""; - bool shouldShow = inputField.isFocused && !string.IsNullOrEmpty(body); - typingPreviewRoot.SetActive(shouldShow); - if (shouldShow) - typingPreviewText.text = body; + string body = ExtractMessageBodyFromVisual(inputField.text ?? ""); + typingPreview?.UpdatePreview(inputField.isFocused, body, CanShowTypingPreviewNow()); + } + + bool CanShowTypingPreviewNow() + { + if (!Application.isMobilePlatform) + return false; + + return TouchScreenKeyboard.visible; } /// diff --git a/Assets/Scripts/TypingPreviewController.cs b/Assets/Scripts/TypingPreviewController.cs new file mode 100644 index 0000000000..e623afadd7 --- /dev/null +++ b/Assets/Scripts/TypingPreviewController.cs @@ -0,0 +1,59 @@ +using TMPro; +using UnityEngine; + +namespace BrewMonster.Scripts.ChatUI +{ + /// + /// Controls typing preview visibility/content and keyboard-anchored position. + /// Kept standalone so other chat/input UIs can reuse the same behavior. + /// + [System.Serializable] + public class TypingPreviewController + { + [Tooltip("Root của box xem trước nội dung đang gõ.")] + [SerializeField] private GameObject typingPreviewRoot; + [Tooltip("Text hiển thị nội dung đang gõ realtime.")] + [SerializeField] private TextMeshProUGUI typingPreviewText; + [Tooltip("Rect của preview box để neo theo keyboard.")] + [SerializeField] private RectTransform typingPreviewRect; + [Tooltip("Khoảng cách dọc cộng thêm cho preview box.")] + [SerializeField] private float previewVerticalOffset = 8f; + + private Vector2 _baseAnchoredPos; + private bool _cachedAnchor; + + public void CacheInitialAnchor() + { + if (_cachedAnchor) + return; + + if (typingPreviewRect != null) + _baseAnchoredPos = typingPreviewRect.anchoredPosition; + + _cachedAnchor = true; + } + + public void ApplyKeyboardOffset(float yOffset) + { + if (typingPreviewRect == null) + return; + + CacheInitialAnchor(); + var previewPos = _baseAnchoredPos; + previewPos.y += yOffset + previewVerticalOffset; + typingPreviewRect.anchoredPosition = previewPos; + } + + public void UpdatePreview(bool isInputFocused, string body, bool canShowPreview) + { + if (typingPreviewRoot == null || typingPreviewText == null) + return; + + string trimmedBody = (body ?? string.Empty).Trim(); + bool shouldShow = canShowPreview && isInputFocused && !string.IsNullOrEmpty(trimmedBody); + typingPreviewRoot.SetActive(shouldShow); + if (shouldShow) + typingPreviewText.text = trimmedBody; + } + } +} diff --git a/Assets/Scripts/TypingPreviewController.cs.meta b/Assets/Scripts/TypingPreviewController.cs.meta new file mode 100644 index 0000000000..d2f7801a37 --- /dev/null +++ b/Assets/Scripts/TypingPreviewController.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: a9934ac12c563f746a3ba562e3de5bff \ No newline at end of file From dfb7874b88b6d21fd973ac65171f98213eb00771 Mon Sep 17 00:00:00 2001 From: Le Duc Anh Date: Fri, 24 Apr 2026 15:33:39 +0700 Subject: [PATCH 06/17] travel on world map --- Assets/PerfectWorld/Scripts/UI/WorldMap.meta | 8 + .../Scripts/UI/{ => WorldMap}/DlgWorldMap.cs | 33 +++- .../UI/{ => WorldMap}/DlgWorldMap.cs.meta | 0 .../UI/WorldMap/WorldMapClickHandler.cs | 59 ++++++ .../UI/WorldMap/WorldMapClickHandler.cs.meta | 2 + Assets/Prefabs/UI/DlgWorldMap.prefab | 175 +++++++++++++++++- 6 files changed, 268 insertions(+), 9 deletions(-) create mode 100644 Assets/PerfectWorld/Scripts/UI/WorldMap.meta rename Assets/PerfectWorld/Scripts/UI/{ => WorldMap}/DlgWorldMap.cs (74%) rename Assets/PerfectWorld/Scripts/UI/{ => WorldMap}/DlgWorldMap.cs.meta (100%) create mode 100644 Assets/PerfectWorld/Scripts/UI/WorldMap/WorldMapClickHandler.cs create mode 100644 Assets/PerfectWorld/Scripts/UI/WorldMap/WorldMapClickHandler.cs.meta diff --git a/Assets/PerfectWorld/Scripts/UI/WorldMap.meta b/Assets/PerfectWorld/Scripts/UI/WorldMap.meta new file mode 100644 index 0000000000..d30ed9d9f7 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/UI/WorldMap.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: fce16fcbe8e82ec4588c7fba89c7cb64 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/PerfectWorld/Scripts/UI/DlgWorldMap.cs b/Assets/PerfectWorld/Scripts/UI/WorldMap/DlgWorldMap.cs similarity index 74% rename from Assets/PerfectWorld/Scripts/UI/DlgWorldMap.cs rename to Assets/PerfectWorld/Scripts/UI/WorldMap/DlgWorldMap.cs index a20a1728ca..425a0028da 100644 --- a/Assets/PerfectWorld/Scripts/UI/DlgWorldMap.cs +++ b/Assets/PerfectWorld/Scripts/UI/WorldMap/DlgWorldMap.cs @@ -1,3 +1,4 @@ +using BrewMonster.Network; using CSNetwork.Common; using UnityEngine; using UnityEngine.UI; @@ -54,10 +55,14 @@ namespace BrewMonster.UI public override bool Render() { - // UpdateHostPlayerPositionImage(); + UpdateHostPlayerPositionImage(); return base.Render(); } + + Vector3 _hostPlayerPosition; + Quaternion _hostPlayerRotation; + private void UpdateHostPlayerPositionImage() { if (_hostPlayerPositionImage == null) @@ -65,7 +70,7 @@ namespace BrewMonster.UI return; } - if (!TryGetHostPlayerPosition(out Vector3 hostPosition)) + if (!TryGetHostPlayerPosition(out _hostPlayerPosition, out _hostPlayerRotation)) { _hostPlayerPositionImage.enabled = false; return; @@ -81,13 +86,16 @@ namespace BrewMonster.UI _hostPlayerPositionImage.enabled = true; RectTransform hostPlayerRectTransform = _hostPlayerPositionImage.rectTransform; hostPlayerRectTransform.anchoredPosition = new Vector2( - hostPosition.x / _positionFactor, - hostPosition.z / _positionFactor); + _hostPlayerPosition.x / _positionFactor, + _hostPlayerPosition.z / _positionFactor); + + hostPlayerRectTransform.localRotation = Quaternion.Euler(0, 0, -_hostPlayerRotation.eulerAngles.y); } - private bool TryGetHostPlayerPosition(out Vector3 hostPlayerPosition) + private bool TryGetHostPlayerPosition(out Vector3 hostPlayerPosition, out Quaternion hostPlayerRotation) { hostPlayerPosition = Vector3.zero; + hostPlayerRotation = Quaternion.identity; CECHostPlayer hostPlayer = GetHostPlayer(); if (hostPlayer == null || hostPlayer.transform == null) @@ -96,6 +104,7 @@ namespace BrewMonster.UI } hostPlayerPosition = hostPlayer.transform.position; + hostPlayerRotation = hostPlayer.transform.rotation; return true; } @@ -139,6 +148,20 @@ namespace BrewMonster.UI } } + /// + /// When user click on the map texture. + /// We will calculate the world coordinates from the local cursor position. Then move the host player to the world coordinates. + /// + /// + public void OnMapClicked(Vector2 localCursorPosition) + { + var worldCoordinates = localCursorPosition * _positionFactor; + UnityGameSession.c2s_CmdGoto(worldCoordinates.x, 1.0f, worldCoordinates.y); + + // close the map + OnCloseButtonClicked(); + } + private void OnCloseButtonClicked() { CloseDialogue(); diff --git a/Assets/PerfectWorld/Scripts/UI/DlgWorldMap.cs.meta b/Assets/PerfectWorld/Scripts/UI/WorldMap/DlgWorldMap.cs.meta similarity index 100% rename from Assets/PerfectWorld/Scripts/UI/DlgWorldMap.cs.meta rename to Assets/PerfectWorld/Scripts/UI/WorldMap/DlgWorldMap.cs.meta diff --git a/Assets/PerfectWorld/Scripts/UI/WorldMap/WorldMapClickHandler.cs b/Assets/PerfectWorld/Scripts/UI/WorldMap/WorldMapClickHandler.cs new file mode 100644 index 0000000000..e051a5b058 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/UI/WorldMap/WorldMapClickHandler.cs @@ -0,0 +1,59 @@ +using UnityEngine; +using UnityEngine.EventSystems; + +namespace BrewMonster.UI +{ + public class WorldMapClickHandler : MonoBehaviour, IPointerClickHandler + { + [Tooltip("The RectTransform of the Map Image (Usually this GameObject)")] + private RectTransform mapRectTransform; + + [SerializeField] private RectTransform _hostPlayerPositionImage; + + public DlgWorldMap dlgWorldMap; + + // The host player player (0,0,0) is not at the center of the map. It usually has an offset that we have to calculate at Awake + private Vector2 _hostPlayerOffsetPosition; + + private void Awake() + { + // Get the RectTransform of the map + mapRectTransform = GetComponent(); + + CalculateHostPlayerOffsetPosition(); + } + + private void CalculateHostPlayerOffsetPosition() + { + _hostPlayerOffsetPosition = _hostPlayerPositionImage.anchoredPosition; + + // if the max/min of the anchor is 0.5 0.5, then the host player is at the center of the map + // however we have to calculate the offset of the player because it is not at the center of the map + // we can calculate the offset by the max/min of the anchor + var maxAnchor = _hostPlayerPositionImage.anchorMax; + var minAnchor = _hostPlayerPositionImage.anchorMin; + _hostPlayerOffsetPosition = new Vector2((maxAnchor.x - 0.5f) * mapRectTransform.rect.width, (maxAnchor.y - 0.5f) * mapRectTransform.rect.height); + } + + // This triggers automatically when the user clicks/taps on this Image + public void OnPointerClick(PointerEventData eventData) + { + Vector2 localCursorPosition; + + // Convert the screen click position to local anchored position inside the Map + bool isConverted = RectTransformUtility.ScreenPointToLocalPointInRectangle( + mapRectTransform, + eventData.position, + eventData.pressEventCamera, + out localCursorPosition + ); + + if (isConverted) + { + // convert the localCursorPosition to the local position of the host player position image + localCursorPosition -= _hostPlayerOffsetPosition; + dlgWorldMap.OnMapClicked(localCursorPosition); + } + } + } +} diff --git a/Assets/PerfectWorld/Scripts/UI/WorldMap/WorldMapClickHandler.cs.meta b/Assets/PerfectWorld/Scripts/UI/WorldMap/WorldMapClickHandler.cs.meta new file mode 100644 index 0000000000..9199d3ecf5 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/UI/WorldMap/WorldMapClickHandler.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 876ab9894018f2b4cb4a0b04bf534bc5 \ No newline at end of file diff --git a/Assets/Prefabs/UI/DlgWorldMap.prefab b/Assets/Prefabs/UI/DlgWorldMap.prefab index 245de697af..3eafab46a6 100644 --- a/Assets/Prefabs/UI/DlgWorldMap.prefab +++ b/Assets/Prefabs/UI/DlgWorldMap.prefab @@ -1,5 +1,80 @@ %YAML 1.1 %TAG !u! tag:unity3d.com,2011: +--- !u!1 &191621185379502263 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 9142400375056319150} + - component: {fileID: 7949922842883969624} + - component: {fileID: 6790297770241223980} + m_Layer: 0 + m_Name: click_pos + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!224 &9142400375056319150 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 191621185379502263} + 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: 7169122999130120872} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.5} + m_AnchorMax: {x: 0.5, y: 0.5} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 15, y: 15} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &7949922842883969624 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 191621185379502263} + m_CullTransparentMesh: 1 +--- !u!114 &6790297770241223980 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 191621185379502263} + 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: 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_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!1 &936441858863998774 GameObject: m_ObjectHideFlags: 0 @@ -35,7 +110,7 @@ RectTransform: m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 403.03613, y: 341.03613} + m_AnchoredPosition: {x: 549, y: 465} m_SizeDelta: {x: 58, y: 58} m_Pivot: {x: 0.5, y: 0.5} --- !u!222 &6772256914059310038 @@ -286,7 +361,7 @@ MonoBehaviour: imageProgress: {fileID: 0} mapImage: {fileID: 1174346096914174862} _hostPlayerPositionImage: {fileID: 4036230907032538800} - _positionFactor: 2.5 + _positionFactor: 1.8 _closeButton: {fileID: 8858186809287203567} --- !u!1 &8308536083041954008 GameObject: @@ -363,6 +438,81 @@ MonoBehaviour: m_FillOrigin: 0 m_UseSpriteMesh: 0 m_PixelsPerUnitMultiplier: 1 +--- !u!1 &8900623989843312765 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 1510574663178069641} + - component: {fileID: 5533213955690836106} + - component: {fileID: 7414570214724661608} + m_Layer: 0 + m_Name: hostplayerpos_debug + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 0 +--- !u!224 &1510574663178069641 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8900623989843312765} + 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: 7169122999130120872} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} + m_AnchorMin: {x: 0.5, y: 0.518} + m_AnchorMax: {x: 0.5, y: 0.518} + m_AnchoredPosition: {x: 0, y: 0} + m_SizeDelta: {x: 13, y: 16} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!222 &5533213955690836106 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8900623989843312765} + m_CullTransparentMesh: 1 +--- !u!114 &7414570214724661608 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8900623989843312765} + 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: 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_Sprite: {fileID: 448046091, guid: 99520ceed6182dd408f2da040fe0c033, type: 3} + 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!1 &8924313797425690832 GameObject: m_ObjectHideFlags: 0 @@ -374,6 +524,7 @@ GameObject: - component: {fileID: 7169122999130120872} - component: {fileID: 7283747291599937432} - component: {fileID: 1174346096914174862} + - component: {fileID: 4521997594693838426} m_Layer: 0 m_Name: MapTexture m_TagString: Untagged @@ -394,12 +545,14 @@ RectTransform: m_ConstrainProportionsScale: 0 m_Children: - {fileID: 5906545349664091413} + - {fileID: 1510574663178069641} + - {fileID: 9142400375056319150} m_Father: {fileID: 7323734624486819451} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0.5, y: 0.5} m_AnchorMax: {x: 0.5, y: 0.5} - m_AnchoredPosition: {x: 0, y: 0} - m_SizeDelta: {x: 1024, y: 1024} + m_AnchoredPosition: {x: -6.0056, y: -6.0056} + m_SizeDelta: {x: 1408.0024, y: 1408.0024} m_Pivot: {x: 0.5, y: 0.5} --- !u!222 &7283747291599937432 CanvasRenderer: @@ -439,3 +592,17 @@ MonoBehaviour: m_FillOrigin: 0 m_UseSpriteMesh: 0 m_PixelsPerUnitMultiplier: 1 +--- !u!114 &4521997594693838426 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 8924313797425690832} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 876ab9894018f2b4cb4a0b04bf534bc5, type: 3} + m_Name: + m_EditorClassIdentifier: + _hostPlayerPositionImage: {fileID: 5906545349664091413} + dlgWorldMap: {fileID: 135853640611757204} From 58b0d9c8eb4dd8babfea93d63f460e2c0a90359a Mon Sep 17 00:00:00 2001 From: vuong dinh hoang Date: Fri, 24 Apr 2026 15:38:00 +0700 Subject: [PATCH 07/17] fix lost music background --- Assets/PerfectWorld/Scene/LoginScene.unity | 4 +- .../Scripts/UI/Login/LoginScreenUI.cs | 3 +- Assets/Prefabs/UI/Music.prefab | 314 ++++++++++++++++++ Assets/Prefabs/UI/Music.prefab.meta | 7 + 4 files changed, 325 insertions(+), 3 deletions(-) create mode 100644 Assets/Prefabs/UI/Music.prefab create mode 100644 Assets/Prefabs/UI/Music.prefab.meta diff --git a/Assets/PerfectWorld/Scene/LoginScene.unity b/Assets/PerfectWorld/Scene/LoginScene.unity index 4de3721598..ce0e50f81b 100644 --- a/Assets/PerfectWorld/Scene/LoginScene.unity +++ b/Assets/PerfectWorld/Scene/LoginScene.unity @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bc028fc3c6b7fe9d9851f84da3d784923ccfb38af77605dd57c1e7e12e4c5817 -size 113080 +oid sha256:ce4ce7c6a9576c56a5fb0b213f19e1253afc78d679474cb203612a1e50e8093d +size 111451 diff --git a/Assets/PerfectWorld/Scripts/UI/Login/LoginScreenUI.cs b/Assets/PerfectWorld/Scripts/UI/Login/LoginScreenUI.cs index e19fe6fe1a..5e2cbc3791 100644 --- a/Assets/PerfectWorld/Scripts/UI/Login/LoginScreenUI.cs +++ b/Assets/PerfectWorld/Scripts/UI/Login/LoginScreenUI.cs @@ -343,8 +343,9 @@ namespace BrewMonster.UI isDoneNPCRender = true; isDoneWorldRender = true; actLoadChar?.Invoke(); + AudioManager.Instance.StopBGM(1f); + WorldMusicController.Instance.InitForWorld(roleInfo.worldtag); UnityGameSession.EnterWorldAsync(roleInfo, OnEnterWorldComplete); - }); #endif }, null); diff --git a/Assets/Prefabs/UI/Music.prefab b/Assets/Prefabs/UI/Music.prefab new file mode 100644 index 0000000000..e9259a9b32 --- /dev/null +++ b/Assets/Prefabs/UI/Music.prefab @@ -0,0 +1,314 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1 &2838129733766203984 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3636302681040170949} + - component: {fileID: 4656951194032224} + m_Layer: 0 + m_Name: SFX + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &3636302681040170949 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2838129733766203984} + 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: 4292995824318243454} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &4656951194032224 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2838129733766203984} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 4cfa292fff0815d40b82f32256b3f2cc, type: 3} + m_Name: + m_EditorClassIdentifier: + _moveSoundSource: {fileID: 8780839799255791761} +--- !u!1 &6634120867767479402 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4292995824318243454} + - component: {fileID: 3455241641456728159} + - component: {fileID: 8780839799255791761} + - component: {fileID: 8959945188973705721} + - component: {fileID: 6104956088685348673} + - component: {fileID: 8206320370371461788} + m_Layer: 0 + m_Name: Music + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &4292995824318243454 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6634120867767479402} + 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: + - {fileID: 3636302681040170949} + m_Father: {fileID: 0} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!114 &3455241641456728159 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6634120867767479402} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: ab7ebb437fbd3fb41a7300a381fcab00, type: 3} + m_Name: + m_EditorClassIdentifier: + bgmSource: {fileID: 8780839799255791761} + _bgmMixerGroup: {fileID: 1439606312574676259, guid: 9c6a7598ca0dfcd4fa51470ebbdd7549, type: 2} + _ambienceMixerGroup: {fileID: 661067059137401939, guid: 9c6a7598ca0dfcd4fa51470ebbdd7549, type: 2} +--- !u!82 &8780839799255791761 +AudioSource: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6634120867767479402} + m_Enabled: 1 + serializedVersion: 4 + OutputAudioMixerGroup: {fileID: 0} + m_audioClip: {fileID: 0} + m_Resource: {fileID: 0} + m_PlayOnAwake: 1 + m_Volume: 1 + m_Pitch: 1 + Loop: 1 + Mute: 0 + Spatialize: 0 + SpatializePostEffects: 0 + Priority: 128 + DopplerLevel: 1 + MinDistance: 1 + MaxDistance: 500 + Pan2D: 0 + rolloffMode: 0 + BypassEffects: 0 + BypassListenerEffects: 0 + BypassReverbZones: 0 + rolloffCustomCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 0 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + panLevelCustomCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + spreadCustomCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + reverbZoneMixCustomCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 +--- !u!82 &8959945188973705721 +AudioSource: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6634120867767479402} + m_Enabled: 1 + serializedVersion: 4 + OutputAudioMixerGroup: {fileID: 0} + m_audioClip: {fileID: 0} + m_Resource: {fileID: 0} + m_PlayOnAwake: 1 + m_Volume: 1 + m_Pitch: 1 + Loop: 0 + Mute: 0 + Spatialize: 0 + SpatializePostEffects: 0 + Priority: 128 + DopplerLevel: 1 + MinDistance: 1 + MaxDistance: 500 + Pan2D: 0 + rolloffMode: 0 + BypassEffects: 0 + BypassListenerEffects: 0 + BypassReverbZones: 0 + rolloffCustomCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 0 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + panLevelCustomCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + spreadCustomCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + reverbZoneMixCustomCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 +--- !u!81 &6104956088685348673 +AudioListener: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6634120867767479402} + m_Enabled: 1 +--- !u!114 &8206320370371461788 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6634120867767479402} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: f520c80aab01bbc45aa22010a90dfd66, type: 3} + m_Name: + m_EditorClassIdentifier: + _worldMusicDB: {fileID: 11400000, guid: 7602c1f71697aae42a7751212c5144dc, type: 2} diff --git a/Assets/Prefabs/UI/Music.prefab.meta b/Assets/Prefabs/UI/Music.prefab.meta new file mode 100644 index 0000000000..ef403827e9 --- /dev/null +++ b/Assets/Prefabs/UI/Music.prefab.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 2c6625b7f2f4bc2469813fb85db84114 +PrefabImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: From 09b2a2b6153634e622c53bbddf930140895d19a1 Mon Sep 17 00:00:00 2001 From: CuongNV <> Date: Fri, 24 Apr 2026 16:27:33 +0700 Subject: [PATCH 08/17] add set focus skill --- Assets/PerfectWorld/Scripts/UI/SkillUI/CDlgSkillSubPool.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Assets/PerfectWorld/Scripts/UI/SkillUI/CDlgSkillSubPool.cs b/Assets/PerfectWorld/Scripts/UI/SkillUI/CDlgSkillSubPool.cs index 6dd920d158..dbbe068e7f 100644 --- a/Assets/PerfectWorld/Scripts/UI/SkillUI/CDlgSkillSubPool.cs +++ b/Assets/PerfectWorld/Scripts/UI/SkillUI/CDlgSkillSubPool.cs @@ -28,6 +28,7 @@ namespace BrewMonster public override void OnEnable() { + _currentSelectSkill = null; UpdateView(); _skillSetUpComboWidget.ShowSetUpContent(false); _skillSetUpComboWidget.OnClickedSkillSlot += OnClickedSkillSlot; @@ -45,6 +46,7 @@ namespace BrewMonster public override void OnDisable() { + _currentSelectComboSlot = null; if (_currentSelectSkill is LearnedSkillUI learnedOnClose) { learnedOnClose.SetFocusFrame(false); From d3a79836370661fffe789186a178e86aa42da0f9 Mon Sep 17 00:00:00 2001 From: Tran Hai Nam Date: Fri, 24 Apr 2026 17:11:05 +0700 Subject: [PATCH 09/17] fix move sfx --- .../Scripts/Sound/AudioManager.cs | 3 +- Assets/Prefabs/UI/Music.prefab | 135 +++++++++++++++++- 2 files changed, 135 insertions(+), 3 deletions(-) diff --git a/Assets/PerfectWorld/Scripts/Sound/AudioManager.cs b/Assets/PerfectWorld/Scripts/Sound/AudioManager.cs index 13887d890a..a59c019a0b 100644 --- a/Assets/PerfectWorld/Scripts/Sound/AudioManager.cs +++ b/Assets/PerfectWorld/Scripts/Sound/AudioManager.cs @@ -11,7 +11,8 @@ public class AudioManager : MonoBehaviour [SerializeField] private AudioMixerGroup _bgmMixerGroup; [SerializeField] private AudioMixerGroup _ambienceMixerGroup; - + [SerializeField] private AudioMixerGroup _sfxMixerGroup; + public AudioMixerGroup GetSfxMixerGroup => _sfxMixerGroup; private AudioSource _ambienceSource; void Awake() diff --git a/Assets/Prefabs/UI/Music.prefab b/Assets/Prefabs/UI/Music.prefab index e9259a9b32..0ef6d16bfd 100644 --- a/Assets/Prefabs/UI/Music.prefab +++ b/Assets/Prefabs/UI/Music.prefab @@ -29,7 +29,8 @@ Transform: m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 - m_Children: [] + m_Children: + - {fileID: 2281112146744556387} m_Father: {fileID: 4292995824318243454} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} --- !u!114 &4656951194032224 @@ -44,7 +45,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: 4cfa292fff0815d40b82f32256b3f2cc, type: 3} m_Name: m_EditorClassIdentifier: - _moveSoundSource: {fileID: 8780839799255791761} + _moveSoundSource: {fileID: 2796487417538969809} --- !u!1 &6634120867767479402 GameObject: m_ObjectHideFlags: 0 @@ -97,6 +98,7 @@ MonoBehaviour: bgmSource: {fileID: 8780839799255791761} _bgmMixerGroup: {fileID: 1439606312574676259, guid: 9c6a7598ca0dfcd4fa51470ebbdd7549, type: 2} _ambienceMixerGroup: {fileID: 661067059137401939, guid: 9c6a7598ca0dfcd4fa51470ebbdd7549, type: 2} + _sfxMixerGroup: {fileID: 217038053835239290, guid: 9c6a7598ca0dfcd4fa51470ebbdd7549, type: 2} --- !u!82 &8780839799255791761 AudioSource: m_ObjectHideFlags: 0 @@ -312,3 +314,132 @@ MonoBehaviour: m_Name: m_EditorClassIdentifier: _worldMusicDB: {fileID: 11400000, guid: 7602c1f71697aae42a7751212c5144dc, type: 2} +--- !u!1 &9129044928287689905 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 2281112146744556387} + - component: {fileID: 2796487417538969809} + m_Layer: 0 + m_Name: MoveSoundSource + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!4 &2281112146744556387 +Transform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9129044928287689905} + 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: 3636302681040170949} + m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} +--- !u!82 &2796487417538969809 +AudioSource: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 9129044928287689905} + m_Enabled: 1 + serializedVersion: 4 + OutputAudioMixerGroup: {fileID: 217038053835239290, guid: 9c6a7598ca0dfcd4fa51470ebbdd7549, type: 2} + m_audioClip: {fileID: 0} + m_Resource: {fileID: 0} + m_PlayOnAwake: 1 + m_Volume: 1 + m_Pitch: 1 + Loop: 0 + Mute: 0 + Spatialize: 0 + SpatializePostEffects: 0 + Priority: 128 + DopplerLevel: 1 + MinDistance: 1 + MaxDistance: 500 + Pan2D: 0 + rolloffMode: 0 + BypassEffects: 0 + BypassListenerEffects: 0 + BypassReverbZones: 0 + rolloffCustomCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + - serializedVersion: 3 + time: 1 + value: 0 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + panLevelCustomCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + spreadCustomCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 0 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 + reverbZoneMixCustomCurve: + serializedVersion: 2 + m_Curve: + - serializedVersion: 3 + time: 0 + value: 1 + inSlope: 0 + outSlope: 0 + tangentMode: 0 + weightedMode: 0 + inWeight: 0.33333334 + outWeight: 0.33333334 + m_PreInfinity: 2 + m_PostInfinity: 2 + m_RotationOrder: 4 From d43f50e8266bdcfee98035228ab7f3ef4f2070c4 Mon Sep 17 00:00:00 2001 From: Tran Hai Nam Date: Fri, 24 Apr 2026 17:49:20 +0700 Subject: [PATCH 10/17] Fix move sfx not stop --- Assets/PerfectWorld/Scripts/Sound/SFXManager.cs | 5 +++++ Assets/Scripts/CECHostPlayer.cs | 11 +++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Assets/PerfectWorld/Scripts/Sound/SFXManager.cs b/Assets/PerfectWorld/Scripts/Sound/SFXManager.cs index f98c969b14..1599880e6c 100644 --- a/Assets/PerfectWorld/Scripts/Sound/SFXManager.cs +++ b/Assets/PerfectWorld/Scripts/Sound/SFXManager.cs @@ -219,5 +219,10 @@ namespace BrewMonster.Scripts _moveSoundSource.Play(); } } + public async UniTaskVoid StopMoveSoundAsync() + { + if (_moveSoundSource == null) return; + _moveSoundSource.Stop(); + } } } diff --git a/Assets/Scripts/CECHostPlayer.cs b/Assets/Scripts/CECHostPlayer.cs index 00d1f5cd6d..9745b693a7 100644 --- a/Assets/Scripts/CECHostPlayer.cs +++ b/Assets/Scripts/CECHostPlayer.cs @@ -4213,7 +4213,7 @@ namespace BrewMonster } else { - newId = 164; + newId = 0; } PlayMoveSound(newId); @@ -4227,7 +4227,14 @@ namespace BrewMonster { if (id == _curMoveSndId) return; _curMoveSndId = id; - SFXManager.Instance?.PlayMoveSoundAsync(id).Forget(); + if(id > 0) + { + SFXManager.Instance?.PlayMoveSoundAsync(id).Forget(); + } + else + { + SFXManager.Instance?.StopMoveSoundAsync().Forget(); + } } /// From 7bb80c2ca0d1240150c4bdc41d16f9b9d648fdb0 Mon Sep 17 00:00:00 2001 From: CuongNV <> Date: Sat, 25 Apr 2026 10:33:08 +0700 Subject: [PATCH 11/17] update verticals offset for prefab typing preview --- Assets/Prefabs/ChatSystem/prefab_ChatSystemUI.prefab | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Assets/Prefabs/ChatSystem/prefab_ChatSystemUI.prefab b/Assets/Prefabs/ChatSystem/prefab_ChatSystemUI.prefab index 7c6f597b49..2c4486ae11 100644 --- a/Assets/Prefabs/ChatSystem/prefab_ChatSystemUI.prefab +++ b/Assets/Prefabs/ChatSystem/prefab_ChatSystemUI.prefab @@ -235,14 +235,11 @@ MonoBehaviour: m_EditorClassIdentifier: inputField: {fileID: 3037472824809581303} chatSystem: {fileID: 11400000, guid: 43f54723aa074c74e83e5be28975bee5, type: 2} - inputBarRoot: {fileID: 3282421669272709967} - keyboardBottomPadding: 0 - followMobileKeyboard: 1 typingPreview: typingPreviewRoot: {fileID: 4018402239503345071} typingPreviewText: {fileID: 5673195965320428126} typingPreviewRect: {fileID: 3282421669272709967} - previewVerticalOffset: 8 + previewVerticalOffset: 100 _spriteMap: {fileID: 11400000, guid: f634ecf63ca3d004f82af9b17c966fc9, type: 2} channelButtons: - channel: 0 @@ -2051,7 +2048,7 @@ RectTransform: m_AnchorMax: {x: 1, y: 0.5} m_AnchoredPosition: {x: 0, y: 0} m_SizeDelta: {x: 0, y: 100} - m_Pivot: {x: 0.5, y: 0.5} + m_Pivot: {x: 0.5, y: 1} --- !u!222 &1622721548801034628 CanvasRenderer: m_ObjectHideFlags: 0 From edc2dc4cf9e24007b6f003c6cf6295dfc0e35718 Mon Sep 17 00:00:00 2001 From: Tran Hai Nam Date: Sat, 25 Apr 2026 10:33:19 +0700 Subject: [PATCH 12/17] fix missing id in weapon sound --- Assets/PerfectWorld/Scripts/Move/CECPlayer.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Assets/PerfectWorld/Scripts/Move/CECPlayer.cs b/Assets/PerfectWorld/Scripts/Move/CECPlayer.cs index 70bc99ecc1..c71f88878e 100644 --- a/Assets/PerfectWorld/Scripts/Move/CECPlayer.cs +++ b/Assets/PerfectWorld/Scripts/Move/CECPlayer.cs @@ -278,6 +278,7 @@ namespace BrewMonster {2, new List{"item/weaponattack/2hlonga", "item/weaponattack/2hlongb", "item/weaponattack/2hlongc", "item/weaponattack/2hlongd"}}, {3, new List{"item/weaponattack/1hshorta", "item/weaponattack/1hshortb", "item/weaponattack/1hshortc"}}, {4, new List{"item/weaponattack/2hlonga", "item/weaponattack/2hlongb", "item/weaponattack/2hlongc", "item/weaponattack/2hlongd"}}, + {5, new List{"item/weaponattack/1hshorta", "item/weaponattack/1hshortb"}}, {6, new List{"item/weaponattack/bow", "item/weaponattack/bowb", "item/weaponattack/drawbow"}}, {7, new List{"item/weaponattack/bow", "item/weaponattack/bowb", "item/weaponattack/drawbow"}}, {8, new List{"item/weaponattack/fista", "item/weaponattack/fistb", "item/weaponattack/fistc", "item/weaponattack/fistd"}}, @@ -297,6 +298,7 @@ namespace BrewMonster {2, new List{"item/weaponattack/hitmace", "item/weaponattack/hitmacebig"}}, {3, new List{"item/weaponattack/hithammer", "item/weaponattack/hithammerbig"}}, {4, new List{"item/weaponattack/hitaxe", "item/weaponattack/hitaxebig"}}, + {5, new List{"item/weaponattack/hithammer"}}, {6, new List{"item/weaponattack/hitthrow"}}, {7, new List{"item/weaponattack/hitthrow"}}, {8, new List{"item/weaponattack/hithand"}}, From 0c6f73b4db3517e0b4aca16010db38b1dbdfa251 Mon Sep 17 00:00:00 2001 From: CuongNV <> Date: Sat, 25 Apr 2026 10:51:54 +0700 Subject: [PATCH 13/17] update logic check height typingPreview --- .../ChatSystem/prefab_ChatSystemUI.prefab | 8 +++---- Assets/Scripts/ChatInputHandler.cs | 19 +---------------- Assets/Scripts/TypingPreviewController.cs | 21 +++++++++++++++++-- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/Assets/Prefabs/ChatSystem/prefab_ChatSystemUI.prefab b/Assets/Prefabs/ChatSystem/prefab_ChatSystemUI.prefab index 2c4486ae11..ddbe8d402a 100644 --- a/Assets/Prefabs/ChatSystem/prefab_ChatSystemUI.prefab +++ b/Assets/Prefabs/ChatSystem/prefab_ChatSystemUI.prefab @@ -2044,10 +2044,10 @@ RectTransform: - {fileID: 2130739312226787242} m_Father: {fileID: 806170753671297629} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} - m_AnchorMin: {x: 0, y: 0.5} - m_AnchorMax: {x: 1, y: 0.5} - m_AnchoredPosition: {x: 0, y: 0} - m_SizeDelta: {x: 0, y: 100} + m_AnchorMin: {x: 0, y: 1} + m_AnchorMax: {x: 1, y: 1} + m_AnchoredPosition: {x: 0, y: -100} + m_SizeDelta: {x: 0, y: 450.9249} m_Pivot: {x: 0.5, y: 1} --- !u!222 &1622721548801034628 CanvasRenderer: diff --git a/Assets/Scripts/ChatInputHandler.cs b/Assets/Scripts/ChatInputHandler.cs index 01480922db..af0c5bca5b 100644 --- a/Assets/Scripts/ChatInputHandler.cs +++ b/Assets/Scripts/ChatInputHandler.cs @@ -997,30 +997,13 @@ namespace BrewMonster.Scripts.ChatUI inputField.ActivateInputField(); } - float GetVisibleKeyboardHeight() - { - if (!TouchScreenKeyboard.visible) - return 0f; - - Rect area = TouchScreenKeyboard.area; - return area.height > 0f ? area.height : 0f; - } - void UpdateTypingPreviewFromInput() { if (inputField == null) return; string body = ExtractMessageBodyFromVisual(inputField.text ?? ""); - typingPreview?.UpdatePreview(inputField.isFocused, body, CanShowTypingPreviewNow()); - } - - bool CanShowTypingPreviewNow() - { - if (!Application.isMobilePlatform) - return false; - - return TouchScreenKeyboard.visible; + typingPreview?.UpdatePreview(inputField.isFocused, body); } /// diff --git a/Assets/Scripts/TypingPreviewController.cs b/Assets/Scripts/TypingPreviewController.cs index e623afadd7..358b3569d0 100644 --- a/Assets/Scripts/TypingPreviewController.cs +++ b/Assets/Scripts/TypingPreviewController.cs @@ -44,16 +44,33 @@ namespace BrewMonster.Scripts.ChatUI typingPreviewRect.anchoredPosition = previewPos; } - public void UpdatePreview(bool isInputFocused, string body, bool canShowPreview) + public void UpdatePreview(bool isInputFocused, string body) { if (typingPreviewRoot == null || typingPreviewText == null) return; string trimmedBody = (body ?? string.Empty).Trim(); - bool shouldShow = canShowPreview && isInputFocused && !string.IsNullOrEmpty(trimmedBody); + bool keyboardVisible = IsMobileKeyboardVisible(); + bool shouldShow = keyboardVisible && isInputFocused && !string.IsNullOrEmpty(trimmedBody); typingPreviewRoot.SetActive(shouldShow); if (shouldShow) typingPreviewText.text = trimmedBody; } + + bool IsMobileKeyboardVisible() + { + if (!Application.isMobilePlatform) + return false; + return GetVisibleKeyboardHeight() > 0f || TouchScreenKeyboard.visible; + } + + float GetVisibleKeyboardHeight() + { + if (!TouchScreenKeyboard.visible) + return 0f; + + Rect area = TouchScreenKeyboard.area; + return area.height > 0f ? area.height : 0f; + } } } From b6db8ce972cc584cd205fab0d45e4d2412edab04 Mon Sep 17 00:00:00 2001 From: NguyenVanDat Date: Sat, 25 Apr 2026 11:12:01 +0700 Subject: [PATCH 14/17] force quit half when confirm log out --- .../Scripts/Network/CSNetwork/GameSession.cs | 2 ++ .../Scripts/UI/Login/BtnBackToSelectRole.cs | 17 +++++++++++------ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs b/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs index 05c1150c86..4bcd7bb5a8 100644 --- a/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs +++ b/Assets/PerfectWorld/Scripts/Network/CSNetwork/GameSession.cs @@ -52,6 +52,8 @@ namespace CSNetwork private Action _createRoleCallback; private RoleInfo _selectedRole; public bool IsConnected => _networkManager?.IsConnected ?? false; + public bool IsConnectedInternet => Application.internetReachability != NetworkReachability.NotReachable; + // When true, suppress *gameplay traffic* (mostly gamedatasend C2S commands) during logout/scene transitions. // We still allow account/role flow protocols like rolelist/selectrole so "Return to Select Role" can work. private volatile bool _suppressGameplayTraffic = false; diff --git a/Assets/PerfectWorld/Scripts/UI/Login/BtnBackToSelectRole.cs b/Assets/PerfectWorld/Scripts/UI/Login/BtnBackToSelectRole.cs index 1c13540f72..5d5abc9835 100644 --- a/Assets/PerfectWorld/Scripts/UI/Login/BtnBackToSelectRole.cs +++ b/Assets/PerfectWorld/Scripts/UI/Login/BtnBackToSelectRole.cs @@ -11,17 +11,17 @@ namespace BrewMonster.UI public void OnClick() { // CECUIManager.Instance.ShowMessageBox( - // title: "Thoát", + // title: "Thoát", // message: "Đang rời khỏi Thế Giới Hoàn Mỹ", // messageBoxType: MessageBoxType.YesButton // ); - + // CECGameRun.Instance.GetPendingLogOut().AppendForSaveConfig(new CECPendingLogoutHalf()); // UnityGameSession.ReturnToSelectRole(); OnCommandRepick(); } - + // void CDlgSystem3::OnCommandRepick(const char *szCommand) // { // a_LogOutput(1, "CDlgSystem3::OnCommandRepick "); @@ -36,8 +36,8 @@ namespace BrewMonster.UI // pMsgBox->SetIsModal(false); // } // } - - + + void OnCommandRepick() { CECUIManager.Instance.ShowMessageBoxYes("Thoát", @@ -47,8 +47,13 @@ namespace BrewMonster.UI void OnClickYes() { CECGameRun.Instance.GetPendingLogOut().AppendForSaveConfig(new CECPendingLogoutHalf()); + if (!UnityGameSession.Instance.GameSession.IsConnectedInternet) + { + //force log out half + EC_Game.GetGameRun().SetLogoutFlag(1); + } } - + } } From 99cb7d0fc0ff23fe60dfcfaff7bdffa5e5695aeb Mon Sep 17 00:00:00 2001 From: CuongNV <> Date: Sat, 25 Apr 2026 11:37:57 +0700 Subject: [PATCH 15/17] add logic show emoji for typingPreview and file md --- .../ChatSystem/prefab_ChatSystemUI.prefab | 433 +++++++++++++++++- Assets/Scripts/TypingPreviewController.cs | 8 +- Assets/Scripts/typing-preview-summary.md | 60 +++ Assets/Scripts/typing-preview-summary.md.meta | 7 + 4 files changed, 481 insertions(+), 27 deletions(-) create mode 100644 Assets/Scripts/typing-preview-summary.md create mode 100644 Assets/Scripts/typing-preview-summary.md.meta diff --git a/Assets/Prefabs/ChatSystem/prefab_ChatSystemUI.prefab b/Assets/Prefabs/ChatSystem/prefab_ChatSystemUI.prefab index ddbe8d402a..11af5afd56 100644 --- a/Assets/Prefabs/ChatSystem/prefab_ChatSystemUI.prefab +++ b/Assets/Prefabs/ChatSystem/prefab_ChatSystemUI.prefab @@ -237,7 +237,7 @@ MonoBehaviour: chatSystem: {fileID: 11400000, guid: 43f54723aa074c74e83e5be28975bee5, type: 2} typingPreview: typingPreviewRoot: {fileID: 4018402239503345071} - typingPreviewText: {fileID: 5673195965320428126} + typingPreviewText: {fileID: 1616725709352662407} typingPreviewRect: {fileID: 3282421669272709967} previewVerticalOffset: 100 _spriteMap: {fileID: 11400000, guid: f634ecf63ca3d004f82af9b17c966fc9, type: 2} @@ -1116,7 +1116,7 @@ MonoBehaviour: m_OnValueChanged: m_PersistentCalls: m_Calls: [] ---- !u!1 &2898638287858915607 +--- !u!1 &2902411919389723293 GameObject: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} @@ -1124,50 +1124,51 @@ GameObject: m_PrefabAsset: {fileID: 0} serializedVersion: 6 m_Component: - - component: {fileID: 2130739312226787242} - - component: {fileID: 9133278188770223934} - - component: {fileID: 5673195965320428126} + - component: {fileID: 9105702282600116830} + - component: {fileID: 8286996527838887664} + - component: {fileID: 1830860820497748550} + - component: {fileID: 4544532093443431984} m_Layer: 5 - m_Name: Text (TMP) + m_Name: Placeholder m_TagString: Untagged m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!224 &2130739312226787242 +--- !u!224 &9105702282600116830 RectTransform: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2898638287858915607} - m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} + m_GameObject: {fileID: 2902411919389723293} + 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: 3282421669272709967} + m_Father: {fileID: 3605044702849397252} 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 &9133278188770223934 +--- !u!222 &8286996527838887664 CanvasRenderer: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2898638287858915607} + m_GameObject: {fileID: 2902411919389723293} m_CullTransparentMesh: 1 ---- !u!114 &5673195965320428126 +--- !u!114 &1830860820497748550 MonoBehaviour: m_ObjectHideFlags: 0 m_CorrespondingSourceObject: {fileID: 0} m_PrefabInstance: {fileID: 0} m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 2898638287858915607} + m_GameObject: {fileID: 2902411919389723293} m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3} @@ -1181,7 +1182,7 @@ MonoBehaviour: m_OnCullStateChanged: m_PersistentCalls: m_Calls: [] - m_text: New Text + m_text: "Nh\u1EADp n\u1ED9i dung..." m_isRightToLeft: 0 m_fontAsset: {fileID: 11400000, guid: 369c2e14814cc9a4b8e3ad4e37769134, type: 2} m_sharedMaterial: {fileID: 9092487103257209053, guid: 369c2e14814cc9a4b8e3ad4e37769134, type: 2} @@ -1190,8 +1191,8 @@ MonoBehaviour: m_fontMaterials: [] m_fontColor32: serializedVersion: 2 - rgba: 4294967295 - m_fontColor: {r: 0, g: 0, b: 0, a: 1} + rgba: 2150773298 + m_fontColor: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 0.5} m_enableVertexGradient: 0 m_colorMode: 3 m_fontColorGradient: @@ -1214,7 +1215,7 @@ MonoBehaviour: m_enableAutoSizing: 0 m_fontSizeMin: 18 m_fontSizeMax: 72 - m_fontStyle: 0 + m_fontStyle: 2 m_HorizontalAlignment: 1 m_VerticalAlignment: 256 m_textAlignment: 65535 @@ -1224,14 +1225,14 @@ MonoBehaviour: m_lineSpacingMax: 0 m_paragraphSpacing: 0 m_charWidthMaxAdj: 0 - m_TextWrappingMode: 1 + m_TextWrappingMode: 0 m_wordWrappingRatios: 0.4 m_overflowMode: 0 m_linkedTextComponent: {fileID: 0} parentLinkedComponent: {fileID: 0} m_enableKerning: 0 m_ActiveFontFeatures: 6e72656b - m_enableExtraPadding: 0 + m_enableExtraPadding: 1 checkPaddingRequired: 0 m_isRichText: 1 m_EmojiFallbackSupport: 1 @@ -1252,6 +1253,26 @@ MonoBehaviour: m_hasFontAssetChanged: 0 m_baseMaterial: {fileID: 0} m_maskOffset: {x: 0, y: 0, z: 0, w: 0} +--- !u!114 &4544532093443431984 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 2902411919389723293} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 306cc8c2b49d7114eaa3623786fc2126, type: 3} + m_Name: + m_EditorClassIdentifier: + m_IgnoreLayout: 1 + m_MinWidth: -1 + m_MinHeight: -1 + m_PreferredWidth: -1 + m_PreferredHeight: -1 + m_FlexibleWidth: -1 + m_FlexibleHeight: -1 + m_LayoutPriority: 1 --- !u!1 &3095457626805744279 GameObject: m_ObjectHideFlags: 0 @@ -1635,6 +1656,142 @@ MonoBehaviour: m_OnClick: m_PersistentCalls: m_Calls: [] +--- !u!1 &3569864358642720813 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 6179475653333898123} + - component: {fileID: 3165768457449138107} + - component: {fileID: 4701185761014729773} + m_Layer: 5 + m_Name: Text + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &6179475653333898123 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3569864358642720813} + 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: 3605044702849397252} + 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 &3165768457449138107 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3569864358642720813} + m_CullTransparentMesh: 1 +--- !u!114 &4701185761014729773 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3569864358642720813} + 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: "\u200B" + m_isRightToLeft: 0 + m_fontAsset: {fileID: 11400000, guid: 369c2e14814cc9a4b8e3ad4e37769134, type: 2} + m_sharedMaterial: {fileID: 9092487103257209053, guid: 369c2e14814cc9a4b8e3ad4e37769134, type: 2} + m_fontSharedMaterials: [] + m_fontMaterial: {fileID: 0} + m_fontMaterials: [] + m_fontColor32: + serializedVersion: 2 + rgba: 4281479730 + m_fontColor: {r: 0.19607843, g: 0.19607843, b: 0.19607843, 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: 256 + m_textAlignment: 65535 + m_characterSpacing: 0 + m_wordSpacing: 0 + m_lineSpacing: 0 + m_lineSpacingMax: 0 + m_paragraphSpacing: 0 + m_charWidthMaxAdj: 0 + m_TextWrappingMode: 3 + m_wordWrappingRatios: 0.4 + m_overflowMode: 0 + m_linkedTextComponent: {fileID: 0} + parentLinkedComponent: {fileID: 0} + m_enableKerning: 0 + m_ActiveFontFeatures: 6e72656b + m_enableExtraPadding: 1 + 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 &3646889256970119758 GameObject: m_ObjectHideFlags: 0 @@ -1813,6 +1970,184 @@ MonoBehaviour: isAlert: 0 m_InputValidator: {fileID: 0} m_ShouldActivateOnSelect: 1 +--- !u!1 &3775041254222000362 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 4921631315982434735} + - component: {fileID: 682447300637418577} + - component: {fileID: 7576867370938653448} + - component: {fileID: 1616725709352662407} + m_Layer: 5 + m_Name: InputField (TMP) + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &4921631315982434735 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3775041254222000362} + 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: 3605044702849397252} + m_Father: {fileID: 3282421669272709967} + 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 &682447300637418577 +CanvasRenderer: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3775041254222000362} + m_CullTransparentMesh: 1 +--- !u!114 &7576867370938653448 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3775041254222000362} + 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: 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_Sprite: {fileID: 10911, guid: 0000000000000000f000000000000000, type: 0} + m_Type: 1 + 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 &1616725709352662407 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 3775041254222000362} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 2da0c512f12947e489f739169773d7ca, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Navigation: + m_Mode: 3 + m_WrapAround: 0 + m_SelectOnUp: {fileID: 0} + m_SelectOnDown: {fileID: 0} + m_SelectOnLeft: {fileID: 0} + m_SelectOnRight: {fileID: 0} + m_Transition: 1 + m_Colors: + m_NormalColor: {r: 1, g: 1, b: 1, a: 1} + m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1} + m_SelectedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1} + m_DisabledColor: {r: 1, g: 1, b: 1, a: 1} + m_ColorMultiplier: 1 + m_FadeDuration: 0.1 + m_SpriteState: + m_HighlightedSprite: {fileID: 0} + m_PressedSprite: {fileID: 0} + m_SelectedSprite: {fileID: 0} + m_DisabledSprite: {fileID: 0} + m_AnimationTriggers: + m_NormalTrigger: Normal + m_HighlightedTrigger: Highlighted + m_PressedTrigger: Pressed + m_SelectedTrigger: Selected + m_DisabledTrigger: Disabled + m_Interactable: 0 + m_TargetGraphic: {fileID: 7576867370938653448} + m_TextViewport: {fileID: 3605044702849397252} + m_TextComponent: {fileID: 4701185761014729773} + m_Placeholder: {fileID: 1830860820497748550} + m_VerticalScrollbar: {fileID: 0} + m_VerticalScrollbarEventHandler: {fileID: 0} + m_LayoutGroup: {fileID: 0} + m_ScrollSensitivity: 1 + m_ContentType: 0 + m_InputType: 0 + m_AsteriskChar: 42 + m_KeyboardType: 0 + m_LineType: 0 + m_HideMobileInput: 0 + m_HideSoftKeyboard: 0 + m_CharacterValidation: 0 + m_RegexValue: + m_GlobalPointSize: 36 + m_CharacterLimit: 0 + m_OnEndEdit: + m_PersistentCalls: + m_Calls: [] + m_OnSubmit: + m_PersistentCalls: + m_Calls: [] + m_OnSelect: + m_PersistentCalls: + m_Calls: [] + m_OnDeselect: + m_PersistentCalls: + m_Calls: [] + m_OnTextSelection: + m_PersistentCalls: + m_Calls: [] + m_OnEndTextSelection: + m_PersistentCalls: + m_Calls: [] + m_OnValueChanged: + m_PersistentCalls: + m_Calls: [] + m_OnTouchScreenKeyboardStatusChanged: + m_PersistentCalls: + m_Calls: [] + m_CaretColor: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1} + m_CustomCaretColor: 0 + m_SelectionColor: {r: 0.65882355, g: 0.80784315, b: 1, a: 0.7529412} + m_Text: + m_CaretBlinkRate: 0.85 + m_CaretWidth: 1 + m_ReadOnly: 0 + m_RichText: 1 + m_GlobalFontAsset: {fileID: 11400000, guid: 369c2e14814cc9a4b8e3ad4e37769134, type: 2} + m_OnFocusSelectAll: 1 + m_ResetOnDeActivation: 1 + m_KeepTextSelectionVisible: 0 + m_RestoreOriginalTextOnEscape: 1 + m_isRichTextEditingAllowed: 0 + m_LineLimit: 0 + isAlert: 0 + m_InputValidator: {fileID: 0} + m_ShouldActivateOnSelect: 1 --- !u!1 &3878366334053570553 GameObject: m_ObjectHideFlags: 0 @@ -2041,13 +2376,13 @@ RectTransform: m_LocalScale: {x: 1, y: 1, z: 1} m_ConstrainProportionsScale: 0 m_Children: - - {fileID: 2130739312226787242} + - {fileID: 4921631315982434735} m_Father: {fileID: 806170753671297629} m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0} m_AnchorMin: {x: 0, y: 1} m_AnchorMax: {x: 1, y: 1} - m_AnchoredPosition: {x: 0, y: -100} - m_SizeDelta: {x: 0, y: 450.9249} + m_AnchoredPosition: {x: 15, y: -100} + m_SizeDelta: {x: -30, y: 450.9249} m_Pivot: {x: 0.5, y: 1} --- !u!222 &1622721548801034628 CanvasRenderer: @@ -4281,6 +4616,58 @@ MonoBehaviour: m_hasFontAssetChanged: 0 m_baseMaterial: {fileID: 0} m_maskOffset: {x: 0, y: 0, z: 0, w: 0} +--- !u!1 &6903426946426451281 +GameObject: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + serializedVersion: 6 + m_Component: + - component: {fileID: 3605044702849397252} + - component: {fileID: 2373715959292045924} + m_Layer: 5 + m_Name: Text Area + m_TagString: Untagged + m_Icon: {fileID: 0} + m_NavMeshLayer: 0 + m_StaticEditorFlags: 0 + m_IsActive: 1 +--- !u!224 &3605044702849397252 +RectTransform: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6903426946426451281} + 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: 9105702282600116830} + - {fileID: 6179475653333898123} + m_Father: {fileID: 4921631315982434735} + 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.50001526} + m_SizeDelta: {x: -20, y: -13} + m_Pivot: {x: 0.5, y: 0.5} +--- !u!114 &2373715959292045924 +MonoBehaviour: + m_ObjectHideFlags: 0 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 6903426946426451281} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 11500000, guid: 3312d7739989d2b4e91e6319e9a96d76, type: 3} + m_Name: + m_EditorClassIdentifier: + m_Padding: {x: -8, y: -5, z: -8, w: -5} + m_Softness: {x: 0, y: 0} --- !u!1 &6957797720283583814 GameObject: m_ObjectHideFlags: 0 diff --git a/Assets/Scripts/TypingPreviewController.cs b/Assets/Scripts/TypingPreviewController.cs index 358b3569d0..fe81ac39b4 100644 --- a/Assets/Scripts/TypingPreviewController.cs +++ b/Assets/Scripts/TypingPreviewController.cs @@ -13,7 +13,7 @@ namespace BrewMonster.Scripts.ChatUI [Tooltip("Root của box xem trước nội dung đang gõ.")] [SerializeField] private GameObject typingPreviewRoot; [Tooltip("Text hiển thị nội dung đang gõ realtime.")] - [SerializeField] private TextMeshProUGUI typingPreviewText; + [SerializeField] private TMP_InputField typingPreviewText; [Tooltip("Rect của preview box để neo theo keyboard.")] [SerializeField] private RectTransform typingPreviewRect; [Tooltip("Khoảng cách dọc cộng thêm cho preview box.")] @@ -49,12 +49,12 @@ namespace BrewMonster.Scripts.ChatUI if (typingPreviewRoot == null || typingPreviewText == null) return; - string trimmedBody = (body ?? string.Empty).Trim(); + string previewBody = body ?? string.Empty; bool keyboardVisible = IsMobileKeyboardVisible(); - bool shouldShow = keyboardVisible && isInputFocused && !string.IsNullOrEmpty(trimmedBody); + bool shouldShow = keyboardVisible && isInputFocused; typingPreviewRoot.SetActive(shouldShow); if (shouldShow) - typingPreviewText.text = trimmedBody; + typingPreviewText.text = previewBody; } bool IsMobileKeyboardVisible() diff --git a/Assets/Scripts/typing-preview-summary.md b/Assets/Scripts/typing-preview-summary.md new file mode 100644 index 0000000000..b86b53a2d8 --- /dev/null +++ b/Assets/Scripts/typing-preview-summary.md @@ -0,0 +1,60 @@ +# Typing Preview - Summary + +## Muc tieu da thuc hien + +- Tach logic `typingPreview` ra class rieng de de tai su dung. +- Lien ket lai logic giua `ChatInputHandler` va `TypingPreviewController`. +- Dieu chinh co che hien/an theo yeu cau mobile keyboard. + +## Cac file da cap nhat + +- `Assets/Scripts/TypingPreviewController.cs` +- `Assets/Scripts/ChatInputHandler.cs` + +## Noi dung da lam + +### 1) Tach typing preview thanh class rieng + +- Tao class `TypingPreviewController` de quan ly: + - Hien/an preview (`typingPreviewRoot`) + - Cap nhat text preview realtime (`typingPreviewText`) + - Dinh vi preview theo offset keyboard (`typingPreviewRect`, `previewVerticalOffset`) + +### 2) Lien ket voi ChatInputHandler + +- `ChatInputHandler` giu `SerializeField`: + - `TypingPreviewController typingPreview` +- Moi khi input thay doi text, `ChatInputHandler` lay message body (bo prefix kenh/whisper) roi day sang controller de cap nhat preview. +- Khi clear input, preview duoc cap nhat lai ngay de an dung trang thai. + +### 3) Co che mobile keyboard (cap nhat moi nhat) + +- Da chuyen logic check keyboard vao thang `TypingPreviewController`: + - `IsMobileKeyboardVisible()` + - `GetVisibleKeyboardHeight()` +- `ChatInputHandler` da bo cac ham keyboard-check khong con dung. +- `ChatInputHandler` chi can goi `typingPreview.UpdatePreview(isFocused, body)`. +- `ChatInputHandler.Update()` van refresh moi frame de bat kip luc keyboard mo/dong. +- Rule hien moi nhat: + - Keyboard mobile vua mo la preview show ngay (neu input dang focus), khong can cho co ky tu. + +## Hanh vi hien tai + +- Preview chi hien khi: + - Dang o mobile + - Keyboard dang mo + - Input dang focus +- Khong con yeu cau body phai khong rong (show ngay khi keyboard mo). +- Khi tat keyboard, preview an ngay. +- Khi mo lai keyboard, noi dung da go truoc do van con trong input va preview hien lai dung noi dung. + +## Emoji tren preview + +- Hien tai preview duoc set text truc tiep tu `body`, nen neu `body` co TMP sprite tag thi co the hien emoji. +- Luu y cau hinh component: + - `typingPreviewText` dang de kieu `TMP_InputField` trong `TypingPreviewController`. + - Neu muon on dinh cho muc dich "preview display", nen doi sang `TMP_Text` hoac `TextMeshProUGUI`. + +## Ghi chu + +- Da kiem tra lint sau cac lan sua, khong phat hien loi lint moi tren cac file da chinh. diff --git a/Assets/Scripts/typing-preview-summary.md.meta b/Assets/Scripts/typing-preview-summary.md.meta new file mode 100644 index 0000000000..8304d161ef --- /dev/null +++ b/Assets/Scripts/typing-preview-summary.md.meta @@ -0,0 +1,7 @@ +fileFormatVersion: 2 +guid: 85a85257c23e84e41ba29cbc51455178 +TextScriptImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: From 09abdad53f7a3cab6a03e1af354df52f1455fad9 Mon Sep 17 00:00:00 2001 From: Tran Hai Nam Date: Sat, 25 Apr 2026 14:48:22 +0700 Subject: [PATCH 16/17] fix null proplem of m_nType --- Assets/ModelRenderer/Editor.meta | 8 + .../Editor/CombineActionSOAssigner.cs | 264 ++++++++++++++++++ .../Editor/CombineActionSOAssigner.cs.meta | 2 + Assets/PerfectWorld/Scripts/NPC/CECModel.cs | 17 +- 4 files changed, 283 insertions(+), 8 deletions(-) create mode 100644 Assets/ModelRenderer/Editor.meta create mode 100644 Assets/ModelRenderer/Editor/CombineActionSOAssigner.cs create mode 100644 Assets/ModelRenderer/Editor/CombineActionSOAssigner.cs.meta diff --git a/Assets/ModelRenderer/Editor.meta b/Assets/ModelRenderer/Editor.meta new file mode 100644 index 0000000000..0a8faaa99c --- /dev/null +++ b/Assets/ModelRenderer/Editor.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: ace5809ec565f244b96e257e231b4abc +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/ModelRenderer/Editor/CombineActionSOAssigner.cs b/Assets/ModelRenderer/Editor/CombineActionSOAssigner.cs new file mode 100644 index 0000000000..e7040e863f --- /dev/null +++ b/Assets/ModelRenderer/Editor/CombineActionSOAssigner.cs @@ -0,0 +1,264 @@ +// UTF-8 with BOM — required for Chinese character paths +using System.Collections.Generic; +using System.IO; +using BrewMonster.Scripts.ECModel; +using UnityEditor; +using UnityEngine; + +public class CombineActionSOAssigner : EditorWindow +{ + // Fixed — this is always where the weapon prefabs live + private const string PrefabRootPath = "Assets/ModelRenderer/Art/Models/models/weapons"; + + private string _soRootPath = "Assets/ModelRenderer/Art/Models/models/weapons"; + private bool _isDryRun = true; + private Vector2 _scrollPos; + private readonly List _log = new(); + + private static readonly GUIStyle HeaderStyle = new(EditorStyles.boldLabel) + { + fontSize = 14 + }; + + [MenuItem("Tools/Brew Monster/Combine Action SO Assigner")] + public static void ShowWindow() + { + var win = GetWindow(false, "SO Assigner", true); + win.minSize = new Vector2(520, 480); + } + + private void OnGUI() + { + DrawHeader(); + DrawConfig(); + DrawButtons(); + DrawLog(); + } + + // ── UI sections ────────────────────────────────────────────────────────── + + private void DrawHeader() + { + EditorGUILayout.Space(6); + EditorGUILayout.LabelField("Combine Action SO Assigner", HeaderStyle); + EditorGUILayout.LabelField("Batch-assigns CombinedActionSO assets to weapon prefabs.", EditorStyles.miniLabel); + DrawSeparator(); + } + + private void DrawConfig() + { + EditorGUILayout.LabelField("Paths", EditorStyles.boldLabel); + + using (new EditorGUI.DisabledScope(true)) + EditorGUILayout.TextField("Prefab Root (fixed)", PrefabRootPath); + + _soRootPath = EditorGUILayout.TextField("SO Root Path", _soRootPath); + + EditorGUILayout.HelpBox( + "SO Root must mirror the prefab folder tree.\n" + + "e.g. SO: {SO Root}/人物/刀剑/15品单刀/15品单刀.asset\n" + + " Prefab: " + PrefabRootPath + "/人物/刀剑/15品单刀/15品单刀.prefab", + MessageType.Info); + + DrawSeparator(); + + EditorGUILayout.LabelField("Options", EditorStyles.boldLabel); + _isDryRun = EditorGUILayout.Toggle("Dry Run (no writes)", _isDryRun); + + if (_isDryRun) + EditorGUILayout.HelpBox("Dry Run ON — matches will be logged but nothing will be saved to disk.", MessageType.Warning); + else + EditorGUILayout.HelpBox("Dry Run OFF — prefabs WILL be modified and saved.", MessageType.Error); + + DrawSeparator(); + } + + private void DrawButtons() + { + EditorGUILayout.BeginHorizontal(); + + if (GUILayout.Button("Orphan Check", GUILayout.Height(30))) + RunOrphanCheck(); + + GUI.backgroundColor = _isDryRun ? Color.yellow : Color.red; + if (GUILayout.Button(_isDryRun ? "Run (Dry)" : "Run (LIVE — writes disk)", GUILayout.Height(30))) + RunAssignment(); + GUI.backgroundColor = Color.white; + + EditorGUILayout.EndHorizontal(); + + EditorGUILayout.Space(4); + } + + private void DrawLog() + { + EditorGUILayout.BeginHorizontal(); + EditorGUILayout.LabelField("Log", EditorStyles.boldLabel); + if (GUILayout.Button("Clear", GUILayout.Width(60))) + _log.Clear(); + EditorGUILayout.EndHorizontal(); + + _scrollPos = EditorGUILayout.BeginScrollView(_scrollPos, GUILayout.ExpandHeight(true)); + foreach (var line in _log) + EditorGUILayout.LabelField(line, EditorStyles.wordWrappedLabel); + EditorGUILayout.EndScrollView(); + } + + private static void DrawSeparator() + { + EditorGUILayout.Space(4); + Rect r = EditorGUILayout.GetControlRect(false, 1); + EditorGUI.DrawRect(r, new Color(0.5f, 0.5f, 0.5f, 0.5f)); + EditorGUILayout.Space(4); + } + + // ── Core logic ─────────────────────────────────────────────────────────── + + private void RunOrphanCheck() + { + _log.Clear(); + _log.Add("=== ORPHAN CHECK ==="); + + string[] guids = AssetDatabase.FindAssets("t:Prefab", new[] { PrefabRootPath }); + int orphans = 0; + + foreach (string guid in guids) + { + string prefabPath = AssetDatabase.GUIDToAssetPath(guid); + string soPath = BuildSOPath(prefabPath); + + if (AssetDatabase.LoadAssetAtPath(soPath) == null) + { + _log.Add($"[ORPHAN] {Path.GetFileNameWithoutExtension(prefabPath)}"); + _log.Add($" expected SO: {soPath}"); + orphans++; + } + } + + _log.Add($"=== {orphans} orphan(s) out of {guids.Length} prefab(s) ==="); + Repaint(); + } + + private void RunAssignment() + { + _log.Clear(); + _log.Add(_isDryRun ? "=== DRY RUN ===" : "=== LIVE RUN ==="); + + string[] guids = AssetDatabase.FindAssets("t:Prefab", new[] { PrefabRootPath }); + int matched = 0, skipped = 0, failed = 0; + + try + { + for (int i = 0; i < guids.Length; i++) + { + string prefabPath = AssetDatabase.GUIDToAssetPath(guids[i]); + string prefabName = Path.GetFileNameWithoutExtension(prefabPath); + string soPath = BuildSOPath(prefabPath); + + EditorUtility.DisplayProgressBar( + "Assigning CombinedActionSO", + prefabName, + (float)i / guids.Length); + + CombinedActionSO so = AssetDatabase.LoadAssetAtPath(soPath); + if (so == null) + { + _log.Add($"[SKIP] {prefabName} → no SO at: {soPath}"); + skipped++; + continue; + } + + if (_isDryRun) + { + _log.Add($"[MATCH] {prefabName} → {soPath}"); + matched++; + } + else + { + if (!TryAssign(prefabPath, so, out string error)) + { + _log.Add($"[ERROR] {prefabName} — {error}"); + failed++; + } + else + { + _log.Add($"[OK] {prefabName}"); + matched++; + } + } + + // Memory management every 100 items + if (i > 0 && i % 100 == 0) + { + Resources.UnloadUnusedAssets(); + System.GC.Collect(); + } + } + } + finally + { + EditorUtility.ClearProgressBar(); + } + + if (!_isDryRun) + AssetDatabase.SaveAssets(); + + _log.Add($"=== DONE — matched: {matched}, skipped: {skipped}, failed: {failed} (total: {guids.Length}) ==="); + Repaint(); + } + + private static bool TryAssign(string prefabPath, CombinedActionSO so, out string error) + { + error = null; + GameObject root = null; + try + { + root = PrefabUtility.LoadPrefabContents(prefabPath); + + CombineActHolder holder = root.GetComponent(); + if (holder == null) + { + error = "CombineActHolder not found on root GameObject"; + return false; + } + + using SerializedObject serializedHolder = new(holder); + SerializedProperty prop = serializedHolder.FindProperty("combinedActionSO"); + if (prop == null) + { + error = "SerializedProperty 'combinedActionSO' not found"; + return false; + } + + prop.objectReferenceValue = so; + serializedHolder.ApplyModifiedPropertiesWithoutUndo(); + + PrefabUtility.SaveAsPrefabAsset(root, prefabPath); + return true; + } + catch (System.Exception ex) + { + error = ex.Message; + return false; + } + finally + { + if (root != null) + PrefabUtility.UnloadPrefabContents(root); + } + } + + // ── Path helpers ───────────────────────────────────────────────────────── + + /// + /// Maps a prefab path to its expected SO path using mirror structure. + /// Prefab: Assets/ModelRenderer/Art/Models/models/weapons/X/Y/Z.prefab + /// SO: {_soRootPath}/X/Y/Z.asset + /// + private string BuildSOPath(string prefabPath) + { + string relative = prefabPath.Substring(PrefabRootPath.Length).TrimStart('/'); + string assetRelative = Path.ChangeExtension(relative, ".asset").Replace('\\', '/'); + return _soRootPath.TrimEnd('/') + "/" + assetRelative; + } +} diff --git a/Assets/ModelRenderer/Editor/CombineActionSOAssigner.cs.meta b/Assets/ModelRenderer/Editor/CombineActionSOAssigner.cs.meta new file mode 100644 index 0000000000..c280205fb8 --- /dev/null +++ b/Assets/ModelRenderer/Editor/CombineActionSOAssigner.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 9a82642463c35e244802b6f20f754c7a \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/NPC/CECModel.cs b/Assets/PerfectWorld/Scripts/NPC/CECModel.cs index fe1d405ddd..a776135f1f 100644 --- a/Assets/PerfectWorld/Scripts/NPC/CECModel.cs +++ b/Assets/PerfectWorld/Scripts/NPC/CECModel.cs @@ -681,20 +681,21 @@ public class CECModel { foreach(var eventInfo in eventInfoList) { - if(eventInfo.m_nType == 0) + //0 is sound event + if (eventInfo is FX_BASE_INFO sfx) { - //0 is sound event - if (eventInfo is FX_BASE_INFO sfx) + if(sfx.m_strFilePaths != null && sfx.m_strFilePaths.Count > 0) { - if(sfx.m_strFilePaths != null && sfx.m_strFilePaths.Count > 0) + string soundpath = AFile.NormalizePath(sfx.m_strFilePaths[0],true); + //we need to determine sfx and gfx. now we dont have logic for this path + if(soundpath.Contains("gfx")) { - string soundpath = AFile.NormalizePath(sfx.m_strFilePaths[0],true); - soundpath = soundpath.ToLower(); - SFXManager.Instance.PlaySkillSfxAtPointAsync(soundpath, Vector3.zero).Forget(); + continue; } + soundpath = soundpath.ToLower(); + SFXManager.Instance.PlaySkillSfxAtPointAsync(soundpath, Vector3.zero).Forget(); } } - } } return true; From aacbb7c604275a0c370e8d95b643fbac0e02f40f Mon Sep 17 00:00:00 2001 From: Tran Hai Nam Date: Sat, 25 Apr 2026 15:00:42 +0700 Subject: [PATCH 17/17] fix debug lag in new tools --- .../Editor/CombineActionSOAssigner.cs | 75 ++++++++++++++++--- 1 file changed, 66 insertions(+), 9 deletions(-) diff --git a/Assets/ModelRenderer/Editor/CombineActionSOAssigner.cs b/Assets/ModelRenderer/Editor/CombineActionSOAssigner.cs index e7040e863f..5fe1ff8806 100644 --- a/Assets/ModelRenderer/Editor/CombineActionSOAssigner.cs +++ b/Assets/ModelRenderer/Editor/CombineActionSOAssigner.cs @@ -1,6 +1,7 @@ // UTF-8 with BOM — required for Chinese character paths using System.Collections.Generic; using System.IO; +using System.Text; using BrewMonster.Scripts.ECModel; using UnityEditor; using UnityEngine; @@ -10,10 +11,14 @@ public class CombineActionSOAssigner : EditorWindow // Fixed — this is always where the weapon prefabs live private const string PrefabRootPath = "Assets/ModelRenderer/Art/Models/models/weapons"; + // Log files land next to the project's Logs/ folder + private static readonly string LogDir = Path.GetFullPath("Logs/SOAssigner"); + private string _soRootPath = "Assets/ModelRenderer/Art/Models/models/weapons"; private bool _isDryRun = true; private Vector2 _scrollPos; private readonly List _log = new(); + private string _lastLogFile; private static readonly GUIStyle HeaderStyle = new(EditorStyles.boldLabel) { @@ -94,10 +99,18 @@ public class CombineActionSOAssigner : EditorWindow { EditorGUILayout.BeginHorizontal(); EditorGUILayout.LabelField("Log", EditorStyles.boldLabel); + if (!string.IsNullOrEmpty(_lastLogFile) && GUILayout.Button("Open Log File", GUILayout.Width(100))) + EditorUtility.RevealInFinder(_lastLogFile); if (GUILayout.Button("Clear", GUILayout.Width(60))) + { _log.Clear(); + _lastLogFile = null; + } EditorGUILayout.EndHorizontal(); + if (!string.IsNullOrEmpty(_lastLogFile)) + EditorGUILayout.LabelField(_lastLogFile, EditorStyles.miniLabel); + _scrollPos = EditorGUILayout.BeginScrollView(_scrollPos, GUILayout.ExpandHeight(true)); foreach (var line in _log) EditorGUILayout.LabelField(line, EditorStyles.wordWrappedLabel); @@ -121,6 +134,7 @@ public class CombineActionSOAssigner : EditorWindow string[] guids = AssetDatabase.FindAssets("t:Prefab", new[] { PrefabRootPath }); int orphans = 0; + var fileLines = new List { $"=== ORPHAN CHECK {Timestamp()} ===" }; foreach (string guid in guids) { @@ -129,24 +143,36 @@ public class CombineActionSOAssigner : EditorWindow if (AssetDatabase.LoadAssetAtPath(soPath) == null) { - _log.Add($"[ORPHAN] {Path.GetFileNameWithoutExtension(prefabPath)}"); - _log.Add($" expected SO: {soPath}"); + string line1 = $"[ORPHAN] {Path.GetFileNameWithoutExtension(prefabPath)}"; + string line2 = $" expected SO: {soPath}"; + _log.Add(line1); + _log.Add(line2); + fileLines.Add(line1); + fileLines.Add(line2); orphans++; } } - _log.Add($"=== {orphans} orphan(s) out of {guids.Length} prefab(s) ==="); + string summary = $"=== {orphans} orphan(s) out of {guids.Length} prefab(s) ==="; + _log.Add(summary); + fileLines.Add(summary); + + _lastLogFile = WriteLogFile("OrphanCheck", fileLines); + Debug.Log($"[SO Assigner] Orphan Check — {orphans}/{guids.Length} orphans. Log: {_lastLogFile}"); Repaint(); } private void RunAssignment() { _log.Clear(); - _log.Add(_isDryRun ? "=== DRY RUN ===" : "=== LIVE RUN ==="); + string header = _isDryRun ? "=== DRY RUN ===" : "=== LIVE RUN ==="; + _log.Add(header); string[] guids = AssetDatabase.FindAssets("t:Prefab", new[] { PrefabRootPath }); int matched = 0, skipped = 0, failed = 0; + var fileLines = new List { $"{header} {Timestamp()}" }; + try { for (int i = 0; i < guids.Length; i++) @@ -163,26 +189,32 @@ public class CombineActionSOAssigner : EditorWindow CombinedActionSO so = AssetDatabase.LoadAssetAtPath(soPath); if (so == null) { - _log.Add($"[SKIP] {prefabName} → no SO at: {soPath}"); + string skipLine = $"[SKIP] {prefabName} → {soPath}"; + fileLines.Add(skipLine); + if (!_isDryRun) + _log.Add(skipLine); // window only gets skips in live mode skipped++; continue; } if (_isDryRun) { - _log.Add($"[MATCH] {prefabName} → {soPath}"); + // File gets every match; window stays quiet + fileLines.Add($"[MATCH] {prefabName} → {soPath}"); matched++; } else { if (!TryAssign(prefabPath, so, out string error)) { - _log.Add($"[ERROR] {prefabName} — {error}"); + string errLine = $"[ERROR] {prefabName} — {error}"; + _log.Add(errLine); + fileLines.Add(errLine); failed++; } else { - _log.Add($"[OK] {prefabName}"); + fileLines.Add($"[OK] {prefabName}"); matched++; } } @@ -203,7 +235,18 @@ public class CombineActionSOAssigner : EditorWindow if (!_isDryRun) AssetDatabase.SaveAssets(); - _log.Add($"=== DONE — matched: {matched}, skipped: {skipped}, failed: {failed} (total: {guids.Length}) ==="); + string summary = $"matched: {matched}, skipped: {skipped}, failed: {failed} (total: {guids.Length})"; + string summaryLine = $"=== DONE — {summary} ==="; + _log.Add(summaryLine); + fileLines.Add(summaryLine); + + _lastLogFile = WriteLogFile(_isDryRun ? "DryRun" : "LiveRun", fileLines); + + Debug.Log($"[SO Assigner] {(_isDryRun ? "DRY RUN" : "LIVE")} — {summary} | Log: {_lastLogFile}"); + + if (skipped > 0) + Debug.LogWarning($"[SO Assigner] {skipped} unmatched prefab(s) — see log file for details: {_lastLogFile}"); + Repaint(); } @@ -248,6 +291,20 @@ public class CombineActionSOAssigner : EditorWindow } } + // ── File logging ───────────────────────────────────────────────────────── + + private static string WriteLogFile(string prefix, List lines) + { + Directory.CreateDirectory(LogDir); + string fileName = $"{prefix}_{System.DateTime.Now:yyyy-MM-dd_HH-mm-ss}.log"; + string fullPath = Path.Combine(LogDir, fileName); + File.WriteAllLines(fullPath, lines, new UTF8Encoding(encoderShouldEmitUTF8Identifier: true)); + return fullPath; + } + + private static string Timestamp() => + System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); + // ── Path helpers ───────────────────────────────────────────────────────── ///