using System; using System.Collections.Generic; using System.Linq; using Cysharp.Threading.Tasks; using LitJson; using UnityEngine; //公会攻防战 public class GuildAtkDefBatManager : GameSystemManager { /// 攻防战的功能MapID public const int DataMapID = 33000; /// 周期配置 [[休战期星期x, ...], [备战期星期x, ...], [激战期星期x, ...]] public int[][] cycleConfig; /// 每人可挑战次数,胜负都算次数 public int challengeCount; /// 备战期开始时参与条件:公会成员达x人 public int prepareMemberLimit; /// 备战开始前x天 public int prepareDayBefore; /// 累计活跃达Y public int prepareActiveLimit; /// 按历史总战力倒序排序后每X个随机匹配 public int matchGroupSize; /// 可调整成员位置的权限所需成员职位:2-副盟,3-盟主 public int adjustPositionJobLevel; /// 挑战胜利奖励 [[物品ID, 个数], ...] public int[][] winAward; /// 挑战失败奖励 [[物品ID, 个数], ...],也是扫荡奖励 public int[][] loseAward; /// 公会胜利奖励 [[物品ID, 个数], ...] public int[][] guildWinAward; /// 公会失败奖励 [[物品ID, 个数], ...] public int[][] guildLoseAward; /// 品质格子最大产出数 {品质:最大产出数},如 {2:9, 3:1},1-普通;2-稀有;3-珍稀,普通品质不限制 public Dictionary qualityMaxOutput; /// 默认最大格子数,当参与成员小于等于该配置时生效 public int defaultMaxGrid; /// 动态扩容格子数,当参与成员大于默认最大格子数时,每次动态扩总格子数。最大总格子=默认最大格子数+动态扩容格子数*n,n=向上取整((参与成员-默认最大格子数)/扩容格子数) public int expandGridCount; public int shopType; /// 日志最多展示条数 public int maxLogCount; public override void Init() { DTC0102_tagCDBPlayer.beforePlayerDataInitializeEventOnRelogin += OnBeforePlayerDataInitializeEventOnRelogin; GuildManager.Instance.EnterOrQuitGuildEvent += OnEnterOrQuitGuildEvent; StoreModel.Instance.RefreshBuyShopLimitEvent += OnRefreshShopLimit; ParseConfig(); } public override void Release() { DTC0102_tagCDBPlayer.beforePlayerDataInitializeEventOnRelogin -= OnBeforePlayerDataInitializeEventOnRelogin; GuildManager.Instance.EnterOrQuitGuildEvent -= OnEnterOrQuitGuildEvent; StoreModel.Instance.RefreshBuyShopLimitEvent -= OnRefreshShopLimit; } private void OnRefreshShopLimit() { UpdateRedpoint(); } private void OnBeforePlayerDataInitializeEventOnRelogin() { hasClosedMainGuildAtkDefBatTip = false; ResetBattleData(); } private void OnEnterOrQuitGuildEvent(bool isEnter) { if (!isEnter) { ResetBattleData(); CurrentChallengeTargetPlayerID = 0; OnUpdateAtkDefBatInfoEvent?.Invoke(); UpdateRedpoint(); } } void ParseConfig() { var configSet = FuncConfigConfig.Get("FmAtkDefBatSet"); cycleConfig = JsonMapper.ToObject(configSet.Numerical1); challengeCount = int.Parse(configSet.Numerical2); var configPrepare = FuncConfigConfig.Get("FmAtkDefBatPrepare"); prepareMemberLimit = int.Parse(configPrepare.Numerical1); var prepareArr = configPrepare.Numerical2.Split('|'); prepareDayBefore = int.Parse(prepareArr[0]); prepareActiveLimit = int.Parse(prepareArr[1]); matchGroupSize = int.Parse(configPrepare.Numerical3); adjustPositionJobLevel = int.Parse(configPrepare.Numerical4); var configAward = FuncConfigConfig.Get("FmAtkDefBatAward"); winAward = JsonMapper.ToObject(configAward.Numerical1); loseAward = JsonMapper.ToObject(configAward.Numerical2); guildWinAward = JsonMapper.ToObject(configAward.Numerical3); guildLoseAward = JsonMapper.ToObject(configAward.Numerical4); var configTreasure = FuncConfigConfig.Get("FmAtkDefBatTreasure"); qualityMaxOutput = ConfigParse.ParseIntDict(configTreasure.Numerical1); defaultMaxGrid = int.Parse(configTreasure.Numerical2); expandGridCount = int.Parse(configTreasure.Numerical3); var configShop = FuncConfigConfig.Get("FmAtkDefBatShop"); shopType = int.Parse(configShop.Numerical1); var configRecord = FuncConfigConfig.Get("FmAtkDefBatRecord"); maxLogCount = int.Parse(configRecord.Numerical1); } /// 当前所在时期信息通知:0-无;1-休战期;2-备战期;3-激战期 public int BatState { get; private set; } /// 己方公会基础信息 public HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatFamily MyFamilyInfo { get; private set; } /// 敌方公会基础信息 (若无参战资格或未匹配到则为null) public HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatFamily EnemyFamilyInfo { get; private set; } /// 己方参与成员字典,Key为PlayerID,方便差异更新与删除查找 public Dictionary MyFamilyMembers = new Dictionary(); /// 当前挑战的防守方成员ID(用于战场界面显示敌方信息) public uint CurrentChallengeTargetPlayerID { get; set; } /// 敌方参与成员字典,Key为PlayerID,方便差异更新与删除查找 public Dictionary EnemyFamilyMembers = new Dictionary(); public int MyFamilyID => PlayerDatas.Instance?.fairyData?.fairy?.FamilyID ?? 0; public event Action OnUpdateAtkDefBatInfoEvent; /// /// 接收 A525 封包,处理全量/增量/删除 /// public void UpdateAtkDefBatInfo(HA525_tagSCFamilyAtkDefBatInfo pack) { if (pack == null) return; // 1. 数据重置判断:从非备战期变为备战期,或者收到无参与资格(公会数=1 且 参战成员=0) bool isEnterPrepare = BatState != 2 && pack.BatState == 2; bool isNotQualified = pack?.FamilyCount == 1 && pack?.BatFamilyList?.Length > 0 && pack?.BatFamilyList[0]?.BatMemCount == 0; if (isEnterPrepare || isNotQualified) ResetBattleData(); BatState = pack.BatState; // 3. 差异更新公会与成员数据 if (pack.BatFamilyList != null) { for (int i = 0; i < pack.FamilyCount; i++) { var familyData = pack.BatFamilyList[i]; if (familyData == null) continue; bool isMyFamily = familyData.FamilyID == MyFamilyID; if (isMyFamily) { MyFamilyInfo = UpdateFamilyBaseInfo(MyFamilyInfo, familyData); UpdateMembersDiff(MyFamilyMembers, familyData.BatMemList); } else { EnemyFamilyInfo = UpdateFamilyBaseInfo(EnemyFamilyInfo, familyData); UpdateMembersDiff(EnemyFamilyMembers, familyData.BatMemList); } } } // 4. 处理成员退出删除同步 if (pack.DelMemID > 0) { if (MyFamilyMembers.ContainsKey(pack.DelMemID)) { MyFamilyMembers.Remove(pack.DelMemID); } else if (EnemyFamilyMembers.ContainsKey(pack.DelMemID)) { EnemyFamilyMembers.Remove(pack.DelMemID); } } OnUpdateAtkDefBatInfoEvent?.Invoke(); // 解析宝库格子记录 ParseTreasureGridRecords(); UpdateRedpoint(); } /// /// 重置新一轮攻防战数据缓存 /// private void ResetBattleData() { MyFamilyInfo = null; EnemyFamilyInfo = null; MyFamilyMembers.Clear(); EnemyFamilyMembers.Clear(); atkDefBatLogs.Clear(); treasureGridRecords.Clear(); hasShowedMainRedpoint = false; } /// /// 更新公会基础信息(剥离掉成员数组单独管理) /// private HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatFamily UpdateFamilyBaseInfo( HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatFamily targetInfo, HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatFamily sourceData) { if (targetInfo == null) targetInfo = new HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatFamily(); targetInfo.FamilyID = sourceData.FamilyID; targetInfo.FamilyName = UIHelper.ServerStringTrim(sourceData.FamilyName); targetInfo.ServerID = sourceData.ServerID; targetInfo.EmblemID = sourceData.EmblemID; targetInfo.EmblemWord = UIHelper.ServerStringTrim(sourceData.EmblemWord); targetInfo.TotalStar = sourceData.TotalStar; targetInfo.OverState = sourceData.OverState; targetInfo.BatMemCount = sourceData.BatMemCount; return targetInfo; } /// /// 对参战成员进行差异替换更新 /// private void UpdateMembersDiff(Dictionary memberDict, HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatMem[] memList) { if (memList == null) return; for (int i = 0; i < memList.Length; i++) { var mem = memList[i]; if (mem == null) continue; memberDict[mem.PlayerID] = mem; } } /// /// 前端自行排序:获取防守成员列表(按防守类型从小到大 -> 站位编号从小到大排序) /// public List GetSortedDefenders(bool isMyFamily) { var dict = isMyFamily ? MyFamilyMembers : EnemyFamilyMembers; var list = new List(); foreach (var mem in dict.Values) { if (mem.DefType != 0) // DefType非0代表是防守人员 list.Add(mem); } // 排序规则:主将(1) -> 副将(2) -> 镇卫(3),同类型内按PosNum从小到大排序 list.Sort((a, b) => { int defTypeCompare = a.DefType.CompareTo(b.DefType); if (defTypeCompare != 0) return defTypeCompare; return a.PosNum.CompareTo(b.PosNum); }); return list; } /// /// 计算成员总战力(long类型) /// 服务端将战力拆分为 FightPower(余亿部分)和 FightPowerEx(整除亿部分)两个uint字段 /// public static long CalcTotalFightPower(uint fightPower, uint fightPowerEx) { return fightPower + fightPowerEx * Constants.ExpPointValue; } /// /// 获取用于核心成员展示的排序列表(最多4个) /// /// 是否己方公会 /// 当前显示状态: 2-备战期, 3-激战期, 4/5-休战期 /// 排序后的成员列表(最多4个) public List GetSortedCoreMembers(bool isMyFamily, int displayState) { var dict = isMyFamily ? MyFamilyMembers : EnemyFamilyMembers; var list = new List(dict.Values); if (list.Count <= 0) return list; bool isPrepareOrBattle = displayState == 2 || displayState == 3; if (isPrepareOrBattle) { if (isMyFamily) { // 己方:按职位(FmLV: 3>2>1>0)降序 → 战力降序 var fairyData = PlayerDatas.Instance?.fairyData; list.Sort((a, b) => { int fmLVA = fairyData?.GetMember((int)a.PlayerID)?.FmLV ?? 0; int fmLVB = fairyData?.GetMember((int)b.PlayerID)?.FmLV ?? 0; if (fmLVB != fmLVA) return fmLVB.CompareTo(fmLVA); long powerA = CalcTotalFightPower(a.FightPower, a.FightPowerEx); long powerB = CalcTotalFightPower(b.FightPower, b.FightPowerEx); return powerB.CompareTo(powerA); }); } else { // 敌方:按职位(FmLV: 3>2>1>0)降序 → 战力降序 list.Sort((a, b) => { if (b.Job != a.Job) return b.Job.CompareTo(a.Job); long powerA = CalcTotalFightPower(a.FightPower, a.FightPowerEx); long powerB = CalcTotalFightPower(b.FightPower, b.FightPowerEx); return powerB.CompareTo(powerA); }); } } else { // 休战期(4/5):按星星(Star)降序 → 已战斗次数(BatCnt)降序 list.Sort((a, b) => { if (b.Star != a.Star) return b.Star.CompareTo(a.Star); return b.BatCnt.CompareTo(a.BatCnt); }); } // 最多取4个 if (list.Count > 4) list = list.GetRange(0, 4); return list; } /// /// 判断是否是系统公会 (按照文档:ID >= 10亿) /// public bool IsSystemFamily(uint familyID) { return familyID >= 1000000000; } /// /// 解析 KilledState,判定防守人员的某个属性百分比是否已经被击败 /// /// 属性索引位(从0开始计) public bool IsPropertyKilled(byte killedState, int propertyIndex) { return (killedState & (1 << propertyIndex)) != 0; } /// 发送更换参战成员请求 /// 要被替换的成员ID /// 替换A成员的成员ID public void SendChangeBatMember(uint playerIDA, uint playerIDB) { var pack = new CA635_tagCSFamilyAtkDefBatChangeMem(); pack.PlayerIDA = playerIDA; pack.PlayerIDB = playerIDB; GameNetSystem.Instance.SendInfo(pack); } public bool IsOpen(bool tip = false) { return FuncOpen.Instance.IsFuncOpen((int)FuncOpenEnum.GuildAtkDefBat, tip); } public bool HasFamilyJoinEligibility() { return !MyFamilyMembers.IsNullOrEmpty() && MyFamilyMembers.Count > 0; } public bool HasMyJoinEligibility() { if (!HasFamilyJoinEligibility()) return false; if (MyFamilyMembers.IsNullOrEmpty()) return false; return MyFamilyMembers.ContainsKey(PlayerDatas.Instance.PlayerId); } private bool hasClosedMainGuildAtkDefBatTip; public bool ShouldShowMainGuildAtkDefBatTip() { if (hasClosedMainGuildAtkDefBatTip) return false; if (!IsOpen()) return false; return BatState == 3 && HasFamilyJoinEligibility() && HasMyJoinEligibility(); } public void ClearMainGuildAtkDefBatTip() { hasClosedMainGuildAtkDefBatTip = true; } /// /// 获取攻防战的当前显示状态标识 /// /// 1-未参战, 2-备战中, 3-激战中, 4-胜利(休战中), 5-失败(休战中) public int GetGuildBattleDisplayState() { if (!HasFamilyJoinEligibility()) return 1; if (BatState == 2) return 2; if (BatState == 3) return 3; // 休战期:根据胜负结果判定 if (BatState == 1 && MyFamilyInfo != null) { int overState = MyFamilyInfo.OverState; if (overState == 1) return 4; if (overState == 2) return 5; } return 1; } /// /// 获取当前玩家剩余进攻次数 /// public int GetMyRemainingChallengeCount() { uint myPlayerId = PlayerDatas.Instance.PlayerId; if (MyFamilyMembers != null && MyFamilyMembers.TryGetValue(myPlayerId, out var myData)) { return challengeCount - myData.BatCnt; } return 0; } /// /// 获取己方公会排序后的参战成员列表 /// 排序规则:Star降序 → BatCnt升序 → PlayerID升序 /// public List GetSortedFamilyMembers() { var members = MyFamilyMembers; if (members == null || members.Count <= 0) return new List(); var list = members.Values.ToList(); list.Sort((a, b) => { if (b.Star != a.Star) return b.Star.CompareTo(a.Star); if (a.BatCnt != b.BatCnt) return a.BatCnt.CompareTo(b.BatCnt); return a.PlayerID.CompareTo(b.PlayerID); }); return list; } #region 红点 Redpoint mainRedpoint = new Redpoint(MainRedDot.MainGuildRedpoint, MainRedDot.GuildAtkDefBatRepoint); Redpoint fightRedpoint = new Redpoint(MainRedDot.GuildAtkDefBatRepoint, MainRedDot.GuildAtkDefBatRepoint * 10 + 1); Redpoint treasureRedpoint = new Redpoint(MainRedDot.GuildAtkDefBatRepoint, MainRedDot.GuildAtkDefBatRepoint * 10 + 2); Redpoint shopRedpoint = new Redpoint(MainRedDot.GuildAtkDefBatRepoint, MainRedDot.GuildAtkDefBatRepoint * 10 + 3); private bool hasShowedMainRedpoint; public void UpdateRedpoint() { mainRedpoint.state = RedPointState.None; fightRedpoint.state = RedPointState.None; treasureRedpoint.state = RedPointState.None; shopRedpoint.state = RedPointState.None; if (!IsOpen()) return; // 商店:有免费商品可领取 if (HasShopFreeItem()) shopRedpoint.state = RedPointState.Simple; if (!HasFamilyJoinEligibility()) return; uint myPlayerId = PlayerDatas.Instance.PlayerId; bool isMyParticipant = MyFamilyMembers.ContainsKey(myPlayerId); // 备战期:参战成员显示mainRedpoint,进入主界面后消失 if (BatState == 2 && isMyParticipant && !hasShowedMainRedpoint) mainRedpoint.state = RedPointState.Simple; // 激战期:有剩余挑战次数的参战成员 if (BatState == 3 && isMyParticipant && GetMyRemainingChallengeCount() > 0) fightRedpoint.state = RedPointState.Simple; // 休战期:有宝库开启次数 if (BatState == 1 && GetMyOpenRemainCount() > 0) treasureRedpoint.state = RedPointState.Simple; } public void ClearMainRedpoint() { hasShowedMainRedpoint = true; UpdateRedpoint(); } private bool HasShopFreeItem() { if (!StoreModel.Instance.freeShopDict.ContainsKey(shopType)) return false; var freeShopList = StoreModel.Instance.freeShopDict[shopType]; for (int i = 0; i < freeShopList.Count; i++) { int shopID = freeShopList[i]; var config = StoreConfig.Get(shopID); if (config != null && StoreModel.Instance.GetShopLimitBuyCount(shopID) < config.LimitCnt) return true; } return false; } #endregion #region 跨服与周期推算相关 /// /// 判断当前攻防战是否处于跨服状态 /// public bool IsCrossServer() { return CrossServerBaseManager.Instance.IsOpenCrossServer(DataMapID); } /// /// 获取当前公会所处的 ZoneID (用于判断取本服还是跨服时间及其他跨服数据) /// /// 返回0代表本服,大于0代表跨服分区ID public int GetZoneID() { var crossInfo = CrossServerBaseManager.Instance.GetCrossZoneInfo(DataMapID); return (int)(crossInfo?.ZoneID ?? 0); } /// /// 根据本地时间和 cycleConfig 配置推算当前处于哪个阶段 /// 返回对应的 BatState 状态值:0-无, 1-休战期, 2-备战期, 3-激战期 /// public int GetCurrentPeriodByConfig() { if (cycleConfig.IsNullOrEmpty()) return 0; int currentWeekday = TimeUtility.GetCommonWeekday(GetZoneID()); for (int i = 0; i < cycleConfig.Length; i++) { if (cycleConfig[i] != null && Array.IndexOf(cycleConfig[i], currentWeekday) >= 0) return i + 1; } return 0; } public string GetGetCountdownToCurrentPeriodEndString(string key = "") { int time = GetCountdownToCurrentPeriodEnd(); if (time <= 0) return string.Empty; string timeStr = TimeUtility.SecondsToShortDHMS(time); return string.IsNullOrEmpty(key) ? timeStr : Language.Get(key, timeStr); } /// /// 获取【当前阶段结束】的倒计时(自动计算到下一个不同阶段的0点) /// public int GetCountdownToCurrentPeriodEnd() { int currentState = GetCurrentPeriodByConfig(); if (currentState == 0) return 0; // 下一个阶段: 1→2, 2→3, 3→1 return GetCountdownToNextPeriod(currentState % 3 + 1); } /// /// 传入目标时期(BatState),获取距离该周期开始(当天0点)的倒计时秒数 /// public int GetCountdownToNextPeriod(int targetBatState) { int targetIdx = targetBatState - 1; if (cycleConfig == null || targetIdx < 0 || targetIdx >= cycleConfig.Length) return 0; int[] targetDays = cycleConfig[targetIdx]; if (targetDays == null || targetDays.Length == 0) return 0; int zoneID = GetZoneID(); DateTime now = TimeUtility.GetCommServerNow(zoneID); int currentWeekday = TimeUtility.GetCommonWeekday(zoneID); // 找到距离最近的配置日(取最小间隔),无需排序 int minDayDiff = 7; // 最大不可能超过7 foreach (int day in targetDays) { int diff = day > currentWeekday ? day - currentWeekday : 7 - currentWeekday + day; if (diff < minDayDiff) minDayDiff = diff; } // 计算目标日期0点 DateTime targetTime = now.AddDays(minDayDiff).Date; return Mathf.Max(0, (int)(targetTime - now).TotalSeconds); } #endregion #region 日志 /// 公会攻防战进攻日志 ActionType public const int GuildAtkDefBatLogActionType = 22; /// 缓存解析后的周x→累计贡献字典 (key: 1-7, value: 累计贡献) private Dictionary activityData = new Dictionary(); public class GuildAtkDefBatPlayerInfo { public int serverID; public int face; public int facePic; public int fmLV; //官职 public int titleID; //称号 public string name; public string fightPower; } public class GuildAtkDefBatLogEntry { public uint familyID; public HA513_tagMCFamilyActionInfo.tagMCFamilyAction action; public GuildAtkDefBatPlayerInfo atkPlayerInfo; public GuildAtkDefBatPlayerInfo defPlayerInfo; } public event Action OnUpdateAtkDefBatLogEvent; public bool UpdateAtkDefBatLogInfo(HA513_tagMCFamilyActionInfo vNetData) { if (vNetData.ActionType != GuildAtkDefBatLogActionType) return false; if (vNetData.FamilyActionList.IsNullOrEmpty()) return false; for (int i = 0; i < vNetData.FamilyActionList.Length; i++) { var action = vNetData.FamilyActionList[i]; if (action == null) continue; var entry = new GuildAtkDefBatLogEntry { familyID = vNetData.FamilyID, action = action, }; ParseLogUseData(action.UseData, entry); atkDefBatLogs.Add(entry); } // 按时间倒序排列(最新在前) atkDefBatLogs.Sort((a, b) => b.action.Time.CompareTo(a.action.Time)); OnUpdateAtkDefBatLogEvent?.Invoke(); return true; } private void ParseLogUseData(string useData, GuildAtkDefBatLogEntry entry) { if (string.IsNullOrEmpty(useData)) return; try { JsonData jsonData = JsonMapper.ToObject(useData); if (jsonData == null || !jsonData.IsObject) return; entry.atkPlayerInfo = ParsePlayerInfo(jsonData["atkInfo"]); entry.defPlayerInfo = ParsePlayerInfo(jsonData["defInfo"]); } catch (Exception e) { Debug.LogError($"[GuildAtkDefBat] ParseLogUseData error: {e.Message}"); } } GuildAtkDefBatPlayerInfo ParsePlayerInfo(JsonData infoArray) { if (infoArray == null || !infoArray.IsArray || infoArray.Count < 7) return null; try { return new GuildAtkDefBatPlayerInfo { serverID = int.Parse(infoArray[0].ToString()), face = int.Parse(infoArray[1].ToString()), facePic = int.Parse(infoArray[2].ToString()), fmLV = int.Parse(infoArray[3].ToString()), titleID = int.Parse(infoArray[4].ToString()), name = UIHelper.ServerStringTrim(infoArray[5].ToString()), fightPower = UIHelper.ReplaceLargeArtNum(long.Parse(infoArray[6].ToString())), }; } catch { return null; } } #endregion #region 累计活跃度 /// 公会攻防战家族行为 ActionType(累计活跃) public const int GuildAtkDefBatActionType = 19; /// 攻防战日志列表(按时间倒序) public List atkDefBatLogs = new List(); public event Action OnUpdateeFamilyActionInfoEvent; public bool UpdateFamilyActionInfo(HA513_tagMCFamilyActionInfo vNetData) { if (vNetData.ActionType != GuildAtkDefBatActionType) return false; if (vNetData.FamilyID != MyFamilyID) return false; if (vNetData.FamilyActionList.IsNullOrEmpty()) return false; foreach (var item in vNetData.FamilyActionList) { ParseUseData(item.UseData); } OnUpdateeFamilyActionInfoEvent?.Invoke(); return true; } /// /// 解析 UseData JSON,提取周x→累计贡献的映射 /// UseData 格式: {"1":2000,"2":3000,"4":1000} /// key 为周几(1-7, 1=周一), value 为当日累计贡献 /// private void ParseUseData(string useData) { if (string.IsNullOrEmpty(useData)) return; activityData.Clear(); try { JsonData jsonData = JsonMapper.ToObject(useData); if (jsonData == null || !jsonData.IsObject) return; foreach (string key in jsonData.Keys) { if (int.TryParse(key, out int weekday)) activityData[weekday] = (long)jsonData[key]; } } catch (Exception e) { Debug.LogError($"[GuildAtkDefBat] ParseUseData error: {e.Message}"); } } /// /// 获取界面展示用的公会攻防战活跃值。 /// 口径:显示下一次备战名单生成所使用的 prepareDayBefore 天窗口。 /// 后端会在进入备战期 0 点判定参战条件,统计备战日前 prepareDayBefore 天。 /// 前端按 cycleConfig[1] 找下一次备战日,再倒推 prepareDayBefore 天;窗口中尚未发生的未来日期按 0 处理, /// 避免 activityData 只有星期 key 时误读到上一轮同星期的旧活跃。 /// public long GetMyActivityLevel() { int daysUntilPrepare = GetDaysUntilNextPrepareStart(); return GetActivityLevelBeforePrepare(daysUntilPrepare); } /// /// 获取距离下一次备战期开始还有几天。 /// 只从 cycleConfig[1] 读取备战期配置,支持策划自由配置备战星期。 /// 如果今天就是备战日,也取下一次备战日,避免把已进入备战期后的活跃算入当前名单口径。 /// private int GetDaysUntilNextPrepareStart() { int currentWeekday = TimeUtility.GetCommonWeekday(GetZoneID()); int[] prepareWeekdays = cycleConfig != null && cycleConfig.Length > 1 ? cycleConfig[1] : null; if (prepareWeekdays.IsNullOrEmpty()) return 1; int minDaysUntilPrepare = 7; foreach (int prepareWeekday in prepareWeekdays) { int daysUntilPrepare = prepareWeekday > currentWeekday ? prepareWeekday - currentWeekday : prepareWeekday + 7 - currentWeekday; if (daysUntilPrepare < minDaysUntilPrepare) minDaysUntilPrepare = daysUntilPrepare; } return minDaysUntilPrepare; } /// /// 统计下一次备战日前 prepareDayBefore 天的活跃值。 /// offsetFromPrepare 表示目标日期距离备战开始还有几天:1=备战前一天,prepareDayBefore=窗口最早一天。 /// dayOffsetFromToday 大于 0 表示目标日期在今天之后,未来活跃按 0,不读取 activityData。 /// private long GetActivityLevelBeforePrepare(int daysUntilPrepare) { long total = 0; int currentWeekday = TimeUtility.GetCommonWeekday(GetZoneID()); for (int offsetFromPrepare = prepareDayBefore; offsetFromPrepare >= 1; offsetFromPrepare--) { int dayOffsetFromToday = daysUntilPrepare - offsetFromPrepare; if (dayOffsetFromToday > 0) continue; int targetWeekday = ((currentWeekday + dayOffsetFromToday - 1 + 7) % 7) + 1; if (activityData.TryGetValue(targetWeekday, out long contribution)) total += contribution; } return total; } #endregion #region 宝库 /// 宝库奖励类型ID (对应CA504的RewardType) public const byte TREASURE_REWARD_TYPE = 11; /// 缓存格子开启记录: Key=格子编号(1~n), Value=开启记录 public Dictionary treasureGridRecords = new Dictionary(); /// 标记宝库窗口是否打开,用于收到A525时自动刷新 public bool isTreasureWinOpen = false; /// 等待播放开启动画的格子编号(A504发包时记录) public int pendingOpenGridIndex = 0; /// 宝库数据刷新事件(供窗口监听) public event Action OnTreasureDataUpdateEvent; /// 宝库格子开启动画事件(参数: 格子编号, AwardID) public event Action OnTreasureGridOpenEvent; /// /// 从A525成员数据中解析宝库格子记录 /// public void ParseTreasureGridRecords() { treasureGridRecords.Clear(); foreach (var mem in MyFamilyMembers.Values) { if (mem.GridNum > 0) { treasureGridRecords[mem.GridNum] = new GuildAtkDefBatTreasureRecord { AwardID = mem.AwardID, PlayerName = UIHelper.ServerStringTrim(mem.Name), }; } } if (isTreasureWinOpen) { int openGrid = pendingOpenGridIndex; GuildAtkDefBatTreasureRecord treasureRecord; if (openGrid > 0 && treasureGridRecords.TryGetValue(openGrid, out treasureRecord)) { pendingOpenGridIndex = 0; OnTreasureGridOpenEvent?.Invoke(openGrid, treasureRecord.AwardID); } OnTreasureDataUpdateEvent?.Invoke(); } } /// /// 获取我的剩余开启次数 /// 仅在休战期(BatState==1)可开启 /// 参战且已战斗成员可开启1次 /// 每人最多1次 /// public int GetMyOpenRemainCount() { if (MyFamilyInfo == null) return 0; // 仅在休战期可开启宝库 if (BatState != 1) return 0; uint myPlayerId = PlayerDatas.Instance.PlayerId; if (!MyFamilyMembers.TryGetValue(myPlayerId, out var myMem)) return 0; // 未参与战斗不能开启 if (myMem.BatCnt <= 0) return 0; // 已开启过则不能再次开启 if (myMem.GridNum > 0) return 0; // 参战且已战斗成员可开启1次(已开启的被 GridNum 过滤) return 1; } /// /// 获取我的最大可开启次数(用于显示 开启次数:{0}/{1}) /// public int GetMyMaxOpenCount() { return 1; } /// /// 发送宝库开启请求 A504 /// public void SendOpenTreasure(byte gridNum) { pendingOpenGridIndex = gridNum; var pack = new CA504_tagCMPlayerGetReward(); pack.RewardType = TREASURE_REWARD_TYPE; pack.DataEx = gridNum; GameNetSystem.Instance.SendInfo(pack); } /// /// 获取宝库的库类型(1-胜者库, 2-败者库) /// public int GetTreasureLibType() { int displayState = GetGuildBattleDisplayState(); return displayState == 4 ? 1 : 2; } #region 宝库奖励延迟展示 /// 缓存待展示的宝库奖励物品列表 private List pendingTreasureItems = new List(); /// 缓存待展示的宝库奖励信息文本 private string pendingTreasureInfo = string.Empty; /// /// 缓存宝库奖励数据(由 BoxGetItemModel.ReceiveAwardNotify 调用) /// 延迟到宝库开启动画结束后再展示 /// public void CacheTreasureAward(HA801_tagMCGiveAwardInfo netPack) { pendingTreasureItems.Clear(); pendingTreasureInfo = string.Empty; // 解析经验 if (netPack.Exp != 0 || netPack.ExpPoint != 0) { long expValue = netPack.Exp + netPack.ExpPoint * Constants.ExpPointValue; pendingTreasureItems.Add(new Item(GeneralDefine.expDisplayId, expValue)); } // 解析货币 if (netPack.MoneyList.Length != 0) { for (int i = 0; i < netPack.MoneyLen; i++) { var moneyType = netPack.MoneyList[i].MoneyType; if (GeneralDefine.MoneyDisplayModel.ContainsKey(moneyType) && netPack.MoneyList[i].MoneyValue != 0) { pendingTreasureItems.Add(new Item(GeneralDefine.MoneyDisplayModel[moneyType], netPack.MoneyList[i].MoneyValue, netPack.MoneyList[i].IsBind)); } } } // 解析物品 if (netPack.ItemList.Length != 0) { for (int i = 0; i < netPack.ItemLen; i++) { pendingTreasureItems.Add(new Item( (int)netPack.ItemList[i].ItemID, netPack.ItemList[i].Count + netPack.ItemList[i].CountEx * Constants.ExpPointValue, netPack.ItemList[i].IsBind)); } } // 奖励说明文本 if (LanguageConfig.HasKey("commonShowAwardEvents_AtkDefBatTreasure")) pendingTreasureInfo = Language.Get("commonShowAwardEvents_AtkDefBatTreasure"); } /// /// 展示缓存的宝库奖励(由格子开启动画 onComplete 回调调用) /// public void ShowTreasureAward() { if (pendingTreasureItems.Count == 0) return; ItemLogicUtility.Instance.ShowGetItem(pendingTreasureItems, "AtkDefBatTreasure"); pendingTreasureItems.Clear(); pendingTreasureInfo = string.Empty; } #endregion #endregion #region 扫荡 /// 是否为扫荡的胜利结算 public bool isSweepVictory = false; /// 扫荡奖励物品列表 public List sweepItemInfos = new List(); /// 扫荡目标玩家ID public uint sweepTagID = 0; /// 接收 0320 副本结束通知(扫荡结果) public void UpdateFBEnd(H0320_tagFBEnd vNetData) { if (string.IsNullOrEmpty(vNetData?.Msg)) return; JsonData jsonData; try { jsonData = JsonMapper.ToObject(vNetData.Msg); } catch { return; } // 判断是否是公会攻防战的扫荡结果 if (!jsonData.ContainsKey("dataMapID") || (int)jsonData["dataMapID"] != DataMapID) return; if (!jsonData.ContainsKey("isSweep") || (int)jsonData["isSweep"] != 1) return; isSweepVictory = true; // 解析目标玩家ID sweepTagID = jsonData.ContainsKey("tagID") ? (uint)(int)jsonData["tagID"] : 0; // 解析奖励物品 sweepItemInfos.Clear(); if (jsonData.ContainsKey("itemInfo") && jsonData["itemInfo"].IsArray) { foreach (JsonData itemData in jsonData["itemInfo"]) { sweepItemInfos.Add(new Item((int)itemData["ItemID"], (long)itemData["Count"])); } } UIManager.Instance.OpenWindow(); } #endregion #region 奖励预览 /// 实时计算最大总格子数 public int CalculateMaxGridCount() { int participantCount = MyFamilyMembers?.Count ?? 0; if (participantCount <= defaultMaxGrid) return defaultMaxGrid; // 最大总格子=默认最大格子数+动态扩容格子数*n,n大于等于0 // n=向上取整((参与成员-默认最大格子数)/扩容格子数)) int n = Mathf.CeilToInt((float)(participantCount - defaultMaxGrid) / expandGridCount); return defaultMaxGrid + expandGridCount * n; } /// 计算非普通品质的总格子产出上限 public int GetNonNormalQualityTotalOutput() { int total = 0; if (qualityMaxOutput != null) { foreach (var kv in qualityMaxOutput) { if (kv.Key != 1) total += kv.Value; } } return total; } #endregion } public class GuildAtkDefBatTreasureRecord { public int AwardID; public string PlayerName; }