From f0c41c38ec5c651dedc645687597d227dccac981 Mon Sep 17 00:00:00 2001
From: hch <305670599@qq.com>
Date: 星期一, 24 十一月 2025 00:49:51 +0800
Subject: [PATCH] 328 【主界面】坐骑系统

---
 Main/System/Arena/ArenaPlayerTop3Cell.cs                     |    4 
 Main/Config/Configs/HorseSkinConfig.cs                       |    5 
 Main/System/Horse/HorseSkinLineCell.cs.meta                  |   11 
 Main/System/Horse/HorseController.cs                         |   49 +++
 Main/Component/UI/Core/ButtonEx.cs                           |    4 
 Main/Config/ConfigManager.cs                                 |    3 
 Main/System/Horse/HorseManager.cs                            |  272 ++++++++++++++++++-
 Main/System/Horse/HorseSkinCell.cs.meta                      |   11 
 Main/System/TianziBillborad/TianziBillboradPlayerTop3Cell.cs |    4 
 Main/System/Redpoint/MainRedDot.cs                           |    2 
 Main/System/Horse/HorseSkinWin.cs                            |  260 ++++++++++++++++++
 Main/System/UIBase/UIBase.cs                                 |    4 
 Main/System/BillboardRank/PlayerTop3Cell.cs                  |    4 
 Main/System/Horse/HorseSkinCell.cs                           |   54 +++
 Main/System/Main/HomeWin.cs                                  |    7 
 Main/Config/PartialConfigs/HorseSkinConfig.cs.meta           |   11 
 Main/Config/PartialConfigs/HorseSkinConfig.cs                |   26 +
 Main/System/Horse/HorseSkinLineCell.cs                       |   26 +
 Main/System/Horse/HorseWin.cs                                |   24 +
 19 files changed, 744 insertions(+), 37 deletions(-)

diff --git a/Main/Component/UI/Core/ButtonEx.cs b/Main/Component/UI/Core/ButtonEx.cs
index e6e423d..a341eb6 100644
--- a/Main/Component/UI/Core/ButtonEx.cs
+++ b/Main/Component/UI/Core/ButtonEx.cs
@@ -9,12 +9,12 @@
 
     public event Action ableTimeChangeEvent;
 
-    public float interval = 0.5f;
+    public float interval = 0.1f;
     public bool customPositiveSound = false;
     public bool customNegativeSound = false;
     public int positiveSound = 0;
     public int negativeSound = 0;
-    public float pressedScale = 1.1f;
+    public float pressedScale = 1.05f;
 
     float m_AbleTime = 0f;
     private Vector3 originalScale;
diff --git a/Main/Config/ConfigManager.cs b/Main/Config/ConfigManager.cs
index 47c6024..c6aa9df 100644
--- a/Main/Config/ConfigManager.cs
+++ b/Main/Config/ConfigManager.cs
@@ -70,6 +70,7 @@
             typeof(OrderInfoConfig),
             typeof(PlayerAttrConfig),
             typeof(PlayerFaceConfig),
+            typeof(PriorBundleConfig),
             typeof(RandomNameConfig),
             typeof(SignInConfig),
             typeof(StoreConfig),
@@ -296,6 +297,8 @@
         ClearConfigDictionary<PlayerAttrConfig>();
         // 娓呯┖ PlayerFaceConfig 瀛楀吀
         ClearConfigDictionary<PlayerFaceConfig>();
+        // 娓呯┖ PriorBundleConfig 瀛楀吀
+        ClearConfigDictionary<PriorBundleConfig>();
         // 娓呯┖ RandomNameConfig 瀛楀吀
         ClearConfigDictionary<RandomNameConfig>();
         // 娓呯┖ SignInConfig 瀛楀吀
diff --git a/Main/Config/Configs/HorseSkinConfig.cs b/Main/Config/Configs/HorseSkinConfig.cs
index 4f73c0f..b2bfe44 100644
--- a/Main/Config/Configs/HorseSkinConfig.cs
+++ b/Main/Config/Configs/HorseSkinConfig.cs
@@ -1,6 +1,6 @@
 锘�//--------------------------------------------------------
 //    [Author]:           YYL
-//    [  Date ]:           2025骞�11鏈�20鏃�
+//    [  Date ]:           2025骞�11鏈�23鏃�
 //--------------------------------------------------------
 
 using System.Collections.Generic;
@@ -30,6 +30,7 @@
 	public string Spine;
 	public int[] Poses;
 	public int heroFirst;
+	public string getWay;
 
     public override int LoadKey(string _key)
     {
@@ -116,6 +117,8 @@
 			}
 
 			int.TryParse(tables[13],out heroFirst); 
