lcy
2026-06-10 1adf0932629c4154f6e2ff854e75c48019a3972b
588 公会攻防战

# Conflicts:
# Main/Config/ConfigManager.cs
# Main/Main.cs
# Main/System/Battle/BattleFieldFactory.cs
21个文件已修改
73个文件已添加
5420 ■■■■■ 已修改文件
Main/Config/ConfigManager.cs 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/Configs/FamilyAtkDefBatDefenderConfig.cs 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/Configs/FamilyAtkDefBatDefenderConfig.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/Configs/FamilyAtkDefBatTreasureConfig.cs 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/Configs/FamilyAtkDefBatTreasureConfig.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/PartialConfigs/FamilyAtkDefBatDefenderConfig.cs 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/PartialConfigs/FamilyAtkDefBatDefenderConfig.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/PartialConfigs/FamilyAtkDefBatTreasureConfig.cs 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/PartialConfigs/FamilyAtkDefBatTreasureConfig.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/PartialConfigs/FamilyEmblemConfig.cs 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/ClientPack/CA6_Family/CA635_tagCSFamilyAtkDefBatChangeMem.cs 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/ClientPack/CA6_Family/CA635_tagCSFamilyAtkDefBatChangeMem.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/DTCFile/ServerPack/H03_MainCharacter/DTC0320_tagFBEnd.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/DTCFile/ServerPack/HA5_Family/DTCA525_tagSCFamilyAtkDefBatInfo.cs 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/DTCFile/ServerPack/HA5_Family/DTCA525_tagSCFamilyAtkDefBatInfo.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/DataToCtl/PackageRegedit.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/ServerPack/HA5_Family/HA513_tagMCFamilyActionInfo.cs 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/ServerPack/HA5_Family/HA525_tagSCFamilyAtkDefBatInfo.cs 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/ServerPack/HA5_Family/HA525_tagSCFamilyAtkDefBatInfo.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Main.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/BattleConst.cs 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/BattleField/GuildAtkDefBatField.cs 120 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/BattleField/GuildAtkDefBatField.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/BattleFieldFactory.cs 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/GuildAtkDefBatBattleWin.cs 310 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/GuildAtkDefBatBattleWin.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/BillboardRank/GuildRankWin.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/FuncPreset/FuncPresetManager.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Guild/GuildBaseWin.cs 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Guild/GuildJoinWin.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Guild/GuildManager.cs 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Guild/GuildPreviewCell.cs 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat.meta 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatAwardPreviewCell.cs 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatAwardPreviewCell.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatAwardPreviewHeaderCell.cs 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatAwardPreviewHeaderCell.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatAwardPreviewNomalCell.cs 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatAwardPreviewNomalCell.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatAwardPreviewTailCell.cs 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatAwardPreviewTailCell.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatAwardPreviewWin.cs 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatAwardPreviewWin.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatBattlePanelHeaderCell.cs 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatBattlePanelHeaderCell.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatBattlePanelItem.cs 253 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatBattlePanelItem.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatBattlePanelNormalCell.cs 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatBattlePanelNormalCell.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatBattlePanelWin.cs 345 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatBattlePanelWin.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatChallengeDifficulty.cs 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatChallengeDifficulty.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatChallengeWin.cs 175 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatChallengeWin.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatChangeMemberCell.cs 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatChangeMemberCell.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatChangeMemberWin.cs 133 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatChangeMemberWin.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatFailWin.cs 122 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatFailWin.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatManager.cs 1012 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatManager.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatPlayerShowCell.cs 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatPlayerShowCell.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatPlayerTop3Cell.cs 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatPlayerTop3Cell.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatRankCell.cs 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatRankCell.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatRankWin.cs 135 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatRankWin.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatRecordCell.cs 161 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatRecordCell.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatRecordWin.cs 193 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatRecordWin.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatShopWin.cs 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatShopWin.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatTreasureCell.cs 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatTreasureCell.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatTreasureGrid.cs 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatTreasureGrid.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatTreasureWin.cs 188 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatTreasureWin.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatVictoryWin.cs 190 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatVictoryWin.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatWin.cs 351 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/GuildAtkDefBat/GuildAtkDefBatWin.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HeroUI/HeroFormationWin.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/ItemTip/BoxGetItemModel.cs 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Redpoint/MainRedDot.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Settlement/BattleSettlementManager.cs 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/ViewNPC/ViewNPCManager.cs 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Utility/EnumHelper.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Utility/UIHelper.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/ConfigManager.cs
@@ -38,12 +38,6 @@
        // 加载配置文件
        // 加载配置文件
        // 加载配置文件
        // 加载配置文件
        // 加载配置文件
        // 加载配置文件
        // 加载配置文件
        HashSet<Type> configTypes = new HashSet<Type>() {
            typeof(ActBillboardAwardConfig),
            typeof(ActHeroAppearArtConfig),
@@ -73,6 +67,9 @@
            typeof(DungeonConfig),
            typeof(DungeonOpenTimeConfig),
            typeof(FaceConfig),
            typeof(FamilyConfig),
            typeof(FamilyAtkDefBatDefenderConfig),
            typeof(FamilyAtkDefBatTreasureConfig),
            typeof(FamilyDonateConfig),
            typeof(FBDJGEffectConfig),
            typeof(FBDJGLevelConfig),
@@ -341,6 +338,12 @@
        ClearConfigDictionary<DungeonOpenTimeConfig>();
        // 清空 FaceConfig 字典
        ClearConfigDictionary<FaceConfig>();
        // 清空 FamilyConfig 字典
        ClearConfigDictionary<FamilyConfig>();
        // 清空 FamilyAtkDefBatDefenderConfig 字典
        ClearConfigDictionary<FamilyAtkDefBatDefenderConfig>();
        // 清空 FamilyAtkDefBatTreasureConfig 字典
        ClearConfigDictionary<FamilyAtkDefBatTreasureConfig>();
        // 清空 FamilyDonateConfig 字典
        ClearConfigDictionary<FamilyDonateConfig>();
        // 清空 FBDJGEffectConfig 字典
@@ -415,8 +418,6 @@
        ClearConfigDictionary<NPCExConfig>();
        // 清空 NPCLineupConfig 字典
        ClearConfigDictionary<NPCLineupConfig>();
        // 清空 OPConfigConfig 字典
        ClearConfigDictionary<OPConfigConfig>();
        // 清空 OrderInfoConfig 字典
        ClearConfigDictionary<OrderInfoConfig>();
        // 清空 PlayerAttrConfig 字典
Main/Config/Configs/FamilyAtkDefBatDefenderConfig.cs
New file
@@ -0,0 +1,71 @@
//--------------------------------------------------------
//    [Author]:           YYL
//    [  Date ]:           2026年5月13日
//--------------------------------------------------------
using System.Collections.Generic;
using System;
using UnityEngine;
using LitJson;
public partial class FamilyAtkDefBatDefenderConfig : ConfigBase<int, FamilyAtkDefBatDefenderConfig>
{
    static FamilyAtkDefBatDefenderConfig()
    {
        // 访问过静态构造函数
        visit = true;
    }
    public int DefenderType;
    public int DefenderCnt;
    public int[] DefBaseAttrPerList;
    public int[] DefStarList;
    public override int LoadKey(string _key)
    {
        int key = GetKey(_key);
        return key;
    }
    public override void LoadConfig(string input)
    {
        try {
        string[] tables = input.Split('\t');
        int.TryParse(tables[0],out DefenderType);
            int.TryParse(tables[1],out DefenderCnt);
            if (tables[2].Contains("["))
            {
                DefBaseAttrPerList = JsonMapper.ToObject<int[]>(tables[2]);
            }
            else
            {
                string[] DefBaseAttrPerListStringArray = tables[2].Trim().Split(StringUtility.splitSeparator,StringSplitOptions.RemoveEmptyEntries);
                DefBaseAttrPerList = new int[DefBaseAttrPerListStringArray.Length];
                for (int i=0;i<DefBaseAttrPerListStringArray.Length;i++)
                {
                     int.TryParse(DefBaseAttrPerListStringArray[i],out DefBaseAttrPerList[i]);
                }
            }
            if (tables[3].Contains("["))
            {
                DefStarList = JsonMapper.ToObject<int[]>(tables[3]);
            }
            else
            {
                string[] DefStarListStringArray = tables[3].Trim().Split(StringUtility.splitSeparator,StringSplitOptions.RemoveEmptyEntries);
                DefStarList = new int[DefStarListStringArray.Length];
                for (int i=0;i<DefStarListStringArray.Length;i++)
                {
                     int.TryParse(DefStarListStringArray[i],out DefStarList[i]);
                }
            }
        }
        catch (Exception exception)
        {
            Debug.LogError(exception);
        }
    }
}
Main/Config/Configs/FamilyAtkDefBatDefenderConfig.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: baf4e0d437dd0f741a570f12c7b89ba1
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Config/Configs/FamilyAtkDefBatTreasureConfig.cs
New file
@@ -0,0 +1,53 @@
//--------------------------------------------------------
//    [Author]:           YYL
//    [  Date ]:           2026年5月20日
//--------------------------------------------------------
using System.Collections.Generic;
using System;
using UnityEngine;
using LitJson;
public partial class FamilyAtkDefBatTreasureConfig : ConfigBase<int, FamilyAtkDefBatTreasureConfig>
{
    static FamilyAtkDefBatTreasureConfig()
    {
        // 访问过静态构造函数
        visit = true;
    }
    public int AwardID;
    public int LibType;
    public int AwardColor;
    public int ItemID;
    public int ItemCount;
    public int SortOrder;
    public override int LoadKey(string _key)
    {
        int key = GetKey(_key);
        return key;
    }
    public override void LoadConfig(string input)
    {
        try {
        string[] tables = input.Split('\t');
        int.TryParse(tables[0],out AwardID);
            int.TryParse(tables[1],out LibType);
            int.TryParse(tables[2],out AwardColor);
            int.TryParse(tables[3],out ItemID);
            int.TryParse(tables[4],out ItemCount);
            int.TryParse(tables[5],out SortOrder);
        }
        catch (Exception exception)
        {
            Debug.LogError(exception);
        }
    }
}
Main/Config/Configs/FamilyAtkDefBatTreasureConfig.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c79ff69b907868e43ab3099509371b32
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Config/PartialConfigs/FamilyAtkDefBatDefenderConfig.cs
New file
@@ -0,0 +1,21 @@
using System.Collections.Generic;
public partial class FamilyAtkDefBatDefenderConfig : ConfigBase<int, FamilyAtkDefBatDefenderConfig>
{
    /// <summary>获取指定防守类型的总星数</summary>
    public static int GetTotalStarCount(int defType)
    {
        var config = Get(defType);
        if (config == null || config.DefStarList == null) return 0;
        int total = 0;
        for (int i = 0; i < config.DefStarList.Length; i++)
            total += config.DefStarList[i];
        return total;
    }
    /// <summary>获取指定防守类型的星数列表</summary>
    public static int[] GetDefStarList(int defType)
    {
        return Get(defType)?.DefStarList;
    }
}
Main/Config/PartialConfigs/FamilyAtkDefBatDefenderConfig.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: aed1e3dae554d7d4fa843f5c5e95a948
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Config/PartialConfigs/FamilyAtkDefBatTreasureConfig.cs
New file
@@ -0,0 +1,48 @@
using System.Collections.Generic;
public partial class FamilyAtkDefBatTreasureConfig : ConfigBase<int, FamilyAtkDefBatTreasureConfig>
{
    // 双层字典:AwardColor(品质) -> LibType(胜负库) -> List<config>
    private static Dictionary<int, Dictionary<int, List<FamilyAtkDefBatTreasureConfig>>> s_TreasureDict;
    protected override void OnConfigParseCompleted()
    {
        if (s_TreasureDict == null)
            s_TreasureDict = new Dictionary<int, Dictionary<int, List<FamilyAtkDefBatTreasureConfig>>>();
        if (!s_TreasureDict.TryGetValue(AwardColor, out var libDict))
        {
            libDict = new Dictionary<int, List<FamilyAtkDefBatTreasureConfig>>();
            s_TreasureDict[AwardColor] = libDict;
        }
        if (!libDict.TryGetValue(LibType, out var list))
        {
            list = new List<FamilyAtkDefBatTreasureConfig>();
            libDict[LibType] = list;
        }
        list.Add(this);
    }
    /// <summary>按品质和库类型获取宝藏奖励配置列表(已按 SortOrder→AwardID 排序)</summary>
    /// <param name="quality">奖励品质(1-普通;2-稀有;3-珍稀)</param>
    /// <param name="libType">胜负库类型(1-胜者库;2-败者库)</param>
    public static List<FamilyAtkDefBatTreasureConfig> GetItemsByQualityAndLibType(int quality, int libType)
    {
        if (s_TreasureDict != null
            && s_TreasureDict.TryGetValue(quality, out var libDict)
            && libDict.TryGetValue(libType, out var list))
        {
            // 排序:SortOrder 小的靠前,SortOrder 相同时 AwardID 小的靠前
            list.Sort((a, b) =>
            {
                int cmp = a.SortOrder.CompareTo(b.SortOrder);
                if (cmp != 0) return cmp;
                return a.AwardID.CompareTo(b.AwardID);
            });
            return list;
        }
        return null;
    }
}
Main/Config/PartialConfigs/FamilyAtkDefBatTreasureConfig.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 930dc3b5cff8b544e8fb6445b706c958
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Config/PartialConfigs/FamilyEmblemConfig.cs
@@ -23,5 +23,15 @@
        return defaultEmblemList[random.Next(defaultEmblemList.Count)];
    }
    /// <summary>
    /// 获取配表中的第一个默认徽章ID(用于系统公会默认显示)
    /// </summary>
    public static int GetFirstDefaultEmblem()
    {
        if (defaultEmblemList.Count == 0)
            return -1;
        return defaultEmblemList[0];
    }
}
Main/Core/NetworkPackage/ClientPack/CA6_Family/CA635_tagCSFamilyAtkDefBatChangeMem.cs
New file
@@ -0,0 +1,20 @@
using UnityEngine;
using System.Collections;
// A6 35 更换攻防战备战成员位置 #tagCSFamilyAtkDefBatChangeMem
public class CA635_tagCSFamilyAtkDefBatChangeMem : GameNetPackBasic {
    public uint PlayerIDA;    // 要被替换的成员ID
    public uint PlayerIDB;    // 替换A成员的成员ID
    public CA635_tagCSFamilyAtkDefBatChangeMem () {
        combineCmd = (ushort)0x03FE;
        _cmd = (ushort)0xA635;
    }
    public override void WriteToBytes () {
        WriteBytes (PlayerIDA, NetDataType.DWORD);
        WriteBytes (PlayerIDB, NetDataType.DWORD);
    }
}
Main/Core/NetworkPackage/ClientPack/CA6_Family/CA635_tagCSFamilyAtkDefBatChangeMem.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 4916b1b8522fea24bac0b2c97306f526
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Core/NetworkPackage/DTCFile/ServerPack/H03_MainCharacter/DTC0320_tagFBEnd.cs
@@ -10,5 +10,6 @@
        BoneFieldManager.Instance.UpdateFBEnd(vNetData);
        TianziBillboradManager.Instance.UpdateFBEnd(vNetData);
        QunyingManager.Instance.UpdateFBEnd(vNetData);
        GuildAtkDefBatManager.Instance.UpdateFBEnd(vNetData);
    }
}
Main/Core/NetworkPackage/DTCFile/ServerPack/HA5_Family/DTCA525_tagSCFamilyAtkDefBatInfo.cs
New file
@@ -0,0 +1,14 @@
using UnityEngine;
using System.Collections;
// A5 25 公会攻防战信息 #tagSCFamilyAtkDefBatInfo
public class DTCA525_tagSCFamilyAtkDefBatInfo : DtcBasic
{
    public override void Done(GameNetPackBasic vNetPack)
    {
        base.Done(vNetPack);
        HA525_tagSCFamilyAtkDefBatInfo vNetData = vNetPack as HA525_tagSCFamilyAtkDefBatInfo;
        GuildAtkDefBatManager.Instance.UpdateAtkDefBatInfo(vNetData);
    }
}
Main/Core/NetworkPackage/DTCFile/ServerPack/HA5_Family/DTCA525_tagSCFamilyAtkDefBatInfo.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7c49aa09acdc77d47900efded1efe6ce
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Core/NetworkPackage/DataToCtl/PackageRegedit.cs
@@ -172,6 +172,7 @@
        Register(typeof(HAA71_tagSCActTaskInfo), typeof(DTCAA71_tagSCActTaskInfo));
        Register(typeof(HAA72_tagSCActTaskPlayerValueInfo), typeof(DTCAA72_tagSCActTaskPlayerValueInfo));
        Register(typeof(HAA73_tagSCActTaskPlayerInfo), typeof(DTCAA73_tagSCActTaskPlayerInfo));
        Register(typeof(HA525_tagSCFamilyAtkDefBatInfo), typeof(DTCA525_tagSCFamilyAtkDefBatInfo));
    }
    //主工程注册封包
Main/Core/NetworkPackage/ServerPack/HA5_Family/HA513_tagMCFamilyActionInfo.cs
Main/Core/NetworkPackage/ServerPack/HA5_Family/HA525_tagSCFamilyAtkDefBatInfo.cs
New file
@@ -0,0 +1,101 @@
using UnityEngine;
using System.Collections;
// A5 25 公会攻防战信息 #tagSCFamilyAtkDefBatInfo
public class HA525_tagSCFamilyAtkDefBatInfo : GameNetPackBasic {
    public byte BatState;    //0-无;1-休战期;2-备战期;3-激战期
    public byte FamilyCount;
    public  tagSCFamilyAtkDefBatFamily[] BatFamilyList;
    public uint DelMemID;    //删除参战成员ID,可能是己方或者对方的成员,参赛成员退出时该值不为0,收到同步时前端同步删除该参赛成员
    public HA525_tagSCFamilyAtkDefBatInfo () {
        _cmd = (ushort)0xA525;
    }
    public override void ReadFromBytes (byte[] vBytes) {
        TransBytes (out BatState, vBytes, NetDataType.BYTE);
        TransBytes (out FamilyCount, vBytes, NetDataType.BYTE);
        BatFamilyList = new tagSCFamilyAtkDefBatFamily[FamilyCount];
        for (int i = 0; i < FamilyCount; i ++) {
            BatFamilyList[i] = new tagSCFamilyAtkDefBatFamily();
            TransBytes (out BatFamilyList[i].FamilyID, vBytes, NetDataType.DWORD);
            TransBytes (out BatFamilyList[i].FamilyName, vBytes, NetDataType.Chars, 33);
            TransBytes (out BatFamilyList[i].ServerID, vBytes, NetDataType.DWORD);
            TransBytes (out BatFamilyList[i].EmblemID, vBytes, NetDataType.DWORD);
            TransBytes (out BatFamilyList[i].EmblemWord, vBytes, NetDataType.Chars, 3);
            TransBytes (out BatFamilyList[i].IsDismiss, vBytes, NetDataType.BYTE);
            TransBytes (out BatFamilyList[i].TotalStar, vBytes, NetDataType.WORD);
            TransBytes (out BatFamilyList[i].OverState, vBytes, NetDataType.BYTE);
            TransBytes (out BatFamilyList[i].BatMemCount, vBytes, NetDataType.BYTE);
            BatFamilyList[i].BatMemList = new tagSCFamilyAtkDefBatMem[BatFamilyList[i].BatMemCount];
            for (int j = 0; j < BatFamilyList[i].BatMemCount; j ++) {
                BatFamilyList[i].BatMemList[j] = new tagSCFamilyAtkDefBatMem();
                TransBytes (out BatFamilyList[i].BatMemList[j].PlayerID, vBytes, NetDataType.DWORD);
                TransBytes (out BatFamilyList[i].BatMemList[j].NameLen, vBytes, NetDataType.BYTE);
                TransBytes (out BatFamilyList[i].BatMemList[j].Name, vBytes, NetDataType.Chars, BatFamilyList[i].BatMemList[j].NameLen);
                TransBytes (out BatFamilyList[i].BatMemList[j].LV, vBytes, NetDataType.WORD);
                TransBytes (out BatFamilyList[i].BatMemList[j].Job, vBytes, NetDataType.BYTE);
                TransBytes (out BatFamilyList[i].BatMemList[j].RealmLV, vBytes, NetDataType.BYTE);
                TransBytes (out BatFamilyList[i].BatMemList[j].Face, vBytes, NetDataType.DWORD);
                TransBytes (out BatFamilyList[i].BatMemList[j].FacePic, vBytes, NetDataType.DWORD);
                TransBytes (out BatFamilyList[i].BatMemList[j].ModelMark, vBytes, NetDataType.DWORD);
                TransBytes (out BatFamilyList[i].BatMemList[j].EquipShowSwitch, vBytes, NetDataType.DWORD);
                TransBytes (out BatFamilyList[i].BatMemList[j].TitleID, vBytes, NetDataType.DWORD);
                TransBytes (out BatFamilyList[i].BatMemList[j].FightPower, vBytes, NetDataType.DWORD);
                TransBytes (out BatFamilyList[i].BatMemList[j].FightPowerEx, vBytes, NetDataType.DWORD);
                TransBytes (out BatFamilyList[i].BatMemList[j].ServerID, vBytes, NetDataType.DWORD);
                TransBytes (out BatFamilyList[i].BatMemList[j].DefType, vBytes, NetDataType.BYTE);
                TransBytes (out BatFamilyList[i].BatMemList[j].PosNum, vBytes, NetDataType.BYTE);
                TransBytes (out BatFamilyList[i].BatMemList[j].BatCnt, vBytes, NetDataType.BYTE);
                TransBytes (out BatFamilyList[i].BatMemList[j].DWinCnt, vBytes, NetDataType.WORD);
                TransBytes (out BatFamilyList[i].BatMemList[j].Star, vBytes, NetDataType.BYTE);
                TransBytes (out BatFamilyList[i].BatMemList[j].KilledState, vBytes, NetDataType.BYTE);
                TransBytes (out BatFamilyList[i].BatMemList[j].GridNum, vBytes, NetDataType.BYTE);
                TransBytes (out BatFamilyList[i].BatMemList[j].AwardID, vBytes, NetDataType.WORD);
                TransBytes (out BatFamilyList[i].BatMemList[j].FmLV, vBytes, NetDataType.BYTE);
            }
        }
        TransBytes (out DelMemID, vBytes, NetDataType.DWORD);
    }
    public class tagSCFamilyAtkDefBatFamily {
        public uint FamilyID;        //非自己的公会ID,就是对手公会ID;为1时代表系统马赛克军团
        public string FamilyName;
        public uint ServerID;        //公会区服ID
        public uint EmblemID;        //旗帜
        public string EmblemWord;        //旗号
        public byte IsDismiss;        //是否已解散,解散的公会算输
        public ushort TotalStar;        //总星数
        public byte OverState;        //胜负结算结果:0-未结算;1-已结算-胜利;2-已结算-失败;3-未结算-提前胜利;4-未结算-提前失败
        public byte BatMemCount;
        public  tagSCFamilyAtkDefBatMem[] BatMemList;        // 参与成员列表,非列表中成员无法备战、进攻、防守
    }
    public class tagSCFamilyAtkDefBatMem {
        public uint PlayerID;
        public byte NameLen;
        public string Name;        //size = NameLen
        public ushort LV;        //等级
        public byte Job;        //职业
        public byte RealmLV;        //境界
        public uint Face;        //基本脸型
        public uint FacePic;        //头像框
        public uint ModelMark;        //变形模型mark
        public uint EquipShowSwitch;        //其他外观信息
        public uint TitleID;        //称号
        public uint FightPower;        //战力,求余亿部分
        public uint FightPowerEx;        //战力,整除亿部分
        public uint ServerID;        //所属区服ID
        public byte DefType;        //防守人员类型,非防守人员时为0
        public byte PosNum;        //防守位置,每种类型位置独立算,都从1开始
        public byte BatCnt;        //已战斗次数
        public ushort DWinCnt;        //防守胜利次数
        public byte Star;        //累计获得星数
        public byte KilledState;        //被击败记录,按属性百分比索引位判断是否已被击败,剩余星可根据此状态计算出
        public byte GridNum;        //开宝库格子编号,0-未打开,>0对应打开的格子编号
        public ushort AwardID;        //开宝库格子奖励ID
        public byte FmLV;        //公会成员职位
    }
}
Main/Core/NetworkPackage/ServerPack/HA5_Family/HA525_tagSCFamilyAtkDefBatInfo.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 29468b7d8b783e847a6710e4584f9aef
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Main.cs
@@ -124,6 +124,7 @@
        managers.Add(FestivalActivityMissionManager.Instance);
        managers.Add(SuperVipManager.Instance);
        managers.Add(HeroSkinFlashSaleManager.Instance);
        managers.Add(GuildAtkDefBatManager.Instance);
        foreach (var manager in managers)
        {
Main/System/Battle/BattleConst.cs
@@ -15,6 +15,8 @@
        typeof(WarlordPavilionBattleWin),
        typeof(PreviewBattleWin),
        typeof(QYBattleWin),
        typeof(QYBattleWin),
        typeof(GuildAtkDefBatBattleWin),
    };
    // 战场名称
