diff --git a/Assets/PerfectWorld/Scripts/Shop.meta b/Assets/PerfectWorld/Scripts/Shop.meta new file mode 100644 index 0000000000..1e53564a68 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Shop.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 1be878d234498514c8356a798b2e4515 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/PerfectWorld/Scripts/Shop/CECBackShop.cs b/Assets/PerfectWorld/Scripts/Shop/CECBackShop.cs new file mode 100644 index 0000000000..260548a8b1 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Shop/CECBackShop.cs @@ -0,0 +1,230 @@ +// Filename : CECBackShop.cs +// Creator : Xu Wenbin +// Date : 2013/12/11 +// Converted to C#: 2024 + +using System; +using System.Collections.Generic; +using UnityEngine; +using BrewMonster.Network; + +namespace PerfectWorld.Scripts.Shop +{ + // CECBackShop + public class CECBackShop : CECShopArrayItems + { + private CECShopItemCategory m_fashionShopCategory = new CECShopItemCategory(); + private static CECBackShop s_instance; + private GShopLoader m_shopLoader; + + private CECBackShop() : base(null) + { + // Initialize with empty list first + m_pItems = new List(); + + // Try to get shop loader data (may not be available immediately) + RefreshShopData(); + + if (IsFashionShopEnabled()) + { + m_fashionShopCategory.InitFromQShopConfig(CECQShopConfig.CID_BACKSHOP_FASHION); + } + } + + public void RefreshShopData() + { + // Initialize with shop loader data + GameObject shopLoaderObj = GameObject.FindFirstObjectByType()?.gameObject; + if (shopLoaderObj != null) + { + m_shopLoader = shopLoaderObj.GetComponent(); + if (m_shopLoader != null && m_shopLoader.secondaryShop != null && m_shopLoader.secondaryShop.items != null) + { + m_pItems = m_shopLoader.secondaryShop.items; + } + } + } + + public static CECBackShop Instance() + { + if (s_instance == null) + { + s_instance = new CECBackShop(); + } + return s_instance; + } + + public override bool GetFromServer(int beginIndex, int endIndex) + { + bool result = false; + // TODO: Implement cooldown check (GP_CT_GET_DIVIDEND_MALL_PRICE) + // For now, always allow if indices are provided + if (beginIndex == 0 && endIndex == 0) + { + // Request all prices + // TODO: Implement UnityGameSession.RequestGetDividendMallItemPrice(0, 0) + result = true; + } + else if (beginIndex != 0 || endIndex != 0) + { + // Request specific range + // TODO: Implement UnityGameSession.RequestGetDividendMallItemPrice(beginIndex, endIndex) + result = true; + } + return result; + } + + public override uint GetLocalTimeStamp() + { + if (m_shopLoader != null && m_shopLoader.secondaryShop != null) + { + return m_shopLoader.secondaryShop.timestamp; + } + return 0; + } + + public override uint GetServerTimeStamp() + { + // TODO: Get from game run state + // return g_pGame->GetGameRun()->GetGShopTimeStamp2(); + return GetLocalTimeStamp(); // Stub for now + } + + public bool UpdateFromServer() // TODO: Add S2C::cmd_dividend_mall_item_price parameter when available + { + // TODO: Implement server update logic when S2C command structure is available + // For now, this is a stub + /* + List items = new List(m_pItems); + if (!ApplyChangesFromServer(items, pCmd)) + { + return false; + } + if (IsSame(items, m_pItems)) + { + return true; + } + m_pItems = items; + OnItemChange(); + */ + return true; + } + + private bool ApplyChangesFromServer(List pItems) // TODO: Add S2C::cmd_dividend_mall_item_price parameter when available + { + // TODO: Implement when S2C command structure is available + /* + int i = 0; + for (i = pCmd->start_index; i < pCmd->end_index; i++) + { + if (i < pItems.Count) + { + GShopItem data = pItems[i]; + for (int j = 0; j < 4; j++) + { + if (data.buy != null && j < data.buy.Length && data.buy[j].type != -1) + { + GShopBuyOption buyOption = data.buy[j]; + buyOption.type = -1; + buyOption.price = 0; + data.buy[j] = buyOption; + } + } + pItems[i] = data; + } + } + for (i = 0; i < pCmd->count; i++) + { + const S2C::cmd_dividend_mall_item_price::good_info& tempList = pCmd->list[i]; + if (tempList.good_index < pItems.Count) + { + GShopItem data = pItems[tempList.good_index]; + if (data.id == (uint)tempList.good_id) + { + if (data.buy != null && tempList.good_slot < data.buy.Length) + { + GShopBuyOption buyOption = data.buy[tempList.good_slot]; + buyOption.price = tempList.good_price; + buyOption.status = tempList.good_status; + buyOption.time = tempList.expire_time; + buyOption.type = 3; + data.buy[tempList.good_slot] = buyOption; + } + pItems[tempList.good_index] = data; + } + else + { + Debug.Assert(data.id == (uint)tempList.good_id); + return false; + } + } + } + */ + return true; + } + + public override int GetCash() + { + // TODO: Integrate with CECHostPlayer.GetDividend() + // For now, return 0 as stub + // return CECHostPlayer.Instance().GetDividend(); + return 0; + } + + public override bool Buy(int itemIndex, int buyIndex) + { + bool bOK = false; + + if (ReadyToBuy(itemIndex, buyIndex)) + { + GShopItem? pItem = GetItem(itemIndex); + if (pItem.HasValue) + { + GShopItem item = pItem.Value; + if (!CECShopItemOwnerNPC.HasOwnerNPC(item)) + { + // Regular dividend mall shopping + // TODO: Implement UnityGameSession.RequestDividendMallShopping(1, itemIndex, item.id, buyIndex) + // For now, use regular mall shopping as fallback + UnityGameSession.Instance.RequestMallShopping(1, (int)item.id, itemIndex, buyIndex); + } + else + { + // NPC server dividend mall shopping + // TODO: Implement UnityGameSession.RequestNPCSevDividendMallShopping(1, itemIndex, item.id, buyIndex) + UnityGameSession.Instance.RequestMallShopping(1, (int)item.id, itemIndex, buyIndex); + } + CECQShopConfig.Instance().OnItemBuyed(item.id); + // TODO: CECShoppingItemsMover::Instance().OnItemBuyed(this, itemIndex, buyIndex); + bOK = true; + } + } + else + { + Debug.Assert(false, "Item not ready to buy"); + } + return bOK; + } + + public override bool IsFashionShopEnabled() + { + return CECUIConfig.Instance().GetGameUI().bEnableBackShopFashionShop; + } + + public override bool IsFashionShopFlashSaleEnabled() + { + return CECUIConfig.Instance().GetGameUI().bEnableBackShopFashionShopFlashSale; + } + + public override string GetFashionShopFlashSaleTitle() + { + return CECUIConfig.Instance().GetGameUI().strBackShopFashionShopFlashSaleTitle; + } + + public override CECShopItemCategory GetFashionShopCategory() + { + return m_fashionShopCategory; + } + } +} + diff --git a/Assets/PerfectWorld/Scripts/Shop/CECBackShop.cs.meta b/Assets/PerfectWorld/Scripts/Shop/CECBackShop.cs.meta new file mode 100644 index 0000000000..e302ac0894 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Shop/CECBackShop.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 01372a5632621c3438a20d995d97bdee \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Shop/CECQShop.cs b/Assets/PerfectWorld/Scripts/Shop/CECQShop.cs new file mode 100644 index 0000000000..5351701d01 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Shop/CECQShop.cs @@ -0,0 +1,228 @@ +// Filename : CECQShop.cs +// Creator : Xu Wenbin +// Date : 2013/12/11 +// Converted to C#: 2024 + +using System; +using System.Collections.Generic; +using UnityEngine; +using BrewMonster.Network; + +namespace PerfectWorld.Scripts.Shop +{ + // CECQShop + public class CECQShop : CECShopArrayItems + { + private CECShopItemCategory m_fashionShopCategory = new CECShopItemCategory(); + private static CECQShop s_instance; + private GShopLoader m_shopLoader; + + private CECQShop() : base(null) + { + // Initialize with empty list first + m_pItems = new List(); + + // Try to get shop loader data (may not be available immediately) + RefreshShopData(); + + if (IsFashionShopEnabled()) + { + m_fashionShopCategory.InitFromQShopConfig(CECQShopConfig.CID_QSHOP_FASHION); + } + } + + public void RefreshShopData() + { + // Initialize with shop loader data + GameObject shopLoaderObj = GameObject.FindFirstObjectByType()?.gameObject; + if (shopLoaderObj != null) + { + m_shopLoader = shopLoaderObj.GetComponent(); + if (m_shopLoader != null && m_shopLoader.primaryShop != null && m_shopLoader.primaryShop.items != null) + { + m_pItems = m_shopLoader.primaryShop.items; + } + } + } + + public static CECQShop Instance() + { + if (s_instance == null) + { + s_instance = new CECQShop(); + } + return s_instance; + } + + public override bool GetFromServer(int beginIndex, int endIndex) + { + bool result = false; + // TODO: Implement cooldown check (GP_CT_GET_MALL_PRICE) + // For now, always allow if indices are provided + if (beginIndex == 0 && endIndex == 0) + { + // Request all prices + // TODO: Implement UnityGameSession.RequestGetMallItemPrice(0, 0) + result = true; + } + else if (beginIndex != 0 || endIndex != 0) + { + // Request specific range + // TODO: Implement UnityGameSession.RequestGetMallItemPrice(beginIndex, endIndex) + result = true; + } + return result; + } + + public override uint GetLocalTimeStamp() + { + if (m_shopLoader != null && m_shopLoader.primaryShop != null) + { + return m_shopLoader.primaryShop.timestamp; + } + return 0; + } + + public override uint GetServerTimeStamp() + { + // TODO: Get from game run state + // return g_pGame->GetGameRun()->GetGShopTimeStamp(); + return GetLocalTimeStamp(); // Stub for now + } + + public bool UpdateFromServer() // TODO: Add S2C::cmd_mall_item_price parameter when available + { + // TODO: Implement server update logic when S2C command structure is available + // For now, this is a stub + /* + List items = new List(m_pItems); + if (!ApplyChangesFromServer(items, pCmd)) + { + return false; + } + if (IsSame(items, m_pItems)) + { + return true; + } + m_pItems = items; + OnItemChange(); + */ + return true; + } + + private bool ApplyChangesFromServer(List pItems) // TODO: Add S2C::cmd_mall_item_price parameter when available + { + // TODO: Implement when S2C command structure is available + /* + int i = 0; + for (i = pCmd->start_index; i < pCmd->end_index; i++) + { + if (i < pItems.Count) + { + GShopItem data = pItems[i]; + for (int j = 0; j < 4; j++) + { + if (data.buy != null && j < data.buy.Length && data.buy[j].type != -1) + { + GShopBuyOption buyOption = data.buy[j]; + buyOption.type = -1; + buyOption.price = 0; + data.buy[j] = buyOption; + } + } + pItems[i] = data; + } + } + for (i = 0; i < pCmd->count; i++) + { + const S2C::cmd_mall_item_price::good_item& tempList = pCmd->list[i]; + if (tempList.good_index < pItems.Count) + { + GShopItem data = pItems[tempList.good_index]; + if (data.id == (uint)tempList.good_id) + { + if (data.buy != null && tempList.good_slot < data.buy.Length) + { + GShopBuyOption buyOption = data.buy[tempList.good_slot]; + buyOption.price = tempList.goods_price; + buyOption.status = tempList.good_status; + buyOption.time = tempList.expire_time; + buyOption.type = 3; + data.buy[tempList.good_slot] = buyOption; + } + pItems[tempList.good_index] = data; + } + else + { + Debug.Assert(data.id == (uint)tempList.good_id); + return false; + } + } + } + */ + return true; + } + + public override int GetCash() + { + // TODO: Integrate with CECHostPlayer.GetCash() + // For now, return 0 as stub + // return CECHostPlayer.Instance().GetCash(); + return 0; + } + + public override bool Buy(int itemIndex, int buyIndex) + { + bool bOK = false; + + if (ReadyToBuy(itemIndex, buyIndex)) + { + GShopItem? pItem = GetItem(itemIndex); + if (pItem.HasValue) + { + GShopItem item = pItem.Value; + if (!CECShopItemOwnerNPC.HasOwnerNPC(item)) + { + // Regular mall shopping + UnityGameSession.Instance.RequestMallShopping(1, (int)item.id, itemIndex, buyIndex); + } + else + { + // NPC server mall shopping + // TODO: Implement UnityGameSession.RequestNPCSevMallShopping(1, itemIndex, item.id, buyIndex) + UnityGameSession.Instance.RequestMallShopping(1, (int)item.id, itemIndex, buyIndex); + } + CECQShopConfig.Instance().OnItemBuyed(item.id); + // TODO: CECShoppingItemsMover::Instance().OnItemBuyed(this, itemIndex, buyIndex); + bOK = true; + } + } + else + { + Debug.Assert(false, "Item not ready to buy"); + } + return bOK; + } + + public override bool IsFashionShopEnabled() + { + return CECUIConfig.Instance().GetGameUI().bEnableQShopFashionShop; + } + + public override bool IsFashionShopFlashSaleEnabled() + { + return CECUIConfig.Instance().GetGameUI().bEnableQShopFashionShopFlashSale; + } + + public override string GetFashionShopFlashSaleTitle() + { + return CECUIConfig.Instance().GetGameUI().strQShopFashionShopFlashSaleTitle; + } + + public override CECShopItemCategory GetFashionShopCategory() + { + return m_fashionShopCategory; + } + } +} + diff --git a/Assets/PerfectWorld/Scripts/Shop/CECQShop.cs.meta b/Assets/PerfectWorld/Scripts/Shop/CECQShop.cs.meta new file mode 100644 index 0000000000..6bf0337ee2 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Shop/CECQShop.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 2d753dbecaf480345acbcb47feb6340f \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Shop/CECQShopConfig.cs b/Assets/PerfectWorld/Scripts/Shop/CECQShopConfig.cs new file mode 100644 index 0000000000..ae7e953c30 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Shop/CECQShopConfig.cs @@ -0,0 +1,40 @@ +// Filename : CECQShopConfig.cs +// Creator : Xu Wenbin +// Date : 2013/12/11 +// Converted to C#: 2024 + +using System; + +namespace PerfectWorld.Scripts.Shop +{ + // Stub implementation - to be properly implemented later + public class CECQShopConfig + { + public const int CID_QSHOP_FASHION = 0; // Stub value + public const int CID_BACKSHOP_FASHION = 1; // Stub value + + private static CECQShopConfig s_instance; + + public static CECQShopConfig Instance() + { + if (s_instance == null) + { + s_instance = new CECQShopConfig(); + } + return s_instance; + } + + public void FindCategory(int idCategory, out int mainType, out int subType) + { + // Stub implementation - to be properly implemented later + mainType = -1; + subType = -1; + } + + public void OnItemBuyed(uint goodsId) + { + // Stub implementation - to be properly implemented later + } + } +} + diff --git a/Assets/PerfectWorld/Scripts/Shop/CECQShopConfig.cs.meta b/Assets/PerfectWorld/Scripts/Shop/CECQShopConfig.cs.meta new file mode 100644 index 0000000000..e77f9bfc42 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Shop/CECQShopConfig.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: c8e321ee92225d04ab0eb82b965fe32d \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Shop/CECShopArrayItems.cs b/Assets/PerfectWorld/Scripts/Shop/CECShopArrayItems.cs new file mode 100644 index 0000000000..0a875f179d --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Shop/CECShopArrayItems.cs @@ -0,0 +1,60 @@ +// Filename : CECShopArrayItems.cs +// Creator : Xu Wenbin +// Date : 2013/12/11 +// Converted to C#: 2024 + +using System.Collections.Generic; +using System.Linq; + +namespace PerfectWorld.Scripts.Shop +{ + public abstract class CECShopArrayItems : CECShopBase + { + protected List m_pItems; + + public CECShopArrayItems(List pItems) + { + m_pItems = pItems; + } + + public override int GetCount() + { + return m_pItems != null ? m_pItems.Count : 0; + } + + public override GShopItem? GetItem(int index) + { + if (m_pItems != null && index >= 0 && index < m_pItems.Count) + { + return m_pItems[index]; + } + return null; + } + + public static bool IsSame(List lhs, List rhs) + { + bool result = false; + if (lhs == rhs) + { + result = true; + } + else if (lhs != null && rhs != null) + { + if (lhs.Count == rhs.Count) + { + result = true; + for (int u = 0; u < lhs.Count; ++u) + { + if (!CECShopBase.IsSame(lhs[u], rhs[u])) + { + result = false; + break; + } + } + } + } + return result; + } + } +} + diff --git a/Assets/PerfectWorld/Scripts/Shop/CECShopArrayItems.cs.meta b/Assets/PerfectWorld/Scripts/Shop/CECShopArrayItems.cs.meta new file mode 100644 index 0000000000..089217a5d3 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Shop/CECShopArrayItems.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: f70fb69e0e992a24c8e03b2558f3ee6f \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Shop/CECShopBase.cs b/Assets/PerfectWorld/Scripts/Shop/CECShopBase.cs new file mode 100644 index 0000000000..e93d9cc1a5 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Shop/CECShopBase.cs @@ -0,0 +1,356 @@ +// Filename : CECShopBase.cs +// Creator : Xu Wenbin +// Date : 2013/12/11 +// Converted to C#: 2024 + +using System; +using System.Linq; +using BrewMonster; + +namespace PerfectWorld.Scripts.Shop +{ + // 封装 gshop 和 backshop 等的数据访问 / Encapsulates data access for gshop and backshop etc. + public static class CECShopConstants + { + public const int CECSHOP_INVALID_PRICE = -1; + public const int BUY_COUNT = 4; + } + + public abstract class CECShopBase : CECObservable + { + private CECShopItemOwnerNPC m_ownerNPC = new CECShopItemOwnerNPC(0); + + // CECShopBase + + public void OnItemChange() + { + CECShopBaseChange change = new CECShopBaseChange((uint)CECShopBaseChange.ChangeMask.ITEM_CHANGED); + NotifyObservers(change); + } + + public bool ValidateTimeStamp() + { + return GetLocalTimeStamp() == GetServerTimeStamp(); + } + + public void SetOwnerNPCID(uint ownerNPCID) + { + if (ownerNPCID == m_ownerNPC.GetOwnerNPCID()) + { + return; + } + m_ownerNPC.SetOwnerNPCID(ownerNPCID); + OnItemChange(); + } + + public uint GetOwnerNPCID() + { + return m_ownerNPC.GetOwnerNPCID(); + } + + public bool HasOwnerNPC() + { + return !m_ownerNPC.IsEmpty(); + } + + public CECShopItemOwnerNPC GetOwnerNPC() + { + return m_ownerNPC; + } + + public bool MatchOwnerNPC(GShopItem rhs) + { + return m_ownerNPC.MatchItem(rhs); + } + + public bool IsValid(int itemIndex, int buyIndex) + { + bool bValid = false; + GShopItem? pItem = GetItem(itemIndex); + if (pItem.HasValue) + { + if (buyIndex >= 0 && buyIndex < CECShopConstants.BUY_COUNT) + { + bValid = true; + } + } + return bValid; + } + + public bool ReadyToBuy(int itemIndex, int buyIndex) + { + bool result = false; + int[] buyType = new int[CECShopConstants.BUY_COUNT]; + if (IsValid(itemIndex, buyIndex) && CalcBuyType(itemIndex, buyType)) + { + for (int i = 0; i < CECShopConstants.BUY_COUNT; ++i) + { + if (buyType[i] == buyIndex) + { + result = true; + break; + } + } + } + return result; + } + + public bool CalcBuyType(int itemIndex, int[] buyTypes) + { + bool bOK = false; + GShopItem? pItem = GetItem(itemIndex); + if (pItem.HasValue) + { + GShopItem item = pItem.Value; + int[] typeDefault = new int[CECShopConstants.BUY_COUNT]; + int[] typeNew = new int[CECShopConstants.BUY_COUNT]; + int index1 = 0; + int index2 = 0; + for (int i = 0; i < CECShopConstants.BUY_COUNT; i++) + { + typeDefault[i] = -1; + typeNew[i] = -1; + if (item.buy != null && i < item.buy.Length) + { + if (item.buy[i].type == 3 && item.buy[i].price > 0) + { + typeNew[index1] = i; + index1++; + } + else if (item.buy[i].type == -1 && item.buy[i].price > 0) + { + typeDefault[index2] = i; + index2++; + } + } + } + if (index1 > 0) + { + Array.Copy(typeNew, buyTypes, CECShopConstants.BUY_COUNT); + bOK = true; + } + else if (index2 > 0) + { + Array.Copy(typeDefault, buyTypes, CECShopConstants.BUY_COUNT); + bOK = true; + } + } + return bOK; + } + + public bool HasSameBuyType(int itemIndexA, int itemIndexB) + { + int[] typeA = new int[CECShopConstants.BUY_COUNT]; + int[] typeB = new int[CECShopConstants.BUY_COUNT]; + if (CalcBuyType(itemIndexA, typeA) && + CalcBuyType(itemIndexB, typeB) && + typeA.SequenceEqual(typeB)) + { + GShopItem? pItemA = GetItem(itemIndexA); + GShopItem? pItemB = GetItem(itemIndexB); + if (pItemA.HasValue && pItemB.HasValue) + { + GShopItem itemA = pItemA.Value; + GShopItem itemB = pItemB.Value; + for (int i = 0; i < CECShopConstants.BUY_COUNT; ++i) + { + int buyIndex = typeA[i]; + if (buyIndex != -1) + { + bool priceAValid = (itemA.buy != null && buyIndex < itemA.buy.Length && itemA.buy[buyIndex].price != 0); + bool priceBValid = (itemB.buy != null && buyIndex < itemB.buy.Length && itemB.buy[buyIndex].price != 0); + if (priceAValid != priceBValid) + { + return false; + } + if (!priceAValid) + { + continue; + } + if (itemA.buy[buyIndex].time != itemB.buy[buyIndex].time) + { + return false; + } + } + } + return true; + } + } + return false; + } + + public bool CalcBuyIndex(int itemIndex, out int buyIndex, int cash = -1) + { + buyIndex = -1; + bool bOK = false; + GShopItem? pItem = GetItem(itemIndex); + if (pItem.HasValue) + { + GShopItem item = pItem.Value; + // 参考实现 CDlgQShopItem::SetItem / Reference implementation CDlgQShopItem::SetItem + + // 判断要显示的购买方式 / Determine which purchase method to display + int[] m_TypeDefault = new int[CECShopConstants.BUY_COUNT]; + int[] m_TypeNew = new int[CECShopConstants.BUY_COUNT]; + int index1 = 0; + int index2 = 0; + for (int i = 0; i < CECShopConstants.BUY_COUNT; i++) + { + m_TypeDefault[i] = -1; + m_TypeNew[i] = -1; + if (item.buy != null && i < item.buy.Length) + { + if (item.buy[i].type == 3 && item.buy[i].price > 0) + { + m_TypeNew[index1] = i; + index1++; + } + else if (item.buy[i].type == -1 && item.buy[i].price > 0) + { + m_TypeDefault[index2] = i; + index2++; + } + } + } + int m_BuyType = -1; + if (index1 > 0) + { + m_BuyType = 0; + } + else + { + m_BuyType = 1; + } + + if (cash == -1) + { + cash = GetCash(); + } + for (int i = 0; i < CECShopConstants.BUY_COUNT; i++) + { + int BuyIndex = 0; + if (m_BuyType == 0) + { + BuyIndex = m_TypeNew[i]; + } + else + { + BuyIndex = m_TypeDefault[i]; + } + if (BuyIndex != -1 && item.buy != null && BuyIndex < item.buy.Length) + { + uint price = item.buy[BuyIndex].price; + if (price != 0 && price <= cash) + { + bOK = true; + buyIndex = BuyIndex; + break; + } + } + } + } + return bOK; + } + + public int GetPrice(int itemIndex, int buyIndex) + { + int price = CECShopConstants.CECSHOP_INVALID_PRICE; + if (IsValid(itemIndex, buyIndex)) + { + GShopItem? pItem = GetItem(itemIndex); + if (pItem.HasValue && pItem.Value.buy != null && buyIndex < pItem.Value.buy.Length) + { + price = (int)pItem.Value.buy[buyIndex].price; + } + } + else + { + UnityEngine.Debug.Assert(false); + } + return price; + } + + public uint GetStatus(int itemIndex, int buyIndex) + { + uint status = 0xFFFFFFFF; // -1 as uint + if (IsValid(itemIndex, buyIndex)) + { + GShopItem? pItem = GetItem(itemIndex); + if (pItem.HasValue && pItem.Value.buy != null && buyIndex < pItem.Value.buy.Length) + { + status = pItem.Value.buy[buyIndex].status; + } + } + else + { + UnityEngine.Debug.Assert(false); + } + return status; + } + + public static bool IsSame(GShopItem lhs, GShopItem rhs) + { + bool result = false; + if (lhs.mainType != rhs.mainType || + lhs.subType != rhs.subType || + lhs.id != rhs.id || + lhs.num != rhs.num || + lhs.idGift != rhs.idGift || + lhs.giftNum != rhs.giftNum) + { + return false; + } + result = true; + if (lhs.buy != null && rhs.buy != null) + { + int buyCount = Math.Min(lhs.buy.Length, rhs.buy.Length); + for (int i = 0; i < buyCount && i < CECShopConstants.BUY_COUNT; ++i) + { + if (lhs.buy[i].type != rhs.buy[i].type || + lhs.buy[i].price != rhs.buy[i].price || + lhs.buy[i].time != rhs.buy[i].time || + lhs.buy[i].status != rhs.buy[i].status) + { + result = false; + break; + } + } + } + return result; + } + + public static int GetOriginalPrice(int finalPrice, uint discountStatus) + { + int result = finalPrice; + if (discountStatus >= 4 && discountStatus <= 12) + { + float originalPrice = finalPrice * (10.0f / (discountStatus - 3)); + if (CECUIConfig.Instance().GetGameUI().bEnableCeilPriceBeforeDiscountToGold) + { + result = (int)Math.Ceiling(originalPrice) + 99; + result /= 100; + result *= 100; + } + else + { + result = (int)Math.Ceiling(originalPrice); + } + } + return result; + } + + // Abstract methods + public abstract bool GetFromServer(int beginIndex, int endIndex); + public abstract uint GetLocalTimeStamp(); + public abstract uint GetServerTimeStamp(); + public abstract int GetCount(); + public abstract GShopItem? GetItem(int index); + public abstract int GetCash(); + public abstract bool Buy(int itemIndex, int buyIndex); + public abstract bool IsFashionShopEnabled(); + public abstract CECShopItemCategory GetFashionShopCategory(); + public abstract bool IsFashionShopFlashSaleEnabled(); + public abstract string GetFashionShopFlashSaleTitle(); + } +} + diff --git a/Assets/PerfectWorld/Scripts/Shop/CECShopBase.cs.meta b/Assets/PerfectWorld/Scripts/Shop/CECShopBase.cs.meta new file mode 100644 index 0000000000..da10aac5bb --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Shop/CECShopBase.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 16ba0a2da752c6a49ad053e4af8be921 \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Shop/CECShopBaseChange.cs b/Assets/PerfectWorld/Scripts/Shop/CECShopBaseChange.cs new file mode 100644 index 0000000000..f147caae25 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Shop/CECShopBaseChange.cs @@ -0,0 +1,36 @@ +// Filename : CECShopBaseChange.cs +// Creator : Xu Wenbin +// Date : 2013/12/11 +// Converted to C#: 2024 + +using BrewMonster; + +namespace PerfectWorld.Scripts.Shop +{ + // 封装 CECShopBase 的变化 / Encapsulates changes to CECShopBase + public class CECShopBaseChange : CECObservableChange + { + public enum ChangeMask + { + ITEM_CHANGED = 0x01, + } + + private uint m_changeMask; + + public CECShopBaseChange(uint changeMask) + { + m_changeMask = changeMask; + } + + public uint GetChangeMask() + { + return m_changeMask; + } + + public bool ItemChanged() + { + return (GetChangeMask() & (uint)ChangeMask.ITEM_CHANGED) != 0; + } + } +} + diff --git a/Assets/PerfectWorld/Scripts/Shop/CECShopBaseChange.cs.meta b/Assets/PerfectWorld/Scripts/Shop/CECShopBaseChange.cs.meta new file mode 100644 index 0000000000..6101ea3b3c --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Shop/CECShopBaseChange.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: e995fc9c415a8a143a75c18021965074 \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Shop/CECShopItemCategory.cs b/Assets/PerfectWorld/Scripts/Shop/CECShopItemCategory.cs new file mode 100644 index 0000000000..914f239c2f --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Shop/CECShopItemCategory.cs @@ -0,0 +1,59 @@ +// Filename : CECShopItemCategory.cs +// Creator : Xu Wenbin +// Date : 2013/12/11 +// Converted to C#: 2024 + +using System; + +namespace PerfectWorld.Scripts.Shop +{ + // 封装 GSHOP_ITEM 的分类信息 / Encapsulates category information for GSHOP_ITEM + public class CECShopItemCategory + { + private int m_mainType; + private int m_subType; + + public CECShopItemCategory(int mainType = -1, int subType = -1) + { + m_mainType = mainType; + m_subType = subType; + } + + public void InitFromQShopConfig(int idCategory) + { + int mainType = -1; + int subType = -1; + CECQShopConfig.Instance().FindCategory(idCategory, out mainType, out subType); + SetCategory(mainType, subType); + } + + public void SetCategory(int mainType, int subType) + { + m_mainType = mainType; + m_subType = subType; + } + + public int GetMainType() + { + return m_mainType; + } + + public int GetSubType() + { + return m_subType; + } + + public bool MatchMainType(int mainType) + { + return m_mainType >= 0 && m_mainType == mainType; + } + + public bool MatchItem(GShopItem item) + { + // 匹配主类型有效时检查 / Check when main type matching is valid + return MatchMainType(item.mainType) + && (m_subType < 0 || item.subType == m_subType); // 子类型小于0时表示不限制子类型 / When sub type < 0, means no sub type restriction + } + } +} + diff --git a/Assets/PerfectWorld/Scripts/Shop/CECShopItemCategory.cs.meta b/Assets/PerfectWorld/Scripts/Shop/CECShopItemCategory.cs.meta new file mode 100644 index 0000000000..77400befcc --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Shop/CECShopItemCategory.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: c8734b057cb3bec4d83b03b28b652b6a \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Shop/CECShopItemOwnerNPC.cs b/Assets/PerfectWorld/Scripts/Shop/CECShopItemOwnerNPC.cs new file mode 100644 index 0000000000..8deb4cabdc --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Shop/CECShopItemOwnerNPC.cs @@ -0,0 +1,79 @@ +// Filename : CECShopItemOwnerNPC.cs +// Creator : Xu Wenbin +// Date : 2013/12/11 +// Converted to C#: 2024 + +using System; + +namespace PerfectWorld.Scripts.Shop +{ + // 封装 GSHOP_ITEM 的挂靠 NPC 查询 / Encapsulates NPC ownership query for GSHOP_ITEM + public class CECShopItemOwnerNPC + { + private const int TREASURE_ITEM_OWNER_NPC_COUNT = 8; + + private uint m_ownerNPCID; + + public CECShopItemOwnerNPC(uint ownerNPCID = 0) + { + m_ownerNPCID = ownerNPCID; + } + + public void SetOwnerNPCID(uint ownerNPCID) + { + m_ownerNPCID = ownerNPCID; + } + + public uint GetOwnerNPCID() + { + return m_ownerNPCID; + } + + public bool IsEmpty() + { + return GetOwnerNPCID() == 0; + } + + public bool Equals(CECShopItemOwnerNPC rhs) + { + return GetOwnerNPCID() == rhs.GetOwnerNPCID(); + } + + public bool MatchID(uint id) + { + return id == m_ownerNPCID; + } + + public bool MatchItem(GShopItem item) + { + if (IsEmpty()) + { + return !HasOwnerNPC(item); + } + bool result = false; + if (item.ownerNpcs != null) + { + for (int i = 0; i < TREASURE_ITEM_OWNER_NPC_COUNT && i < item.ownerNpcs.Length; ++i) + { + if (MatchID(item.ownerNpcs[i])) + { + result = true; // 找到了 / Found + break; + } + if (item.ownerNpcs[i] == 0) + { + break; // 已经到最后一个了,后面都是0 / Already reached the last one, rest are 0 + } + } + } + return result; + } + + public static bool HasOwnerNPC(GShopItem item) + { + // 编辑器保证数组中,第一个是非零的在前 / Editor ensures that in the array, the first non-zero is at the front + return item.ownerNpcs != null && item.ownerNpcs.Length > 0 && item.ownerNpcs[0] != 0; + } + } +} + diff --git a/Assets/PerfectWorld/Scripts/Shop/CECShopItemOwnerNPC.cs.meta b/Assets/PerfectWorld/Scripts/Shop/CECShopItemOwnerNPC.cs.meta new file mode 100644 index 0000000000..6fbe8b414f --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Shop/CECShopItemOwnerNPC.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: f4bc2fd669173534b9f94313c78af59e \ No newline at end of file diff --git a/Assets/PerfectWorld/Scripts/Shop/CECUIConfig.cs b/Assets/PerfectWorld/Scripts/Shop/CECUIConfig.cs new file mode 100644 index 0000000000..3806c34c70 --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Shop/CECUIConfig.cs @@ -0,0 +1,39 @@ +// Filename : CECUIConfig.cs +// Creator : Xu Wenbin +// Date : 2013/12/11 +// Converted to C#: 2024 + +namespace PerfectWorld.Scripts.Shop +{ + // Stub implementation - to be properly implemented later + public class CECUIConfig + { + private static CECUIConfig s_instance; + + public static CECUIConfig Instance() + { + if (s_instance == null) + { + s_instance = new CECUIConfig(); + } + return s_instance; + } + + public GameUIConfig GetGameUI() + { + return new GameUIConfig(); + } + } + + public class GameUIConfig + { + public bool bEnableQShopFashionShop = false; + public bool bEnableQShopFashionShopFlashSale = false; + public string strQShopFashionShopFlashSaleTitle = ""; + public bool bEnableBackShopFashionShop = false; + public bool bEnableBackShopFashionShopFlashSale = false; + public string strBackShopFashionShopFlashSaleTitle = ""; + public bool bEnableCeilPriceBeforeDiscountToGold = false; + } +} + diff --git a/Assets/PerfectWorld/Scripts/Shop/CECUIConfig.cs.meta b/Assets/PerfectWorld/Scripts/Shop/CECUIConfig.cs.meta new file mode 100644 index 0000000000..e1be10b43d --- /dev/null +++ b/Assets/PerfectWorld/Scripts/Shop/CECUIConfig.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: 28ee6f77dbdc1204eb2a2f933941e610 \ No newline at end of file