+
+			getWay = tables[14];
         }
         catch (Exception exception)
         {
diff --git a/Main/Config/PartialConfigs/HorseSkinConfig.cs b/Main/Config/PartialConfigs/HorseSkinConfig.cs
new file mode 100644
index 0000000..1566e4d
--- /dev/null
+++ b/Main/Config/PartialConfigs/HorseSkinConfig.cs
@@ -0,0 +1,26 @@
+using System.Collections.Generic;
+public partial class HorseSkinConfig : ConfigBase<int, HorseSkinConfig>
+{
+    public static List<int> rankLVList = new List<int>();  //鍗囬樁瑙i攣
+    public static List<int> itemsList = new List<int>(); //鐨偆閬撳叿瑙i攣/鍗囨槦
+    protected override void OnConfigParseCompleted()
+    {
+        if (UnlockWay == 1)
+        {
+            if (UnlockValue != 0 && !rankLVList.Contains(UnlockValue))
+            {
+                rankLVList.Add(UnlockValue);
+            }
+        }
+        else if (UnlockWay == 2)
+        {
+            if (UnlockValue != 0 && !itemsList.Contains(UnlockValue))
+            {
+                itemsList.Add(UnlockValue);
+            }
+        }
+
+    }
+
+   
+}
diff --git a/Main/Config/PartialConfigs/HorseSkinConfig.cs.meta b/Main/Config/PartialConfigs/HorseSkinConfig.cs.meta
new file mode 100644
index 0000000..bf28923
--- /dev/null
+++ b/Main/Config/PartialConfigs/HorseSkinConfig.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 638692d04a615ba408293c0eec4e450e
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/Arena/ArenaPlayerTop3Cell.cs b/Main/System/Arena/ArenaPlayerTop3Cell.cs
index a49221b..3351459 100644
--- a/Main/System/Arena/ArenaPlayerTop3Cell.cs
+++ b/Main/System/Arena/ArenaPlayerTop3Cell.cs
@@ -8,7 +8,7 @@
     [SerializeField] Text nameText;
     [SerializeField] OfficialTitleCell officialTitleCell;
     [SerializeField] Button queryPlayerBtn; //鍚庣画娣诲姞鐐瑰嚮鏌ョ湅鐜╁璇︽儏
-    [SerializeField] UIHeroController model;
+    [SerializeField] HorseController model;
 
     public void Display(int rankType, int rank, string valueFormat = "{0}")
     {
@@ -24,7 +24,7 @@
         rankValueText.text = string.Format(valueFormat, UIHelper.ReplaceLargeNum(rankData.cmpValue));
         nameText.text = rankData.name1;
         officialTitleCell.InitUI((int)rankData.value1, (int)rankData.value2);
-        model.Create((int)rankData.value5, 1);
+        model.Create(HorseManager.Instance.GetOtherPlayerHorseSkinID((int)rankData.value6), (int)rankData.value5, 1);
     }
 
 
diff --git a/Main/System/BillboardRank/PlayerTop3Cell.cs b/Main/System/BillboardRank/PlayerTop3Cell.cs
index 0c724ae..610fe84 100644
--- a/Main/System/BillboardRank/PlayerTop3Cell.cs
+++ b/Main/System/BillboardRank/PlayerTop3Cell.cs
@@ -13,7 +13,7 @@
     [SerializeField] Text nameText;
     [SerializeField] OfficialTitleCell officialTitleCell;
     [SerializeField] Button queryPlayerBtn; //鍚庣画娣诲姞鐐瑰嚮鏌ョ湅鐜╁璇︽儏
-    [SerializeField] UIHeroController model;
+    [SerializeField] HorseController model;
 
     public void Display(int rankType, int rank, string valueFormat = "{0}")
     {
@@ -30,7 +30,7 @@
         rankValueText.text = RankModel.Instance.GetStoryInfo(rankData.cmpValue);
         nameText.text = rankData.name1;
         officialTitleCell.InitUI((int)rankData.value1, (int)rankData.value2);
-        model.Create((int)rankData.value5, 1);
+        model.Create(HorseManager.Instance.GetOtherPlayerHorseSkinID((int)rankData.value6), (int)rankData.value5, 1);
     }
 
 
diff --git a/Main/System/Horse/HorseController.cs b/Main/System/Horse/HorseController.cs
index b3c76d3..ba769dd 100644
--- a/Main/System/Horse/HorseController.cs
+++ b/Main/System/Horse/HorseController.cs
@@ -52,7 +52,16 @@
 			instanceGO.transform.localRotation = Quaternion.identity;
 		}
 
-		skeletonGraphic = instanceGO.GetComponentInChildren<SkeletonGraphic>(true);
+		// skeletonGraphic = instanceGO.GetComponentInChildren<SkeletonGraphic>(true);
+		skeletonGraphic = instanceGO.transform.Find("Horse").GetComponent<SkeletonGraphic>();
+		if (string.IsNullOrEmpty(skinConfig.Spine))
+		{
+			//鍗镐笅鍧愰獞鐨勬儏鍐�
+			skeletonGraphic.enabled = false;
+			spineAnimationState = null;
+			CreateHero(_heroSkinID);
+			return;
+        }
 
 		skeletonGraphic.skeletonDataAsset = ResManager.Instance.LoadAsset<SkeletonDataAsset>("UIEffect/Spine/Horse", skinConfig.Spine);
 		if (skeletonGraphic.skeletonDataAsset == null)
@@ -66,6 +75,7 @@
 			Debug.LogError("鏈厤缃畇pine");
 			return;
 		}
+		skeletonGraphic.enabled = true;
 		skeletonGraphic.Initialize(true);
 
 		skeletonGraphic.transform.localPosition = new Vector3(skinConfig.Poses[0], skinConfig.Poses[1], 0);