@@ -26,6 +28,7 @@
    public const string WarlordPavilionBattleField = "WarlordPavilionBattleField";
    public const string PriviewBattleField = "PriviewBattleField";  //预览战斗
    public const string QYBattleField = "QYBattleField";
    public const string GuildAtkDefBatField = "GuildAtkDefBatField";
    public static Dictionary<string, string> battleNameToWinName = new Dictionary<string, string>()
    {
@@ -37,6 +40,7 @@
        { WarlordPavilionBattleField, "WarlordPavilionBattleWin" },
        { PriviewBattleField, "PreviewBattleWin" },
        { QYBattleField, "QYBattleWin" },
        { GuildAtkDefBatField, "GuildAtkDefBatBattleWin" },
    };
    // 索引配置用
@@ -49,6 +53,7 @@
        { WarlordPavilionBattleField, 5 },
        { PriviewBattleField, 6 },
        { QYBattleField, 7 },
        { GuildAtkDefBatField, 8 },
    };
    //和 CreateBattleField 里的对应
@@ -62,6 +67,7 @@
        {30030, WarlordPavilionBattleField},
        {30000, PriviewBattleField},
        {32000, QYBattleField},
        {33000, GuildAtkDefBatField},
    };
    //pvp战斗的战场,不在这个列表的视为pve类型的战场
@@ -69,6 +75,7 @@
    {
        ArenaBattleField,
        QYBattleField,
        GuildAtkDefBatField,
    };
    
Main/System/Battle/BattleField/GuildAtkDefBatField.cs
New file
@@ -0,0 +1,120 @@
using System;
using LitJson;
using UnityEngine;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
public class GuildAtkDefBatField : BattleField
{
    protected int chapter;//   章节
    protected int wave;//  波数
    protected int level;// 关卡
    protected MainChapterConfig chapterConfig;
    protected MainLevelConfig levelConfig;
    public GuildAtkDefBatField(string _guid) : base(_guid)
    {
    }
    public override async UniTask Init(int MapID, int FuncLineID, JsonData _extendData,
        List<TeamBase> _redTeamList, List<TeamBase> _blueTeamList, byte turnMax)
    {
        await base.Init(MapID, FuncLineID, extendData, _redTeamList, _blueTeamList, turnMax);
        chapter = FuncLineID / 10000;
        wave = MapID == 1 ? FuncLineID % 100 : 1;//第几波怪
        level = (FuncLineID % 10000) / 100;
        extendData = _extendData;
        chapterConfig = MainChapterConfig.Get(chapter);
        levelConfig = MainLevelConfig.Get(level);
        SetBattleMode(BattleMode.Record);
    }
    public override void AutoSetBattleMode()
    {
        SetBattleMode(BattleMode.Record);
    }
    public override void TurnFightState(int TurnNum, int State,
        uint FuncLineID, JsonData extendData)
    {
        base.TurnFightState(TurnNum, State, FuncLineID, extendData);
        switch (State)
        {
            //  起始状态标记
            case 0:
                break;
            case 1://准备完毕
                break;
            case 2://战斗中
                break;
            case 3://战斗结束
                break;
            case 4://结算奖励
                break;
            case 5://结束状态标记
                break;
            default:
                BattleDebug.LogError("recieve a unknown State");
                break;
        }
    }
    protected override void OnSettlement(JsonData turnFightStateData)
    {
        base.OnSettlement(turnFightStateData);
    }
    public override void WhaleFall()
    {
        AutoFightModel.Instance.isPause = false;
        Destroy();
        // 切出战斗界面返回公会攻防战战场面板
        if (UIManager.Instance.IsOpened<GuildAtkDefBatBattleWin>())
        {
            UIManager.Instance.CloseWindow<GuildAtkDefBatBattleWin>();
            UIManager.Instance.OpenWindowAsync<GuildAtkDefBatBattlePanelWin>().Forget();
        }
    }
    public override void Run()
    {
        if (operationAgent == null)
        {
            //防范异常
            return;
        }
        base.Run();
    }
    public override void DistributeNextPackage()
    {
        if (IsBattleFinish)
            return;
        //  不要调用base的函数
        BattleManager.Instance.DistributeNextReportPackage(guid);
    }
    public override async void ShowWindow(HB424_tagSCTurnFightInit vNetData)
    {
        GuildAtkDefBatBattleWin fsBattleWin = UIManager.Instance.GetUI<GuildAtkDefBatBattleWin>();// as FullScreenBattleWin;
        if (null == fsBattleWin)
        {
            fsBattleWin = await UIManager.Instance.OpenWindowAsync<GuildAtkDefBatBattleWin>();
        }
        fsBattleWin.SetBattleField(this);
        if (UIManager.Instance.IsOpened<GuildAtkDefBatBattlePanelWin>())
        {
            UIManager.Instance.CloseWindow<GuildAtkDefBatBattlePanelWin>();
        }
    }
}
Main/System/Battle/BattleField/GuildAtkDefBatField.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b81dcd62194a53340958691ee692309f
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/Battle/BattleFieldFactory.cs
@@ -38,6 +38,15 @@
            case 32000:
                battleField = new QYBattleField(guid);
                break;
<<<<<<< HEAD
=======
            case 100:
                battleField = new ArenaBattleField(guid);
                break;
            case 33000:
                battleField = new GuildAtkDefBatField(guid);
                break;
>>>>>>> 0afbb8c1 (588 公会攻防战)
            default:
                break;
        }
Main/System/Battle/GuildAtkDefBatBattleWin.cs
New file
@@ -0,0 +1,310 @@
using System.Collections.Generic;
using LitJson;
using UnityEngine;
using Cysharp.Threading.Tasks;
public class GuildAtkDefBatBattleWin : BaseBattleWin
{
    [SerializeField] Transform transButtons;
    [SerializeField] HeroCountryComponent myCountry;
    [SerializeField] TextEx txtMyLV;
    [SerializeField] TextEx txtMyName;
    [SerializeField] TextEx txtMyHp;
    [SerializeField] ImageEx imgMyHp;
    [SerializeField] TextEx txtMyFightPonit;
    [SerializeField] AvatarCell myAvatarCell;
    [SerializeField] List<ArenaHeroHead> myHeroHeads;
    [SerializeField] HeroCountryComponent enemyCountry;
    [SerializeField] TextEx txtEnemyLV;
    [SerializeField] TextEx txtEnemyName;
    [SerializeField] TextEx txtEnemyHp;
    [SerializeField] ImageEx imgEnemyHp;
    [SerializeField] TextEx txtEnemyFightPonit;
    [SerializeField] AvatarCell enemyAvatarCell;
    [SerializeField] List<ArenaHeroHead> enemyHeroHeads;
    protected override void RegisterBattleEvents()
    {
        base.RegisterBattleEvents();
        EventBroadcast.Instance.AddListener<string, JsonData>(EventName.BATTLE_END, OnBattleEnd);
    }
    protected override void UnregisterBattleEvents()
    {
        base.UnregisterBattleEvents();
        EventBroadcast.Instance.RemoveListener<string, JsonData>(EventName.BATTLE_END, OnBattleEnd);
    }
    protected virtual void OnBattleEnd(string guid, JsonData endData)
    {
        if (battleField != null && guid == battleField.guid)
        {
            DisplayHpInfo();
        }
    }
    protected override void OnPreOpen()
    {
        base.OnPreOpen();
        MainWin.TabChangeEvent += OnTabChangeEvent;
        UIManager.Instance.OnOpenWindow += OnOpenWindow;
        bool isOpenBattleChangeTab = IsOpenBattleChangeTab();
        transButtons.localPosition = new Vector3(0, isOpenBattleChangeTab ? 130 : 0, 0);
        if (isOpenBattleChangeTab)
        {
            UIManager.Instance.GetUI<MainWin>()?.CloseSubUI();
        }
        else
        {
            UIManager.Instance.CloseWindow<MainWin>();
        }
        isClickSkip = false;
    }
    protected override void OnPreClose()
    {
        base.OnPreClose();
        MainWin.TabChangeEvent -= OnTabChangeEvent;
        UIManager.Instance.OnOpenWindow -= OnOpenWindow;
        bool isOpenBattleChangeTab = IsOpenBattleChangeTab();
        if (isOpenBattleChangeTab)
        {
            UIManager.Instance.GetUI<MainWin>()?.RestoreSubUI();
        }
        else
        {
            UIManager.Instance.OpenWindowAsync<MainWin>().Forget();
        }
        if (isClickSkip)
        {
            isClickSkip = false;
            TryPass();
        }
    }
    void OnOpenWindow(UIBase win)
    {
        if (win is GuildAtkDefBatVictoryWin || win is GuildAtkDefBatFailWin)
        {
            isClickSkip = false;
        }
    }
    bool isClickSkip = false;
    protected override void OnClickPass()
    {
        if (!IsPass())
            return;
        isClickSkip = true;
        clickTime = Time.time;  // 记录点击时间
        battleField.ForceFinish();
    }
    float stayTime = 2f;
    float clickTime = 0f;
    void LateUpdate()
    {
        if (isClickSkip && Time.time - clickTime >= stayTime)
        {
            isClickSkip = false;
            TryPass();
        }
    }
    private void TryPass()
    {
        if (UIManager.Instance.IsOpened<GuildAtkDefBatVictoryWin>() ||
            UIManager.Instance.IsOpened<GuildAtkDefBatFailWin>())
            return;
        CloseWindow();
        Debug.LogError($"OnBattleEnd 异常关闭");
        BattleSettlementManager.Instance.WinShowOver(BattleConst.GuildAtkDefBatField);
    }
    private void OnTabChangeEvent()
    {
        UIManager.Instance.CloseWindow<GuildAtkDefBatBattleWin>();
    }
    protected override void OnDamageTaken(BattleDmgInfo damageInfo)
    {
        base.OnDamageTaken(damageInfo);
        if (battleField == null)
            return;
        if (damageInfo.battleFieldGuid == battleField.guid)
        {
            DisplayHpInfo();
        }
    }
    protected override void OnCreateBattleField(string guid, BattleField field)
    {
        if (field is GuildAtkDefBatField)
        {
            SetBattleField(field);
        }
    }
    protected override void RefreshSpecific()
    {
        DisplayHpInfo();
        DisplayPlayerInfo();
    }
    private void DisplayHpInfo()
    {
        if (battleField == null)
            return;
        // 获取我方(红方)队伍数据
        List<BattleObject> myTeam = battleField.battleObjMgr.GetBattleObjList(BattleCamp.Red);
        // 获取敌方(蓝方)队伍数据
        List<BattleObject> enemyTeam = battleField.battleObjMgr.GetBattleObjList(BattleCamp.Blue);
        ulong myMaxHp = GetMaxHP(myTeam);
        ulong enemyMaxHp = GetMaxHP(enemyTeam);
        ulong myNowHp = GetNowHP(myTeam);
        ulong enemyNowHp = GetNowHP(enemyTeam);
        txtMyHp.text = Language.Get("BoneField09", UIHelper.ReplaceLargeNum(myNowHp), UIHelper.ReplaceLargeNum(myMaxHp));
        txtEnemyHp.text = Language.Get("BoneField09", UIHelper.ReplaceLargeNum(enemyNowHp), UIHelper.ReplaceLargeNum(enemyMaxHp));
        imgMyHp.fillAmount = Mathf.Clamp01((myMaxHp > 0) ? (float)myNowHp / myMaxHp : 0f);
        imgEnemyHp.fillAmount = Mathf.Clamp01((enemyMaxHp > 0) ? (float)enemyNowHp / enemyMaxHp : 0f);
    }
    private long GetFightPonit(List<BattleObject> Team)
    {
        if (Team.IsNullOrEmpty())
        {
            return 0;
        }
        long res = 0;
        foreach (var obj in Team)
        {
            if (obj is not HeroBattleObject)
                continue;
            res += obj.GetFightPower();
        }
        return res;
    }
    private void DisplayPlayerInfo()
    {
        if (battleField == null)
            return;
        // 从 GuildAtkDefBatManager 获取防守方数据
        uint targetPlayerID = GuildAtkDefBatManager.Instance.CurrentChallengeTargetPlayerID;
        if (targetPlayerID == 0)
            return;
        if (!GuildAtkDefBatManager.Instance.EnemyFamilyMembers.TryGetValue(targetPlayerID, out var enemyMem))
            return;
        // 获取我方(红方)队伍数据
        List<BattleObject> myTeam = battleField.battleObjMgr.GetBattleObjList(BattleCamp.Red);
        // 获取敌方(蓝方)队伍数据
        List<BattleObject> enemyTeam = battleField.battleObjMgr.GetBattleObjList(BattleCamp.Blue);
        txtEnemyLV.text = Language.Get("Arena22", enemyMem.LV);
        txtEnemyName.text = UIHelper.ServerStringTrim(enemyMem.Name);
        txtEnemyFightPonit.text = UIHelper.ReplaceLargeArtNum(GetFightPonit(enemyTeam));
        enemyAvatarCell.InitUI(AvatarHelper.GetAvatarModel((int)enemyMem.PlayerID, (int)enemyMem.Face, (int)enemyMem.FacePic)).Forget();
        enemyAvatarCell.SetOnLoaded(() =>
        {
            enemyAvatarCell.SetListener(() =>
            {
                AvatarHelper.TryViewOtherPlayerInfo((int)enemyMem.PlayerID, (int)enemyMem.ServerID, viewPlayerLineupType: (int)BattlePreSetType.GuildAtkDefBat);
            });
        });
        var team = GetTeamHeroList(enemyTeam);
        enemyCountry.RefreshOnTeamCountry(team, true);
        txtMyLV.text = Language.Get("Arena22", PlayerDatas.Instance.baseData.LV);
        txtMyName.text = PlayerDatas.Instance.baseData.PlayerName;
        long myFightPower = GetFightPonit(myTeam);
        txtMyFightPonit.text = UIHelper.ReplaceLargeArtNum(myFightPower);
        myAvatarCell.InitUI(AvatarHelper.GetAvatarModel((int)PlayerDatas.Instance.baseData.PlayerID, PlayerDatas.Instance.baseData.face, PlayerDatas.Instance.baseData.facePic)).Forget();
        team = GetTeamHeroList(myTeam);
        myCountry.RefreshOnTeamCountry(team, true);
        for (int i = 0; i < myHeroHeads.Count; i++)
        {
            if (i < myTeam.Count && myTeam[i] is HeroBattleObject heroBattleObject)
            {
                myHeroHeads[i].SetActive(true);
                var teamHero = heroBattleObject.teamHero;
                myHeroHeads[i].Display(teamHero.heroId, teamHero.SkinID, teamHero.level);
            }
            else
            {
                myHeroHeads[i].SetActive(false);
            }
        }
        // 敌方英雄显示(从右到左)
        for (int i = enemyHeroHeads.Count - 1; i >= 0; i--)
        {
            int teamIndex = enemyHeroHeads.Count - 1 - i;  // 建立反向映射
            if (teamIndex < enemyTeam.Count && enemyTeam[teamIndex] is HeroBattleObject heroBattleObject)
            {
                enemyHeroHeads[i].SetActive(true);
                var teamHero = heroBattleObject.teamHero;  // 使用映射后的索引
                enemyHeroHeads[i].Display(teamHero.heroId, teamHero.SkinID, teamHero.level);
            }
            else
            {
                enemyHeroHeads[i].SetActive(false);
            }
        }
    }
    ulong GetMaxHP(List<BattleObject> battleObjects)
    {
        ulong sun = 0;
        if (!battleObjects.IsNullOrEmpty())
        {
            for (int i = 0; i < battleObjects.Count; i++)
            {
                sun += (ulong)battleObjects[i].GetMaxHp();
            }
        }
        return sun;
    }
    ulong GetNowHP(List<BattleObject> battleObjects)
    {
        ulong sun = 0;
        if (!battleObjects.IsNullOrEmpty())
        {
            for (int i = 0; i < battleObjects.Count; i++)
            {
                sun += (ulong)battleObjects[i].GetCurHp();
            }
        }
        return sun;
    }
    List<TeamHero> GetTeamHeroList(List<BattleObject> teams)
    {
        List<TeamHero> teamHeroes = new List<TeamHero>();
        if (teams.IsNullOrEmpty())
            return teamHeroes;
        foreach (var item in teams)
        {
            if (item is HeroBattleObject heroBattleObject)
            {
                teamHeroes.Add(heroBattleObject.teamHero);
            }
        }
        return teamHeroes;
    }
    bool IsOpenBattleChangeTab()
    {
        return FuncOpen.Instance.IsFuncOpen(ArenaManager.Instance.BattleChangeTabFuncId);
    }
}
Main/System/Battle/GuildAtkDefBatBattleWin.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f25dc954ea79904438c465ce78f96de8
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/BillboardRank/GuildRankWin.cs
@@ -40,7 +40,7 @@
    void OnUpdateViewFamilyInfo(int familyID)
    {
        UIManager.Instance.OpenWindowAsync<GuildPreviewWin>(familyID).Forget();
        UIManager.Instance.OpenWindow<GuildPreviewWin>(familyID);
    }
    public void Display()
Main/System/FuncPreset/FuncPresetManager.cs
@@ -404,6 +404,7 @@
    Story = 1, //主线
    Arena = 2,  //演武场防守
    Qunying = 3, //群英战防守
    GuildAtkDefBat = 4,//公会攻防战防守
}
Main/System/Guild/GuildBaseWin.cs
@@ -27,6 +27,23 @@
    [SerializeField] Button requestBtn;
    [SerializeField] ImageEx guildBattleLockImg;
    [SerializeField] Text guildBattleTitleText;
    [SerializeField] Button guildBattleBtn;
    [SerializeField] Text guildBattleState;
    [SerializeField] Image guildBattleStateImg;
    [SerializeField] Text GuildBattleCountDownTime;
    [SerializeField] ImageEx guildBattleImg;
    [SerializeField] UIEffectPlayer guildBattleEffect;
    [SerializeField] Color32 guildBattleState1;//未参战
    [SerializeField] Color32 guildBattleState2;//对战失败
    [SerializeField] Color32 guildBattleState3;//备战中
    [SerializeField] Color32 guildBattleState4;//激战中
    [SerializeField] Color32 guildBattleState5;//对战胜利
    //NPC对话相关
    [Header("行商必须放第一个")]
    [SerializeField] HeroSkinModel[] funcNPCs;
@@ -61,6 +78,12 @@
        {
            UIManager.Instance.OpenWindow<GuildRankWin>();
        });
        guildBattleBtn.AddListener(() =>
        {
            if (!GuildAtkDefBatManager.Instance.IsOpen(true))
                return;
            UIManager.Instance.OpenWindowAsync<GuildAtkDefBatWin>().Forget();
        });
        InitHawker();
        talkBtn.AddListener(OnClickTalkButton);
        talkBtn1.AddListener(OnClickTalkButton);
@@ -71,11 +94,12 @@
    {
        if (PlayerDatas.Instance.fairyData.fairy == null)
            return;
        GlobalTimeEvent.Instance.secondEvent += OnSecondEvent;
        GlobalTimeEvent.Instance.fiveSecondEvent += OnFiveSecondEvent;
        ChatManager.Instance.OnUpdateTalkEvent += OnUpdateTalkEvent;
        PlayerDatas.Instance.fairyData.OnRefreshFairyInfo += OnRefreshFairyInfo;
        FuncOpen.Instance.OnFuncStateChangeEvent += OnFuncStateChangeEvent;
        GuildAtkDefBatManager.Instance.OnUpdateAtkDefBatInfoEvent += OnUpdateAtkDefBatInfoEvent;
        Display();
    }
@@ -85,6 +109,18 @@
        GlobalTimeEvent.Instance.fiveSecondEvent -= OnFiveSecondEvent;
        ChatManager.Instance.OnUpdateTalkEvent -= OnUpdateTalkEvent;
        PlayerDatas.Instance.fairyData.OnRefreshFairyInfo -= OnRefreshFairyInfo;
        FuncOpen.Instance.OnFuncStateChangeEvent -= OnFuncStateChangeEvent;
        GuildAtkDefBatManager.Instance.OnUpdateAtkDefBatInfoEvent -= OnUpdateAtkDefBatInfoEvent;
    }
    private void OnUpdateAtkDefBatInfoEvent()
    {
        ShowGuildBattleInfo();
    }
    private void OnFuncStateChangeEvent(int funcId)
    {
        if (funcId == (int)FuncOpenEnum.GuildAtkDefBat)
            ShowGuildBattleInfo();
    }
    void OnRefreshFairyInfo()
