Files
test/Assets/PerfectWorld/Scripts/UI/NPCShopUIManager.cs
T
2026-04-22 12:44:22 +07:00

1704 lines
60 KiB
C#

// Filename : NPCShopUIManager.cs
// Creator : Converted from C++ EC_Shop
// Date : 2024
using BrewMonster;
using BrewMonster.Network;
using BrewMonster.Scripts;
using BrewMonster.Scripts.Managers;
using BrewMonster.UI;
using CSNetwork.C2SCommand;
using ModelRenderer.Scripts.Common;
using System;
using System.Collections.Generic;
using TMPro;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.UI;
using static CSNetwork.Common.ExpTypes;
public class NPCShopUIManager : AUIDialog
{
[Header("UI Panels")]
public GameObject npcShopMainPanel;
public Image itemIconImage;
public GameObject contentMidBuy;
public GameObject contentRight;
public GameObject contentMidSell;
public GameObject item_info;
public ScrollRect scrollDetailItem;
public ScrollRect scrollListItem;
[Header("Texts")]
public TextMeshProUGUI itemDetailNameText;
public TextMeshProUGUI itemMoneyText;
public NPCShopDetailPanel itemDescriptionText;
public TextMeshProUGUI itemsBuyAmountText;
public TextMeshProUGUI itemsBuyTotalMoneyText;
public TextMeshProUGUI itemsSellTotalMoneyText;
[Header("Tabs")]
public Transform tabButtonContainer;
public GameObject tabButtonPrefab;
public string tabButtonTextComponentName = "Text";
[Header("Item Display")]
public Transform itemContainer;
public GameObject itemPanelPrefab;
[Header("Main Panel Components")]
public Button closeShopButton;
[Header("Sprite")]
[SerializeField] private Sprite khung_item;
[Header("Buttons")]
[SerializeField] private Button m_btn_tab_buy;
[SerializeField] private Button m_btn_tab_sell;
[SerializeField] private Button m_btn_repair;
[SerializeField] private Button m_btn_major_overhaul;
[SerializeField] private Button m_btn_reduce;
[SerializeField] private Button m_btn_incre;
[SerializeField] private Button m_btn_max;
[SerializeField] private Button m_btn_buy;
[SerializeField] private Button m_btn_sell;
[SerializeField] private Button m_use_Item;
[SerializeField] private List<Button> itemReadySell = new List<Button>();
private List<GameObject> currentItemPanels = new List<GameObject>();
private List<GameObject> currentTabButtons = new List<GameObject>();
private readonly Dictionary<int, int> sellSlotToSourceSlot = new Dictionary<int, int>();
private List<int> tabPageMapping = new List<int>(); // Maps tab index to page index
private int currentTabIndex = 0;
private uint currentNPCID = 0;
private NPC_SELL_SERVICE? cachedSellService = null;
private GShopItem currentItem;
private NPCShopUIManager shopManager;
private int shopItemIndex;
private Color colorUnActive = new Color(1f, 1f, 1f, 0f);
private Color colorActive = new Color(1f, 1f, 1f, 1f);
private int buyCount = 1;
private const int BuyCountMin = 1;
private const string BuyUiEmptyValue = "0";
private const int BuyCountFallbackMax = 99;
/// <summary>Current NPC id for this shop session. Send SEVNPC_HELLO with this before buy.</summary>
public uint CurrentNPCID => currentNPCID;
public override void OnEnable()
{
base.OnEnable();
closeShopButton.onClick.AddListener(CloseShop);
if(m_btn_tab_buy != null)
{
m_btn_tab_buy.onClick.AddListener(OnClickTabButtonBuy);
}
if(m_btn_tab_sell != null)
{
m_btn_tab_sell.onClick.AddListener(OnClickTabButtonSell);
}
}
public override void OnDisable()
{
base.OnDisable();
closeShopButton.onClick.RemoveListener(CloseShop);
if(m_btn_tab_buy != null)
{
m_btn_tab_buy.onClick.RemoveListener(OnClickTabButtonBuy);
}
if(m_btn_tab_sell != null)
{
m_btn_tab_sell.onClick.RemoveListener(OnClickTabButtonSell);
}
}
public override void Start()
{
base.Start();
SetupEventListeners();
}
public void OpenNPCShop(uint npcID)
{
if (npcShopMainPanel == null)
{
Debug.LogError("NPCShopUIManager: npcShopMainPanel is null!");
return;
}
// Get NPC ID from CECUIManager
uint actualNPCID = npcID;
CECUIManager uiManager = CECUIManager.Instance;
if (uiManager != null)
{
int managerNPCID = uiManager.GetCurrentTargetNPCID();
if (managerNPCID > 0)
{
actualNPCID = (uint)managerNPCID;
}
}
currentNPCID = actualNPCID;
// Load sell service data
if (!LoadSellService(actualNPCID))
{
Debug.LogError($"NPCShopUIManager: Failed to load sell service for NPC {actualNPCID}");
return;
}
// // Show panel
// npcShopMainPanel.SetActive(true);
// Create tabs
CreateTabs();
// Select first tab
if (tabPageMapping.Count > 0)
{
OnTabSelected(0);
}
RefreshShopDisplay();
}
bool LoadSellService(uint npcID)
{
try
{
var elementDataMan = ElementDataManProvider.GetElementDataMan();
if (elementDataMan == null)
return false;
// Get NPC_ESSENCE
DATA_TYPE dataType = DATA_TYPE.DT_INVALID;
object npcData = elementDataMan.get_data_ptr(npcID, ID_SPACE.ID_SPACE_ESSENCE, ref dataType);
if (dataType != DATA_TYPE.DT_NPC_ESSENCE || npcData == null)
return false;
NPC_ESSENCE npcEssence = (NPC_ESSENCE)npcData;
uint sellServiceID = npcEssence.id_sell_service;
if (sellServiceID == 0)
return false;
// Get NPC_SELL_SERVICE
DATA_TYPE serviceDataType = DATA_TYPE.DT_INVALID;
object serviceData = elementDataMan.get_data_ptr(sellServiceID, ID_SPACE.ID_SPACE_ESSENCE, ref serviceDataType);
if (serviceDataType != DATA_TYPE.DT_NPC_SELL_SERVICE || serviceData == null)
return false;
cachedSellService = (NPC_SELL_SERVICE)serviceData;
return true;
}
catch (System.Exception ex)
{
Debug.LogError($"NPCShopUIManager: Exception loading sell service: {ex.Message}");
return false;
}
}
void CreateTabs()
{
ClearTabs();
if (currentNPCID == 0 || tabButtonContainer == null || tabButtonPrefab == null)
return;
if (!cachedSellService.HasValue || cachedSellService.Value.pages == null)
return;
var sellService = cachedSellService.Value;
tabPageMapping.Clear();
// Create tabs for each page that has items
for (int pageIndex = 0; pageIndex < sellService.pages.Length; pageIndex++)
{
var page = sellService.pages[pageIndex];
// Check if page has any items
bool hasItems = false;
if (page.goods != null)
{
foreach (var good in page.goods)
{
if (good.id != 0)
{
hasItems = true;
break;
}
}
}
if (!hasItems)
continue;
tabPageMapping.Add(pageIndex);
// Get page title
string pageTitle = ByteToStringUtils.UshortArrayToUnicodeString(page.page_title);
if (string.IsNullOrEmpty(pageTitle))
{
pageTitle = $"Page {pageIndex + 1}";
}
// Create tab button
GameObject tabButtonObj = Instantiate(tabButtonPrefab, tabButtonContainer);
tabButtonObj.name = $"Tab_Page_{pageIndex}";
tabButtonObj.SetActive(true);
// Get button component
Button tabButton = tabButtonObj.GetComponent<Button>();
if (tabButton == null)
tabButton = tabButtonObj.GetComponentInChildren<Button>();
// Get text component
TextMeshProUGUI tabText = tabButtonObj.GetComponent<TextMeshProUGUI>();
if (tabText == null)
{
Transform textTransform = tabButtonObj.transform.Find(tabButtonTextComponentName);
if (textTransform != null)
tabText = textTransform.GetComponent<TextMeshProUGUI>();
else
tabText = tabButtonObj.GetComponentInChildren<TextMeshProUGUI>();
}
if (tabText != null)
tabText.text = pageTitle;
// Setup click listener
if (tabButton != null)
{
int tabIndex = tabPageMapping.Count - 1; // Current tab index
tabButton.onClick.AddListener(() => OnTabSelected(tabIndex));
}
currentTabButtons.Add(tabButtonObj);
}
}
void ClearTabs()
{
foreach (GameObject tabButton in currentTabButtons)
{
if (tabButton != null)
Destroy(tabButton);
}
currentTabButtons.Clear();
tabPageMapping.Clear();
}
void OnTabSelected(int tabIndex)
{
if (tabIndex == currentTabIndex || tabIndex < 0 || tabIndex >= tabPageMapping.Count)
return;
currentTabIndex = tabIndex;
RefreshShopDisplay();
// Update tab button states
for (int i = 0; i < currentTabButtons.Count; i++)
{
if (currentTabButtons[i] != null)
{
Button tabButton = currentTabButtons[i].GetComponent<Button>();
if (tabButton == null)
tabButton = currentTabButtons[i].GetComponentInChildren<Button>();
if (tabButton != null)
{
tabButton.interactable = (i != tabIndex);
}
}
}
}
void RefreshShopDisplay()
{
// Clear existing items
ClearItems();
if (itemContainer == null || itemPanelPrefab == null)
return;
if (currentTabIndex < 0 || currentTabIndex >= tabPageMapping.Count)
return;
if (!cachedSellService.HasValue)
return;
int pageIndex = tabPageMapping[currentTabIndex];
var sellService = cachedSellService.Value;
if (pageIndex >= sellService.pages.Length)
return;
var page = sellService.pages[pageIndex];
// Create item panels
if (page.goods != null)
{
var elementDataMan = ElementDataManProvider.GetElementDataMan();
if (elementDataMan == null)
return;
DATA_TYPE itemDataTypeCheck = DATA_TYPE.DT_INVALID;
object itemDataCheck = elementDataMan.get_data_ptr(page.goods[0].id, ID_SPACE.ID_SPACE_ESSENCE, ref itemDataTypeCheck);
if (itemDataCheck == null)
return;
int[] goodIndexSortedByRemap = new int[page.goods.Length];
bool isRemaping = false;
// If 3C want to change shop behavior. Uncomment this code.
// if (itemDataTypeCheck == DATA_TYPE.DT_WEAPON_ESSENCE ||
// itemDataTypeCheck == DATA_TYPE.DT_ARMOR_ESSENCE ||
// itemDataTypeCheck == DATA_TYPE.DT_DECORATION_ESSENCE ||
// itemDataTypeCheck == DATA_TYPE.DT_PROJECTILE_ESSENCE)
// {
// isRemaping = true;
// int[] remapingGoodIndex = new int[page.goods.Length];
// for (int i = 0; i < page.goods.Length; i++)
// {
// //chnge the coord x to coord y and the viceversa with the max length of x is 8
// int newX = i % 8;
// int newY = i / 8;
// remapingGoodIndex[i] = newX * 4 + newY;
// }
// // Original good indices 0..n-1 ordered so remapingGoodIndex[i] is ascending (display order by remap slot).
// for (int j = 0; j < goodIndexSortedByRemap.Length; j++)
// goodIndexSortedByRemap[j] = j;
// Array.Sort(goodIndexSortedByRemap, (a, b) => remapingGoodIndex[a].CompareTo(remapingGoodIndex[b]));
// }
for (int i = 0; i < page.goods.Length; i++)
{
var good = page.goods[isRemaping? goodIndexSortedByRemap[i] : i];
if (good.id == 0)
continue;
// Get item essence
DATA_TYPE itemDataType = DATA_TYPE.DT_INVALID;
object itemData = elementDataMan.get_data_ptr(good.id, ID_SPACE.ID_SPACE_ESSENCE, ref itemDataType);
if (itemData == null)
continue;
// Create GShopItem
GShopItem shopItem = CreateShopItemFromGood(good, itemData, itemDataType);
// Create panel with absolute shop slot index (server validates npc_trade_item.index against full shop list).
int absoluteShopSlotIndex = GetAbsoluteShopSlotIndex(sellService, pageIndex, i);
CreateItemPanel(shopItem, absoluteShopSlotIndex);
// log the name and the position of the item
}
}
}
private static int GetAbsoluteShopSlotIndex(NPC_SELL_SERVICE sellService, int pageIndex, int localIndex)
{
if (pageIndex <= 0)
return localIndex;
int offset = 0;
int safePageIndex = Mathf.Clamp(pageIndex, 0, sellService.pages?.Length ?? 0);
for (int p = 0; p < safePageIndex; p++)
{
var goods = sellService.pages[p].goods;
if (goods != null)
offset += goods.Length;
}
return offset + localIndex;
}
GShopItem CreateShopItemFromGood(NPC_SELL_SERVICE.SellGood good, object itemData, DATA_TYPE itemDataType)
{
GShopItem shopItem = new GShopItem();
shopItem.id = good.id;
shopItem.num = 1;
// Initialize buy array with at least one option
shopItem.buy = new GShopBuyOption[4]; // GShopItem supports up to 4 buy options
// Get item name and price based on type
string itemName = "Unknown";
int shopPrice = 0;
switch (itemDataType)
{
case DATA_TYPE.DT_WEAPON_ESSENCE:
var weaponEssence = (WEAPON_ESSENCE)itemData;
itemName = weaponEssence.Name;
shopPrice = weaponEssence.shop_price;
break;
case DATA_TYPE.DT_ARMOR_ESSENCE:
var armorEssence = (ARMOR_ESSENCE)itemData;
itemName = armorEssence.Name;
shopPrice = armorEssence.shop_price;
break;
case DATA_TYPE.DT_MEDICINE_ESSENCE:
var medicineEssence = (MEDICINE_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(medicineEssence.name);
shopPrice = medicineEssence.shop_price;
break;
case DATA_TYPE.DT_DECORATION_ESSENCE:
var decorationEssence = (DECORATION_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(decorationEssence.name);
shopPrice = decorationEssence.shop_price;
break;
case DATA_TYPE.DT_STONE_ESSENCE:
var stoneEssence = (STONE_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(stoneEssence.name);
shopPrice = stoneEssence.shop_price;
break;
case DATA_TYPE.DT_MATERIAL_ESSENCE:
var materialEssence = (MATERIAL_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(materialEssence.name);
shopPrice = materialEssence.shop_price;
break;
case DATA_TYPE.DT_PET_EGG_ESSENCE:
var petEggEssence = (PET_EGG_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(petEggEssence.name);
shopPrice = petEggEssence.shop_price;
break;
case DATA_TYPE.DT_PET_FOOD_ESSENCE:
var petFoodEssence = (PET_FOOD_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(petFoodEssence.name);
shopPrice = petFoodEssence.shop_price;
break;
case DATA_TYPE.DT_PROJECTILE_ESSENCE:
var projectileEssence = (PROJECTILE_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(projectileEssence.name);
shopPrice = projectileEssence.shop_price;
break;
case DATA_TYPE.DT_REVIVESCROLL_ESSENCE:
var revivescrollEssence = (REVIVESCROLL_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(revivescrollEssence.name);
shopPrice = revivescrollEssence.shop_price;
break;
case DATA_TYPE.DT_TASKNORMALMATTER_ESSENCE:
var tasknormalmatterEssence = (TASKNORMALMATTER_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(tasknormalmatterEssence.name);
shopPrice = tasknormalmatterEssence.shop_price;
break;
case DATA_TYPE.DT_DAMAGERUNE_ESSENCE:
var damageruneEssence = (DAMAGERUNE_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(damageruneEssence.name);
shopPrice = damageruneEssence.shop_price;
break;
case DATA_TYPE.DT_ARMORRUNE_ESSENCE:
var armorruneEssence = (ARMORRUNE_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(armorruneEssence.name);
shopPrice = armorruneEssence.shop_price;
break;
case DATA_TYPE.DT_SKILLTOME_ESSENCE:
var skilltomeEssence = (SKILLTOME_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(skilltomeEssence.name);
shopPrice = skilltomeEssence.shop_price;
break;
case DATA_TYPE.DT_FLYSWORD_ESSENCE:
var flyswordEssence = (FLYSWORD_ESSENCE)itemData;
itemName = flyswordEssence.Name;
shopPrice = flyswordEssence.shop_price;
break;
case DATA_TYPE.DT_WINGMANWING_ESSENCE:
var wingmanwingEssence = (WINGMANWING_ESSENCE)itemData;
itemName = wingmanwingEssence.Name;
shopPrice = wingmanwingEssence.shop_price;
break;
case DATA_TYPE.DT_TOWNSCROLL_ESSENCE:
var townscrollEssence = (TOWNSCROLL_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(townscrollEssence.name);
shopPrice = townscrollEssence.shop_price;
break;
case DATA_TYPE.DT_UNIONSCROLL_ESSENCE:
var unionscrollEssence = (UNIONSCROLL_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(unionscrollEssence.name);
shopPrice = unionscrollEssence.shop_price;
break;
case DATA_TYPE.DT_ELEMENT_ESSENCE:
var elementEssence = (ELEMENT_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(elementEssence.name);
shopPrice = elementEssence.shop_price;
break;
case DATA_TYPE.DT_TOSSMATTER_ESSENCE:
var tossmatterEssence = (TOSSMATTER_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(tossmatterEssence.name);
shopPrice = tossmatterEssence.shop_price;
break;
case DATA_TYPE.DT_FASHION_ESSENCE:
var fashionEssence = (FASHION_ESSENCE)itemData;
itemName = fashionEssence.Name;
shopPrice = fashionEssence.shop_price;
break;
case DATA_TYPE.DT_FACETICKET_ESSENCE:
var faceticketEssence = (FACETICKET_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(faceticketEssence.name);
shopPrice = faceticketEssence.shop_price;
break;
case DATA_TYPE.DT_FACEPILL_ESSENCE:
var facepillEssence = (FACEPILL_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(facepillEssence.name);
shopPrice = facepillEssence.shop_price;
break;
case DATA_TYPE.DT_PET_FACETICKET_ESSENCE:
var petFaceticketEssence = (PET_FACETICKET_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(petFaceticketEssence.name);
shopPrice = petFaceticketEssence.shop_price;
break;
case DATA_TYPE.DT_FIREWORKS_ESSENCE:
var fireworksEssence = (FIREWORKS_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(fireworksEssence.name);
shopPrice = fireworksEssence.shop_price;
break;
case DATA_TYPE.DT_WAR_TANKCALLIN_ESSENCE:
var warTankcallinEssence = (WAR_TANKCALLIN_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(warTankcallinEssence.name);
shopPrice = warTankcallinEssence.shop_price;
break;
case DATA_TYPE.DT_SKILLMATTER_ESSENCE:
var skillmatterEssence = (SKILLMATTER_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(skillmatterEssence.name);
shopPrice = skillmatterEssence.shop_price;
break;
case DATA_TYPE.DT_REFINE_TICKET_ESSENCE:
var refineTicketEssence = (REFINE_TICKET_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(refineTicketEssence.name);
shopPrice = refineTicketEssence.shop_price;
break;
case DATA_TYPE.DT_DESTROYING_ESSENCE:
var destroyingEssence = (DESTROYING_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(destroyingEssence.name);
shopPrice = destroyingEssence.shop_price;
break;
case DATA_TYPE.DT_BIBLE_ESSENCE:
var bibleEssence = (BIBLE_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(bibleEssence.name);
shopPrice = bibleEssence.shop_price;
break;
case DATA_TYPE.DT_SPEAKER_ESSENCE:
var speakerEssence = (SPEAKER_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(speakerEssence.name);
shopPrice = speakerEssence.shop_price;
break;
case DATA_TYPE.DT_AUTOHP_ESSENCE:
var autohpEssence = (AUTOHP_ESSENCE)itemData;
itemName = autohpEssence.Name;
shopPrice = autohpEssence.shop_price;
break;
case DATA_TYPE.DT_AUTOMP_ESSENCE:
var autompEssence = (AUTOMP_ESSENCE)itemData;
itemName = autompEssence.Name;
shopPrice = autompEssence.shop_price;
break;
case DATA_TYPE.DT_DOUBLE_EXP_ESSENCE:
var doubleExpEssence = (DOUBLE_EXP_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(doubleExpEssence.name);
shopPrice = doubleExpEssence.shop_price;
break;
case DATA_TYPE.DT_TRANSMITSCROLL_ESSENCE:
var transmitscrollEssence = (TRANSMITSCROLL_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(transmitscrollEssence.name);
shopPrice = transmitscrollEssence.shop_price;
break;
case DATA_TYPE.DT_DYE_TICKET_ESSENCE:
var dyeTicketEssence = (DYE_TICKET_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(dyeTicketEssence.name);
shopPrice = dyeTicketEssence.shop_price;
break;
case DATA_TYPE.DT_GOBLIN_ESSENCE:
var goblinEssence = (GOBLIN_ESSENCE)itemData;
itemName = goblinEssence.Name;
shopPrice = goblinEssence.shop_price;
break;
case DATA_TYPE.DT_GOBLIN_EQUIP_ESSENCE:
var goblinEquipEssence = (GOBLIN_EQUIP_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(goblinEquipEssence.name);
shopPrice = goblinEquipEssence.shop_price;
break;
case DATA_TYPE.DT_GOBLIN_EXPPILL_ESSENCE:
var goblinExppillEssence = (GOBLIN_EXPPILL_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(goblinExppillEssence.name);
shopPrice = goblinExppillEssence.shop_price;
break;
case DATA_TYPE.DT_SELL_CERTIFICATE_ESSENCE:
var sellCertificateEssence = (SELL_CERTIFICATE_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(sellCertificateEssence.name);
shopPrice = sellCertificateEssence.shop_price;
break;
case DATA_TYPE.DT_TARGET_ITEM_ESSENCE:
var targetItemEssence = (TARGET_ITEM_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(targetItemEssence.name);
shopPrice = targetItemEssence.shop_price;
break;
case DATA_TYPE.DT_LOOK_INFO_ESSENCE:
var lookInfoEssence = (LOOK_INFO_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(lookInfoEssence.name);
shopPrice = lookInfoEssence.shop_price;
break;
case DATA_TYPE.DT_INC_SKILL_ABILITY_ESSENCE:
var incSkillAbilityEssence = (INC_SKILL_ABILITY_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(incSkillAbilityEssence.name);
shopPrice = incSkillAbilityEssence.shop_price;
break;
case DATA_TYPE.DT_WEDDING_BOOKCARD_ESSENCE:
var weddingBookcardEssence = (WEDDING_BOOKCARD_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(weddingBookcardEssence.name);
shopPrice = weddingBookcardEssence.shop_price;
break;
case DATA_TYPE.DT_WEDDING_INVITECARD_ESSENCE:
var weddingInvitecardEssence = (WEDDING_INVITECARD_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(weddingInvitecardEssence.name);
shopPrice = weddingInvitecardEssence.shop_price;
break;
case DATA_TYPE.DT_SHARPENER_ESSENCE:
var sharpenerEssence = (SHARPENER_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(sharpenerEssence.name);
shopPrice = sharpenerEssence.shop_price;
break;
case DATA_TYPE.DT_FACTION_MATERIAL_ESSENCE:
var factionMaterialEssence = (FACTION_MATERIAL_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(factionMaterialEssence.name);
shopPrice = factionMaterialEssence.shop_price;
break;
case DATA_TYPE.DT_CONGREGATE_ESSENCE:
var congregateEssence = (CONGREGATE_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(congregateEssence.name);
shopPrice = congregateEssence.shop_price;
break;
case DATA_TYPE.DT_FORCE_TOKEN_ESSENCE:
var forceTokenEssence = (FORCE_TOKEN_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(forceTokenEssence.name);
shopPrice = forceTokenEssence.shop_price;
break;
case DATA_TYPE.DT_DYNSKILLEQUIP_ESSENCE:
var dynskillequipEssence = (DYNSKILLEQUIP_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(dynskillequipEssence.name);
shopPrice = dynskillequipEssence.shop_price;
break;
case DATA_TYPE.DT_MONEY_CONVERTIBLE_ESSENCE:
var moneyConvertibleEssence = (MONEY_CONVERTIBLE_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(moneyConvertibleEssence.name);
shopPrice = moneyConvertibleEssence.shop_price;
break;
case DATA_TYPE.DT_MONSTER_SPIRIT_ESSENCE:
var monsterSpiritEssence = (MONSTER_SPIRIT_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(monsterSpiritEssence.name);
shopPrice = monsterSpiritEssence.shop_price;
break;
case DATA_TYPE.DT_POKER_DICE_ESSENCE:
var pokerDiceEssence = (POKER_DICE_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(pokerDiceEssence.name);
shopPrice = pokerDiceEssence.shop_price;
break;
case DATA_TYPE.DT_POKER_ESSENCE:
var pokerEssence = (POKER_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(pokerEssence.name);
shopPrice = pokerEssence.shop_price;
break;
case DATA_TYPE.DT_SHOP_TOKEN_ESSENCE:
var shopTokenEssence = (SHOP_TOKEN_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(shopTokenEssence.name);
shopPrice = shopTokenEssence.shop_price;
break;
case DATA_TYPE.DT_UNIVERSAL_TOKEN_ESSENCE:
var universalTokenEssence = (UNIVERSAL_TOKEN_ESSENCE)itemData;
itemName = ByteToStringUtils.UshortArrayToUnicodeString(universalTokenEssence.name);
shopPrice = universalTokenEssence.shop_price;
break;
default:
itemName = $"Item_{good.id}_{itemDataType}";
break;
}
// This is modified item price after tax of server.
float fPriceScale = 1.0f;
int idServiceNPC = EC_Game.GetGameRun().GetHostPlayer().GetCurServiceNPC();
CECNPC pNPC = EC_Game.GetGameRun().GetWorld().GetNPCMan().GetNPC(idServiceNPC);
CECNPCServer pServer = (CECNPCServer)pNPC;
if (pServer)
{
fPriceScale = (1.0f + pServer.GetTaxRate()) * pServer.GetPriceScale();
}
shopItem.name = itemName;
shopPrice = (int)Math.Round(shopPrice * fPriceScale, MidpointRounding.AwayFromZero);
if(shopPrice > 1000)
{
shopPrice = (shopPrice +99) /100 * 100;
}
else if (shopPrice > 100)
{
shopPrice = (shopPrice +9) /10 * 10;
}
// Set price from contribution cost or shop price
if (good.contrib_cost > 0)
{
shopItem.buy[0].price = (uint)good.contrib_cost;
shopItem.buy[0].type = 0; // 0 = permanent
}
else if (shopPrice > 0)
{
shopItem.buy[0].price = (uint)shopPrice;
shopItem.buy[0].type = 0; // 0 = permanent
}
else
{
// Initialize with invalid type if no price
shopItem.buy[0].type = -1; // -1 = invalid
shopItem.buy[0].price = 0;
}
return shopItem;
}
void CreateItemPanel(GShopItem item, int shopItemIndex)
{
if (itemPanelPrefab == null || itemContainer == null)
return;
if(scrollListItem != null)
{
scrollListItem.verticalNormalizedPosition = 1f;
}
if (scrollDetailItem != null)
{
scrollDetailItem.verticalNormalizedPosition = 1f;
}
if (itemDetailNameText != null)
{
itemDetailNameText.text = string.Empty;
}
if (itemMoneyText != null)
{
itemMoneyText.text = string.Empty;
}
if (itemDescriptionText != null)
{
itemDescriptionText.ClearText();
}
if (itemIconImage != null)
{
itemIconImage.sprite = khung_item;
}
if (itemsBuyTotalMoneyText != null)
{
itemsBuyTotalMoneyText.text = string.Empty;
}
GameObject itemPanel = Instantiate(itemPanelPrefab, itemContainer);
itemPanel.SetActive(true);
NPCShopItemPanel itemPanelScript = itemPanel.GetComponent<NPCShopItemPanel>();
if (itemPanelScript == null)
itemPanelScript = itemPanel.GetComponentInChildren<NPCShopItemPanel>();
if (itemPanelScript != null)
{
itemPanelScript.SetupItem(item, this, shopItemIndex);
}
else
{
Debug.LogError($"[NPCShopUIManager] NPCShopItemPanel component not found on prefab! Prefab name: {itemPanelPrefab.name}");
}
currentItemPanels.Add(itemPanel);
}
void ClearItems()
{
foreach (GameObject panel in currentItemPanels)
{
if (panel != null)
Destroy(panel);
}
currentItemPanels.Clear();
}
public void ShowItemDetail(GShopItem item, int shopItemIndex)
{
if (item.id == 0)
return;
SetupDetailPanel(item, this, shopItemIndex);
}
public override void CloseDialogue()
{
base.CloseDialogue();
if (itemDetailNameText != null)
{
itemDetailNameText.text = string.Empty;
}
if (itemMoneyText != null)
{
itemMoneyText.text = string.Empty;
}
if (itemDescriptionText != null)
{
itemDescriptionText.ClearText();
}
if (itemIconImage != null)
{
itemIconImage.sprite = khung_item;
}
if(itemsBuyTotalMoneyText != null)
{
itemsBuyTotalMoneyText.text = string.Empty;
}
contentMidBuy.SetActive(true);
contentRight.SetActive(true);
contentMidSell.SetActive(false);
m_btn_tab_buy.image.color = colorActive;
m_btn_tab_sell.image.color = colorUnActive;
EC_Game.GetGameRun().GetUIManager().GetInGameUIMan().EndNPCService();
}
public void CloseShop()
{
ResetSellReadySlots();
UpdateSellTotalPriceText();
CloseDialogue();
if (npcShopMainPanel != null)
npcShopMainPanel.SetActive(false);
ClearTabs();
ClearItems();
cachedSellService = null;
currentNPCID = 0;
}
void SetupEventListeners()
{
if (m_btn_buy != null)
m_btn_buy.onClick.AddListener(OnBuyButtonClicked);
if (m_btn_reduce != null)
m_btn_reduce.onClick.AddListener(OnReduceBuyCountClicked);
if (m_btn_incre != null)
m_btn_incre.onClick.AddListener(OnIncreaseBuyCountClicked);
if (m_btn_max != null)
m_btn_max.onClick.AddListener(OnMaxBuyCountClicked);
if (m_btn_sell != null)
m_btn_sell.onClick.AddListener(OnSellButtonClicked);
if (m_use_Item != null)
m_use_Item.onClick.AddListener(OnUseItemClicked);
SetupSellReadySlotListeners();
}
public void SetupDetailPanel(GShopItem item, NPCShopUIManager manager, int index)
{
currentItem = item;
shopManager = manager;
shopItemIndex = index;
UpdateDisplay();
}
void UpdateDisplay()
{
if (currentItem.id == 0)
{
Debug.LogWarning("[NPCShopDetailPanel] Current item ID is 0, skipping display update");
ResetBuyUi();
return;
}
if(itemDetailNameText != null)
{
itemDetailNameText.text = currentItem.name;
}
if (itemMoneyText != null)
{
uint money = GetCurrentUnitPrice();
if(money > 0)
itemMoneyText.text = $"Giá {money.ToString()} Ngân lượng";
else
itemMoneyText.text = BuyUiEmptyValue;
}
if (scrollDetailItem != null)
{
scrollDetailItem.verticalNormalizedPosition = 1f;
}
buyCount = BuyCountMin;
UpdateBuyPriceTexts();
// Set item description
if (itemDescriptionText != null)
{
itemDescriptionText.SetupDetailPanel(currentItem, this, shopItemIndex);
}
// if (itemPriceText != null)
// itemPriceText.text = price > 0 ? $"Price: {price}" : "Price: N/A";
// Load icon
if (itemIconImage != null)
{
LoadItemIcon(itemIconImage, (int)currentItem.id);
}
}
private uint GetCurrentUnitPrice()
{
if (currentItem.id == 0)
return 0;
if (currentItem.buy == null || currentItem.buy.Length == 0)
return 0;
for (int i = 0; i < currentItem.buy.Length; i++)
{
if (currentItem.buy[i].price > 0)
return currentItem.buy[i].price;
}
return 0;
}
private int GetMaxBuyCountForCurrentItem()
{
if (currentItem.id == 0)
return BuyCountMin;
int tid = (int)currentItem.id;
if (tid <= 0)
return BuyCountMin;
int pileLimit = EC_IvtrItem.GetPileLimit(tid);
// In some setups the lightweight lookup returns 1 until the local DB data is loaded.
// If it says "1", try to resolve pile limit from a temporary item instance.
if (pileLimit <= BuyCountMin)
{
try
{
var tmp = EC_IvtrItem.CreateItem(tid, 0, 1);
if (tmp != null)
{
tmp.GetDetailDataFromLocal();
int instLimit = tmp.GetPileLimitInstance();
if (instLimit > pileLimit)
pileLimit = instLimit;
}
}
catch
{
// Keep the original pileLimit.
}
}
if (pileLimit < BuyCountMin)
pileLimit = BuyCountMin;
return pileLimit;
}
private void SetBuyCount(int newCount)
{
int max = GetMaxBuyCountForCurrentItem();
if (newCount < BuyCountMin) newCount = BuyCountMin;
if (newCount > max) newCount = max;
buyCount = newCount;
UpdateBuyPriceTexts();
}
private void UpdateBuyPriceTexts()
{
// Only update buy UI while buy panel is active.
if (contentMidBuy != null && !contentMidBuy.activeInHierarchy)
return;
if (currentItem.id == 0)
{
ResetBuyUi();
return;
}
uint unitPrice = GetCurrentUnitPrice();
int max = GetMaxBuyCountForCurrentItem();
if (buyCount < BuyCountMin) buyCount = BuyCountMin;
if (buyCount > max) buyCount = max;
// Quantity controls:
// - Non-stackable items have max == BuyCountMin, so keep +/-/max disabled.
// - Stackable items re-enable controls automatically.
bool canChangeQty = max > BuyCountMin;
if (m_btn_reduce != null) m_btn_reduce.interactable = canChangeQty && buyCount > BuyCountMin;
if (m_btn_incre != null) m_btn_incre.interactable = canChangeQty && buyCount < max;
if (m_btn_max != null) m_btn_max.interactable = canChangeQty && buyCount < max;
long total = unitPrice > 0 ? (long)unitPrice * buyCount : 0;
if (total < 0) total = 0;
if (itemsBuyAmountText != null)
{
itemsBuyAmountText.text = buyCount.ToString();
}
if (itemsBuyTotalMoneyText != null)
{
uint playerMoney = 0;
try
{
var host = CECGameRun.Instance?.GetHostPlayer();
if (host != null)
{
playerMoney = host.GetMoneyAmount();
}
}
catch
{
playerMoney = 0;
}
string totalText = total.ToString();
string playerMoneyStr = playerMoney.ToString();
itemsBuyTotalMoneyText.text = total > playerMoney
? $"<color=red>{total}</color>/{playerMoney}"
: $"{total}/{playerMoney}";
}
if (m_btn_buy != null)
{
m_btn_buy.interactable = unitPrice > 0 && buyCount >= BuyCountMin;
}
}
private void ResetBuyUi()
{
buyCount = BuyCountMin;
if (itemsBuyAmountText != null)
itemsBuyAmountText.text = BuyUiEmptyValue;
if (itemsBuyTotalMoneyText != null)
itemsBuyTotalMoneyText.text = BuyUiEmptyValue;
if (m_btn_buy != null)
m_btn_buy.interactable = false;
if (m_btn_reduce != null) m_btn_reduce.interactable = false;
if (m_btn_incre != null) m_btn_incre.interactable = false;
if (m_btn_max != null) m_btn_max.interactable = false;
}
private void OnReduceBuyCountClicked()
{
if (currentItem.id == 0)
return;
SetBuyCount(buyCount - 1);
}
private void OnIncreaseBuyCountClicked()
{
if (currentItem.id == 0)
return;
SetBuyCount(buyCount + 1);
}
private void OnMaxBuyCountClicked()
{
if (currentItem.id == 0)
return;
SetBuyCount(GetMaxBuyCountForCurrentItem());
}
void LoadItemIcon(Image iconImage, int itemId)
{
if (itemId <= 0 || iconImage == null)
return;
// Ensure the Image component is enabled and properly configured
iconImage.enabled = true;
iconImage.preserveAspect = true;
iconImage.type = Image.Type.Simple;
// Use the existing icon loading system from EC_IvtrItemUtils
Sprite iconSprite = EC_IvtrItemUtils.Instance.ResolveItemIconSprite(itemId);
if (iconSprite != null)
{
iconImage.sprite = iconSprite;
iconImage.color = Color.white;
}
else
{
iconImage.sprite = null;
}
}
string GetItemDescription(int itemId)
{
// First check if description is already in the item data
if (!string.IsNullOrEmpty(currentItem.desc))
{
return currentItem.desc;
}
// Try to use EC_IvtrEquip description for equipment items (like EC_InventoryUI does)
try
{
// Create EC_IvtrEquip for equipment items - this will work for weapons and armor
EC_IvtrEquip equipment = (EC_IvtrEquip)EC_IvtrItem.CreateItem(itemId, 0, 1);
// For NPC shop items, we typically have static data only
// Try to get description using GetNormalDesc() which works with base stats
string equipDesc = equipment.GetDesc();
if (!string.IsNullOrEmpty(equipDesc))
{
// Replace C++ style "\r" line separators with real newlines for TMP
return equipDesc.Replace("\\r", "\n");
}
}
catch (System.Exception ex)
{
// If EC_IvtrEquip fails, fall through to EC_IvtrItem method
Debug.LogWarning($"[NPCShopDetailPanel] Failed to get equipment description for item {itemId}: {ex.Message}");
}
// Fallback: build description using EC_IvtrItem (for non-equipment items)
try
{
EC_IvtrItem item = EC_IvtrItem.CreateItem(itemId, 0, 1);
if (item != null)
{
// For NPC shop, we typically have static data only, so load from local DB
item.GetDetailDataFromLocal();
string description = item.GetDesc();
if (!string.IsNullOrEmpty(description))
{
return description.Replace("\\r", "\n");
}
}
}
catch (System.Exception ex)
{
Debug.LogWarning($"[NPCShopDetailPanel] Failed to get description for item {itemId}: {ex.Message}");
}
return "No description available.";
}
void OnBuyButtonClicked()
{
if (currentItem.id == 0)
{
Debug.LogWarning("[NPCShopDetailPanel] Cannot buy item with ID 0");
return;
}
// Get price from item
uint price = GetCurrentUnitPrice();
if (price == 0)
{
Debug.LogWarning($"[NPCShopDetailPanel] Cannot buy item {currentItem.id} with invalid price");
return;
}
int max = GetMaxBuyCountForCurrentItem();
if (buyCount < BuyCountMin) buyCount = BuyCountMin;
if (buyCount > max) buyCount = max;
// Server requires SEVNPC_HELLO with NPC id before buy, and the correct shop slot index
if (shopManager != null && shopManager.CurrentNPCID != 0)
UnityGameSession.c2s_CmdNPCSevHello((int)shopManager.CurrentNPCID);
// Create npc_trade_item: tid = template ID, index = shop slot (server validates this), count = quantity.
// For this server, multiple entries with the same index are rejected (ERROR_MESSAGE 15),
// so we must send a single entry and put the desired quantity in npc_trade_item.count.
npc_trade_item[] items = new npc_trade_item[1];
items[0] = new npc_trade_item
{
tid = (int)currentItem.id,
index = (uint)shopItemIndex,
count = (uint)buyCount
};
UnityGameSession.c2s_CmdNPCSevBuy(items.Length, items);
Debug.Log($"[NPCShopDetailPanel] Sent buy command for item {currentItem.id}, unitPrice {price}, count {buyCount}");
}
private void OnSellButtonClicked()
{
if (sellSlotToSourceSlot == null || sellSlotToSourceSlot.Count == 0)
{
Debug.LogWarning("[NPCShopUIManager] No items staged for selling");
return;
}
if (CurrentNPCID != 0)
UnityGameSession.c2s_CmdNPCSevHello((int)CurrentNPCID);
var host = CECGameRun.Instance?.GetHostPlayer();
var inv = host?.GetInventory(0);
if (inv == null)
{
Debug.LogWarning("[NPCShopUIManager] Inventory not available; cannot sell");
return;
}
long totalSellValue = 0;
var itemsToSell = new List<npc_sell_item>(sellSlotToSourceSlot.Count);
foreach (var pair in sellSlotToSourceSlot)
{
int sourceSlot = pair.Value;
if (sourceSlot < 0 || sourceSlot >= inv.GetSize())
continue;
var item = inv.GetItem(sourceSlot, false);
if (item == null || item.m_iCount <= 0)
continue;
if (!item.IsSellable())
{
Debug.LogWarning($"[NPCShopUIManager] Item {item.m_tid} in slot {sourceSlot} is not sellable");
continue;
}
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,
index = (uint)sourceSlot,
count = (uint)item.m_iCount,
price = price
});
}
if (itemsToSell.Count == 0)
{
Debug.LogWarning("[NPCShopUIManager] No valid items to sell after filtering");
return;
}
UnityGameSession.c2s_CmdNPCSevSell(itemsToSell.Count, itemsToSell.ToArray());
Debug.Log($"[NPCShopUIManager] Sent sell command for {itemsToSell.Count} item(s) to NPC {CurrentNPCID}");
// 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();
var inventoryUI = FindFirstObjectByType<EC_InventoryUI>();
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<EC_InventoryUI>();
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))
return;
if (itemReadySell == null || itemReadySell.Count == 0)
{
return;
}
foreach(var pair in sellSlotToSourceSlot)
{
if(pair.Value == selectedSlot)
{
return;
}
}
int targetSellSlotIndex = GetFirstEmptySellSlotIndex();
if(targetSellSlotIndex < 0)
{
Debug.LogWarning("[NPCShopDetailPanel] No empty sell slot available");
return;
}
var sellSlotBtn = itemReadySell[targetSellSlotIndex];
var sellSlotBtnImg = GetButtonImage(sellSlotBtn);
if (sellSlotBtnImg == null)
return;
Sprite icon = EC_IvtrItemUtils.Instance.ResolveItemIconSprite((int)selectedItem.m_tid);
if (icon == null)
return;
if (icon == null)
return;
sellSlotBtnImg.sprite = icon;
sellSlotBtnImg.enabled = true;
sellSlotBtnImg.color = Color.white;
sellSlotBtnImg.preserveAspect = true;
sellSlotBtnImg.type = Image.Type.Simple;
Button sourceButton = FindInventoryButtonBySlot(selectedSlot);
SetInventorySlotGray(sourceButton, true);
sellSlotToSourceSlot[targetSellSlotIndex] = selectedSlot;
item_info.SetActive(false);
UpdateSellTotalPriceText();
}
private Image GetButtonImage(Button button)
{
if(button == null)
return null;
var childImage = button.GetComponentsInChildren<Image>(true);
if (childImage != null)
{
for(int i=0; i<childImage.Length; i++)
{
if (childImage[i] != null && childImage[i].gameObject != button.gameObject)
return childImage[i];
}
}
return button.GetComponent<Image>();
}
private int GetFirstEmptySellSlotIndex()
{
if(itemReadySell == null)
return -1;
for(int i=0; i<itemReadySell.Count; i++)
{
if(!sellSlotToSourceSlot.ContainsKey(i))
{
return i;
}
}
return -1;
}
private void ResetSellReadySlots()
{
if (itemReadySell != null)
{
for(int i=0; i<itemReadySell.Count; i++)
{
var image = GetButtonImage(itemReadySell[i]);
if(image == null)
continue;
image.sprite = khung_item;
image.enabled = true;
image.color = Color.white;
image.preserveAspect = true;
image.type = Image.Type.Simple;
}
}
foreach (var pair in sellSlotToSourceSlot)
{
var sourceBtn = FindInventoryButtonBySlot(pair.Value);
SetInventorySlotGray(sourceBtn, false);
}
sellSlotToSourceSlot.Clear();
UpdateSellTotalPriceText();
}
private bool TryGetSelectedInventoryItem(out EC_IvtrItem item, out int slot)
{
item = null;
slot = -1;
var inventoryUI = FindFirstObjectByType<EC_InventoryUI>();
if(inventoryUI == null)
{
return false;
}
var type = typeof(EC_InventoryUI);
var packageField = type.GetField("currentSelectedPackage", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
var slotField = type.GetField("currentSelectedSlot", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
var itemField = type.GetField("currentSelectedItem", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
if (packageField == null || slotField == null || itemField == null)
{
return false;
}
var selectedPackage = (byte)packageField.GetValue(inventoryUI);
if (selectedPackage != 0)
return false;
slot = (int)slotField.GetValue(inventoryUI);
item = itemField.GetValue(inventoryUI) as EC_IvtrItem;
return item != null && slot >= 0;
}
private Button FindInventoryButtonBySlot(int slot)
{
var inventoryUI = FindFirstObjectByType<EC_InventoryUI>();
if (inventoryUI == null)
return null;
var field = typeof(EC_InventoryUI)
.GetField("inventoryPackButtons",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
if (field == null)
return null;
var list = field.GetValue(inventoryUI) as List<Button>;
if (list == null || slot < 0 || slot >= list.Count)
return null;
return list[slot];
}
private void SetInventorySlotGray(Button btn, bool gray)
{
if (btn == null)
return;
var img = btn.GetComponent<Image>();
if (img == null)
return;
img.color = gray
? new Color(0.3f, 0.3f, 0.3f, 1f)
: Color.white;
}
void OnDestroy()
{
ClearTabs();
ClearItems();
ResetSellReadySlots();
CleanupSellReadySlotListeners();
if (m_btn_buy != null)
m_btn_buy.onClick.RemoveListener(OnBuyButtonClicked);
if (m_btn_reduce != null)
m_btn_reduce.onClick.RemoveListener(OnReduceBuyCountClicked);
if (m_btn_incre != null)
m_btn_incre.onClick.RemoveListener(OnIncreaseBuyCountClicked);
if (m_btn_max != null)
m_btn_max.onClick.RemoveListener(OnMaxBuyCountClicked);
if (m_btn_sell != null)
m_btn_sell.onClick.RemoveListener(OnSellButtonClicked);
if(m_use_Item != null)
m_use_Item.onClick.RemoveListener(OnUseItemClicked);
}
private void SetupSellReadySlotListeners()
{
if (itemReadySell == null)
return;
for (int i = 0; i < itemReadySell.Count; i++)
{
var btn = itemReadySell[i];
if(btn == null)
continue;
int capturedIndex = i;
btn.onClick.RemoveAllListeners();
btn.onClick.AddListener(() => OnSellReadySlotClicked(capturedIndex));
}
}
private void CleanupSellReadySlotListeners()
{
if (itemReadySell == null)
return;
for(int i = 0; i < itemReadySell.Count; i++)
{
if (itemReadySell[i] != null)
{
itemReadySell[i].onClick.RemoveAllListeners();
}
}
}
private void OnSellReadySlotClicked(int sellSlotIndex)
{
if(!sellSlotToSourceSlot.TryGetValue(sellSlotIndex, out int sourceSlot))
{
return;
}
var sourceBtn = FindInventoryButtonBySlot(sourceSlot);
SetInventorySlotGray(sourceBtn, false);
var sellBtn = itemReadySell[sellSlotIndex];
var sellImg = GetButtonImage(sellBtn);
if(sellImg != null)
{
sellImg.sprite = khung_item;
sellImg.enabled = true;
sellImg.color = Color.white;
sellImg.preserveAspect = true;
sellImg.type = Image.Type.Simple;
}
sellSlotToSourceSlot.Remove(sellSlotIndex);
UpdateSellTotalPriceText();
}
private void OnClickTabButtonBuy()
{
m_btn_tab_buy.image.color = colorActive;
m_btn_tab_sell.image.color = colorUnActive;
contentMidBuy.SetActive(true);
contentRight.SetActive(true);
contentMidSell.SetActive(false);
UpdateBuyPriceTexts();
}
private void OnClickTabButtonSell()
{
m_btn_tab_buy.image.color = colorUnActive;
m_btn_tab_sell.image.color = colorActive;
contentMidBuy.SetActive(false);
contentRight.SetActive(false);
contentMidSell.SetActive(true);
UpdateSellTotalPriceText();
}
private void UpdateSellTotalPriceText()
{
long totalSellValue = 0;
int stageCount = 0;
try
{
var host = CECGameRun.Instance?.GetHostPlayer();
var inv = host?.GetInventory(0);
if (inv == null || sellSlotToSourceSlot == null)
{
if (itemsSellTotalMoneyText != null)
itemsSellTotalMoneyText.text = BuyUiEmptyValue;
if (m_btn_sell != null)
m_btn_sell.interactable = false;
return;
}
foreach (var pair in sellSlotToSourceSlot)
{
int sourceSlot = pair.Value;
if (sourceSlot < 0 || sourceSlot >= inv.GetSize())
continue;
var item = inv.GetItem(sourceSlot, false);
if (item == null || item.m_iCount <= 0)
continue;
if (!item.IsSellable())
continue;
try
{
item.GetDetailDataFromLocal();
}
catch
{
}
int unitPrice = item.GetScaledPrice();
long itemTotal = (long)unitPrice * item.m_iCount;
if (itemTotal > 0)
{
totalSellValue += itemTotal;
stageCount++;
}
}
}
catch(Exception ex)
{
}
if (itemsSellTotalMoneyText != null)
{
if(stageCount == 0 || totalSellValue <= 0)
{
itemsSellTotalMoneyText.text = BuyUiEmptyValue;
}
else
{
itemsSellTotalMoneyText.text = totalSellValue.ToString();
}
}
if (m_btn_sell != null)
{
m_btn_sell.interactable = stageCount > 0 && totalSellValue > 0;
}
}
}