@@ -83,9 +93,17 @@
 	public void CreateHero(int heroSkinID)
 	{
 		if (instanceGO == null)
-        {
+		{
 			return;
-        }
+		}
+		if (heroSkinID == 0)
+		{
+			if (hero != null && hero.gameObject.activeSelf)
+            {
+                hero.SetActive(false);
+            }
+			return;	
+		}
 		hero = instanceGO.GetComponentInChildren<UIHeroController>(true);
 		if (hero == null)
 		{
@@ -98,8 +116,25 @@
 		}
 		hero.SetActive(true);
 		hero.Create(heroSkinID);
-		//绾﹀畾楠戜箻鐐归楠煎悕
-		hero.GetComponent<BoneFollowerGraphic>().boneName = "mountPoint";
+		
+		// 纭繚 BoneFollowerGraphic 寮曠敤鍒版纭殑鍧愰獞 SkeletonGraphic
+		var boneFollower = hero.GetComponent<BoneFollowerGraphic>();
+		if (boneFollower != null && skeletonGraphic != null && skeletonGraphic.enabled)
+		{
+			boneFollower.enabled = true;
+			// 璁剧疆 SkeletonGraphic 寮曠敤
+			boneFollower.SkeletonGraphic = skeletonGraphic;
+			// 绾﹀畾楠戜箻鐐归楠煎悕
+			boneFollower.boneName = "mountPoint";
+			// 寮哄埗閲嶆柊鍒濆鍖� BoneFollowerGraphic 浠ュ簲鐢ㄦ柊鐨勮缃�
+			boneFollower.Initialize();
+		}
+		else
+		{
+			boneFollower.enabled = false;
+			hero.transform.localPosition = new Vector3(0, -50, 0);
+		}
+		
 		if (isHeroShowBefore)
 		{
 			hero.transform.SetAsLastSibling();
@@ -112,6 +147,8 @@
 
 	public void HeroPlay(string motionName, bool loop = false, bool replay=true)
 	{
+		if (hero == null)
+			return;
 		hero.PlayAnimation(motionName, loop, replay);
 	}
 
@@ -143,6 +180,8 @@
 
 		// 鐩存帴浣跨敤 ToString() 鑰屼笉鏄皟鐢� GetAnimationName
 		spineAnimationState.SetAnimation(0, motionName.ToString(), loop);
+
+		HeroPlay(motionName, loop, replay);
 	}
 
 	// 鎾斁绗竴涓姩鐢伙紙浣滀负榛樿鍔ㄧ敾锛�
diff --git a/Main/System/Horse/HorseManager.cs b/Main/System/Horse/HorseManager.cs
index 3ffeb8c..7993500 100644
--- a/Main/System/Horse/HorseManager.cs
+++ b/Main/System/Horse/HorseManager.cs
@@ -11,13 +11,27 @@
     public int exp;    //褰撳墠闃剁瓑绾х粡楠岋紝姣忕骇浠�0寮�濮�
     public event Action OnHorseUpdateEvent;
 
-    public Dictionary<int, HorseSkin> skinDic = new Dictionary<int, HorseSkin>();
+    public int lastClassLV;  //鐨偆鐣岄潰鐐瑰嚮鍚庣孩鐐瑰埛鏂�
+
+    Dictionary<int, HorseSkin> skinDic = new Dictionary<int, HorseSkin>();
+    public event Action OnSkinUpdateEvent;
 
     //鍗囩骇/鍗囬樁灞炴��
     public Dictionary<int, long> specialAttrDic = new Dictionary<int, long>();
     public Dictionary<int, long> attrDic = new Dictionary<int, long>();
     public Dictionary<int, long> skinAttrDic = new Dictionary<int, long>();
 
+    int m_selectSkinID;
+    public event Action OnSelectEvent;
+    public int selectSkinID
+    {
+        get { return m_selectSkinID; }
+        set
+        {
+            m_selectSkinID = value;
+            OnSelectEvent?.Invoke();
+        }
+    }
     //閰嶇疆
     public int lvUPItemID;
     public int rankUPItemID;
@@ -27,6 +41,7 @@
     {
         DTC0102_tagCDBPlayer.beforePlayerDataInitializeEventOnRelogin += OnBeforePlayerDataInitialize;
         PackManager.Instance.RefreshItemEvent += OnRefreshItemEvent;
+        DTC0403_tagPlayerLoginLoadOK.playerLoginOkEvent += OnPlayerLoginOk;
 
         ParseConfig();
     }
@@ -35,6 +50,7 @@
     {
         DTC0102_tagCDBPlayer.beforePlayerDataInitializeEventOnRelogin -= OnBeforePlayerDataInitialize;
         PackManager.Instance.RefreshItemEvent -= OnRefreshItemEvent;
+        DTC0403_tagPlayerLoginLoadOK.playerLoginOkEvent -= OnPlayerLoginOk;
     }
 
     void ParseConfig()
@@ -51,6 +67,12 @@
         horseLV = 0;
         exp = 0;
         skinDic.Clear();
+        lastClassLV = 0;
+    }
+
+    void OnPlayerLoginOk()
+    {
+        UpdateSkinRedpoint();
     }
 
     void OnRefreshItemEvent(PackType type, int index, int itemID)
@@ -61,18 +83,34 @@
             {
                 UpdateRedpoint();
             }
+
+            if (HorseSkinConfig.itemsList.Contains(itemID))
+            {
+                UpdateSkinRedpoint();
+            }
         }
     }
 
-    public int GetHorseSkinID()
+    //鑾峰彇褰撳墠浣跨敤鍧愰獞鐨偆ID
+    public int GetUsingHorseSkinID(bool useDefault = false)
     {
         var id = (int)PlayerDatas.Instance.baseData.equipShowSwitch % 1000;
         if (id == 0)
         {
-            id = 1;
+            if (useDefault)
+            {
+                return 1;
+            }
         }
         return id;
     }
+
+    //鑾峰彇鍏朵粬鐜╁浣跨敤鍧愰獞鐨偆ID
+    public int GetOtherPlayerHorseSkinID(int value)
+    {
+        return value % 1000;
+    }
+
 
     public void UpdateHorseInfo(HA303_tagSCHorseClassInfo netPack)
     {
@@ -85,11 +123,17 @@
         horseLV = netPack.HorseLV;
         exp = netPack.Exp;
         UpdateRedpoint();
+        UpdateSkinRedpoint();
         RefreshAttr();
         OnHorseUpdateEvent?.Invoke();
         if (isOpenSuccess)
         {
             UIManager.Instance.OpenWindow<HorseSuccessWin>();
+        }
+
+        if (!DTC0403_tagPlayerLoginLoadOK.finishedLogin)
+        {
+            lastClassLV = classLV;
         }
     }
 
@@ -107,19 +151,23 @@
         }
 
         RefreshSkinAttr();
-        UpdateRedpoint();
-        OnHorseUpdateEvent?.Invoke();
+        UpdateSkinRedpoint();
+        OnSkinUpdateEvent?.Invoke();
     }
 
-    Redpoint redpoint = new Redpoint(MainRedDot.RedPoint_HorseKey);
+    #region 绾㈢偣
+    Redpoint redpoint = new Redpoint(MainRedDot.RedPoint_HorseKey, MainRedDot.RedPoint_HorseKey * 10 + 1);
+    Redpoint skinRedpoint = new Redpoint(MainRedDot.RedPoint_HorseKey, MainRedDot.RedPoint_HorseKey * 10 + 2);
+
+
     void UpdateRedpoint()
     {
+        redpoint.state = RedPointState.None;
         if (!FuncOpen.Instance.IsFuncOpen((int)FuncOpenEnum.Horse))
         {
             return;
         }
 
-        redpoint.state = RedPointState.None;
         var state = GetHorseState();
         if (state == 0)
         {
@@ -139,6 +187,128 @@
             }
         }
     }
