using System; using System.Collections.Generic; using System.Linq; using Google.Protobuf.WellKnownTypes; using Sog; using ProtoCSStruct; using LitJson; namespace Game { public enum ShopRefreshType { None = 0, Common = 1, //固定天数 Week = 2, //每周一 Month = 3, //每月一日 WeekSomeDay = 4, //每周固定某天 SomeDayHour = 5, //每天x小时后刷新 } public static class MarketShopSvc { public static int GetShopDataIndex(PlayerOnGame player, int shopId) { for (int i = 0; i < player.RoleData.MarketShopData.ShopData.Count; i++) { if (player.RoleData.MarketShopData.ShopData[i].ShopId == shopId) { return i; } } return -1; } public static ref OneShopData GetShopDataByIndex(PlayerOnGame player, int index) { return ref player.RoleData.MarketShopData.ShopData[index]; } // 返回index, -1表示失败 public static int CreateShopDataIfNotExist(PlayerOnGame player, int shopId, bool notifyClient) { int index = GetShopDataIndex(player, shopId); if (index != -1) { return index; } ref var shopList = ref player.RoleData.MarketShopData.ShopData; if (shopList.Count >= shopList.GetMaxCount()) { TraceLog.Error("MarketShopSvc.CreateShopDataIfNotExist uid {0} shopId {1} not enough space", player.UserID, shopId); return -1; } var desc = MarketShopDescMgr.Instance.GetConfig(shopId); if (desc == null || desc.goodsNum == 0) { TraceLog.Error("MarketShopSvc.CreateShopDataIfNotExist shopId {0} invalid params", shopId); return -1; } var nowSec = GameServerUtils.GetTimeSecond(); var shopData = new OneShopData { ShopId = shopId, LastHandRefreshTime = nowSec }; { if (!RefreshShop(player, ref shopData, desc)) { TraceLog.Error("MarketShopSvc.CreateShopDataIfNotExist shopId {0} refresh shop fail", shopId); return -1; } } var nextAutoRefreshTime = GetNextAutoRefreshTime(ref shopData, nowSec); shopData.NextAutoRefreshTime = nextAutoRefreshTime; shopList.Add(ref shopData); player.MakeDirty(); if (notifyClient) { SendPlayerShopRefreshRes(player, ref shopData); } TraceLog.Debug("MarketShopSvc.CreateShopDataIfNotExist shopId {0} succ", shopId); return shopList.Count - 1; } public static CSErrCode MarketShopBuyGoods(PlayerOnGame player, ref CSMarketShopBuyGoodsReq req , ref OneShopData oneShopdata, ref OneGridGoods gridGoods,ref CSMarketShopBuyGoodsRes res) { var goodsDesc = MarketShopGoodsDescMgr.Instance.GetConfigByInternal(gridGoods.GoodsId); if (goodsDesc == null) { TraceLog.Error("MarketShopSvc.MarketShopBuyGoods invalid shopId {0} or goodsIndex {1}" , req.ShopId, gridGoods.GoodsId); return CSErrCode.Fail; } if (goodsDesc.maxBuyTimes > 0 && gridGoods.CurBuyTimes + req.BuyTimes > goodsDesc.maxBuyTimes) { TraceLog.Error( "MarketShopSvc.MarketShopBuyGoods shopId {0} goodsIndex {1} curr buy {2} req buy {3} > max buy {4}" , req.ShopId, gridGoods.GoodsId, gridGoods.CurBuyTimes, req.BuyTimes, goodsDesc.maxBuyTimes); return CSErrCode.BuyTimesNotEnough; } if (goodsDesc.chapterCondition.Length > 0) { var chapterData = player.RoleData.ChapterData; if (chapterData.ChapterId < goodsDesc.chapterCondition[0] && chapterData.BattleId < goodsDesc.chapterCondition[1]) { TraceLog.Error( "MarketShopSvc.MarketShopBuyGoods shopId {0} goodsIndex {1} chapter condition error" , req.ShopId, gridGoods.GoodsId); return CSErrCode.BuyTimesNotEnough; } } JsonData costInfo = new JsonData(); var bagOp = new UnifyOp(player, BillChangeItemReason.BuyMarketShopGoods, req.ShopId.ToString()); for (int i = 0; i < goodsDesc.costGoodsType.Length; i++) { if (goodsDesc.costGoodsType[i] <= 0) { continue; } if (goodsDesc.costGoodsType[i] != (int)GoodsType.Items) { TraceLog.Error("MarketShopSvc.MarketShopBuyGoods invalid cost type {0}", goodsDesc.costGoodsType[i]); return CSErrCode.Fail; } var costNum = (long)goodsDesc.costGoodsNum[i] * req.BuyTimes; var count = bagOp.GetItemCount(goodsDesc.costGoodsId[i]); if (count < costNum) { return CSErrCode.Fail; } bagOp.CostItem(goodsDesc.costGoodsId[i], costNum); var item = new JsonData { ["cost_item_id"] = goodsDesc.costGoodsId[i], ["cost_type"] = goodsDesc.costGoodsType[i], ["buy_cost"] = costNum }; costInfo.Add(item); } long buyNum = (long)req.BuyTimes * goodsDesc.buyGoodsNum; bagOp.AddGoods(goodsDesc.buyGoodsType, goodsDesc.buyGoodsId, buyNum); List list = new List(); var ret = bagOp.DoOp(retList:list); if (ret != 0) { TraceLog.Error("MarketShopSvc.MarketShopBuyGoods uid {0} bag DoOp fail", player.UserID); return ret; } foreach (var chgInfo in list) { if (chgInfo.ChgCount <= 0) { continue; } res.Rewards.Add(chgInfo); } TraceLog.Debug("MarketShopSvc.MarketShopBuyGoods succ, uid {0} shop {1} goodsIndex {2}" , player.UserID, goodsDesc.shopId, goodsDesc.goodsIndex); GameBDCLogUtils.ShopBuy(player, goodsDesc.buyGoodsType, goodsDesc.buyGoodsId, (int)buyNum, costInfo.ToJson(), req.ShopId); GameTALogUtils.LogShopBuy(player, goodsDesc.buyGoodsType, goodsDesc.buyGoodsId, (int)buyNum, ref costInfo, req.ShopId, gridGoods.GoodsId); TaskEXEvent.TriggerMarkShopEquip(player); TaskEXEvent.TriggerBuyShopGoodsN(player, oneShopdata.ShopId, (int)buyNum); return 0; } public static int GetOneGridGoodsPosByGoodsIndex(ref OneShopData oneShop, int goodsId) { if (goodsId <= 0) { return -1; } for (int i = 0; i < oneShop.GridGoods.Count; i++) { if (oneShop.GridGoods[i].GoodsId == goodsId) { return i; } } return -1; } public static CSErrCode CostItemOnHandRefresh(PlayerOnGame player, MarketShopDesc marketdesc, ref OneShopData shopData) { if (shopData.HandRefreshTimes < CommParamDescMgr.Instance.ShopReFreeTime.int_val) { shopData.HandRefreshTimes++; shopData.LastHandRefreshTime = GameServerUtils.GetTimeSecond(); return CSErrCode.None; } var bagOp = new UnifyOp(player, BillChangeItemReason.RefreshMarketShop, marketdesc.shopId.ToString()); //var desc = CommParamDescMgr.Instance.ShopReCostFrag; // var costId = desc.str_val; // var costNum = desc.int_val; //var num = bagOp.GetItemCount(desc.str_val); //if (num < desc.int_val) //{ var desc = CommParamDescMgr.Instance.ShopReCostDiam; var num = bagOp.GetItemCount(desc.str_val); var costId = desc.str_val; var costNum = desc.int_val; if (num < desc.int_val) { return CSErrCode.Fail; } // } bagOp.CostItem(costId, costNum); CSErrCode ret = bagOp.DoOp(); if (ret != CSErrCode.None) { TraceLog.Error("MarketShopSvc.CostItemOnHandRefresh uid {0} shopId {1} bagOp fail" , player.UserID, shopData.ShopId); } return ret; } // 手动刷新商店 public static CSErrCode HandRefreshShop(PlayerOnGame player, MarketShopDesc marketdesc, ref OneShopData shopData) { var ret = CostItemOnHandRefresh(player, marketdesc, ref shopData); if (ret != CSErrCode.None) { return ret; } if (RefreshShop(player, ref shopData, marketdesc)) { return CSErrCode.None; } return CSErrCode.SysFailure; } public static void AutoRefreshAllShop(PlayerOnGame player, long nowSec) { ref var shopDataList = ref player.RoleData.MarketShopData.ShopData; for (int i = 0; i < shopDataList.Count; i++) { ref var shopData = ref shopDataList[i]; var shopId = shopDataList[i].ShopId; var marketShopDesc = MarketShopDescMgr.Instance.GetConfig(shopId); if (marketShopDesc == null || marketShopDesc.autoRefreshType <= 0) { continue; } AutoRefreshShop(player, ref shopData, nowSec); } } private static void AutoRefreshShop(PlayerOnGame player, ref OneShopData shopData, long nowSec) { if (nowSec < shopData.NextAutoRefreshTime) { return; } MarketShopDesc desc = MarketShopDescMgr.Instance.GetConfig(shopData.ShopId); if (desc == null) { return; } long nextAutoRefreshTime = GetNextAutoRefreshTime(ref shopData, nowSec); if (nextAutoRefreshTime <= nowSec) { TraceLog.Error("MarketShopSvc.AutoRefreshShop shopId {0} error, next refreshTime {1} < now {2}", shopData.ShopId, nextAutoRefreshTime, nowSec); return; } if (!RefreshShop(player, ref shopData, desc)) { return; } shopData.NextAutoRefreshTime = nextAutoRefreshTime; shopData.HandRefreshTimes = 0; SendPlayerShopRefreshRes(player,ref shopData); player.MakeDirty(); } public static void RefreshShopByAdv(PlayerOnGame player, int shopId) { ref var shopDataList = ref player.RoleData.MarketShopData.ShopData; var index = -1; for (var i = 0; i < shopDataList.Count; i++) { if (shopDataList[i].ShopId != shopId) continue; index = i; break; } if (index < 0) { return; } ref var shopData = ref shopDataList[index]; var desc = MarketShopDescMgr.Instance.GetConfig(shopId); if (!RefreshShop(player, ref shopData, desc)) { return; } SendPlayerShopRefreshRes(player,ref shopData); player.MakeDirty(); } private static long GetNextAutoRefreshTime(ref OneShopData shopData, long nowSec) { MarketShopDesc desc = MarketShopDescMgr.Instance.GetConfig(shopData.ShopId); if (desc == null) { return 0; } long nextAutoRefreshTime = 0; switch ((ShopRefreshType)desc.autoRefreshType) { case ShopRefreshType.Common: nextAutoRefreshTime = AppTime.GetNextTime(nowSec + AppTime.SECONDS_ADAY * (desc.autoRefreshDay - 1), 0, AppTime.GetGameResetHour()); break; case ShopRefreshType.Week: nextAutoRefreshTime = AppTime.GetNextTime(nowSec, 1, AppTime.GetGameResetHour()); break; case ShopRefreshType.Month: nextAutoRefreshTime = AppTime.GetNextMonthTime(nowSec); break; case ShopRefreshType.WeekSomeDay: nextAutoRefreshTime = AppTime.GetNextTime(nowSec, desc.autoRefreshWeek, AppTime.GetGameResetHour()); break; case ShopRefreshType.SomeDayHour: var todayStartTime = AppTime.GetTodayStartTime(nowSec); var hour = desc.autoRefreshHour; var maxDuration = 24 / desc.autoRefreshHour; var nextTime = todayStartTime; for (var i = 1; i <= maxDuration; i++) { var curr = todayStartTime + (AppTime.SECONDS_AHOUR * hour * i); if (curr > nowSec) { nextTime = curr; break; } } nextAutoRefreshTime = nextTime; break; } return nextAutoRefreshTime; } public static bool RefreshShop(PlayerOnGame player, ref OneShopData shopData, MarketShopDesc marketdesc) { if (marketdesc.goodsNum <= 0) { TraceLog.Debug("MarketShopSvc.RefreshShop shopId {0} goods num is 0", shopData.ShopId); return true; } var groupDesc = MarketShopGoodsDescMgr.Instance.GetGroup(shopData.ShopId); if (groupDesc == null) { TraceLog.Error("MarketShopSvc.RefreshShop invalid shopId {0}", shopData.ShopId); return false; } TraceLog.Debug("MarketShopSvc.RefreshShop player:{0} shopId {1}", player.UserID, shopData.ShopId); shopData.GridGoods.Clear(); int playerLv = player.GetLevel(); var validGoods = new Dictionary(); // 找到所有符合条件的商品 GetValidGoods(player, validGoods, groupDesc, null, playerLv, 0); int needNum = Math.Min(marketdesc.goodsNum, shopData.GridGoods.GetMaxCount()); var randomList=new List(); for (int i = 1; i <= needNum; i++) { var goodsDesc = RandomOneGoodsIndex(validGoods, i); if (goodsDesc == null) { break; } validGoods.Remove(goodsDesc.goodsIndex); randomList.Add(goodsDesc); } var list = randomList.ToArray(); list = SortShopGoods(shopData, list); foreach (var g in list) { var goods = new OneGridGoods { GoodsId = g.InternalId }; shopData.GridGoods.Add(ref goods); } return true; } private static MarketShopGoodsDesc[] SortShopGoods(OneShopData shopData, MarketShopGoodsDesc[] goods) { var shopId = shopData.ShopId; var shopDesc = MarketShopDescMgr.Instance.GetConfig(shopId); if (shopDesc.sortingType is MarkSortingType.NoSorting or MarkSortingType.None) { return goods; } var ret = new List(); var sortGroup = shopDesc.sortingNum; foreach (var t in sortGroup) { var type = t; var array = goods.Where(g => g.buyGoodsType == type).ToArray(); if (array.Length == 0) { continue; } Array.Sort(array, (p1, p2) => { var v1 = 0; var v2 = 0; switch (p1.buyGoodsType) { case (int)GoodsType.Items: v1 = (int)ItemDescMgr.Instance.GetConfig(p1.buyGoodsId).quality; v2 = (int)ItemDescMgr.Instance.GetConfig(p2.buyGoodsId).quality; break; case (int)GoodsType.Equipment: v1 = (int)EquipDescMgr.Instance.GetConfig(p1.buyGoodsId).quality; v2 = (int)EquipDescMgr.Instance.GetConfig(p2.buyGoodsId).quality; break; case (int)GoodsType.EquipSkin: v1 = (int)RoleModelDescMgr.Instance.GetConfig(int.Parse(p1.buyGoodsId)).quality; v2 = (int)RoleModelDescMgr.Instance.GetConfig(int.Parse(p2.buyGoodsId)).quality; break; } return v2.CompareTo(v1); }); ret.AddRange(array); } return ret.Count == 0 ? goods : ret.ToArray(); } public static MarketShopGoodsDesc RandomOneGoodsIndex( Dictionary validGoods, int pos) { var temp = new Dictionary(); foreach (var desc in validGoods.Values) { if (desc.goodsPos == pos) { temp.Add(desc.goodsIndex, desc); } } // pos没有指定物品就从全部有效物品中随机 if (temp.Count == 0) { temp = validGoods; } if (temp.Count == 1) { return temp.Values.First(); } int totalWeight = 0; foreach (var desc in temp.Values) { totalWeight += desc.goodsWeight; } int randProp = GameServerUtils.GetApp().Rand.Next(totalWeight); foreach (var desc in temp.Values) { if (randProp < desc.goodsWeight) { return desc; } randProp -= desc.goodsWeight; } return null; } public static void GetValidGoods(PlayerOnGame player, Dictionary validGoods, SortedList shopGoodsGroupDesc, HashSet exclude, int playerLv, int pos) { //先清除所有,后逐个添加 validGoods.Clear(); foreach (MarketShopGoodsDesc desc in shopGoodsGroupDesc.Values) { // 排除部分物品 if (exclude != null && exclude.Contains(desc.goodsIndex)) { continue; } // 不符合等级条件 if (playerLv < desc.limitLV_Min || (desc.limitLV_Max != 0 && playerLv > desc.limitLV_Max)) { continue; } var currBattleId = player.RoleData.ChapterData.BattleId; // 不符合章节条件 if (desc.Chapter_Min > 0 && (currBattleId < desc.Chapter_Min || currBattleId > desc.Chapter_Max)) { continue; } // 0号适用于所有pos, 减少策划配置数量 if (desc.goodsPos == 0 || pos == 0 || desc.goodsPos == pos) { validGoods.Add(desc.goodsIndex, desc); } } } public static bool NeedRefreshShop(PlayerOnGame player, ref OneShopData shopData, MarketShopDesc desc) { // 商店配置发送变化时重新刷新 if (shopData.GridGoods.Count != desc.goodsNum) { return true; } int level = player.GetLevel(); // 简单处理, 只要商店有一个商品不符合条件就重新刷新商店 for (int i = 0; i < shopData.GridGoods.Count; i++) { ref var goods = ref shopData.GridGoods[i]; var goodsDesc = MarketShopGoodsDescMgr.Instance.GetConfigByInternal(goods.GoodsId); if (goodsDesc == null) { return true; } if (level < goodsDesc.limitLV_Min || level > goodsDesc.limitLV_Max) { return true; } } return false; } public static void RefreshAllShop(PlayerOnGame player, bool notifyClient) { ref var shopList = ref player.RoleData.MarketShopData.ShopData; // 删除不存在的商店 for (int i = shopList.Count - 1; i >= 0; i--) { var desc = MarketShopDescMgr.Instance.GetConfig(shopList[i].ShopId); if (desc == null) { player.RoleData.MarketShopData.ShopData.RemoveAt(i); } } // 开启或刷新商店 foreach (var desc in MarketShopDescMgr.Instance.ItemTable.Values) { int index = GetShopDataIndex(player, desc.shopId); if (index == -1) { if (SystemUnlockSvc.IsUnlockSys(player, desc.shopUnlock)) { CreateShopDataIfNotExist(player, desc.shopId, notifyClient); } continue; } ref OneShopData shopData = ref GetShopDataByIndex(player, index); if (NeedRefreshShop(player, ref shopData, desc)) //配置变化强制刷新 { RefreshShop(player, ref shopData, desc); if (notifyClient) { SendPlayerShopRefreshRes(player, ref shopData); } } } } public static void SendPlayerShopRefreshRes(PlayerOnGame player, ref OneShopData shopData) { var res = new CSMarketShopRefreshRes { ShopData = shopData }; player.SendToClient((int)CSGameMsgID.MarketShopRefreshRes, ref res); } public static void OnNewDayStart(PlayerOnGame player, bool notifyClient) { ResetShopHeadTimes(player); } public static void ResetShopHeadTimes(PlayerOnGame player) { var now = AppTime.GetNowSysSecond(); ref var marketShopData = ref player.RoleData.MarketShopData; ref var shopList = ref marketShopData.ShopData; for (int i = 0; i < shopList.Count; i++) { ref var shopData = ref shopList[i]; if (AppTime.IsSameDay(now, shopData.LastHandRefreshTime)) { continue; } shopData.HandRefreshTimes = 0; shopData.LastHandRefreshTime = now; SendPlayerShopRefreshRes(player, ref shopData); } } } }