using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; using ProtoCSStruct; using Sog; namespace Game { /// ///抽卡系统 /// public static class RecruitSvc { public static void OnEnter(PlayerOnGame player) { ref var recruitData = ref player.RoleData.RecruitData; foreach (var kv in GachaDescMgr.Instance.ItemTable) { var desc = kv.Value; var index = -1; for (int i = 0; i < recruitData.Count; i++) { if (recruitData[i].PoolId == desc.InternalId) { index = i; break; } } if (index > -1) { continue; } var data = new RecruitData() { PoolId = desc.InternalId, RecruitCount = 0 }; for (var i = 0; i < (int)CommonQuality.Size - 1; i++) { data.Record.Add(0); //why do : find all quality records } recruitData.Add(data); } SyncData(player); } public static void RecruitChooseRewardReq(PlayerOnGame player, StructPacket packet) { ref var req = ref packet.GetMessage(); var poolId = req.PoolId; var res = new CSRecruitRewardRes(); res.PoolId = poolId; res.ChooseId = req.ChooseId; res.ChooseIndex = req.ChooseIndex; ref var recruitData = ref player.RoleData.RecruitData; var index = -1; for (var i = 0; i < recruitData.Count; i++) { if (recruitData[i].PoolId == poolId) { index = i; break; } } if (index < 0) { return ; } ref var data = ref player.RoleData.RecruitData[index]; for(int i = 0; i < data.ChoseReward.Count; i++) { if (data.ChoseReward[i] == req.ChooseId) { res.Ret = -1; player.SendToClient((int)CSGameMsgID.RecruitChooseRewardRes, ref res); return; } } var desc = GachaChooseRewardDescMgr.Instance.GetConfigByInternal(req.ChooseId); if (desc == null) { res.Ret = -1; player.SendToClient((int)CSGameMsgID.RecruitChooseRewardRes, ref res); return; } if(desc.gachaNum < data.TotalCount) { res.Ret = -1; player.SendToClient((int)CSGameMsgID.RecruitChooseRewardRes, ref res); return; } data.TotalCount -= desc.gachaNum; data.ChoseReward.Add(req.ChooseId); var addItemId = desc.rewardId[req.ChooseIndex]; var unifyOp = new UnifyOp(player, BillChangeItemReason.Recruit); unifyOp.AddGoods(desc.rewardType, addItemId, 1); unifyOp.DoOp(true, true, true); res.Ret = 0; res.Data.CopyFrom(ref data); player.SendToClient((int)CSGameMsgID.RecruitChooseRewardRes, ref res); } //抽卡入口 public static void Recruit(PlayerOnGame player, StructPacket packet) { ref var req = ref packet.GetMessage(); var poolId = req.PoolId; var cmd = (int)CSGameMsgID.RecruitRes; var res = new RecruitRes(); if (req.RecruitCount <= 0 || req.PoolId <= 0) { res.Ret = (int)CSErrCode.DescNotFound; player.SendToClient(cmd, ref res); return; } var desc = GachaDescMgr.Instance.GetConfigByInternal(poolId); if (desc == null) { res.Ret = (int)CSErrCode.DescNotFound; player.SendToClient(cmd, ref res); return; } if (desc.unlockId != 0 && !SystemUnlockSvc.IsUnlockSys(player, desc.unlockId)) { res.Ret = (int)CSErrCode.SysUnlocked; player.SendToClient(cmd, ref res); return; } var cost = new IDStringValue(); var ret = RecruitCost(player, poolId, req.RecruitCount, false, ref cost); if (ret != CSErrCode.None) { res.Ret = (int)CSErrCode.DescNotFound; player.SendToClient(cmd, ref res); return; } var unifyOp = new UnifyOp(player, BillChangeItemReason.Recruit); List result = new List(); for (var i = 0; i < req.RecruitCount; i++) { RecruitOne(player, unifyOp, req.PoolId, result); } var list = new List(); unifyOp.DoOp(sendGetItemMsg: false, retList: list); for (var i = 0; i < list.Count; i++) { var count = list[i].ChgCount; if (list[i].Type != (int)GoodsType.Gem) { res.Rewards.Add(list[i]); } else { //宝石需要裁开显示 for (var j = 0; j < count; j++) { var reward = new CSItemChgInfo(); var r = list[i]; reward.CopyFrom(ref r); reward.ChgCount = 1; if (res.Rewards.Count >= res.Rewards.GetMaxCount()) { player.SendToClient(cmd, ref res); res.Rewards.Clear(); } res.Rewards.Add(reward); } } } SyncData(player); PopPackageSvc.TriggerPopPackage(player,PackPopType.Gacha); GameTALogUtils.Recruit(player, 1, (int)desc.type, req.RecruitCount, cost, result); if (res.Rewards.Count > 0) { player.SendToClient(cmd, ref res); } } //看广告给抽卡 public static void RecruitAdv(PlayerOnGame player, int poolId) { var cmd = (int)CSGameMsgID.RecruitRes; var res = new RecruitRes(); var unifyOp = new UnifyOp(player, BillChangeItemReason.RecruitAdv); var cost = new IDStringValue(); RecruitCost(player, poolId, 1, true, ref cost); List result = new List(); RecruitOne(player, unifyOp, poolId, result); unifyOp.DoOp(sendGetItemMsg: true); var desc = GachaDescMgr.Instance.GetConfigByInternal(poolId); GameTALogUtils.Recruit(player, 1, (int)desc.type, 1, cost, result); SyncData(player); PopPackageSvc.TriggerPopPackage(player,PackPopType.Gacha); player.SendToClient(cmd, ref res); } //抽一次 public static CSErrCode RecruitOne(PlayerOnGame player, UnifyOp unifyOp, int poolId, List result) { var desc = GachaDescMgr.Instance.GetConfigByInternal(poolId); var cmd = (int)CSGameMsgID.RecruitRes; var res = new RecruitRes(); if (desc == null) { return CSErrCode.DescNotFound; } ref var recruitData = ref player.RoleData.RecruitData; var index = -1; for (var i = 0; i < recruitData.Count; i++) { if (recruitData[i].PoolId == poolId) { index = i; break; } } if (index < 0) { return CSErrCode.DescNotFound; } string rewardId = ""; int maxQualityIndex = 0; ref var data = ref recruitData[index]; data.SpCount++; data.TotalCount++; bool sp = false; if (data.SpCount >= desc.spguaranteedTimes && desc.spguaranteedTimes > 0) { rewardId = desc.spguaranteedReward; sp = true; } else { data.RecruitCount++; rewardId = GetRecruitRewardId(player, ref data, desc, out maxQualityIndex); //找到抽卡抽到上限制的最高品质 TraceLog.Trace("Recruit.RecruitOne uid={0} recruit rewardId={1} ,guaranteed quality={2}", player.UserID, rewardId, maxQualityIndex + 1); } //发奖 SendRecruitReward(player, unifyOp, rewardId, out var maxQualityVal, result); bool hasSp = false; foreach(var r in result) { if (r.Type == desc.spguaranteedType && maxQualityVal == (int)desc.spguaranteedQuality) { hasSp = true; break; } } if (hasSp) { data.SpCount = 0; TraceLog.Trace("Recruit.RecruitOne uid={0} get sp rewardId={1} ", player.UserID, rewardId ); } if (sp) { data.SpCount = 0; TraceLog.Trace("Recruit.RecruitOne uid={0} get sp baodi rewardId={1} ", player.UserID, rewardId); } else { //抽卡后的处理 maxQualityIndex = Math.Max(maxQualityIndex, maxQualityVal - 1); TraceLog.Trace("Recruit.RecruitOne uid={0} get rewardId={1} quality={2}", player.UserID, rewardId, maxQualityIndex + 1); RecruitProcess(player, ref data, desc, maxQualityIndex); } TaskEXEvent.TriggerGemDrawN(player, 1); return CSErrCode.None; } //获取抽卡奖励 private static string GetRecruitRewardId(PlayerOnGame player, ref RecruitData recruitData, GachaDesc desc, out int maxQualityIndex) { maxQualityIndex = 0; var rewardId = desc.reward; var qualities = desc.guaranteedQuality; for (var i = 0; i < recruitData.Record.Count; i++) { var quality = i + 1; var times = recruitData.Record[i]; //已抽卡次数 var limitTimes = 0; var jd = -1; for (var j = 0; j < qualities.Length; j++) { if ((int)qualities[j] == quality) { jd = j; limitTimes = desc.guaranteedTimes[j]; //保底次数 break; } } if (limitTimes == 0) { continue; } if (times >= (limitTimes - 1) && jd > -1) { rewardId = desc.guaranteedReward[jd]; maxQualityIndex = i; TraceLog.Debug( "Recruit.GetRecruitRewardId uid={0}, recruit trigger replace rewardId={1} quality={2}", player.UserID, rewardId, quality); } } return rewardId; } //发送奖励 private static void SendRecruitReward(PlayerOnGame player, UnifyOp unifyOp, string rewardId, out int maxQualityVal, List result) { maxQualityVal = 0; var rewards = RewardSvc.Reward(player, rewardId, false); foreach (var reward in rewards) { var quality = (int)GetDescQuality((GoodsType)reward.type, reward.code); if (quality == 0) { TraceLog.Error("Recruit.SendRecruitReward uid={0}, reward quality=0, type={1}, code={2}", player.UserID, reward.type, reward.code); continue; } maxQualityVal = Math.Max(maxQualityVal, quality); unifyOp.AddGoods(reward.type, reward.code, reward.amount); result.Add(new TypeIDValueString() { Type = reward.type, Id = new FixedStructString64(reward.code), Value = (int)reward.amount }); } TraceLog.Debug("Recruit.SendRecruitReward uid={0}, get reward recruit max quality={1}", player.UserID, maxQualityVal); } //抽卡后处理 private static void RecruitProcess(PlayerOnGame player, ref RecruitData recruitData, GachaDesc desc, int maxQualityIndex) { for (var i = 0; i < recruitData.Record.Count; i++) { var quality = (CommonQuality)(i + 1); if (i > maxQualityIndex) { if (desc.guaranteedQuality.Contains(quality)) { recruitData.Record[i]++; TraceLog.Debug("Recruit.RecruitProcess uid={0},add recruit num. quality={1},recruit num={2}", player.UserID, (int)quality, recruitData.Record[i]); } } else { recruitData.Record[i] = 0; //把当前替换的品质都置为0 TraceLog.Debug("Recruit.RecruitProcess uid={0},reset quality={1}", player.UserID, (int)quality); } } string info = ""; for (int i = 0; i < recruitData.Record.Count; i++) { var num = recruitData.Record[i]; info += "quality=" + (i + 1) + ",num=" + num + "|"; } TraceLog.Debug("Recruit.RecruitProcess uid={0},recruit record info|={1}", player.UserID, info); } //获取品质 private static CommonQuality GetDescQuality(GoodsType type, string cfgId) { switch (type) { case GoodsType.Items: return ItemDescMgr.Instance.GetConfig(cfgId)?.quality ?? CommonQuality.None; case GoodsType.Equipment: return EquipDescMgr.Instance.GetConfig(cfgId)?.quality ?? CommonQuality.None; case GoodsType.Gem: return GemDescMgr.Instance.GetConfig(cfgId)?.quality ?? CommonQuality.None; case GoodsType.EquipSkin: return RoleModelDescMgr.Instance.GetConfig(int.Parse(cfgId))?.quality ?? CommonQuality.None; case GoodsType.Wing: return CommonQuality.DarkGold; // case GoodsType.Exp: default: break; } return CommonQuality.None; } //抽卡消耗 public static CSErrCode RecruitCost(PlayerOnGame player, int poolId, int recruitCount, bool free, ref IDStringValue cost) { ref var recruitData = ref player.RoleData.RecruitData; var index = -1; for (var i = 0; i < recruitData.Count; i++) { if (recruitData[i].PoolId == poolId) { index = i; break; } } if (index < 0) { return CSErrCode.Fail; } ref var data = ref recruitData[index]; var now = GameServerUtils.GetTimeSecond(); if (free && !AppTime.IsSameDay(data.LastFreeTime, now)) { data.LastFreeTime = now; //免费 data.NextFreeTime = AppTime.GetTodayStartTime(GameServerUtils.GetTimeSecond()) + AppTime.SECONDS_ADAY + 1; return CSErrCode.None; } var desc = GachaDescMgr.Instance.GetConfigByInternal(poolId); var itemId = desc.costItemId; var unifyOp = new UnifyOp(player, BillChangeItemReason.Recruit); var itemCount = unifyOp.GetItemCount((int)GoodsType.Items, itemId); var canRecruitCount = (int)(itemCount / Math.Max(1, desc.costItemValue)); //能抽几次 if (canRecruitCount < recruitCount) { recruitCount -= canRecruitCount; unifyOp.CostItem(itemId, desc.costItemValue * canRecruitCount); //钻石替换 unifyOp.CostDiamond(desc.costDiamondValue * recruitCount); cost.Id.SetString(ItemId.Diamond); cost.Value = desc.costItemValue * recruitCount; var ret = unifyOp.CheckOp(); if (ret != CSErrCode.None) { return ret; } } else { unifyOp.CostItem(itemId, desc.costItemValue * recruitCount); cost.Id.SetString(itemId); cost.Value = desc.costItemValue * recruitCount; var ret = unifyOp.CheckOp(); if (ret != CSErrCode.None) { return ret; } } return unifyOp.DoOp(); } public static void SyncData(PlayerOnGame player) { var sync = new RecruitInfoSync(); sync.RecruitData.CopyFrom(ref player.RoleData.RecruitData); player.SendToClient((int)CSGameMsgID.RecruitInfoSync, ref sync); } } }