+
+    //鐨偆绾㈢偣
+    public void UpdateSkinRedpoint()
+    {
+        skinRedpoint.state = RedPointState.None;
+        if (!FuncOpen.Instance.IsFuncOpen((int)FuncOpenEnum.Horse))
+        {
+            return;
+        }
+
+        if (!DTC0403_tagPlayerLoginLoadOK.finishedLogin)
+        {
+            return;
+        }
+
+        //鍗囬樁瑙i攣绾㈢偣
+        foreach (var lv in HorseSkinConfig.rankLVList)
+        {
+            if (classLV >= lv && lastClassLV < lv)
+            {
+                skinRedpoint.state = RedPointState.Simple;
+                return;
+            }
+        }
+
+        //鍗囨槦瑙i攣绾㈢偣
+        foreach (var skin in HorseSkinConfig.GetValues())
+        {
+            if (skin.UnlockWay != 2)
+                continue;
+
+            if (IsSkinActive(skin.SkinID))
+            {
+                if (skin.StarMax > 0)
+                {
+                    //鐨偆鍗囨槦绾㈢偣
+                    int itemID = skin.UnlockValue;
+                    var count = PackManager.Instance.GetItemCountByID(PackType.Item, itemID);
+                    if (count >= skin.UpNeedCnt && skinDic[skin.SkinID].Star < skin.StarMax)
+                    {
+                        skinRedpoint.state = RedPointState.Simple;
+                        return;
+                    }
+                }
+            }
+            else
+            {
+                //鐨偆瑙i攣绾㈢偣
+                int itemID = skin.UnlockValue;
+                var count = PackManager.Instance.GetItemCountByID(PackType.Item, itemID);
+                if (count >= skin.UnlockNeedCnt)
+                {
+                    skinRedpoint.state = RedPointState.Simple;
+                    return;
+                }
+            }
+
+        }
+    }
+
+    public bool IsSkinActive(int id)
+    {
+        if (skinDic.TryGetValue(id, out var skin))
+        {
+            if (skin.State == 1 && skin.EndTime == 0)
+            {
+                return true;
+            }
+            else if (skin.State == 1 && skin.EndTime > 0 && skin.EndTime > TimeUtility.AllSeconds)
+            {
+                return true;
+            }
+        }
+        var skinConfig = HorseSkinConfig.Get(id);
+        if (skinConfig.UnlockWay == 1)
+        {
+            return classLV >= skinConfig.UnlockValue;
+        }
+        return false;
+    }
+
+    public bool IsShowTheHorseRedImg(int skinID)
+    {
+        var skin = HorseSkinConfig.Get(skinID);
+        int lv = skin.UnlockWay == 1 ? skin.UnlockValue : 0;
+        //鍗囬樁瑙i攣绾㈢偣
+        if (classLV >= lv && lastClassLV < lv)
+        {
+            return true;
+        }
+
+        //鍗囨槦瑙i攣绾㈢偣
+        if (skin.UnlockWay != 2)
+            return false;
+
+        if (IsSkinActive(skin.SkinID))
+        {
+            if (skin.StarMax > 0)
+            {
+                //鐨偆鍗囨槦绾㈢偣
+                int itemID = skin.UnlockValue;
+                var count = PackManager.Instance.GetItemCountByID(PackType.Item, itemID);
+                if (count >= skin.UpNeedCnt && skinDic[skinID].Star < skin.StarMax)
+                {
+                    return true;
+                }
+            }
+        }
+        else
+        {
+            //鐨偆瑙i攣绾㈢偣
+            int itemID = skin.UnlockValue;
+            var count = PackManager.Instance.GetItemCountByID(PackType.Item, itemID);
+            if (count >= skin.UnlockNeedCnt)
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+
 
     //0 鍗囩骇 1 鍗囬樁 2 婊$骇
     public int GetHorseState()
@@ -163,8 +333,9 @@
         return 2;
 
     }
+    #endregion
 
-
+    #region 灞炴��
     public void RefreshAttr()
     {
         specialAttrDic.Clear();
@@ -184,7 +355,7 @@
                     specialAttrDic[config.ClassSpecAttrIDList[i]] += config.ClassSpecAttrValueList[i];
                 }
             }
-            
+
             if (!config.ClassAttrValueList.IsNullOrEmpty())
             {
                 for (int i = 0; i < config.AttrIDList.Length; i++)
@@ -206,9 +377,9 @@
                     {
                         attrDic[config.AttrIDList[i]] = 0;
                     }
-                    var tmpHorseLV = lv != classLV ? config.MaxLV :horseLV;
+                    var tmpHorseLV = lv != classLV ? config.MaxLV : horseLV;
 
-                    attrDic[config.AttrIDList[i]] += config.PerLVAttrValueList[i]*tmpHorseLV;
+                    attrDic[config.AttrIDList[i]] += config.PerLVAttrValueList[i] * tmpHorseLV;
                 }
             }
         }
@@ -218,16 +389,16 @@
     void RefreshSkinAttr()
     {
         skinAttrDic.Clear();
-        foreach(var skinID in skinDic.Keys)
+        foreach (var skinID in skinDic.Keys)
         {
             var skin = skinDic[skinID];
             if (skin.State != 1)
             {
                 continue;
             }
-            
+
             var config = HorseSkinConfig.Get(skinID);
-            
+
             if (config.AttrIDList.IsNullOrEmpty())
             {
                 continue;
@@ -243,7 +414,7 @@
                     skinAttrDic[config.AttrIDList[i]] += config.InitAttrValueList[i];
                 }
             }
-            
+
             if (!config.AttrPerStarAddList.IsNullOrEmpty())
             {
                 for (int i = 0; i < config.AttrIDList.Length; i++)
@@ -252,7 +423,7 @@
                     {
                         skinAttrDic[config.AttrIDList[i]] = 0;
                     }
-                    skinAttrDic[config.AttrIDList[i]] += config.AttrPerStarAddList[i]*skin.Star;
+                    skinAttrDic[config.AttrIDList[i]] += config.AttrPerStarAddList[i] * skin.Star;
                 }
             }
         }
@@ -280,6 +451,75 @@
 
         return 0;
     }
