Files
bansonic_beta_main/Assets/scripts/Team/newTeamSelector.cs
T
2026-07-16 23:04:59 +08:00

527 lines
16 KiB
C#

using System.Collections.Generic;
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class newTeamSelector : MonoBehaviour
{
public static newTeamSelector Instance { get; private set; }
[Header("Inspector")]
public Sprite levelIcon_sprite_C;
public Sprite levelIcon_sprite_B;
public Sprite levelIcon_sprite_A;
public Sprite levelIcon_sprite_S;
[Header("Inspector")]
public int selected_heroSlot01_heroID;
public int selected_heroSlot02_heroID;
public int selected_heroSlot03_heroID;
public int selected_heroSlot04_heroID;
public int selected_heroSlot05_heroID;
[Tooltip("Prefab for a single hero slot. Must contain slots_heroSlots component.")]
public GameObject slotPrefab;
[Tooltip("Container (GameObject with HorizontalLayoutGroup) where slots will be instantiated")]
public RectTransform container;
[Tooltip("Number of slots to create (default 5)")]
public int slotCount = 5;
[Tooltip("Optional override colors used for header images per slot. If not set, prefab's headerImage_colors will be used.")]
public Color[] overrideHeaderColors;
[Tooltip("Per-slot default images used when the slot is empty. Index 0-4 maps to slot 1-5.")]
public Sprite[] slotDefaultImage;
[Tooltip("If true, create slots automatically on Start")]
public bool instantiateOnStart = true;
[Tooltip("Delay skill list rebuild until after slot enter animation to reduce hitching.")]
public float delayedSkillRefreshSeconds = 0.75f;
[Header("Team Share")]
public TeamShareApplyPanel teamShareApplyPanel;
private readonly List<slots_heroSlots> createdSlots = new List<slots_heroSlots>();
private static AllyHero_SO[] cachedHeroes;
private static Dictionary<int, AllyHero_SO> cachedHeroesById;
private Coroutine refreshSkillsRoutine;
private Coroutine startupSkillValidationRoutine;
void Awake()
{
if (Instance == null)
{
Instance = this;
EnsureHeroCache();
}
else
{
Destroy(gameObject);
}
}
void Start()
{
RefreshTeamShareCodeDisplay();
if (instantiateOnStart)
{
CreateSlots();
}
if (startupSkillValidationRoutine != null)
{
StopCoroutine(startupSkillValidationRoutine);
}
startupSkillValidationRoutine = StartCoroutine(StartupLoadRoutine());
}
private IEnumerator StartupLoadRoutine()
{
yield return null;
yield return AllyHeroSkillSelectionValidator.ValidateAllHeroesAsync();
LoadSelectedHeroes();
startupSkillValidationRoutine = null;
}
public void CreateSlots()
{
if (slotPrefab == null || container == null)
{
Debug.LogError("newTeamSelector: slotPrefab or container is not assigned.");
return;
}
// Clear existing
for (int i = container.childCount - 1; i >= 0; i--)
{
var c = container.GetChild(i);
#if UNITY_EDITOR
if (!Application.isPlaying) DestroyImmediate(c.gameObject);
else Destroy(c.gameObject);
#else
Destroy(c.gameObject);
#endif
}
createdSlots.Clear();
for (int i = 0; i < slotCount; i++)
{
var go = Instantiate(slotPrefab, container);
go.transform.SetParent(container, false);
go.name = $"heroSlot_{i}";
var slotComp = go.GetComponent<slots_heroSlots>();
if (slotComp == null)
{
Debug.LogWarning("newTeamSelector: instantiated prefab does not contain slots_heroSlots component.");
continue;
}
slotComp.heroSlot_slotID = i;
// choose color: overrideHeaderColors takes priority, then prefab's headerImage_colors if available
Color chosen = Color.white;
if (overrideHeaderColors != null && i < overrideHeaderColors.Length)
{
chosen = overrideHeaderColors[i];
}
else if (slotComp.headerImage_colors != null && i < slotComp.headerImage_colors.Length)
{
chosen = slotComp.headerImage_colors[i];
}
// Ensure visible alpha
chosen.a = Mathf.Clamp01(chosen.a);
if (chosen.a <= 0f) chosen.a = 1f;
// Apply immediately if possible
if (slotComp.heroSlot_colorImage != null)
{
slotComp.heroSlot_colorImage.color = chosen;
}
createdSlots.Add(slotComp);
}
// Force immediate layout rebuild so HorizontalLayoutGroup arranges children right away
LayoutRebuilder.ForceRebuildLayoutImmediate(container);
// Re-apply colors next frame to override any Start() logic on instantiated prefabs
StartCoroutine(ApplyHeaderColorsNextFrame());
}
private IEnumerator ApplyHeaderColorsNextFrame()
{
yield return null; // wait one frame
for (int i = 0; i < createdSlots.Count; i++)
{
var slot = createdSlots[i];
if (slot == null) continue;
Color chosen = Color.white;
if (overrideHeaderColors != null && i < overrideHeaderColors.Length)
{
chosen = overrideHeaderColors[i];
}
else if (slot.headerImage_colors != null && i < slot.headerImage_colors.Length)
{
chosen = slot.headerImage_colors[i];
}
chosen.a = 1f;
if (slot.heroSlot_colorImage == null)
{
var img = slot.GetComponentInChildren<Image>(true);
if (img != null) slot.heroSlot_colorImage = img;
}
if (slot.heroSlot_colorImage != null)
slot.heroSlot_colorImage.color = chosen;
}
// ensure layout rebuilt after modifications
LayoutRebuilder.ForceRebuildLayoutImmediate(container);
}
public List<slots_heroSlots> GetCreatedSlots() => createdSlots;
public void UpdateAndSaveSelectedHeroes()
{
int[] ids = new int[slotCount > 0 ? slotCount : 5];
for (int i = 0; i < ids.Length; i++)
{
ids[i] = i < createdSlots.Count && createdSlots[i] != null ? Mathf.Max(0, createdSlots[i].heroSlot_heroID) : 0;
}
ApplySelectedHeroIdsToFields(ids);
SyncCurrentTeamFromIds(ids);
SaveSelectedHeroIdsToPlayerPrefs(ids);
PlayerPrefs.Save();
Debug.Log("已保存当前英雄到 PlayerPrefs");
RefreshTeamShareCodeDisplay();
}
public void LoadSelectedHeroes()
{
if (createdSlots.Count == 0) return;
int[] selectedIds;
bool loadedFromCurrentTeam = TryGetCurrentTeamHeroIds(out selectedIds);
if (!loadedFromCurrentTeam)
{
selectedIds = ReadSelectedHeroIdsFromPlayerPrefs();
SyncCurrentTeamFromIds(selectedIds);
}
else
{
SaveSelectedHeroIdsToPlayerPrefs(selectedIds);
}
ApplySelectedHeroIdsToFields(selectedIds);
// Set the slots
if (createdSlots.Count > 0) createdSlots[0].heroSlot_heroID = selected_heroSlot01_heroID;
if (createdSlots.Count > 1) createdSlots[1].heroSlot_heroID = selected_heroSlot02_heroID;
if (createdSlots.Count > 2) createdSlots[2].heroSlot_heroID = selected_heroSlot03_heroID;
if (createdSlots.Count > 3) createdSlots[3].heroSlot_heroID = selected_heroSlot04_heroID;
if (createdSlots.Count > 4) createdSlots[4].heroSlot_heroID = selected_heroSlot05_heroID;
bool changed = false;
// Update each slot's UI
for (int i = 0; i < createdSlots.Count; i++)
{
var slot = createdSlots[i];
if (slot == null)
continue;
if (slot.heroSlot_heroID != 0)
{
// Find the hero SO
AllyHero_SO hero = FindHeroById(slot.heroSlot_heroID);
if (hero != null && hero.isUnlocked)
{
slot.RefreshSlotVisualsFromCurrentHero(false);
}
else
{
slot.heroSlot_heroID = 0;
slot.RefreshSlotVisualsFromCurrentHero(false);
changed = true;
}
}
else
{
slot.heroSlot_heroID = 0;
slot.RefreshSlotVisualsFromCurrentHero(false);
}
}
if (changed)
{
UpdateAndSaveSelectedHeroes();
}
if (refreshSkillsRoutine != null)
StopCoroutine(refreshSkillsRoutine);
refreshSkillsRoutine = StartCoroutine(RefreshSkillsGradually());
StartCoroutine(ForceRefreshSlotNamesNextFrame());
RefreshTeamShareCodeDisplay();
}
private IEnumerator ForceRefreshSlotNamesNextFrame()
{
yield return null;
for (int i = 0; i < createdSlots.Count; i++)
{
var slot = createdSlots[i];
if (slot == null)
continue;
slot.RefreshSlotVisualsFromCurrentHero(false);
}
}
private int[] ReadSelectedHeroIdsFromPlayerPrefs()
{
TeamSelectionDefaults.EnsureDefaultTeamIfMissing();
int[] ids = new int[Mathf.Max(5, slotCount)];
if (ids.Length > 0) ids[0] = TeamSelectionDefaults.ReadHeroId(1);
if (ids.Length > 1) ids[1] = TeamSelectionDefaults.ReadHeroId(2);
if (ids.Length > 2) ids[2] = TeamSelectionDefaults.ReadHeroId(3);
if (ids.Length > 3) ids[3] = TeamSelectionDefaults.ReadHeroId(4);
if (ids.Length > 4) ids[4] = TeamSelectionDefaults.ReadHeroId(5);
return ids;
}
private void SaveSelectedHeroIdsToPlayerPrefs(int[] ids)
{
PlayerPrefs.SetInt("selected_heroSlot01_heroID", GetIdAt(ids, 0));
PlayerPrefs.SetInt("selected_heroSlot02_heroID", GetIdAt(ids, 1));
PlayerPrefs.SetInt("selected_heroSlot03_heroID", GetIdAt(ids, 2));
PlayerPrefs.SetInt("selected_heroSlot04_heroID", GetIdAt(ids, 3));
PlayerPrefs.SetInt("selected_heroSlot05_heroID", GetIdAt(ids, 4));
}
private void ApplySelectedHeroIdsToFields(int[] ids)
{
selected_heroSlot01_heroID = GetIdAt(ids, 0);
selected_heroSlot02_heroID = GetIdAt(ids, 1);
selected_heroSlot03_heroID = GetIdAt(ids, 2);
selected_heroSlot04_heroID = GetIdAt(ids, 3);
selected_heroSlot05_heroID = GetIdAt(ids, 4);
}
private bool TryGetCurrentTeamHeroIds(out int[] ids)
{
ids = new int[Mathf.Max(5, slotCount)];
TeamManager teamManager = TeamManager.Instance;
if (teamManager == null)
return false;
TeamSetting currentTeam = null;
try
{
currentTeam = teamManager.getCurrentSelectedTeam();
}
catch
{
currentTeam = null;
}
if (currentTeam == null || currentTeam.teamIdList == null)
return false;
bool hasAnyHero = false;
for (int i = 0; i < ids.Length && i < currentTeam.teamIdList.Count; i++)
{
CharacterView slot = currentTeam.teamIdList[i];
ids[i] = slot != null ? Mathf.Max(0, slot.characterId) : 0;
if (ids[i] > 0)
hasAnyHero = true;
}
return hasAnyHero;
}
private void SyncCurrentTeamFromIds(int[] ids)
{
TeamManager teamManager = TeamManager.Instance;
if (teamManager == null)
return;
TeamSetting currentTeam = null;
try
{
currentTeam = teamManager.getCurrentSelectedTeam();
}
catch
{
currentTeam = null;
}
if (currentTeam == null)
{
currentTeam = new TeamSetting(new List<CharacterView>(), -1);
}
if (currentTeam.teamIdList == null)
{
currentTeam.teamIdList = new List<CharacterView>();
}
int targetCount = Mathf.Max(5, ids != null ? ids.Length : 0);
while (currentTeam.teamIdList.Count < targetCount)
{
currentTeam.teamIdList.Add(new CharacterView(0, null));
}
for (int i = 0; i < targetCount; i++)
{
int heroId = GetIdAt(ids, i);
string boundaryLevel = ResolveBoundaryLevel(heroId);
if (currentTeam.teamIdList[i] == null)
{
currentTeam.teamIdList[i] = new CharacterView(heroId, boundaryLevel);
}
else
{
currentTeam.teamIdList[i].characterId = heroId;
currentTeam.teamIdList[i].currentBoundaryLevel = boundaryLevel;
}
}
if (currentTeam.LeaderId <= 0 || !ContainsHeroId(currentTeam.teamIdList, currentTeam.LeaderId))
{
currentTeam.LeaderId = FindFirstHeroId(ids);
}
teamManager.setCurrentSelectedTeam(currentTeam, teamManager.currentSelectedTeam);
}
private static int GetIdAt(int[] ids, int index)
{
if (ids == null || index < 0 || index >= ids.Length)
return 0;
return Mathf.Max(0, ids[index]);
}
private static bool ContainsHeroId(List<CharacterView> teamIdList, int heroId)
{
if (teamIdList == null || heroId <= 0)
return false;
for (int i = 0; i < teamIdList.Count; i++)
{
CharacterView view = teamIdList[i];
if (view != null && view.characterId == heroId)
return true;
}
return false;
}
private static int FindFirstHeroId(int[] ids)
{
if (ids == null)
return -1;
for (int i = 0; i < ids.Length; i++)
{
if (ids[i] > 0)
return ids[i];
}
return -1;
}
private string ResolveBoundaryLevel(int heroId)
{
if (heroId <= 0)
return null;
AllyHero_SO hero = FindHeroById(heroId);
return hero != null ? GetRatingFromSO(hero) : null;
}
private IEnumerator RefreshSkillsGradually()
{
if (delayedSkillRefreshSeconds > 0f)
yield return new WaitForSecondsRealtime(delayedSkillRefreshSeconds);
else
yield return null;
for (int i = 0; i < createdSlots.Count; i++)
{
var slot = createdSlots[i];
if (slot == null) continue;
if (slot.heroSlot_heroID != 0)
{
slot.UpdateSkillList(false);
// Spread UI instantiation across frames to reduce hitch.
yield return null;
}
}
TeamSelectorAnimController.PlaySkillsFlash(null);
refreshSkillsRoutine = null;
RefreshTeamShareCodeDisplay();
}
public void RefreshTeamShareCodeDisplay()
{
if (teamShareApplyPanel != null)
{
teamShareApplyPanel.RefreshShareCodeDisplay();
}
}
private AllyHero_SO FindHeroById(int id)
{
EnsureHeroCache();
if (cachedHeroesById != null && cachedHeroesById.TryGetValue(id, out var hero))
return hero;
RebuildHeroCache();
if (cachedHeroesById != null && cachedHeroesById.TryGetValue(id, out hero))
return hero;
return null;
}
private static void EnsureHeroCache()
{
if (cachedHeroesById != null && cachedHeroesById.Count > 0)
return;
RebuildHeroCache();
}
private static void RebuildHeroCache()
{
cachedHeroes = RuntimeResourcesCache.LoadAllAllyHeroes();
cachedHeroesById = new Dictionary<int, AllyHero_SO>();
if (cachedHeroes == null) return;
for (int i = 0; i < cachedHeroes.Length; i++)
{
AllyHero_SO hero = cachedHeroes[i];
if (hero == null) continue;
cachedHeroesById[hero.ally_heroID] = hero;
}
}
private string GetRatingFromSO(AllyHero_SO so)
{
return so != null ? so.GetDisplayLevelRatingKey() : "C";
}
}