@@ -104,11 +140,72 @@
        ShowGuildInfo();
        ShowHawkerTime();
        ShowTalkInfo();
        ShowGuildBattleInfo();
    }
    void OnSecondEvent()
    {
        ShowHawkerTime(true);
        UpdateGuildBattleCountdown();
    }
    /// <summary>
    /// 刷新攻防战的当前状态及对应描边颜色
    /// </summary>
    void ShowGuildBattleInfo()
    {
        bool isOpen = GuildAtkDefBatManager.Instance.IsOpen();
        guildBattleLockImg.SetActive(!isOpen);
        guildBattleTitleText.color = isOpen ? new Color32(222, 212, 200, 255) : UIHelper.GetUIColor(TextColType.Gray);
        guildBattleStateImg.SetActive(isOpen);
        guildBattleImg.SetActive(isOpen);
        guildBattleEffect.SetActive(false);
        if (!isOpen)
            return;
        // 状态标识:1-未参战, 2-备战中, 3-激战中, 4-胜利(休战中), 5-失败(休战中)
        int displayState = GuildAtkDefBatManager.Instance.GetGuildBattleDisplayState();
        switch (displayState)
        {
            case 1: // 未参战
                guildBattleState.text = Language.Get("GuildAtkDefBat27");
                guildBattleState.color = guildBattleState1;
                break;
            case 2: // 备战中
                guildBattleState.text = Language.Get("GuildAtkDefBat25");
                guildBattleState.color = guildBattleState3;
                break;
            case 3: // 激战中
                guildBattleState.text = Language.Get("GuildAtkDefBat24");
                guildBattleState.color = guildBattleState4;
                guildBattleImg.SetActive(false);
                guildBattleEffect.SetActive(true);
                break;
            case 4: // 胜利(休战中)
                guildBattleState.text = Language.Get("GuildAtkDefBat23");
                guildBattleState.color = guildBattleState5;
                break;
            case 5: // 失败(休战中)
                guildBattleState.text = Language.Get("GuildAtkDefBat26");
                guildBattleState.color = guildBattleState2;
                break;
        }
        UpdateGuildBattleCountdown();
    }
    /// <summary>
    /// 刷新倒计时文本 (每秒调用), 仅备战期/激战期显示
    /// </summary>
    void UpdateGuildBattleCountdown()
    {
        int state = GuildAtkDefBatManager.Instance.BatState;
        bool show = state == 2 || state == 3;
        GuildBattleCountDownTime.SetActive(show);
        if (!show) return;
        GuildBattleCountDownTime.text = GuildAtkDefBatManager.Instance.GetGetCountdownToCurrentPeriodEndString();
    }
    void ShowGuildInfo()
Main/System/Guild/GuildJoinWin.cs
@@ -111,7 +111,7 @@
    void OnUpdateViewFamilyInfo(int familyID)
    {
        UIManager.Instance.OpenWindowAsync<GuildPreviewWin>(familyID).Forget();
        UIManager.Instance.OpenWindow<GuildPreviewWin>(familyID);
    }
    private void OnFindBtnClick()