+    #endregion
+
+    public List<int> sortSkinList = new List<int>();
+    public void SortHorseSkinList()
+    {
+        sortSkinList = HorseSkinConfig.GetKeys();
+        sortSkinList.Sort((a, b) =>
+        {
+            var isActiveA = IsSkinActive(a);
+            var isActiveB = IsSkinActive(b);
+            if (isActiveA != isActiveB)
+            {
+                return isActiveA ? -1 : 1;
+            }
+            var skinA = HorseSkinConfig.Get(a);
+            var skinB = HorseSkinConfig.Get(b);
+            return skinA.SkinID - skinB.SkinID;
+        });
+    }
+
+    public Dictionary<int, long> GetAttrBySkinID(HorseSkinConfig config)
+    {
+        skinAttrDic.Clear();
+
+        if (!config.InitAttrValueList.IsNullOrEmpty())
+        {
+            for (int i = 0; i < config.AttrIDList.Length; i++)
+            {
+                if (!skinAttrDic.ContainsKey(config.AttrIDList[i]))
+                {
+                    skinAttrDic[config.AttrIDList[i]] = 0;
+                }
+                skinAttrDic[config.AttrIDList[i]] += config.InitAttrValueList[i];
+            }
+        }
+
+        if (!config.AttrPerStarAddList.IsNullOrEmpty())
+        {
+            var star = 0;
+            if (skinDic.TryGetValue(config.SkinID, out var skin))
+            {
+                star = skin.Star;
+            }
+            for (int i = 0; i < config.AttrIDList.Length; i++)
+            {
+                if (!skinAttrDic.ContainsKey(config.AttrIDList[i]))
+                {
+                    skinAttrDic[config.AttrIDList[i]] = 0;
+                }
+                skinAttrDic[config.AttrIDList[i]] += config.AttrPerStarAddList[i] * star;
+            }
+        }
+        return skinAttrDic;
+    }
+
+    public HorseSkin GetSkinData(int skinID)
+    {
+        skinDic.TryGetValue(skinID, out var skin);
+        return skin;
+    }
+
+    //// 鎿嶄綔 1-婵�娲伙紱2-浣╂埓锛�3-鍗囨槦
+    public void SendSkinOP(int opType, int skinID)
+    {
+        var pack = new CB203_tagCSHorseSkinOP();
+        pack.OPType = (byte)opType;
+        pack.SkinID = (ushort)skinID;
+        GameNetSystem.Instance.SendInfo(pack);
+    }
 }
 
 public class HorseSkin {
diff --git a/Main/System/Horse/HorseSkinCell.cs b/Main/System/Horse/HorseSkinCell.cs
new file mode 100644
index 0000000..197c979
--- /dev/null
+++ b/Main/System/Horse/HorseSkinCell.cs
@@ -0,0 +1,54 @@
+锘縰sing UnityEngine;
+using UnityEngine.UI;
+
+public class HorseSkinCell : MonoBehaviour
+{
+    [SerializeField] HorseController horseModel;
+    [SerializeField] Image emptyImg;
+    [SerializeField] Transform usingObj;
+    [SerializeField] Transform redObj;
+    [SerializeField] Transform timeObj;
+    [SerializeField] Transform lockMaskObj;
+    [SerializeField] Transform canLockObj;
+    [SerializeField] Transform selectObj;
+    [SerializeField] ButtonEx selectBtn;
+
+    int skinID;
+
+    void Start()
+    {
+        selectBtn.AddListener(OnClickFunc);
+    }
+
+
+    void OnClickFunc()
+    {
+        HorseManager.Instance.selectSkinID = skinID;
+
+        var skinConfig = HorseSkinConfig.Get(skinID);
+        if (skinConfig.UnlockWay == 1 && HorseManager.Instance.lastClassLV != HorseManager.Instance.classLV)
+        {
+            HorseManager.Instance.lastClassLV = HorseManager.Instance.classLV;
+            HorseManager.Instance.UpdateSkinRedpoint();
+        }
+
+    }
+
+    public void Display(int _skinID)
+    {
+        skinID = _skinID;
+        horseModel.Create(skinID, 0, 0.6f);
+        emptyImg.SetActive(skinID == 0);
+        usingObj.SetActive(HorseManager.Instance.GetUsingHorseSkinID() == skinID);
+        bool isShowRed = HorseManager.Instance.IsShowTheHorseRedImg(skinID);
+        redObj.SetActive(isShowRed);
+        var skinConfig = HorseSkinConfig.Get(skinID);
+        timeObj.SetActive(skinConfig.ExpireMinutes > 0);
+        bool isActive = HorseManager.Instance.IsSkinActive(skinID);
+        lockMaskObj.SetActive(!isActive);
+        canLockObj.SetActive(!isActive && isShowRed);
+        selectObj.SetActive(HorseManager.Instance.selectSkinID == skinID);
+    }
+
+}
+
diff --git a/Main/System/Horse/HorseSkinCell.cs.meta b/Main/System/Horse/HorseSkinCell.cs.meta
new file mode 100644
index 0000000..cbf034f
--- /dev/null
+++ b/Main/System/Horse/HorseSkinCell.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: d24ae9e9d748eab47b668e4dc44a24a1
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/Horse/HorseSkinLineCell.cs b/Main/System/Horse/HorseSkinLineCell.cs
new file mode 100644
index 0000000..8f1084f
--- /dev/null
+++ b/Main/System/Horse/HorseSkinLineCell.cs
@@ -0,0 +1,26 @@
+锘縰sing UnityEngine;
+using UnityEngine.UI;
+
+public class HorseSkinLineCell : CellView
+{
+    [SerializeField] HorseSkinCell[] horseSkinCells;
+
+    public void Display(int _index)
+    {
+        for (int i = 0; i < horseSkinCells.Length; i++)
+        {
+            int index = _index + i;
+            if (index < HorseManager.Instance.sortSkinList.Count)
+            {
+                horseSkinCells[i].SetActive(true);
+                horseSkinCells[i].Display(HorseManager.Instance.sortSkinList[index]);
+            }
+            else
+            {
+                horseSkinCells[i].SetActive(false);
+            }
+        }
+    }
+
+}
+
diff --git a/Main/System/Horse/HorseSkinLineCell.cs.meta b/Main/System/Horse/HorseSkinLineCell.cs.meta
new file mode 100644
index 0000000..257d118
--- /dev/null
+++ b/Main/System/Horse/HorseSkinLineCell.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 359483a78848f49409e92f6757c41607
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/Horse/HorseSkinWin.cs b/Main/System/Horse/HorseSkinWin.cs
index 13a8fba..9bf1e74 100644
--- a/Main/System/Horse/HorseSkinWin.cs
+++ b/Main/System/Horse/HorseSkinWin.cs
@@ -8,21 +8,275 @@
 /// </summary>
 public class HorseSkinWin : UIBase
 {
-    [SerializeField] Button bagBtn;
+    [SerializeField] Button seeAttrBtn;
+    [SerializeField] HorseController horseModel;
+    [SerializeField] Text horseName;
+    [SerializeField] Text getWayText;
+    [SerializeField] Text attrText;
+    [SerializeField] ScrollerController scroller;
+    [SerializeField] Transform changeOPObj;
+    [SerializeField] Button lvUpBtn;
+    [SerializeField] Text lvUpText;
+    [SerializeField] Text lvUpCostText;
+    [SerializeField] Image lvUpItemImg;
+    [SerializeField] Button changeSkinBtn;
+    [SerializeField] Text timeText;
+    [SerializeField] Transform inSkinMarkObj;   //骞诲寲涓�
+
+    [SerializeField] Button unlockBtn;
+    [SerializeField] Text unlockCostText;
+    [SerializeField] Image unlockItemImg;
 
     protected override void InitComponent()
     {
-
+        seeAttrBtn.AddListener(OnSeeAttrBtnClick);
+        lvUpBtn.AddListener(OnLvUpBtnClick);
+        changeSkinBtn.AddListener(OnChangeSkinBtnClick);
+        unlockBtn.AddListener(OnUnlockBtnClick);
     }
 
     protected override void OnPreOpen()
     {
+        HorseManager.Instance.selectSkinID = HorseManager.Instance.GetUsingHorseSkinID();
+        HorseManager.Instance.SortHorseSkinList();
+        HorseManager.Instance.OnSkinUpdateEvent += OnSkinUpdateEvent;
+        HorseManager.Instance.OnSelectEvent += OnSelectEvent;
+        scroller.OnRefreshCell += OnRefreshCell;
+        GlobalTimeEvent.Instance.secondEvent += OnSecondEvent;
+        PlayerDatas.Instance.playerDataRefreshEvent += OnRefreshPlayerData;
 
+        CreateScroller();
+        Display();
     }
 
     protected override void OnPreClose()
     {
-
+        HorseManager.Instance.lastClassLV = HorseManager.Instance.classLV;
+        HorseManager.Instance.UpdateSkinRedpoint();
+        HorseManager.Instance.OnSkinUpdateEvent -= OnSkinUpdateEvent;
+        HorseManager.Instance.OnSelectEvent -= OnSelectEvent;
+        scroller.OnRefreshCell -= OnRefreshCell;
+        GlobalTimeEvent.Instance.secondEvent -= OnSecondEvent;
+        PlayerDatas.Instance.playerDataRefreshEvent -= OnRefreshPlayerData;
     }
 
+    void OnSkinUpdateEvent()
+    {
+        HorseManager.Instance.SortHorseSkinList();
+        Display();
+    }
+
+    void OnRefreshPlayerData(PlayerDataType type)
+    {
+        if (type == PlayerDataType.EquipShowSwitch)
+        {
+            Display();
+        }
+    }
+    void OnSelectEvent()
+    {
+        Display();
+    }
+
+    void Display()
+    {
+        var skinConfig = HorseSkinConfig.Get(HorseManager.Instance.selectSkinID);
+        horseModel.Create(HorseManager.Instance.selectSkinID, PhantasmPavilionManager.Instance.GetMyModelSkinID());
+        if (string.IsNullOrEmpty(skinConfig.getWay))
+        {
+            getWayText.text = "";
+        }
+        else
+        {
+            getWayText.text = Language.Get("PhantasmPavilion06", skinConfig.getWay);
+        }
+        if (skinConfig.AttrIDList.Length == 0)
+        {
+            attrText.text = "";
+        }
+        else
+        {
+            attrText.text = GetSkinAttrSring(skinConfig);
+        }
+
+        scroller.m_Scorller.RefreshActiveCellViews();
+
+        bool isActive = HorseManager.Instance.IsSkinActive(HorseManager.Instance.selectSkinID);
+        var skinData = HorseManager.Instance.GetSkinData(HorseManager.Instance.selectSkinID);
+        if (skinConfig.StarMax > 0 && skinData.Star > 0)
+        {
+            horseName.text = skinConfig.Name + "+" + skinData.Star;
+        }
+        else
+        {
+            horseName.text = skinConfig.Name;
+        }
+        if (isActive)
+        {
+            changeOPObj.SetActive(true);
+            unlockBtn.SetActive(false);
+            if (skinConfig.StarMax > 0)
+            {
+                lvUpBtn.SetActive(true);
+                if (skinData.Star >= skinConfig.StarMax)
+                {
+                    lvUpCostText.SetActive(false);
+                    lvUpText.text = Language.Get("L1055");
+                    lvUpBtn.SetInteractable(false);
+                }
+                else
+                {
+                    lvUpBtn.SetInteractable(true);
+                    lvUpText.text = Language.Get("L1109");
+                    lvUpCostText.SetActive(true);
+                    lvUpCostText.text = UIHelper.ShowUseItem(PackType.Item, skinConfig.UnlockValue, skinConfig.UpNeedCnt);
+                    lvUpItemImg.SetItemSprite(skinConfig.UnlockValue);
+                }
+            }
+            else
+            {
+                lvUpBtn.SetActive(false);
+            }
+
+            if (HorseManager.Instance.selectSkinID == HorseManager.Instance.GetUsingHorseSkinID())
+            {
+                changeSkinBtn.SetActive(false);
+                inSkinMarkObj.SetActive(true);
+            }
+            else
+            {
+                changeSkinBtn.SetActive(true);
+                inSkinMarkObj.SetActive(false);
+            }
+
+            ShowTime();
+        }
+        else
+        {
+            changeOPObj.SetActive(false);
+            if (skinConfig.UnlockWay == 1)
+            {
+                unlockBtn.SetActive(false);
+            }
+            else
+            {
+                unlockBtn.SetActive(true);
+                unlockCostText.SetActive(true);
+                unlockCostText.text = UIHelper.ShowUseItem(PackType.Item, skinConfig.UnlockValue, skinConfig.UnlockNeedCnt);
+                unlockItemImg.SetItemSprite(skinConfig.UnlockValue);
+            }
+        }
+    }
+
+    void ShowTime()
+    {
+        bool isActive = HorseManager.Instance.IsSkinActive(HorseManager.Instance.selectSkinID);
+        var skinData = HorseManager.Instance.GetSkinData(HorseManager.Instance.selectSkinID);
+        var skinConfig = HorseSkinConfig.Get(HorseManager.Instance.selectSkinID);
+        if (isActive)
+        {
+            if (skinConfig.ExpireMinutes > 0)
+            {
+                timeText.SetActive(true);
+                timeText.text = Language.Get("PhantasmPavilion10", TimeUtility.SecondsToShortDHMS(skinData.EndTime - TimeUtility.AllSeconds));
+            }
+            else
+            {
+                timeText.SetActive(false);
+            }
+        }
+    }
+
+    void OnSecondEvent()
+    {
+        ShowTime();
+    }
+
+
+    void CreateScroller()
+    {
+        scroller.Refresh();
+        for (int i = 0; i < HorseManager.Instance.sortSkinList.Count; i++)
+        {
+            if (i % 3 == 0)
+            {
+                scroller.AddCell(ScrollerDataType.Header, i);
+            }
+        }
+        scroller.Restart();
+    }
+
+    void OnRefreshCell(ScrollerDataType type, CellView cell)
+    {
+        var _cell = cell as HorseSkinLineCell;
+        _cell.Display(cell.index);
+    }
+    
+
+    void OnSeeAttrBtnClick()
+    {
+        AttributeManager.Instance.OpenTotalAttributeWin(HorseManager.Instance.skinAttrDic);
+    }
+
+
+    string GetSkinAttrSring(HorseSkinConfig config)
+    {
+        var dict = HorseManager.Instance.GetAttrBySkinID(config);
+        var attrList = new List<string>();
+        var skinData = HorseManager.Instance.GetSkinData(config.SkinID);
+        if (skinData != null)
+        {
+            //鍗囨槦
+            if (skinData.Star == 0 || skinData.Star >= config.StarMax)
+            {
+                foreach (var kv in dict)
+                {
+                    attrList.Add(UIHelper.AppendColor(TextColType.itemchuanqi, PlayerPropertyConfig.GetFullDescription(kv.Key, kv.Value)));
+                }
+                return Language.Get("PhantasmPavilion07") + string.Join(Language.Get("L1112"), attrList);
+            }
+            else
+            {
+                int i = 0;
+                foreach (var kv in dict)
+                {
+                    attrList.Add(UIHelper.AppendColor(TextColType.itemchuanqi, PlayerPropertyConfig.GetFullDescription(kv.Key, kv.Value)) +
+                    Language.Get("PhantasmPavilion12", UIHelper.AppendColor(TextColType.Green, "+" + PlayerPropertyConfig.GetValueDescription(kv.Key, config.AttrPerStarAddList[i]))));
+                    i++;
+                }
+                return Language.Get("PhantasmPavilion07") + string.Join(Language.Get("L1112"), attrList);
+            }
+        }
+        else
+        {
+            foreach (var kv in dict)
+            {
+                attrList.Add(UIHelper.AppendColor(TextColType.itemchuanqi, PlayerPropertyConfig.GetFullDescription(kv.Key, kv.Value)));
+            }
+            return Language.Get("PhantasmPavilion07") + string.Join(Language.Get("L1112"), attrList);
+        }
+    }
+
+    void OnLvUpBtnClick()
+    {
+        var skinConfig = HorseSkinConfig.Get(HorseManager.Instance.selectSkinID);
+        if (ItemLogicUtility.CheckItemCount(PackType.Item, skinConfig.UnlockValue, skinConfig.UpNeedCnt, 2))
+        {
+            HorseManager.Instance.SendSkinOP(3, HorseManager.Instance.selectSkinID);
+        }
+    }
+    void OnChangeSkinBtnClick()
+    {
+        HorseManager.Instance.SendSkinOP(2, HorseManager.Instance.selectSkinID);
+    }
+
+    void OnUnlockBtnClick()
+    {
+        var skinConfig = HorseSkinConfig.Get(HorseManager.Instance.selectSkinID);
+        if (ItemLogicUtility.CheckItemCount(PackType.Item, skinConfig.UnlockValue, skinConfig.UnlockNeedCnt, 2))
+        {
+            HorseManager.Instance.SendSkinOP(1, HorseManager.Instance.selectSkinID);
+        }
+    }
+    
 }
\ No newline at end of file
diff --git a/Main/System/Horse/HorseWin.cs b/Main/System/Horse/HorseWin.cs
index 5ba4dcd..6d0a280 100644
--- a/Main/System/Horse/HorseWin.cs
+++ b/Main/System/Horse/HorseWin.cs
@@ -34,7 +34,10 @@
 
     protected override void InitComponent()
     {
-        skinBtn.AddListener(()=>{ UIManager.Instance.OpenWindow<HorseSkinWin>();});
+        skinBtn.AddListener(() =>
+        {
+            UIManager.Instance.OpenWindow<HorseSkinWin>();
+        });
         lvupBtn.AddListener(HorseUpgrade);
         lvupBtn.onPress.AddListener(HorseUpgrade);
         quickUpToggle.onValueChanged.AddListener((bool value)=>{ OnToggle(value);});
@@ -47,12 +50,14 @@
         //榛樿鍕鹃��
         quickUpToggle.isOn = !LocalSave.GetBool("HorseQuickUp" + PlayerDatas.Instance.baseData.PlayerID);
         HorseManager.Instance.OnHorseUpdateEvent += OnHorseUpdateEvent;
+        PlayerDatas.Instance.playerDataRefreshEvent += PlayerDataRefresh;
         Display();
     }
 
     protected override void OnPreClose()
     {
         HorseManager.Instance.OnHorseUpdateEvent -= OnHorseUpdateEvent;
+        PlayerDatas.Instance.playerDataRefreshEvent -= PlayerDataRefresh;
     }
 
     void OnHorseUpdateEvent()
@@ -99,7 +104,7 @@
 
     void Display()
     {
-        var skinConfig = HorseSkinConfig.Get(HorseManager.Instance.GetHorseSkinID());
+        var skinConfig = HorseSkinConfig.Get(HorseManager.Instance.GetUsingHorseSkinID());
         modelImg.Create(skinConfig.SkinID, PhantasmPavilionManager.Instance.GetMyModelSkinID());
         nameText.text = skinConfig.Name;
         specialAttrText.text = GetSpecialAttr();
@@ -207,6 +212,19 @@
             costText.text = UIHelper.ShowUseItem(PackType.Item, HorseManager.Instance.lvUPItemID, useCnt);
         }
 
-        
+
+    }
+    
+    void PlayerDataRefresh(PlayerDataType type)
+    {
+        switch (type)
+        {
+            case PlayerDataType.EquipShowSwitch:
+                var skinConfig = HorseSkinConfig.Get(HorseManager.Instance.GetUsingHorseSkinID());
+                modelImg.Create(skinConfig.SkinID, PhantasmPavilionManager.Instance.GetMyModelSkinID());
+                break;
+        }
+
+
     }
 }
