lcy
2 天以前 09bc892c7283df8757a07b646d5af21ddaa263d1
164 天子的考验-客户端
1 文件已重命名
14个文件已修改
1个文件已删除
35个文件已添加
2860 ■■■■■ 已修改文件
Main/Component/UI/Common/IntensifySmoothSlider.cs 183 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Component/UI/Common/IntensifySmoothSlider.cs.meta 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/ConfigManager.cs 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/Configs/NPCConfig.cs 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/Configs/TianziConfig.cs 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/Configs/TianziConfig.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/PartialConfigs/DungeonConfig.cs 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/PartialConfigs/TianziConfig.cs 136 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/PartialConfigs/TianziConfig.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/DTCFile/ServerPack/H03_MainCharacter/DTC0320_tagFBEnd.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/DTCFile/ServerPack/HB2_ActionMap.meta 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/DTCFile/ServerPack/HB2_ActionMap/DTCB201_tagSCTianziKYInfo.cs 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/DTCFile/ServerPack/HB2_ActionMap/DTCB201_tagSCTianziKYInfo.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/DataToCtl/PackageRegedit.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/ServerPack/HB2_ActionMap.meta 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/ServerPack/HB2_ActionMap/HB201_tagSCTianziKYInfo.cs 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/ServerPack/HB2_ActionMap/HB201_tagSCTianziKYInfo.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Main.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/BattleConst.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/BattleField/TianziBillboradBattleField.cs 142 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/BattleField/TianziBillboradBattleField.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/BattleFieldFactory.cs 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/BattleObject/BattleObject.cs 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/TianziBillboradBattleWin.cs 346 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/TianziBillboradBattleWin.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/UIComp/BattleHeroInfoBar.cs 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/BoneField/AdsManager.cs 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/ChallengeTab/TianziBillboradTabHandler.cs 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/ChallengeTab/TianziBillboradTabHandler.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Dungeon/DungeonManager.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Redpoint/MainRedDot.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Settlement/BattleSettlementManager.cs 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Settlement/TianziBillboradVictoryWin.cs 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Settlement/TianziBillboradVictoryWin.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TianziBillborad/IntensifySmoothSlider.cs 115 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TianziBillborad/TianziBillboradAwardCell.cs 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TianziBillborad/TianziBillboradAwardCell.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TianziBillborad/TianziBillboradBossHead.cs 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TianziBillborad/TianziBillboradBossHead.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TianziBillborad/TianziBillboradBox.cs 247 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TianziBillborad/TianziBillboradBox.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TianziBillborad/TianziBillboradManager.cs 295 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TianziBillborad/TianziBillboradManager.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TianziBillborad/TianziBillboradRankWin.cs 164 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TianziBillborad/TianziBillboradRankWin.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TianziBillborad/TianziBillboradSweepTipWin.cs 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TianziBillborad/TianziBillboradSweepTipWin.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TianziBillborad/TianziBillboradWin.cs 265 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TianziBillborad/TianziBillboradWin.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TianziBillborad/TianziDamageBar.cs 224 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/TianziBillborad/TianziDamageBar.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Component/UI/Common/IntensifySmoothSlider.cs
New file
@@ -0,0 +1,183 @@
using System;
using UnityEngine;
using UnityEngine.UI;
[DisallowMultipleComponent]
public class IntensifySmoothSlider : MonoBehaviour
{
    [SerializeField]
    Slider m_Slider;
    public Slider slider
    {
        get { return m_Slider; }
    }
    [SerializeField]
    [Range(0, 10)]
    float m_Delay = 0.2f;
    public float delay
    {
        get { return m_Delay; }
        set
        {
            m_Delay = Mathf.Clamp(value, 0, 10);
        }
    }
    [SerializeField]
    [Range(0, 1)]
    float m_Value = 0f;
    public float value
    {
        get
        {
            return m_Value;
        }
        set
        {
            m_Value = Mathf.Clamp01(value);
        }
    }
    // 值更新事件(ValueChangeAction)的触发分段数。
    // 用于降低 ValueChangeAction 的触发频率,防止UI文本等监听组件频繁刷新。
    //
    // 工作原理:
    // 将滑块的 [0, 1] 范围划分为 'm_ValueUpdateSegments' 个数量相等的分段。
    // ValueChangeAction 事件仅在 slider.value 跨越这些分段边界时才会被触发。
    //
    // 示例:
    // - 设为 10: 则滑块值每变化 0.1 (10%) 触发一次事件 (如从 0.19 跨到 0.21 时)。
    // - 设为 100: 则滑块值每变化 0.01 (1%) 触发一次事件。
    [SerializeField]
    [Range(1, 1000)]
    int m_ValueUpdateSegments = 10;
    public int ValueUpdateSegments
    {
        get { return m_ValueUpdateSegments; }
        set { m_ValueUpdateSegments = Mathf.Max(1, value); } // 至少为1
    }
    // 跟踪上一次触发事件时,滑块值所处的分段索引 (范围从 0 到 m_ValueUpdateSegments-1)。
    private int m_LastNotifiedSegment = -1;
    int m_Stage = 0;
    public int stage
    {
        get { return m_Stage; }
        set { m_Stage = value; }
    }
    private int presentStage;
    public int CurrentStage { get { return presentStage; } }
    float refSpeed = 0f;
    public event Action<int> StageUpAction;
    public event Action<float, int> ValueChangeAction;
    public event Action ChangeEndAction;
    public void ResetStage()
    {
        presentStage = stage;
    }
    public void ResetValue(float _value)
    {
        value = _value;
        if (slider != null)
        {
            slider.value = Mathf.Clamp01(_value);
        }
        // 重置值时,同步更新最后通知的分段索引
        m_LastNotifiedSegment = GetCurrentSegment(slider.value);
    }
    void OnEnable()
    {
        refSpeed = 0f;
        // 启用时,初始化最后通知的分段索引
        m_LastNotifiedSegment = GetCurrentSegment(slider != null ? slider.value : m_Value);
    }
    // 根据当前滑块值(0-1),计算其所属的分段索引(0 到 m_ValueUpdateSegments-1)
    private int GetCurrentSegment(float currentValue)
    {
        // 将 [0, 1] 范围的浮点数值 映射到 [0, m_ValueUpdateSegments-1] 范围的整数索引
        float preciseSegment = currentValue * m_ValueUpdateSegments;
        // 注意:当值为1.0f时,我们希望它落在最后一个分段索引上 (即 segments - 1)
        if (currentValue >= 1.0f)
        {
            return m_ValueUpdateSegments - 1;
        }
        return Mathf.FloorToInt(preciseSegment);
    }
    // 用于跟踪滑块是否正在缓动
    private bool isMoving = false;
    void LateUpdate()
    {
        if (slider == null)
        {
            return;
        }
        if (presentStage < m_Stage)
        {
            slider.value = Mathf.SmoothDamp(slider.value, 1, ref refSpeed, delay / 2);
            if (slider.value >= 0.99f)
            {
                slider.value = 0f;
                presentStage++;
                StageUpAction?.Invoke(presentStage);
                m_LastNotifiedSegment = 0;// 当血条循环时,重置分段索引
            }
            isMoving = true;
            CheckForValueChangeNotification(slider.value);
        }
        else
        {
            if (Mathf.Abs(slider.value - value) > 0.001f)
            {
                slider.value = Mathf.SmoothDamp(slider.value, value, ref refSpeed, delay);
                isMoving = true;
                CheckForValueChangeNotification(slider.value);
            }
            else
            {
                if (isMoving)
                {
                    // 确保值在停止时完全精确
                    if (slider.value != value)
                    {
                        slider.value = value;
                        ValueChangeAction?.Invoke(slider.value, CurrentStage);
                        m_LastNotifiedSegment = GetCurrentSegment(slider.value);
                    }
                    ChangeEndAction?.Invoke();
                    isMoving = false;
                }
            }
        }
    }
    // 检查是否需要触发ValueChangeAction的逻辑
    private void CheckForValueChangeNotification(float currentSliderValue)
    {
        int currentSegment = GetCurrentSegment(currentSliderValue);
        // 如果当前的分段索引与上次触发事件时的索引不同,说明已跨越分段边界。
        if (currentSegment != m_LastNotifiedSegment)
        {
            ValueChangeAction?.Invoke(currentSliderValue, CurrentStage);
            // 更新“上次触发”的索引为当前索引,防止本分段内重复触发。
            m_LastNotifiedSegment = currentSegment;
        }
    }
}
Main/Component/UI/Common/IntensifySmoothSlider.cs.meta
Main/Config/ConfigManager.cs
@@ -71,6 +71,7 @@
            typeof(StoreConfig),
            typeof(SuccessConfig),
            typeof(SysInfoConfig),
            typeof(TianziConfig),
            typeof(TitleStarUpConfig),
            typeof(TreasureSetConfig),
            typeof(TreeLVConfig),
@@ -292,6 +293,8 @@
        ClearConfigDictionary<SuccessConfig>();
        // 清空 SysInfoConfig 字典
        ClearConfigDictionary<SysInfoConfig>();
        // 清空 TianziConfig 字典
        ClearConfigDictionary<TianziConfig>();
        // 清空 TitleStarUpConfig 字典
        ClearConfigDictionary<TitleStarUpConfig>();
        // 清空 TreasureSetConfig 字典
Main/Config/Configs/NPCConfig.cs
@@ -1,6 +1,6 @@
//--------------------------------------------------------
//    [Author]:           YYL
//    [  Date ]:           2025年10月17日
//    [  Date ]:           2025年10月24日
//--------------------------------------------------------
using System.Collections.Generic;
@@ -18,6 +18,7 @@
    public int NPCID;
    public string NPCName;
    public int RelatedHeroID;
    public int LV;
    public int SkinID;
    public float ModelScale;