Main/System/Guild/GuildManager.cs
@@ -479,6 +479,17 @@
    #region  公会自定义记录
    public void UpdateFamilyAction(HA513_tagMCFamilyActionInfo _package)
    {
        // 公会攻防战 需要同时接收敌方攻击日志
        if (GuildAtkDefBatManager.Instance.UpdateFamilyActionInfo(_package))
        {
            return;
        }
        if (GuildAtkDefBatManager.Instance.UpdateAtkDefBatLogInfo(_package))
        {
            return;
        }
        if (PlayerDatas.Instance.baseData.FamilyId != _package.FamilyID)
        {
            return;
Main/System/Guild/GuildPreviewCell.cs
@@ -21,14 +21,14 @@
        int playerID = (int)member.PlayerID;
        int serverID = (int)member.ServerID;
        avatarCell.InitUI(AvatarHelper.GetAvatarModel(0, (int)member.Face, (int)member.FacePic)).Forget();
        avatarCell.InitUI(AvatarHelper.GetAvatarModel(0, (int)member.Face, (int)member.FacePic));
        avatarCell.AddListener(() =>
        {
            OtherPlayerDetailManager.Instance.ViewPlayerDetail(playerID, serverID);
        });
        officialTitle.InitUI(member.RealmLV, (int)member.TitleID).Forget();
        officialTitle.InitUI(member.RealmLV, (int)member.TitleID);
        levelText.text = member.LV.ToString();
        jobText.text = RichTextMsgReplaceConfig.GetRichReplace("FAMILY", member.FmLV);
        nameText.text = member.Name;
Main/System/GuildAtkDefBat.meta
New file
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a3e90fd73fea479458b389e26c19ec9b
folderAsset: yes
DefaultImporter:
  externalObjects: {}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/GuildAtkDefBat/GuildAtkDefBatAwardPreviewCell.cs
New file
@@ -0,0 +1,13 @@
using UnityEngine;
using UnityEngine.UI;
public class GuildAtkDefBatAwardPreviewCell : CellView
{
    [SerializeField] ItemCell itemCell;
    public void SetItemData(int itemId, int count)
    {
        itemCell.Init(new ItemCellModel(itemId, true, count));
        itemCell.SetClickListener(() => { ItemTipUtility.Show(itemId); });
    }
}
Main/System/GuildAtkDefBat/GuildAtkDefBatAwardPreviewCell.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 759d1f789fd7b794a8ab65272d34e34d
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/GuildAtkDefBat/GuildAtkDefBatAwardPreviewHeaderCell.cs
New file
@@ -0,0 +1,12 @@
using UnityEngine;
using UnityEngine.UI;
public class GuildAtkDefBatAwardPreviewHeaderCell : CellView
{
    [SerializeField] TextEx titleText;
    public void SetTitle(string text)
    {
        titleText.text = text;
    }
}
Main/System/GuildAtkDefBat/GuildAtkDefBatAwardPreviewHeaderCell.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 49247c0fb43606847a8a8ac714603924
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/GuildAtkDefBat/GuildAtkDefBatAwardPreviewNomalCell.cs
New file
@@ -0,0 +1,42 @@
using UnityEngine;
using UnityEngine.UI;
public class GuildAtkDefBatAwardPreviewNomalCell : CellView
{
    [SerializeField] ScrollerController scroller;
    public void SetData(int[][] awardData)
    {
        scroller.OnRefreshCell -= OnInnerRefreshCell;
        scroller.OnRefreshCompleteEvent -= OnInnerRefreshComplete;
        scroller.OnRefreshCell += OnInnerRefreshCell;
        scroller.OnRefreshCompleteEvent += OnInnerRefreshComplete;
        scroller.Refresh();
        if (awardData != null)
        {
            for (int i = 0; i < awardData.Length; i++)
            {
                CellInfo info = new CellInfo(awardData[i][0], awardData[i][1], null);
                scroller.AddCell(ScrollerDataType.Header, i, info);
            }
        }
        scroller.Restart();
    }
    void OnInnerRefreshComplete()
    {
        scroller.OnRefreshCell -= OnInnerRefreshCell;
        scroller.OnRefreshCompleteEvent -= OnInnerRefreshComplete;
    }
    void OnInnerRefreshCell(ScrollerDataType type, CellView cell)
    {
        GuildAtkDefBatAwardPreviewCell previewCell = cell as GuildAtkDefBatAwardPreviewCell;
        if (previewCell != null && cell.info.HasValue)
        {
            int itemId = cell.info.Value.infoInt1;
            int count = cell.info.Value.infoInt2;
            previewCell.SetItemData(itemId, count);
        }
    }
}
Main/System/GuildAtkDefBat/GuildAtkDefBatAwardPreviewNomalCell.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 62ddd20b830552c47be3f9565494da8b
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/GuildAtkDefBat/GuildAtkDefBatAwardPreviewTailCell.cs
New file
@@ -0,0 +1,53 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class GuildAtkDefBatAwardPreviewTailCell : CellView
{
    [SerializeField] TextEx awardTypeText;
    [SerializeField] TextEx awardCountText;
    [SerializeField] ScrollerController scroller;
    public void SetData(int quality, int maxCount, int libType)
    {
        awardTypeText.text = Language.Get($"GuildAtkDefBatAwardType{quality}");
        awardCountText.text = Language.Get($"GuildAtkDefBat15", maxCount);
        scroller.OnRefreshCell -= OnInnerRefreshCell;
        scroller.OnRefreshCompleteEvent -= OnInnerRefreshComplete;
        scroller.OnRefreshCell += OnInnerRefreshCell;
        scroller.OnRefreshCompleteEvent += OnInnerRefreshComplete;
        scroller.Refresh();
        var items = FamilyAtkDefBatTreasureConfig.GetItemsByQualityAndLibType(quality, libType);
        if (items == null || items.Count == 0)
        {
            scroller.Restart();
            return;
        }
        for (int i = 0; i < items.Count; i++)
        {
            CellInfo info = new CellInfo(items[i].ItemID, items[i].ItemCount, null);
            scroller.AddCell(ScrollerDataType.Header, i, info);
        }
        scroller.Restart();
    }
    void OnInnerRefreshComplete()
    {
        scroller.OnRefreshCell -= OnInnerRefreshCell;
        scroller.OnRefreshCompleteEvent -= OnInnerRefreshComplete;
    }
    void OnInnerRefreshCell(ScrollerDataType type, CellView cell)
    {
        GuildAtkDefBatAwardPreviewCell previewCell = cell as GuildAtkDefBatAwardPreviewCell;
        if (previewCell != null && cell.info.HasValue)
        {
            int itemId = cell.info.Value.infoInt1;
            int count = cell.info.Value.infoInt2;
            previewCell.SetItemData(itemId, count);
        }
    }
}
Main/System/GuildAtkDefBat/GuildAtkDefBatAwardPreviewTailCell.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8118a95412b4fa448a069fdd99949275
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/GuildAtkDefBat/GuildAtkDefBatAwardPreviewWin.cs
New file
@@ -0,0 +1,107 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class GuildAtkDefBatAwardPreviewWin : FunctionsBaseWin
{
    [SerializeField] ScrollerController scroller;
    protected override void OpenSubUIByTabIndex()
    {
        int[][] awardData;
        if (functionOrder == 0)
            awardData = GuildAtkDefBatManager.Instance.guildLoseAward;
        else
            awardData = GuildAtkDefBatManager.Instance.guildWinAward;
        scroller.OnRefreshCell -= OnOuterRefreshCell;
        scroller.OnRefreshCompleteEvent -= OnOuterRefreshComplete;
        scroller.OnRefreshCell += OnOuterRefreshCell;
        scroller.OnRefreshCompleteEvent += OnOuterRefreshComplete;
        scroller.Refresh();
        // 1. 标题:GuildAtkDefBat13
        scroller.AddCell(ScrollerDataType.Header, 0);
        // 2. NomalCell(奖励物品列表),用 infoInt1 传 functionOrder 便于区分数据源
        CellInfo nomalInfo = new CellInfo(functionOrder, 0, null);
        scroller.AddCell(ScrollerDataType.Normal, 0, nomalInfo);
        // 3. 标题:GuildAtkDefBat14
        scroller.AddCell(ScrollerDataType.Header, 1);
        // 4. TailCell:先加普通品质(1),再遍历 qualityMaxOutput 其他品质 key 升序添加
        var qualityDict = GuildAtkDefBatManager.Instance.qualityMaxOutput;
        int maxGridCount = GuildAtkDefBatManager.Instance.CalculateMaxGridCount();
        int nonNormalTotal = GuildAtkDefBatManager.Instance.GetNonNormalQualityTotalOutput();
        // 4b. 遍历 qualityMaxOutput 动态添加其余品质的 TailCell
        if (qualityDict != null)
        {
            var keys = new List<int>(qualityDict.Keys);
            keys.Sort();
            keys.Reverse();
            for (int i = 0; i < keys.Count; i++)
            {
                int quality = keys[i];
                if (quality == 1) continue;
                CellInfo info = new CellInfo(quality, qualityDict[quality], null);
                info.SetInfo(functionOrder);
                scroller.AddCell(ScrollerDataType.Tail, quality, info);
            }
        }
        // 4a. 品质1(普通):最大产出数 = 最大总格子数 - 非普通品质总产出数,始终添加
        int normalMaxCount = maxGridCount - nonNormalTotal;
        CellInfo normalInfo = new CellInfo(1, normalMaxCount, null);
        normalInfo.SetInfo(functionOrder);
        scroller.AddCell(ScrollerDataType.Tail, 1, normalInfo);
        scroller.Restart();
    }
    void OnOuterRefreshComplete()
    {
        scroller.OnRefreshCell -= OnOuterRefreshCell;
        scroller.OnRefreshCompleteEvent -= OnOuterRefreshComplete;
    }
    void OnOuterRefreshCell(ScrollerDataType type, CellView cell)
    {
        switch (type)
        {
            case ScrollerDataType.Header:
                GuildAtkDefBatAwardPreviewHeaderCell headerCell = cell as GuildAtkDefBatAwardPreviewHeaderCell;
                if (headerCell != null)
                {
                    int headerIndex = cell.index;
                    string langKey = headerIndex == 0 ? "GuildAtkDefBat13" : "GuildAtkDefBat14";
                    headerCell.SetTitle(Language.Get(langKey));
                }
                break;
            case ScrollerDataType.Normal:
                GuildAtkDefBatAwardPreviewNomalCell nomalCell = cell as GuildAtkDefBatAwardPreviewNomalCell;
                if (nomalCell != null && cell.info.HasValue)
                {
                    int tabIndex = cell.info.Value.infoInt1;
                    int[][] awardData = tabIndex == 0
                        ? GuildAtkDefBatManager.Instance.guildLoseAward
                        : GuildAtkDefBatManager.Instance.guildWinAward;
                    nomalCell.SetData(awardData);
                }
                break;
            case ScrollerDataType.Tail:
                GuildAtkDefBatAwardPreviewTailCell tailCell = cell as GuildAtkDefBatAwardPreviewTailCell;
                if (tailCell != null && cell.info.HasValue)
                {
                    int quality = cell.info.Value.infoInt1;
                    int maxCount = cell.info.Value.infoInt2;
                    int tabIndex = cell.info.Value.infoInt3;
                    int libType = tabIndex == 0 ? 2 : 1;
                    tailCell.SetData(quality, maxCount, libType);
                }
                break;
        }
    }
}
Main/System/GuildAtkDefBat/GuildAtkDefBatAwardPreviewWin.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5e3527a68ba8bb74cbfc7d302c0f0b71
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/GuildAtkDefBat/GuildAtkDefBatBattlePanelHeaderCell.cs
New file
@@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using UnityEngine;
public class GuildAtkDefBatBattlePanelHeaderCell : CellView
{
    [SerializeField] ImageEx cityImage;
    [SerializeField] ImageEx[] flagImages;
    [SerializeField] GuildAtkDefBatBattlePanelItem[] items;//3个
    public void Display(bool isMy, List<HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatMem> sortedDefenders)
    {
        // 城市背景:己方蓝色,敌方红色
        cityImage.SetSprite(isMy ? "GuildAtkDefBatCityBlue" : "GuildAtkDefBatCityRed");
        // 旗帜:己方蓝色,敌方红色
        string flagIcon = isMy ? "GuildAtkDefBatFlagBlue" : "GuildAtkDefBatFlagRed";
        for (int i = 0; i < flagImages.Length; i++)
        {
            flagImages[i].SetSprite(flagIcon);
        }
        // 直接取排序列表的前3个,分别赋值给items[0]/[1]/[2]
        // items[1]和items[2]显示城墙,items[0]不显示
        for (int i = 0; i < items.Length; i++)
        {
            bool hasMember = sortedDefenders != null && i < sortedDefenders.Count;
            bool showWall = (i == 1 || i == 2); // 第二和第三个item显示城墙
            items[i].Display(isMy, hasMember ? sortedDefenders[i] : null, showWall);
        }
    }
}
Main/System/GuildAtkDefBat/GuildAtkDefBatBattlePanelHeaderCell.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a96bab34ae8ccc948a4d7343dc787f47
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/GuildAtkDefBat/GuildAtkDefBatBattlePanelItem.cs
New file
@@ -0,0 +1,253 @@
using System;
using System.Drawing;
using Cysharp.Threading.Tasks;
using UnityEngine;
public class GuildAtkDefBatBattlePanelItem : MonoBehaviour
{
    [SerializeField] RectTransform modelRect;
    [SerializeField] HorseController horseModel;
    [SerializeField] UIEffectPlayer uIEffectPlayer;
    [SerializeField] ButtonEx clickButton;
    [SerializeField] RectTransform wallRect;
    [SerializeField] TextEx nameText;
    [SerializeField] OutlineEx nameOutlineEx;
    [SerializeField] Color32 nameColorBlue;
    [SerializeField] Color32 nameColorRed;
    [SerializeField] Color32 nameColorGreen;
    [SerializeField] Color32 nameOutLineColorBlue;
    [SerializeField] Color32 nameOutLineColorRed;
    [SerializeField] Color32 nameOutLineColorGreen;
    [SerializeField] RectTransform starGruoupRect1;
    [SerializeField] ImageEx[] starGruoupImages1;
    [SerializeField] RectTransform starGruoupRect2;
    [SerializeField] ImageEx[] starGruoupImages2;
    [SerializeField] RectTransform starGruoupRect3;
    [SerializeField] ImageEx[] starGruoupImages3;
    [SerializeField] TextEx fightPowerText;
    [SerializeField] OfficialTitleCell officialTitleCell;
    [SerializeField] ButtonEx challengeButton;
    [SerializeField] ButtonEx changeButton;
    [SerializeField] ButtonEx sweepButton;
    public void Display(bool isMy, HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatMem mem, bool showWall)
    {
        if (mem == null)
        {
            this.SetActive(false);
            return;
        }
        this.SetActive(true);
        // modelRect 水平翻转:己方-1,敌方1
        Vector3 localScale = modelRect.localScale;
        localScale.x = isMy ? 1f : -1f;
        modelRect.localScale = localScale;
        // 判断是否所有属性档位均已被击杀
        bool isCompletelyKilled = IsAllPropertyKilled(mem);
        // horseModel:未完全击败显示,完全击败隐藏
        horseModel.SetActive(!isCompletelyKilled);
        if (!isCompletelyKilled)
        {
            // 设置坐骑和角色模型
            int horseSkinID = HorseManager.Instance.GetOtherPlayerHorseSkinID((int)mem.EquipShowSwitch);
            int heroSkinID = ResolveHeroSkinID((int)mem.ModelMark, (int)mem.Job);
            horseModel.Create(horseSkinID, heroSkinID, 0.8f).Forget();
        }
        // uIEffectPlayer:完全击败显示,未完全击败隐藏(与horseModel相反)
        uIEffectPlayer.SetActive(isCompletelyKilled);
        // wallRect:由外部参数控制显隐(仅HeaderCell的副将和镇卫显示)
        wallRect.SetActive(showWall);
        // 官职与称号
        officialTitleCell.InitUI(mem.RealmLV, (int)mem.TitleID).Forget();
        // 名字颜色与描边
        if (isMy)
        {
            // 己方战场:自己的名字绿色,其他人的名字蓝色
            bool isSelf = mem.PlayerID == PlayerDatas.Instance.PlayerId;
            if (isSelf)
            {
                nameText.color = nameColorGreen;
                nameOutlineEx.OutlineColor = nameOutLineColorGreen;
            }
            else
            {
                nameText.color = nameColorBlue;
                nameOutlineEx.OutlineColor = nameOutLineColorBlue;
            }
        }
        else // 敌方统一红色
        {
            nameText.color = nameColorRed;
            nameOutlineEx.OutlineColor = nameOutLineColorRed;
        }
        // 名字
        nameText.text = mem.Name;
        // 战力显示
        long totalFightPower = GuildAtkDefBatManager.CalcTotalFightPower(mem.FightPower, mem.FightPowerEx);
        fightPowerText.text = UIHelper.ReplaceLargeArtNum(totalFightPower);
        // clickButton:点击查看他人详情
        clickButton.SetListener(() =>
        {
            AvatarHelper.TryViewOtherPlayerInfo((int)mem.PlayerID, (int)mem.ServerID, viewPlayerLineupType: (int)BattlePreSetType.GuildAtkDefBat);
        });
        // changeButton:备战期时,公会会长/副会长在己方战场显示
        bool canChange = false;
        if (isMy && GuildAtkDefBatManager.Instance.GetGuildBattleDisplayState() == 2)
        {
            uint myPlayerId = PlayerDatas.Instance.PlayerId;
            if (GuildAtkDefBatManager.Instance.MyFamilyMembers.TryGetValue(myPlayerId, out var myMember))
            {
                canChange = myMember.FmLV >= GuildAtkDefBatManager.Instance.adjustPositionJobLevel;
            }
        }
        changeButton.SetActive(canChange);
        changeButton.SetListener(() =>
        {
            GuildAtkDefBatChangeMemberWin.Open(mem);
        });
        // 全部属性档位被击杀时显示扫荡按钮,否则显示挑战按钮(仅在敌方战场显示)
        bool isAllKilled = IsAllPropertyKilled(mem);
        challengeButton.SetActive(!isMy && !isAllKilled);
        sweepButton.SetActive(!isMy && isAllKilled);
        challengeButton.SetListener(() =>
        {
            if (CheckChallengeCount()) return;
            GuildAtkDefBatChallengeWin.Open(mem);
        });
        sweepButton.SetListener(() =>
        {
            if (CheckChallengeCount()) return;
            // 扫荡封包:与挑战一致,仅 ValueList 第二位为 1,第一位属性百分比可任意值
            BattleManager.Instance.SendTurnFight((uint)GuildAtkDefBatManager.DataMapID, 0, 1, mem.PlayerID, new[] { 100u, 1u }, showLoading: false);
        });
        // 星星显示
        DisplayStars(mem);
    }
    /// <summary>
    /// 判断该成员所有属性档位是否均已被击杀
    /// </summary>
    private bool IsAllPropertyKilled(HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatMem mem)
    {
        int[] starList = FamilyAtkDefBatDefenderConfig.GetDefStarList(mem.DefType);
        if (starList == null || starList.Length == 0)
            return false;
        for (int i = 0; i < starList.Length; i++)
        {
            if (!GuildAtkDefBatManager.Instance.IsPropertyKilled(mem.KilledState, i))
                return false;
        }
        return true;
    }
    /// <summary>
    /// 根据 ModelMark 和 Job 解析英雄皮肤ID
    /// </summary>
    private int ResolveHeroSkinID(int modelMark, int job)
    {
        if (modelMark == 0)
        {
            PhantasmPavilionManager.Instance.TryGetDefaultID(PhantasmPavilionType.Model, job, out modelMark);
        }
        if (!ModelConfig.HasKey(modelMark))
            return 0;
        var modelConfig = ModelConfig.Get(modelMark);
        return modelConfig.SkinID;
    }
    /// <summary>
    /// 显示破阵之星
    /// 根据防守类型从 FamilyAtkDefBatDefenderConfig 获取各档位星数,
    /// 按 KilledState 统计被击杀的总星数,从右往左将暗星排在尾部
    /// </summary>
    private void DisplayStars(HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatMem mem)
    {
        int totalStars = FamilyAtkDefBatDefenderConfig.GetTotalStarCount(mem.DefType);
        if (totalStars <= 0)
        {
            starGruoupRect1.SetActive(false);
            starGruoupRect2.SetActive(false);
            starGruoupRect3.SetActive(false);
            return;
        }
        // 根据总星数选择显示组
        RectTransform activeGroupRect = null;
        ImageEx[] activeStarImages = null;
        if (totalStars <= 3)
        {
            activeGroupRect = starGruoupRect1;
            activeStarImages = starGruoupImages1;
        }
        else if (totalStars <= 6)
        {
            activeGroupRect = starGruoupRect2;
            activeStarImages = starGruoupImages2;
        }
        else // <= 9
        {
            activeGroupRect = starGruoupRect3;
            activeStarImages = starGruoupImages3;
        }
        starGruoupRect1.SetActive(activeGroupRect == starGruoupRect1);
        starGruoupRect2.SetActive(activeGroupRect == starGruoupRect2);
        starGruoupRect3.SetActive(activeGroupRect == starGruoupRect3);
        int[] starList = FamilyAtkDefBatDefenderConfig.GetDefStarList(mem.DefType);
        byte killedState = mem.KilledState;
        // 统计被击杀的总星数(从右往左扣)
        int darkStarCount = 0;
        for (int diffIdx = 0; diffIdx < starList.Length; diffIdx++)
        {
            if ((killedState & (1 << diffIdx)) != 0)
                darkStarCount += starList[diffIdx];
        }
        int litStarCount = totalStars - darkStarCount;
        int displayCount = Mathf.Min(totalStars, activeStarImages.Length);
        for (int starPos = 0; starPos < displayCount; starPos++)
        {
            string icon = starPos < litStarCount ? "GuildAtkDefBatStarLight" : "GuildAtkDefBatStarDark";
            activeStarImages[starPos].SetSprite(icon);
            activeStarImages[starPos].SetActive(true);
        }
        // 隐藏超出总星数的图片
        for (int i = displayCount; i < activeStarImages.Length; i++)
        {
            activeStarImages[i].SetActive(false);
        }
    }
    /// <summary>检查进攻次数,不足时提示并返回 true</summary>
    private bool CheckChallengeCount()
    {
        if (GuildAtkDefBatManager.Instance.GetMyRemainingChallengeCount() <= 0)
        {
            SysNotifyMgr.Instance.ShowTip("GuildAtkDefBat5");
            return true;
        }
        return false;
    }
}
Main/System/GuildAtkDefBat/GuildAtkDefBatBattlePanelItem.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7e15df14b346faa4b96acc09df2446dc
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/GuildAtkDefBat/GuildAtkDefBatBattlePanelNormalCell.cs
New file
@@ -0,0 +1,18 @@
using System;
using Cysharp.Threading.Tasks;
using UnityEngine;
public class GuildAtkDefBatBattlePanelNormalCell : CellView
{
    [SerializeField] GuildAtkDefBatBattlePanelItem[] items; //2个
    public void Display(bool isMy, HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatMem mem1, HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatMem mem2)
    {
        // NormalCell 不显示城墙
        items[0].Display(isMy, mem1, false);
        if (items.Length > 1)
        {
            items[1].Display(isMy, mem2, false);
        }
    }
}
Main/System/GuildAtkDefBat/GuildAtkDefBatBattlePanelNormalCell.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 78880cafb571bd34f81e51b7605b477a
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/GuildAtkDefBat/GuildAtkDefBatBattlePanelWin.cs
New file
@@ -0,0 +1,345 @@
using System;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using UnityEngine;
public class GuildAtkDefBatBattlePanelWin : OneLevelWin
{
    [SerializeField] GuildEmblemCell myEmblemCell;
    [SerializeField] TextEx myNameText;
    [SerializeField] TextEx myServerText;
    [SerializeField] TextEx myStarCntText;
    [SerializeField] RectTransform enemyRect;
    [SerializeField] GuildEmblemCell enemyEmblemCell;
    [SerializeField] TextEx enemyNameText;
    [SerializeField] TextEx enemyServerText;
    [SerializeField] TextEx enemyStarCntText;
    [SerializeField] RectTransform noJoinRect;
    [SerializeField] TextEx timeText;
    [SerializeField] ButtonEx funPresetDefBtn;
    [SerializeField] ButtonEx funPresetAtkBtn;
    [SerializeField] ButtonEx recordBtn;
    [SerializeField] ButtonEx rankButton;
    [SerializeField] ButtonEx myEmblemBtn;
    [SerializeField] ButtonEx enemyEmblemBtn;
    [SerializeField] TextEx[] top3NameText;
    [SerializeField] TextEx rankTipText;
    [SerializeField] ImageEx atkCntImage;
    [SerializeField] ScrollerController scroller;
    // 战场scroller数据
    private bool m_IsMySide;
    // 缓存打开时的 displayState,用于过天判断是否发生变化以关闭窗口
    private int m_CachedDisplayState;
    private List<HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatMem> m_SortedDefenders;
    private List<HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatMem> m_RemainingDefenders;
    protected override void InitComponent()
    {
        base.InitComponent();
        funPresetDefBtn.AddListener(() =>
        {
            FuncPresetManager.Instance.ClickBattlePreset((int)BattlePreSetType.GuildAtkDefBat);
        });
        funPresetAtkBtn.AddListener(() =>
        {
            FuncPresetManager.Instance.ClickBattlePreset((int)BattlePreSetType.Story);
        });
        recordBtn.AddListener(() =>
        {
            UIManager.Instance.OpenWindowAsync<GuildAtkDefBatRecordWin>().Forget();
        });
        rankButton.SetListener(() =>
        {
            UIManager.Instance.OpenWindowAsync<GuildAtkDefBatRankWin>().Forget();
        });
        // 己方公会徽章点击 - 自己的公会
        myEmblemBtn.AddListener(() =>
        {
            SysNotifyMgr.Instance.ShowTip("GuildTip01");
        });
        // 敌方公会徽章点击
        enemyEmblemBtn.AddListener(() =>
        {
            var enemyInfo = GuildAtkDefBatManager.Instance.EnemyFamilyInfo;
            if (enemyInfo == null)
                return;
            int familyID = (int)enemyInfo.FamilyID;
            int serverID = (int)enemyInfo.ServerID;
            // 自己的公会
            if (PlayerDatas.Instance.fairyData.HasFairy && PlayerDatas.Instance.fairyData.fairy.FamilyID == familyID)
            {
                SysNotifyMgr.Instance.ShowTip("GuildTip01");
                return;
            }
            // 系统公会
            if (GuildAtkDefBatManager.Instance.IsSystemFamily(enemyInfo.FamilyID))
                return;
            // 查看公会信息
            GuildManager.Instance.SendViewGuild(familyID, serverID);
        });
    }
    protected override void OnPreOpen()
    {
        // 根据当前时期设置默认Tab:备战期->我方战场,激战期->敌方战场
        int displayState = GuildAtkDefBatManager.Instance.GetGuildBattleDisplayState();
        if (displayState == 2)
            functionOrder = 0; // 备战期默认我方战场
        else if (displayState == 3)
            functionOrder = 1; // 激战期默认敌方战场
        base.OnPreOpen();
        scroller.OnRefreshCell += OnRefreshCell;
        GlobalTimeEvent.Instance.secondEvent += OnSecondEvent;
        GuildAtkDefBatManager.Instance.OnUpdateAtkDefBatInfoEvent += OnUpdateAtkDefBatInfoEvent;
        GuildManager.Instance.OnUpdateViewFamilyInfo += OnUpdateViewFamilyInfo;
        TimeMgr.Instance.OnDayEvent += OnDayRefresh;
        m_CachedDisplayState = displayState;
        Display();
    }
    protected override void OnPreClose()
    {
        base.OnPreClose();
        scroller.OnRefreshCell -= OnRefreshCell;
        GlobalTimeEvent.Instance.secondEvent -= OnSecondEvent;
        GuildAtkDefBatManager.Instance.OnUpdateAtkDefBatInfoEvent -= OnUpdateAtkDefBatInfoEvent;
        GuildManager.Instance.OnUpdateViewFamilyInfo -= OnUpdateViewFamilyInfo;
        TimeMgr.Instance.OnDayEvent -= OnDayRefresh;
    }
    private void OnUpdateAtkDefBatInfoEvent()
    {
        OnDayRefresh();
        Display();
    }
    private void OnDayRefresh()
    {
        int currentState = GuildAtkDefBatManager.Instance.GetGuildBattleDisplayState();
        if (currentState != m_CachedDisplayState)
            CloseWindow();
    }
    private void OnSecondEvent()
    {
        DisplayTime();
    }
    protected override void OnTabButtonClicked(int index)
    {
        // 对敌方Tab进行锁定管理:备战期锁定(阻止GroupButtonEx.OnPointerClick执行SelectBtn),其他时期解锁
        tabButtons[1].isLock = GuildAtkDefBatManager.Instance.GetGuildBattleDisplayState() == 2;
        // 备战期点击敌方战场Tab时阻止切换并提示
        if (tabButtons[1].isLock && index == 1)
        {
            SysNotifyMgr.Instance.ShowTip("GuildAtkDefBat2");
            return;
        }
        base.OnTabButtonClicked(index);
    }
    void DisplayTime()
    {
        timeText.text = Language.Get("PhantasmPavilion10", UIHelper.AppendColor(TextColType.LightGreen, GuildAtkDefBatManager.Instance.GetGetCountdownToCurrentPeriodEndString()));
    }
    void Display()
    {
        DisplayInfo();
        DisplayTime();
        RefreshBattleScroller();
    }
    void DisplayInfo()
    {
        var myFamilyInfo = GuildAtkDefBatManager.Instance.MyFamilyInfo;
        if (myFamilyInfo != null)
        {
            myNameText.text = myFamilyInfo.FamilyName;
            myServerText.text = ServerListCenter.Instance.GetServerName(UIHelper.GetServerIDByAccount(PlayerDatas.Instance.baseData.AccID));
            myEmblemCell.Display((int)myFamilyInfo.EmblemID, myFamilyInfo.EmblemWord, 0.7f);
            myStarCntText.text = myFamilyInfo.TotalStar.ToString();
        }
        else
        {
            myStarCntText.text = "0";
        }
        var enemyFamilyInfo = GuildAtkDefBatManager.Instance.EnemyFamilyInfo;
        if (enemyFamilyInfo != null)
        {
            bool isSystemFamily = GuildAtkDefBatManager.Instance.IsSystemFamily(enemyFamilyInfo.FamilyID);
            if (isSystemFamily)
            {
                string enemyName = Language.Get("GuildAtkDefBat31");
                enemyNameText.text = enemyName;
                enemyEmblemCell.Display(FamilyEmblemConfig.GetFirstDefaultEmblem(), !string.IsNullOrEmpty(enemyName) ? enemyName[0].ToString() : string.Empty, 0.7f);
                enemyServerText.text = ServerListCenter.Instance.GetServerName(UIHelper.GetServerIDByAccount(PlayerDatas.Instance.baseData.AccID));
                enemyStarCntText.text = enemyFamilyInfo.TotalStar.ToString();
            }
            else
            {
                enemyNameText.text = enemyFamilyInfo.FamilyName;
                enemyEmblemCell.Display((int)enemyFamilyInfo.EmblemID, enemyFamilyInfo.EmblemWord, 0.7f);
                enemyServerText.text = ServerListCenter.Instance.GetServerName((int)enemyFamilyInfo.ServerID);
                enemyStarCntText.text = enemyFamilyInfo.TotalStar.ToString();
            }
        }
        else
        {
            enemyStarCntText.text = "0";
        }
        int displayState = GuildAtkDefBatManager.Instance.GetGuildBattleDisplayState();
        noJoinRect.SetActive(displayState == 1 && enemyFamilyInfo == null);
        enemyRect.SetActive(displayState != 1 && enemyFamilyInfo != null);
        ShowTop3();
    }
    void ShowTop3()
    {
        atkCntImage.SetActive(GuildAtkDefBatManager.Instance.BatState == 3);
        var sortedMembers = GuildAtkDefBatManager.Instance.GetSortedFamilyMembers();
        int top3Count = Mathf.Min(top3NameText.Length, sortedMembers.Count);
        for (int i = 0; i < top3NameText.Length; i++)
        {
            if (i < top3Count)
            {
                top3NameText[i].text = sortedMembers[i].Name;
            }
            else
            {
                top3NameText[i].text = Language.Get("L1124");
            }
        }
        rankTipText.text = Language.Get("GuildAtkDefBat37",
            GuildAtkDefBatManager.Instance.GetMyRemainingChallengeCount(),
            GuildAtkDefBatManager.Instance.challengeCount);
    }
    protected override void OpenSubUIByTabIndex()
    {
        RefreshBattleScroller();
    }
    /// <summary>
    /// 刷新战场Scroller数据
    /// </summary>
    private void RefreshBattleScroller()
    {
        // 根据当前Tab设置己方/敌方
        switch (functionOrder)
        {
            case 0:
                m_IsMySide = true;
                break;
            case 1:
                m_IsMySide = false;
                break;
            default:
                m_IsMySide = true;
                break;
        }
        // 获取排序后的防守成员列表
        m_SortedDefenders = GuildAtkDefBatManager.Instance.GetSortedDefenders(m_IsMySide);
        if (m_SortedDefenders == null)
        {
            m_SortedDefenders = new List<HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatMem>();
        }
        // 前3个用于HeaderCell,剩下的给NormalCell
        int headerCount = Mathf.Min(3, m_SortedDefenders.Count);
        m_RemainingDefenders = new List<HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatMem>();
        for (int i = headerCount; i < m_SortedDefenders.Count; i++)
        {
            m_RemainingDefenders.Add(m_SortedDefenders[i]);
        }
        // 重新构建Scroller
        scroller.Refresh();
        // 添加HeaderCell(第一条)
        scroller.AddCell(ScrollerDataType.Header, 0);
        // 剩余成员每2个一组添加NormalCell
        int remainingCount = m_RemainingDefenders.Count;
        int normalCellCount = Mathf.CeilToInt((float)remainingCount / 2);
        for (int i = 0; i < normalCellCount; i++)
        {
            scroller.AddCell(ScrollerDataType.Normal, i + 1);
        }
        scroller.Restart();
    }
    /// <summary>
    /// Scroller Cell刷新回调
    /// </summary>
    private void OnRefreshCell(ScrollerDataType type, CellView cell)
    {
        if (type == ScrollerDataType.Header)
        {
            var headerCell = cell as GuildAtkDefBatBattlePanelHeaderCell;
            if (headerCell != null)
            {
                headerCell.Display(m_IsMySide, m_SortedDefenders);
            }
        }
        else if (type == ScrollerDataType.Normal)
        {
            var normalCell = cell as GuildAtkDefBatBattlePanelNormalCell;
            if (normalCell != null)
            {
                // cell.index 从1开始(0是HeaderCell)
                int normalCellIndex = cell.index - 1;
                int startIdx = normalCellIndex * 2;
                HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatMem mem1 = null;
                HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatMem mem2 = null;
                if (startIdx < m_RemainingDefenders.Count)
                {
                    mem1 = m_RemainingDefenders[startIdx];
                }
                if (startIdx + 1 < m_RemainingDefenders.Count)
                {
                    mem2 = m_RemainingDefenders[startIdx + 1];
                }
                normalCell.Display(m_IsMySide, mem1, mem2);
            }
        }
    }
    private void OnUpdateViewFamilyInfo(int familyID)
    {
        if (!GuildManager.Instance.viewGuildsDict.ContainsKey(familyID))
            return;
        if (!UIManager.Instance.IsOpened<GuildPreviewWin>())
        {
            GuildPreviewWin.SetShowOpearteBtn(false);
            UIManager.Instance.OpenWindowAsync<GuildPreviewWin>(familyID).Forget();
        }
    }
}
Main/System/GuildAtkDefBat/GuildAtkDefBatBattlePanelWin.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: da4a3786af1400a4788ee8580db57daa
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/GuildAtkDefBat/GuildAtkDefBatChallengeDifficulty.cs
New file
@@ -0,0 +1,40 @@
using System;
using UnityEngine;
public class GuildAtkDefBatChallengeDifficulty : MonoBehaviour
{
    [SerializeField] TextEx defBaseAttrPerNumText;
    [SerializeField] TextEx defStarNumText;
    [SerializeField] ButtonEx challengeBtn;
    private int difficultyIndex;
    private int defBaseAttrPer;
    private Action<int, int> onChallenge;
    private void Awake()
    {
        challengeBtn.onClick.RemoveAllListeners();
        challengeBtn.onClick.AddListener(OnClickChallenge);
    }
    public void Display(int index, int attrPer, int starNum, bool isKilled, Action<int, int> challengeCallback)
    {
        difficultyIndex = index;
        defBaseAttrPer = attrPer;
        onChallenge = challengeCallback;
        defBaseAttrPerNumText.text = Language.Get("GuildAtkDefBat03",attrPer);
        defStarNumText.text = starNum.ToString();
        challengeBtn.interactable = !isKilled;
        if (challengeBtn.image is ImageEx buttonImage)
        {
            buttonImage.gray = isKilled;
        }
    }
    private void OnClickChallenge()
    {
        onChallenge?.Invoke(difficultyIndex, defBaseAttrPer);
    }
}
Main/System/GuildAtkDefBat/GuildAtkDefBatChallengeDifficulty.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ec2e847cf6d763d4da15ef8aa956f1e4
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/GuildAtkDefBat/GuildAtkDefBatChallengeWin.cs
New file
@@ -0,0 +1,175 @@
using Cysharp.Threading.Tasks;
using UnityEngine;
public class GuildAtkDefBatChallengeWin : UIBase
{
    [SerializeField] AvatarCell avatarCell;
    [SerializeField] OfficialTitleCell officialTitle;
    [SerializeField] HorseController horseModel;
    [SerializeField] TextEx playerNameText;
    [SerializeField] TextEx fightPowerText;
    [SerializeField] GuildAtkDefBatChallengeDifficulty[] difficultys;
    [SerializeField] TextEx dWinCntText; // 防守胜利次数
    [SerializeField] ButtonEx viewDetailsButton;
    [SerializeField] ImageEx[] starGruoupImages; // 持有星数
    private static HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatMem s_TargetMem;
    private HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatMem targetMem;
    public static void Open(HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatMem mem)
    {
        s_TargetMem = mem;
        UIManager.Instance.OpenWindowAsync<GuildAtkDefBatChallengeWin>().Forget();
    }
    protected override void InitComponent()
    {
        base.InitComponent();
        viewDetailsButton.SetListener(OnClickViewDetails);
    }
    protected override void OnPreOpen()
    {
        base.OnPreOpen();
        targetMem = s_TargetMem;
        if (targetMem == null)
        {
            CloseWindow();
            return;
        }
        Display();
    }
    protected override void OnPreClose()
    {
        base.OnPreClose();
        if (ReferenceEquals(s_TargetMem, targetMem))
        {
            s_TargetMem = null;
        }
    }
    private void Display()
    {
        avatarCell.InitUI(AvatarHelper.GetAvatarModel((int)targetMem.PlayerID, (int)targetMem.Face, (int)targetMem.FacePic, targetMem.Job)).Forget();
        avatarCell.SetListener(OnClickViewDetails);
        officialTitle.InitUI(targetMem.RealmLV, (int)targetMem.TitleID).Forget();
        horseModel.Create(
            HorseManager.Instance.GetOtherPlayerHorseSkinID((int)targetMem.EquipShowSwitch),
            ResolveHeroSkinID((int)targetMem.ModelMark, targetMem.Job),
            0.8f).Forget();
        playerNameText.text = UIHelper.ServerStringTrim(targetMem.Name);
        fightPowerText.text = UIHelper.ReplaceLargeArtNum(GuildAtkDefBatManager.CalcTotalFightPower(targetMem.FightPower, targetMem.FightPowerEx));
        dWinCntText.text = Language.Get("GuildAtkDefBat42", targetMem.DWinCnt);
        DisplayStars();
        DisplayDifficulties();
    }
    private void DisplayStars()
    {
        int[] starList = FamilyAtkDefBatDefenderConfig.GetDefStarList(targetMem.DefType);
        if (starList == null || starList.Length == 0) return;
        // 统计总星数
        int totalStars = 0;
        for (int i = 0; i < starList.Length; i++)
            totalStars += starList[i];
        // 统计被击杀的总星数(从右往左扣)
        int darkStarCount = 0;
        for (int difficultyIndex = 0; difficultyIndex < starList.Length; difficultyIndex++)
        {
            if (GuildAtkDefBatManager.Instance.IsPropertyKilled(targetMem.KilledState, difficultyIndex))
                darkStarCount += starList[difficultyIndex];
        }
        int litStarCount = totalStars - darkStarCount;
        int displayCount = Mathf.Min(totalStars, starGruoupImages.Length);
        for (int starPos = 0; starPos < displayCount; starPos++)
        {
            string icon = starPos < litStarCount ? "GuildAtkDefBatStarLight" : "GuildAtkDefBatStarDark";
            starGruoupImages[starPos].SetActive(true);
            starGruoupImages[starPos].SetSprite(icon);
        }
        for (int i = displayCount; i < starGruoupImages.Length; i++)
        {
            starGruoupImages[i].SetActive(false);
        }
    }
    private void DisplayDifficulties()
    {
        var config = FamilyAtkDefBatDefenderConfig.Get(targetMem.DefType);
        for (int i = 0; i < difficultys.Length; i++)
        {
            bool isKilled = GuildAtkDefBatManager.Instance.IsPropertyKilled(targetMem.KilledState, i);
            difficultys[i].Display(i, config.DefBaseAttrPerList[i], config.DefStarList[i], isKilled, OnClickChallenge);
        }
    }
    private void OnClickViewDetails()
    {
        AvatarHelper.TryViewOtherPlayerInfo((int)targetMem.PlayerID, (int)targetMem.ServerID, viewPlayerLineupType: (int)BattlePreSetType.GuildAtkDefBat);
    }
    private void OnClickChallenge(int difficultyIndex, int defBaseAttrPer)
    {
        var manager = GuildAtkDefBatManager.Instance;
        int displayState = manager.GetGuildBattleDisplayState();
        if (displayState != 3)
        {
            ScrollTip.ShowTip("Guild attack-defense battle is not active");
            return;
        }
        if (!manager.HasMyJoinEligibility())
        {
            SysNotifyMgr.Instance.ShowTip("GuildAtkDefBat1");
            return;
        }
        if (manager.GetMyRemainingChallengeCount() <= 0)
        {
            ScrollTip.ShowTip("Challenge is unavailable");
            return;
        }
        if (manager.IsPropertyKilled(targetMem.KilledState, difficultyIndex))
        {
            ScrollTip.ShowTip("Challenge is unavailable");
            DisplayDifficulties();
            return;
        }
        GuildAtkDefBatManager.Instance.CurrentChallengeTargetPlayerID = targetMem.PlayerID;
        BattleManager.Instance.SendTurnFight(
            (uint)GuildAtkDefBatManager.DataMapID,
            0,
            1,
            targetMem.PlayerID,
            new[] { (uint)defBaseAttrPer, 0u });
        CloseWindow();
    }
    private int ResolveHeroSkinID(int modelMark, int job)
    {
        if (modelMark == 0)
        {
            PhantasmPavilionManager.Instance.TryGetDefaultID(PhantasmPavilionType.Model, job, out modelMark);
        }
        if (!ModelConfig.HasKey(modelMark))
        {
            return 0;
        }
        return ModelConfig.Get(modelMark).SkinID;
    }
}
Main/System/GuildAtkDefBat/GuildAtkDefBatChallengeWin.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a0bb5e815ba8bbf4b864e20c8c0e711e
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/GuildAtkDefBat/GuildAtkDefBatChangeMemberCell.cs
New file
@@ -0,0 +1,65 @@
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;
public class GuildAtkDefBatChangeMemberCell : CellView
{
    [SerializeField] AvatarCell avatarCell;
    [SerializeField] OfficialTitleCell officialTitle;
    [SerializeField] TextEx playerNameText;
    [SerializeField] TextEx fightPowerText;
    [SerializeField] ImageEx jobImage;
    [SerializeField] TextEx jobText;
    [SerializeField] ButtonEx changeButton;
    private HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatMem m_MemberData;
    public void Display(HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatMem mem,
        System.Action<HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatMem> onChangeClick)
    {
        if (mem == null)
        {
            gameObject.SetActive(false);
            return;
        }
        gameObject.SetActive(true);
        m_MemberData = mem;
        // 头像
        avatarCell.InitUI(AvatarHelper.GetAvatarModel((int)mem.PlayerID, (int)mem.Face, (int)mem.FacePic, mem.Job)).Forget();
        avatarCell.SetListener(() =>
        {
            AvatarHelper.TryViewOtherPlayerInfo((int)mem.PlayerID, (int)mem.ServerID, viewPlayerLineupType: (int)BattlePreSetType.GuildAtkDefBat);
        });
        // 官职称号
        officialTitle.InitUI(mem.RealmLV, (int)mem.TitleID).Forget();
        // 名字
        playerNameText.text = UIHelper.ServerStringTrim(mem.Name);
        // 战力
        long totalFightPower = GuildAtkDefBatManager.CalcTotalFightPower(mem.FightPower, mem.FightPowerEx);
        fightPowerText.text = UIHelper.ReplaceLargeArtNum(totalFightPower);
        // 防守位置:主将(1)、副将(2)、镇卫(3)、空(0)
        jobImage.SetActive(mem.DefType > 0);
        if (mem.DefType > 0)
        {
            jobImage.SetSprite($"GuildAtkDefBatPosNum{mem.DefType}");
            jobText.text = Language.Get($"GuildAtkDefBatPosNum{mem.DefType}");
        }
        else
        {
            jobText.text = "";
        }
        // 更换按钮
        changeButton.SetListener(() =>
        {
            onChangeClick?.Invoke(mem);
        });
    }
}
Main/System/GuildAtkDefBat/GuildAtkDefBatChangeMemberCell.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7ee143b6d64f700439937e2b762dd0c6
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/GuildAtkDefBat/GuildAtkDefBatChangeMemberWin.cs
New file
@@ -0,0 +1,133 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;
public class GuildAtkDefBatChangeMemberWin : UIBase
{
    [SerializeField] ScrollerController scroller;
    private static HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatMem s_SourceMember;
    private HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatMem m_SourceMember;
    private List<HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatMem> m_FilteredMembers;
    public static void Open(HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatMem sourceMember)
    {
        s_SourceMember = sourceMember;
        UIManager.Instance.OpenWindowAsync<GuildAtkDefBatChangeMemberWin>().Forget();
    }
    protected override void InitComponent()
    {
        base.InitComponent();
    }
    protected override void OnPreOpen()
    {
        base.OnPreOpen();
        scroller.OnRefreshCell += OnRefreshCell;
        m_SourceMember = s_SourceMember;
        if (m_SourceMember == null)
        {
            CloseWindow();
            return;
        }
        // 1. 获取所有己方参战成员
        var allMembers = GuildAtkDefBatManager.Instance.MyFamilyMembers.Values.ToList();
        // 2. 排除被替换的成员
        m_FilteredMembers = allMembers.Where(m => m.PlayerID != m_SourceMember.PlayerID).ToList();
        // 3. 排序:防守成员靠前(DefType 升序 -> PosNum 升序 -> PlayerID 升序),非防守成员靠后(PlayerID 升序)
        m_FilteredMembers.Sort((a, b) =>
        {
            bool aIsDef = a.DefType != 0;
            bool bIsDef = b.DefType != 0;
            if (aIsDef != bIsDef)
                return aIsDef ? -1 : 1;
            if (aIsDef && bIsDef)
            {
                int defTypeCompare = a.DefType.CompareTo(b.DefType);
                if (defTypeCompare != 0)
                    return defTypeCompare;
                int posCompare = a.PosNum.CompareTo(b.PosNum);
                if (posCompare != 0)
                    return posCompare;
            }
            return a.PlayerID.CompareTo(b.PlayerID);
        });
        // 4. 填充 Scroller
        RefreshScroller();
    }
    private void RefreshScroller()
    {
        scroller.Refresh();
        for (int i = 0; i < m_FilteredMembers.Count; i++)
        {
            scroller.AddCell(ScrollerDataType.Header, i);
        }
        scroller.Restart();
    }
    private void OnRefreshCell(ScrollerDataType type, CellView cell)
    {
        var changeCell = cell as GuildAtkDefBatChangeMemberCell;
        if (changeCell == null) return;
        int index = cell.index;
        if (index < 0 || index >= m_FilteredMembers.Count) return;
        var mem = m_FilteredMembers[index];
        changeCell.Display(mem, OnChangeButtonClick);
    }
    private void OnChangeButtonClick(HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatMem selectedMem)
    {
        bool isDefender = selectedMem.DefType != 0;
        if (isDefender)
        {
            // 选中的是防守成员:弹出互换确认框
            string msg = string.Format(
                Language.Get("GuildAtkDefBat45"),
                m_SourceMember.Name,
                selectedMem.Name);
            ConfirmCancel.ShowPopConfirm(Language.Get("Mail101"), msg, (isOk) =>
            {
                if (isOk)
                {
                    DoChangeMember(selectedMem);
                }
            });
        }
        else
        {
            // 选中的不是防守成员:直接执行更换
            DoChangeMember(selectedMem);
        }
    }
    private void DoChangeMember(HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatMem selectedMem)
    {
        GuildAtkDefBatManager.Instance.SendChangeBatMember(m_SourceMember.PlayerID, selectedMem.PlayerID);
        SysNotifyMgr.Instance.ShowTip("GuildAtkDefBat3");
        CloseWindow();
    }
    protected override void OnPreClose()
    {
        base.OnPreClose();
        scroller.OnRefreshCell -= OnRefreshCell;
        if (ReferenceEquals(s_SourceMember, m_SourceMember))
        {
            s_SourceMember = null;
        }
    }
}
Main/System/GuildAtkDefBat/GuildAtkDefBatChangeMemberWin.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: ce196f2b00521d24da2be791f997f164
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/GuildAtkDefBat/GuildAtkDefBatFailWin.cs
New file
@@ -0,0 +1,122 @@
using LitJson;
using UnityEngine;
using UnityEngine.UI;
using Cysharp.Threading.Tasks;
using System.Collections.Generic;
public class GuildAtkDefBatFailWin : UIBase
{
    [SerializeField] AvatarCell myAvatar;
    [SerializeField] AvatarCell enemyAvatar;
    [SerializeField] TextEx myNameText;
    [SerializeField] TextEx enemyNameText;
    [SerializeField] TextEx txtFuncName;
    [SerializeField] ScrollerController scroller;
    [SerializeField] ButtonEx detailBtn;
    JsonData jsonData;
    string battleName = BattleConst.GuildAtkDefBatField;
    protected override void InitComponent()
    {
        detailBtn.SetListener(() =>
        {
            BattleSettlementManager.Instance.OpenBattleDetailWin(battleName);
        });
    }
    protected override void OnPreOpen()
    {
        scroller.OnRefreshCell += OnRefreshCell;
        jsonData = BattleSettlementManager.Instance.GetBattleSettlement(battleName);
        if (jsonData == null)
        {
            DelayCloseWindow().Forget();
            return;
        }
        Display();
        CreateScroller();
    }
    protected override void OnPreClose()
    {
        scroller.OnRefreshCell -= OnRefreshCell;
        BattleSettlementManager.Instance.WinShowOver(battleName);
    }
    // B4 20 回合制战斗状态 #tagMCTurnFightState  通用的结算状态 State 4-结算奖励
    // Msg 中额外信息
    // "tagID":对战的目标ID,
    // "addStar":增加的星数,
    // itemInfo:奖励物品列表,可能为空
    void Display()
    {
        if (!jsonData.ContainsKey("tagID"))
            return;
        uint tagID = (uint)(int)jsonData["tagID"];
        // 我方数据
        uint myPlayerID = PlayerDatas.Instance.PlayerId;
        GuildAtkDefBatManager.Instance.MyFamilyMembers.TryGetValue(myPlayerID, out var myMem);
        // 敌方数据
        GuildAtkDefBatManager.Instance.EnemyFamilyMembers.TryGetValue(tagID, out var enemyMem);
        int myFace = myMem != null ? (int)myMem.Face : PlayerDatas.Instance.baseData.face;
        int myFacePic = myMem != null ? (int)myMem.FacePic : PlayerDatas.Instance.baseData.facePic;
        myAvatar.InitUI(AvatarHelper.GetAvatarModel((int)myPlayerID, myFace, myFacePic)).Forget();
        int enemyFace = enemyMem != null ? (int)enemyMem.Face : 0;
        int enemyFacePic = enemyMem != null ? (int)enemyMem.FacePic : 0;
        enemyAvatar.InitUI(AvatarHelper.GetAvatarModel((int)tagID, enemyFace, enemyFacePic)).Forget();
        enemyAvatar.SetListener(() =>
        {
            int serverID = enemyMem != null ? (int)enemyMem.ServerID : 0;
            AvatarHelper.TryViewOtherPlayerInfo((int)tagID, serverID, viewPlayerLineupType: (int)BattlePreSetType.GuildAtkDefBat);
        });
        myNameText.text = myMem != null ? UIHelper.ServerStringTrim(myMem.Name) : PlayerDatas.Instance.baseData.PlayerName;
        enemyNameText.text = enemyMem != null ? UIHelper.ServerStringTrim(enemyMem.Name) : "";
        int funcId = (int)FuncOpenEnum.GuildAtkDefBat;
        txtFuncName.text = FuncOpenLVConfig.HasKey(funcId) ? FuncOpenLVConfig.Get(funcId).Name : string.Empty;
    }
    List<Item> showItems = new List<Item>();
    void CreateScroller()
    {
        showItems.Clear();
        scroller.Refresh();
        if (!jsonData.ContainsKey("itemInfo"))
        {
            return;
        }
        var resultStr = jsonData["itemInfo"];
        for (int i = 0; i < resultStr.Count; i++)
        {
            showItems.Add(new Item((int)resultStr[i]["ItemID"], (long)resultStr[i]["Count"]));
        }
        showItems.Sort(SortItem);
        for (int i = 0; i < showItems.Count; i++)
        {
            scroller.AddCell(ScrollerDataType.Header, i);
        }
        scroller.Restart();
    }
    int SortItem(Item itemA, Item itemB)
    {
        var itemConfigA = ItemConfig.Get(itemA.id);
        var itemConfigB = ItemConfig.Get(itemB.id);
        return itemConfigB.ItemColor - itemConfigA.ItemColor;
    }
    void OnRefreshCell(ScrollerDataType type, CellView cell)
    {
        var _cell = cell as SettlementAwardCell;
        var item = showItems[cell.index];
        _cell?.Display(item.id, item.countEx);
    }
}
Main/System/GuildAtkDefBat/GuildAtkDefBatFailWin.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5b329c1284f26ee419c6b48891508eb0
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/GuildAtkDefBat/GuildAtkDefBatManager.cs
New file
@@ -0,0 +1,1012 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Cysharp.Threading.Tasks;
using LitJson;
using UnityEngine;
//公会攻防战
public class GuildAtkDefBatManager : GameSystemManager<GuildAtkDefBatManager>
{
    /// <summary> 攻防战的功能MapID </summary>
    public const int DataMapID = 33000;
    /// <summary>周期配置 [[休战期星期x, ...], [备战期星期x, ...], [激战期星期x, ...]]</summary>
    public int[][] cycleConfig;
    /// <summary>每人可挑战次数,胜负都算次数</summary>
    public int challengeCount;
    /// <summary>备战期开始时参与条件:公会成员达x人</summary>
    public int prepareMemberLimit;
    /// <summary>备战开始前x天</summary>
    public int prepareDayBefore;
    /// <summary>累计活跃达Y</summary>
    public int prepareActiveLimit;
    /// <summary>按历史总战力倒序排序后每X个随机匹配</summary>
    public int matchGroupSize;
    /// <summary>可调整成员位置的权限所需成员职位:2-副盟,3-盟主</summary>
    public int adjustPositionJobLevel;
    /// <summary>挑战胜利奖励 [[物品ID, 个数], ...]</summary>
    public int[][] winAward;
    /// <summary>挑战失败奖励 [[物品ID, 个数], ...],也是扫荡奖励</summary>
    public int[][] loseAward;
    /// <summary>公会胜利奖励 [[物品ID, 个数], ...]</summary>
    public int[][] guildWinAward;
    /// <summary>公会失败奖励 [[物品ID, 个数], ...]</summary>
    public int[][] guildLoseAward;
    /// <summary>品质格子最大产出数 {品质:最大产出数},如 {2:9, 3:1},1-普通;2-稀有;3-珍稀,普通品质不限制</summary>
    public Dictionary<int, int> qualityMaxOutput;
    /// <summary>默认最大格子数,当参与成员小于等于该配置时生效</summary>
    public int defaultMaxGrid;
    /// <summary>动态扩容格子数,当参与成员大于默认最大格子数时,每次动态扩总格子数。最大总格子=默认最大格子数+动态扩容格子数*n,n=向上取整((参与成员-默认最大格子数)/扩容格子数)</summary>
    public int expandGridCount;
    public int shopType;
    /// <summary>日志最多展示条数</summary>
    public int maxLogCount;
    public override void Init()
    {
        DTC0102_tagCDBPlayer.beforePlayerDataInitializeEventOnRelogin += OnBeforePlayerDataInitializeEventOnRelogin;
        StoreModel.Instance.RefreshBuyShopLimitEvent += OnRefreshShopLimit;
        ParseConfig();
    }
    public override void Release()
    {
        DTC0102_tagCDBPlayer.beforePlayerDataInitializeEventOnRelogin -= OnBeforePlayerDataInitializeEventOnRelogin;
        StoreModel.Instance.RefreshBuyShopLimitEvent -= OnRefreshShopLimit;
    }
    private void OnRefreshShopLimit()
    {
        UpdateRedpoint();
    }
    private void OnBeforePlayerDataInitializeEventOnRelogin()
    {
        ResetBattleData();
    }
    void ParseConfig()
    {
        var configSet = FuncConfigConfig.Get("FmAtkDefBatSet");
        cycleConfig = JsonMapper.ToObject<int[][]>(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<int[][]>(configAward.Numerical1);
        loseAward = JsonMapper.ToObject<int[][]>(configAward.Numerical2);
        guildWinAward = JsonMapper.ToObject<int[][]>(configAward.Numerical3);
        guildLoseAward = JsonMapper.ToObject<int[][]>(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);
    }
    /// <summary> 当前所在时期信息通知:0-无;1-休战期;2-备战期;3-激战期 </summary>
    public int BatState { get; private set; }
    /// <summary> 己方公会基础信息 </summary>
    public HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatFamily MyFamilyInfo { get; private set; }
    /// <summary> 敌方公会基础信息 (若无参战资格或未匹配到则为null) </summary>
    public HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatFamily EnemyFamilyInfo { get; private set; }
    /// <summary> 己方参与成员字典,Key为PlayerID,方便差异更新与删除查找 </summary>
    public Dictionary<uint, HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatMem> MyFamilyMembers = new Dictionary<uint, HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatMem>();
    /// <summary> 当前挑战的防守方成员ID(用于战场界面显示敌方信息) </summary>
    public uint CurrentChallengeTargetPlayerID { get; set; }
    /// <summary> 敌方参与成员字典,Key为PlayerID,方便差异更新与删除查找 </summary>
    public Dictionary<uint, HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatMem> EnemyFamilyMembers = new Dictionary<uint, HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatMem>();
    public int MyFamilyID => PlayerDatas.Instance?.fairyData?.fairy?.FamilyID ?? 0;
    public event Action OnUpdateAtkDefBatInfoEvent;
    /// <summary>
    /// 接收 A525 封包,处理全量/增量/删除
    /// </summary>
    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();
    }
    /// <summary>
    /// 重置新一轮攻防战数据缓存
    /// </summary>
    private void ResetBattleData()
    {
        MyFamilyInfo = null;
        EnemyFamilyInfo = null;
        MyFamilyMembers.Clear();
        EnemyFamilyMembers.Clear();
        atkDefBatLogs.Clear();
        treasureGridRecords.Clear();
        hasShowedMainRedpoint = false;
    }
    /// <summary>
    /// 更新公会基础信息(剥离掉成员数组单独管理)
    /// </summary>
    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.IsDismiss = sourceData.IsDismiss;
        targetInfo.TotalStar = sourceData.TotalStar;
        targetInfo.OverState = sourceData.OverState;
        targetInfo.BatMemCount = sourceData.BatMemCount;
        return targetInfo;
    }
    /// <summary>
    /// 对参战成员进行差异替换更新
    /// </summary>
    private void UpdateMembersDiff(Dictionary<uint, HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatMem> 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;
        }
    }
    /// <summary>
    /// 前端自行排序:获取防守成员列表(按防守类型从小到大 -> 站位编号从小到大排序)
    /// </summary>
    public List<HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatMem> GetSortedDefenders(bool isMyFamily)
    {
        var dict = isMyFamily ? MyFamilyMembers : EnemyFamilyMembers;
        var list = new List<HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatMem>();
        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;
    }
    /// <summary>
    /// 计算成员总战力(long类型)
    /// 服务端将战力拆分为 FightPower(余亿部分)和 FightPowerEx(整除亿部分)两个uint字段
    /// </summary>
    public static long CalcTotalFightPower(uint fightPower, uint fightPowerEx)
    {
        return fightPower + fightPowerEx * Constants.ExpPointValue;
    }
    /// <summary>
    /// 获取用于核心成员展示的排序列表(最多4个)
    /// </summary>
    /// <param name="isMyFamily">是否己方公会</param>
    /// <param name="displayState">当前显示状态: 2-备战期, 3-激战期, 4/5-休战期</param>
    /// <returns>排序后的成员列表(最多4个)</returns>
    public List<HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatMem> GetSortedCoreMembers(bool isMyFamily, int displayState)
    {
        var dict = isMyFamily ? MyFamilyMembers : EnemyFamilyMembers;
        var list = new List<HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatMem>(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;
    }
    /// <summary>
    /// 判断是否是系统公会 (按照文档:ID >= 10亿)
    /// </summary>
    public bool IsSystemFamily(uint familyID)
    {
        return familyID >= 1000000000;
    }
    /// <summary>
    /// 解析 KilledState,判定防守人员的某个属性百分比是否已经被击败
    /// </summary>
    /// <param name="propertyIndex">属性索引位(从0开始计)</param>
    public bool IsPropertyKilled(byte killedState, int propertyIndex)
    {
        return (killedState & (1 << propertyIndex)) != 0;
    }
    /// <summary>发送更换参战成员请求</summary>
    /// <param name="playerIDA">要被替换的成员ID</param>
    /// <param name="playerIDB">替换A成员的成员ID</param>
    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);
    }
    /// <summary>
    /// 获取攻防战的当前显示状态标识
    /// </summary>
    /// <returns>1-未参战, 2-备战中, 3-激战中, 4-胜利(休战中), 5-失败(休战中)</returns>
    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 || overState == 3) return 4;
            if (overState == 2 || overState == 4) return 5;
        }
        return 1;
    }
    /// <summary>
    /// 获取当前玩家剩余进攻次数
    /// </summary>
    public int GetMyRemainingChallengeCount()
    {
        uint myPlayerId = PlayerDatas.Instance.PlayerId;
        if (MyFamilyMembers != null && MyFamilyMembers.TryGetValue(myPlayerId, out var myData))
        {
            return challengeCount - myData.BatCnt;
        }
        return 0;
    }
    /// <summary>
    /// 获取己方公会排序后的参战成员列表
    /// 排序规则:Star降序 → BatCnt升序 → PlayerID升序
    /// </summary>
    public List<HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatMem> GetSortedFamilyMembers()
    {
        var members = MyFamilyMembers;
        if (members == null || members.Count <= 0)
            return new List<HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatMem>();
        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;
        mainRedpoint.state = RedPointState.None;
    }
    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 跨服与周期推算相关
    /// <summary>
    /// 判断当前攻防战是否处于跨服状态
    /// </summary>
    public bool IsCrossServer()
    {
        return CrossServerBaseManager.Instance.IsOpenCrossServer(DataMapID);
    }
    /// <summary>
    /// 获取当前公会所处的 ZoneID (用于判断取本服还是跨服时间及其他跨服数据)
    /// </summary>
    /// <returns>返回0代表本服,大于0代表跨服分区ID</returns>
    public int GetZoneID()
    {
        var crossInfo = CrossServerBaseManager.Instance.GetCrossZoneInfo(DataMapID);
        return (int)(crossInfo?.ZoneID ?? 0);
    }
    /// <summary>
    /// 根据本地时间和 cycleConfig 配置推算当前处于哪个阶段
    /// 返回对应的 BatState 状态值:0-无, 1-休战期, 2-备战期, 3-激战期
    /// </summary>
    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);
    }
    /// <summary>
    /// 获取【当前阶段结束】的倒计时(自动计算到下一个不同阶段的0点)
    /// </summary>
    public int GetCountdownToCurrentPeriodEnd()
    {
        int currentState = GetCurrentPeriodByConfig();
        if (currentState == 0) return 0;
        // 下一个阶段: 1→2, 2→3, 3→1
        return GetCountdownToNextPeriod(currentState % 3 + 1);
    }
    /// <summary>
    /// 传入目标时期(BatState),获取距离该周期开始(当天0点)的倒计时秒数
    /// </summary>
    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 日志
    /// <summary>公会攻防战进攻日志 ActionType</summary>
    public const int GuildAtkDefBatLogActionType = 22;
    /// <summary>缓存解析后的周x→累计贡献字典 (key: 1-7, value: 累计贡献)</summary>
    private Dictionary<int, long> activityData = new Dictionary<int, long>();
    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 累计活跃度
    /// <summary>公会攻防战家族行为 ActionType(累计活跃)</summary>
    public const int GuildAtkDefBatActionType = 19;
    /// <summary> 攻防战日志列表(按时间倒序) </summary>
    public List<GuildAtkDefBatLogEntry> atkDefBatLogs = new List<GuildAtkDefBatLogEntry>();
    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;
    }
    /// <summary>
    /// 解析 UseData JSON,提取周x→累计贡献的映射
    /// UseData 格式: {"1":2000,"2":3000,"4":1000}
    /// key 为周几(1-7, 1=周一), value 为当日累计贡献
    /// </summary>
    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}");
        }
    }
    /// <summary>
    /// 获取当前我的功能累计活跃度
    /// 统计相对当天时间前 prepareDayBefore 天的累计活跃贡献之和
    /// </summary>
    public long GetMyActivityLevel()
    {
        int currentWeekday = TimeUtility.GetCommonWeekday(GetZoneID());
        long total = 0;
        for (int i = 1; i <= prepareDayBefore; i++)
        {
            // (currentWeekday - i) 转成 1-7 范围
            int targetWeekday = ((currentWeekday - i - 1 + 7) % 7) + 1;
            if (activityData.TryGetValue(targetWeekday, out long contribution))
                total += contribution;
        }
        return total;
    }
    #endregion
    #region 宝库
    /// <summary>宝库奖励类型ID (对应CA504的RewardType)</summary>
    public const byte TREASURE_REWARD_TYPE = 11;
    /// <summary>缓存格子开启记录: Key=格子编号(1~n), Value=AwardID</summary>
    public Dictionary<int, int> treasureGridRecords = new Dictionary<int, int>();
    /// <summary>标记宝库窗口是否打开,用于收到A525时自动刷新</summary>
    public bool isTreasureWinOpen = false;
    /// <summary>等待播放开启动画的格子编号(A504发包时记录)</summary>
    public int pendingOpenGridIndex = 0;
    /// <summary>宝库数据刷新事件(供窗口监听)</summary>
    public event Action OnTreasureDataUpdateEvent;
    /// <summary>宝库格子开启动画事件(参数: 格子编号, AwardID)</summary>
    public event Action<int, int> OnTreasureGridOpenEvent;
    /// <summary>
    /// 从A525成员数据中解析宝库格子记录
    /// </summary>
    public void ParseTreasureGridRecords()
    {
        treasureGridRecords.Clear();
        foreach (var mem in MyFamilyMembers.Values)
        {
            if (mem.GridNum > 0)
            {
                treasureGridRecords[mem.GridNum] = mem.AwardID;
            }
        }
        if (isTreasureWinOpen)
        {
            int openGrid = pendingOpenGridIndex;
            if (openGrid > 0 && treasureGridRecords.TryGetValue(openGrid, out var awardID))
            {
                pendingOpenGridIndex = 0;
                OnTreasureGridOpenEvent?.Invoke(openGrid, awardID);
            }
            OnTreasureDataUpdateEvent?.Invoke();
        }
    }
    /// <summary>
    /// 获取我的剩余开启次数
    /// 仅在休战期(BatState==1)可开启
    /// 对手公会未解散: 有参与战斗(BatCnt>0)的玩家可开启1次
    /// 对手公会已解散: 所有参赛成员均可开启1次
    /// 每人最多1次
    /// </summary>
    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.GridNum > 0)
            return 0;
        // 对手公会已解散: 所有参赛成员均可开启
        if (EnemyFamilyInfo != null && EnemyFamilyInfo.IsDismiss == 1)
            return 1;
        // 对手公会未解散: 参战成员均可开启1次(已开启的被 GridNum 过滤)
        return 1;
    }
    /// <summary>
    /// 获取我的最大可开启次数(用于显示 开启次数:{0}/{1})
    /// </summary>
    public int GetMyMaxOpenCount()
    {
        return 1;
    }
    /// <summary>
    /// 发送宝库开启请求 A504
    /// </summary>
    public void SendOpenTreasure(byte gridNum)
    {
        pendingOpenGridIndex = gridNum;
        var pack = new CA504_tagCMPlayerGetReward();
        pack.RewardType = TREASURE_REWARD_TYPE;
        pack.DataEx = gridNum;
        GameNetSystem.Instance.SendInfo(pack);
    }
    /// <summary>
    /// 获取宝库的库类型(1-胜者库, 2-败者库)
    /// </summary>
    public int GetTreasureLibType()
    {
        int displayState = GetGuildBattleDisplayState();
        return displayState == 4 ? 1 : 2;
    }
    #region 宝库奖励延迟展示
    /// <summary>缓存待展示的宝库奖励物品列表</summary>
    private List<Item> pendingTreasureItems = new List<Item>();
    /// <summary>缓存待展示的宝库奖励信息文本</summary>
    private string pendingTreasureInfo = string.Empty;
    /// <summary>
    /// 缓存宝库奖励数据(由 BoxGetItemModel.ReceiveAwardNotify 调用)
    /// 延迟到宝库开启动画结束后再展示
    /// </summary>
    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");
    }
    /// <summary>
    /// 展示缓存的宝库奖励(由格子开启动画 onComplete 回调调用)
    /// </summary>
    public void ShowTreasureAward()
    {
        if (pendingTreasureItems.Count == 0)
            return;
        ItemLogicUtility.Instance.ShowGetItem(pendingTreasureItems, "AtkDefBatTreasure");
        pendingTreasureItems.Clear();
        pendingTreasureInfo = string.Empty;
    }
    #endregion
    #endregion
    #region 扫荡
    /// <summary> 是否为扫荡的胜利结算 </summary>
    public bool isSweepVictory = false;
    /// <summary> 扫荡奖励物品列表 </summary>
    public List<Item> sweepItemInfos = new List<Item>();
    /// <summary> 扫荡目标玩家ID </summary>
    public uint sweepTagID = 0;
    /// <summary>接收 0320 副本结束通知(扫荡结果)</summary>
    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.OpenWindowAsync<GuildAtkDefBatVictoryWin>().Forget();
    }
    #endregion
    #region 奖励预览
    /// <summary>实时计算最大总格子数</summary>
    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;
    }
    /// <summary>计算非普通品质的总格子产出上限</summary>
    public int GetNonNormalQualityTotalOutput()
    {
        int total = 0;
        if (qualityMaxOutput != null)
        {
            foreach (var kv in qualityMaxOutput)
            {
                if (kv.Key != 1)
                    total += kv.Value;
            }
        }
        return total;
    }
    #endregion
}
Main/System/GuildAtkDefBat/GuildAtkDefBatManager.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: fc9ee656e73ff204ea07969bc8d41dfb
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/GuildAtkDefBat/GuildAtkDefBatPlayerShowCell.cs
New file
@@ -0,0 +1,28 @@
using Cysharp.Threading.Tasks;
using UnityEngine;
public class GuildAtkDefBatPlayerShowCell : MonoBehaviour
{
    [SerializeField] HorseController model;
    [SerializeField] RectTransform deathRect;
    [SerializeField] ButtonEx clickButton;
    public void Display(int heroSkinID, int horseSkinID, int playerID, int serverID, bool isDeath)
    {
        model.SetActive(!isDeath);
        deathRect.SetActive(isDeath);
        if (!isDeath)
            model.Create(horseSkinID, heroSkinID, 0.8f).Forget();
        clickButton.SetListener(() =>
        {
            AvatarHelper.TryViewOtherPlayerInfo(playerID, serverID, viewPlayerLineupType: (int)BattlePreSetType.GuildAtkDefBat);
        });
    }
    public void SetActive(bool active)
    {
        gameObject.SetActive(active);
    }
}
Main/System/GuildAtkDefBat/GuildAtkDefBatPlayerShowCell.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 160c84b84ed00cb408a95df00b0a382b
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/GuildAtkDefBat/GuildAtkDefBatPlayerTop3Cell.cs
New file
@@ -0,0 +1,60 @@
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;
public class GuildAtkDefBatPlayerTop3Cell : MonoBehaviour
{
    [SerializeField] Text rankValueText;    //战斗次数
    [SerializeField] Text starCnt;          //星数
    [SerializeField] Text nameText;
    [SerializeField] OfficialTitleCell officialTitleCell;
    [SerializeField] Button queryPlayerBtn; //点击查看玩家详情
    [SerializeField] HorseController model;
    public void Display(HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatMem data, int rank)
    {
        if (data == null)
        {
            rankValueText.text = "0";
            nameText.text = Language.Get("L1124");
            officialTitleCell.SetActive(false);
            model.SetActive(false);
            return;
        }
        officialTitleCell.SetActive(true);
        officialTitleCell.InitUI((int)data.RealmLV, (int)data.TitleID).Forget();
        starCnt.text = data.Star.ToString(); // 星数直接显示数字
        rankValueText.text = Language.Get("GuildAtkDefBat36", data.BatCnt); // 战斗次数
        nameText.text = data.Name;
        // 角色模型与坐骑
        model.SetActive(true);
        int heroSkinID = ResolveHeroSkinID((int)data.ModelMark, (int)data.Job);
        int horseSkinID = HorseManager.Instance.GetOtherPlayerHorseSkinID((int)data.EquipShowSwitch);
        model.Create(horseSkinID, heroSkinID, rank == 1 ? 1f : 0.8f).Forget();
        queryPlayerBtn.SetListener(() =>
        {
            AvatarHelper.TryViewOtherPlayerInfo((int)data.PlayerID, (int)data.ServerID, viewPlayerLineupType: (int)BattlePreSetType.GuildAtkDefBat);
        });
    }
    /// <summary>
    /// 根据 ModelMark 和 Job 解析英雄皮肤ID
    /// </summary>
    int ResolveHeroSkinID(int modelMark, int job)
    {
        if (modelMark == 0)
        {
            PhantasmPavilionManager.Instance.TryGetDefaultID(PhantasmPavilionType.Model, job, out modelMark);
        }
        if (!ModelConfig.HasKey(modelMark))
            return 0;
        var modelConfig = ModelConfig.Get(modelMark);
        return modelConfig.SkinID;
    }
}
Main/System/GuildAtkDefBat/GuildAtkDefBatPlayerTop3Cell.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a7fe552302c94f44fb59b049e2b513b9
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/GuildAtkDefBat/GuildAtkDefBatRankCell.cs
New file
@@ -0,0 +1,75 @@
//--------------------------------------------------------
//    [Author]:           玩个游戏
//    [  Date ]:           Wednesday, September 26, 2018
//--------------------------------------------------------
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;
//公会攻防战-玩家排名Cell
public class GuildAtkDefBatRankCell : MonoBehaviour
{
    [SerializeField] AvatarCell avatarCell;
    [SerializeField] Text rankText;
    [SerializeField] Text rankValueText;    //星数
    [SerializeField] Text battleCnt;        //战斗次数
    [SerializeField] Text nameText;
    [SerializeField] OfficialTitleCell officialTitleCell;
    [SerializeField] Button queryPlayerBtn; //点击查看玩家详情
    private uint curPlayerId;
    private int curServerId;
    /// <summary>
    /// 显示排名Cell
    /// </summary>
    /// <param name="data">成员数据,为null时显示空数据</param>
    /// <param name="rank">排名序号,0表示"自己"</param>
    /// <param name="isSelf">是否是自己</param>
    public void Display(HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatMem data, int rank, bool isSelf = false)
    {
        if (data == null)
        {
            // 空数据显示
            officialTitleCell.SetActive(false);
            avatarCell.SetActive(false);
            nameText.text = Language.Get("L1124");
            rankValueText.text = "0";
            battleCnt.SetActive(false);
            rankText.text = isSelf ? Language.Get("L1045") : "0";
            return;
        }
        curPlayerId = data.PlayerID;
        curServerId = (int)data.ServerID;
        nameText.text = data.Name;
        officialTitleCell.SetActive(true);
        officialTitleCell.InitUI((int)data.RealmLV, (int)data.TitleID).Forget();
        avatarCell.SetActive(true);
        avatarCell.InitUI(AvatarHelper.GetAvatarModel((int)data.PlayerID, (int)data.Face, (int)data.FacePic)).Forget();
        avatarCell.SetListener(() =>
        {
            AvatarHelper.TryViewOtherPlayerInfo((int)curPlayerId, curServerId, viewPlayerLineupType: (int)BattlePreSetType.GuildAtkDefBat);
        });
        rankValueText.text = data.Star.ToString(); // 星数直接显示数字
        battleCnt.SetActive(true);
        battleCnt.text = Language.Get("GuildAtkDefBat36", data.BatCnt); // 战斗次数
        rankText.text = rank.ToString();
        if (queryPlayerBtn != null)
        {
            queryPlayerBtn.SetListener(() =>
            {
                AvatarHelper.TryViewOtherPlayerInfo((int)curPlayerId, curServerId, viewPlayerLineupType: (int)BattlePreSetType.GuildAtkDefBat);
            });
        }
    }
}
Main/System/GuildAtkDefBat/GuildAtkDefBatRankCell.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 116cd0aac1c664742869382ce1e3c158
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/GuildAtkDefBat/GuildAtkDefBatRankWin.cs
New file
@@ -0,0 +1,135 @@
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class GuildAtkDefBatRankWin : UIBase
{
    [SerializeField] ButtonEx serverBtn;
    [SerializeField] List<GuildAtkDefBatPlayerTop3Cell> playerTop3Cells;
    [SerializeField] ScrollerController scroller;
    [SerializeField] GuildAtkDefBatRankCell myRankCell;
    [SerializeField] RectTransform noRect;
    GuildAtkDefBatManager manager { get { return GuildAtkDefBatManager.Instance; } }
    /// <summary> 排序后的参战成员列表 </summary>
    List<HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatMem> sortedMembers = new List<HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatMem>();
    protected override void InitComponent()
    {
        serverBtn.SetListener(() =>
        {
            UIHelper.ShowServersPanel(GuildManager.Instance.crossServerIDList);
        });
    }
    protected override void OnPreOpen()
    {
        base.OnPreOpen();
        scroller.OnRefreshCell += OnRefreshCell;
        manager.OnUpdateAtkDefBatInfoEvent += OnUpdateAtkDefBatInfoEvent;
        InitDisplay();
    }
    protected override void OnPreClose()
    {
        base.OnPreClose();
        manager.OnUpdateAtkDefBatInfoEvent -= OnUpdateAtkDefBatInfoEvent;
        scroller.OnRefreshCell -= OnRefreshCell;
    }
    private void OnUpdateAtkDefBatInfoEvent()
    {
        Display();
    }
    public void Display()
    {
        serverBtn.SetActive(manager.IsCrossServer());
        RefreshSortedMembers();
        ShowTop3();
        scroller.m_Scorller.RefreshActiveCellViews();
        DisplayMyRank();
        UpdateNoRect();
    }
    private void InitDisplay()
    {
        serverBtn.SetActive(manager.IsCrossServer());
        RefreshSortedMembers();
        ShowTop3();
        CreateScroller();
        DisplayMyRank();
        UpdateNoRect();
    }
    /// <summary>
    /// 从 Manager 获取排序后的参战成员列表
    /// </summary>
    void RefreshSortedMembers()
    {
        sortedMembers = manager.GetSortedFamilyMembers();
    }
    void CreateScroller()
    {
        scroller.Refresh();
        int cnt = sortedMembers.Count;
        for (int i = 3; i < cnt; i++)
        {
            scroller.AddCell(ScrollerDataType.Header, i);
        }
        scroller.Restart();
    }
    void OnRefreshCell(ScrollerDataType type, CellView cell)
    {
        int memberIndex = cell.index;
        if (memberIndex < 0 || memberIndex >= sortedMembers.Count)
            return;
        var _cell = cell.GetComponent<GuildAtkDefBatRankCell>();
        _cell.Display(sortedMembers[memberIndex], memberIndex + 1);
    }
    private void DisplayMyRank()
    {
        uint myPlayerId = PlayerDatas.Instance.PlayerId;
        int myIndex = sortedMembers.FindIndex(m => m.PlayerID == myPlayerId);
        if (myIndex >= 0)
        {
            // 找到自己的排名
            myRankCell.Display(sortedMembers[myIndex], myIndex + 1, true);
        }
        else
        {
            // 自己不在列表中(未参战等),显示默认信息
            myRankCell.Display(null, 0, true);
        }
    }
    /// <summary>
    /// 当排名列表只有前3名时,显示无更多数据提示
    /// </summary>
    void UpdateNoRect()
    {
        noRect.SetActive(sortedMembers.Count <= 3);
    }
    void ShowTop3()
    {
        int top3Count = Mathf.Min(playerTop3Cells.Count, sortedMembers.Count);
        for (int i = 0; i < playerTop3Cells.Count; i++)
        {
            if (i < top3Count)
            {
                playerTop3Cells[i].Display(sortedMembers[i], i + 1);
            }
            else
            {
                playerTop3Cells[i].Display(null, i + 1);
            }
        }
    }
}
Main/System/GuildAtkDefBat/GuildAtkDefBatRankWin.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 6bcdca743df069040a88b55b8861b927
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/GuildAtkDefBat/GuildAtkDefBatRecordCell.cs
New file
@@ -0,0 +1,161 @@
using System;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;
public class GuildAtkDefBatRecordCell : CellView
{
    [SerializeField] ImageEx titleImage;
    [SerializeField] TextEx dataText;
    [SerializeField] TextEx resultText;
    [SerializeField] TextEx difficultyText;
    [SerializeField] TextEx stateText;
    [SerializeField] TextEx myName;
    [SerializeField] ImageEx myResultImage;
    [SerializeField] AvatarCell myAvatarCell;
    [SerializeField] OfficialTitleCell myOfficialTitle;
    [SerializeField] TextEx myfightPointText;
    [SerializeField] RectTransform myAddStarRect;
    [SerializeField] TextEx myAddStaText;
    [SerializeField] TextEx enemyName;
    [SerializeField] ImageEx enemyResultImage;
    [SerializeField] AvatarCell enemyAvatarCell;
    [SerializeField] OfficialTitleCell enemyOfficialTitle;
    [SerializeField] TextEx enemyfightPointText;
    [SerializeField] RectTransform enemyAddStarRect;
    [SerializeField] TextEx enemyAddStarText;
    /// <summary>
    /// 设置日志数据(Manager 层已预解析 UseData)
    /// </summary>
    public void SetLogData(GuildAtkDefBatManager.GuildAtkDefBatLogEntry logEntry)
    {
        if (logEntry == null || logEntry.action == null) return;
        var action = logEntry.action;
        uint myFamilyID = (uint)GuildAtkDefBatManager.Instance.MyFamilyID;
        bool isMyAttackLog = logEntry.familyID == myFamilyID;
        // 1. dataText — 时间显示 "MM-dd HH:mm:ss"
        DateTime dt = TimeUtility.GetTime(action.Time);
        dataText.text = dt.ToString("MM-dd HH:mm:ss");
        // 2. 判断胜负
        bool attackerWon = action.Value4 == 1;
        bool isMySideWon = isMyAttackLog ? attackerWon : !attackerWon;
        resultText.text = Language.Get(isMySideWon ? "GuildAtkDefBat01" : "GuildAtkDefBat02");
        // 3. difficultyText — 实力百分比 GuildAtkDefBat03 {0}%实力,扫荡类型不显示
        bool isSweepType = action.Value1 == 0;
        difficultyText.SetActive(!isSweepType);
        if (!isSweepType)
            difficultyText.text = Language.Get("GuildAtkDefBat03", action.Value5);
        // 4. stateText — 战斗类型 挑战/扫荡/防守
        bool isDefense = !isMyAttackLog;
        string stateKey;
        if (isDefense)
        {
            stateKey = "GuildAtkDefBat06"; // 防守
            titleImage.SetSprite("GuildAtkDefBatRecordTitleDef");
        }
        else if (action.Value1 == 1)
        {
            stateKey = "GuildAtkDefBat04"; // 挑战
            titleImage.SetSprite("GuildAtkDefBatRecordTitleAtk");
        }
        else
        {
            stateKey = "GuildAtkDefBat05"; // 扫荡
            titleImage.SetSprite("GuildAtkDefBatRecordTitleSweep");
        }
        stateText.text = Language.Get(stateKey);
        // 5. 使用 Manager 预解析的玩家信息
        var myInfo = isMyAttackLog ? logEntry.atkPlayerInfo : logEntry.defPlayerInfo;
        var enemyInfo = isMyAttackLog ? logEntry.defPlayerInfo : logEntry.atkPlayerInfo;
        DisplayPlayerInfo(true, myInfo, action, isMyAttackLog);
        DisplayPlayerInfo(false, enemyInfo, action, isMyAttackLog);
        // 6. 胜负图标
        myResultImage.SetSprite(isMySideWon ? "GuildAtkDefBatRecordVictory" : "GuildAtkDefBatRecordFail");
        enemyResultImage.SetSprite(isMySideWon ? "GuildAtkDefBatRecordFail" : "GuildAtkDefBatRecordVictory");
        // 7. 星星显隐(扫荡不显示星数,挑战/防守显示在胜方,星数<=0不显示)
        bool hasStar = !isSweepType && action.Value6 > 0;
        myAddStarRect.SetActive(hasStar && isMySideWon);
        enemyAddStarRect.SetActive(hasStar && !isMySideWon);
        if (hasStar && isMySideWon && myInfo != null)
            myAddStaText.text = action.Value6.ToString();
        if (hasStar && !isMySideWon && enemyInfo != null)
            enemyAddStarText.text = action.Value6.ToString();
    }
    /// <summary>
    /// 显示玩家信息(头像、称号、名字、战力)
    /// </summary>
    void DisplayPlayerInfo(bool isMySide, GuildAtkDefBatManager.GuildAtkDefBatPlayerInfo playerInfo, HA513_tagMCFamilyActionInfo.tagMCFamilyAction action, bool isMyAttackLog)
    {
        if (playerInfo == null)
        {
            if (isMySide)
            {
                myOfficialTitle.SetActive(false);
                myAvatarCell.SetActive(false);
                myName.text = string.Empty;
                myfightPointText.text = string.Empty;
            }
            else
            {
                enemyOfficialTitle.SetActive(false);
                enemyAvatarCell.SetActive(false);
                enemyName.text = string.Empty;
                enemyfightPointText.text = string.Empty;
            }
            return;
        }
        // 动态计算玩家ID:
        // - 当 isMySide == isMyAttackLog 时,当前方是攻击方 -> 使用 Value2(攻击方ID)
        // - 当 isMySide != isMyAttackLog 时,当前方是防守方 -> 使用 Value3(防守方ID)
        bool useValue2 = (isMySide == isMyAttackLog);
        int playerId = (int)(useValue2 ? action.Value2 : action.Value3);
        if (isMySide)
        {
            myOfficialTitle.SetActive(true);
            myOfficialTitle.InitUI(playerInfo.fmLV, playerInfo.titleID).Forget();
            myAvatarCell.SetActive(true);
            myAvatarCell.InitUI(AvatarHelper.GetAvatarModel(playerId, playerInfo.face, playerInfo.facePic)).Forget();
            myAvatarCell.SetListener(() =>
            {
                AvatarHelper.TryViewOtherPlayerInfo(playerId, playerInfo.serverID, viewPlayerLineupType: (int)BattlePreSetType.GuildAtkDefBat);
            });
            myName.text = playerInfo.name;
            myfightPointText.text = playerInfo.fightPower;
        }
        else
        {
            enemyOfficialTitle.SetActive(true);
            enemyOfficialTitle.InitUI(playerInfo.fmLV, playerInfo.titleID).Forget();
            enemyAvatarCell.SetActive(true);
            enemyAvatarCell.InitUI(AvatarHelper.GetAvatarModel(playerId, playerInfo.face, playerInfo.facePic)).Forget();
            enemyAvatarCell.SetListener(() =>
            {
                AvatarHelper.TryViewOtherPlayerInfo(playerId, playerInfo.serverID, viewPlayerLineupType: (int)BattlePreSetType.GuildAtkDefBat);
            });
            enemyName.text = playerInfo.name;
            enemyfightPointText.text = playerInfo.fightPower;
        }
    }
}
Main/System/GuildAtkDefBat/GuildAtkDefBatRecordCell.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c7944897e6f05e144a6b37fdd6287b16
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/GuildAtkDefBat/GuildAtkDefBatRecordWin.cs
New file
@@ -0,0 +1,193 @@
using System;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;
public class GuildAtkDefBatRecordWin : UIBase
{
    [SerializeField] GuildEmblemCell myEmblem;
    [SerializeField] TextEx myGuildName;
    [SerializeField] TextEx myStatCnt;
    [SerializeField] RectTransform enemyRect;
    [SerializeField] GuildEmblemCell enemyEmblem;
    [SerializeField] TextEx enemyGuildName;
    [SerializeField] TextEx enemyStatCnt;
    [SerializeField] RectTransform noJoinRect;
    [SerializeField] TextEx recordCnt;
    [SerializeField] ScrollerController scroller;
    [SerializeField] RectTransform noRect;
    [SerializeField] ButtonEx myEmblemBtn;
    [SerializeField] ButtonEx enemyEmblemBtn;
    GuildAtkDefBatManager manager { get { return GuildAtkDefBatManager.Instance; } }
    protected override void InitComponent()
    {
        base.InitComponent();
        // 己方公会徽章点击 - 自己的公会
        myEmblemBtn.AddListener(() =>
        {
            SysNotifyMgr.Instance.ShowTip("GuildTip01");
        });
        // 敌方公会徽章点击
        enemyEmblemBtn.AddListener(() =>
        {
            var enemyInfo = manager.EnemyFamilyInfo;
            if (enemyInfo == null)
                return;
            int familyID = (int)enemyInfo.FamilyID;
            int serverID = (int)enemyInfo.ServerID;
            // 自己的公会
            if (PlayerDatas.Instance.fairyData.HasFairy && PlayerDatas.Instance.fairyData.fairy.FamilyID == familyID)
            {
                SysNotifyMgr.Instance.ShowTip("GuildTip01");
                return;
            }
            // 系统公会
            if (manager.IsSystemFamily(enemyInfo.FamilyID))
                return;
            // 查看公会信息
            GuildManager.Instance.SendViewGuild(familyID, serverID);
        });
    }
    protected override void OnPreOpen()
    {
        base.OnPreOpen();
        scroller.OnRefreshCell += OnRefreshCell;
        manager.OnUpdateAtkDefBatLogEvent += OnUpdateLogEvent;
        GuildManager.Instance.OnUpdateViewFamilyInfo += OnUpdateViewFamilyInfo;
        Display();
    }
    protected override void OnPreClose()
    {
        base.OnPreClose();
        scroller.OnRefreshCell -= OnRefreshCell;
        manager.OnUpdateAtkDefBatLogEvent -= OnUpdateLogEvent;
        GuildManager.Instance.OnUpdateViewFamilyInfo -= OnUpdateViewFamilyInfo;
    }
    private void OnUpdateViewFamilyInfo(int familyID)
    {
        if (!GuildManager.Instance.viewGuildsDict.ContainsKey(familyID))
            return;
        if (!UIManager.Instance.IsOpened<GuildPreviewWin>())
        {
            GuildPreviewWin.SetShowOpearteBtn(false);
            UIManager.Instance.OpenWindowAsync<GuildPreviewWin>(familyID).Forget();
        }
    }
    private void OnUpdateLogEvent()
    {
        DisplayLogs();
    }
    public void Display()
    {
        DisplayInfo();
        DisplayLogs();
    }
    /// <summary>
    /// 显示顶部记分板(参照 GuildAtkDefBatWin.DisplayInfo 的规则)
    /// </summary>
    void DisplayInfo()
    {
        var myFamilyInfo = manager.MyFamilyInfo;
        if (myFamilyInfo != null)
        {
            myGuildName.text = myFamilyInfo.FamilyName;
            myEmblem.Display((int)myFamilyInfo.EmblemID, myFamilyInfo.EmblemWord, 0.7f);
            myStatCnt.text = myFamilyInfo.TotalStar.ToString();
        }
        else
        {
            myStatCnt.text = "0";
        }
        var enemyFamilyInfo = manager.EnemyFamilyInfo;
        if (enemyFamilyInfo != null)
        {
            bool isSystemFamily = manager.IsSystemFamily(enemyFamilyInfo.FamilyID);
            if (isSystemFamily)
            {
                string enemyName = Language.Get("GuildAtkDefBat31");
                enemyGuildName.text = enemyName;
                enemyEmblem.Display(FamilyEmblemConfig.GetFirstDefaultEmblem(), !string.IsNullOrEmpty(enemyName) ? enemyName[0].ToString() : string.Empty, 0.7f);
                enemyStatCnt.text = enemyFamilyInfo.TotalStar.ToString();
            }
            else
            {
                enemyGuildName.text = enemyFamilyInfo.FamilyName;
                enemyEmblem.Display((int)enemyFamilyInfo.EmblemID, enemyFamilyInfo.EmblemWord, 0.7f);
                enemyStatCnt.text = enemyFamilyInfo.TotalStar.ToString();
            }
        }
        else
        {
            enemyStatCnt.text = "0";
        }
        int displayState = manager.GetGuildBattleDisplayState();
        noJoinRect.SetActive(displayState == 1 && enemyFamilyInfo == null);
        enemyRect.SetActive(displayState != 1 && enemyFamilyInfo != null);
    }
    /// <summary>
    /// 显示日志 Scroller 列表
    /// </summary>
    void DisplayLogs()
    {
        scroller.Refresh();
        var logs = manager.atkDefBatLogs;
        int showCount = 0;
        if (logs != null && logs.Count > 0)
        {
            int maxCount = Mathf.Max(1, manager.maxLogCount);
            for (int i = 0; i < logs.Count && showCount < maxCount; i++)
            {
                if (IsEnemySweepLog(logs[i]))
                    continue;
                scroller.AddCell(ScrollerDataType.Header, i);
                showCount++;
            }
        }
        recordCnt.text = Language.Get("GuildAtkDefBat10", manager.maxLogCount);
        noRect.SetActive(showCount <= 0);
        scroller.Restart();
    }
    bool IsEnemySweepLog(GuildAtkDefBatManager.GuildAtkDefBatLogEntry logEntry)
    {
        if (logEntry == null || logEntry.action == null)
            return false;
        return logEntry.familyID != (uint)manager.MyFamilyID && logEntry.action.Value1 == 0;
    }
    void OnRefreshCell(ScrollerDataType type, CellView cell)
    {
        var logCell = cell as GuildAtkDefBatRecordCell;
        if (logCell != null && cell.index >= 0 && cell.index < manager.atkDefBatLogs.Count)
        {
            logCell.SetLogData(manager.atkDefBatLogs[cell.index]);
        }
    }
}
Main/System/GuildAtkDefBat/GuildAtkDefBatRecordWin.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: cebb85ac9f2d20547a725fbc8793964b
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/GuildAtkDefBat/GuildAtkDefBatShopWin.cs
New file
@@ -0,0 +1,52 @@
using UnityEngine;
using UnityEngine.UI;
public class GuildAtkDefBatShopWin : UIBase
{
    [SerializeField] ScrollerController scroller;
    protected override void OnPreOpen()
    {
        StoreModel.Instance.selectStoreFuncType = (StoreFunc)GuildAtkDefBatManager.Instance.shopType;
        scroller.OnRefreshCell += OnRefreshCell;
        StoreModel.Instance.RefreshShopEvent += Display;
        StoreModel.Instance.RefreshBuyShopLimitEvent += Display;
        Display();
    }
    protected override void OnPreClose()
    {
        scroller.OnRefreshCell -= OnRefreshCell;
        StoreModel.Instance.RefreshShopEvent -= Display;
        StoreModel.Instance.RefreshBuyShopLimitEvent -= Display;
    }
    void Display()
    {
        CreateScroller();
    }
    void CreateScroller()
    {
        if (!StoreModel.Instance.storeTypeDict.ContainsKey(GuildAtkDefBatManager.Instance.shopType))
            return;
        scroller.Refresh();
        var list = StoreModel.Instance.storeTypeDict[GuildAtkDefBatManager.Instance.shopType];
        for (int i = 0; i < list.Count; i++)
        {
            if (i % 3 == 0)
            {
                scroller.AddCell(ScrollerDataType.Header, i);
            }
        }
        scroller.Restart();
        scroller.lockType = EnhanceLockType.KeepVertical;
    }
    void OnRefreshCell(ScrollerDataType type, CellView cell)
    {
        var _cell = cell as StoreLineCell;
        _cell.Display(cell.index);
    }
}
Main/System/GuildAtkDefBat/GuildAtkDefBatShopWin.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 01ff8b55b832e7d4283165d43cf31eba
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/GuildAtkDefBat/GuildAtkDefBatTreasureCell.cs
New file
@@ -0,0 +1,46 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class GuildAtkDefBatTreasureCell : CellView
{
    [SerializeField] GuildAtkDefBatTreasureGrid[] grids; // 绑定5个格子
    /// <summary>
    /// 设置本行数据
    /// </summary>
    /// <param name="startIndex">起始格子序号(从1开始)</param>
    /// <param name="gridCount">总格子数</param>
    /// <param name="isWin">是否胜利</param>
    /// <param name="openedGrids">已开启的格子字典 Key=格子编号, Value=AwardID</param>
    /// <param name="openAnimGridIndex">播放开启动画的格子编号(0=无)</param>
    public void SetData(int startIndex, int gridCount, bool isWin, Dictionary<int, int> openedGrids, int openAnimGridIndex = 0)
    {
        for (int i = 0; i < grids.Length; i++)
        {
            int gridIndex = startIndex + i;
            if (gridIndex <= gridCount)
            {
                grids[i].SetActive(true);
                int awardID = 0;
                bool isOpened = openedGrids != null && openedGrids.TryGetValue(gridIndex, out awardID);
                bool playAnim = openAnimGridIndex > 0 && gridIndex == openAnimGridIndex;
                grids[i].Init(gridIndex, isWin, isOpened, (ushort)awardID, playAnim);
            }
            else
            {
                grids[i].SetActive(false);
            }
        }
    }
    /// <summary>
    /// 获取指定位置的格子
    /// </summary>
    public GuildAtkDefBatTreasureGrid GetGrid(int index)
    {
        if (index >= 0 && index < grids.Length)
            return grids[index];
        return null;
    }
}
Main/System/GuildAtkDefBat/GuildAtkDefBatTreasureCell.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f916f4087c522ad4ab5d743f0f4ff058
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/GuildAtkDefBat/GuildAtkDefBatTreasureGrid.cs
New file
@@ -0,0 +1,147 @@
using UnityEngine;
using UnityEngine.UI;
public class GuildAtkDefBatTreasureGrid : MonoBehaviour
{
    [SerializeField] ImageEx bdImage;
    [SerializeField] ButtonEx clickButton;
    [SerializeField] ImageEx topImage;
    [SerializeField] TextEx topNumText;
    [SerializeField] OutlineEx topNumOutline;
    [SerializeField] UIEffectPlayer uiEffectPlayer;
    [SerializeField] Color32 topGoldOddColor;
    [SerializeField] Color32 topGoldEvenColor;
    [SerializeField] Color32 topSilverOddColor;
    [SerializeField] Color32 topSilverEvenColor;
    [SerializeField] Color32 topGoldOddOutlineColor;
    [SerializeField] Color32 topGoldEvenOutlineColor;
    [SerializeField] Color32 topSilverOddOutlineColor;
    [SerializeField] Color32 topSilverEvenOutlineColor;
    [SerializeField] ImageEx qualityImage;
    [SerializeField] TextEx numText;
    [SerializeField] ImageEx itemImage;
    [SerializeField] TextEx itemCntText;
    [SerializeField] TextEx itemNameText;
    private int gridIndex;
    private bool isOpened;
    private ushort awardID;
    /// <summary>
    /// 初始化格子
    /// </summary>
    /// <param name="index">格子序号 1~n</param>
    /// <param name="isWin">是否胜利</param>
    /// <param name="opened">是否已开启</param>
    /// <param name="awardId">奖励ID(已开启时有效)</param>
    /// <param name="playOpenAnim">是否播放开启动画(翻牌破碎特效)</param>
    public void Init(int index, bool isWin, bool opened, ushort awardId, bool playOpenAnim = false)
    {
        gridIndex = index;
        isOpened = opened;
        awardID = awardId;
        // 格子序号
        topNumText.text = index.ToString();
        numText.text = index.ToString();
        // topImage按胜负/奇偶设置不同颜色和图片
        bool isOdd = index % 2 == 1;
        string topSpriteKey;
        Color32 numColor;
        Color32 outlineColor;
        if (isWin)
        {
            topSpriteKey = isOdd ? "GuildAtkDefBatGridTopGoldOdd" : "GuildAtkDefBatGridTopGoldEven";
            numColor = isOdd ? topGoldOddColor : topGoldEvenColor;
            outlineColor = isOdd ? topGoldOddOutlineColor : topGoldEvenOutlineColor;
        }
        else
        {
            topSpriteKey = isOdd ? "GuildAtkDefBatGridTopSilverOdd" : "GuildAtkDefBatGridTopSilverEven";
            numColor = isOdd ? topSilverOddColor : topSilverEvenColor;
            outlineColor = isOdd ? topSilverOddOutlineColor : topSilverEvenOutlineColor;
        }
        bdImage.SetSprite(isWin ? "GuildAtkDefBatGridGoldBg" : "GuildAtkDefBatGridSilverBg");
        topImage.SetSprite(topSpriteKey);
        topNumText.color = numColor;
        topNumOutline.OutlineColor = outlineColor;
        clickButton.SetListener(OnClickGrid);
        uiEffectPlayer.onComplete = null; // 清理回调,防止格子复用时残留
        if (playOpenAnim)
            PlayOpenEffect();
        else
            RefreshDisplay();
    }
    private void RefreshDisplay()
    {
        topImage.SetActive(!isOpened);
        topNumText.SetActive(!isOpened);
        qualityImage.SetActive(isOpened);
        if (isOpened && awardID > 0)
        {
            var config = FamilyAtkDefBatTreasureConfig.Get(awardID);
            if (config != null)
            {
                var itemConfig = ItemConfig.Get(config.ItemID);
                if (itemConfig != null)
                {
                    itemImage.SetItemSprite(config.ItemID);
                    itemCntText.text = config.ItemCount > 1 ? $"x{config.ItemCount}" : string.Empty;
                    itemNameText.text = itemConfig.ItemName;
                }
                qualityImage.SetSprite($"GuildAtkDefBatTreasureAwardColor{config.AwardColor}");
            }
        }
    }
    private void OnClickGrid()
    {
        if (isOpened)
        {
            if (awardID > 0)
            {
                var config = FamilyAtkDefBatTreasureConfig.Get(awardID);
                if (config != null)
                    ItemTipUtility.Show(config.ItemID);
            }
            return;
        }
        var mgr = GuildAtkDefBatManager.Instance;
        if (mgr.GetMyOpenRemainCount() <= 0)
        {
            SysNotifyMgr.Instance.ShowTip("GuildAtkDefBat4");
            return;
        }
        mgr.SendOpenTreasure((byte)gridIndex);
    }
    /// <summary>
    /// 播放开启动画(隐藏topImage,播放格子破碎特效)
    /// </summary>
    private void PlayOpenEffect()
    {
        topImage.SetActive(false);
        topNumText.SetActive(false);
        uiEffectPlayer.Play();
        RefreshDisplay();
        uiEffectPlayer.onComplete = OnOpenEffectComplete;
    }
    private void OnOpenEffectComplete()
    {
        uiEffectPlayer.onComplete = null; // 清除回调,防止重复调用
        GuildAtkDefBatManager.Instance.ShowTreasureAward();
    }
}
Main/System/GuildAtkDefBat/GuildAtkDefBatTreasureGrid.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2340dce30756258429472bcb3788578c
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/GuildAtkDefBat/GuildAtkDefBatTreasureWin.cs
New file
@@ -0,0 +1,188 @@
using System;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;
public class GuildAtkDefBatTreasureWin : UIBase
{
    [SerializeField] ImageEx bgImage;
    [SerializeField] RectTransform noOpenRect;
    [SerializeField] TextEx titleNameText;
    [SerializeField] TextEx openCntText;
    [SerializeField] ButtonEx awardButton;
    [SerializeField] ButtonEx lastLineButton;
    [SerializeField] ButtonEx nextLineButton;
    [SerializeField] ImageEx timeImage;
    [SerializeField] TextEx timeText;
    [SerializeField] ScrollerController scroller;
    private const int COLS = 5;
    private const int MAX_ROWS = 8;
    private int maxGridCount;
    private int currentTopRow;
    private bool isWin;
    private int cachedDisplayState;  // 缓存上一次的 displayState,变化时关窗
    GuildAtkDefBatManager manager => GuildAtkDefBatManager.Instance;
    private int pendingAnimGridIndex = 0;
    private int pendingAnimAwardID = 0;
    protected override void InitComponent()
    {
        base.InitComponent();
        awardButton.AddListener(() =>
        {
            UIManager.Instance.OpenWindowAsync<GuildAtkDefBatAwardPreviewWin>().Forget();
        });
        lastLineButton.AddListener(() =>
        {
            if (currentTopRow > 0)
            {
                currentTopRow--;
                RefreshScroller();
            }
        });
        nextLineButton.AddListener(() =>
        {
            int totalRows = Mathf.CeilToInt((float)maxGridCount / COLS);
            if (currentTopRow < totalRows - MAX_ROWS)
            {
                currentTopRow++;
                RefreshScroller();
            }
        });
    }
    protected override void OnPreOpen()
    {
        base.OnPreOpen();
        scroller.OnRefreshCell += OnRefreshCell;
        manager.OnUpdateAtkDefBatInfoEvent += OnUpdateAtkDefBatInfo;
        manager.OnTreasureDataUpdateEvent += OnTreasureDataUpdate;
        manager.OnTreasureGridOpenEvent += OnTreasureGridOpen;
        GlobalTimeEvent.Instance.secondEvent += OnSecondEvent;
        manager.isTreasureWinOpen = true;
        cachedDisplayState = manager.GetGuildBattleDisplayState();
        Display();
    }
    protected override void OnPreClose()
    {
        base.OnPreClose();
        scroller.OnRefreshCell -= OnRefreshCell;
        manager.OnUpdateAtkDefBatInfoEvent -= OnUpdateAtkDefBatInfo;
        manager.OnTreasureDataUpdateEvent -= OnTreasureDataUpdate;
        manager.OnTreasureGridOpenEvent -= OnTreasureGridOpen;
        GlobalTimeEvent.Instance.secondEvent -= OnSecondEvent;
        manager.isTreasureWinOpen = false;
    }
    private void OnUpdateAtkDefBatInfo()
    {
        // A525到达时,displayState变化则关窗(此时翻牌事件尚未触发)
        int curDisplayState = manager.GetGuildBattleDisplayState();
        if (curDisplayState != cachedDisplayState)
            CloseWindow();
    }
    private void OnTreasureDataUpdate()
    {
        openCntText.text = Language.Get("GuildAtkDefBat16", manager.GetMyOpenRemainCount(), manager.GetMyMaxOpenCount());
    }
    private void OnTreasureGridOpen(int gridIndex, int awardId)
    {
        pendingAnimGridIndex = gridIndex;
        pendingAnimAwardID = awardId;
        RefreshScroller();
        pendingAnimGridIndex = 0;
        pendingAnimAwardID = 0;
    }
    private void OnSecondEvent()
    {
        DisplayTime();
    }
    public void Display()
    {
        int displayState = manager.GetGuildBattleDisplayState();
        isWin = displayState == 4;
        bool isTrucePeriod = displayState == 4 || displayState == 5;
        if (isTrucePeriod)
        {
            titleNameText.text = Language.Get(isWin ? "GuildAtkDefBat18" : "GuildAtkDefBat19");
        }
        else
        {
            titleNameText.text = Language.Get("GuildAtkDefBat17");
        }
        bgImage.SetSprite(isWin ? "GuildAtkDefBatTreasureWin" : "GuildAtkDefBatTreasureFail");
        // 不等于休战期就隐藏noOpenRect
        noOpenRect.SetActive(!isTrucePeriod);
        // 开启次数
        openCntText.text = Language.Get("GuildAtkDefBat16", manager.GetMyOpenRemainCount(), manager.GetMyMaxOpenCount());
        // 剩余时间(仅休战期)
        DisplayTime();
        // 计算最大格子数
        maxGridCount = manager.CalculateMaxGridCount();
        // 重置滚动位置
        currentTopRow = 0;
        // 刷新Scroller
        RefreshScroller();
    }
    void DisplayTime()
    {
        int displayState = manager.GetGuildBattleDisplayState();
        bool isTrucePeriod = displayState == 4 || displayState == 5;
        timeImage.SetActive(isTrucePeriod);
        timeText.SetActive(isTrucePeriod);
        if (isTrucePeriod)
        {
            int cd = manager.GetCountdownToCurrentPeriodEnd();
            timeText.text = Language.Get("GuildAtkDefBat49", TimeUtility.SecondsToShortDHMS(cd));
        }
    }
    void RefreshScroller()
    {
        scroller.Refresh();
        int totalRows = Mathf.CeilToInt((float)maxGridCount / COLS);
        int visibleRows = Mathf.Min(MAX_ROWS, totalRows - currentTopRow);
        for (int i = 0; i < visibleRows; i++)
        {
            scroller.AddCell(ScrollerDataType.Header, currentTopRow + i);
        }
        scroller.Restart();
        lastLineButton.SetActive(currentTopRow > 0);
        nextLineButton.SetActive(currentTopRow < totalRows - MAX_ROWS);
    }
    void OnRefreshCell(ScrollerDataType type, CellView cell)
    {
        var tc = cell as GuildAtkDefBatTreasureCell;
        if (tc != null)
        {
            int rowIdx = cell.index;
            int startIdx = rowIdx * COLS + 1;
            tc.SetData(startIdx, maxGridCount, isWin, manager.treasureGridRecords, pendingAnimGridIndex);
        }
    }
}
Main/System/GuildAtkDefBat/GuildAtkDefBatTreasureWin.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 41245a160472ec943b6d9bd66e04c3dc
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/GuildAtkDefBat/GuildAtkDefBatVictoryWin.cs
New file
@@ -0,0 +1,190 @@
using LitJson;
using UnityEngine;
using UnityEngine.UI;
using Cysharp.Threading.Tasks;
using System.Collections.Generic;
public class GuildAtkDefBatVictoryWin : UIBase
{
    [SerializeField] AvatarCell myAvatar;
    [SerializeField] AvatarCell enemyAvatar;
    [SerializeField] TextEx myNameText;
    [SerializeField] TextEx enemyNameText;
    [SerializeField] RichText scoreText;
    [SerializeField] TextEx txtFuncName;
    [SerializeField] ScrollerController scroller;
    [SerializeField] ButtonEx detailBtn;
    JsonData jsonData;
    string battleName = BattleConst.GuildAtkDefBatField;
    bool isSweepVictory = false;
    protected override void InitComponent()
    {
        detailBtn.SetListener(() =>
        {
            BattleSettlementManager.Instance.OpenBattleDetailWin(battleName);
        });
    }
    protected override void OnPreOpen()
    {
        base.OnPreOpen();
        scroller.OnRefreshCell += OnRefreshCell;
        // 读取后立即重置,避免影响后续挑战
        isSweepVictory = GuildAtkDefBatManager.Instance.isSweepVictory;
        GuildAtkDefBatManager.Instance.isSweepVictory = false;
        if (isSweepVictory)
        {
            // 扫荡模式:不显示详情按钮
            detailBtn.SetActive(false);
            Display();
            CreateScroller();
        }
        else
        {
            jsonData = BattleSettlementManager.Instance.GetBattleSettlement(battleName);
            if (jsonData == null)
            {
                DelayCloseWindow().Forget();
                return;
            }
            Display();
            CreateScroller();
        }
    }
    protected override void OnPreClose()
    {
        base.OnPreClose();
        scroller.OnRefreshCell -= OnRefreshCell;
        if (isSweepVictory)
        {
            // 扫荡模式:不调用 WinShowOver(无战报数据需要清理)
        }
        else
        {
            BattleSettlementManager.Instance.WinShowOver(battleName);
        }
    }
    // B4 20 回合制战斗状态 #tagMCTurnFightState  通用的结算状态 State 4-结算奖励
    // Msg 中额外信息
    // "tagID":对战的目标ID,
    // "addStar":增加的星数,
    // itemInfo:奖励物品列表,可能为空
    void Display()
    {
        uint tagID;
        if (isSweepVictory)
        {
            // 扫荡模式:从 Manager 缓存获取目标ID
            tagID = GuildAtkDefBatManager.Instance.sweepTagID;
            if (tagID == 0)
                return;
        }
        else
        {
            if (!jsonData.ContainsKey("tagID"))
                return;
            tagID = (uint)(int)jsonData["tagID"];
        }
        // 我方数据
        uint myPlayerID = PlayerDatas.Instance.PlayerId;
        GuildAtkDefBatManager.Instance.MyFamilyMembers.TryGetValue(myPlayerID, out var myMem);
        // 敌方数据
        GuildAtkDefBatManager.Instance.EnemyFamilyMembers.TryGetValue(tagID, out var enemyMem);
        int myFace = myMem != null ? (int)myMem.Face : PlayerDatas.Instance.baseData.face;
        int myFacePic = myMem != null ? (int)myMem.FacePic : PlayerDatas.Instance.baseData.facePic;
        myAvatar.InitUI(AvatarHelper.GetAvatarModel((int)myPlayerID, myFace, myFacePic)).Forget();
        int enemyFace = enemyMem != null ? (int)enemyMem.Face : 0;
        int enemyFacePic = enemyMem != null ? (int)enemyMem.FacePic : 0;
        enemyAvatar.InitUI(AvatarHelper.GetAvatarModel((int)tagID, enemyFace, enemyFacePic)).Forget();
        enemyAvatar.SetListener(() =>
        {
            int serverID = enemyMem != null ? (int)enemyMem.ServerID : 0;
            AvatarHelper.TryViewOtherPlayerInfo((int)tagID, serverID, viewPlayerLineupType: (int)BattlePreSetType.GuildAtkDefBat);
        });
        myNameText.text = myMem != null ? UIHelper.ServerStringTrim(myMem.Name) : PlayerDatas.Instance.baseData.PlayerName;
        enemyNameText.text = enemyMem != null ? UIHelper.ServerStringTrim(enemyMem.Name) : "";
        if (isSweepVictory)
        {
            // 扫荡模式:不显示加的星数
            scoreText.SetActive(false);
        }
        else
        {
            // 显示增加的星数(星星图标 + 数值)
            scoreText.SetActive(true);
            int addStar = jsonData.ContainsKey("addStar") ? (int)jsonData["addStar"] : 0;
            scoreText.text = Language.Get("GuildAtkDefBat22", "GuildAtkDefBatBattleEx_img_47", addStar);
        }
        int funcId = (int)FuncOpenEnum.GuildAtkDefBat;
        txtFuncName.text = FuncOpenLVConfig.HasKey(funcId) ? FuncOpenLVConfig.Get(funcId).Name : string.Empty;
    }
    List<Item> showItems = new List<Item>();
    void CreateScroller()
    {
        showItems.Clear();
        scroller.Refresh();
        if (isSweepVictory)
        {
            // 扫荡模式:从 Manager 缓存获取奖励数据
            var sweepItems = GuildAtkDefBatManager.Instance.sweepItemInfos;
            if (sweepItems != null && sweepItems.Count > 0)
            {
                showItems.AddRange(sweepItems);
            }
            else
            {
                scroller.Restart();
                return;
            }
        }
        else
        {
            if (!jsonData.ContainsKey("itemInfo"))
            {
                scroller.Restart();
                return;
            }
            var resultStr = jsonData["itemInfo"];
            for (int i = 0; i < resultStr.Count; i++)
            {
                showItems.Add(new Item((int)resultStr[i]["ItemID"], (long)resultStr[i]["Count"]));
            }
        }
        showItems.Sort(SortItem);
        for (int i = 0; i < showItems.Count; i++)
        {
            scroller.AddCell(ScrollerDataType.Header, i);
        }
        scroller.Restart();
    }
    int SortItem(Item itemA, Item itemB)
    {
        var itemConfigA = ItemConfig.Get(itemA.id);
        var itemConfigB = ItemConfig.Get(itemB.id);
        return itemConfigB.ItemColor - itemConfigA.ItemColor;
    }
    void OnRefreshCell(ScrollerDataType type, CellView cell)
    {
        var _cell = cell as SettlementAwardCell;
        var item = showItems[cell.index];
        _cell?.Display(item.id, item.countEx);
    }
}
Main/System/GuildAtkDefBat/GuildAtkDefBatVictoryWin.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 36c523428cd541e46bab69ebe0b53295
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/GuildAtkDefBat/GuildAtkDefBatWin.cs
New file
@@ -0,0 +1,351 @@
using System;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using DG.DemiEditor;
using UnityEngine;
public class GuildAtkDefBatWin : UIBase
{
    [SerializeField] GuildEmblemCell myEmblemCell;
    [SerializeField] TextEx myNameText;
    [SerializeField] TextEx myServerText;
    [SerializeField] ImageEx myLightImage;
    [SerializeField] ImageEx myTextImage;
    [SerializeField] TextEx myStarCntText;
    [SerializeField] GuildAtkDefBatPlayerShowCell[] myModels;
    [SerializeField] RectTransform enemyRect;
    [SerializeField] GuildEmblemCell enemyEmblemCell;
    [SerializeField] TextEx enemyNameText;
    [SerializeField] TextEx enemyServerText;
    [SerializeField] ImageEx enemyLightImage;
    [SerializeField] ImageEx enemyTextImage;
    [SerializeField] TextEx enemyStarCntText;
    [SerializeField] GuildAtkDefBatPlayerShowCell[] enemyModels;
    [SerializeField] RectTransform noJoinRect;
    [SerializeField] RectTransform joinConditionRect;
    [SerializeField] TextEx joinCntText;
    [SerializeField] TextEx activityLevelText;
    [SerializeField] TextEx timeText;
    [SerializeField] ImageEx stateImage;
    [SerializeField] ButtonEx goButton;
    [SerializeField] ButtonEx backButton;
    [SerializeField] ButtonEx rankButton;
    [SerializeField] ButtonEx recordButton;
    [SerializeField] ButtonEx shopButton;
    [SerializeField] ButtonEx treasureButton;
    [SerializeField] ButtonEx myEmblemBtn;
    [SerializeField] ButtonEx enemyEmblemBtn;
    protected override void InitComponent()
    {
        backButton.AddListener(CloseWindow);
        rankButton.AddListener(() =>
        {
            UIManager.Instance.OpenWindowAsync<GuildAtkDefBatRankWin>().Forget();
        });
        recordButton.AddListener(() =>
        {
            UIManager.Instance.OpenWindowAsync<GuildAtkDefBatRecordWin>().Forget();
        });
        shopButton.AddListener(() =>
        {
            UIManager.Instance.OpenWindowAsync<GuildAtkDefBatShopWin>().Forget();
        });
        treasureButton.AddListener(() =>
        {
            UIManager.Instance.OpenWindowAsync<GuildAtkDefBatTreasureWin>().Forget();
        });
        goButton.AddListener(async () =>
        {
            // 检查是否有正在进行的战斗(参考ArenaTabHandler的模式)
            BattleField guildAtkDefBatBattle = BattleManager.Instance.GetBattleFieldByMapID(GuildAtkDefBatManager.DataMapID);
            if (guildAtkDefBatBattle != null)
            {
                GuildAtkDefBatBattleWin battleWin;
                if (!UIManager.Instance.IsOpened<GuildAtkDefBatBattleWin>())
                {
                    battleWin = await UIManager.Instance.OpenWindowAsync<GuildAtkDefBatBattleWin>();
                }
                else
                {
                    battleWin = UIManager.Instance.GetUI<GuildAtkDefBatBattleWin>();
                }
                battleWin.SetBattleField(guildAtkDefBatBattle);
                CloseWindow();
                return;
            }
            // 对手公会已解散,不可进入战斗
            var enemyInfo = GuildAtkDefBatManager.Instance.EnemyFamilyInfo;
            if (enemyInfo != null && enemyInfo.IsDismiss == 1)
            {
                SysNotifyMgr.Instance.ShowTip("GuildAtkDefBat6");
                return;
            }
            bool hasEligibility = GuildAtkDefBatManager.Instance.HasMyJoinEligibility();
            if (!hasEligibility)
            {
                SysNotifyMgr.Instance.ShowTip("GuildAtkDefBat1");
                return;
            }
            UIManager.Instance.OpenWindowAsync<GuildAtkDefBatBattlePanelWin>().Forget();
        });
        // 己方公会徽章点击 - 自己的公会
        myEmblemBtn.AddListener(() =>
        {
            SysNotifyMgr.Instance.ShowTip("GuildTip01");
        });
        // 敌方公会徽章点击
        enemyEmblemBtn.AddListener(() =>
        {
            var enemyInfo = GuildAtkDefBatManager.Instance.EnemyFamilyInfo;
            if (enemyInfo == null)
                return;
            int familyID = (int)enemyInfo.FamilyID;
            int serverID = (int)enemyInfo.ServerID;
            // 自己的公会
            if (PlayerDatas.Instance.fairyData.HasFairy && PlayerDatas.Instance.fairyData.fairy.FamilyID == familyID)
            {
                SysNotifyMgr.Instance.ShowTip("GuildTip01");
                return;
            }
            // 系统公会
            if (GuildAtkDefBatManager.Instance.IsSystemFamily(enemyInfo.FamilyID))
                return;
            // 查看公会信息
            GuildManager.Instance.SendViewGuild(familyID, serverID);
        });
    }
    protected override void OnPreOpen()
    {
        GlobalTimeEvent.Instance.secondEvent += OnSecondEvent;
        GuildAtkDefBatManager.Instance.OnUpdateeFamilyActionInfoEvent += OnUpdateeFamilyActionInfoEvent;
        GuildAtkDefBatManager.Instance.OnUpdateAtkDefBatInfoEvent += OnUpdateAtkDefBatInfoEvent;
        GuildManager.Instance.OnUpdateViewFamilyInfo += OnUpdateViewFamilyInfo;
        GuildAtkDefBatManager.Instance.ClearMainRedpoint();
        Display();
    }
    protected override void OnPreClose()
    {
        GlobalTimeEvent.Instance.secondEvent -= OnSecondEvent;
        GuildAtkDefBatManager.Instance.OnUpdateeFamilyActionInfoEvent -= OnUpdateeFamilyActionInfoEvent;
        GuildAtkDefBatManager.Instance.OnUpdateAtkDefBatInfoEvent -= OnUpdateAtkDefBatInfoEvent;
        GuildManager.Instance.OnUpdateViewFamilyInfo -= OnUpdateViewFamilyInfo;
    }
    private void OnUpdateViewFamilyInfo(int familyID)
    {
        if (!GuildManager.Instance.viewGuildsDict.ContainsKey(familyID))
            return;
        if (!UIManager.Instance.IsOpened<GuildPreviewWin>())
        {
            GuildPreviewWin.SetShowOpearteBtn(false);
            UIManager.Instance.OpenWindowAsync<GuildPreviewWin>(familyID).Forget();
        }
    }
    private void OnUpdateeFamilyActionInfoEvent()
    {
        Display();
    }
    private void OnUpdateAtkDefBatInfoEvent()
    {
        Display();
    }
    private void OnSecondEvent()
    {
        DisplayTime();
    }
    void Display()
    {
        DisplayInfo();
        DisplayCoreMembers();
        DisplayStateImage();
        DisplayTime();
        DisplayButton();
    }
    void DisplayStateImage()
    {
        int displayState = GuildAtkDefBatManager.Instance.GetGuildBattleDisplayState();
        stateImage.SetSprite(displayState switch
        {
            5 => "GuildAtkDefBatState4",
            _ => $"GuildAtkDefBatState{displayState}"
        });
    }
    void DisplayTime()
    {
        int displayState = GuildAtkDefBatManager.Instance.GetGuildBattleDisplayState();
        timeText.text = Language.Get(displayState switch
        {
            2 => "GuildAtkDefBat28",
            3 => "GuildAtkDefBat29",
            _ => "GuildAtkDefBat30",
        },
        UIHelper.AppendColor(TextColType.LightGreen, GuildAtkDefBatManager.Instance.GetGetCountdownToCurrentPeriodEndString()));
    }
    void DisplayButton()
    {
        int displayState = GuildAtkDefBatManager.Instance.GetGuildBattleDisplayState();
        goButton.SetActive(displayState == 2 || displayState == 3);
    }
    /// <summary>
    /// 展示左右两侧核心成员的角色模型+坐骑
    /// </summary>
    void DisplayCoreMembers()
    {
        int displayState = GuildAtkDefBatManager.Instance.GetGuildBattleDisplayState();
        // displayState == 1 未参战,不显示角色
        bool showMembers = displayState != 1;
        // 己方成员展示
        var myMembers = showMembers ? GuildAtkDefBatManager.Instance.GetSortedCoreMembers(true, displayState) : null;
        DisplayMemberModels(myModels, myMembers);
        // 敌方成员展示
        var enemyMembers = showMembers ? GuildAtkDefBatManager.Instance.GetSortedCoreMembers(false, displayState) : null;
        DisplayMemberModels(enemyModels, enemyMembers);
    }
    /// <summary>
    /// 将成员数据填充到模型展示Cell中
    /// </summary>
    void DisplayMemberModels(GuildAtkDefBatPlayerShowCell[] models, List<HA525_tagSCFamilyAtkDefBatInfo.tagSCFamilyAtkDefBatMem> members)
    {
        if (members == null || members.Count <= 0)
        {
            for (int i = 0; i < models.Length; i++)
                models[i].SetActive(false);
            return;
        }
        for (int i = 0; i < models.Length; i++)
        {
            if (i < members.Count)
            {
                models[i].SetActive(true);
                var mem = members[i];
                // 解析外观数据
                int heroSkinID = ResolveHeroSkinID((int)mem.ModelMark, (int)mem.Job);
                int horseSkinID = HorseManager.Instance.GetOtherPlayerHorseSkinID((int)mem.EquipShowSwitch);
                models[i].Display(heroSkinID, horseSkinID, (int)mem.PlayerID, (int)mem.ServerID, false);
            }
            else
            {
                models[i].SetActive(false);
            }
        }
    }
    /// <summary>
    /// 根据 ModelMark 和 Job 解析英雄皮肤ID
    /// </summary>
    int ResolveHeroSkinID(int modelMark, int job)
    {
        if (modelMark == 0)
        {
            PhantasmPavilionManager.Instance.TryGetDefaultID(PhantasmPavilionType.Model, job, out modelMark);
        }
        if (!ModelConfig.HasKey(modelMark))
            return 0;
        var modelConfig = ModelConfig.Get(modelMark);
        return modelConfig.SkinID;
    }
    void DisplayInfo()
    {
        var myFamilyInfo = GuildAtkDefBatManager.Instance.MyFamilyInfo;
        if (myFamilyInfo != null)
        {
            myNameText.text = myFamilyInfo.FamilyName;
            myServerText.text = ServerListCenter.Instance.GetServerName(UIHelper.GetServerIDByAccount(PlayerDatas.Instance.baseData.AccID));
            myEmblemCell.Display((int)myFamilyInfo.EmblemID, myFamilyInfo.EmblemWord, 0.7f);
            myStarCntText.text = myFamilyInfo.TotalStar.ToString();
        }
        else
        {
            myStarCntText.text = "0";
        }
        var enemyFamilyInfo = GuildAtkDefBatManager.Instance.EnemyFamilyInfo;
        if (enemyFamilyInfo != null)
        {
            //如果是是系统公会
            bool isSystemFamily = GuildAtkDefBatManager.Instance.IsSystemFamily(enemyFamilyInfo.FamilyID);
            if (isSystemFamily)
            {
                string enemyName = Language.Get("GuildAtkDefBat31");
                enemyNameText.text = enemyName;
                enemyEmblemCell.Display(FamilyEmblemConfig.GetFirstDefaultEmblem(), !enemyName.IsNullOrEmpty() ? enemyName[0].ToString() : string.Empty, 0.7f);
                enemyServerText.text = ServerListCenter.Instance.GetServerName(UIHelper.GetServerIDByAccount(PlayerDatas.Instance.baseData.AccID));
                enemyStarCntText.text = enemyFamilyInfo.TotalStar.ToString();
            }
            else
            {
                enemyNameText.text = enemyFamilyInfo.FamilyName;
                enemyEmblemCell.Display((int)enemyFamilyInfo.EmblemID, enemyFamilyInfo.EmblemWord, 0.7f);
                enemyServerText.text = ServerListCenter.Instance.GetServerName((int)enemyFamilyInfo.ServerID);
                enemyStarCntText.text = enemyFamilyInfo.TotalStar.ToString();
            }
        }
        else
        {
            enemyStarCntText.text = "0";
        }
        int displayState = GuildAtkDefBatManager.Instance.GetGuildBattleDisplayState();
        noJoinRect.SetActive(displayState == 1 && enemyFamilyInfo == null);
        enemyRect.SetActive(displayState != 1 && enemyFamilyInfo != null);
        bool isShowJoinConditionRect = displayState == 1 || displayState == 4 || displayState == 5;
        joinConditionRect.SetActive(isShowJoinConditionRect);
        if (isShowJoinConditionRect)
        {
            int prepareMemberLimit = GuildAtkDefBatManager.Instance.prepareMemberLimit;
            int myGuildMemberCnt = PlayerDatas.Instance.fairyData?.fairy?.MemberCount ?? 0;
            joinCntText.text = Language.Get("GuildAtkDefBat32", UIHelper.AppendColor(myGuildMemberCnt >= prepareMemberLimit ? TextColType.LightGreen : TextColType.Red, myGuildMemberCnt.ToString()), prepareMemberLimit);
            int prepareActiveLimit = GuildAtkDefBatManager.Instance.prepareActiveLimit;
            long nowActivityLevel = GuildAtkDefBatManager.Instance.GetMyActivityLevel();
            activityLevelText.text = Language.Get("GuildAtkDefBat33", UIHelper.AppendColor(nowActivityLevel >= prepareActiveLimit ? TextColType.LightGreen : TextColType.Red, nowActivityLevel.ToString()), prepareActiveLimit);
        }
        bool isShowResult = displayState == 4 || displayState == 5;
        myLightImage.SetActive(isShowResult);
        enemyLightImage.SetActive(isShowResult);
        myLightImage.SetSprite(displayState == 4 ? "GuildAtkDefBatLightVictory" : "GuildAtkDefBatLightFail");
        enemyLightImage.SetSprite(displayState == 4 ? "GuildAtkDefBatLightFail" : "GuildAtkDefBatLightVictory");
        myTextImage.SetSprite(displayState == 4 ? "GuildAtkDefBatTextVictory" : "GuildAtkDefBatTextFail");
        enemyTextImage.SetSprite(displayState == 4 ? "GuildAtkDefBatTextFail" : "GuildAtkDefBatTextVictory");
    }
}
Main/System/GuildAtkDefBat/GuildAtkDefBatWin.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 95086816e0974a74a9c7d8b8def793c8
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/HeroUI/HeroFormationWin.cs
@@ -67,8 +67,6 @@
        scroller.OnRefreshCell -= OnRefreshCell;
    }
    void OnRefreshCell(ScrollerDataType type, CellView cell)
    {
        var _cell = cell as HeroFormationCell;
Main/System/ItemTip/BoxGetItemModel.cs
@@ -1,4 +1,4 @@
using System;
using System;
using LitJson;
using System.Collections.Generic;
@@ -32,6 +32,13 @@
            return;
        }
        // 公会攻防战宝库 - 由宝库开启动画控制展示时机
        if (eventName == "AtkDefBatTreasure")
        {
            GuildAtkDefBatManager.Instance.CacheTreasureAward(netPack);
            return;
        }
        List<Item> showItems = new List<Item>();
        if (netPack.Exp != 0 || netPack.ExpPoint != 0)