\ No newline at end of file
diff --git a/Main/System/Main/HomeWin.cs b/Main/System/Main/HomeWin.cs
index 151229e..c6e0127 100644
--- a/Main/System/Main/HomeWin.cs
+++ b/Main/System/Main/HomeWin.cs
@@ -165,6 +165,7 @@
         OfficialRankManager.Instance.OnOfficialCanLVUpEvent += OnOfficialCanLVUpEvent;
         FirstChargeManager.Instance.OnFirstChargeTaskUpdateEvent += OnFirstChargeTaskUpdateEvent;
         OfficialRankManager.Instance.RealmMissionRefreshEvent += OnOfficialCanLVUpEvent;
+        HorseManager.Instance.OnHorseUpdateEvent += DisplayHorse;
         Display();
         DisplayFirstChargeBtn();
 
@@ -190,6 +191,7 @@
         OfficialRankManager.Instance.OnOfficialCanLVUpEvent -= OnOfficialCanLVUpEvent;
         FirstChargeManager.Instance.OnFirstChargeTaskUpdateEvent -= OnFirstChargeTaskUpdateEvent;
         OfficialRankManager.Instance.RealmMissionRefreshEvent -= OnOfficialCanLVUpEvent;
+        HorseManager.Instance.OnHorseUpdateEvent -= DisplayHorse;
         //  鍏抽棴鐨勬椂鍊欐妸鎴樻枟鐣岄潰涔熺粰鍏充簡 铏界劧鏄湪澶栭潰寮�鐨�
         UIManager.Instance.CloseWindow<BattleWin>();
     }
