diff --git a/Assets/PerfectWorld/Scripts/UI/GamePlay/SkillUI/CDlgSkillSubListItem.cs b/Assets/PerfectWorld/Scripts/UI/GamePlay/SkillUI/CDlgSkillSubListItem.cs index d000d78607..335e40fb64 100644 --- a/Assets/PerfectWorld/Scripts/UI/GamePlay/SkillUI/CDlgSkillSubListItem.cs +++ b/Assets/PerfectWorld/Scripts/UI/GamePlay/SkillUI/CDlgSkillSubListItem.cs @@ -113,19 +113,43 @@ namespace BrewMonster // Message = str, // OnClickedYes = () => UnityGameSession.c2s_SendCmdNPCSevLearnSkill(m_skillID) // }); - var messagebox = uiManager.ShowMessageBoxYes("Game_LearnSkill", str, this, - () => { - UnityGameSession.c2s_SendCmdNPCSevLearnSkill(m_skillID); - int skillID = (int)GetData(); - int nCondition = EC_Game.GetGameRun().GetHostPlayer().CheckSkillLearnCondition(skillID, true); - BMLogger.LogError("HoangDev: CDlgSkillSubListItem OnCommand_Upgrade clicked yes for nCondition " + nCondition); + var messagebox = uiManager.ShowMessageBoxYes( + "Game_LearnSkill", + str, + this, + () => + { + CECHostPlayer host = GetHostPlayer(); + if (host == null) + return; - if (0 == nCondition) + int skillID = m_skillID; + int recheck = host.CheckSkillLearnCondition(skillID, true); + BMLogger.LogError("HoangDev: CDlgSkillSubListItem OnCommand_Upgrade clicked yes for nCondition " + recheck); + + if (recheck != 0) { - UnityGameSession.c2s_SendCmdNPCSevLearnSkill(skillID); + UpdateUpgradeBtn(); + if (!string.IsNullOrEmpty(m_upgradeDisabledReason)) + uiManager.ShowMessageBoxGeneral("MessageBox", m_upgradeDisabledReason, this); + return; } + int costMoney = CECHostSkillModel.Instance.GetSkillMoney(skillID, m_curLevel + 1); + int costSp = CECHostSkillModel.Instance.GetSkillSp(skillID, m_curLevel + 1); + + if (!host.TryConsumeSkillLearnCost(costMoney, costSp)) + { + UpdateUpgradeBtn(); + if (!string.IsNullOrEmpty(m_upgradeDisabledReason)) + uiManager.ShowMessageBoxGeneral("MessageBox", m_upgradeDisabledReason, this); + return; + } + + UnityGameSession.c2s_SendCmdNPCSevLearnSkill(skillID); + UpdateUpgradeBtn(); }); + messagebox.SetData((uint)m_skillID); //GetGameUIMan()->MessageBox("Game_LearnSkill", str, //GetGameUIMan()->GetStringFromTable(231), // MB_OKCANCEL, A3DCOLORRGBA(255, 255, 255, 160), &pMsgBox); diff --git a/Assets/PerfectWorld/Scripts/UI/NPCShopUIManager.cs b/Assets/PerfectWorld/Scripts/UI/NPCShopUIManager.cs index 90454bde48..b1186b88cd 100644 --- a/Assets/PerfectWorld/Scripts/UI/NPCShopUIManager.cs +++ b/Assets/PerfectWorld/Scripts/UI/NPCShopUIManager.cs @@ -1247,7 +1247,6 @@ public class NPCShopUIManager : AUIDialog return; } - // Server requires greeting before NPC service commands. if (CurrentNPCID != 0) UnityGameSession.c2s_CmdNPCSevHello((int)CurrentNPCID); @@ -1259,7 +1258,9 @@ public class NPCShopUIManager : AUIDialog return; } + long totalSellValue = 0; var itemsToSell = new List(sellSlotToSourceSlot.Count); + foreach (var pair in sellSlotToSourceSlot) { int sourceSlot = pair.Value; @@ -1276,10 +1277,13 @@ public class NPCShopUIManager : AUIDialog continue; } - // Ensure local DB props (including price) are present when needed. item.GetDetailDataFromLocal(); int price = item.GetScaledPrice(); + long itemTotal = (long)price * item.m_iCount; + if (itemTotal > 0) + totalSellValue += itemTotal; + itemsToSell.Add(new npc_sell_item { tid = item.m_tid, @@ -1298,17 +1302,56 @@ public class NPCShopUIManager : AUIDialog UnityGameSession.c2s_CmdNPCSevSell(itemsToSell.Count, itemsToSell.ToArray()); Debug.Log($"[NPCShopUIManager] Sent sell command for {itemsToSell.Count} item(s) to NPC {CurrentNPCID}"); - ResetSellReadySlots(); + // Optimistic local money update: cộng tiền ngay trên client + if (host != null && totalSellValue > 0) + { + ulong currentMoney = host.GetMoneyAmount(); + ulong nextMoney = currentMoney + (ulong)totalSellValue; + if (nextMoney > uint.MaxValue) + nextMoney = uint.MaxValue; + + host.SetMoneyAmount((uint)nextMoney); + RefreshMoneyUiFromHost(host); + } + + ResetSellReadySlots(); + UpdateSellTotalPriceText(); + UpdateBuyPriceTexts(); - // Ask server for refreshed inventory, then repaint UI so sold items disappear. var inventoryUI = FindFirstObjectByType(); UnityGameSession.RequestInventoryAsync(0, () => { if (inventoryUI != null) inventoryUI.RefreshAll(); + + var callbackHost = CECGameRun.Instance?.GetHostPlayer(); + if (callbackHost != null) + RefreshMoneyUiFromHost(callbackHost); + + UpdateBuyPriceTexts(); + UpdateSellTotalPriceText(); }); } + private void RefreshMoneyUiFromHost(CECHostPlayer host) + { + if (host == null) + return; + + ulong amount = host.GetMoneyAmount(); + ulong maxAmount = (ulong)Math.Max(0, host.GetMaxMoneyAmount()); + + var inventoryUI = FindFirstObjectByType(); + if (inventoryUI != null && inventoryUI.gameObject.activeInHierarchy) + { + inventoryUI.UpdateMoney(amount, maxAmount); + } + else + { + EC_InventoryUI.CacheMoney(amount, maxAmount); + } + } + private void OnUseItemClicked() { if (!TryGetSelectedInventoryItem(out var selectedItem, out var selectedSlot)) diff --git a/Assets/Scripts/CECHostPlayer.Inventory.cs b/Assets/Scripts/CECHostPlayer.Inventory.cs index b542a8b890..3bcf0669ed 100644 --- a/Assets/Scripts/CECHostPlayer.Inventory.cs +++ b/Assets/Scripts/CECHostPlayer.Inventory.cs @@ -1732,5 +1732,27 @@ namespace BrewMonster return bCanPick; } + + /// + /// Consume money + SP for learning skill on client side after player confirms. + /// Returns false if resources are not enough at commit time. + /// + public bool TryConsumeSkillLearnCost(int moneyCost, int spCost) + { + if (moneyCost < 0) + moneyCost = 0; + if (spCost < 0) + spCost = 0; + + if ((long)GetMoneyAmount() < moneyCost) + return false; + + if (m_BasicProps.iSP < spCost) + return false; + + AddMoneyAmount(-moneyCost); + m_BasicProps.iSP = Math.Max(0, m_BasicProps.iSP - spCost); + return true; + } } }