Main/System/Redpoint/MainRedDot.cs
@@ -159,6 +159,7 @@
    public const int FestivalActivityRepoint = 485;    //节日活动
    public const int HeroSkinFlashSaleRepoint = 486;
    public const int SuperVIPRepoint = 487;
    public const int GuildAtkDefBatRepoint = 488;//公会攻防战
    public void Register()
    {
Main/System/Settlement/BattleSettlementManager.cs
@@ -44,6 +44,9 @@
            case BattleConst.ArenaBattleField:
                PopupWindowsProcessor.Instance.Add(isWin ? "ArenaBattleVictoryWin" : "ArenaBattleFailWin", false, BattleConst.ArenaBattleField);
                break;
            case BattleConst.GuildAtkDefBatField:
                PopupWindowsProcessor.Instance.Add(isWin ? "GuildAtkDefBatVictoryWin" : "GuildAtkDefBatFailWin", false, BattleConst.GuildAtkDefBatField);
                break;
            case BattleConst.BoneBattleField:
                PopupWindowsProcessor.Instance.Add(isWin ? "BoneBattleVictoryWin" : "BoneBattleFailWin", false, BattleConst.BoneBattleField);
                break;
@@ -76,6 +79,16 @@
                else
                {
                    UIManager.Instance.OpenWindow<ArenaBattleFailWin>();
                }
                break;
            case BattleConst.GuildAtkDefBatField:
                if (isWin)
                {
                    UIManager.Instance.OpenWindowAsync<GuildAtkDefBatVictoryWin>().Forget();
                }
                else
                {
                    UIManager.Instance.OpenWindowAsync<GuildAtkDefBatFailWin>().Forget();
                }
                break;
            case BattleConst.BoneBattleField:
@@ -376,6 +389,12 @@
                    return detailName;
                detailName = info.PlayerName;
                break;
            case BattleConst.GuildAtkDefBatField:
                uint targetPlayerID = GuildAtkDefBatManager.Instance.CurrentChallengeTargetPlayerID;
                if (targetPlayerID == 0 || !GuildAtkDefBatManager.Instance.EnemyFamilyMembers.TryGetValue(targetPlayerID, out var enemyMem))
                    return detailName;
                detailName = enemyMem.Name;
                break;
        }
        return detailName;
    }