@@ -239,6 +241,9 @@
 
             case PlayerDataType.RealmLevel:
                 OnOfficialCanLVUpEvent();
+                break;
+            case PlayerDataType.EquipShowSwitch:
+                DisplayHorse();
                 break;
         }
 
@@ -504,7 +509,7 @@
         {
             horseBGImg.SetActive(true);
             //equipShowSwitch;//褰撳墠閰嶇疆鐨勫潗楠戝瑙侷D瀛樺偍鍦紙鏈�澶ф敮鎸� 1~999锛�
-            var skinConfig = HorseSkinConfig.Get(HorseManager.Instance.GetHorseSkinID());
+            var skinConfig = HorseSkinConfig.Get(HorseManager.Instance.GetUsingHorseSkinID(true));
             horseImg.Create(skinConfig.SkinID, 0, 0.6f);
             horseLVText.text = Language.Get("Horse8",HorseManager.Instance.classLV, HorseManager.Instance.horseLV);
         }
diff --git a/Main/System/Redpoint/MainRedDot.cs b/Main/System/Redpoint/MainRedDot.cs
index d9d482f..0ff1c94 100644
--- a/Main/System/Redpoint/MainRedDot.cs
+++ b/Main/System/Redpoint/MainRedDot.cs
@@ -61,6 +61,8 @@
 
     //鍧愰獞
     public const int RedPoint_HorseKey = 108;
