using System.Collections.Generic; using System.Linq; using UnityEngine; using LitJson; using System; public class ArenaManager : GameSystemManager { public readonly int rankType = 1; // 榜单类型 public readonly int corssRankType = 151; // 榜单类型 public readonly int funcId = 27; // 功能Id public readonly int DeployTroopsFuncId = 31; // 布阵功能ID public readonly int BattleChangeTabFuncId = 32; // 战斗能切换页签功能ID public readonly int recType = 308; // 演武场玩家挑战记录类型 308 public readonly int ChallengeMoneyType = 53; public readonly int NeedChallengeMoneyCnt = 1; public readonly int RecordType = 308; // 演武场玩家挑战记录 public int initialPoints; // 玩家初始积分 public int challengeRecordCount; // 被挑战记录条数(不超过50) public int challengeTicketLimit; // 挑战券基础存储上限 public int[][] fixedChallengeRewards; // 固定挑战奖励 [[物品ID,个数], ...] public int[][] matchVictoryPoints; // 匹配位置挑战胜利对应增减积分 [[挑战方增加积分, 防守方扣除积分], ...],长度即为匹配的人数,从最高分开始配置 public int lowerRankStart; // 从比自己低几个段的名次开始匹配 public int rankStep; // 每个匹配段跨x名,如配置 2|10,即代表从自己名次向后20名开始匹配,每跨10名匹配1人 public Dictionary robotMatchCounts; // 玩家所在小于等于该名次对应匹配机器人数 {名次:[匹配机器人数下限, 上限], ...} public int currencyType; // 刷新匹配固定消耗货币类型 public int currencyValue; // 刷新匹配固定消耗货币值 public Dictionary dailyRankRewards; // 每日排行奖励 {"名次":[[物品ID, 个数,是否拍品], ...], ...} 配置的名次key,自动按小于等于对应名次给奖励 public Dictionary seasonRankRewards; // 赛季排行奖励 {"名次":[[物品ID, 个数,是否拍品], ...], ...} public Dictionary crossRewardTemps;//数值2:跨服赛季排行奖励模版 {"互通服务器数";对应活动排行奖励表模版编号, ...} 按小于等于判断,大于最大配置的服务器数时默认取配置中最大服务器数对应奖励 public Dictionary crossWeekRewards; public uint score; // 当前积分 public int totalWinCnt; //累计胜利次数 public List matchInfoList = new List(); //用于用来拿战斗胜利失败的头像信息 public Dictionary allFaceInfoDict = new Dictionary(); public Dictionary> gameRecDict = new Dictionary>(); // public uint atkPlayerId; public Redpoint parentRedpoint = new Redpoint(MainRedDot.MainChallengeRedpoint, MainRedDot.ArenaRepoint); public Redpoint challengeButtonRedpoint; public int nowAwardTabIndex = 0; public event Action OnArenaMatchListEvent; public event Action OnUpdateArenaPlayerInfo; public event Action OnUpdateGameRecInfo; private int lastLocalWeekday; // 上次本服weekday (1-7, 周一=1, 周日=7) private int lastCrossWeekday; // 上次跨服weekday public override void Init() { DTC0102_tagCDBPlayer.beforePlayerDataInitializeEventOnRelogin += BeforePlayerDataInitializeEventOnRelogin; PlayerDatas.Instance.playerDataRefreshEvent += PlayerDataRefresh; GlobalTimeEvent.Instance.secondEvent += OnSecondEvent; CrossServerBaseManager.Instance.OnCrossZoneInfoUpdateEvent += OnCrossZoneInfoUpdate; InitTable(); InitRedpoint(); InitCrossServerState(); } public override void Release() { DTC0102_tagCDBPlayer.beforePlayerDataInitializeEventOnRelogin -= BeforePlayerDataInitializeEventOnRelogin; PlayerDatas.Instance.playerDataRefreshEvent -= PlayerDataRefresh; GlobalTimeEvent.Instance.secondEvent -= OnSecondEvent; CrossServerBaseManager.Instance.OnCrossZoneInfoUpdateEvent -= OnCrossZoneInfoUpdate; } void InitCrossServerState() { lastLocalWeekday = TimeUtility.GetCommonWeekday(); // zoneID=0本服 int crossZoneID = GetCrossZoneID(); lastCrossWeekday = crossZoneID > 0 ? TimeUtility.GetCommonWeekday(crossZoneID) : lastLocalWeekday; } void OnSecondEvent() { CheckCrossWeekChange(); } // 跨服状态更新事件处理 void OnCrossZoneInfoUpdate(int mapID) { if (mapID != DataMapID) return; bool currentIsOpenCrossServer = IsOpenCrossServer(); // 只会从未跨服变为跨服,变为跨服时关闭ArenaWin if (currentIsOpenCrossServer) { CloseArenaWinAndClearData(); } int crossZoneID = GetCrossZoneID(); lastCrossWeekday = crossZoneID > 0 ? TimeUtility.GetCommonWeekday(crossZoneID) : TimeUtility.GetCommonWeekday(); } int GetCrossZoneID() { var crossZoneInfo = CrossServerBaseManager.Instance.GetCrossZoneInfo(DataMapID); return crossZoneInfo != null ? (int)crossZoneInfo.ZoneID : 0; } void CheckCrossWeekChange() { int currentLocalWeekday = TimeUtility.GetCommonWeekday(); // zoneID=0本服 int crossZoneID = GetCrossZoneID(); int currentCrossWeekday = crossZoneID > 0 ? TimeUtility.GetCommonWeekday(crossZoneID) : currentLocalWeekday; // 条件1: 本服时间过周 (weekday从7变为1, 周日->周一) if (lastLocalWeekday == 7 && currentLocalWeekday == 1) { CloseArenaWinAndClearData(); } lastLocalWeekday = currentLocalWeekday; bool currentIsOpenCrossServer = IsOpenCrossServer(); if (!currentIsOpenCrossServer) return; // 条件2: 跨服时间过周 (weekday从7变为1, 周日->周一) if (crossZoneID > 0 && lastCrossWeekday == 7 && currentCrossWeekday == 1) { CloseArenaWinAndClearData(); } lastCrossWeekday = currentCrossWeekday; } void CloseArenaWinAndClearData() { if (UIManager.Instance.IsOpened()) { UIManager.Instance.CloseWindow(); } matchInfoList.Clear(); gameRecDict.Clear(); } public void BeforePlayerDataInitializeEventOnRelogin() { matchInfoList.Clear(); gameRecDict.Clear(); } void InitRedpoint() { challengeButtonRedpoint = new Redpoint(MainRedDot.ArenaRepoint, GetRedPonitId(1)); } void InitTable() { FuncConfigConfig config = FuncConfigConfig.Get("ArenaSet"); initialPoints = int.Parse(config.Numerical1); challengeRecordCount = int.Parse(config.Numerical2); challengeTicketLimit = int.Parse(config.Numerical3); fixedChallengeRewards = JsonMapper.ToObject(config.Numerical4); config = FuncConfigConfig.Get("ArenaMatch"); matchVictoryPoints = JsonMapper.ToObject(config.Numerical1); string[] matchParams = config.Numerical2.Split('|'); lowerRankStart = int.Parse(matchParams[0]); rankStep = int.Parse(matchParams[1]); robotMatchCounts = ConfigParse.GetDic(config.Numerical3); string[] costParams = config.Numerical4.Split('|'); currencyType = int.Parse(costParams[0]); currencyValue = int.Parse(costParams[1]); config = FuncConfigConfig.Get("ArenaBillboradAward"); dailyRankRewards = ConfigParse.ParseIntArray2Dict(config.Numerical1); seasonRankRewards = ConfigParse.ParseIntArray2Dict(config.Numerical2); config = FuncConfigConfig.Get("ArenaCross"); crossRewardTemps = ConfigParse.ParseIntDict(config.Numerical2); } public void UpdateRedPonit() { parentRedpoint.state = RedPointState.None; challengeButtonRedpoint.state = RedPointState.None; if (!FuncOpen.Instance.IsFuncOpen(funcId)) return; if (UIHelper.GetMoneyCnt(ChallengeMoneyType) > 0) { challengeButtonRedpoint.state = RedPointState.Simple; } } public bool TryGetPlayerInfo(uint playerID, out ArenaMatchInfo info) { return allFaceInfoDict.TryGetValue(playerID, out info); } // 1 挑战按钮 public int GetRedPonitId(int num) { return MainRedDot.ArenaRepoint * 10 + num; } private void PlayerDataRefresh(PlayerDataType type) { if (type != PlayerDataType.ChallengeVoucher) return; UpdateRedPonit(); } public int GetMaxChallengeCount() { return challengeTicketLimit + InvestModel.Instance.GetArenaAddMaxCount(); } public void OnArenaMatchList(HA922_tagSCArenaMatchList vNetData) { if (vNetData == null || vNetData.MatchList.IsNullOrEmpty()) return; matchInfoList.Clear(); foreach (var item in vNetData.MatchList) { var matchInfo = new ArenaMatchInfo { PlayerID = item.PlayerID, PlayerName = item.PlayerName, Lv = item.LV, RealmLV = item.RealmLV, FightPower = (ulong)item.FightPowerEx * 100000000 + (ulong)item.FightPower, Face = item.Face, FacePic = item.FacePic, TitleID = item.TitleID, ServerID = item.ServerID, }; matchInfoList.Add(matchInfo); allFaceInfoDict[item.PlayerID] = matchInfo; } matchInfoList.Reverse(); OnArenaMatchListEvent?.Invoke(); } public void UpdateArenaPlayerInfo(HA923_tagSCArenaPlayerInfo vNetData) { if (vNetData == null) return; this.score = vNetData.Score; totalWinCnt = (int)vNetData.WinCnt; OnUpdateArenaPlayerInfo?.Invoke(); } public void UpdateGameRecInfo(HA009_tagSCGameRecInfo vNetData) { if (vNetData == null || vNetData.RecType != recType) return; gameRecDict.Clear(); uint recID = vNetData.RecID; if (!gameRecDict.ContainsKey(recID)) gameRecDict[recID] = new List(); foreach (var rec in vNetData.RecList) { try { var userData = JsonMapper.ToObject(rec.UserData); string name = userData["Name"].ToString(); int addScore = int.Parse(userData["AddScore"].ToString()); ulong fightPower = ulong.Parse(userData["FightPower"].ToString()); uint serverID = 0; if (userData.ContainsKey("ServerID")) { serverID = uint.Parse(userData["ServerID"].ToString()); } uint cross = 0; if (userData.ContainsKey("Cross")) { cross = uint.Parse(userData["Cross"].ToString()); } var arenaGameRec = new ArenaGameRec { Time = rec.Time, Value1 = rec.Value1, Value2 = rec.Value2, Value3 = rec.Value3, Value4 = rec.Value4, Value5 = rec.Value5, Value6 = rec.Value6, Value7 = rec.Value7, Value8 = rec.Value8, Name = name, AddScore = addScore, FightPower = fightPower, ServerID = serverID, Cross = cross }; gameRecDict[recID].Add(arenaGameRec); if (recID == PlayerDatas.Instance.baseData.PlayerID) { allFaceInfoDict[rec.Value3] = new ArenaMatchInfo { Face = rec.Value5, FacePic = rec.Value6, RealmLV = (ushort)rec.Value7, Lv = (ushort)rec.Value8, PlayerName = name, FightPower = fightPower, }; } } catch (Exception ex) { Debug.LogError($"JSON解析错误: {ex.Message}, UserData: {rec.UserData}"); continue; } } OnUpdateGameRecInfo?.Invoke(); } public Dictionary GetArenaAwardDict(int functionOrder) { return functionOrder == 0 ? seasonRankRewards : dailyRankRewards; } public void SendViewGameRecPack() { CA008_tagCSViewGameRec pack = new CA008_tagCSViewGameRec(); pack.RecType = (ushort)RecordType; GameNetSystem.Instance.SendInfo(pack); } public void SendArenaMatch(bool isRefresh = false) { CB209_tagCSArenaMatch pack = new CB209_tagCSArenaMatch(); pack.IsRefresh = isRefresh ? (byte)1 : (byte)0; GameNetSystem.Instance.SendInfo(pack); } public bool IsTimeInvalid(uint time) { DateTime dateTime = TimeUtility.GetTime(time); GetCurrentSeasonDates(out DateTime seasonStartDate, out DateTime seasonEndDate); return dateTime < seasonStartDate || dateTime > seasonEndDate; } /// /// 根据索引和排序方向获取积分 /// /// 索引位置(从0开始) /// true-正序,false-倒序 /// true-挑战方增加积分,false-防守方扣除积分 /// 对应的积分值 public int GetChallengePoints(int index, bool isAscending = false, bool isChallenger = true) { // 检查索引是否在有效范围内 if (matchVictoryPoints.IsNullOrEmpty() || index < 0 || index >= matchVictoryPoints.Length) return 0; int targetIndex = isAscending ? index : matchVictoryPoints.Length - 1 - index; return matchVictoryPoints[targetIndex][isChallenger ? 0 : 1]; } /// /// 获得当前赛季的起止日期。赛季从周一 00:00:00 开始,到周日 23:59:59 结束。 /// 服务端定义周一为每周的第一天。 /// /// 输出参数:赛季的起始日期(本周一 00:00:00) /// 输出参数:赛季的结束日期(本周日 23:59:59) public void GetCurrentSeasonDates(out DateTime seasonStartDate, out DateTime seasonEndDate) { DateTime now = TimeUtility.ServerNow; // 在 .NET 中,DayOfWeek 枚举 Sunday = 0, Monday = 1, ..., Saturday = 6。 // 为了符合周一是一周第一天的计算标准,我们将周日视为一周的第7天。 int currentDayOfWeek = (int)now.DayOfWeek; if (currentDayOfWeek == 0) // 如果是周日 (Sunday = 0) { currentDayOfWeek = 7; } // 从当前日期减去相应的天数,得到周一的日期。 // 例如,如果是周三(3),则需要减去 3-1=2 天。 // 如果是周日(7),则需要减去 7-1=6 天。 DateTime monday = now.AddDays(-(currentDayOfWeek - 1)); // 设置赛季的起始时间为周一的 0点0分0秒 seasonStartDate = new DateTime(monday.Year, monday.Month, monday.Day, 0, 0, 0); // 赛季的结束日期是开始日期(周一)加上6天,即周日 DateTime sunday = seasonStartDate.AddDays(6); // 设置赛季的结束时间为周日的 23点59分59秒 seasonEndDate = new DateTime(sunday.Year, sunday.Month, sunday.Day, 23, 59, 59); } /// /// 根据recID获取按时间从大到小排序的List /// /// 记录ID /// 输出参数:按时间从大到小排序的ArenaGameRec列表 /// 如果recID存在且成功获取列表返回true,否则返回false public bool TryGetSortedArenaGameRecList(uint recID, out List sortedList) { sortedList = null; if (!gameRecDict.ContainsKey(recID)) return false; sortedList = new List(gameRecDict[recID]); sortedList.Sort((a, b) => b.Time.CompareTo(a.Time)); // 按时间从大到小排序 return true; } //优先取进攻队伍的战力,没有进攻队伍,取主线战力 public long GetMyFightPower() { var arenaDefenseTeam = TeamManager.Instance.GetTeam(BattlePreSetType.Arena); return arenaDefenseTeam.IsEmpty() || arenaDefenseTeam.GetTeamHeroCount() <= 0 ? PlayerDatas.Instance.baseData.FightPower : FightPowerManager.Instance.GetTeamFightPower(TeamManager.Instance.GetTeamID(BattlePreSetType.Arena), false); } public readonly int DataMapID = 3; public bool IsOpenCrossServer() { bool isOpenCrossServer = CrossServerBaseManager.Instance.IsOpenCrossServer(DataMapID); return isOpenCrossServer; } public Dictionary GetCrossArenaAwardDict(int functionOrder) { return functionOrder switch { 0 => dailyRankRewards, 1 => seasonRankRewards, 2 => GetCrossWeekRewards(), _ => new Dictionary() }; } public Dictionary GetCrossWeekRewards() { var crossZoneInfo = CrossServerBaseManager.Instance.GetCrossZoneInfo(DataMapID); if (crossZoneInfo == null || crossZoneInfo.ServerIDList == null || crossRewardTemps.IsNullOrEmpty()) { return new Dictionary(); } int serverCount = crossZoneInfo.ServerIDList.Length; // 先对key进行排序,避免遍历顺序依赖 var sortedKeys = crossRewardTemps.Keys.OrderBy(k => k).ToList(); int templateID = 0; int matchedKey = 0; foreach (var key in sortedKeys) { if (key <= serverCount && key >= matchedKey) { matchedKey = key; templateID = crossRewardTemps[key]; } } // 如果没有匹配的key(serverCount小于所有配置的key),取最小key对应的奖励 if (templateID == 0 && sortedKeys.Count > 0) { var minKey = sortedKeys[0]; templateID = crossRewardTemps[minKey]; } if (templateID == 0) return new Dictionary(); var rankList = ActBillboardAwardConfig.GetRankASortList(templateID); if (rankList.IsNullOrEmpty()) return new Dictionary(); var result = new Dictionary(); foreach (var rankA in rankList) { var config = ActBillboardAwardConfig.GetConfig(templateID, rankA); if (config?.AwardItemList != null) { result[rankA] = config.AwardItemList; } } return result; } /// /// 获得当前赛季的起止日期(跨服版本)。赛季从周一 00:00:00 开始,到周日 23:59:59 结束。 /// 服务端定义周一为每周的第一天。 /// /// 跨服zoneID,0表示本服时间,非0表示跨服时间 /// 输出参数:赛季的起始日期(本周一 00:00:00) /// 输出参数:赛季的结束日期(本周日 23:59:59) public void GetCrossCurrentSeasonDates(int zoneID, out DateTime seasonStartDate, out DateTime seasonEndDate) { DateTime now = TimeUtility.GetCommServerNow(zoneID); // 为了符合周一是一周第一天的计算标准,将周日视为一周的第7天。 int currentDayOfWeek = (int)now.DayOfWeek; if (currentDayOfWeek == 0) // 如果是周日 (Sunday = 0) { currentDayOfWeek = 7; } DateTime monday = now.AddDays(-(currentDayOfWeek - 1)); seasonStartDate = new DateTime(monday.Year, monday.Month, monday.Day, 0, 0, 0); DateTime sunday = seasonStartDate.AddDays(6); seasonEndDate = new DateTime(sunday.Year, sunday.Month, sunday.Day, 23, 59, 59); } } public class ArenaMatchInfo { public uint PlayerID; //目标玩家ID public string PlayerName; public ushort Lv; //等级 public ushort RealmLV; //境界,机器人读境界表取等级对应境界 public uint TitleID; public ulong FightPower; //战力 public uint Face; //基本脸型 public uint FacePic; //头像框 public uint ServerID; } public class ArenaGameRec { public uint Time; //战斗时间戳 public uint Value1; //更新积分,战斗后的最终积分 public uint Value2; //攻击类型 1-发起攻击的,2-被攻击的 public uint Value3; //相对攻击类型的目标玩家ID public uint Value4; //是否获胜 1-获胜;2-失败 public uint Value5; //目标头像 public uint Value6; //目标头像框 public uint Value7; //目标官职 public uint Value8; //目标等级 public string Name; //目标名称 public int AddScore; //本次自己变更的积分,有正负 public ulong FightPower; //目标战力 public int TitileId; //未来接入 public uint ServerID; //对手区服ID public uint Cross; //是否跨服记录 }