diff --git a/Assets/PerfectWorld/Prefab/UIManager.prefab b/Assets/PerfectWorld/Prefab/UIManager.prefab index 8bc84f9f6f..e9cf3ea870 100644 --- a/Assets/PerfectWorld/Prefab/UIManager.prefab +++ b/Assets/PerfectWorld/Prefab/UIManager.prefab @@ -4834,7 +4834,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 1 + m_IsActive: 0 --- !u!224 &7306104429597638794 RectTransform: m_ObjectHideFlags: 0 @@ -7774,7 +7774,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 0 + m_IsActive: 1 --- !u!4 &330835371656537333 Transform: m_ObjectHideFlags: 0 @@ -11321,7 +11321,7 @@ GameObject: m_Icon: {fileID: 0} m_NavMeshLayer: 0 m_StaticEditorFlags: 0 - m_IsActive: 1 + m_IsActive: 0 --- !u!224 &5823843793071880086 RectTransform: m_ObjectHideFlags: 0 diff --git a/Assets/PerfectWorld/Resources/UI/LoginScreenCharacterRT.renderTexture b/Assets/PerfectWorld/Resources/UI/LoginScreenCharacterRT.renderTexture index 4accc2f58e..bcbb5508fb 100644 --- a/Assets/PerfectWorld/Resources/UI/LoginScreenCharacterRT.renderTexture +++ b/Assets/PerfectWorld/Resources/UI/LoginScreenCharacterRT.renderTexture @@ -12,8 +12,8 @@ RenderTexture: Hash: 00000000000000000000000000000000 m_IsAlphaChannelOptional: 0 serializedVersion: 6 - m_Width: 512 - m_Height: 512 + m_Width: 1024 + m_Height: 1024 m_AntiAliasing: 1 m_MipCount: -1 m_DepthStencilFormat: 90 diff --git a/Assets/PerfectWorld/Scripts/Managers/A3DSkillGfxMan.cs b/Assets/PerfectWorld/Scripts/Managers/A3DSkillGfxMan.cs index d19c956ff4..0a5ce5450e 100644 --- a/Assets/PerfectWorld/Scripts/Managers/A3DSkillGfxMan.cs +++ b/Assets/PerfectWorld/Scripts/Managers/A3DSkillGfxMan.cs @@ -146,7 +146,6 @@ namespace BrewMonster pEvent.SetDelay(dwDelayTime); pEvent.SetReverse(bReverse); pEvent.SetParam(param); - BMLogger.LogError("[HoangDev] bTraceTarget=" + bTraceTarget); pEvent.SetTraceTarget(bTraceTarget); pEvent.SetModifier(dwModifier); pEvent.SetIsCluster(bCluster); diff --git a/Assets/PerfectWorld/Scripts/Managers/EC_InventoryUI.cs b/Assets/PerfectWorld/Scripts/Managers/EC_InventoryUI.cs index f3773eb874..6f18117f1f 100644 --- a/Assets/PerfectWorld/Scripts/Managers/EC_InventoryUI.cs +++ b/Assets/PerfectWorld/Scripts/Managers/EC_InventoryUI.cs @@ -49,6 +49,9 @@ namespace BrewMonster.Scripts.Managers [SerializeField] private Button splitDecreaseButton; [SerializeField] private Button splitMaxButton; + [Header("Stack combine — merge into another stack (C++ inventory drag-merge, assign in Inspector)")] + [SerializeField] private Button combineStackButton; + private int _splitAmount = 1; private int _splitMaxAmount = 1; @@ -126,6 +129,7 @@ namespace BrewMonster.Scripts.Managers view = new InventoryView(); WireBagTabButtons(); WireSplitUI(); + WireCombineUI(); //if (currentDragImage == null) //{ @@ -207,10 +211,21 @@ namespace BrewMonster.Scripts.Managers } } + private void WireCombineUI() + { + if (combineStackButton != null) + { + combineStackButton.onClick.RemoveAllListeners(); + combineStackButton.onClick.AddListener(() => CombineSelectedStack()); + } + } + private void ShowSplitPanel(bool show) { if (splitPanelRoot != null) splitPanelRoot.SetActive(show); + if (IsSplitCloseButtonOnModal() && splitCloseButton != null) + splitCloseButton.gameObject.SetActive(show); } /// @@ -775,6 +790,100 @@ namespace BrewMonster.Scripts.Managers return true; } + /// + /// Merge from the selected slot into another stack of the same template (C++ CDlgInventory::ExchangeItem + /// branch that calls c2s_CmdMoveIvtrItem when dest matches and pile limit allows). + /// Picks the lowest-index compatible destination with free stack space. + /// + public bool CombineSelectedStack() + { + if (currentSelectedItem == null) + { + Debug.LogWarning("[InventoryUI] CombineSelectedStack: no item selected"); + return false; + } + + if (currentSelectedPackage != PKG_INVENTORY) + { + Debug.LogWarning($"[InventoryUI] CombineSelectedStack: unsupported package {currentSelectedPackage} (only PKG_INVENTORY supported)"); + return false; + } + + if (!TryGetInventoryMergeTarget(currentSelectedSlot, currentSelectedItem, out int dstSlot, out int moveAmount)) + { + Debug.LogWarning("[InventoryUI] CombineSelectedStack: no merge target with free stack space"); + return false; + } + + UnityGameSession.RequestMoveIvtrItem((byte)currentSelectedSlot, (byte)dstSlot, (uint)moveAmount); + RefreshAll(); + return true; + } + + /// + /// Pile cap for stacking: instance (from essence pile_num_max) is authoritative; + /// may still be 1 if element reflection misses field names. + /// + private static int GetEffectivePileLimitForStack(int tid, EC_IvtrItem a, EC_IvtrItem b) + { + int p = Math.Max(1, EC_IvtrItem.GetPileLimit(tid)); + if (a != null) + p = Math.Max(p, Math.Max(1, a.GetPileLimitInstance())); + if (b != null) + p = Math.Max(p, Math.Max(1, b.GetPileLimitInstance())); + return p; + } + + /// First eligible slot (lowest index) that can accept part or all of . + private static bool TryGetInventoryMergeTarget(int srcSlot, EC_IvtrItem srcItem, out int dstSlot, out int moveAmount) + { + dstSlot = -1; + moveAmount = 0; + if (srcItem == null || srcItem.IsFrozen()) + return false; + + int tid = srcItem.GetTemplateID(); + int srcCount = srcItem.GetCount(); + if (srcCount < 1) + return false; + + var host = CECGameRun.Instance?.GetHostPlayer(); + var inv = host?.GetInventory(PKG_INVENTORY); + if (inv == null) + return false; + + int size = inv.GetSize(); + for (int i = 0; i < size; i++) + { + if (i == srcSlot) + continue; + + var dst = inv.GetItem(i, false); + if (dst == null || dst.IsFrozen()) + continue; + if (dst.GetTemplateID() != tid) + continue; + + int pile = GetEffectivePileLimitForStack(tid, srcItem, dst); + if (pile <= 1) + continue; + + int room = pile - dst.GetCount(); + if (room <= 0) + continue; + + int move = Math.Min(srcCount, room); + if (move <= 0) + continue; + + dstSlot = i; + moveAmount = move; + return true; + } + + return false; + } + private int FindEmptySlotInPackage(byte package) { var host = CECGameRun.Instance?.GetHostPlayer(); @@ -1197,6 +1306,67 @@ namespace BrewMonster.Scripts.Managers private void ShowDetailPanel(bool show) { EC_UIUtility.ShowPanel(detailPanelRoot.gameObject, show); + if (!show) + { + RefreshSplitControlsVisibility(0, null); + RefreshCombineControlsVisibility(0, null); + } + } + + /// + /// Split affordance only for main bag stacks (same as ). + /// If splitCloseButton is parented under splitPanelRoot, its visibility follows the modal (not this rule). + /// + private bool IsSplitCloseButtonOnModal() + { + return splitPanelRoot != null && splitCloseButton != null && + splitCloseButton.transform.IsChildOf(splitPanelRoot.transform); + } + + private bool CanShowSplitControls(byte package, EC_IvtrItem item) + { + if (item == null) + return false; + if (package != PKG_INVENTORY) + return false; + int n = item.GetCount(); + if (n < 2 && item.m_iCount >= 2) + n = item.m_iCount; + return n >= 2; + } + + /// + /// splitOpenButton: show only when a splittable main-bag stack is selected (detail panel). + /// splitCloseButton: if it lives on the split modal, show only when modal is open; else same as open (detail-placed cancel). + /// + private void RefreshSplitControlsVisibility(byte package, EC_IvtrItem item) + { + bool canSplit = CanShowSplitControls(package, item); + + if (splitOpenButton != null) + splitOpenButton.gameObject.SetActive(canSplit); + + if (splitCloseButton != null) + { + if (IsSplitCloseButtonOnModal()) + splitCloseButton.gameObject.SetActive(splitPanelRoot != null && splitPanelRoot.activeSelf); + else + splitCloseButton.gameObject.SetActive(canSplit); + } + } + + private bool CanShowCombineControls(byte package, EC_IvtrItem item) + { + if (item == null || package != PKG_INVENTORY) + return false; + return TryGetInventoryMergeTarget(currentSelectedSlot, item, out _, out _); + } + + private void RefreshCombineControlsVisibility(byte package, EC_IvtrItem item) + { + bool canCombine = CanShowCombineControls(package, item); + if (combineStackButton != null) + combineStackButton.gameObject.SetActive(canCombine); } private Button GetButtonForSlot(byte package, int slot) @@ -1297,6 +1467,9 @@ namespace BrewMonster.Scripts.Managers // Show panel first // 先显示面板 ShowDetailPanel(true); + // After detail root is active so child buttons actually appear (fixes split controls under inactive parents). + RefreshSplitControlsVisibility(package, item); + RefreshCombineControlsVisibility(package, item); //Refresh the position of the description text. Used for UI logic purpose. descriptionText.tmp.gameObject.GetComponent()?.RefreshLayout(); diff --git a/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem/EC_IvtrItem.cs b/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem/EC_IvtrItem.cs index 308510be39..183694dda1 100644 --- a/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem/EC_IvtrItem.cs +++ b/Assets/PerfectWorld/Scripts/Managers/EC_IvtrItem/EC_IvtrItem.cs @@ -505,7 +505,7 @@ namespace BrewMonster.Scripts // Common field/property names across item essences string[] names = new[] { - "pilelimit", "pile_limit", "pileLimit", "stack", "stack_max", "stackMax", "max_stack", "maxStack" + "pile_num_max", "pilelimit", "pile_limit", "pileLimit", "stack", "stack_max", "stackMax", "max_stack", "maxStack" }; foreach (var name in names) { diff --git a/Assets/PerfectWorld/Scripts/Move/CECPlayer.cs b/Assets/PerfectWorld/Scripts/Move/CECPlayer.cs index c449c659f8..70749e0773 100644 --- a/Assets/PerfectWorld/Scripts/Move/CECPlayer.cs +++ b/Assets/PerfectWorld/Scripts/Move/CECPlayer.cs @@ -3159,6 +3159,8 @@ namespace BrewMonster try { await SetPlayerModel((byte)m_iProfession, (byte)m_iGender); + + // load all equipments } catch (Exception ex) { diff --git a/Assets/PerfectWorld/Scripts/Objet/CECMatter.cs b/Assets/PerfectWorld/Scripts/Objet/CECMatter.cs index f96cae980d..ae4f5c1308 100644 --- a/Assets/PerfectWorld/Scripts/Objet/CECMatter.cs +++ b/Assets/PerfectWorld/Scripts/Objet/CECMatter.cs @@ -6,12 +6,16 @@ using BrewMonster.Scripts; using CSNetwork.GPDataType; using ModelRenderer.Scripts.Common; using PerfectWorld.Scripts.Managers; +using TMPro; using UnityEngine; namespace PerfectWorld.Scripts { public class CECMatter : CECObject { + private static Mesh s_itemNameQuadMesh; + private static Material s_itemNameBgMaterial; + // Matter information got from server public struct INFO { @@ -118,12 +122,12 @@ namespace PerfectWorld.Scripts public static async Task Init(info_matter Info) { INFO matterInfo = new INFO(); - matterInfo.mid = Info.mid; + matterInfo.mid = Info.mid; matterInfo.tid = Info.tid & 0x0000ffff; // get the matter template from elementdataman DATA_TYPE DataType = DATA_TYPE.DT_INVALID; var matterData = ElementDataManProvider.GetElementDataMan().get_data_ptr((uint)matterInfo.tid, ID_SPACE.ID_SPACE_ESSENCE, ref DataType); - + // Determine matter type based on DataType / Determine matter type based on DataType uint dwMatterType = MATTER_UNKNOWN; if (DataType == DATA_TYPE.DT_MINE_ESSENCE) @@ -142,7 +146,7 @@ namespace PerfectWorld.Scripts { dwMatterType = MATTER_MONEY; } - + if (matterData != null) { var matterDataType = matterData.GetType(); @@ -179,8 +183,8 @@ namespace PerfectWorld.Scripts collider.size = size; } // Create text object to display item name above the cube - // CreateItemNameText(matterObject, info.tid); - + CreateItemNameText(matterObject, Info.tid); + // Add a script to handle click events // MatterCubeClickHandler clickHandler = matterObject.AddComponent(); // clickHandler.Initialize(Info.mid, this); @@ -189,7 +193,7 @@ namespace PerfectWorld.Scripts matterScript.m_iCID = Class_ID.OCID_MATTER; matterScript.SetMatterInfo(matterInfo); matterScript.m_dwMatterType = dwMatterType; // Set matter type / Set matter type - + // Set level requirement and gather distance for mines / Set level requirement and gather distance for mines if (dwMatterType == MATTER_MINE && DataType == DATA_TYPE.DT_MINE_ESSENCE) { @@ -214,11 +218,11 @@ namespace PerfectWorld.Scripts // For non-mine items, set a default pickup distance / For non-mine items, set a default pickup distance matterScript.m_fGatherDist = 3.0f; // Default pickup distance / Default pickup distance } - + matterScript.SetUpCECObject(); // This will reset m_iCID, so we set it again after / This will reset m_iCID, so we set it again after // Force set CID again after SetUpCECObject (which resets it to OCID_OBJECT) / Force set CID again after SetUpCECObject (which resets it to OCID_OBJECT) matterScript.m_iCID = Class_ID.OCID_MATTER; - + // Store reference to the cube // matterGameObjects[info.mid] = matterObject; @@ -237,10 +241,108 @@ namespace PerfectWorld.Scripts return null; } + private static void CreateItemNameText(GameObject matterObject, int tid) + { + if (matterObject == null) + return; + + // Avoid duplicating if prefab already contains it (or Init called twice). + if (matterObject.transform.Find("ItemNameText") != null) + return; + + var textObject = new GameObject("ItemNameText"); + textObject.transform.SetParent(matterObject.transform, false); + textObject.transform.localPosition = new Vector3(0f, 0.6f, 0f); + + var textMesh = textObject.AddComponent(); + + string itemName = null; + if (EC_IvtrItemUtils.Instance != null) + itemName = EC_IvtrItemUtils.Instance.ResolveItemName(tid); + + if (string.IsNullOrEmpty(itemName)) + itemName = $"Item {tid}"; + + textMesh.text = itemName; + textMesh.fontSize = 3f; + textMesh.color = Color.white; + textMesh.alignment = TextAlignmentOptions.Center; + textMesh.textWrappingMode = TextWrappingModes.NoWrap; + textMesh.overflowMode = TextOverflowModes.Overflow; + + textObject.AddComponent(); + + var background = new GameObject("TextBackground"); + background.transform.SetParent(textObject.transform, false); + background.transform.localPosition = Vector3.zero; + background.transform.localScale = new Vector3(2f, 0.8f, 0.1f); + + var bgRenderer = background.AddComponent(); + var bgFilter = background.AddComponent(); + + bgFilter.sharedMesh = GetOrCreateItemNameQuadMesh(); + bgRenderer.sharedMaterial = GetOrCreateItemNameBgMaterial(); + } + + private static Mesh GetOrCreateItemNameQuadMesh() + { + if (s_itemNameQuadMesh != null) + return s_itemNameQuadMesh; + + var quadMesh = new Mesh { name = "CECMatter_ItemNameQuad" }; + quadMesh.vertices = new[] + { + new Vector3(-0.5f, -0.5f, 0), + new Vector3(0.5f, -0.5f, 0), + new Vector3(0.5f, 0.5f, 0), + new Vector3(-0.5f, 0.5f, 0) + }; + quadMesh.triangles = new[] { 0, 1, 2, 0, 2, 3 }; + quadMesh.uv = new[] + { + new Vector2(0, 0), + new Vector2(1, 0), + new Vector2(1, 1), + new Vector2(0, 1) + }; + quadMesh.RecalculateNormals(); + quadMesh.RecalculateBounds(); + + s_itemNameQuadMesh = quadMesh; + return s_itemNameQuadMesh; + } + + private static Material GetOrCreateItemNameBgMaterial() + { + if (s_itemNameBgMaterial != null) + return s_itemNameBgMaterial; + + var shader = Shader.Find("Unlit/Color"); + if (shader == null) + shader = Shader.Find("Sprites/Default"); + if (shader == null) + shader = Shader.Find("Standard"); + + if (shader == null) + return null; + + var mat = new Material(shader) { name = "CECMatter_ItemNameBg" }; + mat.color = new Color(0f, 0f, 0f, 0.7f); + + // Best-effort: make it transparent if the shader supports it. + if (mat.HasProperty("_Mode")) + mat.SetFloat("_Mode", 3f); + if (mat.HasProperty("_Surface")) + mat.SetFloat("_Surface", 1f); + + s_itemNameBgMaterial = mat; + return s_itemNameBgMaterial; + } + private new void Update() { base.Update(); - + // Recovery: after Unity domain reload, manager dictionaries reset but scene objects persist. // Keep trying until we successfully register. if (!m_registeredToManMatter) @@ -269,17 +371,17 @@ namespace PerfectWorld.Scripts // return; // // Ray ray = mainCamera.ScreenPointToRay(screenPosition); - // + // // RaycastHit[] hits = Physics.RaycastAll(ray); - // + // // foreach (RaycastHit hit in hits) // { - // if (hit.collider.gameObject == this.gameObject || + // if (hit.collider.gameObject == this.gameObject || // hit.collider.transform.IsChildOf(this.transform)) // { // Debug.Log($"CECMatter::RaycastHit():: mid: {m_MatterInfo.mid}"); // UnityGameSession.RequestPickupItem(m_MatterInfo.mid, m_MatterInfo.tid); - // break; + // break; // } // } // } @@ -290,4 +392,4 @@ namespace PerfectWorld.Scripts return m_MatterInfo.mid; } } -} \ No newline at end of file +} diff --git a/Assets/PerfectWorld/Scripts/Objet/Shortcut/CECShortcutSet.cs b/Assets/PerfectWorld/Scripts/Objet/Shortcut/CECShortcutSet.cs index d32fa88ab6..3bf69f4fbd 100644 --- a/Assets/PerfectWorld/Scripts/Objet/Shortcut/CECShortcutSet.cs +++ b/Assets/PerfectWorld/Scripts/Objet/Shortcut/CECShortcutSet.cs @@ -506,14 +506,14 @@ namespace BrewMonster // TODO: implement other shortcut types switch ((CECShortcut.ShortcutType)pSC.GetType()) { - /* case CECShortcut.ShortcutType.SCT_COMMAND: - { - CECSCCommand cmdSC = (CECSCCommand)pSC; - data.AddRange(BitConverter.GetBytes(cmdSC.GetCommandID())); - data.AddRange(BitConverter.GetBytes((int)cmdSC.GetParam())); - break; - } - */ + case CECShortcut.ShortcutType.SCT_COMMAND: + { + CECSCCommand cmdSC = (CECSCCommand)pSC; + data.AddRange(BitConverter.GetBytes(cmdSC.GetCommandID())); + data.AddRange(BitConverter.GetBytes((int)cmdSC.GetParam())); + break; + } + case CECShortcut.ShortcutType.SCT_SKILL: { CECSCSkill skillSC = (CECSCSkill)pSC; @@ -600,7 +600,7 @@ namespace BrewMonster switch ((CECShortcut.ShortcutType)iSCType) { - /*case CECShortcut.ShortcutType.SCT_COMMAND: + case CECShortcut.ShortcutType.SCT_COMMAND: { int iCommand = BitConverter.ToInt32(pDataBuf, offset); offset += sizeof(int); @@ -612,17 +612,15 @@ namespace BrewMonster offset += sizeof(int); } - CECSCCommand pCmdSC = null; - if (iCommand == CECSCCommand.CMD_PLAYPOSE) - pCmdSC = pGameRun.GetPoseCmdShortcut(iParam); - else - pCmdSC = pGameRun.GetCmdShortcut(iCommand); - - if (pCmdSC != null) - CreateClonedShortcut(iSlot, pCmdSC); + // Recreate command shortcut from serialized command id + param. + // This is required so "action/command" shortcuts on quickbar persist after relog. + var cmd = new CECSCCommand(iCommand); + if (dwVer >= 2) + cmd.SetParam((uint)iParam); + SetShortcut(iSlot, cmd); break; - }*/ + } case CECShortcut.ShortcutType.SCT_SKILL: { @@ -935,6 +933,8 @@ namespace BrewMonster } public CECSCCommand(CECSCCommand src) { + // Keep shortcut type when cloning + m_iSCType = src.m_iSCType; m_iCommand = src.m_iCommand; m_dwParam = src.m_dwParam; } diff --git a/Assets/PerfectWorld/Scripts/Players/CECPlayerClone.cs b/Assets/PerfectWorld/Scripts/Players/CECPlayerClone.cs index db2433f3d4..940d8c3160 100644 --- a/Assets/PerfectWorld/Scripts/Players/CECPlayerClone.cs +++ b/Assets/PerfectWorld/Scripts/Players/CECPlayerClone.cs @@ -1,7 +1,7 @@ using BrewMonster.Scripts; using System.Threading.Tasks; using UnityEngine; -using CSNetwork.GPDataType; +using Cysharp.Threading.Tasks; namespace BrewMonster { public class CECClonePlayer : CECPlayer @@ -37,6 +37,7 @@ namespace BrewMonster } return false; } + protected async Task LoadFrom(CECHostPlayer player, bool atOnce) { int i = 0; @@ -87,6 +88,7 @@ namespace BrewMonster return await Load(player, atOnce); } + protected async Task LoadFrom(EC_ElsePlayer player, bool atOnce){ // Create equipments for (int i=0; i < InventoryConst.SIZE_ALL_EQUIPIVTR; i++){ @@ -97,6 +99,7 @@ namespace BrewMonster } return await Load(player, atOnce); } + protected async Task Load(CECPlayer player, bool atOnce){ player.CloneSimplePropertyTo(this); OnCloneSimpleProperty(); @@ -104,24 +107,205 @@ namespace BrewMonster BMLogger.LogError("CECCloneElsePlayer::Load, Failed to load skeleton."); return false; } + await LoadAllPlayerEquipments(); SetNewExtendStates(0, m_aExtStates, OBJECT_EXT_STATE_COUNT); if (player.GetOriginalShapeID() != 0){ await TransformShape(player.GetShapeMask(), true); } return true; - } + } + protected virtual bool ShouldLoadEquipment(int index) { return true; } + protected virtual void OnCloneSimpleProperty(){} + public virtual CECModel GetRenderModel(){ return m_pPlayerCECModel; //m_pPetModel ? m_pPetModel : m_pPlayerModel; //Todo: add pet model when implement pet? system } + public void PlayActionByName(string szActName){ m_pActionController.PlayNonSkillActionWithName((int)PLAYER_ACTION_TYPE.ACT_MAX, szActName); } + + + private async UniTask LoadAllPlayerEquipments() + { + var elemendataman = BrewMonster.ElementDataManProvider.GetElementDataMan(); + + DATA_TYPE DataType = default; + bool useDefaultUpper = true; + bool useDefaultLower = true; + bool useDefaultWrist = true; + bool useDefaultFoot = true; + + int equipment = 0; + + for(int i = 0; i < m_aEquips.Length; i++) + { + equipment = m_aEquips[i]; + + var equipData = elemendataman.get_data_ptr((uint)equipment, ID_SPACE.ID_SPACE_ESSENCE, ref DataType); + + if (equipData == null) + { + switch (i) + { + case InventoryConst.EQUIPIVTR_BODY: + useDefaultUpper = true; + break; + case InventoryConst.EQUIPIVTR_LEG: + useDefaultLower = true; + break; + case InventoryConst.EQUIPIVTR_WRIST: + useDefaultWrist = true; + break; + case InventoryConst.EQUIPIVTR_FOOT: + useDefaultFoot = true; + break; + } + continue; + } + + switch (DataType) + { + case DATA_TYPE.DT_WEAPON_ESSENCE: + var weaponData = (WEAPON_ESSENCE)equipData; + + string fileModelRight = AFile.NormalizePath(weaponData.FileModelRight, true).ToLower(); + string fileModelLeft = AFile.NormalizePath(weaponData.FileModelLeft, true).ToLower(); + + GameObject weaponPrefab = null; + if (!string.IsNullOrEmpty(fileModelRight)) + { + weaponPrefab = await AddressableManager.Instance.LoadPrefabAsync(fileModelRight); + var weaponObject = Instantiate(weaponPrefab); + if (weaponObject != null) + { + weaponObject.transform.SetParent(FindChildObjectRecursive(transform, _hh_right_hand_weapon).transform); + weaponObject.transform.localPosition = weaponPrefab.transform.localPosition; + weaponObject.transform.localRotation = weaponPrefab.transform.localRotation; + weaponObject.transform.localScale = Vector3.one; + weaponObject.SetActive(true); + } + } + + if (!string.IsNullOrEmpty(fileModelLeft)) + { + weaponPrefab = await AddressableManager.Instance.LoadPrefabAsync(fileModelLeft); + var weaponObject = Instantiate(weaponPrefab); + if (weaponObject != null) + { + weaponObject.transform.SetParent(FindChildObjectRecursive(transform, _hh_left_hand_weapon).transform); + weaponObject.transform.localPosition = weaponPrefab.transform.localPosition; + weaponObject.transform.localRotation = weaponPrefab.transform.localRotation; + weaponObject.transform.localScale = Vector3.one; + weaponObject.SetActive(true); + } + } + break; + case DATA_TYPE.DT_ARMOR_ESSENCE: + var pArmor = (ARMOR_ESSENCE)equipData; + var nLocation = pArmor.equip_location; + // BMLogger.Log($"ShowEquipments():: Armor Essence: {pArmor.RealName}"); + + var armorSkinPath = _GenEquipmentSkinPath(m_iProfession, m_iGender, pArmor.RealName); + if (!armorSkinPath.EndsWith(".ecm")) + { + armorSkinPath += ".ecm"; + } + var armorPrefab = await AddressableManager.Instance.LoadPrefabAsync(armorSkinPath); + if (armorPrefab != null) + { + var armorObject = Instantiate(armorPrefab); + armorObject.transform.SetParent(GetSkeletonBuilder()?.transform); + armorObject.transform.localPosition = Vector3.zero; + armorObject.transform.localRotation = Quaternion.identity; + armorObject.transform.localScale = Vector3.one; + + var skinnedMeshRenderer = armorObject.GetComponent(); + var skinnedMeshRenderereFromDataList = armorObject.GetComponentsInChildren(); + foreach (var skinnedMeshRenderereFromData in skinnedMeshRenderereFromDataList) + { + if (skinnedMeshRenderereFromData != null) + { + skinnedMeshRenderereFromData._skinnedMeshRenderer.bones = GetSkeletonBuilder().GetBones(skinnedMeshRenderereFromData.BoneNames); + skinnedMeshRenderereFromData._skinnedMeshRenderer.rootBone = skinnedMeshRenderereFromData._skinnedMeshRenderer.bones[^1]; + } + } + + // disable/enable the default equipment + switch (nLocation) + { + case (uint)CECPlayer.SkinIndex.SKIN_UPPER_BODY_INDEX: + useDefaultUpper = false; + break; + case (uint)CECPlayer.SkinIndex.SKIN_LOWER_INDEX: + useDefaultLower = false; + break; + case (uint)CECPlayer.SkinIndex.SKIN_WRIST_INDEX: + useDefaultWrist = false; + break; + case (uint)CECPlayer.SkinIndex.SKIN_FOOT_INDEX: + useDefaultFoot = false; + break; + } + } + break; + //TODO: Handle Wings later + // case DATA_TYPE.DT_WINGMANWING_ESSENCE: + // m_wingType = enumWingType.WINGTYPE_WING; + // //ChangeWing(pResult, static_cast(pEquip)->file_model); + // var pWingData = (WINGMANWING_ESSENCE)equipData; + // //string path1 = "models/players/通用装备/翅膀/天鹅之翼/天鹅之翼_Test.ecm"; + // //var pWingPrefab = await AddressableManager.Instance.LoadPrefabAsync(path1); + // var pWingPrefab = await AddressableManager.Instance.LoadPrefabAsync(pWingData.FileModel.ToLower().Replace('\\', '/')); + // //Transform parentWing = FindChildRecursive(_pPlayerModel.transform, "HH_chibang"); + // if(pWingPrefab != null) + // { + // var pflySwordObject = Instantiate(pWingPrefab).transform; + // pflySwordObject.parent = m_pPlayerCECModel.m_skeletonBuilder.GetHook("HH_chibang").transform; + // pflySwordObject.localPosition = Vector3.zero; + // pflySwordObject.localRotation = Quaternion.identity; + // pflySwordObject.localScale = Vector3.one; + + // m_Wing = pflySwordObject.transform; + + // m_Wing.gameObject.SetActive(false); + // } + // BMLogger.Log($"ShowEquipments():: Wingman Wing Essence: {pWingData.id} {pWingData.Name} -- {pWingData.FileModel}"); + // break; + default: + break; + + + + switch (i) + { + case InventoryConst.EQUIPIVTR_BODY: + useDefaultUpper = false; + break; + case InventoryConst.EQUIPIVTR_LEG: + useDefaultLower = false; + break; + case InventoryConst.EQUIPIVTR_WRIST: + useDefaultWrist = false; + break; + case InventoryConst.EQUIPIVTR_FOOT: + useDefaultFoot = false; + break; + } + } + } + + PlayerDefaultEquipments.DefaultUpper.SetActive(useDefaultUpper); + PlayerDefaultEquipments.DefaultLower.SetActive(useDefaultLower); + PlayerDefaultEquipments.DefaultWirst.SetActive(useDefaultWrist); + PlayerDefaultEquipments.DefaultFoot.SetActive(useDefaultFoot); + } } } \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Players/EC_ElsePlayer.cs b/Assets/PerfectWorld/Scripts/Players/EC_ElsePlayer.cs index 8b7cbe0ea4..6b697df049 100644 --- a/Assets/PerfectWorld/Scripts/Players/EC_ElsePlayer.cs +++ b/Assets/PerfectWorld/Scripts/Players/EC_ElsePlayer.cs @@ -19,6 +19,9 @@ using PerfectWorld.Scripts.Managers; using UnityEngine; using BrewMonster.Scripts.Managers; using System.Collections.Generic; +using static BrewMonster.CECHostPlayer; +using TMPro; +using BrewMonster.PerfectWorld.Scripts.UI; namespace BrewMonster { @@ -995,6 +998,7 @@ namespace BrewMonster m_bBaseInfoReady = true; SetPlayerName(szName ?? ""); EC_Game.GetGameRun().AddPlayerName(m_PlayerInfo.cid, szName, true); + GetComponentInChildren().RefreshName(); } // Level up public void LevelUp() diff --git a/Assets/PerfectWorld/Scripts/Players/PlayerModelPreview.cs b/Assets/PerfectWorld/Scripts/Players/PlayerModelPreview.cs index 7724b5e1d1..d6a9a17567 100644 --- a/Assets/PerfectWorld/Scripts/Players/PlayerModelPreview.cs +++ b/Assets/PerfectWorld/Scripts/Players/PlayerModelPreview.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using BrewMonster.Scripts.Managers; using CSNetwork.Protocols.RPCData; +using Cysharp.Threading.Tasks; using UnityEngine; namespace BrewMonster.Scripts @@ -42,7 +43,10 @@ namespace BrewMonster.Scripts for (int i = 0; i < roleInfos.Count; i++) { RoleInfo role = roleInfos[i]; - GameObject model = await NPCManager.Instance.GetModelPlayer(role.occupation, role.gender); + GameObject model = await LoadPlayerModel(role); + + if (model == null) + continue; if (version != _loadVersion) { @@ -104,5 +108,202 @@ namespace BrewMonster.Scripts playerModels.Clear(); playerModelIds.Clear(); } + + private async UniTask LoadPlayerModel(RoleInfo role) + { + var elemendataman = BrewMonster.ElementDataManProvider.GetElementDataMan(); + GameObject prefab = await NPCManager.Instance.GetModelPlayer(role.occupation, role.gender); + if (prefab == null) + { + return null; + } + GameObject model = Instantiate(prefab); + var playerDefaultEquipments = model.GetComponentInChildren(); + if (playerDefaultEquipments == null) + { + return null; + } + DATA_TYPE DataType = default; + bool useDefaultUpper = true; + bool useDefaultLower = true; + bool useDefaultWrist = true; + bool useDefaultFoot = true; + + GRoleInventory equipment; + + for(int i = 0; i < role.equipment.Count; i++) + { + equipment = role.equipment[i]; + + var equipData = elemendataman.get_data_ptr((uint)equipment.id, ID_SPACE.ID_SPACE_ESSENCE, ref DataType); + + switch (DataType) + { + case DATA_TYPE.DT_WEAPON_ESSENCE: + var weaponData = (WEAPON_ESSENCE)equipData; + + string fileModelRight = AFile.NormalizePath(weaponData.FileModelRight, true).ToLower(); + string fileModelLeft = AFile.NormalizePath(weaponData.FileModelLeft, true).ToLower(); + + GameObject weaponPrefab = null; + if (!string.IsNullOrEmpty(fileModelRight)) + { + weaponPrefab = await AddressableManager.Instance.LoadPrefabAsync(fileModelRight); + var weaponObject = Instantiate(weaponPrefab); + if (weaponObject != null) + { + weaponObject.transform.SetParent(FindChildObjectRecursive(model.transform, CECPlayer._hh_right_hand_weapon).transform); + weaponObject.transform.localPosition = weaponPrefab.transform.localPosition; + weaponObject.transform.localRotation = weaponPrefab.transform.localRotation; + weaponObject.transform.localScale = Vector3.one; + weaponObject.SetActive(true); + } + } + + if (!string.IsNullOrEmpty(fileModelLeft)) + { + weaponPrefab = await AddressableManager.Instance.LoadPrefabAsync(fileModelLeft); + var weaponObject = Instantiate(weaponPrefab); + if (weaponObject != null) + { + weaponObject.transform.SetParent(FindChildObjectRecursive(model.transform, CECPlayer._hh_left_hand_weapon).transform); + weaponObject.transform.localPosition = weaponPrefab.transform.localPosition; + weaponObject.transform.localRotation = weaponPrefab.transform.localRotation; + weaponObject.transform.localScale = Vector3.one; + weaponObject.SetActive(true); + } + } + break; + case DATA_TYPE.DT_ARMOR_ESSENCE: + var pArmor = (ARMOR_ESSENCE)equipData; + var nLocation = pArmor.equip_location; + // BMLogger.Log($"ShowEquipments():: Armor Essence: {pArmor.RealName}"); + + var armorSkinPath = CECPlayer._GenEquipmentSkinPath(role.occupation, role.gender, pArmor.RealName); + if (!armorSkinPath.EndsWith(".ecm")) + { + armorSkinPath += ".ecm"; + } + var armorPrefab = await AddressableManager.Instance.LoadPrefabAsync(armorSkinPath); + if (armorPrefab != null) + { + var armorObject = Instantiate(armorPrefab); + armorObject.transform.SetParent(GetSkeletonBuilder(model)?.transform); + armorObject.transform.localPosition = Vector3.zero; + armorObject.transform.localRotation = Quaternion.identity; + armorObject.transform.localScale = Vector3.one; + + var skinnedMeshRenderer = armorObject.GetComponent(); + var skinnedMeshRenderereFromDataList = armorObject.GetComponentsInChildren(); + foreach (var skinnedMeshRenderereFromData in skinnedMeshRenderereFromDataList) + { + if (skinnedMeshRenderereFromData != null) + { + skinnedMeshRenderereFromData._skinnedMeshRenderer.bones = GetSkeletonBuilder(model).GetBones(skinnedMeshRenderereFromData.BoneNames); + skinnedMeshRenderereFromData._skinnedMeshRenderer.rootBone = skinnedMeshRenderereFromData._skinnedMeshRenderer.bones[^1]; + } + } + + // disable/enable the default equipment + switch (nLocation) + { + case (uint)CECPlayer.SkinIndex.SKIN_UPPER_BODY_INDEX: + useDefaultUpper = false; + break; + case (uint)CECPlayer.SkinIndex.SKIN_LOWER_INDEX: + useDefaultLower = false; + break; + case (uint)CECPlayer.SkinIndex.SKIN_WRIST_INDEX: + useDefaultWrist = false; + break; + case (uint)CECPlayer.SkinIndex.SKIN_FOOT_INDEX: + useDefaultFoot = false; + break; + } + } + break; + //TODO: Handle Wings later + // case DATA_TYPE.DT_WINGMANWING_ESSENCE: + // m_wingType = enumWingType.WINGTYPE_WING; + // //ChangeWing(pResult, static_cast(pEquip)->file_model); + // var pWingData = (WINGMANWING_ESSENCE)equipData; + // //string path1 = "models/players/通用装备/翅膀/天鹅之翼/天鹅之翼_Test.ecm"; + // //var pWingPrefab = await AddressableManager.Instance.LoadPrefabAsync(path1); + // var pWingPrefab = await AddressableManager.Instance.LoadPrefabAsync(pWingData.FileModel.ToLower().Replace('\\', '/')); + // //Transform parentWing = FindChildRecursive(_pPlayerModel.transform, "HH_chibang"); + // if(pWingPrefab != null) + // { + // var pflySwordObject = Instantiate(pWingPrefab).transform; + // pflySwordObject.parent = m_pPlayerCECModel.m_skeletonBuilder.GetHook("HH_chibang").transform; + // pflySwordObject.localPosition = Vector3.zero; + // pflySwordObject.localRotation = Quaternion.identity; + // pflySwordObject.localScale = Vector3.one; + + // m_Wing = pflySwordObject.transform; + + // m_Wing.gameObject.SetActive(false); + // } + // BMLogger.Log($"ShowEquipments():: Wingman Wing Essence: {pWingData.id} {pWingData.Name} -- {pWingData.FileModel}"); + // break; + default: + break; + + + + switch (equipment.pos) + { + case InventoryConst.EQUIPIVTR_BODY: + useDefaultUpper = false; + break; + case InventoryConst.EQUIPIVTR_LEG: + useDefaultLower = false; + break; + case InventoryConst.EQUIPIVTR_WRIST: + useDefaultWrist = false; + break; + case InventoryConst.EQUIPIVTR_FOOT: + useDefaultFoot = false; + break; + } + } + } + + playerDefaultEquipments.DefaultUpper.SetActive(useDefaultUpper); + playerDefaultEquipments.DefaultLower.SetActive(useDefaultLower); + playerDefaultEquipments.DefaultWirst.SetActive(useDefaultWrist); + playerDefaultEquipments.DefaultFoot.SetActive(useDefaultFoot); + return model; + } + + private GameObject FindChildObjectRecursive(Transform parent, string name) + { + foreach (Transform child in parent) + { + if (child.name == name) + { + return child.gameObject; + } + var childObject = FindChildObjectRecursive(child, name); + if (childObject != null) + { + return childObject; + } + } + return null; + } + + private SkeletonBuilder GetSkeletonBuilder(GameObject characterModel) + { + if (characterModel == null) + { + return null; + } + SkeletonBuilder skeletonBuilder = null; + if (skeletonBuilder == null) + { + skeletonBuilder = characterModel.GetComponentInChildren(); + } + return skeletonBuilder; + } } } diff --git a/Assets/PerfectWorld/Scripts/Skills/SkillStubs1/skill88.cs b/Assets/PerfectWorld/Scripts/Skills/SkillStubs1/skill88.cs index 3179f32537..5749a3b41c 100644 --- a/Assets/PerfectWorld/Scripts/Skills/SkillStubs1/skill88.cs +++ b/Assets/PerfectWorld/Scripts/Skills/SkillStubs1/skill88.cs @@ -86,9 +86,9 @@ namespace BrewMonster public Skill88Stub() : base(88) { cls = 1; - name = "ӿȪ"; - nativename = "ӿȪ"; - icon = "ӿȪ"; + name = "涌泉"; + nativename = "涌泉"; + icon = "涌泉"; max_level = 10; type = 1; apcost = 0; @@ -109,11 +109,40 @@ namespace BrewMonster long_range = 0; restrict_corpse = 0; allow_forms = 1; - effect = "ӿȪ"; + effect = "涌泉"; doenchant = 1; dobless = 0; commoncooldown = 0; commoncooldowntime = 0; + m_szFlyGfxPath = string.Empty; + m_szHitGrndGfxPath = string.Empty; + m_szHitGfxPath = "策划联入/人物技能/飞行/涌泉中招.gfx"; + + // GFX Movement and Behavior Parameters / GFX移动和行为参数 + m_MoveMode = (GfxMoveMode)0; + m_TargetMode = (GfxTargetMode)0; + m_AttFlyMode = (GfxAttackMode)0; + m_AttHitMode = (GfxAttackMode)0; + m_dwFlyTime = 0; + m_bTraceTarget = false; + m_FlyClusterCount = 1; + m_FlyClusterInterval = 0; + m_HitClusterCount = 1; + m_HitClusterInterval = 0; + m_bOneHit = true; + m_bFadeOut = false; + m_bRelScl = true; + m_fDefTarScl = 1.5f; + + // Area parameters (commented out) / 区域参数(已注释) + // m_bArea = false; + // m_Shape = (EmitShape)0; + // m_vSize = new Vector3(0.0f, 0.0f, 0.0f); + + // Param (commented out) / 参数(已注释) + // m_paramType = (GfxSkillValType)1; + // m_param = new GFX_SKILL_PARAM(); + // m_param.nVal = 0; restrict_weapons.Add(0); restrict_weapons.Add(292); range = new Range(); diff --git a/Assets/PerfectWorld/Scripts/Skills/SkillStubs2/skill150.cs b/Assets/PerfectWorld/Scripts/Skills/SkillStubs2/skill150.cs index 4f04ff911b..1ba1d105a0 100644 --- a/Assets/PerfectWorld/Scripts/Skills/SkillStubs2/skill150.cs +++ b/Assets/PerfectWorld/Scripts/Skills/SkillStubs2/skill150.cs @@ -86,9 +86,9 @@ namespace BrewMonster public Skill150Stub() : base(150) { cls = 4; - name = "˺ҧ"; - nativename = "˺ҧ"; - icon = "˺ҧ"; + name = "撕咬"; + nativename = "撕咬"; + icon = "撕咬"; max_level = 10; type = 1; apcost = 20; @@ -109,11 +109,40 @@ namespace BrewMonster long_range = 0; restrict_corpse = 0; allow_forms = 2; - effect = "˺ҧ"; + effect = "撕咬"; doenchant = 1; dobless = 0; commoncooldown = 0; commoncooldowntime = 0; + m_szFlyGfxPath = string.Empty; + m_szHitGrndGfxPath = string.Empty; + m_szHitGfxPath = "策划联入/人物技能/击中/撕咬击中.gfx"; + + // GFX Movement and Behavior Parameters / GFX移动和行为参数 + m_MoveMode = (GfxMoveMode)0; + m_TargetMode = (GfxTargetMode)0; + m_AttFlyMode = (GfxAttackMode)0; + m_AttHitMode = (GfxAttackMode)0; + m_dwFlyTime = 0; + m_bTraceTarget = false; + m_FlyClusterCount = 1; + m_FlyClusterInterval = 0; + m_HitClusterCount = 1; + m_HitClusterInterval = 0; + m_bOneHit = true; + m_bFadeOut = false; + m_bRelScl = true; + m_fDefTarScl = 1.5f; + + // Area parameters (commented out) / 区域参数(已注释) + // m_bArea = false; + // m_Shape = (EmitShape)0; + // m_vSize = new Vector3(0.0f, 0.0f, 0.0f); + + // Param (commented out) / 参数(已注释) + // m_paramType = (GfxSkillValType)1; + // m_param = new GFX_SKILL_PARAM(); + // m_param.nVal = 0; restrict_weapons.Add(9); restrict_weapons.Add(0); range = new Range(); diff --git a/Assets/PerfectWorld/Scripts/UI/Action/CDlgSkillSubAction.cs b/Assets/PerfectWorld/Scripts/UI/Action/CDlgSkillSubAction.cs index f8659fc4ee..80dfd55700 100644 --- a/Assets/PerfectWorld/Scripts/UI/Action/CDlgSkillSubAction.cs +++ b/Assets/PerfectWorld/Scripts/UI/Action/CDlgSkillSubAction.cs @@ -37,7 +37,7 @@ namespace BrewMonster OnShowDialog(); } - public void Init() + private void Init() { int[] objCount = { 8, 2, 2, 27 }; @@ -53,17 +53,17 @@ namespace BrewMonster image = orderTP, pLabel = orderTP.GetComponentInChildren() }); - + } } for (int j = 0; j < 27; j++) { var actionTP = Instantiate(actionTemplate, actionContain); actionTP.gameObject.SetActive(true); - + // Set up click handler for action items / 为动作项设置点击处理程序 SetupActionClickHandler(actionTP); - + m_aActionInfo.Add(new ActionInfo { image = actionTP, @@ -73,7 +73,7 @@ namespace BrewMonster //force refresh layout orderContain anc actionContain //then refresh layout of this gameobject } - public void OnShowDialog() + private void OnShowDialog() { AUIImagePicture pImage; TextMeshProUGUI pLabel; @@ -129,14 +129,14 @@ namespace BrewMonster Debug.Log($"CDlgSkillSubAction::SetupActionClickHandler():: Setting up click handler for action item: {actionImage.name}"); // Get the button component / 获取按钮组件 var button = actionImage.GetComponentInChildren(); - if (button == null) + if (button == null) return; - + // Remove existing listeners and add our custom handler / 移除现有监听器并添加我们的自定义处理程序 button.onClick.RemoveAllListeners(); button.onClick.AddListener(() => OnActionClicked(actionImage)); } - + /// /// Handle action icon click, similar to wave hand logic / 处理动作图标点击,类似于挥手逻辑 /// @@ -147,7 +147,7 @@ namespace BrewMonster // Get the shortcut from the action image / 从动作图像获取快捷方式 actionImage.Execute(); } - + [Serializable] public struct ActionInfo { diff --git a/Assets/PerfectWorld/Scripts/UI/GamePlay/AUIImagePicture.cs b/Assets/PerfectWorld/Scripts/UI/GamePlay/AUIImagePicture.cs index 8a8e1cb271..54e191f526 100644 --- a/Assets/PerfectWorld/Scripts/UI/GamePlay/AUIImagePicture.cs +++ b/Assets/PerfectWorld/Scripts/UI/GamePlay/AUIImagePicture.cs @@ -1,3 +1,4 @@ +using BrewMonster; using BrewMonster.UI; using System; using System.Collections; @@ -74,7 +75,7 @@ namespace BrewMonster.Assets.PerfectWorld.Scripts.UI.GamePlay { var rectTransform = GetComponent(); int skillID = int.Parse(this.name.Split('_')[1]); - uiManager.ShowSkillTooltip(m_hintText, rectTransform, () => EventBus.Publish(new OpenAssignSkillEvent(skillID))); + uiManager.ShowSkillTooltip(m_hintText, rectTransform, () => EventBus.Publish(new AssignSkillSelectionChangedEvent(skillID, true))); } } } diff --git a/Assets/PerfectWorld/Scripts/UI/GamePlay/AUIToggle.cs b/Assets/PerfectWorld/Scripts/UI/GamePlay/AUIToggle.cs index 3df0df3674..0f5d8e52c7 100644 --- a/Assets/PerfectWorld/Scripts/UI/GamePlay/AUIToggle.cs +++ b/Assets/PerfectWorld/Scripts/UI/GamePlay/AUIToggle.cs @@ -32,38 +32,33 @@ namespace BrewMonster } } - public struct OpenAssignSkillEvent + /// Merged open/close skill selection for assign UI (replaces OpenAssignSkillEvent + CloseAssignSkillEvent). + public struct AssignSkillSelectionChangedEvent { public int skillID; - public OpenAssignSkillEvent(int id) + public bool selected; + public AssignSkillSelectionChangedEvent(int skillId, bool selected) { - skillID = id; - } - } - public struct CloseAssignSkillEvent - { - public int skillID; - public CloseAssignSkillEvent(int id) - { - skillID = id; - } - } - public struct OpenAssignSlotEvent - { - public int slotIndex; - public OpenAssignSlotEvent(int index) - { - slotIndex = index; - } - } - public struct CloseAssignSlotEvent - { - public int slotIndex; - public CloseAssignSlotEvent(int index) - { - slotIndex = index; + skillID = skillId; + this.selected = selected; } } + // public struct OpenAssignSlotEvent + // { + // public int slotIndex; + // public OpenAssignSlotEvent(int index) + // { + // slotIndex = index; + // } + // } + // public struct CloseAssignSlotEvent + // { + // public int slotIndex; + // public CloseAssignSlotEvent(int index) + // { + // slotIndex = index; + // } + // } public struct OnAssignSkillEvent { public int skillID; @@ -74,4 +69,32 @@ namespace BrewMonster this.slotIndex = slotIndex; } } + + /// Action palette row selection for quickbar assign (same order as CDlgSkillSubAction shortcut sets). + public struct AssignActionSelectionChangedEvent + { + public int actionSetIndex; + public int shortcutIndexInSet; + public bool selected; + public AssignActionSelectionChangedEvent(int actionSetIndex, int shortcutIndexInSet, bool selected) + { + this.actionSetIndex = actionSetIndex; + this.shortcutIndexInSet = shortcutIndexInSet; + this.selected = selected; + } + } + + /// Published after an action shortcut is written to the assign cache; palette uses indices to UncheckAfterAssign. + public struct OnAssignActionEvent + { + public int actionSetIndex; + public int shortcutIndexInSet; + public int quickbarSlotIndex; + public OnAssignActionEvent(int actionSetIndex, int shortcutIndexInSet, int quickbarSlotIndex) + { + this.actionSetIndex = actionSetIndex; + this.shortcutIndexInSet = shortcutIndexInSet; + this.quickbarSlotIndex = quickbarSlotIndex; + } + } } diff --git a/Assets/PerfectWorld/Scripts/UI/GamePlay/AUIToggleActionAssign.cs b/Assets/PerfectWorld/Scripts/UI/GamePlay/AUIToggleActionAssign.cs new file mode 100644 index 0000000000..38986b8982 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/UI/GamePlay/AUIToggleActionAssign.cs @@ -0,0 +1,62 @@ +using System; +using UnityEngine; +using UnityEngine.UI; + +namespace BrewMonster +{ + /// Assign UI row for command/action shortcuts; toggle only — does not Execute(). + public class AUIToggleActionAssign : AUIToggle + { + [Header("ActionAssign")] + [SerializeField] private int actionSetIndex; + [SerializeField] private int shortcutIndexInSet; + + /// bool: isOn, int: actionSetIndex, int: shortcutIndexInSet + public event Action OnActionAssignToggleChanged; + + public override void Awake() + { + UnSubscribeEvents(); + SubscribeEvents(); + } + + public void OnDestroy() + { + UnSubscribeEvents(); + } + + private void UnSubscribeEvents() + { + if (uiToggle != null) + uiToggle.onValueChanged.RemoveAllListeners(); + } + + private void SubscribeEvents() + { + if (uiToggle != null) + uiToggle.onValueChanged.AddListener(OnToggleValueChanged); + } + + private void OnToggleValueChanged(bool isOn) + { + OnActionAssignToggleChanged?.Invoke(isOn, actionSetIndex, shortcutIndexInSet); + } + + public void SetActionSlotIndices(int setIndex, int indexInSet) + { + actionSetIndex = setIndex; + shortcutIndexInSet = indexInSet; + } + + public int GetActionSetIndexForAssign() => actionSetIndex; + public int GetShortcutIndexInSetForAssign() => shortcutIndexInSet; + + public Toggle GetUIToggle() => uiToggle; + + public void UncheckAfterAssign() + { + if (uiToggle != null) + uiToggle.isOn = false; + } + } +} diff --git a/Assets/PerfectWorld/Scripts/UI/GamePlay/AUIToggleActionAssign.cs.meta b/Assets/PerfectWorld/Scripts/UI/GamePlay/AUIToggleActionAssign.cs.meta new file mode 100644 index 0000000000..518a3808ec --- /dev/null +++ b/Assets/PerfectWorld/Scripts/UI/GamePlay/AUIToggleActionAssign.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b8e4d32c9f5c56dab2e3f45678901bcd +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/PerfectWorld/Scripts/UI/GamePlay/AUIToggleAssignSlot.cs b/Assets/PerfectWorld/Scripts/UI/GamePlay/AUIToggleAssignSlot.cs index b85c6f5b64..f595822757 100644 --- a/Assets/PerfectWorld/Scripts/UI/GamePlay/AUIToggleAssignSlot.cs +++ b/Assets/PerfectWorld/Scripts/UI/GamePlay/AUIToggleAssignSlot.cs @@ -1,13 +1,12 @@ -using BrewMonster.Assets.PerfectWorld.Scripts.UI.GamePlay; +using System; using UnityEngine; -using BrewMonster.UI; -using BrewMonster.Network; + namespace BrewMonster { public class AUIToggleAssignSlot : AUIToggle { - // [Header("AssignSlotToggleUI")] - // [SerializeField] int slotIndex; + public event Action OnSetSlot; + public override void Awake() { UnSubscribeEvents(); @@ -21,31 +20,21 @@ namespace BrewMonster private void UnSubscribeEvents() { uiToggle.onValueChanged.RemoveAllListeners(); - EventBus.Unsubscribe(OnAssignSlotEvent); } private void SubscribeEvents() { - Debug.Log($"HoangDev: SubscribeEvents: AssignSlots"); uiToggle.onValueChanged.AddListener(OnToggleValueChanged); - EventBus.Subscribe(OnAssignSlotEvent); } private void OnToggleValueChanged(bool isOn) { - if (isOn) - { - EventBus.Publish(new OpenAssignSlotEvent(slotIndex)); - }else{ - EventBus.Publish(new CloseAssignSlotEvent(slotIndex)); - } + OnSetSlot?.Invoke(isOn, slotIndex); } - private void OnAssignSlotEvent(OnAssignSkillEvent @event) + + public int GetSlotIndexForAssign() => slotIndex; + + public void UncheckAfterAssign() { - if(@event.slotIndex == slotIndex) - { - var processSkill = CECGameRun.Instance.GetHostPlayer().GetPositiveSkillByID(@event.skillID); - EC_Game.GetGameRun().GetUIManager().GetInGameUIMan().SetCover(this, processSkill.GetIconFile(), EC_GAMEUI_ICONS.ICONS_SKILL); - uiToggle.isOn = false; - } + uiToggle.isOn = false; } } } diff --git a/Assets/PerfectWorld/Scripts/UI/GamePlay/AUIToggleSkillAssign.cs b/Assets/PerfectWorld/Scripts/UI/GamePlay/AUIToggleSkillAssign.cs index 598255858a..d2f779a452 100644 --- a/Assets/PerfectWorld/Scripts/UI/GamePlay/AUIToggleSkillAssign.cs +++ b/Assets/PerfectWorld/Scripts/UI/GamePlay/AUIToggleSkillAssign.cs @@ -1,11 +1,13 @@ -using BrewMonster.Assets.PerfectWorld.Scripts.UI.GamePlay; +using System; using UnityEngine; -using UnityEngine.UI; namespace BrewMonster { public class AUIToggleSkillAssign : AUIToggle { + /// bool: isOn, int: skillID + public event Action OnAssignToggleChanged; + public override void Awake() { UnSubscribeEvents(); @@ -19,29 +21,21 @@ namespace BrewMonster private void UnSubscribeEvents() { uiToggle.onValueChanged.RemoveAllListeners(); - EventBus.Unsubscribe(OnAssignSkillEvent); } private void SubscribeEvents() { - Debug.Log($"HoangDev: SubscribeEvents: Slot"); uiToggle.onValueChanged.AddListener(OnToggleValueChanged); - EventBus.Subscribe(OnAssignSkillEvent); } private void OnToggleValueChanged(bool isOn) { - if (isOn) - { - EventBus.Publish(new OpenAssignSkillEvent(skillID)); - }else{ - EventBus.Publish(new CloseAssignSkillEvent(skillID)); - } + OnAssignToggleChanged?.Invoke(isOn, skillID); } - private void OnAssignSkillEvent(OnAssignSkillEvent @event) + + public int GetSkillIdForAssign() => skillID; + + /// Used when assignment completes — unchecks row (fires AssignSkillSelectionChangedEvent via DlgAssignSub). + public void UncheckAfterAssign() { - if(@event.skillID != skillID) - { - return; - } uiToggle.isOn = false; } } diff --git a/Assets/PerfectWorld/Scripts/UI/GamePlay/CdlgQuickBar.cs b/Assets/PerfectWorld/Scripts/UI/GamePlay/CdlgQuickBar.cs index b29d19d512..0e76887c8c 100644 --- a/Assets/PerfectWorld/Scripts/UI/GamePlay/CdlgQuickBar.cs +++ b/Assets/PerfectWorld/Scripts/UI/GamePlay/CdlgQuickBar.cs @@ -260,7 +260,7 @@ namespace BrewMonster pClock.SetColor(new Color32(0, 0, 0, 128)); } } - + //if (pSCItem.GetInventory == InventoryConst.IVTRTYPE_EQUIPPACK) //{ // pCell.SetColor(new Color(128, 128, 255, 128)); @@ -271,6 +271,35 @@ namespace BrewMonster string itemIcon = pItem.GetIconFile(); GetGameUIMan().SetCover(pCell, itemIcon, EC_GAMEUI_ICONS.ICONS_INVENTORY); } + } + + else if (pSC.GetType() == (int)CECShortcut.ShortcutType.SCT_COMMAND) + { + iIconFile = (int)EC_GAMEUI_ICONS.ICONS_ACTION; + if (pSC.GetType() == (int)CECShortcut.ShortcutType.SCT_COMMAND) + { + CECSCCommand pCommandSC = (CECSCCommand)pSC; + if (pHost.IsInvisible()) + { + if (pCommandSC.GetCommandID() == (int)CECSCCommand.CommandID.CMD_STARTTRADE || + pCommandSC.GetCommandID() == (int)CECSCCommand.CommandID.CMD_SELLBOOTH || + pCommandSC.GetCommandID() == (int)CECSCCommand.CommandID.CMD_BINDBUDDY) + { + pCell.SetColor(new Color(128, 128, 128, 255)); + } + else + { + pCell.SetColor(new Color(255, 255, 255, 255)); + + } + } + else + { + pCell.SetColor(new Color(255, 255, 255, 255)); + } + + pCell.SetInteract(true); + } } //else if(pSC.GetType() == (int)CECShortcut.ShortcutType.SCT_PET) //{ @@ -374,6 +403,10 @@ namespace BrewMonster // fix later now haven't skill group icon yet GetGameUIMan().SetCover(pCell, "unknown", EC_GAMEUI_ICONS.ICONS_SKILL); } + else if (pSC.GetType() == (int)CECShortcut.ShortcutType.SCT_COMMAND) + { + GetGameUIMan().SetCover(pCell, ((CECSCCommand)pSC).GetIconFile(), EC_GAMEUI_ICONS.ICONS_ACTION); + } else { if (pSkill != null) diff --git a/Assets/PerfectWorld/Scripts/UI/HUDPlayer.cs b/Assets/PerfectWorld/Scripts/UI/HUDPlayer.cs index 473c4b17fe..060f487618 100644 --- a/Assets/PerfectWorld/Scripts/UI/HUDPlayer.cs +++ b/Assets/PerfectWorld/Scripts/UI/HUDPlayer.cs @@ -98,7 +98,6 @@ namespace BrewMonster if(hostPlayer == null) return; var iconStates = hostPlayer.m_aIconStates; - Debug.LogError($"UpdateBuffIcons: iconStates.Count = {iconStates.Count}"); if(buffIcons.Count < iconStates.Count) { for(int i = buffIcons.Count; i < iconStates.Count; i++) diff --git a/Assets/PerfectWorld/Scripts/UI/SkillUI/AssignSkill.cs b/Assets/PerfectWorld/Scripts/UI/SkillUI/AssignSkill.cs index d3579a1e81..3fbb173687 100644 --- a/Assets/PerfectWorld/Scripts/UI/SkillUI/AssignSkill.cs +++ b/Assets/PerfectWorld/Scripts/UI/SkillUI/AssignSkill.cs @@ -23,7 +23,6 @@ namespace BrewMonster actionButton.onClick.AddListener(OnActionButtonClicked); itemSkillButton.onClick.AddListener(OnItemButtonClicked); buttonClose.onClick.AddListener(OnCloseButtonClicked); - actionButton.interactable = false; itemSkillButton.interactable = false; } void OnEnable() diff --git a/Assets/PerfectWorld/Scripts/UI/SkillUI/DlgAssignSlots.cs b/Assets/PerfectWorld/Scripts/UI/SkillUI/DlgAssignSlots.cs index 3845fc9d0c..41894a66d7 100644 --- a/Assets/PerfectWorld/Scripts/UI/SkillUI/DlgAssignSlots.cs +++ b/Assets/PerfectWorld/Scripts/UI/SkillUI/DlgAssignSlots.cs @@ -10,6 +10,7 @@ using CSNetwork.GPDataType; using System.Collections.Generic; using System.Linq; using UnityEngine; +using UnityEngine.Serialization; using UnityEngine.UI; using static BrewMonster.PET_EGG_ESSENCE; @@ -20,7 +21,7 @@ namespace BrewMonster //[SerializeField] List m_aSkillImage = new List(); //[SerializeField] List