From fe2805709cbd8cee561b475eda53674fbf06ef9e Mon Sep 17 00:00:00 2001
From: lcy <1459594991@qq.com>
Date: 星期二, 23 六月 2026 17:02:26 +0800
Subject: [PATCH] 662 武将试炼
---
Main/System/ChallengeTab/ArenaTabHandler.cs | 5
Main/System/Battle/BattleConst.cs | 7
Main/Config/ConfigManager.cs | 6
Main/System/HeroTrial/HeroTrialBattleWin.cs.meta | 11
Main/System/ChallengeTab/BaseChallengeTabHandler.cs | 14
Main/System/HeroTrial/HeroTrialFailWin.cs | 54 +
Main/System/Redpoint/MainRedDot.cs | 1
Main/System/HeroTrial/HeroTrialVictoryWin.cs | 85 +
Main/System/Battle/BattleFieldFactory.cs | 5
Main/System/HeroTrial/CarouselView.cs | 236 +++++
Main/System/HeroTrial/HeroTrialBattleWin.cs | 292 ++++++
Main/System/HeroTrial/HeroTrialWin.cs | 171 +++
Main/System/Settlement/BattleSettlementManager.cs | 16
Main/Config/Configs/HeroTrialConfig.cs | 80 +
Main/System/HeroTrial/HeroTrialHeroItem.cs.meta | 11
Main/Main.cs | 1
Main/Config/PartialConfigs/HeroTrialConfig.cs.meta | 11
Main/System/ChallengeTab/ChallengeTabButton.cs | 10
Main/Core/NetworkPackage/ServerPack/HB2_ActionMap/HB203_tagSCHeroTrialInfo.cs | 29
Main/Config/Configs/HeroTrialConfig.cs.meta | 11
Main/System/HeroTrial/HeroTrialManager.cs | 811 ++++++++++++++++++
Main/Config/PartialConfigs/HeroTrialConfig.cs | 109 ++
Main/Core/NetworkPackage/ServerPack/HB2_ActionMap/HB203_tagSCHeroTrialInfo.cs.meta | 11
Main/Utility/EnumHelper.cs | 1
Main/System/HeroTrial/HeroTrialCarouselView.cs.meta | 11
Main/System/HeroTrial/HeroTrialCell.cs.meta | 11
Main/System/HeroTrial/HeroTrialManager.cs.meta | 11
Main/System/ChallengeTab/HeroTrialTabHandler.cs.meta | 11
Main/System/HeroTrial/HeroTrialWin.cs.meta | 11
Main/Core/NetworkPackage/DTCFile/ServerPack/HB2_ActionMap/DTCB203_tagSCHeroTrialInfo.cs.meta | 11
Main/System/Battle/BattleField/HeroTrialBattleField.cs.meta | 11
Main/Core/NetworkPackage/DTCFile/ServerPack/HB2_ActionMap/DTCB203_tagSCHeroTrialInfo.cs | 12
Main/System/HeroTrial/HeroTrialCarouselView.cs | 104 ++
Main/System/ChallengeTab/HeroTrialTabHandler.cs | 57 +
Main/System/Battle/BattleField/HeroTrialBattleField.cs | 130 ++
Main/System/HeroTrial/HeroTrialCell.cs | 114 ++
Main/System/HeroTrial/HeroTrialHeroItem.cs | 85 +
Main/System/HeroTrial/HeroTrialVictoryWin.cs.meta | 11
Main/System/WarlordPavilion/TowerBaseWin.cs | 6
Main/Core/NetworkPackage/DataToCtl/PackageRegedit.cs | 3
Main/System/HeroTrial.meta | 8
Main/System/HeroTrial/HeroTrialFailWin.cs.meta | 11
Main/System/ChallengeTab/WarlordPavilionTabHandler.cs | 5
Main/System/ChallengeTab/QunyingTabHandler.cs | 5
Main/System/ChallengeTab/BoneFieldTabHandler.cs | 5
Main/System/ChallengeTab/TianziBillboradTabHandler.cs | 5
Main/System/HeroTrial/CarouselView.cs.meta | 11
47 files changed, 2,614 insertions(+), 23 deletions(-)
diff --git a/Main/Config/ConfigManager.cs b/Main/Config/ConfigManager.cs
index e5612c4..5c842f4 100644
--- a/Main/Config/ConfigManager.cs
+++ b/Main/Config/ConfigManager.cs
@@ -90,6 +90,8 @@
typeof(HeroLineupHaloConfig),
typeof(HeroQualityLVConfig),
typeof(HeroSkinAttrConfig),
+ typeof(HeroTalentConfig),
+ typeof(HeroTrialConfig),
typeof(HorseClassConfig),
typeof(HorseIDConfig),
typeof(HorseSkinConfig),
@@ -384,6 +386,10 @@
ClearConfigDictionary<HeroQualityLVConfig>();
// 娓呯┖ HeroSkinAttrConfig 瀛楀吀
ClearConfigDictionary<HeroSkinAttrConfig>();
+ // 娓呯┖ HeroTalentConfig 瀛楀吀
+ ClearConfigDictionary<HeroTalentConfig>();
+ // 娓呯┖ HeroTrialConfig 瀛楀吀
+ ClearConfigDictionary<HeroTrialConfig>();
// 娓呯┖ HorseClassConfig 瀛楀吀
ClearConfigDictionary<HorseClassConfig>();
// 娓呯┖ HorseIDConfig 瀛楀吀
diff --git a/Main/Config/Configs/HeroTrialConfig.cs b/Main/Config/Configs/HeroTrialConfig.cs
new file mode 100644
index 0000000..6e0a945
--- /dev/null
+++ b/Main/Config/Configs/HeroTrialConfig.cs
@@ -0,0 +1,80 @@
+锘�//--------------------------------------------------------
+// [Author]: YYL
+// [ Date ]: Tuesday, June 9, 2026
+//--------------------------------------------------------
+
+using System.Collections.Generic;
+using System;
+using UnityEngine;
+using LitJson;
+
+public partial class HeroTrialConfig : ConfigBase<int, HeroTrialConfig>
+{
+ static HeroTrialConfig()
+ {
+ // 璁块棶杩囬潤鎬佹瀯閫犲嚱鏁�
+ visit = true;
+ }
+
+ public int CfgID;
+ public int HeroID;
+ public int LevelNum;
+ public int LVLimit;
+ public int RealmLimit;
+ public int[][] PassAwardList;
+ public int[] LineupIDList;
+ public int NPCLV;
+ public float Difficulty;
+ public long FightPower;
+ public int SortOrder;
+
+ public override int LoadKey(string _key)
+ {
+ int key = GetKey(_key);
+ return key;
+ }
+
+ public override void LoadConfig(string input)
+ {
+ try {
+ string[] tables = input.Split('\t');
+ int.TryParse(tables[0],out CfgID);
+
+ int.TryParse(tables[1],out HeroID);
+
+ int.TryParse(tables[2],out LevelNum);
+
+ int.TryParse(tables[3],out LVLimit);
+
+ int.TryParse(tables[4],out RealmLimit);
+
+ PassAwardList = JsonMapper.ToObject<int[][]>(tables[5].Replace("(", "[").Replace(")", "]"));
+
+ if (tables[6].Contains("["))
+ {
+ LineupIDList = JsonMapper.ToObject<int[]>(tables[6]);
+ }
+ else
+ {
+ string[] LineupIDListStringArray = tables[6].Trim().Split(StringUtility.splitSeparator,StringSplitOptions.RemoveEmptyEntries);
+ LineupIDList = new int[LineupIDListStringArray.Length];
+ for (int i=0;i<LineupIDListStringArray.Length;i++)
+ {
+ int.TryParse(LineupIDListStringArray[i],out LineupIDList[i]);
+ }
+ }
+
+ int.TryParse(tables[7],out NPCLV);
+
+ float.TryParse(tables[8],out Difficulty);
+
+ long.TryParse(tables[9],out FightPower);
+
+ int.TryParse(tables[10],out SortOrder);
+ }
+ catch (Exception exception)
+ {
+ Debug.LogError(exception);
+ }
+ }
+}
diff --git a/Main/Config/Configs/HeroTrialConfig.cs.meta b/Main/Config/Configs/HeroTrialConfig.cs.meta
new file mode 100644
index 0000000..aa0347c
--- /dev/null
+++ b/Main/Config/Configs/HeroTrialConfig.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 86da543a944b1b94c8cfd3581f709bd8
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Main/Config/PartialConfigs/HeroTrialConfig.cs b/Main/Config/PartialConfigs/HeroTrialConfig.cs
new file mode 100644
index 0000000..0894010
--- /dev/null
+++ b/Main/Config/PartialConfigs/HeroTrialConfig.cs
@@ -0,0 +1,109 @@
+using System.Collections.Generic;
+using System.Linq;
+
+public partial class HeroTrialConfig : ConfigBase<int, HeroTrialConfig>
+{
+ //<姝﹀皢ID,<鍏冲崱缂栧彿,閰嶇疆ID>>
+ static Dictionary<int, Dictionary<int, int>> idDict = new Dictionary<int, Dictionary<int, int>>();
+
+ protected override void OnConfigParseCompleted()
+ {
+ if (!idDict.ContainsKey(HeroID))
+ {
+ idDict[HeroID] = new Dictionary<int, int>();
+ }
+ idDict[HeroID][LevelNum] = CfgID;
+ }
+
+ public static bool TryGetHeroLevelDict(int heroID, out Dictionary<int, int> dict)
+ {
+ dict = null;
+ return idDict != null && idDict.TryGetValue(heroID, out dict);
+ }
+
+ public static bool TryGetConfigID(int heroID, int levelNum, out int cfgID)
+ {
+ cfgID = 0;
+ return idDict != null && idDict.TryGetValue(heroID, out var dict) && dict.TryGetValue(levelNum, out cfgID);
+ }
+
+ public static bool TryGetHeroTrialConfig(int heroID, int levelNum, out HeroTrialConfig config)
+ {
+ config = null;
+ if (TryGetConfigID(heroID, levelNum, out int cfgID) && HasKey(cfgID))
+ {
+ config = Get(cfgID);
+ return true;
+ }
+ return false;
+ }
+
+ public static bool TryGetHeroTrialConfig(int cfgID, out HeroTrialConfig config)
+ {
+ config = null;
+ if (HasKey(cfgID))
+ {
+ config = Get(cfgID);
+ return true;
+ }
+ return false;
+ }
+
+ public static List<int> GetSortedHeroIDList()
+ {
+ if (idDict.IsNullOrEmpty())
+ return new List<int>();
+
+ return idDict.Keys
+ .OrderBy(GetHeroSortOrder)
+ .ThenBy(heroID => heroID)
+ .ToList();
+ }
+
+ public static bool TryGetHeroConfigList(int heroID, out List<HeroTrialConfig> configs)
+ {
+ configs = null;
+ if (!TryGetHeroLevelDict(heroID, out var dict) || dict.IsNullOrEmpty())
+ return false;
+
+ configs = dict.Keys
+ .OrderBy(levelNum => levelNum)
+ .Select(levelNum => TryGetHeroTrialConfig(heroID, levelNum, out var config) ? config : null)
+ .Where(config => config != null)
+ .ToList();
+
+ return !configs.IsNullOrEmpty();
+ }
+
+ public static bool TryGetFirstLevelConfig(int heroID, out HeroTrialConfig config)
+ {
+ config = null;
+ if (!TryGetHeroLevelDict(heroID, out var dict) || dict.IsNullOrEmpty())
+ return false;
+
+ int levelNum = dict.Keys.Min();
+ return TryGetHeroTrialConfig(heroID, levelNum, out config);
+ }
+
+ public static bool TryGetMaxLevelNum(int heroID, out int maxLevelNum)
+ {
+ maxLevelNum = 0;
+ if (!TryGetHeroLevelDict(heroID, out var dict) || dict.IsNullOrEmpty())
+ return false;
+
+ maxLevelNum = dict.Keys.Max();
+ return true;
+ }
+
+ public static bool TryGetNextLevelConfig(int heroID, int passLevelNum, out HeroTrialConfig config)
+ {
+ config = null;
+ int nextLevelNum = passLevelNum + 1;
+ return TryGetHeroTrialConfig(heroID, nextLevelNum, out config);
+ }
+
+ static int GetHeroSortOrder(int heroID)
+ {
+ return TryGetFirstLevelConfig(heroID, out var config) ? config.SortOrder : int.MaxValue;
+ }
+}
diff --git a/Main/Config/PartialConfigs/HeroTrialConfig.cs.meta b/Main/Config/PartialConfigs/HeroTrialConfig.cs.meta
new file mode 100644
index 0000000..994d636
--- /dev/null
+++ b/Main/Config/PartialConfigs/HeroTrialConfig.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 8b0e016a7a404a9fa2d61f7327a1fb3f
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Main/Core/NetworkPackage/DTCFile/ServerPack/HB2_ActionMap/DTCB203_tagSCHeroTrialInfo.cs b/Main/Core/NetworkPackage/DTCFile/ServerPack/HB2_ActionMap/DTCB203_tagSCHeroTrialInfo.cs
new file mode 100644
index 0000000..ef59fc6
--- /dev/null
+++ b/Main/Core/NetworkPackage/DTCFile/ServerPack/HB2_ActionMap/DTCB203_tagSCHeroTrialInfo.cs
@@ -0,0 +1,12 @@
+using UnityEngine;
+using System.Collections;
+
+// B2 03 姝﹀皢璇曠偧淇℃伅 #tagSCHeroTrialInfo
+
+public class DTCB203_tagSCHeroTrialInfo : DtcBasic {
+ public override void Done(GameNetPackBasic vNetPack) {
+ base.Done(vNetPack);
+ HB203_tagSCHeroTrialInfo vNetData = vNetPack as HB203_tagSCHeroTrialInfo;
+ HeroTrialManager.Instance.UpdateHeroTrialInfo(vNetData);
+ }
+}
diff --git a/Main/Core/NetworkPackage/DTCFile/ServerPack/HB2_ActionMap/DTCB203_tagSCHeroTrialInfo.cs.meta b/Main/Core/NetworkPackage/DTCFile/ServerPack/HB2_ActionMap/DTCB203_tagSCHeroTrialInfo.cs.meta
new file mode 100644
index 0000000..8419bed
--- /dev/null
+++ b/Main/Core/NetworkPackage/DTCFile/ServerPack/HB2_ActionMap/DTCB203_tagSCHeroTrialInfo.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 0b6aa926e39b4ec4aa2d86f794c9ed8a
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Main/Core/NetworkPackage/DataToCtl/PackageRegedit.cs b/Main/Core/NetworkPackage/DataToCtl/PackageRegedit.cs
index d5a1737..57c672d 100644
--- a/Main/Core/NetworkPackage/DataToCtl/PackageRegedit.cs
+++ b/Main/Core/NetworkPackage/DataToCtl/PackageRegedit.cs
@@ -141,6 +141,7 @@
Register(typeof(HB040_tagSCTravelInfo), typeof(DTCB040_tagSCTravelInfo));
Register(typeof(HA3C7_tagMCGubaoInfo), typeof(DTCA3C7_tagMCGubaoInfo));
Register(typeof(HB202_tagSCDingjungeInfo), typeof(DTCB202_tagSCDingjungeInfo));
+ Register(typeof(HB203_tagSCHeroTrialInfo), typeof(DTCB203_tagSCHeroTrialInfo));
Register(typeof(HB432_tagSCViewNPCAttrRet), typeof(DTCB432_tagSCViewNPCAttrRet));
Register(typeof(HA503_tagSCFamilyTaofaInfo), typeof(DTCA503_tagSCFamilyTaofaInfo));
Register(typeof(HA504_tagSCFamilyTaofaAtkRet), typeof(DTCA504_tagSCFamilyTaofaAtkRet));
@@ -259,4 +260,4 @@
}
return null;
}
-}
\ No newline at end of file
+}
diff --git a/Main/Core/NetworkPackage/ServerPack/HB2_ActionMap/HB203_tagSCHeroTrialInfo.cs b/Main/Core/NetworkPackage/ServerPack/HB2_ActionMap/HB203_tagSCHeroTrialInfo.cs
new file mode 100644
index 0000000..da3504d
--- /dev/null
+++ b/Main/Core/NetworkPackage/ServerPack/HB2_ActionMap/HB203_tagSCHeroTrialInfo.cs
@@ -0,0 +1,29 @@
+using UnityEngine;
+using System.Collections;
+
+// B2 03 姝﹀皢璇曠偧淇℃伅 #tagSCHeroTrialInfo
+
+public class HB203_tagSCHeroTrialInfo : GameNetPackBasic {
+ public byte Count;
+ public tagSCHeroTrial[] HeroTrialList;
+
+ public HB203_tagSCHeroTrialInfo () {
+ _cmd = (ushort)0xB203;
+ }
+
+ public override void ReadFromBytes (byte[] vBytes) {
+ TransBytes (out Count, vBytes, NetDataType.BYTE);
+ HeroTrialList = new tagSCHeroTrial[Count];
+ for (int i = 0; i < Count; i ++) {
+ HeroTrialList[i] = new tagSCHeroTrial();
+ TransBytes (out HeroTrialList[i].HeroID, vBytes, NetDataType.DWORD);
+ TransBytes (out HeroTrialList[i].PassLevelNum, vBytes, NetDataType.BYTE);
+ }
+ }
+
+ public class tagSCHeroTrial {
+ public uint HeroID; // 姝﹀皢ID
+ public byte PassLevelNum; // 鏈灏嗗凡杩囧叧鍒扮殑鍏冲崱缂栧彿
+ }
+
+}
diff --git a/Main/Core/NetworkPackage/ServerPack/HB2_ActionMap/HB203_tagSCHeroTrialInfo.cs.meta b/Main/Core/NetworkPackage/ServerPack/HB2_ActionMap/HB203_tagSCHeroTrialInfo.cs.meta
new file mode 100644
index 0000000..5a7b0a0
--- /dev/null
+++ b/Main/Core/NetworkPackage/ServerPack/HB2_ActionMap/HB203_tagSCHeroTrialInfo.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: c0adf674df224db49ab40b4853819da8
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Main/Main.cs b/Main/Main.cs
index b6a8b0f..2d87679 100644
--- a/Main/Main.cs
+++ b/Main/Main.cs
@@ -125,6 +125,7 @@
managers.Add(SuperVipManager.Instance);
managers.Add(HeroSkinFlashSaleManager.Instance);
managers.Add(GuildAtkDefBatManager.Instance);
+ managers.Add(HeroTrialManager.Instance);
foreach (var manager in managers)
{
diff --git a/Main/System/Battle/BattleConst.cs b/Main/System/Battle/BattleConst.cs
index 8c83f47..c425101 100644
--- a/Main/System/Battle/BattleConst.cs
+++ b/Main/System/Battle/BattleConst.cs
@@ -13,6 +13,7 @@
typeof(BoneFieldBattleWin),
typeof(TianziBillboradBattleWin),
typeof(WarlordPavilionBattleWin),
+ typeof(HeroTrialBattleWin),
typeof(PreviewBattleWin),
typeof(QYBattleWin),
typeof(QYBattleWin),
@@ -26,6 +27,7 @@
public const string BoneBattleField = "BoneBattleField";
public const string TianziBillboradBattleField = "TianziBillboradBattleField";
public const string WarlordPavilionBattleField = "WarlordPavilionBattleField";
+ public const string HeroTrialBattleField = "HeroTrialBattleField";
public const string PriviewBattleField = "PriviewBattleField"; //棰勮鎴樻枟
public const string QYBattleField = "QYBattleField";
public const string GuildAtkDefBatField = "GuildAtkDefBatField";
@@ -38,6 +40,7 @@
{ BoneBattleField, "BoneFieldBattleWin" },
{ TianziBillboradBattleField, "TianziBillboradBattleWin" },
{ WarlordPavilionBattleField, "WarlordPavilionBattleWin" },
+ { HeroTrialBattleField, "HeroTrialBattleWin" },
{ PriviewBattleField, "PreviewBattleWin" },
{ QYBattleField, "QYBattleWin" },
{ GuildAtkDefBatField, "GuildAtkDefBatBattleWin" },
@@ -54,6 +57,7 @@
{ PriviewBattleField, 6 },
{ QYBattleField, 7 },
{ GuildAtkDefBatField, 8 },
+ { HeroTrialBattleField, 9 },
};
//鍜� CreateBattleField 閲岀殑瀵瑰簲
@@ -65,6 +69,7 @@
{30010, BoneBattleField},
{30020, TianziBillboradBattleField},
{30030, WarlordPavilionBattleField},
+ {30040, HeroTrialBattleField},
{30000, PriviewBattleField},
{32000, QYBattleField},
{33000, GuildAtkDefBatField},
@@ -294,4 +299,4 @@
public const int DodgeSoundID = 5999999; // 闂伩闊虫晥ID
#endregion
-}
\ No newline at end of file
+}
diff --git a/Main/System/Battle/BattleField/HeroTrialBattleField.cs b/Main/System/Battle/BattleField/HeroTrialBattleField.cs
new file mode 100644
index 0000000..73ef1a5
--- /dev/null
+++ b/Main/System/Battle/BattleField/HeroTrialBattleField.cs
@@ -0,0 +1,130 @@
+using Cysharp.Threading.Tasks;
+using LitJson;
+using System.Collections.Generic;
+using System.Linq;
+
+public class HeroTrialBattleField : BattleField
+{
+ HeroTrialConfig trialConfig;
+
+ public HeroTrialBattleField(string _guid) : base(_guid)
+ {
+ }
+
+ public override async UniTask Init(int MapID, int FuncLineID, JsonData _extendData,
+ List<TeamBase> _redTeamList, List<TeamBase> _blueTeamList, byte turnMax)
+ {
+ await base.Init(MapID, FuncLineID, _extendData, _redTeamList, _blueTeamList, turnMax);
+
+ extendData = _extendData;
+ HeroTrialManager.Instance.TryGetConfigByFuncLineID(FuncLineID, out trialConfig);
+
+ SetBattleMode(BattleMode.Record);
+ }
+
+ public override void AutoSetBattleMode()
+ {
+ SetBattleMode(BattleMode.Record);
+ }
+
+ public override void TurnFightState(int TurnNum, int State,
+ uint FuncLineID, JsonData extendData)
+ {
+ base.TurnFightState(TurnNum, State, FuncLineID, extendData);
+
+ switch (State)
+ {
+ case 0:
+ break;
+ case 1:
+ break;
+ case 2:
+ break;
+ case 3:
+ break;
+ case 4:
+ break;
+ case 5:
+ break;
+ default:
+ BattleDebug.LogError("recieve a unknown State");
+ break;
+ }
+ }
+
+ protected override void OnSettlement(JsonData turnFightStateData)
+ {
+ base.OnSettlement(turnFightStateData);
+ }
+
+ public override void WhaleFall()
+ {
+ AutoFightModel.Instance.isPause = false;
+ Destroy();
+
+ if (UIManager.Instance.IsOpened<HeroTrialBattleWin>())
+ {
+ UIManager.Instance.CloseWindow<HeroTrialBattleWin>();
+ UIManager.Instance.OpenWindowAsync<TowerBaseWin>(1).Forget();
+ }
+ }
+
+ public override void Run()
+ {
+ if (operationAgent == null)
+ {
+ return;
+ }
+ base.Run();
+ }
+
+ public override void DistributeNextPackage()
+ {
+ if (IsBattleFinish)
+ return;
+
+ BattleManager.Instance.DistributeNextReportPackage(guid);
+ }
+
+ public override async void ShowWindow(HB424_tagSCTurnFightInit vNetData)
+ {
+ HeroTrialBattleWin battleWin = UIManager.Instance.GetUI<HeroTrialBattleWin>();
+ if (battleWin == null)
+ {
+ battleWin = await UIManager.Instance.OpenWindowAsync<HeroTrialBattleWin>();
+ }
+ battleWin.SetBattleField(this);
+
+ if (UIManager.Instance.IsOpened<TowerBaseWin>())
+ {
+ UIManager.Instance.CloseWindow<TowerBaseWin>();
+ }
+ }
+
+ public NPCLineupConfig GetBossLineupConfig()
+ {
+ if (trialConfig == null && !HeroTrialManager.Instance.TryGetConfigByFuncLineID(FuncLineID, out trialConfig))
+ return null;
+
+ int[] lineupIDList = trialConfig.LineupIDList;
+ if (lineupIDList.IsNullOrEmpty())
+ return null;
+
+ int lineupID = lineupIDList[0];
+ if (!NPCLineupConfig.HasKey(lineupID))
+ return null;
+
+ return NPCLineupConfig.Get(lineupID);
+ }
+
+ public override BattleObject FindBoss()
+ {
+ var config = GetBossLineupConfig();
+ if (config != null)
+ {
+ int bossId = config.BossID;
+ return battleObjMgr.allBattleObjDict.Values.FirstOrDefault(bo => bo.GetNPCID() == bossId);
+ }
+ return null;
+ }
+}
diff --git a/Main/System/Battle/BattleField/HeroTrialBattleField.cs.meta b/Main/System/Battle/BattleField/HeroTrialBattleField.cs.meta
new file mode 100644
index 0000000..ea30de5
--- /dev/null
+++ b/Main/System/Battle/BattleField/HeroTrialBattleField.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 51d9ddba2fa6f604497431ded91e9081
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Main/System/Battle/BattleFieldFactory.cs b/Main/System/Battle/BattleFieldFactory.cs
index df43786..d62cb1c 100644
--- a/Main/System/Battle/BattleFieldFactory.cs
+++ b/Main/System/Battle/BattleFieldFactory.cs
@@ -32,6 +32,9 @@
case 30030:
battleField = new WarlordPavilionBattleField(guid);
break;
+ case 30040:
+ battleField = new HeroTrialBattleField(guid);
+ break;
case 30000:
battleField = new PriviewBattleField(guid);
break;
@@ -52,4 +55,4 @@
}
-}
\ No newline at end of file
+}
diff --git a/Main/System/ChallengeTab/ArenaTabHandler.cs b/Main/System/ChallengeTab/ArenaTabHandler.cs
index 72d02ee..16798f2 100644
--- a/Main/System/ChallengeTab/ArenaTabHandler.cs
+++ b/Main/System/ChallengeTab/ArenaTabHandler.cs
@@ -3,7 +3,8 @@
public class ArenaTabHandler : BaseChallengeTabHandler
{
- protected override int GetIndex() => 1;
+ protected override string GetIconKey() => "ChallengeTab1";
+ protected override string GetNameText() => Language.Get("ChallengeTab1");
protected override int GetOpenState() => 0; // 0=FuncID
protected override int GetFuncId() => ArenaManager.Instance.funcId;
protected override int GetRedpointId() => MainRedDot.ArenaRepoint;
@@ -63,4 +64,4 @@
}
}
-}
\ No newline at end of file
+}
diff --git a/Main/System/ChallengeTab/BaseChallengeTabHandler.cs b/Main/System/ChallengeTab/BaseChallengeTabHandler.cs
index 04b67b8..49ba4ad 100644
--- a/Main/System/ChallengeTab/BaseChallengeTabHandler.cs
+++ b/Main/System/ChallengeTab/BaseChallengeTabHandler.cs
@@ -17,7 +17,8 @@
// 鍒濆鍖栦竴娆� DisplayData锛屼箣鍚庡彧淇敼鍙樺寲鐨勫瓧娈�
displayData = new ChallengeTabButton.DisplayData
{
- Index = GetIndex(),
+ IconKey = GetIconKey(),
+ NameText = GetNameText(),
RedpointId = GetRedpointId(),
OpenState = GetOpenState(),
FuncId = GetFuncId(),
@@ -67,9 +68,14 @@
}
/// <summary>
- /// 鑾峰彇Tab鐨勭储寮曪紙鐢ㄤ簬Icon鍜孨ame锛�
+ /// 鑾峰彇Tab鍥炬爣Key
/// </summary>
- protected abstract int GetIndex();
+ protected abstract string GetIconKey();
+
+ /// <summary>
+ /// 鑾峰彇Tab鏄剧ず鍚嶇О
+ /// </summary>
+ protected abstract string GetNameText();
/// <summary>
/// 鑾峰彇寮�鍚柟寮� (0=FuncID, 1=娲诲姩)
@@ -105,4 +111,4 @@
/// 鍙栨秷璁㈤槄姝ab鐗规湁鐨勪簨浠�
/// </summary>
protected abstract void UnsubscribeFromSpecificEvents();
-}
\ No newline at end of file
+}
diff --git a/Main/System/ChallengeTab/BoneFieldTabHandler.cs b/Main/System/ChallengeTab/BoneFieldTabHandler.cs
index d3f48a6..388c1ad 100644
--- a/Main/System/ChallengeTab/BoneFieldTabHandler.cs
+++ b/Main/System/ChallengeTab/BoneFieldTabHandler.cs
@@ -3,7 +3,8 @@
public class BoneFieldTabHandler : BaseChallengeTabHandler
{
- protected override int GetIndex() => 2;
+ protected override string GetIconKey() => "ChallengeTab2";
+ protected override string GetNameText() => Language.Get("ChallengeTab2");
protected override int GetOpenState() => 0; // 0=FuncID
protected override int GetFuncId() => (int)FuncOpenEnum.BoneBattle;
protected override int GetRedpointId() => MainRedDot.BoneFieldRepoint;
@@ -75,4 +76,4 @@
Refresh();
}
}
-}
\ No newline at end of file
+}
diff --git a/Main/System/ChallengeTab/ChallengeTabButton.cs b/Main/System/ChallengeTab/ChallengeTabButton.cs
index 591620d..67282b2 100644
--- a/Main/System/ChallengeTab/ChallengeTabButton.cs
+++ b/Main/System/ChallengeTab/ChallengeTabButton.cs
@@ -16,7 +16,8 @@
public struct DisplayData
{
- public int Index;
+ public string IconKey;
+ public string NameText;
public int RedpointId;
public int OpenState;//0 FuncID 1 娲诲姩
public int FuncId;
@@ -68,9 +69,8 @@
txtLockInfo.SetActive(!isOpen);
// 璁剧疆鍥炬爣鍜屽悕绉�
- string spriteAndLangKey = StringUtility.Concat("ChallengeTab", data.Index.ToString());
- imgIcon.SetSprite(spriteAndLangKey);
- txtName.text = Language.Get(spriteAndLangKey);
+ imgIcon.SetSprite(data.IconKey);
+ txtName.text = data.NameText;
// 璁剧疆TIPS鏂囨湰
txtCount.text = data.CountInfo;
@@ -79,4 +79,4 @@
// 瀛樺偍鐐瑰嚮鍥炶皟
this._onClickAction = data.OnClickAction;
}
-}
\ No newline at end of file
+}
diff --git a/Main/System/ChallengeTab/HeroTrialTabHandler.cs b/Main/System/ChallengeTab/HeroTrialTabHandler.cs
new file mode 100644
index 0000000..0c83d3a
--- /dev/null
+++ b/Main/System/ChallengeTab/HeroTrialTabHandler.cs
@@ -0,0 +1,57 @@
+锘縰sing System;
+using UnityEngine;
+using Cysharp.Threading.Tasks;
+
+public class HeroTrialTabHandler : BaseChallengeTabHandler
+{
+ protected override string GetIconKey() => "ChallengeTab7";
+ protected override string GetNameText() => Language.Get("ChallengeTab7");
+ protected override int GetOpenState() => 0; // 0=FuncID
+ protected override int GetFuncId() => (int)FuncOpenEnum.HeroTrial;
+ protected override int GetRedpointId() => MainRedDot.HeroTrialRepoint;
+
+ protected override string GetCountInfo()
+ {
+ return HeroTrialManager.Instance.HasAnyChallengeableLevel() ? Language.Get("HeroTrial06") : string.Empty;
+ }
+
+ protected override Action GetOnClickAction()
+ {
+ return HandleHeroTrialNavigation;
+ }
+
+ private async void HandleHeroTrialNavigation()
+ {
+ if (!FuncOpen.Instance.IsFuncOpen(GetFuncId(), true))
+ return;
+ UIManager.Instance.CloseWindow<ChallengeTabWin>();
+ BattleField battleField = BattleManager.Instance.GetBattleFieldByMapID(HeroTrialManager.Instance.DataMapID);
+ if (battleField != null)
+ {
+ HeroTrialBattleWin battleWin;
+ if (!UIManager.Instance.IsOpened<HeroTrialBattleWin>())
+ {
+ battleWin = await UIManager.Instance.OpenWindowAsync<HeroTrialBattleWin>();
+ }
+ else
+ {
+ battleWin = UIManager.Instance.GetUI<HeroTrialBattleWin>();
+ }
+ battleWin.SetBattleField(battleField as HeroTrialBattleField);
+ }
+ else
+ {
+ UIManager.Instance.OpenWindowAsync<TowerBaseWin>(1).Forget();
+ }
+ }
+
+ protected override void SubscribeToSpecificEvents()
+ {
+ HeroTrialManager.Instance.OnUpdateHeroTrialInfoEvent += Refresh;
+ }
+
+ protected override void UnsubscribeFromSpecificEvents()
+ {
+ HeroTrialManager.Instance.OnUpdateHeroTrialInfoEvent -= Refresh;
+ }
+}
diff --git a/Main/System/ChallengeTab/HeroTrialTabHandler.cs.meta b/Main/System/ChallengeTab/HeroTrialTabHandler.cs.meta
new file mode 100644
index 0000000..eae34bc
--- /dev/null
+++ b/Main/System/ChallengeTab/HeroTrialTabHandler.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: c410f4b7a26f00640b6cf94a2817d3c1
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Main/System/ChallengeTab/QunyingTabHandler.cs b/Main/System/ChallengeTab/QunyingTabHandler.cs
index 289f9c8..210bcc2 100644
--- a/Main/System/ChallengeTab/QunyingTabHandler.cs
+++ b/Main/System/ChallengeTab/QunyingTabHandler.cs
@@ -3,7 +3,8 @@
public class QunyingTabHandler : BaseChallengeTabHandler
{
- protected override int GetIndex() => 5;
+ protected override string GetIconKey() => "ChallengeTab5";
+ protected override string GetNameText() => Language.Get("ChallengeTab5");
protected override int GetOpenState() => 0; // 0=FuncID
protected override int GetFuncId() => (int)FuncOpenEnum.Qunying;
protected override int GetRedpointId() => MainRedDot.Qunying;
@@ -64,4 +65,4 @@
Refresh();
}
}
-}
\ No newline at end of file
+}
diff --git a/Main/System/ChallengeTab/TianziBillboradTabHandler.cs b/Main/System/ChallengeTab/TianziBillboradTabHandler.cs
index a432e2a..192a7ac 100644
--- a/Main/System/ChallengeTab/TianziBillboradTabHandler.cs
+++ b/Main/System/ChallengeTab/TianziBillboradTabHandler.cs
@@ -3,7 +3,8 @@
public class TianziBillboradTabHandler : BaseChallengeTabHandler
{
- protected override int GetIndex() => 3;
+ protected override string GetIconKey() => "ChallengeTab3";
+ protected override string GetNameText() => Language.Get("ChallengeTab3");
protected override int GetOpenState() => 0; // 0=FuncID
protected override int GetFuncId() => TianziBillboradManager.Instance.funcId;
protected override int GetRedpointId() => MainRedDot.TianziBillboradRepoint;
@@ -72,4 +73,4 @@
Refresh();
}
}
-}
\ No newline at end of file
+}
diff --git a/Main/System/ChallengeTab/WarlordPavilionTabHandler.cs b/Main/System/ChallengeTab/WarlordPavilionTabHandler.cs
index 2734406..d197859 100644
--- a/Main/System/ChallengeTab/WarlordPavilionTabHandler.cs
+++ b/Main/System/ChallengeTab/WarlordPavilionTabHandler.cs
@@ -3,7 +3,8 @@
public class WarlordPavilionTabHandler : BaseChallengeTabHandler
{
- protected override int GetIndex() => 4;
+ protected override string GetIconKey() => "ChallengeTab4";
+ protected override string GetNameText() => Language.Get("ChallengeTab4");
protected override int GetOpenState() => 0; // 0=FuncID
protected override int GetFuncId() => (int)FuncOpenEnum.WarlordPavilion;
protected override int GetRedpointId() => MainRedDot.WarlordPavilionRepoint;
@@ -60,4 +61,4 @@
Refresh();
}
-}
\ No newline at end of file
+}
diff --git a/Main/System/HeroTrial.meta b/Main/System/HeroTrial.meta
new file mode 100644
index 0000000..a6a4d6e
--- /dev/null
+++ b/Main/System/HeroTrial.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 4fe029ba0b48b2646908738defb90bca
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Main/System/HeroTrial/CarouselView.cs b/Main/System/HeroTrial/CarouselView.cs
new file mode 100644
index 0000000..22b2726
--- /dev/null
+++ b/Main/System/HeroTrial/CarouselView.cs
@@ -0,0 +1,236 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.EventSystems;
+
+public interface ICarouselItem
+{
+ RectTransform RectTransform { get; }
+ void SetSelected(bool selected);
+}
+
+public abstract class CarouselView<TItem, TData> : MonoBehaviour, IDragHandler, IEndDragHandler, IBeginDragHandler
+ where TItem : MonoBehaviour, ICarouselItem
+{
+ public event Action OnSelectedIndexChanged;
+
+ readonly List<TItem> items = new List<TItem>();
+ List<TData> dataList;
+
+ float currentCenterOffset;
+ float targetCenterOffset;
+ bool isDragging;
+ int currentSelectedIndex;
+
+ protected abstract RectTransform Content { get; }
+ protected abstract GameObject ItemPrefab { get; }
+ protected abstract float ItemSpacing { get; }
+ protected abstract float ScaleMax { get; }
+ protected abstract float ScaleMin { get; }
+ protected virtual int PoolSize => 7;
+ protected int CurrentSelectedIndex => currentSelectedIndex;
+ protected IReadOnlyList<TData> DataList => dataList;
+
+ public void InitPool()
+ {
+ if (items.Count > 0)
+ return;
+
+ for (int i = 0; i < PoolSize; i++)
+ {
+ GameObject go = Instantiate(ItemPrefab, Content);
+ TItem item = go.GetComponent<TItem>();
+ if (item == null)
+ continue;
+ items.Add(item);
+ }
+ }
+
+ public void InitWithIndex(int index)
+ {
+ if (dataList.IsNullOrEmpty())
+ return;
+
+ index = Mathf.Clamp(index, 0, dataList.Count - 1);
+ currentSelectedIndex = index;
+ currentCenterOffset = index;
+ targetCenterOffset = index;
+ UpdateItems();
+ }
+
+ public void SelectIndex(int index)
+ {
+ if (dataList.IsNullOrEmpty())
+ return;
+
+ index = GetRealIndex(index);
+
+ int oldIndex = currentSelectedIndex;
+ currentSelectedIndex = index;
+ targetCenterOffset = index;
+ currentCenterOffset = index;
+ UpdateItems();
+
+ if (oldIndex != currentSelectedIndex)
+ OnSelectedIndexChanged?.Invoke();
+ }
+
+ public void SelectPrevious()
+ {
+ if (dataList.IsNullOrEmpty())
+ return;
+
+ int newIndex = (currentSelectedIndex - 1 + dataList.Count) % dataList.Count;
+ SelectIndex(newIndex);
+ }
+
+ public void SelectNext()
+ {
+ if (dataList.IsNullOrEmpty())
+ return;
+
+ int newIndex = (currentSelectedIndex + 1) % dataList.Count;
+ SelectIndex(newIndex);
+ }
+
+ public void RefreshAllItems()
+ {
+ if (!dataList.IsNullOrEmpty())
+ UpdateItems();
+ }
+
+ protected void SetDataList(List<TData> newDataList, int selectedIndex)
+ {
+ dataList = newDataList;
+
+ if (dataList.IsNullOrEmpty())
+ {
+ HideAllItems();
+ return;
+ }
+
+ InitWithIndex(selectedIndex);
+ }
+
+ protected bool TryGetCurrentData(out TData data)
+ {
+ data = default(TData);
+ if (dataList.IsNullOrEmpty() || currentSelectedIndex < 0)
+ return false;
+
+ int realIndex = GetRealIndex(currentSelectedIndex);
+ data = dataList[realIndex];
+ return true;
+ }
+
+ protected void HideAllItems()
+ {
+ for (int i = 0; i < items.Count; i++)
+ {
+ if (items[i] != null)
+ items[i].gameObject.SetActive(false);
+ }
+ }
+
+ protected virtual void OnDestroy()
+ {
+ items.Clear();
+ }
+
+ protected virtual void Update()
+ {
+ if (!isDragging && Mathf.Abs(currentCenterOffset - targetCenterOffset) > 0.001f)
+ {
+ currentCenterOffset = Mathf.Lerp(currentCenterOffset, targetCenterOffset, Time.deltaTime * 10f);
+ if (Mathf.Abs(currentCenterOffset - targetCenterOffset) <= 0.001f)
+ currentCenterOffset = targetCenterOffset;
+ UpdateItems();
+ }
+ }
+
+ void UpdateItems()
+ {
+ if (dataList.IsNullOrEmpty())
+ return;
+
+ int poolSize = items.Count;
+ int startOffset = Mathf.FloorToInt(currentCenterOffset) - (poolSize / 2);
+ int selectedLogicIndex = Mathf.RoundToInt(targetCenterOffset);
+
+ for (int i = 0; i < poolSize; i++)
+ {
+ int logicIndex = startOffset + i;
+ int realIndex = GetRealIndex(logicIndex);
+
+ float distanceToCenter = logicIndex - currentCenterOffset;
+ float posX = distanceToCenter * ItemSpacing;
+
+ TItem itemUI = items[i];
+ if (itemUI == null || itemUI.RectTransform == null)
+ continue;
+
+ itemUI.gameObject.SetActive(true);
+ itemUI.RectTransform.anchoredPosition = new Vector2(posX, 0);
+
+ float absDist = Mathf.Abs(distanceToCenter);
+ float scaleProgress = Mathf.Clamp01(1f - absDist);
+ float currentScale = Mathf.Lerp(ScaleMin, ScaleMax, scaleProgress);
+ itemUI.RectTransform.localScale = Vector3.one * currentScale;
+
+ bool isSelected = logicIndex == selectedLogicIndex;
+ RefreshItem(itemUI, dataList[realIndex], realIndex, isSelected);
+ itemUI.SetSelected(isSelected);
+ }
+ }
+
+ int GetRealIndex(int logicIndex)
+ {
+ return (logicIndex % dataList.Count + dataList.Count) % dataList.Count;
+ }
+
+ protected abstract void RefreshItem(TItem item, TData data, int realIndex, bool isSelected);
+
+ public void OnBeginDrag(PointerEventData eventData)
+ {
+ isDragging = true;
+ }
+
+ public void OnDrag(PointerEventData eventData)
+ {
+ if (dataList.IsNullOrEmpty())
+ return;
+
+ float dragDeltaX = eventData.delta.x;
+ currentCenterOffset -= dragDeltaX / ItemSpacing;
+ UpdateItems();
+ }
+
+ public void OnEndDrag(PointerEventData eventData)
+ {
+ if (dataList.IsNullOrEmpty())
+ return;
+
+ isDragging = false;
+
+ float speedX = eventData.delta.x;
+ if (speedX > 5f)
+ {
+ targetCenterOffset = Mathf.Floor(currentCenterOffset);
+ }
+ else if (speedX < -5f)
+ {
+ targetCenterOffset = Mathf.Ceil(currentCenterOffset);
+ }
+ else
+ {
+ targetCenterOffset = Mathf.Round(currentCenterOffset);
+ }
+
+ int oldIndex = currentSelectedIndex;
+ currentSelectedIndex = GetRealIndex(Mathf.RoundToInt(targetCenterOffset));
+ UpdateItems();
+
+ if (oldIndex != currentSelectedIndex)
+ OnSelectedIndexChanged?.Invoke();
+ }
+}
diff --git a/Main/System/HeroTrial/CarouselView.cs.meta b/Main/System/HeroTrial/CarouselView.cs.meta
new file mode 100644
index 0000000..4aa11dd
--- /dev/null
+++ b/Main/System/HeroTrial/CarouselView.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 552093c27b834d40ae2245fc840daa24
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Main/System/HeroTrial/HeroTrialBattleWin.cs b/Main/System/HeroTrial/HeroTrialBattleWin.cs
new file mode 100644
index 0000000..4e52036
--- /dev/null
+++ b/Main/System/HeroTrial/HeroTrialBattleWin.cs
@@ -0,0 +1,292 @@
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.UI;
+using Cysharp.Threading.Tasks;
+
+public class HeroTrialBattleWin : BaseBattleWin
+{
+ [SerializeField] Transform transButtons;
+ [SerializeField] BossLifeBar bossLifeBar;
+ [SerializeField] SkillWordCell[] skillWordCells;
+ [SerializeField] BossHeadCell bossHeadCell;
+ [SerializeField] Text txtBossName;
+
+ [SerializeField] List<BattleBuffCell> buffCells;
+ [SerializeField] HeroCountryComponent myCountry;
+ [SerializeField] HeroCountryComponent enemyCountry;
+ [SerializeField] ButtonEx buffInfoButton;
+ private BattleObject bossBattleObject = null;
+
+ protected override void OnPreOpen()
+ {
+ base.OnPreOpen();
+ MainWin.TabChangeEvent += OnTabChangeEvent;
+ UIManager.Instance.OnOpenWindow += OnOpenWindow;
+ bool isOpenBattleChangeTab = IsOpenBattleChangeTab();
+ transButtons.localPosition = new Vector3(0, isOpenBattleChangeTab ? 130 : 0, 0);
+ if (isOpenBattleChangeTab)
+ {
+ UIManager.Instance.GetUI<MainWin>()?.CloseSubUI();
+ }
+ else
+ {
+ UIManager.Instance.CloseWindow<MainWin>();
+ }
+ isClickSkip = false;
+ }
+
+ protected override void OnPreClose()
+ {
+ base.OnPreClose();
+
+ MainWin.TabChangeEvent -= OnTabChangeEvent;
+ UIManager.Instance.OnOpenWindow -= OnOpenWindow;
+ bool isOpenBattleChangeTab = IsOpenBattleChangeTab();
+ if (isOpenBattleChangeTab)
+ {
+ UIManager.Instance.GetUI<MainWin>()?.RestoreSubUI();
+ }
+ else
+ {
+ UIManager.Instance.OpenWindowAsync<MainWin>().Forget();
+ }
+
+ if (bossBattleObject != null)
+ {
+ var buffMgr = bossBattleObject.GetBuffMgr();
+ if (buffMgr != null)
+ {
+ buffMgr.onBuffChanged -= OnBuffChanged;
+ }
+ bossBattleObject = null;
+ }
+
+ if (isClickSkip)
+ {
+ isClickSkip = false;
+ TryPass();
+ }
+ }
+
+ void OnOpenWindow(UIBase win)
+ {
+ if (win is HeroTrialVictoryWin || win is HeroTrialFailWin)
+ {
+ isClickSkip = false;
+ }
+ }
+
+ bool isClickSkip = false;
+ protected override void OnClickPass()
+ {
+ if (!IsPass())
+ return;
+ isClickSkip = true;
+ clickTime = Time.time; // 璁板綍鐐瑰嚮鏃堕棿
+ battleField.ForceFinish();
+
+ }
+
+ float stayTime = 2f;
+ float clickTime = 0f;
+ void LateUpdate()
+ {
+ if (isClickSkip && Time.time - clickTime >= stayTime)
+ {
+ isClickSkip = false;
+ TryPass();
+ }
+ }
+
+ private void TryPass()
+ {
+ if (UIManager.Instance.IsOpened<HeroTrialVictoryWin>() ||
+ UIManager.Instance.IsOpened<HeroTrialFailWin>())
+ return;
+ CloseWindow();
+ Debug.LogError($"OnBattleEnd 寮傚父鍏抽棴");
+ BattleSettlementManager.Instance.WinShowOver(BattleConst.HeroTrialBattleField);
+
+ }
+ private void OnTabChangeEvent()
+ {
+ UIManager.Instance.CloseWindow<HeroTrialBattleWin>();
+ }
+
+ protected override void OnCreateBattleField(string guid, BattleField field)
+ {
+ if (field is HeroTrialBattleField)
+ {
+ SetBattleField(field);
+ }
+ }
+
+ protected override void RefreshSpecific()
+ {
+ HeroTrialBattleField heroTrialField = battleField as HeroTrialBattleField;
+ if (heroTrialField == null) return;
+
+ NPCLineupConfig lineupConfig = heroTrialField.GetBossLineupConfig();
+
+ if (bossBattleObject != null)
+ {
+ var buffMgr = bossBattleObject.GetBuffMgr();
+ if (buffMgr != null)
+ {
+ buffMgr.onBuffChanged -= OnBuffChanged;
+ }
+ bossBattleObject = null;
+ }
+
+ bossBattleObject = heroTrialField.FindBoss();
+
+ DisplaySkillWordsList(lineupConfig);
+
+ if (null != bossBattleObject && bossBattleObject is HeroBattleObject heroBattleObject)
+ {
+ TeamHero teamHero = heroBattleObject.teamHero;
+ bossHeadCell.SetTeamHero(teamHero);
+ txtBossName.text = teamHero.name;
+ NPCConfig npcConfig = NPCConfig.Get(teamHero.NPCID);
+ bossLifeBar.SetBaseInfo(Mathf.Max(1, npcConfig.LifeBarCount), (ulong)teamHero.curHp, (ulong)teamHero.maxHp);
+ var buffMgr = bossBattleObject.GetBuffMgr();
+ if (buffMgr != null)
+ {
+ buffMgr.onBuffChanged -= OnBuffChanged;
+ buffMgr.onBuffChanged += OnBuffChanged;
+ }
+ }
+ else
+ {
+ bossHeadCell.SetTeamHero(null);
+ txtBossName.text = string.Empty;
+ bossLifeBar.SetBaseInfo(2, 2, 2);
+ Debug.LogError("鎵句笉鍒癰oss");
+ }
+
+ 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 OnBuffChanged()
+ {
+ var buffList = new List<HB428_tagSCBuffRefresh>();
+ if (null != bossBattleObject)
+ {
+ var buffMgr = bossBattleObject.GetBuffMgr();
+ buffList = buffMgr != null ? buffMgr.GetBuffIconList() : new List<HB428_tagSCBuffRefresh>();
+ }
+ RefreshBuff(buffList);
+ }
+
+ private void RefreshHP()
+ {
+ if (null != bossBattleObject && bossBattleObject is HeroBattleObject heroBattleObject)
+ {
+ TeamHero teamHero = heroBattleObject.teamHero;
+ bossLifeBar.Show((ulong)teamHero.curHp, (ulong)teamHero.maxHp);
+ }
+ }
+
+ protected override void OnDamageTaken(BattleDmgInfo info)
+ {
+ // Debug.LogError("OnDamageTaken 琚皟鐢� 璋冪敤鑰呮槸 " + info.battleHurtParam.caster.casterObj?.teamHero.name + " 瀵硅薄 " + info.battleHurtParam.hurter.hurtObj?.teamHero.name);
+ base.OnDamageTaken(info);
+
+ if (battleField == null || info.battleFieldGuid != battleField.guid)
+ return;
+
+ if (null == bossBattleObject)
+ return;
+
+ // 鈽呪槄鈽� 瀹屽叏浣跨敤 StoryBossBattleWin 鐨勯�昏緫 鈽呪槄鈽�
+ if (info.battleHurtParam.hurter.hurtObj != null && bossBattleObject.ObjID == info.battleHurtParam.hurter.hurtObj.ObjID)
+ {
+ bossLifeBar.Show((ulong)info.battleHurtParam.hurter.toHp, (ulong)bossBattleObject.GetMaxHp());
+ }
+ else if (info.battleHurtParam.caster.casterObj != null && bossBattleObject.ObjID == info.battleHurtParam.caster.casterObj.ObjID)
+ {
+ bossLifeBar.Show((ulong)info.battleHurtParam.caster.toHp, (ulong)bossBattleObject.GetMaxHp());
+ }
+ }
+
+ protected override void OnClose()
+ {
+ base.OnClose();
+ }
+
+ 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.OpenWindowAsync<SmallTipWin>().Forget();
+ }).Forget();
+ }
+ else
+ {
+ skillWordCells[i].SetActive(false);
+ }
+ }
+ }
+
+ public void RefreshBuff(List<HB428_tagSCBuffRefresh> datas)
+ {
+ RefreshBuffCells(buffCells, datas);
+
+ buffInfoButton.SetListener(() =>
+ {
+ if (bossBattleObject == null || datas.IsNullOrEmpty()) return;
+ EventBroadcast.Instance.Broadcast(EventName.BATTLE_CLICK_BUFF, new BattleClickBuffData()
+ {
+ isMySide = false,
+ heroID = (bossBattleObject as HeroBattleObject)?.teamHero?.heroId ?? 0,
+ skinID = (bossBattleObject as HeroBattleObject)?.teamHero?.SkinID ?? 0,
+ datas = datas,
+ });
+ });
+ }
+
+ 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)
+ {
+ if (item is HeroBattleObject heroBattleObject)
+ {
+ teamHeroes.Add(heroBattleObject.teamHero);
+ }
+ }
+ return teamHeroes;
+
+ }
+}
diff --git a/Main/System/HeroTrial/HeroTrialBattleWin.cs.meta b/Main/System/HeroTrial/HeroTrialBattleWin.cs.meta
new file mode 100644
index 0000000..2772d77
--- /dev/null
+++ b/Main/System/HeroTrial/HeroTrialBattleWin.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 25165ebda169aa7428e88278f78606b8
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Main/System/HeroTrial/HeroTrialCarouselView.cs b/Main/System/HeroTrial/HeroTrialCarouselView.cs
new file mode 100644
index 0000000..a2b086b
--- /dev/null
+++ b/Main/System/HeroTrial/HeroTrialCarouselView.cs
@@ -0,0 +1,104 @@
+using System.Collections.Generic;
+using UnityEngine;
+
+public class HeroTrialCarouselView : CarouselView<HeroTrialHeroItem, int>
+{
+ [Header("UI References")]
+ public RectTransform content;
+ public GameObject heroTrialItemPrefab;
+
+ [Header("Carousel Settings")]
+ public float itemSpacing = 150f;
+ public float scaleMax = 1.3f;
+ public float scaleMin = 0.8f;
+
+ bool isEventBound;
+
+ protected override RectTransform Content => content;
+ protected override GameObject ItemPrefab => heroTrialItemPrefab;
+ protected override float ItemSpacing => itemSpacing;
+ protected override float ScaleMax => scaleMax;
+ protected override float ScaleMin => scaleMin;
+
+ public uint CurrentHeroID
+ {
+ get
+ {
+ if (TryGetCurrentData(out int heroID))
+ return (uint)heroID;
+ return 0;
+ }
+ }
+
+ public byte CurrentPassLevelNum
+ {
+ get
+ {
+ uint heroID = CurrentHeroID;
+ if (heroID != 0 && HeroTrialManager.Instance.TryGetHeroPassLevelNum(heroID, out byte passLevelNum))
+ return passLevelNum;
+ return 0;
+ }
+ }
+
+ protected override void OnDestroy()
+ {
+ UnbindEvents();
+ base.OnDestroy();
+ }
+
+ public void BindEvents()
+ {
+ if (isEventBound)
+ return;
+
+ isEventBound = true;
+ if (HeroTrialManager.Instance != null)
+ HeroTrialManager.Instance.OnUpdateHeroTrialInfoEvent += OnHeroTrialDataUpdate;
+ }
+
+ public void UnbindEvents()
+ {
+ if (!isEventBound)
+ return;
+
+ isEventBound = false;
+ if (HeroTrialManager.Instance != null)
+ HeroTrialManager.Instance.OnUpdateHeroTrialInfoEvent -= OnHeroTrialDataUpdate;
+ }
+
+ void OnHeroTrialDataUpdate()
+ {
+ RefreshData();
+ }
+
+ public void RefreshData()
+ {
+ uint prevHeroID = CurrentHeroID;
+ List<int> heroIDs = HeroTrialManager.Instance.GetHeroTrialHeroIDList();
+
+ int newIndex = FindHeroIndex(heroIDs, prevHeroID);
+ SetDataList(heroIDs, newIndex >= 0 ? newIndex : 0);
+ }
+
+ int FindHeroIndex(List<int> heroIDs, uint heroID)
+ {
+ if (heroID == 0 || heroIDs.IsNullOrEmpty())
+ return -1;
+
+ for (int i = 0; i < heroIDs.Count; i++)
+ {
+ if ((uint)heroIDs[i] == heroID)
+ return i;
+ }
+
+ return -1;
+ }
+
+ protected override void RefreshItem(HeroTrialHeroItem item, int heroID, int realIndex, bool isSelected)
+ {
+ HeroTrialManager.Instance.TryGetHeroPassLevelNum((uint)heroID, out byte passLevelNum);
+ item.UpdateData((uint)heroID, passLevelNum, realIndex);
+ item.onItemClick = SelectIndex;
+ }
+}
diff --git a/Main/System/HeroTrial/HeroTrialCarouselView.cs.meta b/Main/System/HeroTrial/HeroTrialCarouselView.cs.meta
new file mode 100644
index 0000000..475bfa0
--- /dev/null
+++ b/Main/System/HeroTrial/HeroTrialCarouselView.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 97cf454b383948fcb6b584596e6ac35d
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Main/System/HeroTrial/HeroTrialCell.cs b/Main/System/HeroTrial/HeroTrialCell.cs
new file mode 100644
index 0000000..c493a33
--- /dev/null
+++ b/Main/System/HeroTrial/HeroTrialCell.cs
@@ -0,0 +1,114 @@
+using Cysharp.Threading.Tasks;
+using UnityEngine;
+
+public class HeroTrialCell : CellView
+{
+ [SerializeField] ImageEx heroHeadImage;
+ [SerializeField] TextEx fightPowerText;
+ [SerializeField] TextEx heroNameText;
+ [SerializeField] ItemCell[] itemCells;
+
+ [SerializeField] TextEx lockText;
+ [SerializeField] ImageEx passMark;
+ [SerializeField] ButtonEx challengeBtn;
+ [SerializeField] ImageEx challengeImg;
+ [SerializeField] TextEx challengeText1;
+ [SerializeField] TextEx challengeText2;
+ [SerializeField] ImageEx challengeImage;
+ [SerializeField] ImageEx redImage;
+ HeroTrialConfig currentConfig;
+
+ public void UpdateData(HeroTrialConfig config, byte passLevelNum, bool heroUnlocked, string heroUnlockTip)
+ {
+ if (config == null)
+ return;
+
+ currentConfig = config;
+ RefreshHeroInfo(config.HeroID);
+ RefreshAwards(config.PassAwardList);
+
+ fightPowerText.text = UIHelper.ReplaceLargeArtNum(config.FightPower);
+
+ bool isPassed = heroUnlocked && config.LevelNum <= passLevelNum;
+ string challengeTip = string.Empty;
+ bool canChallenge = heroUnlocked && !isPassed && HeroTrialManager.Instance.CanChallenge(config, out challengeTip);
+ bool showRed = canChallenge && HeroTrialManager.Instance.IsShowHeroTrialLevelRed(config);
+ string lockTip = GetLockTip(config, isPassed, canChallenge, heroUnlocked, heroUnlockTip, challengeTip);
+ RefreshState(isPassed, canChallenge, lockTip, showRed);
+ challengeBtn.SetListener(OnClickChallenge);
+ }
+
+ string GetLockTip(HeroTrialConfig config, bool isPassed, bool canChallenge, bool heroUnlocked, string heroUnlockTip, string challengeTip)
+ {
+ if (isPassed || canChallenge)
+ return string.Empty;
+
+ bool isFirstLevel = HeroTrialConfig.TryGetFirstLevelConfig(config.HeroID, out var firstConfig) && firstConfig.CfgID == config.CfgID;
+ if (!heroUnlocked && isFirstLevel && !string.IsNullOrEmpty(heroUnlockTip))
+ return heroUnlockTip;
+
+ if (!string.IsNullOrEmpty(challengeTip))
+ return challengeTip;
+
+ return Language.Get("HeroTrial05");
+ }
+
+ void RefreshHeroInfo(int heroID)
+ {
+ HeroConfig heroConfig = HeroConfig.Get(heroID);
+ if (heroConfig == null)
+ return;
+
+ heroNameText.text = heroConfig.Name;
+
+ if (!heroConfig.SkinIDList.IsNullOrEmpty())
+ {
+ int skinID = heroConfig.SkinIDList[0];
+ var skinConfig = HeroSkinConfig.Get(skinID);
+ if (skinConfig != null)
+ {
+ UILoader.LoadSprite("HeroHead", skinConfig.SquareIcon, heroHeadImage, "herohead_default").Forget();
+ }
+ }
+ }
+
+ void RefreshAwards(int[][] awards)
+ {
+ if (itemCells.IsNullOrEmpty())
+ return;
+
+ for (int i = 0; i < itemCells.Length; i++)
+ {
+ bool hasAward = awards != null && i < awards.Length && awards[i] != null && awards[i].Length >= 2;
+ itemCells[i].SetActive(hasAward);
+ if (hasAward)
+ {
+ int itemID = awards[i][0];
+ itemCells[i].Init(new ItemCellModel(awards[i][0], true, awards[i][1]));
+ itemCells[i].SetClickListener(() => ItemTipUtility.Show(itemID));
+ }
+ }
+ }
+
+ void RefreshState(bool isPassed, bool canChallenge, string lockTip, bool showRed)
+ {
+ bool isLocked = !string.IsNullOrEmpty(lockTip);
+
+ lockText.SetActive(isLocked);
+ lockText.text = lockTip;
+
+ passMark.SetActive(isPassed);
+ challengeBtn.SetActive(!isPassed);
+ challengeBtn.interactable = canChallenge;
+ challengeImg.gray = !canChallenge;
+ challengeText1.SetActive(!canChallenge);
+ challengeText2.SetActive(canChallenge);
+ challengeImage.SetActive(canChallenge);
+ redImage.SetActive(showRed);
+ }
+
+ void OnClickChallenge()
+ {
+ HeroTrialManager.Instance.SendChallenge(currentConfig);
+ }
+}
diff --git a/Main/System/HeroTrial/HeroTrialCell.cs.meta b/Main/System/HeroTrial/HeroTrialCell.cs.meta
new file mode 100644
index 0000000..b1fa72e
--- /dev/null
+++ b/Main/System/HeroTrial/HeroTrialCell.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: b102d9604f5b5f040873d38526654381
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Main/System/HeroTrial/HeroTrialFailWin.cs b/Main/System/HeroTrial/HeroTrialFailWin.cs
new file mode 100644
index 0000000..603be9b
--- /dev/null
+++ b/Main/System/HeroTrial/HeroTrialFailWin.cs
@@ -0,0 +1,54 @@
+using UnityEngine;
+using UnityEngine.UI;
+using Cysharp.Threading.Tasks;
+
+//鎴樺満缁撶畻鐣岄潰锛屽瓨鍦ㄥ涓殑鎯呭喌
+public class HeroTrialFailWin : UIBase
+{
+ [SerializeField] TextEx txtFuncName;
+ [SerializeField] Button tipHeroCultivateBtn;
+ [SerializeField] Button tipEquipBtn;
+ [SerializeField] Button tipHeroPosBtn;
+ [SerializeField] ButtonEx detailBtn;
+ string battleName = BattleConst.HeroTrialBattleField;
+ protected override void InitComponent()
+ {
+ tipHeroCultivateBtn.AddListener(() =>
+ {
+ CloseWindow();
+ });
+
+ tipEquipBtn.AddListener(() =>
+ {
+ CloseWindow();
+ });
+
+ tipHeroPosBtn.AddListener(() =>
+ {
+ CloseWindow();
+ UIManager.Instance.OpenWindowAsync<HeroPosWin>().Forget();
+ });
+
+ detailBtn.SetListener(() =>
+ {
+ BattleSettlementManager.Instance.OpenBattleDetailWin(battleName);
+ });
+ }
+
+ protected override void OnPreOpen()
+ {
+ int funcId = (int)FuncOpenEnum.HeroTrial;
+ txtFuncName.text = FuncOpenLVConfig.HasKey(funcId) ? FuncOpenLVConfig.Get(funcId).Name : string.Empty;
+ if (!FirstChargeManager.Instance.GetLocalFail())
+ {
+ FirstChargeManager.Instance.SetLocalFail();
+ }
+ FirstChargeManager.Instance.TryPopWin();
+ }
+
+
+ protected override void OnPreClose()
+ {
+ BattleSettlementManager.Instance.WinShowOver(battleName);
+ }
+}
diff --git a/Main/System/HeroTrial/HeroTrialFailWin.cs.meta b/Main/System/HeroTrial/HeroTrialFailWin.cs.meta
new file mode 100644
index 0000000..ef6e34c
--- /dev/null
+++ b/Main/System/HeroTrial/HeroTrialFailWin.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: db691e748d974d246aecac137f9b0035
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Main/System/HeroTrial/HeroTrialHeroItem.cs b/Main/System/HeroTrial/HeroTrialHeroItem.cs
new file mode 100644
index 0000000..ab8e637
--- /dev/null
+++ b/Main/System/HeroTrial/HeroTrialHeroItem.cs
@@ -0,0 +1,85 @@
+using System;
+using Cysharp.Threading.Tasks;
+using UnityEngine;
+using UnityEngine.UI;
+
+public class HeroTrialHeroItem : MonoBehaviour, ICarouselItem
+{
+ [SerializeField] Image bgImage;
+ [SerializeField] Image isActImage;
+ [SerializeField] Image qualityBgImage;
+ [SerializeField] Image heroHeadImage;
+ [SerializeField] Transform lockRect;
+
+ [SerializeField] TextEx lockTipText;
+ [SerializeField] TextEx goText;
+ [SerializeField] ButtonEx clickButton;
+ [SerializeField] Image redImage;
+ public RectTransform rectTransform;
+ public Action<int> onItemClick;
+ public int dataIndex;
+
+ public RectTransform RectTransform
+ {
+ get
+ {
+ if (rectTransform == null)
+ rectTransform = GetComponent<RectTransform>();
+ return rectTransform;
+ }
+ }
+
+ void Awake()
+ {
+ rectTransform = GetComponent<RectTransform>();
+ }
+
+
+ public void UpdateData(uint heroID, byte passLevelNum, int index)
+ {
+ dataIndex = index;
+
+ HeroConfig heroConfig = HeroConfig.Get((int)heroID);
+ if (heroConfig == null)
+ return;
+
+ isActImage.SetActive(heroConfig.IsActLimit > 0);
+
+ qualityBgImage.SetSprite("heroheadBG" + heroConfig.Quality);
+
+ if (!heroConfig.SkinIDList.IsNullOrEmpty())
+ {
+ int skinID = heroConfig.SkinIDList[0];
+ var skinConfig = HeroSkinConfig.Get(skinID);
+ if (skinConfig != null)
+ UILoader.LoadSprite("HeroHead", skinConfig.SquareIcon, heroHeadImage, "herohead_default").Forget();
+ }
+
+ UpdateLockState(heroConfig);
+ RefreshRed(heroConfig.HeroID);
+ clickButton.SetListener(() => onItemClick?.Invoke(dataIndex));
+ }
+
+ void RefreshRed(int heroID)
+ {
+ redImage.SetActive(HeroTrialManager.Instance.IsShowHeroItemRed(heroID));
+ }
+
+ /// <summary>
+ /// 鏇存柊閿佸畾鐘舵��
+ /// </summary>
+ void UpdateLockState(HeroConfig heroConfig)
+ {
+ bool isLocked = !HeroTrialManager.Instance.IsHeroTrialHeroUnlocked(heroConfig.HeroID, out string tip);
+
+ lockRect.SetActive(isLocked);
+ goText.SetActive(!isLocked);
+ lockTipText.text = tip;
+ }
+
+ public void SetSelected(bool selected)
+ {
+ string bgKey = selected ? "HeroTrialHeroBgSelect" : "HeroTrialHeroBgUnSelect";
+ bgImage.SetSprite(bgKey);
+ }
+}
diff --git a/Main/System/HeroTrial/HeroTrialHeroItem.cs.meta b/Main/System/HeroTrial/HeroTrialHeroItem.cs.meta
new file mode 100644
index 0000000..943e4bd
--- /dev/null
+++ b/Main/System/HeroTrial/HeroTrialHeroItem.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 3bb421ee0c4449d1a1f06efbc03bc29a
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Main/System/HeroTrial/HeroTrialManager.cs b/Main/System/HeroTrial/HeroTrialManager.cs
new file mode 100644
index 0000000..1add955
--- /dev/null
+++ b/Main/System/HeroTrial/HeroTrialManager.cs
@@ -0,0 +1,811 @@
+using System;
+using System.Collections.Generic;
+
+public class HeroTrialManager : GameSystemManager<HeroTrialManager>
+{
+ public readonly int funcId = (int)FuncOpenEnum.HeroTrial; // 68 姝﹀皢璇曠偧鍔熻兘ID
+ public readonly int DataMapID = 30040;
+
+ /// <summary>姝﹀皢璇曠偧杩涘害瀛楀吀 <HeroID, PassLevelNum></summary>
+ public Dictionary<uint, byte> heroTrialDict = new Dictionary<uint, byte>();
+
+ int challengeRecordDay;
+ // 褰撳ぉ鍙戣捣杩囨寫鎴樼殑姝﹀皢涓嶅啀鏄剧ず绾㈢偣锛岃儨璐熸棤鍏炽��
+ HashSet<int> challengedHeroIDsToday = new HashSet<int>();
+ // 褰撳ぉ鏂板紑鏀剧殑鍙彁绀烘灏嗭紝浼樺厛浜庢櫘閫氬�欓�夋帓闃熼『寤躲��
+ List<int> pendingNewRedHeroIDsToday = new List<int>();
+ int currentRedHeroID;
+ bool isChallengeRecordLoaded;
+
+ string challengeRecordDayKey { get { return StringUtility.Concat("HeroTrial_ChallengeRecordDay_", PlayerDatas.Instance.PlayerId.ToString()); } }
+ string lastChallengeHeroIDTodayKey { get { return StringUtility.Concat("HeroTrial_LastChallengeHeroIDToday_", PlayerDatas.Instance.PlayerId.ToString()); } }
+ string challengedHeroIDsTodayKey { get { return StringUtility.Concat("HeroTrial_ChallengedHeroIDsToday_", PlayerDatas.Instance.PlayerId.ToString()); } }
+ string normalRedConsumedTodayKey { get { return StringUtility.Concat("HeroTrial_NormalRedConsumedToday_", PlayerDatas.Instance.PlayerId.ToString()); } }
+ string pendingNewRedHeroIDsTodayKey { get { return StringUtility.Concat("HeroTrial_PendingNewRedHeroIDsToday_", PlayerDatas.Instance.PlayerId.ToString()); } }
+ string currentRedHeroIDKey { get { return StringUtility.Concat("HeroTrial_CurrentRedHeroID_", PlayerDatas.Instance.PlayerId.ToString()); } }
+
+ /// <summary>鏁版嵁鏇存柊浜嬩欢锛孶I璁㈤槄鍒锋柊</summary>
+ public event Action OnUpdateHeroTrialInfoEvent;
+
+ /// <summary>涓绘寫鎴樼孩鐐�</summary>
+ public Redpoint parentRedpoint = new Redpoint(MainRedDot.MainChallengeRedpoint, MainRedDot.HeroTrialRepoint);
+
+ // 褰撳ぉ鏈�鍚庝竴娆″彂璧锋寫鎴樼殑姝﹀皢鍓湰锛岀敤浜庣晫闈㈤娆℃墦寮�鏃剁殑榛樿璺宠浆銆�
+ int lastChallengeHeroIDToday;
+ // 鏅�氱孩鐐瑰綋澶╁彧鎻愮ず涓�娆★紱褰撳ぉ鏂板紑鏀炬灏嗗壇鏈粛鍙嫭绔嬫帓闃熸彁绀恒��
+ bool normalRedConsumedToday;
+
+ public override void Init()
+ {
+ DTC0102_tagCDBPlayer.beforePlayerDataInitializeEventOnRelogin += OnBeforePlayerDataInitializeEventOnRelogin;
+ FuncOpen.Instance.OnFuncStateChangeEvent += OnFuncStateChangeEvent;
+ PlayerDatas.Instance.playerDataRefreshEvent += OnPlayerDataRefreshEvent;
+ TimeUtility.OnServerOpenDayRefresh += OnServerOpenDayRefresh;
+ TimeUtility.OnServerTimeRefresh += OnServerTimeRefresh;
+ }
+
+ public override void Release()
+ {
+ DTC0102_tagCDBPlayer.beforePlayerDataInitializeEventOnRelogin -= OnBeforePlayerDataInitializeEventOnRelogin;
+ FuncOpen.Instance.OnFuncStateChangeEvent -= OnFuncStateChangeEvent;
+ PlayerDatas.Instance.playerDataRefreshEvent -= OnPlayerDataRefreshEvent;
+ TimeUtility.OnServerOpenDayRefresh -= OnServerOpenDayRefresh;
+ TimeUtility.OnServerTimeRefresh -= OnServerTimeRefresh;
+ }
+
+ void OnBeforePlayerDataInitializeEventOnRelogin()
+ {
+ heroTrialDict.Clear();
+ ResetTodayChallengeRecordCache();
+ parentRedpoint.state = RedPointState.None;
+ }
+
+ void OnFuncStateChangeEvent(int obj)
+ {
+ if (obj != funcId)
+ return;
+ RefreshHeroTrialState();
+ }
+
+ void OnPlayerDataRefreshEvent(PlayerDataType type)
+ {
+ if (type != PlayerDataType.LV && type != PlayerDataType.RealmLevel)
+ return;
+
+ RefreshHeroTrialState();
+ }
+
+ void OnServerOpenDayRefresh()
+ {
+ RefreshHeroTrialState();
+ }
+
+ void OnServerTimeRefresh()
+ {
+ int oldDay = challengeRecordDay;
+ EnsureTodayChallengeRecord();
+ if (oldDay != challengeRecordDay)
+ RefreshHeroTrialState();
+ }
+
+ /// <summary>
+ /// 澶勭悊 B203 灏佸寘锛屾洿鏂版灏嗚瘯鐐兼暟鎹�
+ /// </summary>
+ public void UpdateHeroTrialInfo(HB203_tagSCHeroTrialInfo vNetData)
+ {
+ if (vNetData == null || vNetData.HeroTrialList == null)
+ return;
+
+ for (int i = 0; i < vNetData.HeroTrialList.Length; i++)
+ {
+ var item = vNetData.HeroTrialList[i];
+ heroTrialDict[item.HeroID] = item.PassLevelNum;
+ }
+
+ RefreshHeroTrialState();
+ }
+
+ void RefreshHeroTrialState()
+ {
+ EnsureTodayChallengeRecord();
+ RefreshCurrentRedHeroID();
+ UpdateRedPoint();
+ OnUpdateHeroTrialInfoEvent?.Invoke();
+ }
+
+ void RefreshCurrentRedHeroID()
+ {
+ bool isDirty = false;
+ if (!FuncOpen.Instance.IsFuncOpen(funcId))
+ {
+ if (currentRedHeroID != 0)
+ {
+ currentRedHeroID = 0;
+ isDirty = true;
+ }
+ if (isDirty)
+ SaveTodayChallengeRecord();
+ return;
+ }
+
+ // 绾㈢偣瑙勫垯锛�
+ // 1. 褰撳ぉ鏂板紑鏀剧殑姝﹀皢浼樺厛鏄剧ず锛屽苟鎸夐厤缃『搴忛『寤躲��
+ // 2. 褰撳ぉ鏂板紑鏀鹃槦鍒楄鎸戞垬瀹屽悗锛屽綋澶╀笉鍐嶅洖娴佸埌鏃ф灏嗗壇鏈��
+ // 3. 浠婂ぉ娌℃湁鏂板紑鏀炬彁绀洪摼璺笖鏅�氱孩鐐规湭琚寫鎴樻秷璐规椂锛屾樉绀烘墍鏈夊凡瑙i攣涓旀湁鍙寫鎴樺叧鍗$殑姝﹀皢閲岋紝褰撳墠鍙寫鎴樺叧鍗℃垬鍔涙渶浣庣殑閭d釜銆�
+ if (currentRedHeroID != 0 && !CanShowHeroItemRed(currentRedHeroID))
+ {
+ currentRedHeroID = 0;
+ isDirty = true;
+ }
+
+ List<int> heroIDList = GetHeroTrialHeroIDList();
+ List<int> candidateHeroIDs = BuildCandidateHeroIDs(heroIDList);
+ List<int> todayOpenHeroIDs = BuildTodayOpenHeroIDs(candidateHeroIDs);
+ RemoveHeroIDs(candidateHeroIDs, todayOpenHeroIDs);
+
+ if (NormalizePendingNewRedHeroIDs(heroIDList))
+ isDirty = true;
+
+ if (AppendTodayOpenPendingCandidates(todayOpenHeroIDs))
+ isDirty = true;
+
+ if (NormalizePendingNewRedHeroIDs(heroIDList))
+ isDirty = true;
+
+ if (PromoteTodayOpenPendingRed(todayOpenHeroIDs))
+ {
+ isDirty = true;
+ }
+ else if (HasChallengedTodayOpenHeroTrialToday() &&
+ pendingNewRedHeroIDsToday.Count == 0 &&
+ (currentRedHeroID == 0 || !IsHeroTrialHeroOpenToday(currentRedHeroID)))
+ {
+ if (currentRedHeroID != 0)
+ {
+ currentRedHeroID = 0;
+ isDirty = true;
+ }
+ }
+ else if (!normalRedConsumedToday && (currentRedHeroID == 0 || !IsHeroTrialHeroOpenToday(currentRedHeroID)))
+ {
+ int bestHeroID = GetLowestCanChallengeHeroID(heroIDList);
+ if (currentRedHeroID != bestHeroID)
+ {
+ currentRedHeroID = bestHeroID;
+ isDirty = true;
+ }
+ }
+
+ if (isDirty)
+ SaveTodayChallengeRecord();
+ }
+
+ List<int> BuildCandidateHeroIDs(List<int> sortedHeroIDList)
+ {
+ List<int> candidateHeroIDs = new List<int>();
+ for (int i = 0; i < sortedHeroIDList.Count; i++)
+ {
+ int heroID = sortedHeroIDList[i];
+ if (!CanShowHeroItemRed(heroID))
+ continue;
+ candidateHeroIDs.Add(heroID);
+ }
+ return candidateHeroIDs;
+ }
+
+ List<int> BuildTodayOpenHeroIDs(List<int> candidateHeroIDs)
+ {
+ List<int> todayOpenHeroIDs = new List<int>();
+ for (int i = 0; i < candidateHeroIDs.Count; i++)
+ {
+ int heroID = candidateHeroIDs[i];
+ if (IsHeroTrialHeroOpenToday(heroID))
+ todayOpenHeroIDs.Add(heroID);
+ }
+ return todayOpenHeroIDs;
+ }
+
+ bool HasChallengedTodayOpenHeroTrialToday()
+ {
+ foreach (int heroID in challengedHeroIDsToday)
+ {
+ if (IsHeroTrialHeroOpenToday(heroID))
+ return true;
+ }
+
+ return false;
+ }
+
+ bool IsHeroTrialHeroOpenToday(int heroID)
+ {
+ HeroConfig heroConfig = HeroConfig.Get(heroID);
+ return heroConfig != null &&
+ heroConfig.OpenCollectionDay > 0 &&
+ heroConfig.OpenCollectionDay == TimeUtility.OpenDay + 1;
+ }
+
+ void RemoveHeroIDs(List<int> heroIDs, List<int> removeHeroIDs)
+ {
+ if (removeHeroIDs.IsNullOrEmpty())
+ return;
+
+ for (int i = heroIDs.Count - 1; i >= 0; i--)
+ {
+ if (removeHeroIDs.Contains(heroIDs[i]))
+ heroIDs.RemoveAt(i);
+ }
+ }
+
+ bool AppendTodayOpenPendingCandidates(List<int> candidateHeroIDs)
+ {
+ bool isDirty = false;
+ for (int i = 0; i < candidateHeroIDs.Count; i++)
+ {
+ int heroID = candidateHeroIDs[i];
+ if (AppendPendingCandidate(heroID))
+ isDirty = true;
+ }
+
+ return isDirty;
+ }
+
+ bool AppendPendingCandidate(int heroID)
+ {
+ if (heroID == currentRedHeroID || pendingNewRedHeroIDsToday.Contains(heroID))
+ return false;
+
+ pendingNewRedHeroIDsToday.Add(heroID);
+ return true;
+ }
+
+ bool NormalizePendingNewRedHeroIDs(List<int> sortedHeroIDList)
+ {
+ // 闃熷垪鍙繚鐣欎粛鍙彁绀虹殑褰撳ぉ鏂板紑鏀炬灏嗭紝骞舵寜閰嶇疆椤哄簭褰掍竴锛屼繚璇� E -> F -> G 椤哄欢銆�
+ List<int> normalizedHeroIDs = new List<int>();
+ for (int i = 0; i < pendingNewRedHeroIDsToday.Count; i++)
+ {
+ int heroID = pendingNewRedHeroIDsToday[i];
+ if (heroID == currentRedHeroID)
+ continue;
+
+ if (!IsHeroTrialHeroOpenToday(heroID))
+ continue;
+
+ if (!CanShowHeroItemRed(heroID))
+ continue;
+
+ if (!normalizedHeroIDs.Contains(heroID))
+ normalizedHeroIDs.Add(heroID);
+ }
+
+ normalizedHeroIDs.Sort((a, b) => GetHeroSortIndex(sortedHeroIDList, a).CompareTo(GetHeroSortIndex(sortedHeroIDList, b)));
+ if (IsSameHeroIDList(pendingNewRedHeroIDsToday, normalizedHeroIDs))
+ return false;
+
+ pendingNewRedHeroIDsToday.Clear();
+ pendingNewRedHeroIDsToday.AddRange(normalizedHeroIDs);
+ return true;
+ }
+
+ int GetHeroSortIndex(List<int> sortedHeroIDList, int heroID)
+ {
+ int index = sortedHeroIDList.IndexOf(heroID);
+ return index >= 0 ? index : int.MaxValue;
+ }
+
+ bool IsSameHeroIDList(List<int> listA, List<int> listB)
+ {
+ if (listA.Count != listB.Count)
+ return false;
+
+ for (int i = 0; i < listA.Count; i++)
+ {
+ if (listA[i] != listB[i])
+ return false;
+ }
+
+ return true;
+ }
+
+ int GetLowestCanChallengeHeroID(List<int> heroIDList)
+ {
+ int bestHeroID = 0;
+ long bestFightPower = long.MaxValue;
+ int bestSortIndex = int.MaxValue;
+
+ for (int i = 0; i < heroIDList.Count; i++)
+ {
+ int heroID = heroIDList[i];
+ if (!CanShowHeroItemRed(heroID))
+ continue;
+
+ if (!TryGetLowestCanChallengeConfig(heroID, out var config))
+ continue;
+
+ if (config.FightPower < bestFightPower)
+ {
+ bestHeroID = heroID;
+ bestFightPower = config.FightPower;
+ bestSortIndex = i;
+ continue;
+ }
+
+ if (config.FightPower == bestFightPower && i < bestSortIndex)
+ {
+ bestHeroID = heroID;
+ bestSortIndex = i;
+ }
+ }
+
+ return bestHeroID;
+ }
+
+ int GetLowestEnterHeroID(List<int> heroIDList)
+ {
+ int bestHeroID = 0;
+ long bestFightPower = long.MaxValue;
+ int bestSortIndex = int.MaxValue;
+
+ for (int i = 0; i < heroIDList.Count; i++)
+ {
+ int heroID = heroIDList[i];
+ if (!IsHeroTrialHeroUnlocked(heroID))
+ continue;
+ if (!HasChallengeableLevel(heroID))
+ continue;
+
+ if (!TryGetLowestCanChallengeConfig(heroID, out var config))
+ continue;
+
+ if (config.FightPower < bestFightPower)
+ {
+ bestHeroID = heroID;
+ bestFightPower = config.FightPower;
+ bestSortIndex = i;
+ continue;
+ }
+
+ if (config.FightPower == bestFightPower && i < bestSortIndex)
+ {
+ bestHeroID = heroID;
+ bestSortIndex = i;
+ }
+ }
+
+ return bestHeroID;
+ }
+
+ bool TryGetLowestCanChallengeConfig(int heroID, out HeroTrialConfig config)
+ {
+ config = null;
+ if (!HeroTrialConfig.TryGetHeroConfigList(heroID, out var configs))
+ return false;
+
+ for (int i = 0; i < configs.Count; i++)
+ {
+ if (!CanChallenge(configs[i], out _))
+ continue;
+
+ if (config == null || configs[i].FightPower < config.FightPower)
+ config = configs[i];
+ }
+
+ return config != null;
+ }
+
+ bool PromoteTodayOpenPendingRed(List<int> todayOpenHeroIDs)
+ {
+ if (todayOpenHeroIDs.IsNullOrEmpty())
+ return false;
+
+ if (currentRedHeroID != 0 && todayOpenHeroIDs.Contains(currentRedHeroID))
+ return false;
+
+ for (int i = 0; i < pendingNewRedHeroIDsToday.Count; i++)
+ {
+ int heroID = pendingNewRedHeroIDsToday[i];
+ if (!todayOpenHeroIDs.Contains(heroID))
+ continue;
+
+ // 浠婂ぉ鏂板紑鏀剧殑姝﹀皢浼樺厛灞曠ず锛涢槦鍒楄�楀敖鍚庡綋澶╀笉鍐嶅洖娴佸埌鏃ф灏嗗壇鏈��
+ currentRedHeroID = heroID;
+ pendingNewRedHeroIDsToday.RemoveAt(i);
+ return true;
+ }
+
+ return false;
+ }
+
+ /// <summary>
+ /// 鏇存柊绾㈢偣鐘舵��
+ /// </summary>
+ public void UpdateRedPoint()
+ {
+ parentRedpoint.state = RedPointState.None;
+
+ if (!FuncOpen.Instance.IsFuncOpen(funcId))
+ return;
+
+ if (currentRedHeroID != 0)
+ {
+ parentRedpoint.state = RedPointState.Simple;
+ }
+ }
+
+ public int GetFuncLineID(HeroTrialConfig config)
+ {
+ if (config == null)
+ return 0;
+ return GetFuncLineID(config.HeroID, config.LevelNum);
+ }
+
+ public int GetFuncLineID(int heroID, int levelNum)
+ {
+ return heroID * 1000 + levelNum;
+ }
+
+ public void AnalysisFuncLineID(int funcLineID, out int heroID, out int levelNum)
+ {
+ heroID = funcLineID / 1000;
+ levelNum = funcLineID % 1000;
+ }
+
+ public bool TryGetConfigByFuncLineID(int funcLineID, out HeroTrialConfig config)
+ {
+ AnalysisFuncLineID(funcLineID, out int heroID, out int levelNum);
+ return HeroTrialConfig.TryGetHeroTrialConfig(heroID, levelNum, out config);
+ }
+
+ public bool CanChallenge(HeroTrialConfig config, out string tip)
+ {
+ tip = string.Empty;
+ if (config == null)
+ return false;
+
+ if (!IsHeroTrialHeroUnlocked(config.HeroID, out string heroUnlockTip))
+ {
+ tip = heroUnlockTip;
+ return false;
+ }
+
+ if (!IsTrialLevelLimitMet(config, out tip))
+ return false;
+
+ bool hasProgress = TryGetHeroPassLevelNum((uint)config.HeroID, out byte passLevelNum);
+ bool isFirstLevel = HeroTrialConfig.TryGetFirstLevelConfig(config.HeroID, out var firstConfig) && firstConfig.CfgID == config.CfgID;
+ if (!hasProgress)
+ {
+ passLevelNum = 0;
+ if (isFirstLevel && passLevelNum < config.LevelNum)
+ return true;
+
+ tip = Language.Get("HeroTrial05");
+ return false;
+ }
+
+ if (isFirstLevel && passLevelNum < config.LevelNum)
+ return true;
+
+ if (config.LevelNum != passLevelNum + 1)
+ {
+ tip = Language.Get("HeroTrial05");
+ return false;
+ }
+
+ return true;
+ }
+
+ bool IsTrialLevelLimitMet(HeroTrialConfig config, out string tip)
+ {
+ tip = string.Empty;
+ if (config == null)
+ return false;
+
+ if (config.LVLimit > 0 && PlayerDatas.Instance.baseData.LV < config.LVLimit)
+ {
+ tip = Language.Get("HeroTrial04", config.LVLimit);
+ return false;
+ }
+
+ if (config.RealmLimit > 0 && PlayerDatas.Instance.baseData.realmLevel < config.RealmLimit)
+ {
+ var realmConfig = RealmConfig.Get(config.RealmLimit);
+ tip = Language.Get("HeroTrial03", realmConfig != null ? realmConfig.Name : string.Empty);
+ return false;
+ }
+
+ return true;
+ }
+
+ public bool IsShowHeroItemRed(int heroID)
+ {
+ return heroID != 0 &&
+ heroID == currentRedHeroID &&
+ CanShowHeroItemRed(heroID) &&
+ TryGetLowestCanChallengeConfig(heroID, out _);
+ }
+
+ public bool IsShowHeroTrialLevelRed(HeroTrialConfig config)
+ {
+ if (config == null || config.HeroID != currentRedHeroID)
+ return false;
+
+ if (!CanChallenge(config, out _))
+ return false;
+
+ return TryGetLowestCanChallengeConfig(config.HeroID, out var lowestConfig) &&
+ lowestConfig != null &&
+ lowestConfig.CfgID == config.CfgID;
+ }
+
+ public bool HasAnyChallengeableLevel()
+ {
+ List<int> heroIDList = GetHeroTrialHeroIDList();
+ for (int i = 0; i < heroIDList.Count; i++)
+ {
+ if (HasChallengeableLevel(heroIDList[i]))
+ return true;
+ }
+
+ return false;
+ }
+
+ bool CanShowHeroItemRed(int heroID)
+ {
+ return FuncOpen.Instance.IsFuncOpen(funcId) &&
+ IsHeroTrialHeroUnlocked(heroID) &&
+ !HasChallengedHeroTrialToday(heroID) &&
+ HasChallengeableLevel(heroID);
+ }
+
+ bool HasChallengeableLevel(int heroID)
+ {
+ if (!HeroTrialConfig.TryGetHeroConfigList(heroID, out var configs))
+ return false;
+
+ for (int i = 0; i < configs.Count; i++)
+ {
+ if (CanChallenge(configs[i], out _))
+ return true;
+ }
+
+ return false;
+ }
+
+ public void SendChallenge(HeroTrialConfig config)
+ {
+ if (!CanChallenge(config, out string tip))
+ {
+ if (!string.IsNullOrEmpty(tip))
+ {
+ SysNotifyMgr.Instance.ShowStringTip(tip);
+ }
+ return;
+ }
+
+ MarkHeroTrialChallengedToday(config.HeroID);
+ if (lastChallengeHeroIDToday != config.HeroID)
+ {
+ lastChallengeHeroIDToday = config.HeroID;
+ SaveTodayChallengeRecord();
+ }
+ bool isDirty = pendingNewRedHeroIDsToday.Remove(config.HeroID);
+ if (currentRedHeroID == config.HeroID)
+ {
+ if (!IsHeroTrialHeroOpenToday(config.HeroID))
+ normalRedConsumedToday = true;
+
+ currentRedHeroID = 0;
+ isDirty = true;
+ }
+ if (isDirty)
+ SaveTodayChallengeRecord();
+ RefreshHeroTrialState();
+ BattleManager.Instance.SendTurnFight((uint)DataMapID, (uint)GetFuncLineID(config));
+ }
+
+ public bool HasChallengedHeroTrialToday(int heroID)
+ {
+ EnsureTodayChallengeRecord();
+ return challengedHeroIDsToday.Contains(heroID);
+ }
+
+ /// <summary>
+ /// 鐣岄潰棣栨鎵撳紑鏃剁殑榛樿钀界偣锛�
+ /// 1. 褰撳墠绾㈢偣姝﹀皢锛�
+ /// 2. 涓婃鎸戞垬杩囦笖褰撳墠浠嶅彲鎸戞垬鐨勬灏嗭紱
+ /// 3. 鎵�鏈夊凡瑙i攣涓斿彲鎸戞垬鍏冲崱涓垬鍔涙渶浣庣殑姝﹀皢銆�
+ /// </summary>
+ public int GetHeroTrialEnterHeroID()
+ {
+ EnsureTodayChallengeRecord();
+
+ if (FuncOpen.Instance.IsFuncOpen(funcId))
+ {
+ if (currentRedHeroID != 0 && IsShowHeroItemRed(currentRedHeroID))
+ return currentRedHeroID;
+ }
+
+ if (lastChallengeHeroIDToday != 0 && HasChallengeableLevel(lastChallengeHeroIDToday))
+ return lastChallengeHeroIDToday;
+
+ List<int> heroIDList = GetHeroTrialHeroIDList();
+ return GetLowestEnterHeroID(heroIDList);
+ }
+
+ void MarkHeroTrialChallengedToday(int heroID)
+ {
+ EnsureTodayChallengeRecord();
+ if (challengedHeroIDsToday.Add(heroID))
+ SaveTodayChallengeRecord();
+ }
+
+ void EnsureTodayChallengeRecord()
+ {
+ if (!isChallengeRecordLoaded)
+ LoadTodayChallengeRecord();
+
+ int today = GetTodayID();
+ if (challengeRecordDay == today)
+ return;
+
+ challengeRecordDay = today;
+ challengedHeroIDsToday.Clear();
+ lastChallengeHeroIDToday = 0;
+ normalRedConsumedToday = false;
+ pendingNewRedHeroIDsToday.Clear();
+ currentRedHeroID = 0;
+ SaveTodayChallengeRecord();
+ }
+
+ void ResetTodayChallengeRecordCache()
+ {
+ challengeRecordDay = 0;
+ challengedHeroIDsToday.Clear();
+ lastChallengeHeroIDToday = 0;
+ normalRedConsumedToday = false;
+ pendingNewRedHeroIDsToday.Clear();
+ currentRedHeroID = 0;
+ isChallengeRecordLoaded = false;
+ }
+
+ void LoadTodayChallengeRecord()
+ {
+ isChallengeRecordLoaded = true;
+ challengeRecordDay = LocalSave.GetInt(challengeRecordDayKey);
+ if (challengeRecordDay != GetTodayID())
+ {
+ challengeRecordDay = GetTodayID();
+ challengedHeroIDsToday.Clear();
+ lastChallengeHeroIDToday = 0;
+ normalRedConsumedToday = false;
+ pendingNewRedHeroIDsToday.Clear();
+ currentRedHeroID = 0;
+ SaveTodayChallengeRecord();
+ return;
+ }
+
+ LoadHeroIDSet(challengedHeroIDsTodayKey, challengedHeroIDsToday);
+ lastChallengeHeroIDToday = LocalSave.GetInt(lastChallengeHeroIDTodayKey);
+ normalRedConsumedToday = LocalSave.GetInt(normalRedConsumedTodayKey) != 0;
+ LoadHeroIDList(pendingNewRedHeroIDsTodayKey, pendingNewRedHeroIDsToday);
+ currentRedHeroID = LocalSave.GetInt(currentRedHeroIDKey);
+ }
+
+ void SaveTodayChallengeRecord()
+ {
+ LocalSave.SetInt(challengeRecordDayKey, challengeRecordDay);
+ LocalSave.SetIntArray(challengedHeroIDsTodayKey, ToArray(challengedHeroIDsToday));
+ LocalSave.SetInt(lastChallengeHeroIDTodayKey, lastChallengeHeroIDToday);
+ LocalSave.SetInt(normalRedConsumedTodayKey, normalRedConsumedToday ? 1 : 0);
+ LocalSave.SetIntArray(pendingNewRedHeroIDsTodayKey, pendingNewRedHeroIDsToday.ToArray());
+ LocalSave.SetInt(currentRedHeroIDKey, currentRedHeroID);
+ }
+
+ void LoadHeroIDSet(string key, HashSet<int> heroIDSet)
+ {
+ heroIDSet.Clear();
+ int[] heroIDs = LocalSave.GetIntArray(key);
+ if (heroIDs == null)
+ return;
+
+ for (int i = 0; i < heroIDs.Length; i++)
+ {
+ if (heroIDs[i] != 0)
+ heroIDSet.Add(heroIDs[i]);
+ }
+ }
+
+ void LoadHeroIDList(string key, List<int> heroIDList)
+ {
+ heroIDList.Clear();
+ int[] heroIDs = LocalSave.GetIntArray(key);
+ if (heroIDs == null)
+ return;
+
+ for (int i = 0; i < heroIDs.Length; i++)
+ {
+ if (heroIDs[i] != 0 && !heroIDList.Contains(heroIDs[i]))
+ heroIDList.Add(heroIDs[i]);
+ }
+ }
+
+ int[] ToArray(HashSet<int> heroIDSet)
+ {
+ int[] heroIDs = new int[heroIDSet.Count];
+ heroIDSet.CopyTo(heroIDs);
+ return heroIDs;
+ }
+
+ int GetTodayID()
+ {
+ DateTime now = TimeUtility.ServerNow;
+ return now.Year * 10000 + now.Month * 100 + now.Day;
+ }
+
+ /// <summary>
+ /// 灏濊瘯鑾峰彇鏌愭灏嗙殑閫氬叧杩涘害
+ /// </summary>
+ public bool TryGetHeroPassLevelNum(uint heroID, out byte passLevelNum)
+ {
+ return heroTrialDict.TryGetValue(heroID, out passLevelNum);
+ }
+
+ public bool IsHeroTrialHeroUnlocked(int heroID)
+ {
+ return IsHeroTrialHeroUnlocked(heroID, out _);
+ }
+
+ public bool IsHeroTrialHeroUnlocked(int heroID, out string lockTip)
+ {
+ lockTip = string.Empty;
+
+ HeroConfig heroConfig = HeroConfig.Get(heroID);
+ if (heroConfig == null)
+ return false;
+
+ if (heroConfig.IsActLimit > 0)
+ {
+ bool isOpenCollectionDayMet = heroConfig.OpenCollectionDay <= 0 ||
+ TimeUtility.OpenDay + 1 >= heroConfig.OpenCollectionDay;
+ if (!isOpenCollectionDayMet)
+ {
+ lockTip = Language.Get("HeroTrial02", heroConfig.Name);
+ return false;
+ }
+ }
+ else
+ {
+ if (HeroTrialConfig.TryGetFirstLevelConfig(heroConfig.HeroID, out var trialConfig))
+ {
+ if (trialConfig.LVLimit > 0)
+ {
+ if (PlayerDatas.Instance.baseData.LV < trialConfig.LVLimit)
+ {
+ lockTip = Language.Get("HeroTrial04", trialConfig.LVLimit);
+ return false;
+ }
+ }
+ else if (trialConfig.RealmLimit > 0)
+ {
+ if (PlayerDatas.Instance.baseData.realmLevel < trialConfig.RealmLimit)
+ {
+ var config = RealmConfig.Get(trialConfig.RealmLimit);
+ lockTip = Language.Get("HeroTrial03", config != null ? config.Name : string.Empty);
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+ }
+
+ /// <summary>
+ /// 鑾峰彇姝﹀皢璇曠偧姝﹀皢鍒楄〃锛氭寜閰嶇疆琛ㄦ帓搴忥紝涓嶄緷璧栨湇鍔″櫒杩涘害鏁版嵁
+ /// </summary>
+ public List<int> GetHeroTrialHeroIDList()
+ {
+ return HeroTrialConfig.GetSortedHeroIDList();
+ }
+}
diff --git a/Main/System/HeroTrial/HeroTrialManager.cs.meta b/Main/System/HeroTrial/HeroTrialManager.cs.meta
new file mode 100644
index 0000000..98890e0
--- /dev/null
+++ b/Main/System/HeroTrial/HeroTrialManager.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 53a5e2b3e306f7e4eac9cb9750168c6a
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Main/System/HeroTrial/HeroTrialVictoryWin.cs b/Main/System/HeroTrial/HeroTrialVictoryWin.cs
new file mode 100644
index 0000000..f8e3571
--- /dev/null
+++ b/Main/System/HeroTrial/HeroTrialVictoryWin.cs
@@ -0,0 +1,85 @@
+using System.Collections.Generic;
+using Cysharp.Threading.Tasks;
+using UnityEngine;
+
+public class HeroTrialVictoryWin : UIBase
+{
+ [SerializeField] ButtonEx detailBtn;
+ [SerializeField] TextEx txtFuncName;
+ [SerializeField] ScrollerController scroller;
+
+ string battleName = BattleConst.HeroTrialBattleField;
+
+ protected override void InitComponent()
+ {
+ detailBtn.SetListener(() =>
+ {
+ BattleSettlementManager.Instance.OpenBattleDetailWin(battleName);
+ });
+ }
+
+ protected override void OnPreOpen()
+ {
+ scroller.OnRefreshCell += OnRefreshCell;
+ CreateScroller();
+ int funcId = (int)FuncOpenEnum.HeroTrial;
+ txtFuncName.text = FuncOpenLVConfig.HasKey(funcId) ? FuncOpenLVConfig.Get(funcId).Name : string.Empty;
+ }
+
+
+ protected override void OnPreClose()
+ {
+ scroller.OnRefreshCell -= OnRefreshCell;
+ BattleSettlementManager.Instance.WinShowOver(battleName);
+ }
+
+ List<Item> showItems = new List<Item>();
+ void CreateScroller()
+ {
+ var jsonData = BattleSettlementManager.Instance.GetBattleSettlement(battleName);
+ if (jsonData == null)
+ {
+ DelayCloseWindow().Forget();
+ return;
+ }
+ showItems.Clear();
+ scroller.Refresh();
+
+ if (!jsonData.ContainsKey("itemInfo"))
+ {
+ return;
+ }
+
+ var resultStr = jsonData["itemInfo"];
+ for (int i = 0; i < resultStr.Count; i++)
+ {
+ showItems.Add(new Item((int)resultStr[i]["ItemID"], (long)resultStr[i]["Count"]));
+ }
+
+ showItems.Sort(SortItem);
+ for (int i = 0; i < showItems.Count; i++)
+ {
+ scroller.AddCell(ScrollerDataType.Header, i);
+ }
+ scroller.Restart();
+ }
+
+
+
+ int SortItem(Item itemA, Item itemB)
+ {
+ var itemConfigA = ItemConfig.Get(itemA.id);
+ var itemConfigB = ItemConfig.Get(itemB.id);
+ return itemConfigB.ItemColor - itemConfigA.ItemColor;
+ }
+
+
+
+
+ void OnRefreshCell(ScrollerDataType type, CellView cell)
+ {
+ var _cell = cell as SettlementAwardCell;
+ var item = showItems[cell.index];
+ _cell?.Display(item.id, item.countEx);
+ }
+}
diff --git a/Main/System/HeroTrial/HeroTrialVictoryWin.cs.meta b/Main/System/HeroTrial/HeroTrialVictoryWin.cs.meta
new file mode 100644
index 0000000..72e2eac
--- /dev/null
+++ b/Main/System/HeroTrial/HeroTrialVictoryWin.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 35108f3aae26e9e459e60e4368a648f3
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Main/System/HeroTrial/HeroTrialWin.cs b/Main/System/HeroTrial/HeroTrialWin.cs
new file mode 100644
index 0000000..da57a92
--- /dev/null
+++ b/Main/System/HeroTrial/HeroTrialWin.cs
@@ -0,0 +1,171 @@
+using System.Collections.Generic;
+using UnityEngine;
+
+public class HeroTrialWin : UIBase
+{
+ [SerializeField] HeroTrialCarouselView carouselView;
+ [SerializeField] ScrollerController scroller;
+
+ List<HeroTrialConfig> currentConfigs = new List<HeroTrialConfig>();
+ uint currentHeroID;
+ byte currentPassLevelNum;
+ bool currentHeroUnlocked;
+ string currentHeroUnlockTip = string.Empty;
+ // OnPreOpen 棣栨鎵撳紑鏃剁敤浜庡畾浣嶅埌绾㈢偣鍏冲崱鐨� dataIndex锛�-1 琛ㄧず涓嶈烦杞��
+ int jumpTargetDataIndex = -1;
+
+ protected override void InitComponent()
+ {
+ }
+
+ protected override void OnPreOpen()
+ {
+ carouselView.InitPool();
+ carouselView.BindEvents();
+ carouselView.OnSelectedIndexChanged += OnSelectedIndexChanged;
+
+ scroller.OnRefreshCell += OnRefreshCell;
+ HeroTrialManager.Instance.OnUpdateHeroTrialInfoEvent += OnHeroTrialInfoUpdate;
+
+ carouselView.RefreshData();
+ carouselView.InitWithIndex(GetDefaultHeroIndex());
+
+ RefreshScroller();
+ }
+
+ protected override void OnPreClose()
+ {
+ carouselView.UnbindEvents();
+ carouselView.OnSelectedIndexChanged -= OnSelectedIndexChanged;
+ scroller.OnRefreshCell -= OnRefreshCell;
+ HeroTrialManager.Instance.OnUpdateHeroTrialInfoEvent -= OnHeroTrialInfoUpdate;
+ }
+
+ void OnSelectedIndexChanged()
+ {
+ RefreshScroller();
+ }
+
+ void OnHeroTrialInfoUpdate()
+ {
+ int redHeroIndex = FindRedHeroIndex();
+ if (redHeroIndex >= 0)
+ {
+ List<int> heroIDList = HeroTrialManager.Instance.GetHeroTrialHeroIDList();
+ int redHeroID = heroIDList[redHeroIndex];
+ if (carouselView.CurrentHeroID != redHeroID)
+ {
+ carouselView.SelectIndex(redHeroIndex);
+ return;
+ }
+ }
+
+ RefreshScroller();
+ }
+
+ int GetDefaultHeroIndex()
+ {
+ List<int> heroIDList = HeroTrialManager.Instance.GetHeroTrialHeroIDList();
+ if (heroIDList.IsNullOrEmpty())
+ return 0;
+
+ int targetHeroID = HeroTrialManager.Instance.GetHeroTrialEnterHeroID();
+ if (targetHeroID != 0)
+ {
+ for (int i = 0; i < heroIDList.Count; i++)
+ {
+ if (heroIDList[i] == targetHeroID)
+ return i;
+ }
+ }
+
+ return 0;
+ }
+
+ int FindRedHeroIndex()
+ {
+ List<int> heroIDList = HeroTrialManager.Instance.GetHeroTrialHeroIDList();
+ if (heroIDList.IsNullOrEmpty())
+ return -1;
+
+ for (int i = 0; i < heroIDList.Count; i++)
+ {
+ if (HeroTrialManager.Instance.IsShowHeroItemRed(heroIDList[i]))
+ return i;
+ }
+
+ return -1;
+ }
+
+ /// <summary>
+ /// 鏍规嵁褰撳墠閫変腑鐨勬灏嗭紝鍒锋柊涓嬫柟鐨勫叧鍗croll
+ /// </summary>
+ void RefreshScroller()
+ {
+ currentConfigs.Clear();
+ currentHeroID = carouselView.CurrentHeroID;
+ currentHeroUnlockTip = string.Empty;
+ currentHeroUnlocked = currentHeroID != 0 && HeroTrialManager.Instance.IsHeroTrialHeroUnlocked((int)currentHeroID, out currentHeroUnlockTip);
+ if (currentHeroID == 0 || !HeroTrialManager.Instance.TryGetHeroPassLevelNum(currentHeroID, out currentPassLevelNum))
+ {
+ currentPassLevelNum = 0;
+ }
+ if (!currentHeroUnlocked)
+ {
+ currentPassLevelNum = 0;
+ }
+
+ scroller.Refresh();
+ if (currentHeroID != 0 && HeroTrialConfig.TryGetHeroConfigList((int)currentHeroID, out var configs))
+ {
+ currentConfigs.AddRange(configs);
+ for (int i = 0; i < currentConfigs.Count; i++)
+ {
+ scroller.AddCell(ScrollerDataType.Header, currentConfigs[i].CfgID);
+ }
+ }
+ scroller.Restart();
+
+ // 棣栨鎵撳紑鏃讹紝瀹氫綅鍒板綋鍓嶆灏嗗壇鏈笅绗竴涓兘鎸戞垬鐨勫叧鍗★紱娌℃湁鍒欎繚鎸侀粯璁ょ疆椤躲��
+ jumpTargetDataIndex = FindJumpTargetDataIndex();
+ if (jumpTargetDataIndex >= 0)
+ scroller.JumpIndex(jumpTargetDataIndex);
+ }
+
+ /// <summary>
+ /// 鍦� currentConfigs 涓煡鎵惧綋鍓嶆灏嗗壇鏈涓�涓兘鎸戞垬鐨勫叧鍗″搴旂殑 dataIndex锛�
+ /// 鑻ュ綋鍓嶆灏嗘棤浠讳綍鍙寫鎴樺叧鍗℃垨鏁版嵁寮傚父鍒欒繑鍥� -1銆�
+ /// </summary>
+ int FindJumpTargetDataIndex()
+ {
+ if (currentHeroID == 0)
+ return -1;
+ if (currentConfigs.IsNullOrEmpty())
+ return -1;
+
+ for (int i = 0; i < currentConfigs.Count; i++)
+ {
+ var config = currentConfigs[i];
+ if (config == null || config.HeroID != currentHeroID)
+ continue;
+ if (HeroTrialManager.Instance.CanChallenge(config, out _))
+ return i;
+ }
+ return -1;
+ }
+
+ /// <summary>
+ /// 鍒锋柊鍏冲崱Cell鏄剧ず
+ /// </summary>
+ void OnRefreshCell(ScrollerDataType type, CellView cell)
+ {
+ var trialCell = cell as HeroTrialCell;
+ if (trialCell == null)
+ return;
+
+ if (!HeroTrialConfig.TryGetHeroTrialConfig(cell.index, out var config))
+ return;
+
+ trialCell.UpdateData(config, currentPassLevelNum, currentHeroUnlocked, currentHeroUnlockTip);
+ }
+}
diff --git a/Main/System/HeroTrial/HeroTrialWin.cs.meta b/Main/System/HeroTrial/HeroTrialWin.cs.meta
new file mode 100644
index 0000000..91b7f09
--- /dev/null
+++ b/Main/System/HeroTrial/HeroTrialWin.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: d6e3215d80964334f807ea63e0a306b8
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Main/System/Redpoint/MainRedDot.cs b/Main/System/Redpoint/MainRedDot.cs
index aa9e5aa..ed6ecab 100644
--- a/Main/System/Redpoint/MainRedDot.cs
+++ b/Main/System/Redpoint/MainRedDot.cs
@@ -160,6 +160,7 @@
public const int HeroSkinFlashSaleRepoint = 486;
public const int SuperVIPRepoint = 487;
public const int GuildAtkDefBatRepoint = 488;//鍏細鏀婚槻鎴�
+ public const int HeroTrialRepoint = 489;//姝﹀皢璇曠偧
public void Register()
{
diff --git a/Main/System/Settlement/BattleSettlementManager.cs b/Main/System/Settlement/BattleSettlementManager.cs
index 48325ca..73f544d 100644
--- a/Main/System/Settlement/BattleSettlementManager.cs
+++ b/Main/System/Settlement/BattleSettlementManager.cs
@@ -50,6 +50,9 @@
case BattleConst.BoneBattleField:
PopupWindowsProcessor.Instance.Add(isWin ? "BoneBattleVictoryWin" : "BoneBattleFailWin", false, BattleConst.BoneBattleField);
break;
+ case BattleConst.HeroTrialBattleField:
+ PopupWindowsProcessor.Instance.Add(isWin ? "HeroTrialVictoryWin" : "HeroTrialFailWin", false, BattleConst.HeroTrialBattleField);
+ break;
case BattleConst.TianziBillboradBattleField:
PopupWindowsProcessor.Instance.Add("TianziBillboradVictoryWin", false, BattleConst.TianziBillboradBattleField);
break;
@@ -99,6 +102,16 @@
else
{
UIManager.Instance.OpenWindow<BoneBattleFailWin>();
+ }
+ break;
+ case BattleConst.HeroTrialBattleField:
+ if (isWin)
+ {
+ UIManager.Instance.OpenWindowAsync<HeroTrialVictoryWin>().Forget();
+ }
+ else
+ {
+ UIManager.Instance.OpenWindowAsync<HeroTrialFailWin>().Forget();
}
break;
case BattleConst.TianziBillboradBattleField:
@@ -376,6 +389,7 @@
// 鏁屾柟鍚嶅瓧鏄疊oss鍚嶅瓧
case BattleConst.StoryBossBattleField:
case BattleConst.BoneBattleField:
+ case BattleConst.HeroTrialBattleField:
case BattleConst.TianziBillboradBattleField:
case BattleConst.WarlordPavilionBattleField:
BattleObject bossBattleObject = battleField.FindBoss();
@@ -488,4 +502,4 @@
{
public int ItemID;
public int Count;
-}
\ No newline at end of file
+}
diff --git a/Main/System/WarlordPavilion/TowerBaseWin.cs b/Main/System/WarlordPavilion/TowerBaseWin.cs
index 40e5ab0..74840fd 100644
--- a/Main/System/WarlordPavilion/TowerBaseWin.cs
+++ b/Main/System/WarlordPavilion/TowerBaseWin.cs
@@ -3,14 +3,20 @@
public class TowerBaseWin : OneLevelWin
{
[SerializeField] Transform WarlordPavilionWinTop;
+
+ [SerializeField] Transform HeroTrialWinTop;
protected override void OpenSubUIByTabIndex()
{
WarlordPavilionWinTop.SetActive(functionOrder == 0);
+ HeroTrialWinTop.SetActive(functionOrder == 1);
switch (functionOrder)
{
case 0:
currentSubUI = UIManager.Instance.OpenWindow<WarlordPavilionWin>();
break;
+ case 1:
+ currentSubUI = UIManager.Instance.OpenWindow<HeroTrialWin>();
+ break;
default:
Debug.LogWarning("鏈煡鐨勬爣绛剧储寮�: " + functionOrder);
break;
diff --git a/Main/Utility/EnumHelper.cs b/Main/Utility/EnumHelper.cs
index 3415f88..83f3962 100644
--- a/Main/Utility/EnumHelper.cs
+++ b/Main/Utility/EnumHelper.cs
@@ -862,6 +862,7 @@
FestivalActivity = 65,//鑺傛棩娲诲姩
HeroSkinFlashSale = 66,//鏃惰鐗瑰崠
GuildAtkDefBat = 67,//鍏細鏀婚槻鎴�
+ HeroTrial = 68,//姝﹀皢璇曠偧
}
--
Gitblit v1.8.0