Main/System/ViewNPC/ViewNPCManager.cs
@@ -73,6 +73,17 @@
                    playerId = (int)QunyingManager.Instance.atkPlayerId;
                    OtherPlayerDetailManager.Instance.ViewPlayerDetail(playerId, 0, (int)ViewPlayerType.viewPVPBattleEnemyHero, (int)BattlePreSetType.Qunying);
                    break;
                case BattleConst.GuildAtkDefBatField:
                    uint targetPlayerID = GuildAtkDefBatManager.Instance.CurrentChallengeTargetPlayerID;
                    if (targetPlayerID == 0)
                        break;
                    int serverID = 0;
                    if (GuildAtkDefBatManager.Instance.EnemyFamilyMembers.TryGetValue(targetPlayerID, out var enemyMem))
                        serverID = (int)enemyMem.ServerID;
                    OtherPlayerDetailManager.Instance.ViewPlayerDetail((int)targetPlayerID, serverID, (int)ViewPlayerType.viewPVPBattleEnemyHero, (int)BattlePreSetType.GuildAtkDefBat);
                    break;
            }
            return;
        }
Main/Utility/EnumHelper.cs
@@ -861,6 +861,7 @@
    HeroReturn = 64,  //武将返场
    FestivalActivity = 65,//节日活动
    HeroSkinFlashSale = 66,//时装特卖
    GuildAtkDefBat = 67,//公会攻防战
}
Main/Utility/UIHelper.cs
@@ -1120,6 +1120,7 @@
        {2, PlayerDataType.GoldPaper},
        {3, PlayerDataType.Silver},
        {15, PlayerDataType.UnionLiven},
        {18, PlayerDataType.CDBPlayerRefresh_Honor},
        {41, PlayerDataType.default26},
        {43, PlayerDataType.default34},
        {42, PlayerDataType.default33},