@@ -37,13 +38,15 @@
            NPCName = tables[1];
            int.TryParse(tables[2],out LV);
            int.TryParse(tables[2],out RelatedHeroID);
            int.TryParse(tables[3],out SkinID);
            int.TryParse(tables[3],out LV);
            float.TryParse(tables[4],out ModelScale);
            int.TryParse(tables[4],out SkinID);
            int.TryParse(tables[5],out LifeBarCount);
            float.TryParse(tables[5],out ModelScale);
            int.TryParse(tables[6],out LifeBarCount);
        }
        catch (Exception exception)
        {
Main/Config/Configs/TianziConfig.cs
New file
@@ -0,0 +1,59 @@
//--------------------------------------------------------
//    [Author]:           YYL
//    [  Date ]:           2025年10月28日
//--------------------------------------------------------
using System.Collections.Generic;
using System;
using UnityEngine;
using LitJson;
public partial class TianziConfig : ConfigBase<int, TianziConfig>
{
    static TianziConfig()
    {
        // 访问过静态构造函数
        visit = true;
    }
    public int ID;
    public int BossID;
    public int HPNum;
    public int Atk;
    public int Def;
    public long MaxHP;
    public string OtherAttrDict;
    public int[][] RandWeightItemList;
    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 ID);
            int.TryParse(tables[1],out BossID);
            int.TryParse(tables[2],out HPNum);
            int.TryParse(tables[3],out Atk);
            int.TryParse(tables[4],out Def);
            long.TryParse(tables[5],out MaxHP);
            OtherAttrDict = tables[6];
            RandWeightItemList = JsonMapper.ToObject<int[][]>(tables[7].Replace("(", "[").Replace(")", "]"));
        }
        catch (Exception exception)
        {
            Debug.LogError(exception);
        }
    }
}
Main/Config/Configs/TianziConfig.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c157a3f99bdd3ca4a9311a1438687170
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Config/PartialConfigs/DungeonConfig.cs
@@ -18,8 +18,8 @@
        return dungeonIndexDict.TryGetValue(mapID, out var dict) && dict.TryGetValue(lineID, out dungeonID);
    }
    public static Dictionary<int, Dictionary<int, int>> GetDungeonIndexDict()
    public static bool TryGetDictByMapID(int mapID, out Dictionary<int, int> dict)
    {
        return dungeonIndexDict;
        return dungeonIndexDict.TryGetValue(mapID, out dict);
    }
}
Main/Config/PartialConfigs/TianziConfig.cs
New file
@@ -0,0 +1,136 @@
using System.Collections.Generic;
using System.Linq;
public partial class TianziConfig : ConfigBase<int, TianziConfig>
{
    //<BossID<HPNum,TianziConfig>>
    private static Dictionary<int, Dictionary<int, TianziConfig>> bossIDAllInfoDict = new Dictionary<int, Dictionary<int, TianziConfig>>();
    protected override void OnConfigParseCompleted()
    {
        if (!bossIDAllInfoDict.ContainsKey(BossID))
        {
            bossIDAllInfoDict[BossID] = new Dictionary<int, TianziConfig>();
        }
        bossIDAllInfoDict[BossID][HPNum] = this;
    }
    public static bool TryGetAllInfoDictByBossID(int BossID, out Dictionary<int, TianziConfig> dict)
    {
        return bossIDAllInfoDict.TryGetValue(BossID, out dict);
    }
    public static bool TryGetTianziConfigByBossIDAndHPNum(int BossID, int HPNum, out TianziConfig tianziConfig)
    {
        tianziConfig = null;
        if (bossIDAllInfoDict.TryGetValue(BossID, out var hpDict))
        {
            if (hpDict.TryGetValue(HPNum, out tianziConfig))
            {
                return true;
            }
        }
        return false;
    }
    public static bool TryGetTianziConfigByBossIDAndMaxHP(int BossID, long MaxHP, out TianziConfig tianziConfig)
    {
        tianziConfig = null;
        if (bossIDAllInfoDict.TryGetValue(BossID, out var hpDict))
        {
            foreach (var item in hpDict.Values)
            {
                if (item.MaxHP == MaxHP)
                {
                    tianziConfig = Get(item.ID);
                    return true;
                }
            }
        }
        return false;
    }
    /// <summary>
    /// 根据传入的伤害计算当前是哪个血条,返回对应的TianziConfig
    /// </summary>
    /// <param name="BossID">Boss ID</param>
    /// <param name="damage">累计伤害值</param>
    /// <param name="tianziConfig">返回的TianziConfig</param>
    /// <returns>是否成功找到对应的血条配置</returns>
    public static bool TryGetTianziConfigByBossIDAndDamage(int BossID, ulong damage, out TianziConfig tianziConfig)
    {
        tianziConfig = null;
        if (!bossIDAllInfoDict.TryGetValue(BossID, out var hpDict))
            return false;
        // 按血条编号排序,从小到大计算累计生命值
        var sortedHPNums = hpDict.Keys.ToList();
        sortedHPNums.Sort();
        ulong accumulatedHP = 0;
        foreach (int hpNum in sortedHPNums)
        {
            if (!hpDict.TryGetValue(hpNum, out var config))
                continue;
            // 累计生命值
            accumulatedHP += (ulong)config.MaxHP;
            // 如果伤害 <= 累计生命值,说明当前血条就是这个
            if (damage <= accumulatedHP)
            {
                tianziConfig = Get(config.ID);
                return true;
            }
        }
        // 如果伤害 >= 所有血条的总生命值,返回最后一个血条
        if (sortedHPNums.Count > 0)
        {
            var lastHPNum = sortedHPNums[sortedHPNums.Count - 1];
            if (hpDict.TryGetValue(lastHPNum, out var lastConfig))
            {
                tianziConfig = Get(lastConfig.ID);
                return true;
            }
        }
        return false;
    }
    public static ulong GetCurrentHPDamage(int BossID, ulong totalDamage)
    {
        if (!bossIDAllInfoDict.TryGetValue(BossID, out var hpDict))
            return 0;
        var sortedHPNums = hpDict.Keys.ToList();
        sortedHPNums.Sort();
        ulong accumulatedHP = 0;
        ulong previousAccumulatedHP = 0;
        foreach (int hpNum in sortedHPNums)
        {
            if (!hpDict.TryGetValue(hpNum, out var config))
                continue;
            // 累计生命值
            previousAccumulatedHP = accumulatedHP;
            accumulatedHP += (ulong)config.MaxHP;
            // 如果总伤害 <= 累计生命值,说明当前血条就是这个
            if (totalDamage <= accumulatedHP)
            {
                // 对当前血条造成的伤害 = 总伤害 - 之前血条的总生命值
                ulong currentHPDamage = totalDamage - previousAccumulatedHP;
                return currentHPDamage;
            }
        }
        return totalDamage - accumulatedHP;
    }
}
Main/Config/PartialConfigs/TianziConfig.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 78c01f83db1dcb54493c13af7086637f
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Core/NetworkPackage/DTCFile/ServerPack/H03_MainCharacter/DTC0320_tagFBEnd.cs
@@ -8,5 +8,6 @@
        base.Done(vNetPack);
        H0320_tagFBEnd vNetData = vNetPack as H0320_tagFBEnd;
        BoneFieldManager.Instance.UpdateFBEnd(vNetData);
        TianziBillboradManager.Instance.UpdateFBEnd(vNetData);
    }
}
Main/Core/NetworkPackage/DTCFile/ServerPack/HB2_ActionMap.meta
New file
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e956e1ea8a1bd304890b23f7228a9e4c
folderAsset: yes
DefaultImporter:
  externalObjects: {}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Core/NetworkPackage/DTCFile/ServerPack/HB2_ActionMap/DTCB201_tagSCTianziKYInfo.cs
New file
@@ -0,0 +1,12 @@
using UnityEngine;
using System.Collections;
// B2 01 天子考验信息 #tagSCTianziKYInfo
public class DTCB201_tagSCTianziKYInfo : DtcBasic {
    public override void Done(GameNetPackBasic vNetPack) {
        base.Done(vNetPack);
        HB201_tagSCTianziKYInfo vNetData = vNetPack as HB201_tagSCTianziKYInfo;
        TianziBillboradManager.Instance.UpdateTianziKYInfo(vNetData);
    }
}
Main/Core/NetworkPackage/DTCFile/ServerPack/HB2_ActionMap/DTCB201_tagSCTianziKYInfo.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8c47281476c6caf44994a8bbb384f2a7
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Core/NetworkPackage/DataToCtl/PackageRegedit.cs
@@ -119,6 +119,7 @@
        Register(typeof(HA922_tagSCArenaMatchList), typeof(DTCA922_tagSCArenaMatchList));
        Register(typeof(HA923_tagSCArenaPlayerInfo), typeof(DTCA923_tagSCArenaPlayerInfo));
        Register(typeof(HB109_tagSCDailyTaskInfo), typeof(DTCB109_tagSCDailyTaskInfo));
        Register(typeof(HB201_tagSCTianziKYInfo), typeof(DTCB201_tagSCTianziKYInfo));
    }
    //主工程注册封包
Main/Core/NetworkPackage/ServerPack/HB2_ActionMap.meta
New file
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: a0f8b16704633e541aef7538d8ca2e6b
folderAsset: yes
DefaultImporter:
  externalObjects: {}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Core/NetworkPackage/ServerPack/HB2_ActionMap/HB201_tagSCTianziKYInfo.cs
New file
@@ -0,0 +1,25 @@
using UnityEngine;
using System.Collections;
// B2 01 天子考验信息 #tagSCTianziKYInfo
public class HB201_tagSCTianziKYInfo : GameNetPackBasic {
    public byte LineID;    //今日是哪个lineID,对应副本表的功能线路ID
    public uint HistoryHurt;    //本考验历史最大伤害,求余亿部分
    public uint HistoryHurtEx;    //本考验历史最大伤害,整除亿部分
    public uint TodayHurt;    //本考验今日最大伤害,求余亿部分
    public uint TodayHurtEx;    //本考验今日最大伤害,整除亿部分
    public HB201_tagSCTianziKYInfo () {
        _cmd = (ushort)0xB201;
    }
    public override void ReadFromBytes (byte[] vBytes) {
        TransBytes (out LineID, vBytes, NetDataType.BYTE);
        TransBytes (out HistoryHurt, vBytes, NetDataType.DWORD);
        TransBytes (out HistoryHurtEx, vBytes, NetDataType.DWORD);
        TransBytes (out TodayHurt, vBytes, NetDataType.DWORD);
        TransBytes (out TodayHurtEx, vBytes, NetDataType.DWORD);
    }
}
Main/Core/NetworkPackage/ServerPack/HB2_ActionMap/HB201_tagSCTianziKYInfo.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e11b3a9a5992d644db0e2626ac7dd9ca
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/Main.cs
@@ -83,7 +83,7 @@
        managers.Add(ArenaManager.Instance);
        managers.Add(DayMissionManager.Instance);
        managers.Add(BattlePassManager.Instance);
        managers.Add(TianziBillboradManager.Instance);
        foreach (var manager in managers)
        {
            manager.Init();
Main/System/Battle/BattleConst.cs
@@ -10,6 +10,7 @@
        typeof(StoryBossBattleWin),
        typeof(ArenaBattleWin),
        typeof(BoneFieldBattleWin),
        typeof(TianziBillboradBattleWin),
    };
    public static Dictionary<string, string> battleNameToWinName = new Dictionary<string, string>()
@@ -18,6 +19,7 @@
        { "StoryBossBattleField", "StoryBossBattleWin" },
        { "ArenaBattleField", "ArenaBattleWin" },
        { "BoneBattleField", "BoneFieldBattleWin" },
        { "TianziBillboradBattleField", "TianziBillboradBattleWin" },
    };
    public const int BattleStartEffectID = 1001; // Example effect ID for battle start
Main/System/Battle/BattleField/TianziBillboradBattleField.cs
New file
@@ -0,0 +1,142 @@
using LitJson;
using System;
using System.Collections.Generic;
using System.Linq;
public class TianziBillboradBattleField : BattleField
{
    protected JsonData extendData;
    protected MainLevelConfig levelConfig;
    public TianziBillboradBattleField(string _guid) : base(_guid)
    {
    }
    public override void Init(int MapID, int FuncLineID, JsonData _extendData,
        List<TeamBase> _redTeamList, List<TeamBase> _blueTeamList, byte turnMax)
    {
        base.Init(MapID, FuncLineID, extendData, _redTeamList, _blueTeamList, turnMax);
        int level = FuncLineID;// 关卡
        extendData = _extendData;
        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://结算奖励
                if (extendData != null && extendData.ContainsKey("totalHurt"))
                {
                    ulong totalHurt = ulong.Parse(extendData["totalHurt"].ToString());
                    TianziBillboradBattleWin.TianziDamageBarEndDataAction?.Invoke(totalHurt);
                }
                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<TianziBillboradBattleWin>())
        {
            UIManager.Instance.CloseWindow<TianziBillboradBattleWin>();
            UIManager.Instance.OpenWindow<TianziBillboradWin>();
        }
    }
    public override void Run()
    {
        if (operationAgent == null)
        {
            //防范异常
            return;
        }
        base.Run();
    }
    public override void DistributeNextPackage()
    {
        if (IsBattleFinish)
            return;
        //  不要调用base的函数
        BattleManager.Instance.DistributeNextReportPackage(guid);
    }
    public override void ShowWindow(HB424_tagSCTurnFightInit vNetData)
    {
        TianziBillboradBattleWin fsBattleWin = UIManager.Instance.GetUI<TianziBillboradBattleWin>();// as FullScreenBattleWin;
        if (null == fsBattleWin)
        {
            fsBattleWin = UIManager.Instance.OpenWindow<TianziBillboradBattleWin>();
        }
        fsBattleWin.SetBattleField(this);
        if (UIManager.Instance.IsOpened<TianziBillboradWin>())
        {
            UIManager.Instance.CloseWindow<TianziBillboradWin>();
        }
    }
    public NPCLineupConfig GetBossLineupConfig()
    {
        if (!DungeonConfig.TryGetDungeonID(MapID, FuncLineID, out int dungeonID))
            return null;
        if (!DungeonConfig.HasKey(dungeonID))
            return null;
        int[] lineupIDList = DungeonConfig.Get(dungeonID).LineupIDList;
        if (lineupIDList.IsNullOrEmpty())
            return null;
        int lineupID = lineupIDList[0];
        if (!NPCLineupConfig.HasKey(lineupID))
            return null;
        NPCLineupConfig nPCLineupConfig = NPCLineupConfig.Get(lineupID);
        return nPCLineupConfig;
    }
    public override BattleObject FindBoss()
    {
        var config = GetBossLineupConfig();
        if (config != null)
        {
            int bossId = config.BossID;
            BattleObject bo = battleObjMgr.allBattleObjDict.Values.FirstOrDefault(bo => bo.teamHero.NPCID == bossId);
            return bo;
        }
        return null;
    }
}
Main/System/Battle/BattleField/TianziBillboradBattleField.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c9799b3f52f4bd442b3952bdec14c779
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/Battle/BattleFieldFactory.cs
@@ -26,6 +26,9 @@
            case 30010:
                battleField = new BoneBattleField(guid);
                break;
            case 30020:
                battleField = new TianziBillboradBattleField(guid);
                break;
            default:
                break;
        }
