using System; using System.Collections.Generic; using Animancer; using Logic; using Logic.Action; using MapWaterType = Logic.MapWaterType; using Logic.AI; using Logic.Audio; using Logic.CrashSight; using Logic.Multilingual; using ParadoxNotion; using RuntimeData; using MapConfig = RuntimeData.MapConfig; using Steamworks; using TH1_Core.Events; using TH1_Core.Managers; using TH1_Logic.Action; using TH1_Logic.Core; using TH1_Logic.MatchConfig; using TH1_Logic.Net; using TH1_Logic.Steam; using TH1_UI.View.Announce; using TH1Resource; using TMPro; using Unity.VisualScripting; using UnityEngine; using UnityEngine.UI; namespace TH1_UI.View.Outside { public class UIOutsideSelectView : Base.View { [Header("按钮")] public Button CloseButton; public Button SettingButton; [Header("选择列表")] public List CivForceList; public List PicList; public GameObject SelectAvatarPrefab; public GameObject SelectList; private List _monoList; private UIOutsideSelectAvatarMono _randomAvatarMono; private Empire _selectEmpire; private bool _selectRandom; //暴露当前选中的阵营,供 Controller 在开始游戏时传给 Loading 界面 public Empire SelectedEmpire => _selectEmpire; [Header("详细信息")] public RectTransform InfoGroup; public RectTransform CivBG; public TextMeshProUGUI ForceName; public TextMeshProUGUI CivName; public TextMeshProUGUI LeaderDesc; public TextMeshProUGUI EmpireDesc; public List StarList; public List GreyStarList; public GameObject SkillCircleGroup; public GameObject SkillCirclePrefab; public List SkillCircleList; public GameObject RandomPanel; public AnimancerComponent RandomPanelAnimancer; [Header("中央立绘区")] public Image BG1; public Image BG2; public List ForceGroupList; [Header("子界面")] public UIOutsideSelectCheckPanelMono SelectCheckPanelMono; //关闭时执行的委托 public ViDelegateAssisstant.Dele OnBtnCloseClick; //开始游戏时执行的委托(目前委托内容就是执行controller的Close()) public ViDelegateAssisstant.Dele OnStartGame; // PlayerPrefs key for saving last selected empire private const string LAST_SELECTED_CIV_KEY = "LastSelectedCiv"; private const string LAST_SELECTED_FORCE_KEY = "LastSelectedForce"; // PlayerPrefs key for saving last selected map size in CREATIVE mode private const string LAST_MAP_SIZE_INDEX_KEY = "LastCreativeMapSizeIndex"; protected override void OnInit() { base.OnInit(); //清空List下的内容 for(int i = SelectList.transform.childCount - 1;i >= 0;i --) { Transform child = SelectList.transform.GetChild(i); GameObject.Destroy(child.gameObject); } _monoList = new List(); foreach (var t in CivForceList) { if (!ContentGate.CanSelectEmpire(t)) continue; // TODO: 暂时屏蔽第四阵营(Indian/Satori),待正式上线后移除此判断 //if (t.Civ == CivEnum.Indian) continue; var obj = Instantiate(SelectAvatarPrefab, SelectList.transform); var mono = obj.GetComponent(); if (mono == null) continue; mono.SetContent(t,OnAvatarClicked); _monoList.Add(mono); } CreateRandomAvatar(); SetRandomPanelVisible(false, true); CloseButton.onClick.RemoveAllListeners(); CloseButton.onClick.AddListener(() => { OnBtnCloseClick?.Invoke(); }); //处理ChekPanel SelectCheckPanelMono.Init(OnStartClicked); SettingButton.onClick.RemoveAllListeners(); SettingButton.onClick.AddListener(OnSettingClicked); } private void OnAvatarClicked(Empire playerEmpire) { if (!ContentGate.CanUseEmpire(playerEmpire)) return; _selectRandom = false; foreach(var t in _monoList) if(t.PlayerEmpire != playerEmpire) t.SetHide(); SetRandomPanelVisible(false); SetCivForceInfo(playerEmpire); } private void OnRandomAvatarClicked(UIOutsideSelectAvatarMono randomMono) { _selectRandom = true; foreach(var t in _monoList) if(t != randomMono) t.SetHide(); SelectCheckPanelMono?.SetPlayerEmpire(new Empire(CivEnum.Common, ForceEnum.Common), true); SetRandomPanelVisible(true); } public void Update() { } public void SetContent(ShowUIOutsideSelect evt) { if (_monoList.Count == 0) return; SelectCheckPanelMono.gameObject.SetActive(false) ; // Try to restore last selected empire Empire? lastSelectedEmpire = GetLastSelectedEmpire(); UIOutsideSelectAvatarMono targetMono = null; if (lastSelectedEmpire.HasValue) { // Find the mono that matches the last selected empire foreach (var mono in _monoList) { if (mono.PlayerEmpire.Civ == lastSelectedEmpire.Value.Civ && mono.PlayerEmpire.Force == lastSelectedEmpire.Value.Force) { targetMono = mono; break; } } } // If no last selection found or not available, default to first if (targetMono == null) { targetMono = GetFirstSelectableAvatar(); if (targetMono == null) return; } targetMono.Button.onClick.Invoke(); SetCivForceInfo(targetMono.PlayerEmpire); } public void SetCivForceInfo(Empire playerEmpire) { if (!ContentGate.CanUseEmpire(playerEmpire)) return; _selectRandom = false; _selectEmpire = playerEmpire; SelectCheckPanelMono?.SetPlayerEmpire(playerEmpire); if (!Table.Instance.PlayerDataAssets.GetPlayerInfo(playerEmpire.Civ, playerEmpire.Force, out var info)) return; //Step #1 设置文明基础文字信息 MultilingualManager.Instance.SetUIText(ForceName,info.ForceName); MultilingualManager.Instance.SetUIText(CivName,info.CivName); LayoutRebuilder.ForceRebuildLayoutImmediate(CivBG); //Step #2 设置文明的难度情况 var dif = info.Diff; var grey = 4 - dif; for(int i = 0;i < StarList.Count ; i++) StarList[i].SetActive(i < dif); for(int i = 0;i < GreyStarList.Count ; i++) GreyStarList[i].SetActive(i < grey); //Step #2 设置文明特色SkillGroup //清空SkillGroup for (int i = SkillCircleGroup.transform.childCount - 1; i >= 0; i--) { Transform child = SkillCircleGroup.transform.GetChild(i); child.gameObject.SetActive(false); } for (int i = SkillCircleGroup.transform.childCount; i < info.TechAtomList.Count; i++) { var pre = Instantiate(SkillCirclePrefab, SkillCircleGroup.transform); var mono = pre?.GetComponent(); if(mono == null)continue; SkillCircleList.Add(mono); } SkillCircleList = new List(); for (int i = 0; i < info.TechAtomList.Count; i++) { Transform child = SkillCircleGroup.transform.GetChild(i); if (child == null) break; child.gameObject.SetActive(true); var mono = child?.GetComponent(); if(mono == null)continue; SkillCircleList.Add(mono); mono.SetContent(playerEmpire,info.TechAtomList[i]); } //Step #3 设置文明基础文字信息 MultilingualManager.Instance.SetUIText(EmpireDesc,info.EmpireDesc); MultilingualManager.Instance.SetUIText(LeaderDesc,info.LeaderDesc); //LayoutRebuilder.ForceRebuildLayoutImmediate(CivBG); //LayoutRebuilder.ForceRebuildLayoutImmediate(SkillCircleGroup.GetComponent()); LayoutRebuilder.ForceRebuildLayoutImmediate(InfoGroup); //Step #4 设置立绘 foreach (var forceGroup in ForceGroupList) { if (forceGroup.Empire == playerEmpire) { forceGroup.gameObject.SetActive(true); BG1.color = info.Color; BG2.color = info.Color; foreach(var heroPack in forceGroup.HeroList) heroPack.SetContent(); } else forceGroup.gameObject.SetActive(false); } //Step #5 播放对应音乐 AudioManager.Instance.PlayMusic(info.MusicName,1f,1f,true); } //在Controller 调用Close的时候,会先调用这个closeview public void OnCloseView() { } public void OnSettingClicked() { SelectCheckPanelMono.Show(); } public void OnStartClicked() { bool wasRandomSelected = _selectRandom; if (wasRandomSelected) { ApplyRandomSelectedEmpire(); } if (!ContentGate.CanUseEmpire(_selectEmpire)) { var targetMono = GetFirstSelectableAvatar(); if (targetMono == null) return; SetCivForceInfo(targetMono.PlayerEmpire); } GameMode gameMode = SelectCheckPanelMono.GameMode.SelectedIndex switch { 0 => GameMode.DOMINATION, 1 => GameMode.PERFECT, _ => GameMode.CREATIVE }; uint playerCount = SelectCheckPanelMono.PlayerCount.SelectedIndex + 2; uint mapSize; mapSize = SelectCheckPanelMono.MapSize.SelectedIndex switch { 0 => 11, 1 => 14, 2 => 16, 3 => 18, 4 => 20, 5 => 24, 6 => 30, _ => 18 }; AIDifficult diff = SelectCheckPanelMono.Diff.SelectedIndex switch { 0 => AIDifficult.EASY, 1 => AIDifficult.NORMAL, 2 => AIDifficult.HARD, _ => AIDifficult.LUNATIC }; MapWaterType waterType = (MapWaterType)(SelectCheckPanelMono.Water != null ? SelectCheckPanelMono.Water.SelectedIndex : 2); // default Pangea // domination => 2, perfect => 6, creative => custom win group uint matchConfigId = gameMode switch { GameMode.PERFECT => 6, GameMode.CREATIVE => SelectCheckPanelMono.GetSelectedCreativeMatchConfigId(), _ => 2 // DOMINATION }; Main.Instance.MapConfig.ChangeByMapConfig(MatchConfigManager.Instance.GetMatchConfig(matchConfigId)); Main.Instance.MapConfig.Id = matchConfigId; Main.Instance.MapConfig.GameMode = gameMode; Main.Instance.MapConfig.Width = mapSize; Main.Instance.MapConfig.Height = mapSize; Main.Instance.MapConfig.SetPlayerCount(playerCount, NetMode.Single); Main.Instance.MapConfig.AIDiff = diff; Main.Instance.MapConfig.WaterType = waterType; Main.Instance.MapConfig.DisableNearbySpawnPoints = gameMode == GameMode.CREATIVE && SelectCheckPanelMono.DisableNearbySpawnPoints; SelectCheckPanelMono.SaveDisableNearbySpawnPointsPreference(); Main.Instance.MapConfig.SetSinglePlayerCiv( Table.Instance.TransCivEnumToCivId(_selectEmpire.Civ), Table.Instance.TransForceEnumToForceId(_selectEmpire.Force)); SelectCheckPanelMono.PrepareAISettingsForStart(playerCount, gameMode == GameMode.CREATIVE); ApplyAISettingsToMapConfig(gameMode); // Save the selected empire for next time if (!wasRandomSelected) { SaveLastSelectedEmpire(_selectEmpire); } // CREATIVE模式下保存MapSize选择 if (gameMode == GameMode.CREATIVE) { SaveLastMapSize(SelectCheckPanelMono.MapSize.SelectedIndex); } //设置好参数后,调用Controller的委托 OnStartGame?.Invoke(); } private void ApplyAISettingsToMapConfig(GameMode gameMode) { if (SelectCheckPanelMono == null || Main.Instance.MapConfig?.MultiCivs == null) return; int maxTeamId = Mathf.Max(1, (int)Main.Instance.MapConfig.PlayerCount); var selfSlot = Main.Instance.MapConfig.GetPlayerSlot(0, NetMode.Single); if (selfSlot != null) { selfSlot.TeamId = gameMode == GameMode.CREATIVE ? 1 : MapConfig.NoTeamId; selfSlot.IsAI = false; selfSlot.IsHostControlled = false; selfSlot.IsCivFixed = true; } foreach (var setting in SelectCheckPanelMono.AISettings) { var slot = Main.Instance.MapConfig.GetPlayerSlot(setting.SlotIndex, NetMode.Single); if (slot == null) continue; slot.Index = setting.SlotIndex; slot.MemberId = 0; slot.PlayerId = 0; slot.CivId = setting.IsRandom ? MapConfig.RandomCivId : setting.CivId; slot.ForceId = setting.IsRandom ? MapConfig.RandomForceId : setting.ForceId; slot.TeamId = gameMode == GameMode.CREATIVE ? Mathf.Clamp(setting.TeamId, 1, maxTeamId) : MapConfig.NoTeamId; slot.IsCivFixed = !setting.IsRandom; slot.IsReady = false; slot.IsAI = !SelectCheckPanelMono.IsTestMode; slot.IsHostControlled = SelectCheckPanelMono.IsTestMode; } Main.Instance.MapConfig.ResolveRandomPlayerSlotCivsForStart(NetMode.Single); LogSingleCreativeTeamConfig(gameMode); } private void LogSingleCreativeTeamConfig(GameMode gameMode) { var config = Main.Instance.MapConfig; if (gameMode != GameMode.CREATIVE || config?.MultiCivs == null) return; var sb = new System.Text.StringBuilder(); sb.Append("[SingleCreativeTeam] BeforeStart "); for (int i = 0; i < config.MultiCivs.Count; i++) { var slot = config.MultiCivs[i]; if (slot == null) continue; if (i > 0) sb.Append(" | "); sb.Append($"slot={slot.Index},team={slot.TeamId},civ={slot.CivId},force={slot.ForceId},ai={slot.IsAI},hostControlled={slot.IsHostControlled}"); } LogSystem.LogInfo(sb.ToString()); } private void CreateRandomAvatar() { if (SelectAvatarPrefab == null || SelectList == null) return; var obj = Instantiate(SelectAvatarPrefab, SelectList.transform); _randomAvatarMono = obj.GetComponent(); if (_randomAvatarMono == null) return; var randomAvatar = Table.Instance?.PlayerDataAssets?.CommonPlayerAvatar; if (randomAvatar == null) { randomAvatar = TH1Resource.ResourceLoader.Load("TH1UI/Common/CommonAvatar/CommonAvatar"); } var randomTitle = Table.Instance?.TextDataAssets?.OutsideSelectRandomEmpire; _randomAvatarMono.SetRandomContent(randomAvatar, randomTitle, OnRandomAvatarClicked); _monoList.Add(_randomAvatarMono); } private void ApplyRandomSelectedEmpire() { var candidates = new List(); foreach (var mono in _monoList) { if (mono == null || mono.IsRandom) continue; candidates.Add(mono.PlayerEmpire); } if (candidates.Count == 0) return; var index = UnityEngine.Random.Range(0, candidates.Count); SetCivForceInfo(candidates[index]); } private UIOutsideSelectAvatarMono GetFirstSelectableAvatar() { foreach (var mono in _monoList) { if (mono == null || mono.IsRandom) continue; if (ContentGate.CanUseEmpire(mono.PlayerEmpire)) return mono; } return null; } private void SetRandomPanelVisible(bool visible, bool immediate = false) { if (RandomPanel == null) return; var canvasGroup = RandomPanel.GetComponent(); if (immediate) { if (RandomPanelAnimancer != null) RandomPanelAnimancer.Stop(); if (canvasGroup != null) canvasGroup.alpha = visible ? 1f : 0f; RandomPanel.SetActive(visible); return; } if (visible) { RandomPanel.SetActive(true); var clip = ResourceCache.Instance?.AnimCache?.UICommonPanelFadeIn; if (RandomPanelAnimancer != null && RandomPanelAnimancer.Animator != null && clip != null) { RandomPanelAnimancer.Play(clip); } else if (canvasGroup != null) { canvasGroup.alpha = 1f; } return; } if (!RandomPanel.activeSelf) return; var fadeOutClip = ResourceCache.Instance?.AnimCache?.UICommonPanelFadeOut; if (RandomPanelAnimancer != null && RandomPanelAnimancer.Animator != null && fadeOutClip != null) { var state = RandomPanelAnimancer.Play(fadeOutClip); state.Events.OnEnd = () => { if (RandomPanel != null) RandomPanel.SetActive(false); state.Events.OnEnd = null; }; } else { if (canvasGroup != null) canvasGroup.alpha = 0f; RandomPanel.SetActive(false); } } /// /// Save the last selected empire to PlayerPrefs /// private void SaveLastSelectedEmpire(Empire empire) { PlayerPrefs.SetInt(LAST_SELECTED_CIV_KEY, (int)empire.Civ); PlayerPrefs.SetInt(LAST_SELECTED_FORCE_KEY, (int)empire.Force); PlayerPrefs.Save(); } /// /// Save the last selected map size index for CREATIVE mode /// private void SaveLastMapSize(uint mapSizeIndex) { PlayerPrefs.SetInt(LAST_MAP_SIZE_INDEX_KEY, (int)mapSizeIndex); PlayerPrefs.Save(); } /// /// Get the last selected empire from PlayerPrefs /// private Empire? GetLastSelectedEmpire() { if (!PlayerPrefs.HasKey(LAST_SELECTED_CIV_KEY) || !PlayerPrefs.HasKey(LAST_SELECTED_FORCE_KEY)) return null; int civValue = PlayerPrefs.GetInt(LAST_SELECTED_CIV_KEY); int forceValue = PlayerPrefs.GetInt(LAST_SELECTED_FORCE_KEY); // Validate the values are valid enums if (!Enum.IsDefined(typeof(CivEnum), civValue) || !Enum.IsDefined(typeof(ForceEnum), forceValue)) return null; var empire = new Empire((CivEnum)civValue, (ForceEnum)forceValue); return ContentGate.CanUseEmpire(empire) ? empire : null; } public void CloseView() { AudioManager.Instance.StopMusic(); } } }