+    Redpoint redPointHorse = new Redpoint(RedPoint_HorseKey);
+    
     //鐗规潈鍗�
     public const int RedPoint_PrivilegeCard = 109;
     Redpoint pcardRedpoint = new Redpoint(RedPoint_PrivilegeCard);
diff --git a/Main/System/TianziBillborad/TianziBillboradPlayerTop3Cell.cs b/Main/System/TianziBillborad/TianziBillboradPlayerTop3Cell.cs
index a7d192b..a842cd2 100644
--- a/Main/System/TianziBillborad/TianziBillboradPlayerTop3Cell.cs
+++ b/Main/System/TianziBillborad/TianziBillboradPlayerTop3Cell.cs
@@ -13,7 +13,7 @@
     [SerializeField] Text nameText;
     [SerializeField] OfficialTitleCell officialTitleCell;
     [SerializeField] Button queryPlayerBtn; //鍚庣画娣诲姞鐐瑰嚮鏌ョ湅鐜╁璇︽儏
-    [SerializeField] UIHeroController model;
+    [SerializeField] HorseController model;
 
     public void Display(int rankType, int rank, string valueFormat = "{0}")
     {
@@ -29,7 +29,7 @@
         rankValueText.text = string.Format(valueFormat, UIHelper.ReplaceLargeNum(rankData.cmpValue2 + rankData.cmpValue * Constants.ExpPointValue));
         nameText.text = rankData.name1;
         officialTitleCell.InitUI((int)rankData.value1, (int)rankData.value2);
-        model.Create((int)rankData.value5, 1);
+        model.Create(HorseManager.Instance.GetOtherPlayerHorseSkinID((int)rankData.value6), (int)rankData.value5, 1);
     }
 
 
diff --git a/Main/System/UIBase/UIBase.cs b/Main/System/UIBase/UIBase.cs
index 0f76fe6..6eec54a 100644
--- a/Main/System/UIBase/UIBase.cs
+++ b/Main/System/UIBase/UIBase.cs
@@ -150,7 +150,11 @@
         }
 
         if (screenMask != null)
+        {
+            screenMask.GetComponent<Image>().color = new Color(0, 0, 0, 0.7f);
             screenMask.transform.SetAsFirstSibling();
+            
+        }
         InitClickEmptySpaceBtn();
 
     }

--
Gitblit v1.8.0