Main/System/Battle/BattleObject/BattleObject.cs
@@ -162,6 +162,10 @@
    public void OnObjInfoRefresh(H0418_tagObjInfoRefresh _refreshInfo)
    {
        // 天子的挑战拦截血条逻辑
        BattleObject boss = battleField.FindBoss();
        if (boss != null && battleField.MapID == 30020 && boss.ObjID == _refreshInfo.ObjID)
            return;
        switch ((PlayerDataType)_refreshInfo.RefreshType)
        {
            case PlayerDataType.HP:
@@ -186,6 +190,10 @@
    public void ObjPropertyRefreshView(HB418_tagSCObjPropertyRefreshView vNetData)
    {
        // 天子的挑战拦截血条逻辑
        BattleObject boss = battleField.FindBoss();
        if (boss != null && battleField.MapID == 30020 && boss.ObjID == vNetData.ObjID)
            return;
        switch ((PlayerDataType)vNetData.RefreshType)
        {
            case PlayerDataType.HP:
@@ -324,7 +332,7 @@
                PushDropItems(battleHurtParam.battleDrops);
            }
            battleField.OnObjsDead(new List<HB422_tagMCTurnFightObjDead>() { battleHurtParam.deadPack });
        }
        else
        {
@@ -411,13 +419,23 @@
    protected virtual BattleDmgInfo PopDamage(BattleHurtParam battleHurtParam)
    {
        BattleDmgInfo battleDmgInfo = new BattleDmgInfo(battleField.guid, battleHurtParam);
        // 天子的挑战拦截血条逻辑
        BattleObject boss = battleField.FindBoss();
        if (boss != null && battleField.MapID == 30020 && boss.ObjID == battleHurtParam.hurtObj.ObjID)
        {
            EventBroadcast.Instance.Broadcast(EventName.BATTLE_DAMAGE_TAKEN, battleDmgInfo);
            return battleDmgInfo;
        }
        else
        {
            // 使用传入的 fromHp 和 toHp 更新血条显示
            heroInfoBar.UpdateHP(battleHurtParam.fromHp, battleHurtParam.toHp, teamHero.maxHp);
        // 使用传入的 fromHp 和 toHp 更新血条显示
        heroInfoBar.UpdateHP(battleHurtParam.fromHp, battleHurtParam.toHp, teamHero.maxHp);
            // YYL TODO 是否需要挂在在自身的follow点上
            EventBroadcast.Instance.Broadcast(EventName.BATTLE_DAMAGE_TAKEN, battleDmgInfo);
            return battleDmgInfo;
        }
        // YYL TODO 是否需要挂在在自身的follow点上
        EventBroadcast.Instance.Broadcast(EventName.BATTLE_DAMAGE_TAKEN, battleDmgInfo);
        return battleDmgInfo;
    }
    public RectTransform GetAliasTeamNode()
Main/System/Battle/TianziBillboradBattleWin.cs
New file
@@ -0,0 +1,346 @@
using System;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using LitJson;
using UnityEngine;
using UnityEngine.UI;
public class TianziBillboradBattleWin : BaseBattleWin
{
    [SerializeField] Transform transButtons;
    [SerializeField] public TianziDamageBar tianziDamageBar;
    [SerializeField] public SkillWordCell[] skillWordCells;
    [SerializeField] public BossHeadCell bossHeadCell;
    [SerializeField] public Text txtBossName;
    [SerializeField] HeroCountryComponent myCountry;
    [SerializeField] HeroCountryComponent enemyCountry;
    private BattleObject bossBattleObject = null;
    [SerializeField] public List<BattleBuffCell> buffCells;
    [SerializeField] RectTransform rectBoxEnd;
    [SerializeField] UIEffectPlayer uiEffectPlayer;
    public static Action<ulong> TianziDamageBarEndDataAction;
    protected override void OnPreOpen()
    {
        base.OnPreOpen();
        tianziDamageBar.StageUp += OnStageUp;
        tianziDamageBar.ValueChangeAction += OnValueChangeAction;
        tianziDamageBar.ChangeEndAction += OnChangeEndAction;
        //tianziDamageBar.IsLastHitUnLockEvent += OnIsLastHitUnLockEvent;
        TianziDamageBarEndDataAction += OnTianziDamageBarEndData;
        MainWin.TabChangeEvent += OnTabChangeEvent;
        EventBroadcast.Instance.AddListener<string, JsonData>(EventName.BATTLE_END, OnSettlement);
        EventBroadcast.Instance.AddListener<HB419_tagSCObjHPRefresh>(EventName.BATTLE_TIANZI_REFRESH_HP, OnUpdateHpNum);
        bool isOpenBattleChangeTab = IsOpenBattleChangeTab();
        transButtons.localPosition = new Vector3(0, isOpenBattleChangeTab ? 130 : 0, 0);
        if (isOpenBattleChangeTab)
        {
            UIManager.Instance.GetUI<MainWin>()?.CloseSubUI();
        }
        else
        {
            UIManager.Instance.CloseWindow<MainWin>();
        }
    }
    protected override void OnPreClose()
    {
        base.OnPreClose();
        tianziDamageBar.StageUp -= OnStageUp;
        tianziDamageBar.ValueChangeAction -= OnValueChangeAction;
        tianziDamageBar.ChangeEndAction += OnChangeEndAction;
        //tianziDamageBar.IsLastHitUnLockEvent += OnIsLastHitUnLockEvent;
        TianziDamageBarEndDataAction -= OnTianziDamageBarEndData;
        MainWin.TabChangeEvent -= OnTabChangeEvent;
        EventBroadcast.Instance.RemoveListener<string, JsonData>(EventName.BATTLE_END, OnSettlement);
        EventBroadcast.Instance.RemoveListener<HB419_tagSCObjHPRefresh>(EventName.BATTLE_TIANZI_REFRESH_HP, OnUpdateHpNum);
        bool isOpenBattleChangeTab = IsOpenBattleChangeTab();
        if (isOpenBattleChangeTab)
        {
            UIManager.Instance.GetUI<MainWin>()?.RestoreSubUI();
        }
        else
        {
            UIManager.Instance.OpenWindow<MainWin>();
        }
        if (bossBattleObject != null)
        {
            if (bossBattleObject.buffMgr != null)
            {
                bossBattleObject.buffMgr.onBuffChanged -= OnBuffChanged;
            }
            bossBattleObject = null;
        }
    }
    private void OnTianziDamageBarEndData(ulong obj)
    {
        tianziDamageBar.Show(obj);
    }
    private void OnStageUp(int stage)
    {
        GameObject hero = bossBattleObject.heroGo;
        if (hero == null || stage <= 1)
            return;
        uiEffectPlayer.Play();
        GameObject prefab = UIUtility.CreateWidget("TianziBillboradBox", "TianziBillboradBox");
        prefab.transform.SetParentEx(hero.transform, Vector3.zero, Quaternion.identity, Vector3.one);
        TianziBillboradBox boxAnimator = prefab.GetComponent<TianziBillboradBox>();
        if (boxAnimator != null)
        {
            Vector3 startPos = hero.transform.position;
            Vector3 endPos = rectBoxEnd.position;
            boxAnimator.StartAnimation(startPos, endPos);
        }
        else
        {
            Destroy(prefab);
        }
    }
    private void OnTabChangeEvent()
    {
        CloseWindow();
    }
    protected override void OnCreateBattleField(string guid, BattleField field)
    {
        if (field is TianziBillboradBattleField)
        {
            SetBattleField(field);
        }
    }
    protected override void RefreshSpecific()
    {
        DestroyExistingTianziBillboradBoxes();
        TianziBillboradBattleField boneField = battleField as TianziBillboradBattleField;
        if (boneField == null) return;
        NPCLineupConfig lineupConfig = boneField.GetBossLineupConfig();
        if (bossBattleObject != null)
        {
            if (bossBattleObject.buffMgr != null)
            {
                bossBattleObject.buffMgr.onBuffChanged -= OnBuffChanged;
            }
            bossBattleObject = null;
        }
        bossBattleObject = boneField.FindBoss();
        DisplaySkillWordsList(lineupConfig);
        hpB419 = 0;
        maxHpB419 = 0;
        tianziDamageBar.Init();
        if (null != bossBattleObject)
        {
            TeamHero teamHero = bossBattleObject.teamHero;
            bossHeadCell.SetTeamHero(teamHero);
            txtBossName.text = teamHero.name;
            NPCConfig npcConfig = NPCConfig.Get(teamHero.NPCID);
            bossBattleObject.buffMgr.onBuffChanged -= OnBuffChanged;
            bossBattleObject.buffMgr.onBuffChanged += OnBuffChanged;
        }
        else
        {
            bossHeadCell.SetTeamHero(null);
            txtBossName.text = string.Empty;
            Debug.LogError("找不到boss");
        }
        OnRoundChange(battleField.round, battleField.turnMax); // 确保回合显示被调用
        OnBuffChanged();
        // 获取我方(红方)队伍数据
        List<BattleObject> myTeam = battleField.battleObjMgr.GetBattleObjList(BattleCamp.Red);
        // 获取敌方(蓝方)队伍数据
        List<BattleObject> enemyTeam = battleField.battleObjMgr.GetBattleObjList(BattleCamp.Blue);
        myCountry.RefreshOnTeamCountry(GetTeamHeroList(myTeam), true);
        enemyCountry.RefreshOnTeamCountry(GetTeamHeroList(enemyTeam), true);
    }
    private void DestroyExistingTianziBillboradBoxes()
    {
        // 查找场景中所有存在的TianziBillboradBox实例
        TianziBillboradBox[] existingBoxes = FindObjectsOfType<TianziBillboradBox>();
        foreach (TianziBillboradBox box in existingBoxes)
        {
            if (box != null && box.gameObject != null)
            {
                Destroy(box.gameObject);
            }
        }
    }
    private void OnBuffChanged()
    {
        var buffList = new List<HB428_tagSCBuffRefresh>();
        if (null != bossBattleObject)
        {
            buffList = bossBattleObject.buffMgr.GetBuffList();
        }
        RefreshBuff(buffList);
    }
    private void OnSettlement(string _guid, JsonData data)
    {
        if (string.Empty == _guid)
            return;
        var battle = BattleManager.Instance.GetBattleField(_guid);
        if (battle == null)
            return;
        var battleName = battle.ToString();
        if (battleName != "TianziBillboradBattleField")
            return;
        if (data != null && data.ContainsKey("totalHurt"))
        {
            ulong totalHurt = ulong.Parse(data["totalHurt"].ToString());
            tianziDamageBar.Show(totalHurt);
        }
    }
    private void OnIsLastHitUnLockEvent()
    {
        if (bossBattleObject == null)
            return;
        bossBattleObject.teamHero.curHp = (long)hpB419;
        bossBattleObject.teamHero.maxHp = (long)maxHpB419;
        Debug.Log($"TianziDamageBar OnIsLastHitUnLockEvent hpB419 {hpB419} maxHpB419 {maxHpB419}");
    }
    ulong hpB419;
    ulong maxHpB419;
    private void OnUpdateHpNum(HB419_tagSCObjHPRefresh info)
    {
        if (bossBattleObject == null || info.ObjID != bossBattleObject.ObjID)
            return;
        ulong curHp = (ulong)GeneralDefine.GetFactValue(info.HP, info.HPEx);
        ulong maxHp = (ulong)GeneralDefine.GetFactValue(info.MaxHP, info.MaxHPEx);
        hpB419 = curHp;
        maxHpB419 = maxHp;
        //tianziDamageBar.ShowByB419(curHp, maxHp);
    }
    protected override void OnDamageTaken(BattleDmgInfo info)
    {
        base.OnDamageTaken(info);
        if (battleField == null || info.battleFieldGuid != battleField.guid)
            return;
        if (bossBattleObject != null && info.hurtObj.ObjID == bossBattleObject.ObjID)
        {
            TeamHero teamHero = bossBattleObject.teamHero;
            tianziDamageBar.Show(info);
            //tianziDamageBar.Show((ulong)teamHero.curHp, (ulong)teamHero.maxHp, info);
        }
    }
    private void OnValueChangeAction(float nowValue, int CurrentStage)
    {
        if (bossBattleObject == null || bossBattleObject.heroInfoBar == null)
            return;
        bossBattleObject.heroInfoBar.UpdateHP(nowValue);
        //Debug.Log($"TianziDamageBar nowValue {nowValue} 时间: {DateTime.Now:HH:mm:ss}");
    }
    private void OnChangeEndAction(ulong nowHunt, ulong nowHpMax)
    {
        if (bossBattleObject == null || bossBattleObject.heroInfoBar == null)
            return;
        if (nowHpMax > 0)
        {
            float percentage = Mathf.Clamp(nowHunt, 0, nowHpMax) / (float)nowHpMax;
            bossBattleObject.heroInfoBar.UpdateHP(percentage);
            //Debug.Log($"TianziDamageBar nowValue {percentage} 时间: {DateTime.Now:HH:mm:ss}");
        }
    }
    public void DisplaySkillWordsList(NPCLineupConfig lineUPConfig)
    {
        if (skillWordCells.IsNullOrEmpty())
            return;
        if (null == lineUPConfig)
            return;
        for (int i = 0; i < skillWordCells.Length; i++)
        {
            if (i < lineUPConfig.SkillIDExList.Length)
            {
                skillWordCells[i].SetActive(true);
                int skillID = lineUPConfig.SkillIDExList[i];
                skillWordCells[i].Init(skillID, () =>
                {
                    SmallTipWin.showText = Language.Get("SmallTipFomat", SkillConfig.Get(skillID)?.SkillName, SkillConfig.Get(skillID)?.Description);
                    SmallTipWin.worldPos = CameraManager.uiCamera.ScreenToWorldPoint(Input.mousePosition);
                    SmallTipWin.isDownShow = true;
                    UIManager.Instance.OpenWindow<SmallTipWin>();
                });
            }
            else
            {
                skillWordCells[i].SetActive(false);
            }
        }
    }
    public void RefreshBuff(List<HB428_tagSCBuffRefresh> datas)
    {
        if (buffCells.IsNullOrEmpty())
            return;
        for (int i = 0; i < buffCells.Count; i++)
        {
            if (i < datas.Count)
            {
                buffCells[i].SetActive(true);
                HB428_tagSCBuffRefresh buffData = datas[i];
                buffCells[i].Init(buffData, () =>
                {
                    //  点击buff图标 显示buff描述/当前身上所有buff
                });
            }
            else
            {
                buffCells[i].SetActive(false);
            }
        }
    }
    bool IsOpenBattleChangeTab()
    {
        return FuncOpen.Instance.IsFuncOpen(ArenaManager.Instance.BattleChangeTabFuncId);
    }
    List<TeamHero> GetTeamHeroList(List<BattleObject> teams)
    {
        List<TeamHero> teamHeroes = new List<TeamHero>();
        if (teams.IsNullOrEmpty())
            return teamHeroes;
        foreach (var item in teams)
        {
            teamHeroes.Add(item.teamHero);
        }
        return teamHeroes;
    }
}
Main/System/Battle/TianziBillboradBattleWin.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7e1803332c96b3d40af8e8958c6c2296
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/Battle/UIComp/BattleHeroInfoBar.cs
@@ -167,6 +167,15 @@
    }
    /// <summary>
    /// !!!临时的用于天子更新血量显示,等接口完善后删除
    /// </summary>
    public void UpdateHP(float value)
    {
        sliderHp.value = value;
        //Debug.Log("TianziDamageBar UpdateHP value:" + value);
    }
    /// <summary>
    /// 更新怒气显示
    /// </summary>
    public void UpdateXP(long fromXp, long toXp, long maxXp, bool tween = true)
Main/System/BoneField/AdsManager.cs
@@ -4,7 +4,7 @@
{
    //<广告ID,今日已领取广告奖励次数>
    private Dictionary<int, int> adsInfoDict = new Dictionary<int, int>();
    public event Action<int,int> OnAdsInfoListUpdateEvent;//ADID ADMapID
    public event Action<int, int> OnAdsInfoListUpdateEvent;//ADID ADMapID
    public override void Init()
    {
        DTC0102_tagCDBPlayer.beforePlayerDataInitializeEvent += OnBeforePlayerDataInitializeEvent;
@@ -30,16 +30,22 @@
    public void PlayAds(int ADID)
    {
        if (ADID == 1)
        switch (ADID)
        {
            int dataMapID = BoneFieldManager.Instance.DataMapID;
            if (!DungeonManager.Instance.TryGetFBInfoByMapID(dataMapID, out var fbInfo))
                return;
            SendGetReward(ADID);
            BoneFieldManager.Instance.SendBBeginFBWipeOut(dataMapID, (int)fbInfo.PassLineID);
            case 1:
                if (!DungeonManager.Instance.TryGetFBInfoByMapID(BoneFieldManager.Instance.DataMapID, out var fbInfo1))
                    return;
                SendGetReward(ADID);
                BoneFieldManager.Instance.SendBBeginFBWipeOut(BoneFieldManager.Instance.DataMapID, (int)fbInfo1.PassLineID);
                break;
            case 2:
                if (!DungeonManager.Instance.TryGetFBInfoByMapID(TianziBillboradManager.Instance.DataMapID, out var fbInfo2))
                    return;
                SendGetReward(ADID);
                BoneFieldManager.Instance.SendBBeginFBWipeOut(TianziBillboradManager.Instance.DataMapID, (int)fbInfo2.PassLineID);
                break;
        }
    }
    public int GetADCntByADID(int ADID)
Main/System/ChallengeTab/TianziBillboradTabHandler.cs
New file
@@ -0,0 +1,75 @@
using System;
using UnityEngine;
public class TianziBillboradTabHandler : BaseChallengeTabHandler
{
    protected override int GetIndex() => 3;
    protected override int GetOpenState() => 0; // 0=FuncID
    protected override int GetFuncId() => TianziBillboradManager.Instance.funcId;
    protected override int GetRedpointId() => MainRedDot.TianziBillboradRepoint;
    protected override string GetCountInfo()
    {
        int count = TianziBillboradManager.Instance.GetRemainChallageCount();
        return UIHelper.AppendColor(count > 0 ? TextColType.Green : TextColType.Red, Language.Get("Challenge01", count));
    }
    protected override Action GetOnClickAction()
    {
        return HandleBoneFieldNavigation;
    }
    private void HandleBoneFieldNavigation()
    {
        if (!FuncOpen.Instance.IsFuncOpen(GetFuncId(), true))
            return;
        UIManager.Instance.CloseWindow<ChallengeTabWin>();
        BattleField battleField = BattleManager.Instance.GetBattleFieldByMapID(TianziBillboradManager.Instance.DataMapID);
        if (battleField != null)
        {
            TianziBillboradBattleWin battleWin;
            if (!UIManager.Instance.IsOpened<TianziBillboradBattleWin>())
            {
                battleWin = UIManager.Instance.OpenWindow<TianziBillboradBattleWin>();
            }
            else
            {
                battleWin = UIManager.Instance.GetUI<TianziBillboradBattleWin>();
            }
            battleWin.SetBattleField(battleField as TianziBillboradBattleField);
        }
        else
        {
            UIManager.Instance.OpenWindow<TianziBillboradWin>();
        }
    }
    protected override void SubscribeToSpecificEvents()
    {
        DungeonManager.Instance.UpdateFBInfoChangeEvent += OnUpdateFBInfoChange;
        AdsManager.Instance.OnAdsInfoListUpdateEvent += OnAdsInfoListUpdate;
    }
    protected override void UnsubscribeFromSpecificEvents()
    {
        DungeonManager.Instance.UpdateFBInfoChangeEvent -= OnUpdateFBInfoChange;
        AdsManager.Instance.OnAdsInfoListUpdateEvent -= OnAdsInfoListUpdate;
    }
    private void OnUpdateFBInfoChange(int mapID, bool isADAddCntChange, bool isBuyAddCntChange, bool isItemAddCntChange)
    {
        if (mapID == TianziBillboradManager.Instance.DataMapID)
        {
            Refresh();
        }
    }
    private void OnAdsInfoListUpdate(int adID, int mapID)
    {
        if (mapID == TianziBillboradManager.Instance.DataMapID)
        {
            Refresh();
        }
    }
}
Main/System/ChallengeTab/TianziBillboradTabHandler.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c2c989a90e5f4d140a0ee55e2f47788c
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/Dungeon/DungeonManager.cs
@@ -10,8 +10,6 @@
    private Dictionary<int, FBInfo> fbInfoDict = new Dictionary<int, FBInfo>();
    public event Action<int> UpdateFBInfoListEvent;//int mapID
    public event Action<int, bool, bool, bool> UpdateFBInfoChangeEvent;
    public event Action<int> UpdateFBInfoListEventByADAddCnt;//int mapID  广告增加次数有更新
    public event Action<int> UpdateFBInfoListEventNotByADAddCnt;//int mapID  不是广告增加次数导致的更新
    Dictionary<int, DungeonRecord> dungeonRecords = new Dictionary<int, DungeonRecord>();
    public event Action updateDungeonBuyCnt;
Main/System/Redpoint/MainRedDot.cs
@@ -114,6 +114,7 @@
    public const int FirstChargeRepoint = 468; //首充
    public const int BoneFieldRepoint = 469; //白骨盈野
    public const int ArenaRepoint = 470; //演武场
    public const int TianziBillboradRepoint= 471; //天子的考验
    public void Register()
    {
Main/System/Settlement/BattleSettlementManager.cs
@@ -41,6 +41,9 @@
            case "BoneBattleField":
                PopupWindowsProcessor.Instance.Add(isWin ? "BoneBattleVictoryWin" : "BoneBattleFailWin", false);
                break;
            case "TianziBillboradBattleField":
                PopupWindowsProcessor.Instance.Add("TianziBillboradVictoryWin", false);
                break;
            default:
                PopupWindowsProcessor.Instance.Add(isWin ? "BattleVictoryWin" : "BattleFailWin", false);
                break;
@@ -72,6 +75,10 @@
                    UIManager.Instance.OpenWindow<BoneBattleFailWin>();
                }
                break;
            case "TianziBillboradBattleField":
                TianziBillboradManager.Instance.isSweepVictory = false;
                UIManager.Instance.OpenWindow<TianziBillboradVictoryWin>();
                break;
            default:
                if (isWin)
                {
Main/System/Settlement/TianziBillboradVictoryWin.cs
New file
@@ -0,0 +1,96 @@
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using UnityEngine;
public class TianziBillboradVictoryWin : UIBase
{
    [SerializeField] ScrollerController scroller;
    [SerializeField] TextEx txtHunt;
    bool isSweepVictory = false;
    string battleName = "TianziBillboradBattleField";
    protected override void OnPreOpen()
    {
        base.OnPreOpen();
        isSweepVictory = TianziBillboradManager.Instance.isSweepVictory;
        scroller.OnRefreshCell += OnRefreshCell;
        CreateScroller();
        Display();
    }
    protected override void OnPreClose()
    {
        base.OnPreClose();
        scroller.OnRefreshCell -= OnRefreshCell;
        TianziBillboradManager.Instance.isSweepVictory = false;
        BattleSettlementManager.Instance.WinShowOver(battleName);
    }
    private void Display()
    {
        var jsonData = BattleSettlementManager.Instance.GetBattleSettlement(battleName);
        if (isSweepVictory)
        {
            txtHunt.text = Language.Get("TianziBillborad04", UIHelper.ReplaceLargeNum(TianziBillboradManager.Instance.totalHurtSweep));
        }
        else
        {
            txtHunt.text = !jsonData.ContainsKey("totalHurt") ? string.Empty : Language.Get("TianziBillborad04", UIHelper.ReplaceLargeNum(ulong.Parse(jsonData["totalHurt"].ToString())));
        }
    }
    List<Item> showItems = new List<Item>();
    void CreateScroller()
    {
        if (isSweepVictory)
        {
            showItems = TianziBillboradManager.Instance.itemInfos;
        }
        else
        {
            var jsonData = BattleSettlementManager.Instance.GetBattleSettlement(battleName);
            if (jsonData == null)
            {
                DelayCloseWindow().Forget();
                return;
            }
            if (!jsonData.ContainsKey("itemInfo"))
            {
                return;
            }
            showItems.Clear();
            var resultStr = jsonData["itemInfo"];
            for (int i = 0; i < resultStr.Count; i++)
            {
                showItems.Add(new Item((int)resultStr[i]["ItemID"], (long)resultStr[i]["Count"]));
            }
        }
        scroller.Refresh();
        if (!showItems.IsNullOrEmpty())
        {
            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/Settlement/TianziBillboradVictoryWin.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 67d3b4a0c1a70064899aba53b673f291
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/TianziBillborad/IntensifySmoothSlider.cs
File was deleted
Main/System/TianziBillborad/TianziBillboradAwardCell.cs
New file
@@ -0,0 +1,55 @@
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class TianziBillboradAwardCell : CellView
{
    [SerializeField] ImageEx imgRank;
    [SerializeField] TextEx txtRank;
    [SerializeField] ItemCell[] itemCells;
    TianziBillboradManager model { get { return TianziBillboradManager.Instance; } }
    public void Display(int index)
    {
        Dictionary<int, int[][]> rewardDict = model.rankAwards;
        if (rewardDict.IsNullOrEmpty())
            return;
        var list = rewardDict.Keys.ToList();
        list.Sort();
        int rank = list[index];
        if (rank <= 3)
        {
            imgRank.SetActive(true);
            txtRank.SetActive(false);
            imgRank.SetSprite(StringUtility.Contact("Rank", rank));
            txtRank.text = rank.ToString();
        }
        else
        {
            imgRank.SetActive(false);
            txtRank.SetActive(true);
            int lastIndex = index - 1;
            txtRank.text = lastIndex > 0 && lastIndex < list.Count ? Language.Get("Arena15", list[lastIndex] + 1, rank) : string.Empty;
        }
        int key = list[index];
        int[][] rewardArr = rewardDict[key];
        for (int i = 0; i < itemCells.Length; i++)
        {
            var itemCell = itemCells[i];
            if (!rewardArr.IsNullOrEmpty() && i < rewardArr.Length)
            {
                int itemCellIndex = i;
                itemCell.SetActive(true);
                itemCell.Init(new ItemCellModel(rewardArr[i][0], true, rewardArr[i][1]));
                itemCell.button.SetListener(() => ItemTipUtility.Show(rewardArr[itemCellIndex][0], true));
            }
            else
            {
                itemCell.SetActive(false);
            }
        }
    }
}
Main/System/TianziBillborad/TianziBillboradAwardCell.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8bd24f1d85f3c204696199cade01645b
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/TianziBillborad/TianziBillboradBossHead.cs
New file
@@ -0,0 +1,79 @@
using System;
using UnityEngine;
public class TianziBillboradBossHead : MonoBehaviour
{
    [SerializeField] RectTransform rectTransform;
    [SerializeField] ImageEx imgQuality;
    [SerializeField] ImageEx imgHeadIcon;
    [SerializeField] TextEx txtTime;
    private bool isTodayBoss = false;
    TianziBillboradManager model { get { return TianziBillboradManager.Instance; } }
    public void Display(int bossId)
    {
        if (!NPCConfig.HasKey(bossId))
            return;
        NPCConfig npcConfig = NPCConfig.Get(bossId);
        int heroID = npcConfig.RelatedHeroID;
        if (!HeroConfig.HasKey(heroID))
            return;
        var heroConfig = HeroConfig.Get(heroID);
        int skinID = heroConfig.SkinIDList[0];
        if (!HeroSkinConfig.HasKey(skinID))
            return;
        if (!model.TryGetBossConfig(model.DataMapID, model.todayLineID, out DungeonConfig dungeonConfig, out NPCLineupConfig npcLineupConfig, out NPCConfig npcConfigToday))
            return;
        isTodayBoss = npcConfigToday.NPCID == bossId;
        // --- 设置尺寸 ---
        imgQuality.rectTransform.sizeDelta = isTodayBoss ? new Vector2(104, 104) : new Vector2(94, 94);
        rectTransform.sizeDelta = isTodayBoss ? new Vector2(104, 104) : new Vector2(94, 94);
        // --- 设置图像和状态 ---
        var heroSkinConfig = HeroSkinConfig.Get(skinID);
        imgQuality.SetSprite("heroheadBG" + heroConfig.Quality);
        imgQuality.gray = !isTodayBoss;
        var sprite = UILoader.LoadSprite("HeroHead", heroSkinConfig.SquareIcon);
        if (sprite == null)
        {
            // 内网未配置时
            imgHeadIcon.SetSprite("herohead_default");
        }
        else
        {
            imgHeadIcon.overrideSprite = sprite;
        }
        imgHeadIcon.gray = !isTodayBoss;
        txtTime.SetActive(isTodayBoss);
        if (isTodayBoss)
        {
            UpdateTimer();
        }
    }
    public void UpdateTimer()
    {
        if (!isTodayBoss)
            return;
        DateTime endDay = TimeUtility.ServerNow.AddDays(1).Date;
        TimeSpan remainingTime = endDay - TimeUtility.ServerNow;
        int remainingSeconds = (int)remainingTime.TotalSeconds;
        if (remainingSeconds > 0)
        {
            string countdownText = TimeUtility.SecondsToHMS(remainingSeconds);
            txtTime.text = countdownText;
        }
        else
        {
            // 倒计时结束,隐藏文本
            txtTime.SetActive(false);
        }
    }
}
Main/System/TianziBillborad/TianziBillboradBossHead.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0e9e4bca577e25b4e8b2a684125f7d46
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/TianziBillborad/TianziBillboradBox.cs
New file
@@ -0,0 +1,247 @@
using UnityEngine;
using DG.Tweening;
/// <summary>
/// 控制天子看板战箱子(回血时)掉落和收集动画的组件。
/// 动画序列: 抛射弧形 -> 多次弹跳 -> 等待 -> 飞向收集点 -> 销毁
/// </summary>
[RequireComponent(typeof(RectTransform))]
public class TianziBillboradBox : MonoBehaviour
{
    private RectTransform rectTransform;
    [Header("1. 抛射阶段 (从Boss位置)")]
    [SerializeField]
    [Tooltip("初始位置的X/Y偏移量(像素单位)。箱子将从[Boss位置 + 偏移]开始抛射。")]
    Vector2 initialPositionOffset = Vector2.zero; // NEW: 初始位置偏移
    [SerializeField]
    [Tooltip("箱子抛射的持续时间(秒)。")]
    float throwDuration = 0.6f;
    [SerializeField]
    [Tooltip("抛射的缓动动画类型。\n" +
             "Ease.OutQuad: 模拟抛物线,开始快,落地慢。")]
    Ease throwEase = Ease.OutQuad;
    [SerializeField]
    [Tooltip("抛射的基础水平距离(像素单位)。")]
    float throwHorizontalDistance = 200f;
    [SerializeField]
    [Tooltip("抛射距离的随机变化范围(百分比)。")]
    [Range(0f, 1f)]
    float throwDistanceRandomness = 0.3f;
    [SerializeField]
    [Tooltip("抛射的弧形最高点,相对于起点的高度(像素单位)。")]
    float throwArcHeight = 150f;
    [SerializeField]
    [Tooltip("抛射落地的最小垂直距离(像素单位),即比起点低多少。")]
    float minThrowVerticalDrop = 120f;
    [SerializeField]
    [Tooltip("抛射落地的最大垂直距离(像素单位),即比起点低多少。")]
    float maxThrowVerticalDrop = 180f;
    [Header("2. 弹跳阶段")]
    [SerializeField]
    [Tooltip("弹跳次数(1-3次)。")]
    [Range(1, 3)]
    int bounceCount = 2;
    [SerializeField]
    [Tooltip("第一次弹跳的高度(像素单位)。")]
    float firstBounceHeight = 80f;
    [SerializeField]
    [Tooltip("第二次弹跳的高度(像素单位)。")]
    float secondBounceHeight = 50f;
    [SerializeField]
    [Tooltip("第三次弹跳的高度(像素单位)。")]
    float thirdBounceHeight = 30f;
    [SerializeField]
    [Tooltip("每次弹跳的基础水平距离(像素单位)。")]
    float bounceHorizontalDistance = 100f;
    [SerializeField]
    [Tooltip("弹跳距离的随机变化范围(百分比)。")]
    [Range(0f, 1f)]
    float bounceDistanceRandomness = 0.4f;
    [SerializeField]
    [Tooltip("每次弹跳的持续时间(秒)。")]
    float bounceDuration = 0.3f;
    [SerializeField]
    [Tooltip("弹跳的缓动动画类型。")]
    Ease bounceEase = Ease.OutQuad;
    [Header("3. 等待阶段")]
    [SerializeField]
    [Tooltip("弹跳停止后等待的时间(秒)。")]
    float waitAfterBounce = 0.5f;
    [Header("4. 收集移动阶段 (飞向终点)")]
    [SerializeField]
    [Tooltip("箱子从地面移动到最终收集位置(rectBoxEnd)所需的时间(秒)。")]
    float moveDuration = 0.4f;
    [SerializeField]
    [Tooltip("移动时的缓动动画类型。\n" +
             "Ease.InSine: 实现先慢后快的加速效果。")]
    Ease moveEase = Ease.InSine;
    // ----------------------------------------------------------------------
    private void Awake()
    {
        rectTransform = GetComponent<RectTransform>();
    }
    /// <summary>
    /// 启动箱子动画序列
    /// </summary>
    /// <param name="startWorldPos">动画开始的世界坐标(例如 Boss 头像位置)</param>
    /// <param name="endWorldPos">动画结束的世界坐标(rectBoxEnd 的位置)</param>
    public void StartAnimation(Vector3 startWorldPos, Vector3 endWorldPos)
    {
        /// Debug.Log($"TianziBillboradBox startWorldPos {startWorldPos} endWorldPos {endWorldPos}");
        if (rectTransform == null)
        {
            rectTransform = GetComponent<RectTransform>();
        }
        // --- 0. 在开始动画前,将箱子移动到boss的父物体 ---
        Transform currentParent = transform.parent;
        if (currentParent != null)
        {
            Transform bossParent = currentParent.parent; // boss的父物体
            if (bossParent != null)
            {
                transform.SetParent(bossParent);
                transform.localScale = Vector3.one; // 将scale设置为1
            }
        }
        // --- 1. 计算并设置初始位置 (应用偏移) ---
        Vector3 actualStartPos = new Vector3(
            startWorldPos.x + initialPositionOffset.x / 100f,
            startWorldPos.y + initialPositionOffset.y / 100f,
            startWorldPos.z
        );
        rectTransform.position = actualStartPos; // MODIFIED: 使用偏移后的位置
        gameObject.SetActive(true);
        // --- 2. 随机选择抛射方向(左或右)和角度 ---
        bool throwRight = Random.Range(0, 2) == 1;
        float horizontalDirection = throwRight ? 1f : -1f;
        // 添加随机角度偏移
        float angleOffset = Random.Range(-45f, 45f);
        float angleRad = angleOffset * Mathf.Deg2Rad;
        // 计算带随机性的抛射距离
        float randomThrowDistance = throwHorizontalDistance *
            (1f + Random.Range(-throwDistanceRandomness, throwDistanceRandomness));
        // --- 3. 计算抛射终点位置 ---
        //计算随机的垂直下落距离
        float randomVerticalDrop = Random.Range(minThrowVerticalDrop, maxThrowVerticalDrop);
        Vector3 throwEndPos = new Vector3(
            actualStartPos.x + randomThrowDistance / 100 * Mathf.Cos(angleRad) * horizontalDirection, // MODIFIED
            actualStartPos.y - randomVerticalDrop / 100, // MODIFIED
            actualStartPos.z
        );
        // --- 4. 计算抛射控制点(用于弧形轨迹)---
        Vector3 controlPoint = new Vector3(
            actualStartPos.x + randomThrowDistance / 100 * Mathf.Cos(angleRad) * horizontalDirection * 0.5f, // MODIFIED
            actualStartPos.y + throwArcHeight / 100, // MODIFIED
            actualStartPos.z
        );
        // --- 5. 创建一个动画序列 ---
        Sequence animSequence = DOTween.Sequence();
        // --- 6. 抛射阶段(弧形轨迹)---
        animSequence.Append(
            rectTransform.DOPath(
                new Vector3[] { actualStartPos, controlPoint, throwEndPos }, // MODIFIED
                throwDuration,
                PathType.CatmullRom
            ).SetEase(throwEase)
        );
        // --- 7. 弹跳阶段(向抛射方向继续跳1-3次)---
        Vector3 currentPos = throwEndPos;
        int actualBounceCount = Random.Range(1, bounceCount + 1); // 随机1-3次
        for (int i = 0; i < actualBounceCount; i++)
        {
            // 计算当前弹跳的高度
            float currentBounceHeight = i switch
            {
                0 => firstBounceHeight,
                1 => secondBounceHeight,
                _ => thirdBounceHeight
            };
            // 计算带随机性的弹跳距离
            float randomBounceDistance = bounceHorizontalDistance *
                (1f + Random.Range(-bounceDistanceRandomness, bounceDistanceRandomness));
            // 计算弹跳终点位置(继续向抛射方向移动)
            Vector3 bounceEndPos = new Vector3(
                currentPos.x + randomBounceDistance / 100 * horizontalDirection,
                currentPos.y,
                currentPos.z
            );
            // 弹跳控制点(弧形轨迹)
            Vector3 bounceControlPoint = new Vector3(
                currentPos.x + randomBounceDistance / 100 * horizontalDirection * 0.5f,
                currentPos.y + currentBounceHeight / 100,
                currentPos.z
            );
            // 添加弹跳动画(弧形轨迹)
            animSequence.Append(
                rectTransform.DOPath(
                    new Vector3[] { currentPos, bounceControlPoint, bounceEndPos },
                    bounceDuration,
                    PathType.CatmullRom
                ).SetEase(bounceEase)
            );
            currentPos = bounceEndPos; // 更新当前位置
        }
        // --- 8. 等待阶段 ---
        animSequence.AppendInterval(waitAfterBounce);
        // --- 9. 移动到终点 ---
        animSequence.Append(
            rectTransform.DOMove(endWorldPos, moveDuration)
                .SetEase(moveEase)
        );
        // --- 10. 动画播放完毕后销毁 ---
        animSequence.OnComplete(() =>
        {
            if (this != null && gameObject != null)
            {
                Destroy(gameObject);
            }
        });
        // --- 11. 关联生命周期 ---
        animSequence.SetTarget(this);
    }
}
Main/System/TianziBillborad/TianziBillboradBox.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bf7cf077ef7274a4d998cf2909992027
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/TianziBillborad/TianziBillboradManager.cs
New file
@@ -0,0 +1,295 @@
using System;
using System.Collections.Generic;
using System.Linq;
using LitJson;
public class TianziBillboradManager : GameSystemManager<TianziBillboradManager>
{
    public readonly int rankType = 2;    // 榜单类型
    public readonly int funcId = 39;    // 功能Id
    public readonly int DataMapID = 30020;
    public int nowTabIndex;     // 0 排行榜 1 奖励
    public byte todayLineID;    //今日是哪个lineID,对应副本表的功能线路ID
    public ulong historyHurt;    //本考验历史最大伤害
    public ulong todayHurt;    //本考验今日最大伤害
    public bool isSkipSweepTip = false;
    public event Action UpdateTianziKYInfoExent;
    public Dictionary<int, int[][]> rankAwards;// 每日排行奖励 {"名次":[[物品ID, 个数,是否拍品], ...], ...} 配置的名次key,自动按小于等于对应名次给奖励
    public Redpoint parentRedpoint = new Redpoint(MainRedDot.MainChallengeRedpoint, MainRedDot.TianziBillboradRepoint);
    public override void Init()
    {
        DTC0102_tagCDBPlayer.beforePlayerDataInitializeEvent += OnBeforePlayerDataInitializeEvent;
        DungeonManager.Instance.UpdateFBInfoChangeEvent += OnUpdateFBInfoChangeEvent;
        AdsManager.Instance.OnAdsInfoListUpdateEvent += OnAdsInfoListUpdateEvent;
        FuncOpen.Instance.OnFuncStateChangeEvent += OnFuncStateChangeEvent;
        TimeMgr.Instance.OnDayEvent += OnDayEvent;
        UpdateTianziKYInfoExent += OnUpdateTianziKYInfoExent;
        FuncConfigConfig config = FuncConfigConfig.Get("TianziBillboradAward");
        rankAwards = ConfigParse.ParseIntArray2Dict(config.Numerical1);
    }
    public override void Release()
    {
        DTC0102_tagCDBPlayer.beforePlayerDataInitializeEvent -= OnBeforePlayerDataInitializeEvent;
        DungeonManager.Instance.UpdateFBInfoChangeEvent -= OnUpdateFBInfoChangeEvent;
        AdsManager.Instance.OnAdsInfoListUpdateEvent -= OnAdsInfoListUpdateEvent;
        FuncOpen.Instance.OnFuncStateChangeEvent -= OnFuncStateChangeEvent;
        TimeMgr.Instance.OnDayEvent -= OnDayEvent;
        UpdateTianziKYInfoExent -= OnUpdateTianziKYInfoExent;
    }
    private void OnBeforePlayerDataInitializeEvent()
    {
        nowTabIndex = 0;
        todayLineID = 0;
        historyHurt = 0;
        todayHurt = 0;
        isSkipSweepTip = false;
    }
    private void OnUpdateTianziKYInfoExent()
    {
        UpdateRedPoint();
    }
    private void OnFuncStateChangeEvent(int obj)
    {
        if (obj != funcId)
            return;
        UpdateRedPoint();
    }
    private void OnDayEvent()
    {
        UpdateRedPoint();
    }
    private void OnUpdateFBInfoChangeEvent(int mapID, bool isADAddCntChange, bool isBuyAddCntChange, bool isItemAddCntChange)
    {
        int dataMapID = DataMapID;
        if (mapID != dataMapID)
            return;
        if (isADAddCntChange)
            return;
        UpdateRedPoint();
    }
    private void OnAdsInfoListUpdateEvent(int id, int mapId)
    {
        if (mapId != DataMapID)
            return;
        UpdateRedPoint();
    }
    public void UpdateRedPoint()
    {
        parentRedpoint.state = RedPointState.None;
        if (!FuncOpen.Instance.IsFuncOpen(funcId))
            return;
        int remainChallageCount = GetRemainChallageCount();
        if (remainChallageCount > 0)
        {
            parentRedpoint.state = RedPointState.Simple;
        }
    }
    public int GetRemainChallageCount()
    {
        if (!DungeonManager.Instance.TryGetFBInfoByMapID(DataMapID, out FBInfo fbInfo))
            return 0;
        if (!DungeonOpenTimeConfig.HasKey(DataMapID))
            return 0;
        DungeonOpenTimeConfig dungeonOpenTimeConfig = DungeonOpenTimeConfig.Get(DataMapID);
        int baseCount = dungeonOpenTimeConfig.DayTimes + dungeonOpenTimeConfig.PayCntMax;
        int realMaxCount = baseCount + fbInfo.ADAddCnt + fbInfo.BuyAddCnt + fbInfo.ItemAddCnt;
        return realMaxCount - fbInfo.EnterCnt;
    }
    public List<int> GetBossIDList(int dataMapID)
    {
        if (!DungeonConfig.TryGetDictByMapID(dataMapID, out Dictionary<int, int> dict))
            return new List<int>();
        List<int> list = dict.Keys.ToList();
        list.Sort();
        List<int> res = new List<int>();
        for (int i = 0; i < list.Count; i++)
        {
            int dungeonId = dict[list[i]];
            int lineupID = DungeonConfig.Get(dungeonId).LineupIDList[0];
            if (!NPCLineupConfig.HasKey(lineupID))
                continue;
            NPCLineupConfig npcLineupConfig = NPCLineupConfig.Get(lineupID);
            int bossId = npcLineupConfig.BossID;
            if (!res.Contains(bossId))
                res.Add(bossId);
        }
        return res;
    }
    public bool TryGetADAwardConfigByMapId(int dataMapID, out ADAwardConfig adAwardConfig)
    {
        adAwardConfig = null;
        if (!ADAwardConfig.TryGetADIDByADMapID(dataMapID, out int adID) || !ADAwardConfig.HasKey(adID))
            return false;
        adAwardConfig = ADAwardConfig.Get(adID);
        return true;
    }
    public bool TryGetBossConfig(int dataMapID, int lineID, out DungeonConfig dungeonConfig, out NPCLineupConfig npcLineupConfig, out NPCConfig npcConfig)
    {
        dungeonConfig = null;
        npcLineupConfig = null;
        npcConfig = null;
        if (!DungeonConfig.TryGetDungeonID(dataMapID, lineID, out int dungeonID) || !DungeonConfig.HasKey(dungeonID))
            return false;
        dungeonConfig = DungeonConfig.Get(dungeonID);
        int lineupID = dungeonConfig.LineupIDList[0];
        if (!NPCLineupConfig.HasKey(lineupID))
            return false;
        npcLineupConfig = NPCLineupConfig.Get(lineupID);
        int bossId = npcLineupConfig.BossID;
        if (!NPCConfig.HasKey(bossId))
            return false;
        npcConfig = NPCConfig.Get(bossId);
        return true;
    }
    public int[][] GetPossibleRewards(int bossID)
    {
        if (!TianziConfig.TryGetAllInfoDictByBossID(bossID, out Dictionary<int, TianziConfig> dict))
            return new int[0][];
        List<int> itemIds = new List<int>();
        foreach (var item in dict.Values)
        {
            int[][] award = item.RandWeightItemList;
            for (int i = 0; i < award.Length; i++)
            {
                int itemId = award[i][1];
                if (!itemIds.Contains(itemId))
                {
                    itemIds.Add(itemId);
                }
            }
        }
        // 根据物品品质排序,品质高的排在前面
        itemIds.Sort((itemId1, itemId2) =>
        {
            var itemConfig1 = ItemConfig.Get(itemId1);
            var itemConfig2 = ItemConfig.Get(itemId2);
            if (itemConfig1 == null && itemConfig2 == null) return 0;
            if (itemConfig1 == null) return -1;
            if (itemConfig2 == null) return 1;
            return itemConfig2.ItemColor.CompareTo(itemConfig1.ItemColor);
        });
        // 转换为 int[itemID][数量(0)] 格式
        int[][] result = new int[itemIds.Count][];
        for (int i = 0; i < itemIds.Count; i++)
        {
            result[i] = new int[] { itemIds[i], 0 };
        }
        return result;
    }
    /// <summary>
    /// 计算扫荡能获得箱子数量
    /// </summary>
    /// <param name="damage">伤害值</param>
    /// <returns>能获得的箱子数量</returns>
    public int CalculateSweepBoxCount(ulong damage)
    {
        if (!TryGetBossConfig(DataMapID, todayLineID, out DungeonConfig dungeonConfig, out NPCLineupConfig npcLineupConfig, out NPCConfig npcConfig))
            return 0;
        int bossID = npcLineupConfig.BossID;
        if (!TianziConfig.TryGetAllInfoDictByBossID(bossID, out Dictionary<int, TianziConfig> hpDict))
            return 0;
        int boxCount = 0;
        ulong accumulatedHP = 0;
        // 按血条编号排序,从小到大计算累计生命值
        var sortedHPNums = hpDict.Keys.ToList();
        sortedHPNums.Sort();
        foreach (int hpNum in sortedHPNums)
        {
            if (hpDict.TryGetValue(hpNum, out TianziConfig tianziConfig))
            {
                // 累计生命值
                accumulatedHP += (ulong)tianziConfig.MaxHP;
                // 如果伤害 >= 累计生命值,则获得一个箱子
                if (damage >= accumulatedHP)
                {
                    boxCount++;
                }
                else
                {
                    // 如果伤害不足以获得当前血条的箱子,则停止计算
                    break;
                }
            }
        }
        return boxCount;
    }
    public List<Item> itemInfos = new List<Item>();
    public bool isSweepVictory = false;
    public ulong totalHurtSweep;
    public ulong todayHurtTotalSweep;
    public void UpdateFBEnd(H0320_tagFBEnd vNetData)
    {
        if (vNetData.Msg == null)
            return;
        JsonData jsonData = JsonMapper.ToObject(vNetData.Msg);
        int isSweep = int.Parse(jsonData["isSweep"].ToString());
        int dataMapID = int.Parse(jsonData["dataMapID"].ToString());
        totalHurtSweep = ulong.Parse(jsonData["totalHurt"].ToString());
        int lineID = int.Parse(jsonData["lineID"].ToString());
        todayHurtTotalSweep = ulong.Parse(jsonData["todayHurtTotal"].ToString());
        int isPass = int.Parse(jsonData["isPass"].ToString());
        if (dataMapID != DataMapID)
            return;
        isSweepVictory = true;
        itemInfos.Clear();
        if (jsonData["itemInfo"] != null && jsonData["itemInfo"].IsArray)
        {
            for (int i = 0; i < jsonData["itemInfo"].Count; i++)
            {
                JsonData itemData = jsonData["itemInfo"][i];
                Item itemInfo = new Item((int)itemData["ItemID"], (long)itemData["ItemID"]);
                itemInfos.Add(itemInfo);
            }
        }
        isSweepVictory = true;
        UIManager.Instance.OpenWindow<TianziBillboradVictoryWin>();
    }
    public void SendBBeginFBWipeOut()
    {
        CA505_tagCMBeginFBWipeOut pack = new CA505_tagCMBeginFBWipeOut();
        pack.MapID = (uint)DataMapID;
        pack.LineID = (ushort)todayLineID;
        pack.Cnt = 1;
        GameNetSystem.Instance.SendInfo(pack);
    }
    public void UpdateTianziKYInfo(HB201_tagSCTianziKYInfo vNetData)
    {
        if (vNetData == null)
            return;
        todayLineID = vNetData.LineID;
        historyHurt = (ulong)vNetData.HistoryHurtEx * 100000000 + (ulong)vNetData.HistoryHurt;
        todayHurt = (ulong)vNetData.TodayHurtEx * 100000000 + (ulong)vNetData.TodayHurt;
        UpdateTianziKYInfoExent?.Invoke();
    }
}
Main/System/TianziBillborad/TianziBillboradManager.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: f142b2e5ad3e5494f97ad68a5ccb95cd
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/TianziBillborad/TianziBillboradRankWin.cs
New file
@@ -0,0 +1,164 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class TianziBillboradRankWin : FunctionsBaseWin
{
    [Header("通用部分")]
    [SerializeField] Transform transRank;
    [SerializeField] Transform transAward;
    [SerializeField] ButtonEx btnClose;
    [SerializeField] TextEx txtTitle;
    [SerializeField] PlayerRankCell myRankCell;
    [HideInInspector] public string valueFormat = "{0}";
    [Header("奖励")]
    [SerializeField] TextEx txtCountdown;
    [SerializeField] ScrollerController scrAward;
    [Header("排行")]
    [SerializeField] List<PlayerTop3Cell> playerTop3Cells;
    [SerializeField] ScrollerController scrollerController;
    [HideInInspector] public int groupValue1 = 0;   //一般用于跨服
    [HideInInspector] public int groupValue2 = 0;   //一般用于跨服
    TianziBillboradManager model { get { return TianziBillboradManager.Instance; } }
    protected override void InitComponent()
    {
        base.InitComponent();
        btnClose.SetListener(CloseWindow);
    }
    protected override void OnPreOpen()
    {
        base.OnPreOpen();
        tabButtons[functionOrder].SelectBtn(true);
        RankModel.Instance.ResetQueryParam();
        RankModel.Instance.QueryRankByPage(model.rankType, watchID: (int)PlayerDatas.Instance.baseData.PlayerID);
        RankModel.Instance.onRankRefresh += OnRankRefresh;
        GlobalTimeEvent.Instance.secondEvent += OnSecondEvent;
        scrollerController.OnRefreshCell += OnRefreshCell;
        scrAward.OnRefreshCell += OnRefreshAwardCell;
        model.UpdateTianziKYInfoExent += OnUpdateTianziKYInfoExent;
        Display();
    }
    protected override void OnPreClose()
    {
        base.OnPreClose();
        model.UpdateTianziKYInfoExent -= OnUpdateTianziKYInfoExent;
        RankModel.Instance.onRankRefresh -= OnRankRefresh;
        GlobalTimeEvent.Instance.secondEvent -= OnSecondEvent;
        scrollerController.OnRefreshCell -= OnRefreshCell;
        scrAward.OnRefreshCell -= OnRefreshAwardCell;
    }
    private void OnUpdateTianziKYInfoExent()
    {
        Refresh();
    }
    private void OnSecondEvent()
    {
        DateTime endDay = TimeUtility.ServerNow.AddDays(1).Date;
        TimeSpan remainingTime = endDay - TimeUtility.ServerNow;
        int remainingSeconds = (int)remainingTime.TotalSeconds;
        if (remainingSeconds > 0)
        {
            string countdownText = TimeUtility.SecondsToHMS(remainingSeconds);
            txtCountdown.SetActive(true);
            txtCountdown.text = Language.Get("Arena14", countdownText);
        }
        else
        {
            txtCountdown.SetActive(false);
        }
    }
    void OnRankRefresh(int type)
    {
        Refresh();
    }
    protected override void OpenSubUIByTabIndex()
    {
        TianziBillboradManager.Instance.nowTabIndex = functionOrder;
        transRank.SetActive(functionOrder == 0);
        transAward.SetActive(functionOrder == 1);
        Display();
    }
    private void OnRefreshAwardCell(ScrollerDataType type, CellView cell)
    {
        var _cell = cell.GetComponent<TianziBillboradAwardCell>();
        _cell?.Display(cell.index);
    }
    void Refresh()
    {
        ShowTop3();
        scrollerController.m_Scorller.RefreshActiveCellViews();
        DisplayMyRank();
    }
    private void Display()
    {
        txtTitle.text = Language.Get(functionOrder == 0 ? "L1118" : "Arena04");
        if (functionOrder == 0)
        {
            ShowTop3();
            CreateScroller();
        }
        else
        {
            CreateAwardScroller();
            OnSecondEvent();
        }
        DisplayMyRank();
    }
    void CreateScroller()
    {
        scrollerController.Refresh();
        var cnt = RankModel.Instance.GetRankShowMaxCnt(model.rankType);
        for (int i = 3; i < cnt; i++)
        {
            scrollerController.AddCell(ScrollerDataType.Header, i);
        }
        scrollerController.Restart();
    }
    void OnRefreshCell(ScrollerDataType type, CellView cell)
    {
        var _cell = cell.GetComponent<PlayerRankCell>();
        _cell.Display(model.rankType, cell.index + 1, valueFormat);
        RankModel.Instance.ListenRankPage(model.rankType, cell.index, groupValue1, groupValue2);
    }
    private void CreateAwardScroller()
    {
        scrAward.Refresh();
        Dictionary<int, int[][]> rewardDict = model.rankAwards;
        if (!rewardDict.IsNullOrEmpty())
        {
            var list = rewardDict.Keys.ToList();
            list.Sort();
            for (int i = 0; i < list.Count; i++)
            {
                CellInfo cellInfo = new CellInfo();
                scrAward.AddCell(ScrollerDataType.Header, i, cellInfo);
            }
        }
        scrAward.Restart();
    }
    private void DisplayMyRank()
    {
        myRankCell.Display(model.rankType, 0, valueFormat);
    }
    void ShowTop3()
    {
        for (int i = 0; i < playerTop3Cells.Count; i++)
        {
            playerTop3Cells[i].Display(model.rankType, i + 1);
        }
    }
}
Main/System/TianziBillborad/TianziBillboradRankWin.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 0cee596ea898c8d418ecf8baada9df12
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/TianziBillborad/TianziBillboradSweepTipWin.cs
New file
@@ -0,0 +1,48 @@
using UnityEngine;
using UnityEngine.UI;
public class TianziBillboradSweepTipWin : UIBase
{
    [SerializeField] ButtonEx btnOK;
    [SerializeField] ButtonEx btnClose;
    [SerializeField] TextEx txtTodayHurt;
    [SerializeField] TextEx txtBoxCount;
    [SerializeField] Toggle toggle;
    TianziBillboradManager model { get { return TianziBillboradManager.Instance; } }
    protected override void InitComponent()
    {
        base.InitComponent();
        btnClose.SetListener(CloseWindow);
        btnOK.SetListener(() =>
        {
            int remainChallageCount = model.GetRemainChallageCount();
            if (remainChallageCount <= 0)
            {
                SysNotifyMgr.Instance.ShowTip("TianziBillborad02");
                return;
            }
            model.SendBBeginFBWipeOut();
            CloseWindow();
        });
        toggle.onValueChanged.AddListener(OnValueChanged);
    }
    protected override void OnPreOpen()
    {
        base.OnPreOpen();
        Display();
    }
    public void Display()
    {
        toggle.isOn = false;
        txtTodayHurt.text = Language.Get("TianziBillborad05", UIHelper.ReplaceLargeNum(model.historyHurt));
        int boxCount = model.CalculateSweepBoxCount(model.todayHurt);
        txtBoxCount.text = UIHelper.AppendColor(boxCount > 0 ? TextColType.Green : TextColType.Red, Language.Get("TianziBillborad07", boxCount));
    }
    private void OnValueChanged(bool value)
    {
        model.isSkipSweepTip = value;
    }
}
Main/System/TianziBillborad/TianziBillboradSweepTipWin.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: b393705c8be17b542b181b3ed5b0f0ce
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/TianziBillborad/TianziBillboradWin.cs
New file
@@ -0,0 +1,265 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class TianziBillboradWin : UIBase
{
    [SerializeField] float modelSize;
    [SerializeField] ButtonEx btnClose;
    [SerializeField] ButtonEx btnRank;
    [SerializeField] ButtonEx btnAds;
    [SerializeField] ButtonEx btnChallenge1;
    [SerializeField] ButtonEx btnChallenge2;
    [SerializeField] ButtonEx btnSweep;
    [SerializeField] ImageEx imgChallengeRed1;
    [SerializeField] ImageEx imgChallengeRed2;
    [SerializeField] ImageEx imgSweepRed;
    [SerializeField] TextEx txtBossName;
    [SerializeField] UIHeroController bossModel;
    [SerializeField] UIEffectPlayer uiEffectPlayer;
    [SerializeField] List<SkillWordCell> skillWords;
    [SerializeField] List<ItemCell> possibleRewards;
    [SerializeField] TextEx txtTodayChallengeCount;
    [SerializeField] TextEx txtTodayAdsCount;
    [SerializeField] TextEx txtHistoryHurt;
    [SerializeField] TextEx txtTodayHurt;
    [SerializeField] List<TianziBillboradBossHead> bossHeads;
    [SerializeField] RectTransform layoutGroupRect;
    TianziBillboradManager model { get { return TianziBillboradManager.Instance; } }
    DungeonManager dungeonModel { get { return DungeonManager.Instance; } }
    AdsManager adsModel { get { return AdsManager.Instance; } }
    TimeMgr timeModel { get { return TimeMgr.Instance; } }
    protected override void InitComponent()
    {
        base.InitComponent();
        btnClose.SetListener(CloseWindow);
        btnRank.SetListener(() => { UIManager.Instance.OpenWindow<TianziBillboradRankWin>(); });
        btnAds.SetListener(() =>
        {
            if (!model.TryGetADAwardConfigByMapId(model.DataMapID, out ADAwardConfig adAwardConfig))
                return;
            adsModel.PlayAds(adAwardConfig.ADID);
        });
        btnChallenge1.SetListener(OnClickChallenge);
        btnChallenge2.SetListener(OnClickChallenge);
        btnSweep.SetListener(() =>
        {
            if (model.isSkipSweepTip)
            {
                int remainChallageCount = model.GetRemainChallageCount();
                if (remainChallageCount <= 0)
                {
                    SysNotifyMgr.Instance.ShowTip("TianziBillborad02");
                    return;
                }
                model.SendBBeginFBWipeOut();
            }
            else
            {
                UIManager.Instance.OpenWindow<TianziBillboradSweepTipWin>();
            }
        });
    }
    void OnClickChallenge()
    {
        int remainChallageCount = model.GetRemainChallageCount();
        if (remainChallageCount <= 0)
        {
            SysNotifyMgr.Instance.ShowTip("TianziBillborad01");
            return;
        }
        BattleManager.Instance.SendTurnFight((uint)model.DataMapID, model.todayLineID);
    }
    protected override void OnPreOpen()
    {
        base.OnPreOpen();
        model.UpdateTianziKYInfoExent += OnUpdateTianziKYInfoExent;
        dungeonModel.UpdateFBInfoListEvent += OnUpdateFBInfoListEvent;
        adsModel.OnAdsInfoListUpdateEvent += OnAdsInfoListUpdateEvent;
        timeModel.OnDayEvent += OnDayEvent;
        GlobalTimeEvent.Instance.secondEvent += OnSecondEvent;
        Display();
    }
    protected override void OnPreClose()
    {
        base.OnPreClose();
        model.UpdateTianziKYInfoExent -= OnUpdateTianziKYInfoExent;
        dungeonModel.UpdateFBInfoListEvent -= OnUpdateFBInfoListEvent;
        adsModel.OnAdsInfoListUpdateEvent -= OnAdsInfoListUpdateEvent;
        timeModel.OnDayEvent -= OnDayEvent;
        GlobalTimeEvent.Instance.secondEvent -= OnSecondEvent;
    }
    private void OnUpdateTianziKYInfoExent()
    {
        Display();
    }
    private void OnSecondEvent()
    {
        if (bossHeads == null)
            return;
        foreach (var head in bossHeads)
        {
            if (head != null)
            {
                head.UpdateTimer();
            }
        }
    }
    private void OnUpdateFBInfoListEvent(int mapID)
    {
        if (mapID != model.DataMapID)
            return;
        if (!model.TryGetADAwardConfigByMapId(model.DataMapID, out ADAwardConfig adAwardConfig))
            return;
        Display();
    }
    private void OnAdsInfoListUpdateEvent(int id, int mapId)
    {
        if (mapId != model.DataMapID)
            return;
        if (!model.TryGetADAwardConfigByMapId(model.DataMapID, out ADAwardConfig adAwardConfig))
            return;
        Display();
    }
    private void OnDayEvent()
    {
        Display();
    }
    public void Display()
    {
        if (!model.TryGetBossConfig(model.DataMapID, model.todayLineID, out DungeonConfig dungeonConfig, out NPCLineupConfig npcLineupConfig, out NPCConfig npcConfig))
            return;
        if (!DungeonManager.Instance.TryGetFBInfoByMapID(model.DataMapID, out var fbInfo))
            return;
        if (!model.TryGetADAwardConfigByMapId(model.DataMapID, out ADAwardConfig adAwardConfig))
            return;
        DisplayFBInfo(npcConfig);
        DisplayButton(adAwardConfig, fbInfo);
        DisplaySkillWordsList(npcLineupConfig);
        DisplayItemCellList(possibleRewards, model.GetPossibleRewards(npcConfig.NPCID));
        DisplayBossHeadList(bossHeads, model.GetBossIDList(model.DataMapID));
    }
    private void DisplayButton(ADAwardConfig adAwardConfig, FBInfo fbInfo)
    {
        bool isTodayFirst = fbInfo.EnterCnt <= 0;
        btnChallenge2.SetActive(isTodayFirst);
        btnChallenge1.SetActive(!isTodayFirst);
        btnSweep.SetActive(!isTodayFirst);
        btnAds.SetActive(!isTodayFirst);
        if (!DungeonOpenTimeConfig.HasKey(model.DataMapID))
            return;
        DungeonOpenTimeConfig dungeonOpenTimeConfig = DungeonOpenTimeConfig.Get(model.DataMapID);
        int remainChallageCount = model.GetRemainChallageCount();
        int maxCount = dungeonOpenTimeConfig.DayTimes;
        txtTodayChallengeCount.text = UIHelper.AppendColor(remainChallageCount > 0 ? TextColType.Green : TextColType.Red, Language.Get("TianziBillborad03", remainChallageCount, maxCount));
        imgChallengeRed1.SetActive(remainChallageCount > 0);
        imgChallengeRed2.SetActive(remainChallageCount > 0);
        imgSweepRed.SetActive(false);
        int adsCnt = AdsManager.Instance.GetADCntByADID(adAwardConfig.ADID);
        bool isShowAds = adsCnt < adAwardConfig.ADCntMax;
        int remainAdsCount = adAwardConfig.ADCntMax - adsCnt;
        btnAds.SetActive(!isTodayFirst && isShowAds);
        txtTodayAdsCount.text = UIHelper.AppendColor(isShowAds ? TextColType.Green : TextColType.Red, Language.Get("BoneField09", remainAdsCount, adAwardConfig.ADCntMax)); ;
    }
    private void DisplayFBInfo(NPCConfig npcConfig)
    {
        txtBossName.text = npcConfig.NPCName;
        uiEffectPlayer.Play();
        bossModel.Create(npcConfig.SkinID, modelSize);
        txtHistoryHurt.text = Language.Get("TianziBillborad01", UIHelper.ReplaceLargeNum(model.historyHurt));
        txtTodayHurt.text = Language.Get("TianziBillborad02", UIHelper.ReplaceLargeNum(model.todayHurt));
    }
    public void DisplayItemCellList(List<ItemCell> itemCells, int[][] items)
    {
        if (itemCells.IsNullOrEmpty() || items.IsNullOrEmpty())
            return;
        for (int i = 0; i < itemCells.Count; i++)
        {
            if (i < items.Length)
            {
                int index = i;
                itemCells[i].SetActive(true);
                itemCells[i].Init(new ItemCellModel(items[i][0], true, items[i][1]));
                itemCells[i].button.SetListener(() =>
                {
                    ItemTipUtility.Show(items[index][0], true);
                });
            }
            else
            {
                itemCells[i].SetActive(false);
            }
        }
    }
    public void DisplaySkillWordsList(NPCLineupConfig npcLineupConfig)
    {
        if (skillWords.IsNullOrEmpty())
            return;
        for (int i = 0; i < skillWords.Count; i++)
        {
            if (i < npcLineupConfig.SkillIDExList.Length)
            {
                skillWords[i].SetActive(true);
                int skillID = npcLineupConfig.SkillIDExList[i];
                skillWords[i].Init(skillID, () =>
                {
                    SmallTipWin.showText = Language.Get("SmallTipFomat", SkillConfig.Get(skillID)?.SkillName, SkillConfig.Get(skillID)?.Description);
                    SmallTipWin.worldPos = CameraManager.uiCamera.ScreenToWorldPoint(Input.mousePosition);
                    SmallTipWin.isDownShow = true;
                    UIManager.Instance.OpenWindow<SmallTipWin>();
                });
            }
            else
            {
                skillWords[i].SetActive(false);
            }
        }
    }
    public void DisplayBossHeadList(List<TianziBillboradBossHead> heads, List<int> bossIds)
    {
        if (heads.IsNullOrEmpty() || bossIds.IsNullOrEmpty())
            return;
        for (int i = 0; i < heads.Count; i++)
        {
            if (i < bossIds.Count)
            {
                heads[i].SetActive(true);
                heads[i].Display(bossIds[i]);
            }
            else
            {
                heads[i].SetActive(false);
            }
        }
        ExecuteNextFrame(() =>
        {
            if (this != null && gameObject.activeInHierarchy && !isClosing && layoutGroupRect != null)
            {
                LayoutRebuilder.ForceRebuildLayoutImmediate(layoutGroupRect);
            }
        });
    }
}
Main/System/TianziBillborad/TianziBillboradWin.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 5506c1e2daa61a346bbf1eb95adc48b0
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/TianziBillborad/TianziDamageBar.cs
New file
@@ -0,0 +1,224 @@
using System;
using UnityEngine;
using UnityEngine.UI;
public class TianziDamageBar : MonoBehaviour
{
    [SerializeField] IntensifySmoothSlider m_IntensifySlider;
    [SerializeField] Text m_HurtInfo;
    [SerializeField] Text m_BoxCount;
    int bossId;
    ulong nowHunt;    // 当前对Boss的伤害
    ulong nowHpMax; // 当前Boss最大血量
    int nowHpNum;   // 当前是Boss第几条血
    public event Action<int> StageUp;
    public event Action<float, int> ValueChangeAction;
    public event Action<ulong, ulong> ChangeEndAction;
    public void Awake()
    {
        m_IntensifySlider.StageUpAction += OnStageUp;
        m_IntensifySlider.ValueChangeAction += OnValueChange;
        m_IntensifySlider.ChangeEndAction += OnChangeEndAction;
    }
    public void OnDestroy()
    {
        m_IntensifySlider.StageUpAction -= OnStageUp;
        m_IntensifySlider.ValueChangeAction -= OnValueChange;
        m_IntensifySlider.ChangeEndAction -= OnChangeEndAction;
    }
    private void OnChangeEndAction()
    {
        ChangeEndAction.Invoke(nowHunt, nowHpMax);
        m_HurtInfo.text = Language.Get("BoneField09", nowHunt, UIHelper.ReplaceLargeNum(nowHpMax));
    }
    private void OnValueChange(float nowValue, int CurrentStage)
    {
        int hpNum = CurrentStage;
        if (!TianziConfig.TryGetTianziConfigByBossIDAndHPNum(bossId, hpNum, out TianziConfig tianziConfig))
            return;
        ulong hpMax = (ulong)tianziConfig.MaxHP;
        if (hpMax > 0)
        {
            m_HurtInfo.text = Language.Get("BoneField09", (int)(nowValue * hpMax), UIHelper.ReplaceLargeNum(hpMax));
        }
        //Debug.Log($"TianziDamageBar nowValue {nowValue} CurrentStage {CurrentStage} 时间: {DateTime.Now:HH:mm:ss}");
        ValueChangeAction?.Invoke(nowValue, CurrentStage);
    }
    private void OnStageUp(int stage)
    {
        m_BoxCount.text = Language.Get("TianziBillborad07", Mathf.Max(stage - 1, 0));
        StageUp?.Invoke(stage);
    }
    public void Init()
    {
        int dataMapID = TianziBillboradManager.Instance.DataMapID;
        int lineID = TianziBillboradManager.Instance.todayLineID;
        if (!TianziBillboradManager.Instance.TryGetBossConfig(dataMapID, lineID, out DungeonConfig dungeonConfig, out NPCLineupConfig npcLineupConfig, out NPCConfig npcConfig))
            return;
        bossId = npcConfig.NPCID;
        nowHpNum = 1; // 默认从第1条血开始
        if (!TianziConfig.TryGetTianziConfigByBossIDAndHPNum(bossId, nowHpNum, out TianziConfig tianziConfig))
            return;
        m_IntensifySlider.stage = 0;
        m_IntensifySlider.ResetStage();
        nowHunt = 0; // 初始血量为0
        nowHpMax = (ulong)tianziConfig.MaxHP;
        m_BoxCount.text = Language.Get("TianziBillborad07", 0);
        // 除零保护
        float percentage = 0f;
        if (nowHpMax > 0)
        {
            percentage = Mathf.Clamp(nowHunt, 0, nowHpMax) / (float)nowHpMax;
        }
        m_IntensifySlider.value = percentage;
        m_IntensifySlider.stage = nowHpNum; // 设置当前阶段
        m_HurtInfo.text = Language.Get("BoneField09", nowHunt, UIHelper.ReplaceLargeNum(nowHpMax));
    }
    // public event Action IsLastHitUnLockEvent;
    // public void Show(ulong hp, ulong maxHp, BattleDmgInfo info)  // 显示伤害条
    // {
    //     if (!TianziConfig.TryGetTianziConfigByBossIDAndMaxHP(bossId, (long)maxHp, out TianziConfig tianziConfig))
    //         return;
    //     if (info.isLastHit && isLock)
    //     {
    //         isLock = false;
    //         IsLastHitUnLockEvent?.Invoke();
    //     }
    //     int hpNum = tianziConfig.HPNum;
    //     ulong hunt = maxHp - hp;
    //     if (maxHp < nowHpMax)
    //     {
    //         Debug.LogWarning($"TianziDamageBar SkillID  {info.skillConfig.SkillID} hp {hp} maxHp {maxHp} hunt {hunt} nowHpNum {nowHpNum} nowHunt {nowHunt} nowHpMax {nowHpMax} 时间: {DateTime.Now:HH:mm:ss}");
    //         return;
    //     }
    //     if (!isLock)
    //     {
    //         // 根据maxHp获得当前是第几条血
    //         nowHpNum = hpNum;
    //         nowHunt = hunt;
    //         nowHpMax = maxHp;
    //         // 除零保护
    //         float percentage = 0f;
    //         if (nowHpMax > 0)
    //         {
    //             percentage = Mathf.Clamp(nowHunt, 0, nowHpMax) / (float)nowHpMax;
    //         }
    //         m_IntensifySlider.value = percentage;
    //         m_IntensifySlider.stage = nowHpNum; // 设置当前阶段
    //         Debug.Log($"TianziDamageBar SkillID  {info.skillConfig.SkillID} hp {hp} maxHp {maxHp} hunt {hunt} nowHpNum {nowHpNum} nowHunt {nowHunt} nowHpMax {nowHpMax} 时间: {DateTime.Now:HH:mm:ss}");
    //     }
    // }
    // bool isLock = false;
    // public void ShowByB419(ulong hp, ulong maxHp)
    // {
    //     if (!TianziConfig.TryGetTianziConfigByBossIDAndMaxHP(bossId, (long)maxHp, out TianziConfig tianziConfig))
    //         return;
    //     isLock = true;
    //     int hpNum = tianziConfig.HPNum;
    //     ulong hunt = maxHp - hp;
    //     if (maxHp < nowHpMax)
    //     {
    //         Debug.LogWarning($"TianziDamageBar  B419 hp {hp} maxHp {maxHp} hunt {hunt} nowHpNum {nowHpNum} nowHunt {nowHunt} nowHpMax {nowHpMax} 时间: {DateTime.Now:HH:mm:ss}");
    //         return;
    //     }
    //     nowHpNum = hpNum;
    //     nowHunt = hunt;
    //     nowHpMax = maxHp;
    //     // 除零保护
    //     float percentage = 0f;
    //     if (nowHpMax > 0)
    //     {
    //         percentage = Mathf.Clamp(nowHunt, 0, nowHpMax) / (float)nowHpMax;
    //     }
    //     m_IntensifySlider.value = percentage;
    //     m_IntensifySlider.stage = nowHpNum; // 设置当前阶段
    //     Debug.Log($"TianziDamageBar B419 hp {hp} maxHp {maxHp} hunt {hunt} nowHpNum {nowHpNum} nowHunt {nowHunt} nowHpMax{nowHpMax} 时间: {DateTime.Now:HH:mm:ss}");
    // }
    public void Show(ulong totalHP)  // 显示伤害条
    {
        if (!TianziConfig.TryGetTianziConfigByBossIDAndDamage(bossId, totalHP, out TianziConfig tianziConfig))
            return;
        ulong endMaxHp = (ulong)tianziConfig.MaxHP;
        int endHpNum = tianziConfig.HPNum;
        ulong endNowHunt = TianziConfig.GetCurrentHPDamage(bossId, totalHP);
        nowHpNum = endHpNum;
        nowHunt = endNowHunt;
        nowHpMax = endMaxHp;
        // 除零保护
        float percentage = 0f;
        if (nowHpMax > 0)
        {
            percentage = Mathf.Clamp(nowHunt, 0, nowHpMax) / (float)nowHpMax;
        }
        m_IntensifySlider.value = percentage;
        m_IntensifySlider.stage = nowHpNum; // 设置当前阶段
        //Debug.Log($"TianziDamageBar end nowHpNum {nowHpNum} nowHunt {nowHunt} nowHpMax{nowHpMax} 时间: {DateTime.Now:HH:mm:ss}");
    }
    ulong loaclNowHunt = 0;
    ulong loaclAllHunt = 0;
    ulong loaclMaxHp = 0;
    int loaclHpNum = 0;
    public void Show(BattleDmgInfo _damageInfo)
    {
        //闪避和回血类不算伤害
        if (_damageInfo.IsType(DamageType.Dodge) || _damageInfo.IsType(DamageType.Recovery))
            return;
        var damages = _damageInfo.damageList;
        for (int i = 0; i < damages.Count; i++)
        {
            ulong hunt = (ulong)damages[i];
            loaclAllHunt += hunt;
            if (!TianziConfig.TryGetTianziConfigByBossIDAndDamage(bossId, loaclAllHunt, out TianziConfig tianziConfig))
                return;
            loaclMaxHp = (ulong)tianziConfig.MaxHP;
            loaclHpNum = tianziConfig.HPNum;
            loaclNowHunt = TianziConfig.GetCurrentHPDamage(bossId, loaclAllHunt);
            // if (loaclMaxHp < nowHpMax || loaclHpNum < nowHpNum)
            // {
            //     Debug.LogWarning($"TianziDamageBar hunt {hunt} loaclAllHunt {loaclAllHunt} loaclHpNum {loaclHpNum} loaclNowHunt {loaclNowHunt} nowHpNum {nowHpNum} nowHunt {nowHunt} nowHpMax{nowHpMax} 时间: {DateTime.Now:HH:mm:ss}");
            //     return;
            // }
            // if (loaclNowHunt < nowHunt)
            // {
            //     Debug.LogWarning($"TianziDamageBar hunt {hunt} loaclAllHunt {loaclAllHunt} loaclHpNum {loaclHpNum} loaclNowHunt {loaclNowHunt} nowHpNum {nowHpNum} nowHunt {nowHunt} nowHpMax{nowHpMax} 时间: {DateTime.Now:HH:mm:ss}");
            //     return;
            // }
            nowHunt = loaclNowHunt;
            nowHpMax = loaclMaxHp;
            nowHpNum = loaclHpNum;
            // 除零保护
            float percentage = 0f;
            if (nowHpMax > 0)
            {
                percentage = Mathf.Clamp(nowHunt, 0, nowHpMax) / (float)nowHpMax;
            }
            m_IntensifySlider.value = percentage;
            m_IntensifySlider.stage = nowHpNum;
            //Debug.Log($"TianziDamageBar hunt {hunt} loaclAllHunt {loaclAllHunt} loaclHpNum {loaclHpNum} loaclNowHunt {loaclNowHunt} nowHpNum {nowHpNum} nowHunt {nowHunt} nowHpMax{nowHpMax} 时间: {DateTime.Now:HH:mm:ss}");
        }
    }
}
Main/System/TianziBillborad/TianziDamageBar.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c0588389dd6e50a4ea91f1d5779482ff
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant: